commit
This commit is contained in:
@@ -2,8 +2,17 @@ package controller
|
||||
|
||||
import (
|
||||
"ai_xhs/common"
|
||||
"ai_xhs/database"
|
||||
"ai_xhs/models"
|
||||
"ai_xhs/service"
|
||||
"ai_xhs/utils"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -29,7 +38,10 @@ func (ctrl *EmployeeController) SendXHSCode(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err := ctrl.service.SendXHSCode(req.XHSPhone)
|
||||
// 获取当前登录用户ID
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
err := ctrl.service.SendXHSCode(req.XHSPhone, employeeID)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
@@ -59,24 +71,149 @@ func (ctrl *EmployeeController) GetProfile(c *gin.Context) {
|
||||
"name": displayName,
|
||||
"username": employee.Username,
|
||||
"real_name": employee.RealName,
|
||||
"nickname": employee.Nickname,
|
||||
"email": employee.Email,
|
||||
"phone": employee.Phone,
|
||||
"role": employee.Role,
|
||||
"enterprise_id": employee.EnterpriseID,
|
||||
"enterprise_name": employee.Enterprise.Name,
|
||||
"avatar": employee.Icon,
|
||||
"is_bound_xhs": employee.IsBoundXHS,
|
||||
"xhs_account": employee.XHSAccount,
|
||||
"xhs_phone": employee.XHSPhone,
|
||||
"has_xhs_cookie": employee.XHSCookie != "", // 标识是否有Cookie,不返回完整Cookie
|
||||
}
|
||||
|
||||
if employee.BoundAt != nil {
|
||||
data["bound_at"] = employee.BoundAt.Format("2006-01-02 15:04:05")
|
||||
// 如果已绑定,从 ai_authors 表获取小红书账号信息(根据 created_user_id 查询)
|
||||
if employee.IsBoundXHS == 1 {
|
||||
var author models.Author
|
||||
err := database.DB.Where(
|
||||
"created_user_id = ? AND enterprise_id = ? AND channel = 1 AND status = 'active'",
|
||||
employeeID, employee.EnterpriseID,
|
||||
).First(&author).Error
|
||||
|
||||
if err == nil {
|
||||
data["xhs_account"] = author.XHSAccount
|
||||
data["xhs_phone"] = author.XHSPhone
|
||||
data["has_xhs_cookie"] = author.XHSCookie != ""
|
||||
if author.BoundAt != nil {
|
||||
data["bound_at"] = author.BoundAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
} else {
|
||||
// 没有找到author记录,返回默认值
|
||||
data["xhs_account"] = ""
|
||||
data["xhs_phone"] = ""
|
||||
data["has_xhs_cookie"] = false
|
||||
}
|
||||
} else {
|
||||
data["xhs_account"] = ""
|
||||
data["xhs_phone"] = ""
|
||||
data["has_xhs_cookie"] = false
|
||||
}
|
||||
|
||||
common.Success(c, data)
|
||||
}
|
||||
|
||||
// BindXHS 绑定小红书账号
|
||||
// UpdateProfile 更新个人资料(昵称、邮箱、头像)
|
||||
func (ctrl *EmployeeController) UpdateProfile(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
var req struct {
|
||||
Nickname *string `json:"nickname"`
|
||||
Email *string `json:"email"`
|
||||
Avatar *string `json:"avatar"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if req.Nickname == nil && req.Email == nil && req.Avatar == nil {
|
||||
common.Error(c, common.CodeInvalidParams, "没有可更新的字段")
|
||||
return
|
||||
}
|
||||
|
||||
// 简单校验邮箱格式
|
||||
if req.Email != nil && *req.Email != "" {
|
||||
if !strings.Contains(*req.Email, "@") {
|
||||
common.Error(c, common.CodeInvalidParams, "邮箱格式不正确")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := ctrl.service.UpdateProfile(employeeID, req.Nickname, req.Email, req.Avatar); err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "更新成功", nil)
|
||||
}
|
||||
|
||||
// UploadAvatar 上传头像
|
||||
func (ctrl *EmployeeController) UploadAvatar(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
// 获取上传的文件
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "请选择要上传的图片")
|
||||
return
|
||||
}
|
||||
|
||||
// 校验文件类型
|
||||
contentType := file.Header.Get("Content-Type")
|
||||
if !strings.HasPrefix(contentType, "image/") {
|
||||
common.Error(c, common.CodeInvalidParams, "只能上传图片文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 校验文件大小(5MB)
|
||||
if file.Size > 5*1024*1024 {
|
||||
common.Error(c, common.CodeInvalidParams, "图片大小不能超过5MB")
|
||||
return
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, "打开文件失败")
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
// 读取文件内容
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(src)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, "读取文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到 OSS
|
||||
fileExt := ".jpg"
|
||||
if strings.Contains(contentType, "png") {
|
||||
fileExt = ".png"
|
||||
} else if strings.Contains(contentType, "webp") {
|
||||
fileExt = ".webp"
|
||||
}
|
||||
|
||||
fileName := fmt.Sprintf("avatar_%d_%d%s", employeeID, time.Now().Unix(), fileExt)
|
||||
ossURL, err := utils.UploadToOSS(bytes.NewReader(buf.Bytes()), fileName)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, fmt.Sprintf("上传失败: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
if err := ctrl.service.UpdateProfile(employeeID, nil, nil, &ossURL); err != nil {
|
||||
common.Error(c, common.CodeInternalError, "更新头像失败")
|
||||
return
|
||||
}
|
||||
|
||||
common.Success(c, map[string]interface{}{
|
||||
"url": ossURL,
|
||||
})
|
||||
}
|
||||
|
||||
// BindXHS 绑定小红书账号(异步处理)
|
||||
func (ctrl *EmployeeController) BindXHS(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
@@ -90,17 +227,31 @@ func (ctrl *EmployeeController) BindXHS(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
xhsAccount, err := ctrl.service.BindXHS(employeeID, req.XHSPhone, req.Code)
|
||||
_, err := ctrl.service.BindXHS(employeeID, req.XHSPhone, req.Code)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeBindXHSFailed, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "绑定成功", map[string]interface{}{
|
||||
"xhs_account": xhsAccount,
|
||||
// 立即返回成功,告知前端正在处理
|
||||
common.SuccessWithMessage(c, "正在验证登录,请稍候...", map[string]interface{}{
|
||||
"status": "processing",
|
||||
})
|
||||
}
|
||||
|
||||
// GetBindXHSStatus 获取小红书绑定状态
|
||||
func (ctrl *EmployeeController) GetBindXHSStatus(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
status, err := ctrl.service.GetBindXHSStatus(employeeID)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.Success(c, status)
|
||||
}
|
||||
|
||||
// UnbindXHS 解绑小红书账号
|
||||
func (ctrl *EmployeeController) UnbindXHS(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
@@ -246,14 +397,24 @@ func (ctrl *EmployeeController) CheckXHSStatus(c *gin.Context) {
|
||||
|
||||
// GetProducts 获取产品列表
|
||||
func (ctrl *EmployeeController) GetProducts(c *gin.Context) {
|
||||
data, err := ctrl.service.GetProducts()
|
||||
employeeID := c.GetInt("employee_id")
|
||||
if employeeID == 0 {
|
||||
common.Error(c, common.CodeUnauthorized, "未登录或token无效")
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
|
||||
data, hasMore, err := ctrl.service.GetProducts(employeeID, page, pageSize)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.Success(c, map[string]interface{}{
|
||||
"list": data,
|
||||
"list": data,
|
||||
"has_more": hasMore,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -294,3 +455,294 @@ func (ctrl *EmployeeController) UpdateArticleStatus(c *gin.Context) {
|
||||
|
||||
common.SuccessWithMessage(c, message, nil)
|
||||
}
|
||||
|
||||
// UpdateArticleContent 更新文案内容(标题、正文)
|
||||
func (ctrl *EmployeeController) UpdateArticleContent(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
articleID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "文案ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证标题和内容字数
|
||||
if len([]rune(req.Title)) > 20 {
|
||||
common.Error(c, common.CodeInvalidParams, "标题最多20字")
|
||||
return
|
||||
}
|
||||
if len([]rune(req.Content)) > 1000 {
|
||||
common.Error(c, common.CodeInvalidParams, "内容最多1000字")
|
||||
return
|
||||
}
|
||||
|
||||
err = ctrl.service.UpdateArticleContent(employeeID, articleID, req.Title, req.Content)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "更新成功", nil)
|
||||
}
|
||||
|
||||
// UpdatePublishRecord 编辑发布记录(修改标题、内容、图片、标签)
|
||||
func (ctrl *EmployeeController) UpdatePublishRecord(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
recordID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "记录ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req service.UpdatePublishRecordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证标题和内容字数
|
||||
if req.Title != nil && len([]rune(*req.Title)) > 20 {
|
||||
common.Error(c, common.CodeInvalidParams, "标题最多20字")
|
||||
return
|
||||
}
|
||||
if req.Content != nil && len([]rune(*req.Content)) > 1000 {
|
||||
common.Error(c, common.CodeInvalidParams, "内容最多1000字")
|
||||
return
|
||||
}
|
||||
|
||||
if err := ctrl.service.UpdatePublishRecord(employeeID, recordID, req); err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "更新成功", nil)
|
||||
}
|
||||
|
||||
// RepublishRecord 重新发布种草内容
|
||||
func (ctrl *EmployeeController) RepublishRecord(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
recordID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "记录ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
publishLink, err := ctrl.service.RepublishRecord(employeeID, recordID)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "重新发布成功", map[string]interface{}{
|
||||
"publish_link": publishLink,
|
||||
})
|
||||
}
|
||||
|
||||
// AddArticleImage 添加文案图片
|
||||
func (ctrl *EmployeeController) AddArticleImage(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
articleID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "文案ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ImageURL string `json:"image_url" binding:"required"`
|
||||
ImageThumbURL string `json:"image_thumb_url"`
|
||||
KeywordsName string `json:"keywords_name"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有缩略图,使用原图
|
||||
if req.ImageThumbURL == "" {
|
||||
req.ImageThumbURL = req.ImageURL
|
||||
}
|
||||
|
||||
image, err := ctrl.service.AddArticleImage(employeeID, articleID, req.ImageURL, req.ImageThumbURL, req.KeywordsName)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "添加成功", image)
|
||||
}
|
||||
|
||||
// DeleteArticleImage 删除文案图片
|
||||
func (ctrl *EmployeeController) DeleteArticleImage(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
imageID, err := strconv.Atoi(c.Param("imageId"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "图片ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
err = ctrl.service.DeleteArticleImage(employeeID, imageID)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "删除成功", nil)
|
||||
}
|
||||
|
||||
// UpdateArticleImagesOrder 更新文案图片排序
|
||||
func (ctrl *EmployeeController) UpdateArticleImagesOrder(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
articleID, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "文案ID参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ImageOrders []map[string]int `json:"image_orders" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
err = ctrl.service.UpdateArticleImagesOrder(employeeID, articleID, req.ImageOrders)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "更新成功", nil)
|
||||
}
|
||||
|
||||
// UploadImage 上传图片(支持base64和multipart/form-data)
|
||||
func (ctrl *EmployeeController) UploadImage(c *gin.Context) {
|
||||
// 尝试从表单获取文件
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err == nil {
|
||||
// 处理文件上传
|
||||
defer file.Close()
|
||||
|
||||
// 验证文件类型
|
||||
contentType := header.Header.Get("Content-Type")
|
||||
if !strings.HasPrefix(contentType, "image/") {
|
||||
common.Error(c, common.CodeInvalidParams, "只支持图片文件")
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到OSS
|
||||
imageURL, err := utils.UploadToOSS(file, header.Filename)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, fmt.Sprintf("上传失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "上传成功", map[string]interface{}{
|
||||
"image_url": imageURL,
|
||||
"image_thumb_url": imageURL, // 简化处理,缩略图与原图相同
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试介ase64获取
|
||||
var req struct {
|
||||
Base64 string `json:"base64" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "请上传文件或base64数据")
|
||||
return
|
||||
}
|
||||
|
||||
// 解析base64
|
||||
var imageData []byte
|
||||
if strings.Contains(req.Base64, "base64,") {
|
||||
// 移除data:image/xxx;base64,前缀
|
||||
parts := strings.Split(req.Base64, "base64,")
|
||||
if len(parts) != 2 {
|
||||
common.Error(c, common.CodeInvalidParams, "base64格式错误")
|
||||
return
|
||||
}
|
||||
imageData, err = base64.StdEncoding.DecodeString(parts[1])
|
||||
} else {
|
||||
imageData, err = base64.StdEncoding.DecodeString(req.Base64)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "base64解码失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 上传到OSS
|
||||
reader := bytes.NewReader(imageData)
|
||||
imageURL, err := utils.UploadToOSS(reader, "image.jpg")
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, fmt.Sprintf("上传失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "上传成功", map[string]interface{}{
|
||||
"image_url": imageURL,
|
||||
"image_thumb_url": imageURL,
|
||||
})
|
||||
}
|
||||
|
||||
// RevokeUserToken 禁用用户(撤销Token)
|
||||
func (ctrl *EmployeeController) RevokeUserToken(c *gin.Context) {
|
||||
// 只有管理员可以禁用用户
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
// 获取当前用户信息,检查是否为管理员
|
||||
var currentUser models.User
|
||||
if err := database.DB.Where("id = ?", employeeID).First(¤tUser).Error; err != nil {
|
||||
common.Error(c, common.CodeUnauthorized, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if currentUser.Role != "admin" {
|
||||
common.Error(c, common.CodeUnauthorized, "无权操作,只有管理员可以禁用用户")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
TargetUserID int `json:"target_user_id" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误:需要提供目标用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
// 不能禁用自己
|
||||
if req.TargetUserID == employeeID {
|
||||
common.Error(c, common.CodeInvalidParams, "不能禁用自己")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查目标用户是否存在
|
||||
var targetUser models.User
|
||||
if err := database.DB.Where("id = ?", req.TargetUserID).First(&targetUser).Error; err != nil {
|
||||
common.Error(c, common.CodeNotFound, "目标用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 撤销该用户的Token
|
||||
ctx := context.Background()
|
||||
if err := utils.RevokeToken(ctx, req.TargetUserID); err != nil {
|
||||
common.Error(c, common.CodeInternalError, fmt.Sprintf("禁用失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, fmt.Sprintf("已禁用用户 %s (手机号: %s),该用户需要重新登录", targetUser.Username, targetUser.Phone), nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user