🎨 优化登录逻辑,添加设备ID和名称生成,更新二维码状态处理
All checks were successful
BuildImage / build-image (push) Successful in 2m3s
All checks were successful
BuildImage / build-image (push) Successful in 2m3s
This commit is contained in:
parent
37b766368f
commit
2f6b3fac01
@ -19,9 +19,9 @@ docker:
|
|||||||
imageName: "lxh01/xybotv2:latest"
|
imageName: "lxh01/xybotv2:latest"
|
||||||
network: "bridge"
|
network: "bridge"
|
||||||
redis:
|
redis:
|
||||||
host: "localhost"
|
host: "10.0.0.31"
|
||||||
password: "password"
|
password: "pGhQKwj7DE7FbFL1"
|
||||||
db: 0
|
db: 2
|
||||||
|
|
||||||
auth:
|
auth:
|
||||||
secretKey: "dev-secret-key"
|
secretKey: "dev-secret-key"
|
||||||
|
1
go.mod
1
go.mod
@ -14,6 +14,7 @@ require (
|
|||||||
gorm.io/driver/postgres v1.5.11
|
gorm.io/driver/postgres v1.5.11
|
||||||
gorm.io/driver/sqlite v1.5.7
|
gorm.io/driver/sqlite v1.5.7
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
|
gorm.io/plugin/soft_delete v1.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
8
go.sum
8
go.sum
@ -64,6 +64,8 @@ github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk
|
|||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
@ -81,6 +83,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
@ -243,10 +246,15 @@ gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
|||||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||||
|
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
|
||||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||||
|
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
|
gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
|
gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
|
||||||
|
gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
|
||||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
|
@ -44,7 +44,13 @@ type CheckUuidResponse struct {
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Data struct {
|
Data struct {
|
||||||
Status int `json:"Status"`
|
Uuid string `json:"uuid"`
|
||||||
|
Status int `json:"status"` // 状态
|
||||||
|
PushLoginUrlExpiredTime int `json:"pushLoginUrlexpiredTime"` // 推送登录url过期时间
|
||||||
|
ExpiredTime int `json:"expiredTime"` // 过期时间(秒)
|
||||||
|
HeadImgUrl string `json:"headImgUrl"` // 头像
|
||||||
|
NickName string `json:"nickName"` // 昵称
|
||||||
|
AcctSectResp map[string]any `json:"acctSectResp"` // 账号信息-登录成功之后才有
|
||||||
} `json:"data"`
|
} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +155,7 @@ func GetQRCode(ctx context.Context, containerID string, containerHost string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckUuid 检查二维码状态
|
// CheckUuid 检查二维码状态
|
||||||
func CheckUuid(ctx context.Context, containerID string, uuid string, containerHost string) (*CheckUuidResponse, error) {
|
func CheckUuid(ctx context.Context, uuid string, containerHost string) (*CheckUuidResponse, error) {
|
||||||
client := newHTTPClient()
|
client := newHTTPClient()
|
||||||
|
|
||||||
url := fmt.Sprintf("http://%s/CheckUuid", containerHost)
|
url := fmt.Sprintf("http://%s/CheckUuid", containerHost)
|
||||||
|
@ -2,6 +2,10 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"gitee.ltd/lxh/wechat-robot/internal/config"
|
||||||
|
"github.com/gofiber/fiber/v2/log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -33,8 +37,8 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
|||||||
// 获取机器人实例
|
// 获取机器人实例
|
||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
var robot model.Robot
|
var robot model.Robot
|
||||||
if err := db.First(&robot, id).Error; err != nil {
|
if err = db.First(&robot, id).Error; err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "机器人不存在",
|
"message": "机器人不存在",
|
||||||
@ -51,7 +55,7 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// 调用CheckUuid API检查二维码状态,传递容器访问地址
|
// 调用CheckUuid API检查二维码状态,传递容器访问地址
|
||||||
response, err := docker.CheckUuid(ctx, robot.ContainerID, uuid, robot.ContainerHost)
|
response, err := docker.CheckUuid(ctx, uuid, robot.ContainerHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"success": false,
|
"success": false,
|
||||||
@ -59,8 +63,23 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg, _ := config.Load(); cfg.Server.Env == "development" {
|
||||||
|
bs, _ := json.Marshal(response)
|
||||||
|
log.Debugf("扫码返回结果: %s", bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果返回status=1,表示已扫码,暂存一下昵称和头像
|
||||||
|
if response.Data.Status == 1 {
|
||||||
|
robot.Nickname = response.Data.NickName
|
||||||
|
robot.Avatar = response.Data.HeadImgUrl
|
||||||
|
db.Save(&robot)
|
||||||
|
}
|
||||||
|
|
||||||
// 如果检测到已登录,更新机器人状态
|
// 如果检测到已登录,更新机器人状态
|
||||||
if response.Success && response.Data.Status == 2 {
|
if response.Success && response.Data.AcctSectResp != nil {
|
||||||
|
response.Data.Status = 99
|
||||||
|
robot.WechatID = response.Data.AcctSectResp["userName"].(string)
|
||||||
|
|
||||||
// 开启自动心跳,传递容器访问地址
|
// 开启自动心跳,传递容器访问地址
|
||||||
if robot.WechatID != "" {
|
if robot.WechatID != "" {
|
||||||
_, _ = docker.AutoHeartbeatStart(ctx, robot.ContainerID, robot.WechatID, robot.ContainerHost)
|
_, _ = docker.AutoHeartbeatStart(ctx, robot.ContainerID, robot.WechatID, robot.ContainerHost)
|
||||||
@ -74,8 +93,9 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"success": response.Success,
|
"success": response.Success,
|
||||||
"status": response.Data.Status,
|
"status": response.Data.Status,
|
||||||
"message": response.Message,
|
"message": response.Message,
|
||||||
|
"userInfo": response.Data.AcctSectResp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -79,9 +80,9 @@ func CreateRobot(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
if err := db.Create(&robot).Error; err != nil {
|
if err = db.Create(&robot).Error; err != nil {
|
||||||
// 如果数据库创建失败,尝试删除容器
|
// 如果数据库创建失败,尝试删除容器
|
||||||
docker.RemoveContainer(ctx, containerID, true)
|
_ = docker.RemoveContainer(ctx, containerID, true)
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "保存机器人信息失败: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "保存机器人信息失败: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
var robot model.Robot
|
var robot model.Robot
|
||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
|
|
||||||
if err := db.First(&robot, id).Error; err != nil {
|
if err = db.First(&robot, id).Error; err != nil {
|
||||||
// 针对API请求返回JSON
|
// 针对API请求返回JSON
|
||||||
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||||
@ -141,7 +142,7 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
"message": "机器人不存在",
|
"message": "机器人不存在",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
||||||
@ -152,14 +153,14 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if robot.Status == model.RobotStatusOnline {
|
if robot.Status == model.RobotStatusOnline {
|
||||||
if err := docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
|
if err = docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
|
||||||
log.Printf("登出机器人失败: %v", err)
|
log.Printf("登出机器人失败: %v", err)
|
||||||
// 继续删除流程,不因登出失败而中断
|
// 继续删除流程,不因登出失败而中断
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除容器
|
// 删除容器
|
||||||
if err := docker.RemoveContainer(ctx, robot.ContainerID, true); err != nil {
|
if err = docker.RemoveContainer(ctx, robot.ContainerID, true); err != nil {
|
||||||
log.Printf("删除容器失败: %v", err)
|
log.Printf("删除容器失败: %v", err)
|
||||||
// 继续删除流程,不因容器删除失败而中断
|
// 继续删除流程,不因容器删除失败而中断
|
||||||
}
|
}
|
||||||
@ -169,7 +170,7 @@ func DeleteRobot(c *fiber.Ctx) error {
|
|||||||
monitor.RemoveRobot(robot.ContainerID)
|
monitor.RemoveRobot(robot.ContainerID)
|
||||||
|
|
||||||
// 删除数据库记录
|
// 删除数据库记录
|
||||||
if err := db.Delete(&robot).Error; err != nil {
|
if err = db.Delete(&robot).Error; err != nil {
|
||||||
// 针对API请求返回JSON
|
// 针对API请求返回JSON
|
||||||
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
if strings.HasPrefix(c.Get("Accept"), "application/json") || c.Method() == "DELETE" {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
@ -201,13 +202,16 @@ func RobotLogin(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
var robot model.Robot
|
var robot model.Robot
|
||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
|
if err = db.First(&robot, id).Error; err != nil {
|
||||||
if err := db.First(&robot, id).Error; err != nil {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
||||||
}
|
}
|
||||||
|
// 处理一下设备信息
|
||||||
|
if robot.CheckDevice() {
|
||||||
|
db.Save(&robot)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取登录二维码
|
// 获取登录二维码
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
@ -219,12 +223,6 @@ func RobotLogin(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, "获取登录二维码失败: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "获取登录二维码失败: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存二维码相关信息到数据库
|
|
||||||
robot.QRCodePath = qrcodeResp.Data.QRCodeBase64
|
|
||||||
if err := db.Save(&robot).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "保存二维码信息失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染登录页面,包含二维码和UUID(用于后续状态检查)
|
// 渲染登录页面,包含二维码和UUID(用于后续状态检查)
|
||||||
return c.Render("robot/login", fiber.Map{
|
return c.Render("robot/login", fiber.Map{
|
||||||
"Title": "微信登录",
|
"Title": "微信登录",
|
||||||
@ -245,8 +243,8 @@ func RobotLogout(c *fiber.Ctx) error {
|
|||||||
var robot model.Robot
|
var robot model.Robot
|
||||||
db := model.GetDB()
|
db := model.GetDB()
|
||||||
|
|
||||||
if err := db.First(&robot, id).Error; err != nil {
|
if err = db.First(&robot, id).Error; err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
|
||||||
}
|
}
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
||||||
@ -258,20 +256,19 @@ func RobotLogout(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
// 使用新的LogOut API接口,传递容器访问地址
|
// 使用新的LogOut API接口,传递容器访问地址
|
||||||
if robot.WechatID != "" {
|
if robot.WechatID != "" {
|
||||||
if _, err := docker.LogOut(ctx, robot.ContainerID, robot.WechatID, robot.ContainerHost); err != nil {
|
if _, err = docker.LogOut(ctx, robot.ContainerID, robot.WechatID, robot.ContainerHost); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果没有WechatID,使用原来的方法
|
// 如果没有WechatID,使用原来的方法
|
||||||
if err := docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
|
if err = docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新机器人状态
|
// 更新机器人状态
|
||||||
robot.Status = model.RobotStatusOffline
|
robot.Status = model.RobotStatusOffline
|
||||||
robot.QRCodePath = ""
|
if err = db.Save(&robot).Error; err != nil {
|
||||||
if err := db.Save(&robot).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "更新状态失败")
|
return fiber.NewError(fiber.StatusInternalServerError, "更新状态失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@ package model
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/plugin/soft_delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseModel 是所有模型的基础结构
|
// BaseModel 是所有模型的基础结构
|
||||||
type BaseModel struct {
|
type BaseModel struct {
|
||||||
ID uint `gorm:"primarykey" json:"id"`
|
ID uint `gorm:"primarykey" json:"id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt int64 `json:"-" gorm:"index:deleted; default:0"`
|
||||||
|
IsDel soft_delete.DeletedAt `json:"-" gorm:"softDelete:flag,DeletedAtField:DeletedAt; index:deleted; default:0; type:tinyint(1)"`
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"gitee.ltd/lxh/wechat-robot/internal/utils"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -18,15 +19,16 @@ const (
|
|||||||
// Robot 表示微信机器人实例
|
// Robot 表示微信机器人实例
|
||||||
type Robot struct {
|
type Robot struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
ContainerID string `gorm:"column:container_id;uniqueIndex:idx_container_id,length:64" json:"container_id"`
|
ContainerID string `gorm:"column:container_id;index:deleted,unique,length:64" json:"container_id"`
|
||||||
ShortContainerID string `gorm:"column:short_container_id;index" json:"short_container_id"` // 容器ID的前12位
|
ShortContainerID string `gorm:"column:short_container_id;index" json:"short_container_id"` // 容器ID的前12位
|
||||||
ContainerHost string `gorm:"column:container_host" json:"container_host"` // 容器访问地址,格式为ip:port
|
ContainerHost string `gorm:"column:container_host" json:"container_host"` // 容器访问地址,格式为ip:port
|
||||||
WechatID string `gorm:"column:wechat_id;index:idx_wechat_id,length:64" json:"wechat_id"`
|
DeviceId string `gorm:"column:device_id;index:deleted,unique;" json:"deviceId"` // 设备Id
|
||||||
|
DeviceName string `gorm:"column:device_name" json:"deviceName"` // 设备名称
|
||||||
|
WechatID string `gorm:"column:wechat_id;index:deleted,unique,length:64" json:"wechat_id"`
|
||||||
Nickname string `gorm:"column:nickname" json:"nickname"`
|
Nickname string `gorm:"column:nickname" json:"nickname"`
|
||||||
Avatar string `gorm:"column:avatar" json:"avatar"`
|
Avatar string `gorm:"column:avatar" json:"avatar"`
|
||||||
Status RobotStatus `gorm:"column:status;default:'offline'" json:"status"`
|
Status RobotStatus `gorm:"column:status;default:'offline'" json:"status"`
|
||||||
LastLoginAt *time.Time `gorm:"column:last_login_at" json:"last_login_at"`
|
LastLoginAt *time.Time `gorm:"column:last_login_at" json:"last_login_at"`
|
||||||
QRCodePath string `gorm:"column:qrcode_path" json:"qrcode_path"` // 二维码路径,用于登录
|
|
||||||
ErrorMessage string `gorm:"column:error_message" json:"error_message"`
|
ErrorMessage string `gorm:"column:error_message" json:"error_message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,3 +70,22 @@ func (r *Robot) BeforeDelete(tx *gorm.DB) error {
|
|||||||
// 在删除机器人之前,可以处理相关依赖
|
// 在删除机器人之前,可以处理相关依赖
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckDevice
|
||||||
|
// @description: 检查设备信息
|
||||||
|
// @receiver r
|
||||||
|
// @return bool 是否重新处理过,如果是,就返回true
|
||||||
|
func (r *Robot) CheckDevice() (flag bool) {
|
||||||
|
// 如果是空的,就获取环境变量配置的值,如果还是空的,就生成一个新的
|
||||||
|
if r.DeviceId == "" {
|
||||||
|
r.DeviceId = utils.GetDeviceId()
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是空的,就获取环境变量配置的值,如果还是空的,就生成一个新的
|
||||||
|
if r.DeviceName == "" {
|
||||||
|
r.DeviceName = utils.GetDeviceName()
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
66
internal/utils/device.go
Normal file
66
internal/utils/device.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetDeviceId
|
||||||
|
// @description: 生成设备ID
|
||||||
|
// @return deviceId 设备ID
|
||||||
|
func GetDeviceId() (deviceId string) {
|
||||||
|
// 创建一个有种子的随机数生成器
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
// 定义可能的字母
|
||||||
|
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
// 初始化字符串构建器
|
||||||
|
var sb strings.Builder
|
||||||
|
// 生成15个随机字母
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
sb.WriteByte(letters[r.Intn(len(letters))])
|
||||||
|
}
|
||||||
|
// 计算MD5哈希
|
||||||
|
hash := md5.Sum([]byte(sb.String()))
|
||||||
|
hashString := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
// 返回49加上MD5哈希的第3个字符开始的部分
|
||||||
|
return "49" + hashString[2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceName
|
||||||
|
// @description: 生成设备名称
|
||||||
|
// @return deviceName 设备名称
|
||||||
|
func GetDeviceName() (deviceName string) {
|
||||||
|
firstNames := []string{
|
||||||
|
"Oliver", "Emma", "Liam", "Ava", "Noah", "Sophia", "Elijah", "Isabella",
|
||||||
|
"James", "Mia", "William", "Amelia", "Benjamin", "Harper", "Lucas", "Evelyn",
|
||||||
|
"Henry", "Abigail", "Alexander", "Ella", "Jackson", "Scarlett", "Sebastian",
|
||||||
|
"Grace", "Aiden", "Chloe", "Matthew", "Zoey", "Samuel", "Lily", "David",
|
||||||
|
"Aria", "Joseph", "Riley", "Carter", "Nora", "Owen", "Luna", "Daniel",
|
||||||
|
"Sofia", "Gabriel", "Ellie", "Matthew", "Avery", "Isaac", "Mila", "Leo",
|
||||||
|
"Julian", "Layla",
|
||||||
|
}
|
||||||
|
|
||||||
|
lastNames := []string{
|
||||||
|
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
|
||||||
|
"Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson",
|
||||||
|
"Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee", "Perez", "Thompson",
|
||||||
|
"White", "Harris", "Sanchez", "Clark", "Ramirez", "Lewis", "Robinson", "Walker",
|
||||||
|
"Young", "Allen", "King", "Wright", "Scott", "Torres", "Nguyen", "Hill",
|
||||||
|
"Flores", "Green", "Adams", "Nelson", "Baker", "Hall", "Rivera", "Campbell",
|
||||||
|
"Mitchell", "Carter", "Roberts", "Gomez", "Phillips", "Evans",
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用随机数生成器
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
// 随机选择名字和姓氏
|
||||||
|
firstName := firstNames[r.Intn(len(firstNames))]
|
||||||
|
lastName := lastNames[r.Intn(len(lastNames))]
|
||||||
|
|
||||||
|
// 返回组合后的字符串
|
||||||
|
return firstName + " " + lastName + "'s Pad"
|
||||||
|
}
|
@ -110,7 +110,7 @@
|
|||||||
已扫描,等待确认
|
已扫描,等待确认
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
} else if (data.status === 2) {
|
} else if (data.status === 99 && data.userInfo && Object.keys(data.userInfo).length > 0) {
|
||||||
qrcodeStatus.innerHTML = `
|
qrcodeStatus.innerHTML = `
|
||||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
|
||||||
<i class="fas fa-check-circle mr-1"></i>
|
<i class="fas fa-check-circle mr-1"></i>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
# 数据库迁移
|
|
||||||
|
|
||||||
此目录包含数据库迁移文件:
|
|
||||||
- 表结构创建
|
|
||||||
- 数据初始化
|
|
||||||
- 架构升级脚本
|
|
@ -1,7 +0,0 @@
|
|||||||
# 静态资源
|
|
||||||
|
|
||||||
此目录包含Web应用的静态资源:
|
|
||||||
- CSS样式
|
|
||||||
- JavaScript文件
|
|
||||||
- 图片资源
|
|
||||||
- 字体文件
|
|
Loading…
x
Reference in New Issue
Block a user