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) } } }