Files
2026-02-17 13:06:23 +08:00

430 lines
16 KiB
Go

package service
import (
"fmt"
"reflect"
"strings"
"xiawan/wx/api/req"
"xiawan/wx/api/vo"
"xiawan/wx/clientsdk"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/clientsdk/proxynet"
"xiawan/wx/db"
"xiawan/wx/db/table"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv"
"xiawan/wx/srv/srvconfig"
"xiawan/wx/srv/websrv"
"xiawan/wx/srv/wxcore"
"xiawan/wx/srv/wxface"
"xiawan/wx/srv/wxrouter"
"github.com/lunny/log"
)
// 基础服务功能
var WXServer wxface.IWXServer
type BusinessFunc = func(connect wxface.IWXConnect, newIWXConnect bool) vo.DTO
type USerInfoCallFunc = func(account *srv.WXAccount, invoker wxface.IWXReqInvoker, state uint32) vo.DTO
// 初始化微信响应路由
func InitWXServerRouter() {
// 开启服务器
wxServer := wxcore.NewWXServer()
// 注册微信响应路由
// wxServer.AddWXRouter(baseinfo.MMRequestTypeGetLoginQRCode, new(wxrouter.WXGetLoginQrcodeRouter)) // 获取登陆二维码响应
wxServer.AddWXRouter(baseinfo.MMRequestTypeCheckLoginQRCode, new(wxrouter.WXCheckQrcodeRouter)) // 检测二维码状态请求
wxServer.AddWXRouter(baseinfo.MMRequestTypePushQrLogin, new(wxrouter.WXPushQrCodeLoginRouter)) // 唤醒登录
wxServer.AddWXRouter(baseinfo.MMRequestTypeManualAuth, new(wxrouter.WXManualAuthRouter))
wxServer.AddWXRouter(baseinfo.MMRequestTypeHybridManualAuth, new(wxrouter.WXManualAuthRouter)) // 登录
wxServer.AddWXRouter(baseinfo.MMRequestTypeLogout, new(wxrouter.WXLogoutRouter)) // 退出登录
wxServer.AddWXRouter(baseinfo.MMRequestTypeAutoAuth, new(wxrouter.WXAutoAuthRouter)) // token登陆
wxServer.AddWXRouter(baseinfo.MMRequestTypeNewSync, new(wxrouter.WXNewSyncRouter)) // 同步消息,联系人
wxServer.AddWXRouter(baseinfo.MMRequestTypeNewInit, new(wxrouter.WXNewInitRouter)) // 首次登录初始化
wxServer.AddWXRouter(baseinfo.MMRequestTypeHeartBeat, new(wxrouter.WXHeartBeatRouter)) // 心跳包
wxServer.AddWXRouter(baseinfo.MMRequestTypeGetProfile, new(wxrouter.WXGetProfileRouter)) // 获取帐号信息
wxServer.AddWXRouter(baseinfo.MMRequestTypeGetCdnDNS, new(wxrouter.WXGetCDNDnsRouter)) // 获取CDNDns信息
wxServer.AddWXRouter(baseinfo.MMRequestTypeInitContact, new(wxrouter.WXInitContactRouter)) // 初始化联系人
wxServer.AddWXRouter(baseinfo.MMRequestTypeBatchGetContactBriefInfo, new(wxrouter.WXBatchGetContactBriefInfoReqRouter)) // 初始化联系人
// wxServer.AddWXRouter(baseinfo.MMRequestTypeGetContact, new(wxrouter.WXGetContactRouter)) // 批量获取联系人信息
wxServer.AddWXRouter(baseinfo.MMRequestTypeReceiveWxHB, new(wxrouter.WXReceiveHBRouter)) // 接收红包
wxServer.AddWXRouter(baseinfo.MMRequestTypeOpenWxHB, new(wxrouter.WXOpenHBRouter)) // 打开红包
// wxServer.AddWXRouter(baseinfo.MMRequestTypeOplog, new(wxrouter.WXOplogRouter)) // Oplog请求
// wxServer.AddWXRouter(baseinfo.MMRequestTypeNewSendMsg, new(wxrouter.WXNewSendMsgRouter)) // 发送文本消息
wxServer.AddWXRouter(baseinfo.MMRequestTypeFavSync, new(wxrouter.WXFavSyncRouter)) // 同步收藏
wxServer.AddWXRouter(baseinfo.MMRequestTypeGetFavInfo, new(wxrouter.WXGetFavInfoRouter)) // 获取收藏信息
wxServer.AddWXRouter(baseinfo.MMRequestTypeBatchGetFavItem, new(wxrouter.WXBatchGetFavItemRouter)) // 获取单条收藏(收藏转发)
wxServer.AddWXRouter(baseinfo.MMRequestTypeMMSnsPost, new(wxrouter.WXSnsPostRouter)) // 发送朋友圈
wxServer.AddWXRouter(baseinfo.MMRequestTypeMMSnsSync, new(wxrouter.WXSnsSyncRouter)) // 同步朋友圈
wxServer.AddWXRouter(baseinfo.MMRequestTypeMMSnsUserPage, new(wxrouter.WXSnsUserPageRouter)) // 朋友圈同步转发
wxServer.AddWXRouter(baseinfo.MMRequestTypeMMSnsTimeLine, new(wxrouter.WXSnsTimeLineRouter)) // 同步朋友圈
wxServer.AddWXRouter(baseinfo.MMRequestTypeMMSnsComment, new(wxrouter.WXSnsCommentRouter)) // 评论/点赞朋友圈
wxServer.AddWXRouter(baseinfo.MMRequestTypeGetContactLabelList, new(wxrouter.WXGetContactLabelListRouter)) // 获取标签列表
wxServer.AddWXRouter(baseinfo.MMRequestTypeAddContactLabel, new(wxrouter.WXAddContactLabelRouter)) // 新增标签列表
wxServer.AddWXRouter(baseinfo.MMRequestTypeGetQrCode, new(wxrouter.WXGetQrcodeRouter)) // 获取二维码
// wxServer.AddWXRouter(baseinfo.MMRequestTypeBindQueryNew, new(wxrouter.WXBindQueryNewRouter)) // 获取钱包信息
wxServer.Start()
WXServer = wxServer
}
func CreateWXConnectByQueryKey(queryKey, proxy string, wxAccount *srv.WXAccount) wxface.IWXConnect {
var proxyInfo *proxynet.WXProxyInfo
if proxy != "" {
proxyInfo = proxynet.ParseWXProxyInfo(proxy)
}
if wxAccount == nil {
wxAccount = srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, proxyInfo, uint32(0), nil)
}
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
return wxConnect
}
// checkExIdReturn 检测实例Id并返回实例
func checkExIdReturn(queryKey string, ClientVersion uint32) wxface.IWXConnect {
// 判断 ClientVersion == 0
if !(int(ClientVersion) > 0) {
ClientVersion = baseinfo.ClientVersion
}
//获取实例管理器
connectMgr := WXServer.GetWXConnectMgr()
//查询的queryKey为空创建一个链接实例
if queryKey == "" {
return nil
}
//查询该链接是否存在
iwxConnect := connectMgr.GetWXConnectByUserInfoUUID(queryKey)
if iwxConnect != nil {
//执行回调方法
return iwxConnect
} else {
//如果链接管理器不存在该链接查询数据库是否存在
dbUserInfo := db.GetUSerInfoByUUID(queryKey)
//数据库存在该链接数据 重新实例化一个链接对象
if dbUserInfo != nil {
if !(int(dbUserInfo.ClientVersion) > 0) {
dbUserInfo.ClientVersion = ClientVersion
}
//创建一个用户信息
wxAccount := srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, nil, dbUserInfo.ClientVersion, dbUserInfo)
//创建一个新链接
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
//设置用户信息
wxAccount.SetUserInfo(dbUserInfo)
return wxConnect
} else {
//创建新一个用户信息
wxAccount := srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, nil, ClientVersion, dbUserInfo)
//创建一个新链接
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
return wxConnect
}
}
}
// 检查实例Id是否存在 不存在创建新的链接
func checkExIdPerform(queryKey string, ClientVersion uint32, businessFunc BusinessFunc) vo.DTO {
// 判断 ClientVersion == 0
if !(int(ClientVersion) > 0) {
ClientVersion = baseinfo.ClientVersion
}
//获取实例管理器
connectMgr := WXServer.GetWXConnectMgr()
//查询的queryKey为空创建一个链接实例
if queryKey == "" {
return businessFunc(nil, true)
}
//查询该链接是否存在
iwxConnect := connectMgr.GetWXConnectByUserInfoUUID(queryKey)
if iwxConnect != nil {
//执行回调方法
return businessFunc(iwxConnect, false)
} else {
//如果链接管理器不存在该链接查询数据库是否存在
dbUserInfo := db.GetUSerInfoByUUID(queryKey)
//数据库存在该链接数据 重新实例化一个链接对象
if dbUserInfo != nil {
if !(int(dbUserInfo.ClientVersion) > 0) {
dbUserInfo.ClientVersion = ClientVersion
}
// 设置代理信息
key := fmt.Sprintf("%s%s", "wechat:Proxy:", queryKey)
exists, _ := db.Exists(key)
newModel := &req.GetLoginQrCodeModel{
Proxy: "",
Check: false,
}
if exists {
db.GETObj(key, &newModel)
}
var proxyInfo *proxynet.WXProxyInfo
if newModel.Proxy != "" {
proxyInfo = proxynet.ParseWXProxyInfo(newModel.Proxy)
} else {
proxyInfo = nil
}
//创建一个用户信息
wxAccount := srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, proxyInfo, dbUserInfo.ClientVersion, dbUserInfo)
//创建一个新链接
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
//设置用户信息
wxAccount.SetUserInfo(dbUserInfo)
return businessFunc(wxConnect, false)
} else {
// 设置代理信息
key := fmt.Sprintf("%s%s", "wechat:Proxy:", queryKey)
exists, _ := db.Exists(key)
newModel := &req.GetLoginQrCodeModel{
Proxy: "",
Check: false,
}
if exists {
db.GETObj(key, &newModel)
}
var proxyInfo *proxynet.WXProxyInfo
if newModel.Proxy != "" {
proxyInfo = proxynet.ParseWXProxyInfo(newModel.Proxy)
} else {
proxyInfo = nil
}
//创建新一个用户信息
wxAccount := srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, proxyInfo, ClientVersion, dbUserInfo)
//创建一个新链接
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
return businessFunc(wxConnect, true)
}
}
}
// 检查实例Id是否存在 链接不存在返回错误不创建新链接
func checkExIdPerformNoCreateConnect(queryKey string, businessFunc BusinessFunc) vo.DTO {
ClientVersion := baseinfo.ClientVersion
//获取实例管理器
connectMgr := WXServer.GetWXConnectMgr()
//查询的queryKey为空创建一个链接实例
if queryKey == "" {
return businessFunc(nil, true)
}
//查询该链接是否存在
iwxConnect := connectMgr.GetWXConnectByUserInfoUUID(queryKey)
if iwxConnect != nil {
//执行回调方法
return businessFunc(iwxConnect, false)
} else {
//如果链接管理器不存在该链接查询数据库是否存在
dbUserInfo := db.GetUSerInfoByUUID(queryKey)
//数据库存在该链接数据 重新实例化一个链接对象
if dbUserInfo != nil {
if !(int(dbUserInfo.ClientVersion) > 0) {
dbUserInfo.ClientVersion = ClientVersion
}
// 设置代理信息
key := fmt.Sprintf("%s%s", "wechat:Proxy:", dbUserInfo.UUID)
exists, _ := db.Exists(key)
newModel := &req.GetLoginQrCodeModel{
Proxy: "",
Check: false,
}
if exists {
db.GETObj(key, &newModel)
}
var proxyInfo *proxynet.WXProxyInfo
if newModel.Proxy != "" {
proxyInfo = proxynet.ParseWXProxyInfo(newModel.Proxy)
} else {
proxyInfo = nil
}
//创建一个用户信息
wxAccount := srv.NewWXAccount(&websrv.TaskInfo{
UUID: queryKey,
}, proxyInfo, dbUserInfo.ClientVersion, dbUserInfo)
//创建一个新链接
wxConnect := wxcore.NewWXConnect(WXServer, wxAccount)
//设置用户信息
wxAccount.SetUserInfo(dbUserInfo)
return businessFunc(wxConnect, false)
} else {
// 诊断逻辑:为什么查不到?
reason := "未知原因"
var uEntity table.UserInfoEntity
err := db.MysqlDB.Where("uuid = ?", queryKey).First(&uEntity).Error
if err != nil {
reason = fmt.Sprintf("数据库UserInfo表未找到该UUID记录: %v", err)
} else {
// UUID 存在,检查 TargetIp
if srvconfig.GlobalSetting.TargetIp != "" && uEntity.TargetIp != srvconfig.GlobalSetting.TargetIp {
reason = fmt.Sprintf("TargetIp不匹配(DB:%s, Config:%s)", uEntity.TargetIp, srvconfig.GlobalSetting.TargetIp)
} else {
// 检查 DeviceInfo
// 判断是62还是A16
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", uEntity.WxId)
exists, _ := db.Exists(key)
if !exists {
var dEntity table.DeviceInfoEntity
if err := db.MysqlDB.Where("wxId = ?", uEntity.WxId).First(&dEntity).Error; err != nil {
reason = fmt.Sprintf("缺少设备信息(DeviceInfo表无记录且Redis无A16缓存), WxId: %s, Err: %v", uEntity.WxId, err)
} else {
reason = "UserInfo和DeviceInfo都存在,但GetUSerInfoByUUID返回nil,可能是其他数据完整性问题"
}
} else {
reason = "UserInfo存在且有A16缓存,但GetUSerInfoByUUID返回nil,可能是反序列化或其他问题"
}
}
}
return vo.DTO{
Code: vo.FAIL_UUID,
Data: nil,
Text: fmt.Sprintf("%s 该链接不存在!调试信息: %s", queryKey, reason),
}
}
}
}
// 获取缓存中已存在的 wx 链接
func getExistWxConnect(queryKey string, businessFunc BusinessFunc) vo.DTO {
//获取实例管理器
connectMgr := WXServer.GetWXConnectMgr()
//查询的queryKey为空创建一个链接实例
if queryKey == "" {
return businessFunc(nil, true)
}
//查询该链接是否存在
iwxConnect := connectMgr.GetWXConnectByUserInfoUUID(queryKey)
if iwxConnect != nil {
//执行回调方法
userInfo := iwxConnect.GetWXAccount().GetUserInfo()
iwxConnect.GetWXAccount().SetUserInfo(userInfo)
return businessFunc(iwxConnect, false)
}
return businessFunc(nil, true)
}
func DeepFields(ifaceType reflect.Type) []reflect.StructField {
var fields []reflect.StructField
for i := 0; i < ifaceType.NumField(); i++ {
v := ifaceType.Field(i)
if v.Anonymous && v.Type.Kind() == reflect.Struct {
fields = append(fields, DeepFields(v.Type)...)
} else {
fields = append(fields, v)
}
}
return fields
}
func StructCopy(DstStructPtr interface{}, SrcStructPtr interface{}) {
srcv := reflect.ValueOf(SrcStructPtr)
dstv := reflect.ValueOf(DstStructPtr)
srct := reflect.TypeOf(SrcStructPtr)
dstt := reflect.TypeOf(DstStructPtr)
if srct.Kind() != reflect.Ptr || dstt.Kind() != reflect.Ptr ||
srct.Elem().Kind() == reflect.Ptr || dstt.Elem().Kind() == reflect.Ptr {
panic("Fatal error:type of parameters must be Ptr of value")
}
if srcv.IsNil() || dstv.IsNil() {
panic("Fatal error:value of parameters should not be nil")
}
srcV := srcv.Elem()
dstV := dstv.Elem()
srcfields := DeepFields(reflect.ValueOf(SrcStructPtr).Elem().Type())
for _, v := range srcfields {
if v.Anonymous {
continue
}
dst := dstV.FieldByName(v.Name)
src := srcV.FieldByName(v.Name)
if !dst.IsValid() {
continue
}
if src.Type() == dst.Type() && dst.CanSet() {
dst.Set(src)
continue
}
if src.Kind() == reflect.Ptr && !src.IsNil() && src.Type().Elem() == dst.Type() {
dst.Set(src.Elem())
continue
}
if dst.Kind() == reflect.Ptr && dst.Type().Elem() == src.Type() {
dst.Set(reflect.New(src.Type()))
dst.Elem().Set(src)
continue
}
}
return
}
/*
*
获取DeviceToken
*/
func checkDeviceToken(tmpUserInfo *baseinfo.UserInfo) {
//如果是android
if strings.HasPrefix(tmpUserInfo.LoginDataInfo.LoginData, "A") {
key := fmt.Sprintf("%s%s", "wechat:deviceTokenA16:", tmpUserInfo.LoginDataInfo.UserName)
exists, _ := db.Exists(key)
if exists {
//A16存redis
trustRes := &wechat.TrustResp{}
error := db.GETObj(key, &trustRes)
if error != nil {
log.Error("redis deviceToken is error=" + error.Error())
}
tmpUserInfo.DeviceInfoA16.DeviceToken = trustRes
} else {
tmpUserInfo.DeviceInfoA16.DeviceId = []byte(tmpUserInfo.LoginDataInfo.LoginData[:15])
tmpUserInfo.DeviceInfoA16.DeviceIdStr = tmpUserInfo.LoginDataInfo.LoginData
deviceTokenRsp, err := clientsdk.SendAndroIdDeviceTokenRequest(tmpUserInfo)
if err != nil {
log.Error("android 请求 deviceTokenRequest error!")
}
//保存5天
db.SETExpirationObj(key, &deviceTokenRsp, 60*60*24*5)
tmpUserInfo.DeviceInfoA16.DeviceToken = deviceTokenRsp
}
} else if strings.HasPrefix(tmpUserInfo.LoginDataInfo.LoginData, "62") {
key := fmt.Sprintf("%s%s", "wechat:deviceTokenIos:", tmpUserInfo.LoginDataInfo.UserName)
exists, _ := db.Exists(key)
if exists {
//ios存redis
trustRes := &wechat.TrustResp{}
error := db.GETObj(key, &trustRes)
if error != nil {
log.Error("ios redis deviceTokenIos is error=" + error.Error())
}
tmpUserInfo.DeviceInfo.DeviceToken = trustRes
} else {
deviceTokenRsp, err := clientsdk.SendIosDeviceTokenRequest(tmpUserInfo)
if err != nil {
log.Error("ios 请求 deviceTokenRequest error!")
}
//保存5天
db.SETExpirationObj(key, &deviceTokenRsp, 60*60*24*5)
tmpUserInfo.DeviceInfo.DeviceToken = deviceTokenRsp
fmt.Println(deviceTokenRsp, 333)
}
}
}