2025-03-27 16:27:41 +08:00

225 lines
6.2 KiB
Go

package handler
import (
"context"
"log"
"strconv"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
"gitee.ltd/lxh/wechat-robot/internal/config"
"gitee.ltd/lxh/wechat-robot/internal/docker"
"gitee.ltd/lxh/wechat-robot/internal/model"
)
// ListRobots 列出所有机器人
func ListRobots(c *fiber.Ctx) error {
db := model.GetDB()
var robots []model.Robot
if err := db.Find(&robots).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "获取机器人列表失败")
}
return c.Render("robot/index", fiber.Map{
"Title": "机器人列表",
"Robots": robots,
})
}
// NewRobotForm 显示创建机器人表单
func NewRobotForm(c *fiber.Ctx) error {
return c.Render("robot/new", fiber.Map{
"Title": "新建机器人",
})
}
// CreateRobot 创建新机器人
func CreateRobot(c *fiber.Ctx) error {
robotName := c.FormValue("name")
if robotName == "" {
return fiber.NewError(fiber.StatusBadRequest, "机器人名称不能为空")
}
// 加载配置
cfg, err := config.Load()
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "加载配置失败")
}
// 创建Docker容器
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
containerID, err := docker.CreateRobotContainer(ctx, &cfg.Docker, robotName)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "创建容器失败: "+err.Error())
}
// 创建机器人数据库记录
robot := model.Robot{
ContainerID: containerID,
Nickname: robotName,
Status: model.RobotStatusOffline,
}
db := model.GetDB()
if err := db.Create(&robot).Error; err != nil {
// 如果数据库创建失败,尝试删除容器
docker.RemoveContainer(ctx, containerID, true)
return fiber.NewError(fiber.StatusInternalServerError, "保存机器人信息失败: "+err.Error())
}
// 添加到监控器
// 注意:这里假设有一个全局可访问的监控器实例,实际中可能需要通过依赖注入或其他方式获取
monitor := docker.NewContainerMonitor(db, time.Minute)
monitor.AddRobot(containerID)
return c.Redirect("/admin/robots/" + strconv.Itoa(int(robot.ID)))
}
// ShowRobot 显示机器人详情
func ShowRobot(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
}
var robot model.Robot
db := model.GetDB()
if err := db.First(&robot, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
}
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
}
return c.Render("robot/show", fiber.Map{
"Title": robot.Nickname,
"Robot": robot,
})
}
// DeleteRobot 删除机器人
func DeleteRobot(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
}
var robot model.Robot
db := model.GetDB()
if err := db.First(&robot, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
}
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
}
// 首先尝试登出微信
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if robot.Status == model.RobotStatusOnline {
if err := docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
log.Printf("登出机器人失败: %v", err)
// 继续删除流程,不因登出失败而中断
}
}
// 删除容器
if err := docker.RemoveContainer(ctx, robot.ContainerID, true); err != nil {
log.Printf("删除容器失败: %v", err)
// 继续删除流程,不因容器删除失败而中断
}
// 从监控器移除
monitor := docker.NewContainerMonitor(db, time.Minute)
monitor.RemoveRobot(robot.ContainerID)
// 删除数据库记录
if err := db.Delete(&robot).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "删除机器人失败: "+err.Error())
}
return c.Redirect("/admin/robots")
}
// RobotLogin 显示微信登录二维码
func RobotLogin(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
}
var robot model.Robot
db := model.GetDB()
if err := db.First(&robot, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
}
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
}
// 获取登录二维码
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
qrcode, err := docker.GetLoginQRCode(ctx, robot.ContainerID)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "获取登录二维码失败: "+err.Error())
}
// 保存二维码路径到数据库
robot.QRCodePath = qrcode
if err := db.Save(&robot).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "保存二维码路径失败")
}
return c.Render("robot/login", fiber.Map{
"Title": "微信登录",
"Robot": robot,
"QRCode": qrcode,
})
}
// RobotLogout 机器人退出微信登录
func RobotLogout(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "无效的ID")
}
var robot model.Robot
db := model.GetDB()
if err := db.First(&robot, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return fiber.NewError(fiber.StatusNotFound, "机器人不存在")
}
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
}
// 登出微信
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := docker.LogoutWechatBot(ctx, robot.ContainerID); err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
}
// 更新机器人状态
robot.Status = model.RobotStatusOffline
robot.QRCodePath = ""
if err := db.Save(&robot).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, "更新状态失败")
}
return c.Redirect("/admin/robots/" + c.Params("id"))
}