commit
This commit is contained in:
@@ -2,7 +2,10 @@ package controller
|
||||
|
||||
import (
|
||||
"ai_xhs/common"
|
||||
"ai_xhs/config"
|
||||
"ai_xhs/service"
|
||||
"ai_xhs/utils"
|
||||
"context"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -98,3 +101,137 @@ func (ctrl *AuthController) PhoneLogin(c *gin.Context) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// PhonePasswordLogin 手机号密码登录
|
||||
func (ctrl *AuthController) PhonePasswordLogin(c *gin.Context) {
|
||||
var req struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 调用手机号密码登录服务
|
||||
token, employee, err := ctrl.authService.PhonePasswordLogin(req.Phone, req.Password)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户显示名称(优先使用真实姓名,其次用户名)
|
||||
displayName := employee.RealName
|
||||
if displayName == "" {
|
||||
displayName = employee.Username
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "登录成功", gin.H{
|
||||
"token": token,
|
||||
"employee": gin.H{
|
||||
"id": employee.ID,
|
||||
"name": displayName,
|
||||
"username": employee.Username,
|
||||
"real_name": employee.RealName,
|
||||
"phone": employee.Phone,
|
||||
"role": employee.Role,
|
||||
"enterprise_id": employee.EnterpriseID,
|
||||
"enterprise_name": employee.EnterpriseName,
|
||||
"is_bound_xhs": employee.IsBoundXHS,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// XHSPhoneCodeLogin 小红书手机号验证码登录
|
||||
func (ctrl *AuthController) XHSPhoneCodeLogin(c *gin.Context) {
|
||||
var req struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 调用手机号验证码登录服务
|
||||
token, employee, err := ctrl.authService.XHSPhoneCodeLogin(req.Phone, req.Code)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户显示名称(优先使用真实姓名,其次用户名)
|
||||
displayName := employee.RealName
|
||||
if displayName == "" {
|
||||
displayName = employee.Username
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "登录成功", gin.H{
|
||||
"token": token,
|
||||
"employee": gin.H{
|
||||
"id": employee.ID,
|
||||
"name": displayName,
|
||||
"username": employee.Username,
|
||||
"real_name": employee.RealName,
|
||||
"phone": employee.Phone,
|
||||
"role": employee.Role,
|
||||
"enterprise_id": employee.EnterpriseID,
|
||||
"enterprise_name": employee.EnterpriseName,
|
||||
"is_bound_xhs": employee.IsBoundXHS,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SendXHSVerificationCode 发送小红书手机号验证码
|
||||
func (ctrl *AuthController) SendXHSVerificationCode(c *gin.Context) {
|
||||
var req struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 预检查:验证手机号是否存在于user表中
|
||||
if err := ctrl.authService.CheckPhoneExists(req.Phone); err != nil {
|
||||
common.Error(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 调用短信服务发送验证码
|
||||
smsService := service.GetSmsService()
|
||||
code, err := smsService.SendVerificationCode(req.Phone)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 开发环境返回验证码,生产环境不返回
|
||||
response := gin.H{
|
||||
"message": "验证码已发送,5分钟内有效",
|
||||
}
|
||||
|
||||
if config.AppConfig.Server.Mode == "debug" {
|
||||
response["code"] = code // 仅开发环境返回
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "验证码已发送", response)
|
||||
}
|
||||
|
||||
// Logout 退出登录(删除Redis中的Token)
|
||||
func (ctrl *AuthController) Logout(c *gin.Context) {
|
||||
employeeID := c.GetInt("employee_id")
|
||||
|
||||
// 从Redis删除token
|
||||
ctx := context.Background()
|
||||
if err := utils.RevokeToken(ctx, employeeID); err != nil {
|
||||
// 即使删除失败也返回成功,因为token有过期时间
|
||||
common.SuccessWithMessage(c, "退出成功", nil)
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "退出成功", nil)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
104
go_backend/controller/feedback_controller.go
Normal file
104
go_backend/controller/feedback_controller.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"ai_xhs/common"
|
||||
"ai_xhs/models"
|
||||
"ai_xhs/service"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CreateFeedbackRequest 创建反馈请求
|
||||
type CreateFeedbackRequest struct {
|
||||
FeedbackType string `json:"feedback_type" binding:"required"`
|
||||
Description string `json:"description" binding:"required,max=500"`
|
||||
ContactInfo string `json:"contact_info"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
// FeedbackController 反馈控制器
|
||||
type FeedbackController struct {
|
||||
feedbackService *service.FeedbackService
|
||||
}
|
||||
|
||||
// NewFeedbackController 创建反馈控制器
|
||||
func NewFeedbackController(feedbackService *service.FeedbackService) *FeedbackController {
|
||||
return &FeedbackController{
|
||||
feedbackService: feedbackService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateFeedback 创建反馈
|
||||
func (fc *FeedbackController) CreateFeedback(c *gin.Context) {
|
||||
var req CreateFeedbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取员工ID
|
||||
employeeID, exists := c.Get("employee_id")
|
||||
if !exists {
|
||||
common.Error(c, common.CodeUnauthorized, "未登录")
|
||||
return
|
||||
}
|
||||
|
||||
feedback := &models.Feedback{
|
||||
FeedbackType: req.FeedbackType,
|
||||
Description: req.Description,
|
||||
ContactInfo: req.ContactInfo,
|
||||
Nickname: req.Nickname,
|
||||
CreatedUserID: employeeID.(int),
|
||||
Status: "待处理",
|
||||
}
|
||||
|
||||
if err := fc.feedbackService.CreateFeedback(feedback); err != nil {
|
||||
common.Error(c, common.CodeInternalError, "提交反馈失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "反馈提交成功", feedback)
|
||||
}
|
||||
|
||||
// GetFeedbackList 获取反馈列表
|
||||
func (fc *FeedbackController) GetFeedbackList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
feedbackType := c.Query("feedback_type")
|
||||
status := c.Query("status")
|
||||
|
||||
// 从上下文获取员工ID(仅查看自己的反馈)
|
||||
employeeID, exists := c.Get("employee_id")
|
||||
if !exists {
|
||||
common.Error(c, common.CodeUnauthorized, "未登录")
|
||||
return
|
||||
}
|
||||
|
||||
feedbacks, total, err := fc.feedbackService.GetFeedbackList(employeeID.(int), page, pageSize, feedbackType, status)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInternalError, "获取反馈列表失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.SuccessResponseWithPage(feedbacks, total, page, pageSize, "获取成功"))
|
||||
}
|
||||
|
||||
// GetFeedbackDetail 获取反馈详情
|
||||
func (fc *FeedbackController) GetFeedbackDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.Atoi(idStr)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeInvalidParams, "无效的反馈ID")
|
||||
return
|
||||
}
|
||||
|
||||
feedback, err := fc.feedbackService.GetFeedbackByID(id)
|
||||
if err != nil {
|
||||
common.Error(c, common.CodeNotFound, "反馈不存在")
|
||||
return
|
||||
}
|
||||
|
||||
common.SuccessWithMessage(c, "获取成功", feedback)
|
||||
}
|
||||
Reference in New Issue
Block a user