Files
wechat_ipad_pro/db/mysql_db.go

1764 lines
48 KiB
Go
Raw Permalink 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 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 完成后
// 这样可以确保所有账号连接都已建立
}
// InitMessageCallbacks 初始化所有消息回调配置
func InitMessageCallbacks() {
// 账号已经初始化完成,稍微等待一下确保连接稳定
time.Sleep(3 * time.Second)
log.Infof("开始初始化消息回调配置...")
// 检查并重新连接数据库
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
for _, config := range configs {
// 确保Key字段正确
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++
}
}
log.Infof("========================================")
log.Infof("回调配置激活完成: 成功 %d/%d", successCount, len(configs))
log.Infof("========================================")
}
// 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)
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
}
log.Errorf("[回调调试] 查询配置出错 [UUID: %s]: %v", uuid, result.Error)
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
}