1764 lines
48 KiB
Go
1764 lines
48 KiB
Go
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
|
||
}
|