Files
wechat_ipad_pro/db/mysql_db.go

1764 lines
48 KiB
Go
Raw Normal View History

2026-02-17 13:06:23 +08:00
package db
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math/rand"
"reflect"
"strings"
"sync"
"time"
"xiawan/wx/api/req"
"xiawan/wx/clientsdk/baseinfo"
"xiawan/wx/db/table"
"xiawan/wx/protobuf/wechat"
pb "xiawan/wx/protobuf/wechat"
"xiawan/wx/srv"
"xiawan/wx/srv/srvconfig"
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/lunny/log"
)
// MysqlDB MySQL数据库实例
var (
MysqlDB *gorm.DB
dbMutex sync.Mutex // 用于保护连接重置的互斥锁
contactMutex sync.Mutex // 联系人存储锁
mutexMap = make(map[string]*sync.Mutex) // 联系人存储锁
maxRetry = 10 // 最大重试次数
retryDelay = time.Second * 2 // 每次重试的间隔时间
)
// 联系人存储锁uuid 区分
func getContactMutex(uuid string) *sync.Mutex {
contactMutex.Lock()
defer contactMutex.Unlock()
m, exists := mutexMap[uuid]
if !exists {
m = &sync.Mutex{}
mutexMap[uuid] = m
}
return m
}
// InitDB 初始化数据库
func InitDB() {
dbMutex.Lock() // 加锁,防止并发重连
defer dbMutex.Unlock()
var mysqlUrl = srvconfig.GlobalSetting.MysqlConnectStr
var err error
for i := 0; i < maxRetry; i++ {
MysqlDB, err = gorm.Open("mysql", mysqlUrl)
if err == nil {
break
}
fmt.Printf("Failed to connect MySQL, retrying... (%d/%d)\n", i+1, maxRetry)
time.Sleep(retryDelay)
}
if err != nil {
log.Errorf("Failed to connect MySQL after %d attempts: %v", maxRetry, err)
return
}
MysqlDB.DB().SetConnMaxLifetime(time.Minute * 5)
//设置连接池中的最大闲置连接数
MysqlDB.DB().SetMaxIdleConns(10)
//设置数据库的最大连接数量
MysqlDB.DB().SetMaxOpenConns(120)
fmt.Println("connect MySQL success")
MysqlDB.SingularTable(true)
MysqlDB.AutoMigrate(&table.UserInfoEntity{}) //用户信息 自动建表
MysqlDB.AutoMigrate(&table.DeviceInfoEntity{}) //设备信息 自动建表
MysqlDB.AutoMigrate(&table.UserLoginLog{}) //登录日志 自动建表
MysqlDB.AutoMigrate(&table.UserBusinessLog{}) //用户 自动建表
MysqlDB.AutoMigrate(&table.LicenseKey{}) //自动建表
MysqlDB.AutoMigrate(&table.UserMessageLog{}) //自动建表
MysqlDB.AutoMigrate(&table.Command{}) //自动建表
MysqlDB.AutoMigrate(&table.ModContactDB{}) //自动建表
MysqlDB.AutoMigrate(&table.AddMsgDB{}) //自动建表
MysqlDB.AutoMigrate(&table.ProxyMapping{}) //代理映射表 自动建表
MysqlDB.AutoMigrate(&table.MessageCallbackConfig{}) //消息回调配置表 自动建表
fmt.Println("auto create MySQL tables success")
MysqlDB.LogMode(false)
// 注意:回调配置的初始化移到了 InitAnewLogin 完成后
// 这样可以确保所有账号连接都已建立
2026-02-17 13:06:23 +08:00
}
// InitMessageCallbacks 初始化所有消息回调配置
func InitMessageCallbacks() {
// 账号已经初始化完成,稍微等待一下确保连接稳定
time.Sleep(3 * time.Second)
log.Infof("开始初始化消息回调配置...")
2026-02-17 13:06:23 +08:00
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
log.Errorf("Failed to initialize message callbacks: %v", err)
return
}
// 获取所有启用的回调配置
var configs []table.MessageCallbackConfig
result := MysqlDB.Where("enabled = ?", true).Find(&configs)
if result.Error != nil {
log.Errorf("Failed to query enabled callbacks: %v", result.Error)
return
}
if len(configs) == 0 {
log.Infof("未找到启用的消息回调配置")
return
}
log.Infof("找到 %d 个启用的回调配置,开始重新激活...", len(configs))
// 重新保存每个配置,相当于重新设置一遍,激活回调
successCount := 0
2026-02-17 13:06:23 +08:00
for _, config := range configs {
// 确保Key字段正确
2026-02-17 13:06:23 +08:00
if config.Key == "" {
config.Key = config.UUID
}
// 重新保存配置(相当于执行 SetCallback 操作)
err := SaveMessageCallbackConfig(&config)
if err != nil {
log.Errorf("✗ 重新激活回调失败 [UUID: %s]: %v", config.UUID, err)
} else {
log.Infof("✓ 重新激活回调成功 [UUID: %s] -> %s", config.UUID, config.CallbackURL)
successCount++
2026-02-17 13:06:23 +08:00
}
}
log.Infof("========================================")
log.Infof("回调配置激活完成: 成功 %d/%d", successCount, len(configs))
log.Infof("========================================")
2026-02-17 13:06:23 +08:00
}
// checkAndReconnect 检查并在需要时重新连接
func checkAndReconnect() error {
if MysqlDB == nil {
InitDB() // 初始化连接
}
// Ping数据库验证连接是否有效
if err := MysqlDB.DB().Ping(); err != nil {
log.Error("Database connection lost, reconnecting...")
InitDB() // 重新连接
if MysqlDB == nil {
return errors.New("failed to reconnect to database")
}
}
return nil
}
// Command 更新命令
func UpdateCommand(uuid string, key string, intValue int, strValue string) (bool, error) {
fmt.Println("UpdateCommand uuid:", uuid, "key:", key, "intValue:", intValue, "strValue:", strValue)
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return false, err
}
commandInfo := &table.Command{
UUID: uuid,
}
if err := MysqlDB.Model(commandInfo).Where("uuid = ?", uuid).First(&commandInfo).Error; err != nil {
if err == gorm.ErrRecordNotFound {
commandInfo.UUID = uuid
commandInfo.A301Str = ""
if err := MysqlDB.Create(&commandInfo).Error; err != nil {
// 创建失败
return false, err
}
// 创建成功,继续执行
}
// 错误
return false, err
}
// 记录找到
if key == "A101" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a101": intValue,
})
return true, nil
}
if key == "A102" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a102": intValue,
})
return true, nil
}
if key == "A103" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a103": intValue,
})
return true, nil
}
if key == "A104" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a104": intValue,
})
return true, nil
}
if key == "A104Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a104_str": strValue,
})
return true, nil
}
if key == "A105" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a105": intValue,
})
return true, nil
}
if key == "A106" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a106": intValue,
})
return true, nil
}
if key == "A107" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a107": intValue,
})
return true, nil
}
if key == "A109" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a109": intValue,
})
return true, nil
}
if key == "A111" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a111": intValue,
})
return true, nil
}
if key == "A116" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a116": intValue,
})
return true, nil
}
if key == "A116Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a116_str": strValue,
})
return true, nil
}
if key == "A118" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a118": intValue,
})
return true, nil
}
if key == "A118Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a118_str": strValue,
})
return true, nil
}
if key == "A301" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a301": intValue,
})
return true, nil
}
if key == "A301Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a301_str": strValue,
})
return true, nil
}
if key == "A401" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a401": intValue,
})
return true, nil
}
if key == "A402" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a402": intValue,
})
return true, nil
}
if key == "A403" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a403": intValue,
})
return true, nil
}
if key == "A601" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a601": intValue,
})
return true, nil
}
if key == "A801" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a801": intValue,
})
return true, nil
}
if key == "B001" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b001": intValue,
})
return true, nil
}
if key == "B001Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b001_str": strValue,
})
return true, nil
}
if key == "A811" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"a811": intValue,
})
return true, nil
}
if key == "B002" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b002": intValue,
})
return true, nil
}
if key == "B002Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b002_str": strValue,
})
return true, nil
}
if key == "B003" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b003": intValue,
})
return true, nil
}
if key == "B003Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b003_str": strValue,
})
return true, nil
}
if key == "B004" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b004": intValue,
})
return true, nil
}
if key == "B004Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b004_str": strValue,
})
return true, nil
}
if key == "B005" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b005": intValue,
})
return true, nil
}
if key == "B005Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b005_str": strValue,
})
return true, nil
}
if key == "B006" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b006": intValue,
})
return true, nil
}
if key == "B006Str" {
MysqlDB.Model(&table.Command{}).Where("uuid = ? ", uuid).Updates(map[string]interface{}{
"b006_str": strValue,
})
return true, nil
}
return true, nil
}
// Command 根据 uuid 查询命令
func QueryCommand(uuid string) (table.Command, error) {
commandInfo := &table.Command{
UUID: uuid,
}
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return *commandInfo, err
}
if err := MysqlDB.Model(commandInfo).Where("uuid = ?", uuid).First(&commandInfo).Error; err != nil {
if err == gorm.ErrRecordNotFound {
commandInfo.UUID = uuid
commandInfo.A118Str = "词1,词2,词3"
commandInfo.A116Str = ""
commandInfo.A301Str = ""
commandInfo.B001Str = ""
commandInfo.B002Str = ""
commandInfo.B003Str = ""
if err := MysqlDB.Create(&commandInfo).Error; err != nil {
// 创建失败
return *commandInfo, err
}
// 创建成功,继续执行
}
// 错误
return *commandInfo, err
}
// 记录找到
return *commandInfo, nil
}
// 提交登录日志
func SetLoginLog(loginType string, acc *srv.WXAccount, errMsg string, state int32) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
var userName string
userInfo := acc.GetUserInfo()
if len(userInfo.LoginDataInfo.UserName) > 0 {
userName = userInfo.LoginDataInfo.UserName
} else {
userName = userInfo.WxId
}
// 更新登录时间信息
UpdateLoginTimeInfo(userInfo)
userLog := &table.UserLoginLog{
UUId: userInfo.UUID,
UserName: userName,
NickName: userInfo.NickName,
LoginType: loginType,
RetCode: state,
ErrMsg: errMsg,
}
userLog.TargetIp = srvconfig.GlobalSetting.TargetIp
MysqlDB.Save(userLog)
}
// UpdateLoginTimeInfo 更新用户的登录时间相关信息
func UpdateLoginTimeInfo(userInfo *baseinfo.UserInfo) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
_ = MysqlDB.Model(&table.UserInfoEntity{}).
Where("wxId = ? ", userInfo.WxId).
Updates(map[string]interface{}{
"device_create_time": userInfo.DeviceCreateTime,
"last_login_time": userInfo.LastLoginTime,
"last_auth_time": userInfo.LastAuthTime,
})
}
// 获取登录日志
func GetLoginJournal(userName string) []table.UserLoginLog {
logs := make([]table.UserLoginLog, 0)
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return logs
}
MysqlDB.Where("user_name=?", userName).Find(&logs)
return logs
}
// 获取登录错误信息
func GetUSerLoginErrMsg(userName string) string {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return ""
}
userInfoEntity := &table.UserInfoEntity{
WxId: userName,
}
MysqlDB.Model(userInfoEntity).First(userInfoEntity)
return userInfoEntity.ErrMsg
}
// 保存登录状态
func UpdateLoginStatus(userName string, state int32, errMsg string) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
data := make(map[string]interface{})
data["State"] = state //零值字段
data["ErrMsg"] = errMsg
v := GetUserInfoEntityByWxId(userName) // *table.UserInfoEntity
if v != nil && v.State != state {
MysqlDB.Model(&table.UserInfoEntity{
WxId: userName,
}).Update(data)
_ = PublishSyncMsgLoginState(userName, uint32(state))
}
}
// 保存初始化联系人结果
func UpdateInitContactStatus(uuid string, state int32) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
_ = MysqlDB.Model(&table.UserInfoEntity{}).
Where("uuid = ? ", uuid).
Updates(map[string]interface{}{
"initcontact": state,
})
}
// 查询初始化是否完成
func QueryInitContactStatus(uuid string) int32 {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return 1
}
var userInfoEntity table.UserInfoEntity
// 获取消息
if err := MysqlDB.Where("uuid = ?", uuid).First(&userInfoEntity).Error; err != nil {
return 1 // 发生其他错误
}
if userInfoEntity.InitContact == 0 {
fmt.Println("["+uuid+"]", "开始初始化联系人")
} else {
fmt.Println("["+uuid+"]", "无需初始化联系人")
}
return userInfoEntity.InitContact
}
// 更新同步消息Key
func UpdateSyncMsgKey() {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
}
// 更新用户信息
func UpdateUserInfo(userInfo *baseinfo.UserInfo) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
if userInfo.WxId == "" {
userInfo.WxId = userInfo.GetUserName()
}
var userInfoEntity table.UserInfoEntity
SimpleCopyProperties(&userInfoEntity, userInfo)
userInfoEntity.AutoAuthKey = base64.StdEncoding.EncodeToString(userInfo.AutoAuthKey)
userInfoEntity.SyncKey = base64.StdEncoding.EncodeToString(userInfo.SyncKey)
userInfoEntity.FavSyncKey = base64.StdEncoding.EncodeToString(userInfo.FavSyncKey)
userInfoEntity.TargetIp = srvconfig.GlobalSetting.TargetIp
userInfoEntity.UserName = userInfo.LoginDataInfo.UserName
userInfoEntity.Password = userInfo.LoginDataInfo.PassWord
userInfoEntity.State = int32(userInfo.GetLoginState())
// userInfoEntity.Proxy = userInfo.ProxyInfo.ProxyUrl
userInfoEntity.ClientVersion = userInfo.ClientVersion
if !(int(userInfoEntity.ClientVersion) > 0) {
userInfoEntity.ClientVersion = baseinfo.ClientVersion
}
// log.Infof("更新用户信息:%v", userInfoEntity.ClientVersion)
MysqlDB.Model(new(table.UserInfoEntity)).Update(&userInfoEntity)
}
// 更新用户信息,手机号和微信号
func UpdateUserInfoByPhone(modUserInfo *wechat.ModUserInfo) {
// 用户信息为空
if modUserInfo == nil {
return
}
// 微信id 为空
if modUserInfo.GetUserName() == nil {
return
}
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
wxId := modUserInfo.GetUserName().GetStr()
bindmobile := ""
if modUserInfo.GetBindMobile() != nil {
bindmobile = modUserInfo.GetBindMobile().GetStr()
}
alias := modUserInfo.GetAlias()
MysqlDB.Model(new(table.UserInfoEntity)).Where("wxId = ?", wxId).Updates(map[string]interface{}{
"bindmobile": bindmobile,
"alias": alias,
})
// wx_id
MysqlDB.Model(new(table.LicenseKey)).Where("wx_id = ?", wxId).Updates(map[string]interface{}{
"bindmobile": bindmobile,
"alias": alias,
})
}
// SaveUserInfo 保存用户信息
func SaveUserInfo(userInfo *baseinfo.UserInfo) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
if userInfo.WxId == "" {
userInfo.WxId = userInfo.GetUserName()
}
var userInfoEntity table.UserInfoEntity
SimpleCopyProperties(&userInfoEntity, userInfo)
userInfoEntity.AutoAuthKey = base64.StdEncoding.EncodeToString(userInfo.AutoAuthKey)
userInfoEntity.SyncKey = base64.StdEncoding.EncodeToString(userInfo.SyncKey)
userInfoEntity.FavSyncKey = base64.StdEncoding.EncodeToString(userInfo.FavSyncKey)
userInfoEntity.TargetIp = srvconfig.GlobalSetting.TargetIp
userInfoEntity.UserName = userInfo.LoginDataInfo.UserName
userInfoEntity.Password = userInfo.LoginDataInfo.PassWord
MysqlDB.Save(&userInfoEntity)
//判断是62还是A16
if strings.HasPrefix(userInfo.LoginDataInfo.LoginData, "A") {
//A16存redis
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", userInfo.WxId)
error := SETExpirationObj(key, &userInfo.DeviceInfoA16, 60*60*24*15)
if error != nil {
log.Error("保存redis is error=" + error.Error())
}
} else {
//62存DB
var deviceInfoEntity table.DeviceInfoEntity
deviceInfoEntity.WxId = userInfo.WxId
SimpleCopyProperties(&deviceInfoEntity, userInfo.DeviceInfo)
deviceInfoEntity.SoftTypeXML = base64.StdEncoding.EncodeToString([]byte(deviceInfoEntity.SoftTypeXML))
deviceInfoEntity.ClientCheckDataXML = base64.StdEncoding.EncodeToString([]byte(deviceInfoEntity.ClientCheckDataXML))
MysqlDB.Save(&deviceInfoEntity)
}
}
// 获取所有登录用户
func QueryListUserInfo() []table.UserInfoEntity {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return make([]table.UserInfoEntity, 0)
}
// 判断 srvconfig.GlobalSetting.TargetIp 相同
var userInfoEntity = make([]table.UserInfoEntity, 0)
MysqlDB.Where("state = ? OR (state = 2 AND wxId IS NOT NULL AND wxId != '' AND autoauthkey IS NOT NULL AND autoauthkey != '')", 1).Find(&userInfoEntity)
for _, v := range userInfoEntity {
if v.State == 2 {
v.State = 1
}
}
// 过滤
var newUserInfoEntity = make([]table.UserInfoEntity, 0)
for _, v := range userInfoEntity {
if srvconfig.GlobalSetting.TargetIp == "" || v.TargetIp == srvconfig.GlobalSetting.TargetIp {
newUserInfoEntity = append(newUserInfoEntity, v)
}
}
return newUserInfoEntity
}
// 获取所有用户
func QueryUserList() []table.UserInfoEntity {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return make([]table.UserInfoEntity, 0)
}
var userInfoEntity = make([]table.UserInfoEntity, 0)
MysqlDB.Where("1=1").Find(&userInfoEntity)
return userInfoEntity
}
// GetUserInfoEntity 获取登录信息
func GetUserInfoEntity(uuid string) *table.UserInfoEntity {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
var userInfoEntity table.UserInfoEntity
if err := MysqlDB.Where("uuid=?", uuid).First(&userInfoEntity).Error; err != nil {
return nil
}
return &userInfoEntity
}
// GetUserInfoEntity 获取登录信息
func GetUserInfoEntityByWxId(wxId string) *table.UserInfoEntity {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
var userInfoEntity table.UserInfoEntity
if err := MysqlDB.Where("wxId=?", wxId).First(&userInfoEntity).Error; err != nil {
return nil
}
return &userInfoEntity
}
// GetUserInfo 从数据库获取UserInfo
func GetUserInfo(uuid string) *baseinfo.UserInfo {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
userInfo := &baseinfo.UserInfo{}
var userInfoEntity table.UserInfoEntity
userInfoEntity.UUID = uuid
if err := MysqlDB.First(&userInfoEntity).Error; err != nil {
return nil
}
var deviceInfoEntity table.DeviceInfoEntity
deviceInfoEntity.WxId = userInfoEntity.WxId
if err := MysqlDB.First(&deviceInfoEntity).Error; err != nil {
return nil
}
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", userInfoEntity.WxId)
//判断是62还是A16
exists, _ := Exists(key)
if exists {
//A16存redis
deviceInfoA16 := &baseinfo.AndroidDeviceInfo{}
error := GETObj(key, &deviceInfoA16)
if error != nil {
log.Error("保存redis is error=" + error.Error())
}
userInfo.DeviceInfoA16 = deviceInfoA16
} else {
deviceInfo := &baseinfo.DeviceInfo{}
SimpleCopyProperties(deviceInfo, deviceInfoEntity)
decodeSoftTypeXML, _ := base64.StdEncoding.DecodeString(deviceInfo.SoftTypeXML)
deviceInfo.SoftTypeXML = string(decodeSoftTypeXML)
decodeClientCheckDataXML, _ := base64.StdEncoding.DecodeString(deviceInfo.ClientCheckDataXML)
deviceInfo.ClientCheckDataXML = string(decodeClientCheckDataXML)
userInfo.DeviceInfo = deviceInfo
}
SimpleCopyProperties(userInfo, userInfoEntity)
userInfo.AutoAuthKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.AutoAuthKey)
userInfo.SyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.SyncKey)
userInfo.FavSyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.FavSyncKey)
return userInfo
}
func GetUSerInfoByUUID(uuid string) *baseinfo.UserInfo {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
userInfo := &baseinfo.UserInfo{}
var userInfoEntity table.UserInfoEntity
userInfoEntity.UUID = uuid
if srvconfig.GlobalSetting.TargetIp != "" {
userInfoEntity.TargetIp = srvconfig.GlobalSetting.TargetIp
}
if err := MysqlDB.Model(&table.UserInfoEntity{}).Where(&userInfoEntity).First(&userInfoEntity).Error; err != nil {
return nil
}
//判断是62还是A16
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", userInfoEntity.WxId)
exists, _ := Exists(key)
if exists {
//A16存redis
deviceInfoA16 := &baseinfo.AndroidDeviceInfo{}
error := GETObj(key, &deviceInfoA16)
if error != nil {
log.Error("保存redis is error=" + error.Error())
}
userInfo.DeviceInfoA16 = deviceInfoA16
} else {
var deviceInfoEntity table.DeviceInfoEntity
deviceInfoEntity.WxId = userInfoEntity.WxId
if err := MysqlDB.First(&deviceInfoEntity).Error; err != nil {
return nil
}
deviceInfo := &baseinfo.DeviceInfo{}
SimpleCopyProperties(deviceInfo, deviceInfoEntity)
decodeSoftTypeXML, _ := base64.StdEncoding.DecodeString(deviceInfo.SoftTypeXML)
deviceInfo.SoftTypeXML = string(decodeSoftTypeXML)
decodeClientCheckDataXML, _ := base64.StdEncoding.DecodeString(deviceInfo.ClientCheckDataXML)
deviceInfo.ClientCheckDataXML = string(decodeClientCheckDataXML)
userInfo.DeviceInfo = deviceInfo
}
SimpleCopyProperties(userInfo, userInfoEntity)
userInfo.AutoAuthKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.AutoAuthKey)
userInfo.SyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.SyncKey)
userInfo.FavSyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.FavSyncKey)
userInfo.SetLoginState(uint32(userInfoEntity.State))
userInfo.ClientVersion = userInfoEntity.ClientVersion
if !(int(userInfo.ClientVersion) > 0) {
userInfo.ClientVersion = baseinfo.ClientVersion
userInfoEntity.ClientVersion = baseinfo.ClientVersion
}
return userInfo
}
// GetUserInfoByWXID 从数据库获取UserInfo
func GetUserInfoByWXID(wxID string) *baseinfo.UserInfo {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
userInfo := &baseinfo.UserInfo{}
var userInfoEntity table.UserInfoEntity
userInfoEntity.WxId = wxID
if err := MysqlDB.First(&userInfoEntity).Error; err != nil {
return nil
}
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", wxID)
exists, _ := Exists(key)
if exists {
//A16存redis
key := fmt.Sprintf("%s%s", "wechat:a16DeviceInfo:", wxID)
deviceInfoA16 := &baseinfo.AndroidDeviceInfo{}
error := GETObj(key, &deviceInfoA16)
if error != nil {
log.Error("获取redis is error=" + error.Error())
}
userInfo.DeviceInfoA16 = deviceInfoA16
} else {
var deviceInfoEntity table.DeviceInfoEntity
deviceInfoEntity.WxId = userInfoEntity.WxId
if err := MysqlDB.First(&deviceInfoEntity).Error; err != nil {
return nil
}
deviceInfo := &baseinfo.DeviceInfo{}
SimpleCopyProperties(deviceInfo, deviceInfoEntity)
decodeSoftTypeXML, _ := base64.StdEncoding.DecodeString(deviceInfo.SoftTypeXML)
deviceInfo.SoftTypeXML = string(decodeSoftTypeXML)
decodeClientCheckDataXML, _ := base64.StdEncoding.DecodeString(deviceInfo.ClientCheckDataXML)
deviceInfo.ClientCheckDataXML = string(decodeClientCheckDataXML)
userInfo.DeviceInfo = deviceInfo
}
SimpleCopyProperties(userInfo, userInfoEntity)
userInfo.AutoAuthKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.AutoAuthKey)
userInfo.SyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.SyncKey)
userInfo.FavSyncKey, _ = base64.StdEncoding.DecodeString(userInfoEntity.FavSyncKey)
return userInfo
}
// GetDeviceInfo 根据URLkey查询DeviceInfo 查不到返回nil
func GetDeviceInfo(queryKey string) *baseinfo.DeviceInfo {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
var userInfoEntity table.UserInfoEntity
MysqlDB.Where("querykey = ?", "queryKey").First(&userInfoEntity)
var count int
MysqlDB.Model(&table.UserInfoEntity{}).Where("querykey = ?", "queryKey").Count(&count)
if count == 0 {
return nil
}
deviceInfo := &baseinfo.DeviceInfo{}
_ = SimpleCopyProperties(deviceInfo, userInfoEntity)
return deviceInfo
}
// SimpleCopyProperties 拷贝属性
func SimpleCopyProperties(dst, src interface{}) (err error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
// 防止意外panic
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)
// dst必须结构体指针类型
if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
return errors.New("dst type should be a struct pointer")
}
// src必须为结构体或者结构体指针
if srcType.Kind() == reflect.Ptr {
srcType, srcValue = srcType.Elem(), srcValue.Elem()
}
if srcType.Kind() != reflect.Struct {
return errors.New("src type should be a struct or a struct pointer")
}
// 取具体内容
dstType, dstValue = dstType.Elem(), dstValue.Elem()
// 属性个数
propertyNums := dstType.NumField()
for i := 0; i < propertyNums; i++ {
// 属性
property := dstType.Field(i)
// 待填充属性值
propertyValue := srcValue.FieldByName(property.Name)
// 无效说明src没有这个属性 || 属性同名但类型不同
if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
// 确保 propertyValue 是有效的并且类型支持 Name() 方法
if propertyValue.IsValid() && propertyValue.Kind() == reflect.Struct {
if propertyValue.Type().Name() == "LocalTime" {
// 数据库字段为 LocalTime 类型结构体
if !dstValue.Field(i).CanSet() {
continue
}
dstValue.Field(i).Set(propertyValue.Field(0))
}
}
continue
}
if dstValue.Field(i).CanSet() {
dstValue.Field(i).Set(propertyValue)
}
}
return nil
}
// IsLicenseBind 查询许可证是否绑定
func IsLicenseBind(license string) (*table.LicenseKey, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var licenseKey table.LicenseKey
result := MysqlDB.Where("license = ? AND status = 1", license).First(&licenseKey)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, result.Error
}
return &licenseKey, nil
}
// UpdateLicenseBindStatus 更新许可证绑定状态
func UpdateLicenseBindStatus(deviceToken, license string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
result := MysqlDB.Model(&table.LicenseKey{}).Where("license = ? ", license).Updates(map[string]interface{}{
"device_token": deviceToken,
})
if result.Error != nil {
return result.Error
}
return nil
}
// 如果没有过期时间,则生成过期时间
func UpdateLicenseExpiryDate(license string) (*table.LicenseKey, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var licenseKey table.LicenseKey
resultObj := MysqlDB.Where("license = ?", license).First(&licenseKey)
if resultObj.Error != nil {
if resultObj.Error == gorm.ErrRecordNotFound {
return &licenseKey, nil
}
return &licenseKey, resultObj.Error
}
if licenseKey.ExpiryDate == "" {
// 生成过期时间
licenseKey.ExpiryDate = generateExpiryDate3(licenseKey.Type)
// 生成开始时间
licenseKey.StartDate = time.Now().Format("2006-01-02 15:04:05")
result := MysqlDB.Model(&table.LicenseKey{}).Where("license = ?", license).Updates(map[string]interface{}{
"expiry_date": licenseKey.ExpiryDate,
"start_date": licenseKey.StartDate,
})
if result.Error != nil {
return &licenseKey, result.Error
}
}
return &licenseKey, nil
}
// 绑定微信
func UpdateLicenseBindWxid(wxid, nickName, license string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
MysqlDB.Model(&table.LicenseKey{}).Where("license = ? AND status = 0", license).Updates(map[string]interface{}{
"status": 1,
"wx_id": wxid,
"nick_name": nickName,
})
return nil
}
// HasLicense 查询是否存在许可证
func HasLicense(license string) (*table.LicenseKey, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var licenseKey table.LicenseKey
result := MysqlDB.Where("license = ?", license).First(&licenseKey)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, result.Error
}
return &licenseKey, nil
}
// CheckExpiry 检查日期是否过期
func CheckExpiry(expiryDateString string, itype int) bool {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return false
}
//1日 7 周 30月 90季 180 半年 365年 30000
types := []int{1, 7, 30, 90, 180, 365, 30000}
// 判断 itype 在 types 中
isType := false
for _, t := range types {
if t == itype {
isType = true
break
}
}
// type 类型存在 同时 不存在 过期时间
if isType && expiryDateString == "" {
return false
}
// 将字符串解析为时间对象
expiryDate, err := time.Parse("2006-01-02", expiryDateString)
if err != nil {
fmt.Println("Error parsing expiry date:", err)
return false
}
// 获取当前时间
currentTime := time.Now()
// 比较过期日期和当前日期
if expiryDate.Before(currentTime) {
return true
}
return false
}
func generateExpiryDate(expiryDays int) string {
// 获取当前日期
currentDate := time.Now()
// 计算有效期后的日期
expiryDate := currentDate.AddDate(0, 0, expiryDays)
// 格式化日期字符串
expiryDateStr := expiryDate.Format("2006-01-02")
return expiryDateStr
}
// generateExpiryDate 根据特定时间段生成有效期日期
func generateExpiryDate3(duration int) string {
// 获取当前日期
currentDate := time.Now()
var expiryDate time.Time
// 根据输入的周期计算有效期后的日期
switch duration {
case 1:
expiryDate = currentDate.AddDate(0, 0, 1)
case 7:
expiryDate = currentDate.AddDate(0, 0, 7)
case 30:
expiryDate = currentDate.AddDate(0, 1, 0)
case 90:
expiryDate = currentDate.AddDate(0, 3, 0)
case 180:
expiryDate = currentDate.AddDate(0, 6, 0)
case 365:
expiryDate = currentDate.AddDate(1, 0, 0)
case 30000:
expiryDate = currentDate.AddDate(100, 0, 0)
default:
// 默认过期时间为当前日期的前10天
expiryDate = currentDate.AddDate(0, 0, -10)
}
// 格式化日期字符串
expiryDateStr := expiryDate.Format("2006-01-02")
return expiryDateStr
}
//func generateActivationCode(length int, count int) []string {
// activationCodes := make([]string, count)
//
// for i := 0; i < count; i++ {
// code := generateSingleActivationCode()
// activationCodes[i] = code
// }
//
// return activationCodes
//}
func generateSingleActivationCode() string {
chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var sb strings.Builder
for i := 0; i < 20; i++ {
sb.WriteByte(chars[rand.Intn(len(chars))])
}
return sb.String()
}
// generateRandomString 生成指定长度的随机字符串
func generateRandomString(length int) string {
// 设置种子(仅需设置一次)
rand.Seed(time.Now().UnixNano())
// 定义字符集
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var builder strings.Builder
for i := 0; i < length; i++ {
builder.WriteByte(charset[rand.Intn(len(charset))]) // 从字符集中随机选择一个字符
}
return builder.String()
}
func CreateLicense(count, expiryDays int) string {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return ""
}
ExpiryDate := generateExpiryDate(expiryDays)
licenses := make([]string, 0, count)
for {
if count == 0 {
break
}
if len(licenses) >= count {
break
}
//key := generateSingleActivationCode()
// key := guuid.New().String()
key := "HB" + generateRandomString(10)
licenseKey := table.LicenseKey{
Status: 0,
License: key,
ExpiryDate: ExpiryDate,
}
result := MysqlDB.Create(&licenseKey)
if result.Error != nil {
if mysqlErr, ok := result.Error.(*mysql.MySQLError); ok {
if mysqlErr.Number == 1062 {
// 唯一性 License 冲突
fmt.Println("License key already exists!")
continue
}
}
fmt.Println("failed to create license key")
break
}
licenses = append(licenses, key)
}
return strings.Join(licenses, "\n\t")
}
func CreateLicense3(count, itype int) string {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return ""
}
// ExpiryDate := generateExpiryDate(itype)
licenses := make([]string, 0, count)
for {
if count == 0 {
break
}
if len(licenses) >= count {
break
}
//key := generateSingleActivationCode()
// key := guuid.New().String()
key := "HB" + generateRandomString(10)
licenseKey := table.LicenseKey{
Status: 0,
License: key,
ExpiryDate: "",
Type: itype,
}
result := MysqlDB.Create(&licenseKey)
if result.Error != nil {
if mysqlErr, ok := result.Error.(*mysql.MySQLError); ok {
if mysqlErr.Number == 1062 {
// 唯一性 License 冲突
fmt.Println("License key already exists!")
continue
}
}
fmt.Println("failed to create license key")
break
}
licenses = append(licenses, key)
}
return strings.Join(licenses, "\n\t")
}
func DelayLicense(m req.DelayAuthKeyModel) (string, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return "", err
}
origin, err := HasLicense(m.Key)
if origin == nil || err != nil {
return "", errors.New(fmt.Sprintf("%s 该 key 无效! 请 检查正确性 或 联系管理员生成", m.Key))
}
expiryDateStr := origin.ExpiryDate
if m.Days > 0 {
// 获取当前的过期日期
originDate, err := time.Parse("2006-01-02", origin.ExpiryDate)
if err != nil {
return "", err
}
// 计算延期后的日期
expiryDate := originDate.AddDate(0, 0, m.Days)
// 格式化日期字符串
expiryDateStr = expiryDate.Format("2006-01-02")
} else {
expiryDateStr = m.ExpiryDate
}
result := MysqlDB.Model(&table.LicenseKey{}).
Where("license = ?", origin.License).
Updates(map[string]interface{}{
"expiry_date": expiryDateStr,
})
if result.Error != nil {
return "", result.Error
}
return expiryDateStr, nil
}
func DeleteLicense(key string, opt int) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
has, err := HasLicense(key)
if has == nil || err != nil {
return errors.New(fmt.Sprintf("%s 该 key 无效! 请 检查正确性 或 联系管理员生成", key))
}
// 目前仅支持 0 1 2 三个删除操作 新增不删除授权码 删除userinfo deviceinfo 以及 登录日志3
if opt < 0 || opt > 3 {
return errors.New("不存在该授权码 删除操作")
}
if opt != 3 {
// 删除 license_key 数据库表的授权码信息
err = MysqlDB.Where("license = ?", key).Delete(&table.LicenseKey{}).Error
if err != nil {
return err
}
}
if opt == 0 { // 仅删除 License 信息
return err
}
// op=1 时, 删除该 key 授权码对应的 UserInfoEntity DeviceInfoEntity
if has.Status == 0 || len(strings.TrimSpace(has.WxId)) == 0 {
// 该授权码还没有绑定微信号
return nil
}
// 删除 user_info_entity 数据库表的数据
_ = MysqlDB.Where("wxId = ?", has.WxId).Delete(&table.UserInfoEntity{}).Error
// 删除 device_info_entity 数据库表的数据
_ = MysqlDB.Where("wxid = ?", has.WxId).Delete(&table.DeviceInfoEntity{}).Error
if opt == 1 {
return nil
}
// 删除 UserLoginLog 数据库表的数据
_ = MysqlDB.Where("user_name = ?", has.WxId).Delete(&table.UserLoginLog{}).Error
return nil
}
// 禁用1 启用0
func DisableLicense(key string, opt int) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
result := MysqlDB.Model(&table.LicenseKey{}).
Where("license = ?", key).
Updates(map[string]interface{}{
"is_banned": opt,
})
if result.Error != nil {
return result.Error
}
return nil
}
// 创建黑名单
func CreateSB(owner string, blacker map[string]string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
blackerJSON, err := json.Marshal(blacker)
if err != nil {
return err
}
// 在这里执行 SQL 查询,直接将 blackerJSON 作为参数赋值给 Blacker 字段
record := table.BlackList{Owner: owner, Blacker: string(blackerJSON)}
result := MysqlDB.Create(&record)
if result.Error != nil {
return result.Error
}
key := owner + "_blacklist"
SETExpirationObj(key, blacker, -1)
return nil
}
// 查询黑名单
func QuerySB(owner string) (*table.BlackList, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var record table.BlackList
result := MysqlDB.Where("owner = ?", owner).First(&record)
if result.Error != nil {
return nil, result.Error
}
return &record, nil
}
// 更新黑名单
func UpdateSB(owner string, blacker map[string]string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
record, err := QuerySB(owner)
if err != nil {
return err
}
if record == nil {
CreateSB(owner, blacker)
return nil
}
blackerJSON, err := json.Marshal(blacker)
if err != nil {
return err
}
result := MysqlDB.Model(record).Update("Blacker", string(blackerJSON))
if result.Error != nil {
return result.Error
}
key := owner + "_blacklist"
SETExpirationObj(key, blacker, -1)
return nil
}
// 联系人操作
func SaveOrUpdateContact(contact *pb.ModContact, uuid string) error {
// 捕获 panic防止程序崩溃
defer func() {
if r := recover(); r != nil {
fmt.Printf("Recovered from panic: %v\n", r)
// 这里可以记录日志或者执行其他的恢复操作
}
}()
if contact == nil {
return errors.New("contact cannot be nil")
}
if contact.UserName == nil || contact.UserName.GetStr() == "" {
return errors.New("userName cannot be nil or empty")
}
contactJson, err := json.Marshal(contact)
if err != nil {
return err
}
m := getContactMutex(uuid)
m.Lock()
defer m.Unlock()
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
combinedKey := uuid + "-" + *contact.UserName.Str
// 进行更新或插入操作
err = MysqlDB.Where(table.ModContactDB{UserUUIDCombined: combinedKey}).
Assign(table.ModContactDB{Data: string(contactJson), UUID: uuid, UserName: *contact.UserName.Str}).
FirstOrCreate(&table.ModContactDB{}).Error
if err != nil {
return err
}
return nil
}
func GetContact(userName string, uuid string) *pb.ModContact {
// m := getContactMutex(uuid)
// m.Lock()
// defer m.Unlock()
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
var contactDB table.ModContactDB
combinedKey := uuid + "-" + userName
result := MysqlDB.First(&contactDB, "user_uuid_combined = ?", combinedKey)
if gorm.IsRecordNotFoundError(result.Error) {
return nil
}
if result.Error != nil {
return nil
}
var contact pb.ModContact
err := json.Unmarshal([]byte(contactDB.Data), &contact)
if err != nil {
return nil
}
return &contact
}
// 删除联系人
func DeleteContact(userName string, uuid string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
combinedKey := uuid + "-" + userName
result := MysqlDB.Where("user_uuid_combined = ?", combinedKey).Delete(&table.ModContactDB{})
if result.Error != nil {
return result.Error
}
return nil
}
// 消息操作
func SaveMsg(msg pb.AddMsg, uuid string) error {
if msg.NewMsgId == nil {
return errors.New("NewMsgId cannot be nil")
}
// 序列化消息为 JSON
msgJson, err := json.Marshal(msg)
if err != nil {
return err
}
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
combinedKey := uuid + fmt.Sprintf("-%d", *msg.NewMsgId)
// m := getContactMutex(uuid + "-msg")
// m.Lock()
// defer m.Unlock()
// 检查消息是否已存在
var existingMsg table.AddMsgDB
// 如果没有找到记录,会返回 gorm.ErrRecordNotFound
err = MysqlDB.Where("msg_uuid_combined = ?", combinedKey).First(&existingMsg).Error
if err == nil {
return nil // 已存在,不更新,直接返回
}
if !gorm.IsRecordNotFoundError(err) {
return err // 返回数据库操作中的其他错误
}
// 保存新消息
newMsg := &table.AddMsgDB{
UUID: uuid,
NewMsgId: msg.GetNewMsgId(),
MsgUUIDCombined: combinedKey,
Data: string(msgJson),
CreateTime: msg.GetCreateTime(),
}
if err := MysqlDB.Create(newMsg).Error; err != nil {
return err // 返回创建消息时的任何错误
}
return nil
}
func GetAndDeleteMsg(newMsgId int64, uuid string) *pb.AddMsg {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil
}
combinedKey := uuid + fmt.Sprintf("-%d", newMsgId)
// m := getContactMutex(combinedKey)
// m.Lock()
// defer m.Unlock()
var msgDB table.AddMsgDB
// 获取消息
if err := MysqlDB.Where("msg_uuid_combined = ?", combinedKey).First(&msgDB).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
return nil // 消息不存在
}
return nil // 发生其他错误
}
// 删除消息
if err := MysqlDB.Delete(&msgDB).Error; err != nil {
return nil // 发生删除错误
}
// 解析消息内容
var msg pb.AddMsg
if err := json.Unmarshal([]byte(msgDB.Data), &msg); err != nil {
return nil // 解析错误
}
return &msg
}
// StartCleanupTask 定时清理消息
func StartCleanupTask() {
// 多个协议,启动一个即可
if srvconfig.GlobalSetting.OpenClearMsg == false {
return
}
ticker := time.NewTicker(3 * time.Minute)
go func() {
for range ticker.C {
cleanupOldMessages()
}
}()
}
func cleanupOldMessages() {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
cutoffTime := uint32(time.Now().Add(-3 * time.Minute).Unix())
tx := MysqlDB.Begin()
if tx.Error != nil {
return
}
if err := tx.Where("create_time < ?", cutoffTime).Delete(&table.AddMsgDB{}).Error; err != nil {
tx.Rollback()
return
}
tx.Commit()
log.Println("Old messages cleaned up successfully.")
}
// 消息回调配置相关操作
// SaveMessageCallbackConfig 保存或更新消息回调配置
func SaveMessageCallbackConfig(config *table.MessageCallbackConfig) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
// 确保UUID和Key字段为相同的值
if config.UUID == "" {
return errors.New("UUID字段不能为空")
}
// 设置Key字段和UUID保持一致
config.Key = config.UUID
var existingConfig table.MessageCallbackConfig
result := MysqlDB.Where("uuid = ?", config.UUID).First(&existingConfig)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
// 记录不存在,创建新记录
log.Infof("[回调调试] 创建新配置记录 [UUID: %s]", config.UUID)
2026-02-17 13:06:23 +08:00
newConfig := table.MessageCallbackConfig{
UUID: config.UUID,
Key: config.UUID, // 确保Key和UUID相同
CallbackURL: config.CallbackURL,
Enabled: config.Enabled,
}
return MysqlDB.Create(&newConfig).Error
}
return result.Error
}
return MysqlDB.Model(&existingConfig).Updates(map[string]interface{}{
"key": config.UUID, // 更新Key字段
"callback_url": config.CallbackURL,
"enabled": config.Enabled,
}).Error
}
// GetMessageCallbackConfig 获取消息回调配置
func GetMessageCallbackConfig(uuid string) (*table.MessageCallbackConfig, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var config table.MessageCallbackConfig
result := MysqlDB.Where("uuid = ?", uuid).First(&config)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
log.Debugf("[回调调试] 数据库中未找到配置 [UUID: %s]", uuid)
return nil, nil
2026-02-17 13:06:23 +08:00
}
log.Errorf("[回调调试] 查询配置出错 [UUID: %s]: %v", uuid, result.Error)
2026-02-17 13:06:23 +08:00
return nil, result.Error
}
return &config, nil
}
// DeleteMessageCallbackConfig 删除消息回调配置
func DeleteMessageCallbackConfig(uuid string) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
return MysqlDB.Where("uuid = ?", uuid).Delete(&table.MessageCallbackConfig{}).Error
}
// GetAllEnabledCallbacks 获取所有启用的回调配置
func GetAllEnabledCallbacks() ([]table.MessageCallbackConfig, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var configs []table.MessageCallbackConfig
result := MysqlDB.Where("enabled = ?", true).Find(&configs)
if result.Error != nil {
return nil, result.Error
}
return configs, nil
}
// GetActiveLicenseKeys 获取所有激活状态的卡密
func GetActiveLicenseKeys() ([]*table.LicenseKey, error) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return nil, err
}
var licenseKeys []*table.LicenseKey
err := MysqlDB.Where("status = ?", 1).Find(&licenseKeys).Error
if err != nil {
return nil, err
}
return licenseKeys, nil
}
// SaveShortLinkStatusToDB 将短连接状态保存到数据库
func SaveShortLinkStatusToDB(uuid string, isEnabled bool) {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return
}
data := make(map[string]interface{})
data["short_link_enabled"] = isEnabled
// 更新用户信息表的短连接状态
_ = MysqlDB.Model(&table.UserInfoEntity{}).
Where("uuid = ? ", uuid).
Updates(data)
// 同时更新缓存
SaveShortLinkStatus(uuid, isEnabled)
}
// LoadShortLinkStatusFromDB 从数据库加载短连接状态
func LoadShortLinkStatusFromDB(uuid string) *ShortLinkStatus {
// 先尝试从缓存中获取
status := GetShortLinkStatus(uuid)
if status != nil {
return status
}
// 缓存中不存在,从数据库加载
if err := checkAndReconnect(); err != nil {
return nil
}
var userInfoEntity table.UserInfoEntity
if err := MysqlDB.Where("uuid = ?", uuid).First(&userInfoEntity).Error; err != nil {
return nil
}
// 创建短连接状态对象并缓存
status = &ShortLinkStatus{
IsEnabled: userInfoEntity.ShortLinkEnabled,
}
SaveShortLinkStatus(uuid, status.IsEnabled)
return status
}
// SaveDeviceInfo 保存设备信息到数据库
func SaveDeviceInfo(deviceInfo *table.DeviceInfoEntity) error {
// 检查并重新连接数据库
if err := checkAndReconnect(); err != nil {
return err
}
// 使用 Upsert 操作,如果记录存在则更新,不存在则插入
result := MysqlDB.Save(deviceInfo)
return result.Error
}