Files
wechat_ipad_pro/api/service/baseService.go
2026-02-17 13:06:23 +08:00

430 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}
}