From 15b579d64a2edc12624a6a17852e2f3cb9d8f52a Mon Sep 17 00:00:00 2001 From: sjk <2513533895@qq.com> Date: Sat, 20 Dec 2025 01:05:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=96=87=E6=A1=88=E7=9A=84?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=B5=81=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go_backend/config/config.dev.yaml | 2 +- go_backend/config/config.prod.yaml | 2 +- go_backend/controller/employee_controller.go | 39 +++ go_backend/database/database.go | 1 + go_backend/models/models.go | 280 ++++++++++-------- go_backend/router/router.go | 3 + go_backend/service/auth_service.go | 94 +++++- go_backend/service/employee_service.go | 137 ++++++++- miniprogram/miniprogram/config/api.ts | 4 +- .../miniprogram/pages/articles/articles.ts | 111 ++++++- .../miniprogram/pages/articles/articles.wxml | 14 +- .../miniprogram/pages/articles/articles.wxss | 32 +- miniprogram/miniprogram/services/employee.ts | 9 + 13 files changed, 547 insertions(+), 181 deletions(-) diff --git a/go_backend/config/config.dev.yaml b/go_backend/config/config.dev.yaml index 3a1a2eb..8039d4e 100644 --- a/go_backend/config/config.dev.yaml +++ b/go_backend/config/config.dev.yaml @@ -27,7 +27,7 @@ xhs: python_service_url: "http://localhost:8000" # Python服务地址 scheduler: - enabled: true # 是否启用定时任务 + enabled: false # 是否启用定时任务 publish_cron: "* * * * * *" # 每1小时执行一次(开发环境测试用) max_concurrent: 2 # 最大并发发布数 publish_timeout: 300 # 发布超时时间(秒) diff --git a/go_backend/config/config.prod.yaml b/go_backend/config/config.prod.yaml index 77c6e39..d81bf90 100644 --- a/go_backend/config/config.prod.yaml +++ b/go_backend/config/config.prod.yaml @@ -27,7 +27,7 @@ xhs: python_service_url: "http://localhost:8000" # Python服务地址,生产环境请修改为实际地址 scheduler: - enabled: true # 是否启用定时任务 + enabled: false # 是否启用定时任务 publish_cron: "0 0 */2 * * *" # 每2小时执行一次(防封号策略) max_concurrent: 2 # 最大并发发布数 publish_timeout: 300 # 发布超时时间(秒) diff --git a/go_backend/controller/employee_controller.go b/go_backend/controller/employee_controller.go index 5cb75c4..61c75c5 100644 --- a/go_backend/controller/employee_controller.go +++ b/go_backend/controller/employee_controller.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" ) + type EmployeeController struct { service *service.EmployeeService } @@ -255,3 +256,41 @@ func (ctrl *EmployeeController) GetProducts(c *gin.Context) { "list": data, }) } + +// UpdateArticleStatus 更新文案状态(通过/拒绝) +func (ctrl *EmployeeController) UpdateArticleStatus(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 { + Status string `json:"status" binding:"required"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + common.Error(c, common.CodeInvalidParams, "参数错误") + return + } + + // 验证status值 + if req.Status != "approved" && req.Status != "rejected" { + common.Error(c, common.CodeInvalidParams, "status只能为approved或rejected") + return + } + + err = ctrl.service.UpdateArticleStatus(employeeID, articleID, req.Status) + if err != nil { + common.Error(c, common.CodeInternalError, err.Error()) + return + } + + message := "已通过" + if req.Status == "rejected" { + message = "已拒绝" + } + + common.SuccessWithMessage(c, message, nil) +} diff --git a/go_backend/database/database.go b/go_backend/database/database.go index 992aa15..8030af2 100644 --- a/go_backend/database/database.go +++ b/go_backend/database/database.go @@ -54,6 +54,7 @@ func AutoMigrate() error { &models.PublishRecord{}, &models.ArticleImage{}, &models.ArticleTag{}, + &models.Author{}, &models.Log{}, // &models.XHSAccount{}, // 不再使用,小红书信息直接存储在 ai_users 表中 ) diff --git a/go_backend/models/models.go b/go_backend/models/models.go index 9db1159..35d724c 100644 --- a/go_backend/models/models.go +++ b/go_backend/models/models.go @@ -6,24 +6,25 @@ import ( // Enterprise 企业表 type Enterprise struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - EnterpriseID string `gorm:"type:varchar(255);not null;default:''" json:"enterprise_id" comment:"企业ID"` - Name string `gorm:"type:varchar(200);not null;default:''" json:"name" comment:"企业名称"` - ShortName string `gorm:"type:varchar(100);not null;default:''" json:"short_name" comment:"企业简称"` - Icon string `gorm:"type:varchar(500);not null;default:''" json:"icon" comment:"企业图标URL"` - Phone string `gorm:"type:varchar(20);not null;default:'';uniqueIndex:uk_phone" json:"phone" comment:"登录手机号"` - Password string `gorm:"type:varchar(255);not null;default:''" json:"-" comment:"登录密码(加密存储)"` - Email string `gorm:"type:varchar(128);not null;default:''" json:"email" comment:"企业邮箱"` - Website string `gorm:"type:varchar(255);not null;default:''" json:"website" comment:"企业网站"` - Address string `gorm:"type:varchar(255);not null;default:''" json:"address" comment:"企业地址"` - Status string `gorm:"type:enum('active','disabled');not null;default:'active';index:idx_status" json:"status" comment:"状态"` - UsersTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"users_total" comment:"员工总数"` - ProductsTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"products_total" comment:"产品总数"` - ArticlesTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"articles_total" comment:"文章总数"` - ReleasedMonthTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"released_month_total" comment:"本月发布数量"` - LinkedToXHSNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"linked_to_xhs_num" comment:"绑定小红书"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` - UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID string `gorm:"type:varchar(255);not null;default:''" json:"enterprise_id" comment:"企业ID"` + Name string `gorm:"type:varchar(200);not null;default:''" json:"name" comment:"企业名称"` + ShortName string `gorm:"type:varchar(100);not null;default:''" json:"short_name" comment:"企业简称"` + Icon string `gorm:"type:varchar(500);not null;default:''" json:"icon" comment:"企业图标URL"` + Phone string `gorm:"type:varchar(20);not null;default:'';uniqueIndex:uk_phone" json:"phone" comment:"登录手机号"` + Password string `gorm:"type:varchar(255);not null;default:''" json:"-" comment:"登录密码(加密存储)"` + Email string `gorm:"type:varchar(128);not null;default:''" json:"email" comment:"企业邮箱"` + Website string `gorm:"type:varchar(255);not null;default:''" json:"website" comment:"企业网站"` + Address string `gorm:"type:varchar(255);not null;default:''" json:"address" comment:"企业地址"` + Status string `gorm:"type:enum('active','disabled');not null;default:'active';index:idx_status" json:"status" comment:"状态"` + UsersTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"users_total" comment:"员工总数"` + ProductsTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"products_total" comment:"产品总数"` + PublishedTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"published_total" comment:"文章发布总数"` + ArticlesTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"articles_total" comment:"文章总数"` + ReleasedMonthTotal int `gorm:"type:int(10) unsigned;not null;default:0" json:"released_month_total" comment:"本月发布数量"` + LinkedToXHSNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"linked_to_xhs_num" comment:"绑定小红书"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` } // User 用户账号表(原Employee,对应ai_users) @@ -37,15 +38,15 @@ type User struct { RealName string `gorm:"type:varchar(50)" json:"real_name" comment:"真实姓名"` Email string `gorm:"type:varchar(100)" json:"email" comment:"邮箱"` Phone string `gorm:"type:varchar(20)" json:"phone" comment:"手机号"` - WechatOpenID *string `gorm:"type:varchar(100);uniqueIndex:uk_wechat_openid" json:"wechat_openid,omitempty" comment:"微信OpenID"` - WechatUnionID *string `gorm:"type:varchar(100)" json:"wechat_unionid,omitempty" comment:"微信UnionID"` + WechatOpenID *string `gorm:"column:wechat_openid;type:varchar(100);uniqueIndex:uk_wechat_openid" json:"wechat_openid,omitempty" comment:"微信OpenID"` + WechatUnionID *string `gorm:"column:wechat_unionid;type:varchar(100)" json:"wechat_unionid,omitempty" comment:"微信UnionID"` XHSPhone string `gorm:"type:varchar(20);not null;default:''" json:"xhs_phone" comment:"小红书绑定手机号"` XHSAccount string `gorm:"type:varchar(255);not null;default:''" json:"xhs_account" comment:"小红书账号名称"` XHSCookie string `gorm:"type:text" json:"xhs_cookie" comment:"小红书Cookie"` IsBoundXHS int `gorm:"type:tinyint(1);not null;default:0;index:idx_is_bound_xhs" json:"is_bound_xhs" comment:"是否绑定小红书:0=未绑定,1=已绑定"` BoundAt *time.Time `json:"bound_at" comment:"绑定小红书的时间"` Department string `gorm:"type:varchar(50)" json:"department" comment:"部门"` - Role string `gorm:"type:enum('admin','editor','reviewer','publisher','each_title_reviewer');default:'editor'" json:"role" comment:"角色"` + Role string `gorm:"type:enum('admin','editor','reviewer','publisher','each_title_reviewer','enterprise');default:'editor'" json:"role" comment:"角色"` Status string `gorm:"type:enum('active','inactive','deleted');default:'active';index:idx_status" json:"status" comment:"状态"` CreatedAt time.Time `json:"created_at" comment:"创建时间"` UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` @@ -59,6 +60,7 @@ type Product struct { ID int `gorm:"primaryKey;autoIncrement" json:"id"` EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` Name string `gorm:"type:varchar(200);not null;default:''" json:"name" comment:"产品名称"` + TypeID int `gorm:"type:int unsigned;not null;default:0" json:"type_id" comment:"type_id类型ID"` TypeName string `gorm:"type:varchar(128);not null;default:''" json:"type_name" comment:"产品类型"` ImageURL string `gorm:"type:varchar(500);not null;default:''" json:"image_url" comment:"产品主图URL"` ImageThumbnailURL string `gorm:"type:varchar(500);not null;default:''" json:"image_thumbnail_url" comment:"缩略图URL"` @@ -72,35 +74,35 @@ type Product struct { // Article 文章表(原Copy,对应ai_articles) type Article struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - BatchID uint64 `gorm:"type:bigint unsigned;not null;default:0" json:"batch_id" comment:"批次ID"` - EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` - ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` - TopicTypeID int `gorm:"type:int unsigned;not null;default:0" json:"topic_type_id" comment:"topic类型ID"` - PromptWorkflowID int `gorm:"type:int unsigned;not null;default:0" json:"prompt_workflow_id" comment:"提示词工作流ID"` - Topic string `gorm:"type:varchar(255);not null;default:''" json:"topic" comment:"topic主题"` - Title string `gorm:"type:varchar(200);not null;default:''" json:"title" comment:"标题"` - Content string `gorm:"type:text" json:"content" comment:"文章内容"` - Department string `gorm:"type:varchar(255);not null;default:''" json:"department" comment:"部门"` - DepartmentIDs string `gorm:"column:departmentids;type:varchar(255);not null;default:''" json:"department_ids" comment:"部门IDs"` - AuthorID *int `json:"author_id" comment:"作者ID"` - AuthorName string `gorm:"type:varchar(100)" json:"author_name" comment:"作者名称"` - DepartmentID *int `json:"department_id" comment:"部门ID"` - DepartmentName string `gorm:"type:varchar(255)" json:"department_name" comment:"部门名称"` - CreatedUserID int `gorm:"not null;default:0" json:"created_user_id" comment:"创建用户ID"` - ReviewUserID *int `json:"review_user_id" comment:"审核用户ID"` - PublishUserID *int `json:"publish_user_id" comment:"发布用户ID"` - Status string `gorm:"type:enum('topic','cover_image','generate','generate_failed','draft','pending_review','approved','rejected','published_review','published','failed');default:'draft';index:idx_status" json:"status" comment:"状态"` - Channel int `gorm:"type:tinyint(1);not null;default:1" json:"channel" comment:"渠道:1=baidu|2=toutiao|3=weixin"` - ReviewComment string `gorm:"type:text" json:"review_comment" comment:"审核评论"` - PublishTime *time.Time `json:"publish_time" comment:"发布时间"` - BaijiahaoID string `gorm:"type:varchar(100)" json:"baijiahao_id" comment:"百家号ID"` - BaijiahaoStatus string `gorm:"type:varchar(50)" json:"baijiahao_status" comment:"百家号状态"` - WordCount int `gorm:"default:0" json:"word_count" comment:"字数统计"` - ImageCount int `gorm:"default:0" json:"image_count" comment:"图片数量"` - CozeTag string `gorm:"type:varchar(500)" json:"coze_tag" comment:"Coze生成的标签"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` - UpdatedAt time.Time `gorm:"index:idx_updated_at" json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + BatchID uint64 `gorm:"type:bigint unsigned;not null;default:0" json:"batch_id" comment:"批次ID"` + EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` + ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` + TopicTypeID int `gorm:"type:int unsigned;not null;default:0" json:"topic_type_id" comment:"topic类型ID"` + PromptWorkflowID int `gorm:"type:int unsigned;not null;default:0" json:"prompt_workflow_id" comment:"提示词工作流ID"` + Topic string `gorm:"type:varchar(255);not null;default:''" json:"topic" comment:"topic主题"` + Title string `gorm:"type:varchar(200);not null;default:''" json:"title" comment:"标题"` + Content string `gorm:"type:text" json:"content" comment:"文章内容"` + Department string `gorm:"type:varchar(255);not null;default:''" json:"department" comment:"部门"` + DepartmentIDs string `gorm:"column:departmentids;type:varchar(255);not null;default:''" json:"department_ids" comment:"部门IDs"` + AuthorID *int `json:"author_id" comment:"作者ID"` + AuthorName string `gorm:"type:varchar(100)" json:"author_name" comment:"作者名称"` + DepartmentID *int `json:"department_id" comment:"部门ID"` + DepartmentName string `gorm:"type:varchar(255)" json:"department_name" comment:"部门名称"` + CreatedUserID int `gorm:"not null;default:0" json:"created_user_id" comment:"创建用户ID"` + ReviewUserID *int `json:"review_user_id" comment:"审核用户ID"` + PublishUserID *int `json:"publish_user_id" comment:"发布用户ID"` + Status string `gorm:"type:enum('topic','cover_image','generate','generate_failed','draft','pending_review','assign_authors','approved','rejected','published_review','published','failed');default:'draft';index:idx_status" json:"status" comment:"状态"` + Channel int `gorm:"type:tinyint(1);not null;default:1" json:"channel" comment:"渠道:1=baidu|2=toutiao|3=weixin"` + ReviewComment string `gorm:"type:text" json:"review_comment" comment:"审核评论"` + PublishTime *time.Time `json:"publish_time" comment:"发布时间"` + BaijiahaoID string `gorm:"type:varchar(100)" json:"baijiahao_id" comment:"百家号ID"` + BaijiahaoStatus string `gorm:"type:varchar(50)" json:"baijiahao_status" comment:"百家号状态"` + WordCount int `gorm:"default:0" json:"word_count" comment:"字数统计"` + ImageCount int `gorm:"default:0" json:"image_count" comment:"图片数量"` + CozeTag string `gorm:"type:varchar(500)" json:"coze_tag" comment:"Coze生成的标签"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `gorm:"index:idx_updated_at" json:"updated_at" comment:"更新时间"` } // Copy 文案表别名,兼容旧代码 @@ -108,83 +110,85 @@ type Copy = Article // PublishRecord 发布记录表(对应ai_article_published_records) type PublishRecord struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - ArticleID *int `gorm:"index:idx_article_id" json:"article_id" comment:"文章ID"` - EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` - ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` - Topic string `gorm:"type:varchar(255);not null;default:''" json:"topic" comment:"topic主题"` - Title string `gorm:"type:varchar(200);not null;default:''" json:"title" comment:"标题"` - CreatedUserID int `gorm:"not null;default:0;index:idx_created_user_id" json:"created_user_id" comment:"创建用户ID"` - ReviewUserID *int `json:"review_user_id" comment:"审核用户ID"` - PublishUserID *int `json:"publish_user_id" comment:"发布用户ID"` - Status string `gorm:"type:enum('topic','cover_image','generate','generate_failed','draft','pending_review','approved','rejected','published_review','published','failed');default:'draft';index:idx_status" json:"status" comment:"状态"` - Channel int `gorm:"type:tinyint(1);not null;default:1" json:"channel" comment:"渠道:1=baidu|2=toutiao|3=weixin"` - ReviewComment string `gorm:"type:text" json:"review_comment" comment:"审核评论"` - PublishTime *time.Time `json:"publish_time" comment:"发布时间"` - PublishLink string `gorm:"type:varchar(128);not null;default:''" json:"publish_link" comment:"发布访问链接"` - WordCount int `gorm:"default:0" json:"word_count" comment:"字数统计"` - ImageCount int `gorm:"default:0" json:"image_count" comment:"图片数量"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` - UpdatedAt time.Time `gorm:"index:idx_updated_at" json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + ArticleID *int `gorm:"index:idx_article_id" json:"article_id" comment:"文章ID"` + EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` + ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` + Topic string `gorm:"type:varchar(255);not null;default:''" json:"topic" comment:"topic主题"` + Title string `gorm:"type:varchar(200);not null;default:''" json:"title" comment:"标题"` + CreatedUserID int `gorm:"not null;default:0;index:idx_created_user_id" json:"created_user_id" comment:"创建用户ID"` + ReviewUserID *int `json:"review_user_id" comment:"审核用户ID"` + PublishUserID *int `json:"publish_user_id" comment:"发布用户ID"` + Status string `gorm:"type:enum('topic','cover_image','generate','generate_failed','draft','pending_review','assign_authors','approved','rejected','published_review','published','failed');default:'draft';index:idx_status" json:"status" comment:"状态"` + Channel int `gorm:"type:tinyint(1);not null;default:1" json:"channel" comment:"渠道:1=baidu|2=toutiao|3=weixin"` + ReviewComment string `gorm:"type:text" json:"review_comment" comment:"审核评论"` + PublishTime *time.Time `json:"publish_time" comment:"发布时间"` + PublishLink string `gorm:"type:varchar(128);not null;default:''" json:"publish_link" comment:"发布访问链接"` + WordCount int `gorm:"default:0" json:"word_count" comment:"字数统计"` + ImageCount int `gorm:"default:0" json:"image_count" comment:"图片数量"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `gorm:"index:idx_updated_at" json:"updated_at" comment:"更新时间"` } // XHSAccount 小红书账号表(保持兼容) type XHSAccount struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - EmployeeID int `gorm:"not null;default:0;uniqueIndex:uk_employee_id" json:"employee_id"` - Employee User `gorm:"foreignKey:EmployeeID" json:"employee,omitempty"` - XHSUserID string `gorm:"type:varchar(100);not null;default:'';index:idx_xhs_user_id" json:"xhs_user_id"` - XHSNickname string `gorm:"type:varchar(100);not null;default:''" json:"xhs_nickname"` - XHSPhone string `gorm:"type:varchar(20);not null;default:''" json:"xhs_phone"` - XHSAvatar string `gorm:"type:varchar(500);not null;default:''" json:"xhs_avatar"` - FansCount int `gorm:"not null;default:0" json:"fans_count"` - NotesCount int `gorm:"not null;default:0" json:"notes_count"` - Cookies string `gorm:"type:text" json:"cookies"` - AccessToken string `gorm:"type:varchar(500);not null;default:''" json:"access_token"` - RefreshToken string `gorm:"type:varchar(500);not null;default:''" json:"refresh_token"` - TokenExpireAt *time.Time `json:"token_expire_at"` - Status string `gorm:"type:enum('active','expired','banned');default:'active';index:idx_status" json:"status"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EmployeeID int `gorm:"not null;default:0;uniqueIndex:uk_employee_id" json:"employee_id"` + Employee User `gorm:"foreignKey:EmployeeID" json:"employee,omitempty"` + XHSUserID string `gorm:"type:varchar(100);not null;default:'';index:idx_xhs_user_id" json:"xhs_user_id"` + XHSNickname string `gorm:"type:varchar(100);not null;default:''" json:"xhs_nickname"` + XHSPhone string `gorm:"type:varchar(20);not null;default:''" json:"xhs_phone"` + XHSAvatar string `gorm:"type:varchar(500);not null;default:''" json:"xhs_avatar"` + FansCount int `gorm:"not null;default:0" json:"fans_count"` + NotesCount int `gorm:"not null;default:0" json:"notes_count"` + Cookies string `gorm:"type:text" json:"cookies"` + AccessToken string `gorm:"type:varchar(500);not null;default:''" json:"access_token"` + RefreshToken string `gorm:"type:varchar(500);not null;default:''" json:"refresh_token"` + TokenExpireAt *time.Time `json:"token_expire_at"` + Status string `gorm:"type:enum('active','expired','banned');default:'active';index:idx_status" json:"status"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } // PromptWorkflow 提示词工作流表 type PromptWorkflow struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` - PromptWorkflowName string `gorm:"type:varchar(100);not null;default:''" json:"prompt_workflow_name" comment:"提示词工作流名称"` - AuthToken string `gorm:"type:varchar(100);not null;default:''" json:"auth_token" comment:"认证Token"` - WorkflowID string `gorm:"type:varchar(100);not null;default:'';index:idx_workflow_id" json:"workflow_id" comment:"工作流ID"` - Content string `gorm:"type:text" json:"content" comment:"提示词内容"` - UsageCount int `gorm:"not null;default:0" json:"usage_count" comment:"使用次数统计"` - CreatedUserID int `gorm:"not null;default:0" json:"created_user_id" comment:"创建用户ID"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` - UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` + PromptWorkflowName string `gorm:"type:varchar(100);not null;default:''" json:"prompt_workflow_name" comment:"提示词工作流名称"` + AuthToken string `gorm:"type:varchar(100);not null;default:''" json:"auth_token" comment:"认证Token"` + WorkflowID string `gorm:"type:varchar(100);not null;default:'';index:idx_workflow_id" json:"workflow_id" comment:"工作流ID"` + Content string `gorm:"type:text" json:"content" comment:"提示词内容"` + UsageCount int `gorm:"not null;default:0" json:"usage_count" comment:"使用次数统计"` + CreatedUserID int `gorm:"not null;default:0" json:"created_user_id" comment:"创建用户ID"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` } // ProductImage 产品图片库表 type ProductImage struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` - ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` - ImageID int `gorm:"not null;default:0" json:"image_id" comment:"图片ID"` - ImageName string `gorm:"type:varchar(255);not null;default:''" json:"image_name" comment:"图片名称"` - ImageURL string `gorm:"type:varchar(500);not null;default:''" json:"image_url" comment:"图片URL"` - ThumbnailURL string `gorm:"type:varchar(500);not null;default:''" json:"thumbnail_url" comment:"缩略图URL"` - TypeName string `gorm:"type:varchar(50);not null;default:''" json:"type_name" comment:"图片类型"` - Description string `gorm:"type:varchar(500);not null;default:''" json:"description" comment:"图片描述"` - FileSize *int64 `json:"file_size" comment:"文件大小"` - Width *int `json:"width" comment:"图片宽度"` - Height *int `json:"height" comment:"图片高度"` - UploadUserID int `gorm:"not null;default:0" json:"upload_user_id" comment:"上传用户ID"` - Status string `gorm:"type:enum('active','deleted');default:'active';index:idx_status" json:"status" comment:"状态"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"上传时间"` - UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` + ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` + ProductName string `gorm:"type:varchar(256);not null;default:''" json:"product_name" comment:"产品名称"` + ImageID int `gorm:"not null;default:0" json:"image_id" comment:"图片ID"` + ImageName string `gorm:"type:varchar(255);not null;default:''" json:"image_name" comment:"图片名称"` + ImageURL string `gorm:"type:varchar(500);not null;default:''" json:"image_url" comment:"图片URL"` + ThumbnailURL string `gorm:"type:varchar(500);not null;default:''" json:"thumbnail_url" comment:"缩略图URL"` + TypeName string `gorm:"type:varchar(50);not null;default:''" json:"type_name" comment:"图片类型"` + Description string `gorm:"type:varchar(500);not null;default:''" json:"description" comment:"图片描述"` + FileSize *int64 `json:"file_size" comment:"文件大小"` + Width *int `json:"width" comment:"图片宽度"` + Height *int `json:"height" comment:"图片高度"` + UploadUserID int `gorm:"not null;default:0" json:"upload_user_id" comment:"上传用户ID"` + Status string `gorm:"type:enum('active','deleted');default:'active';index:idx_status" json:"status" comment:"状态"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"上传时间"` + UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` } // ArticleImage 文章图片表 type ArticleImage struct { ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0" json:"enterprise_id" comment:"所属企业ID"` ArticleID int `gorm:"not null;default:0;index:idx_article_id" json:"article_id" comment:"文章ID"` ImageID int `gorm:"not null;default:0;index:idx_image_id" json:"image_id" comment:"图片ID"` ImageURL string `gorm:"type:varchar(500);not null;default:''" json:"image_url" comment:"图片URL"` @@ -202,23 +206,24 @@ type ArticleImage struct { // ArticleTag 文章标签表 type ArticleTag struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - ArticleID int `gorm:"not null;default:0;uniqueIndex:uk_article_tag" json:"article_id" comment:"文章ID"` - CozeTag string `gorm:"type:varchar(500)" json:"coze_tag" comment:"Coze生成的标签"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0" json:"enterprise_id" comment:"所属企业ID"` + ArticleID int `gorm:"not null;default:0;uniqueIndex:uk_article_tag" json:"article_id" comment:"文章ID"` + CozeTag string `gorm:"type:varchar(500)" json:"coze_tag" comment:"Coze生成的标签"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` } // DataStatistics 数据统计表 type DataStatistics struct { - ID int `gorm:"primaryKey;autoIncrement" json:"id"` - EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` - ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` - CumulativeReleasesNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"cumulative_releases_num" comment:"累计发布"` - PublishedTodayNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"published_today_num" comment:"今日发布"` - PublishedWeekNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"published_week_num" comment:"本周发布"` - ParticipatingEmployees int `gorm:"type:int(10) unsigned;not null;default:0" json:"participating_employees_num" comment:"参与员工"` - CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` - UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0;index:idx_enterprise_id" json:"enterprise_id" comment:"所属企业ID"` + ProductID int `gorm:"not null;default:0;index:idx_product_id" json:"product_id" comment:"关联产品ID"` + CumulativeReleasesNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"cumulative_releases_num" comment:"累计发布"` + PublishedTodayNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"published_today_num" comment:"今日发布"` + PublishedWeekNum int `gorm:"type:int(10) unsigned;not null;default:0" json:"published_week_num" comment:"本周发布"` + ParticipatingEmployees int `gorm:"type:int(10) unsigned;not null;default:0" json:"participating_employees_num" comment:"参与员工"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` } // Log 操作日志表(对应ai_logs) @@ -238,6 +243,31 @@ type Log struct { CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` } +// Author 作者表(对应ai_authors) +type Author struct { + ID int `gorm:"primaryKey;autoIncrement" json:"id"` + EnterpriseID int `gorm:"not null;default:0" json:"enterprise_id" comment:"所属企业ID"` + CreatedUserID int `gorm:"not null;default:0" json:"created_user_id" comment:"创建用户ID"` + Phone string `gorm:"type:varchar(20)" json:"phone" comment:"手机号"` + AuthorName string `gorm:"type:varchar(100);not null;default:''" json:"author_name" comment:"作者名称"` + AppID string `gorm:"type:varchar(127);not null;default:''" json:"app_id" comment:"应用ID"` + AppToken string `gorm:"type:varchar(127);not null;default:''" json:"app_token" comment:"应用Token"` + DepartmentID int `gorm:"not null;default:0" json:"department_id" comment:"部门ID"` + DepartmentName string `gorm:"type:varchar(255);not null;default:''" json:"department_name" comment:"部门名称"` + Department string `gorm:"type:varchar(50);not null;default:''" json:"department" comment:"部门"` + Title string `gorm:"type:varchar(50)" json:"title" comment:"职称"` + Hospital string `gorm:"type:varchar(100)" json:"hospital" comment:"医院"` + Specialty string `gorm:"type:text" json:"specialty" comment:"专业"` + ToutiaoCookie string `gorm:"type:text" json:"toutiao_cookie" comment:"头条Cookie"` + ToutiaoImagesCookie string `gorm:"type:text" json:"toutiao_images_cookie" comment:"头条图片Cookie"` + Introduction string `gorm:"type:text" json:"introduction" comment:"介绍"` + AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url" comment:"头像URL"` + Status string `gorm:"type:enum('active','inactive');default:'active';index:idx_status" json:"status" comment:"状态"` + Channel int `gorm:"type:tinyint(1);not null;default:1" json:"channel" comment:"渠道:1=baidu|2=toutiao|3=weixin"` + CreatedAt time.Time `gorm:"index:idx_created_at" json:"created_at" comment:"创建时间"` + UpdatedAt time.Time `json:"updated_at" comment:"更新时间"` +} + // TableName 指定表名(带ai_前缀) func (Enterprise) TableName() string { return "ai_enterprises" @@ -286,3 +316,7 @@ func (DataStatistics) TableName() string { func (Log) TableName() string { return "ai_logs" } + +func (Author) TableName() string { + return "ai_authors" +} diff --git a/go_backend/router/router.go b/go_backend/router/router.go index af9e8db..af237f8 100644 --- a/go_backend/router/router.go +++ b/go_backend/router/router.go @@ -66,6 +66,9 @@ func SetupRouter(r *gin.Engine) { // 10.9 检查小红书绑定与Cookie状态 employee.GET("/xhs/status", employeeCtrl.CheckXHSStatus) + + // 10.10 更新文案状态(通过/拒绝) + employee.POST("/article/:id/status", employeeCtrl.UpdateArticleStatus) } } } diff --git a/go_backend/service/auth_service.go b/go_backend/service/auth_service.go index e98bc43..f628431 100644 --- a/go_backend/service/auth_service.go +++ b/go_backend/service/auth_service.go @@ -12,6 +12,8 @@ import ( "io" "log" "net/http" + + "gorm.io/gorm" ) type AuthService struct{} @@ -141,33 +143,95 @@ func (s *AuthService) WechatLogin(code string, phone string, phoneCode string) ( } } - // 2. 根据OpenID查找或创建员工 + // 2. 根据手机号查找用户(手机号必填) + if phone == "" { + return "", nil, errors.New("请提供手机号") + } + var employee models.User - - // 优先通过OpenID查找(注意:使用IS NOT NULL过滤空值) - result := database.DB.Where("wechat_openid = ? AND wechat_openid IS NOT NULL", wxResp.OpenID).First(&employee) - + // 通过手机号查找员工 + result := database.DB.Where("phone = ? AND status = ?", phone, "active").First(&employee) if result.Error != nil { - // OpenID不存在,需要绑定OpenID - if phone == "" { - return "", nil, errors.New("首次登录请提供手机号") + // 手机号不存在,不允许登录 + return "", nil, errors.New("手机号不存在,请联系管理员添加") + } + + // 3. 检查微信绑定信息 + // 如果该用户已绑定微信信息,必须与当前登录的微信信息一致(单设备登录) + if employee.WechatOpenID != nil && *employee.WechatOpenID != "" { + // 已绑定微信,检查是否一致 + if *employee.WechatOpenID != wxResp.OpenID { + return "", nil, errors.New("该账号已在其他设备登录,请使用原设备登录或联系管理员") } - // 通过手机号查找员工 - result = database.DB.Where("phone = ? AND status = ?", phone, "active").First(&employee) - if result.Error != nil { - return "", nil, errors.New("员工不存在,请联系管理员添加") + // 如果有UnionID,也需要检查一致性 + if employee.WechatUnionID != nil && *employee.WechatUnionID != "" && wxResp.UnionID != "" { + if *employee.WechatUnionID != wxResp.UnionID { + return "", nil, errors.New("微信账号信息不匹配,请使用原设备登录") + } } - // 绑定OpenID和UnionID(使用指针) + log.Printf("[微信登录] 用户 %s (ID:%d) 微信验证通过", employee.Phone, employee.ID) + } else { + // 微信信息为空,说明是新用户首次登录,保存微信信息 + log.Printf("[微信登录] 新用户首次登录,绑定微信信息: OpenID=%s, UnionID=%s", wxResp.OpenID, wxResp.UnionID) + employee.WechatOpenID = &wxResp.OpenID if wxResp.UnionID != "" { employee.WechatUnionID = &wxResp.UnionID } - database.DB.Save(&employee) + + // 使用事务保存微信绑定信息并创建作者记录 + err := database.DB.Transaction(func(tx *gorm.DB) error { + // 1. 保存微信绑定信息 + if err := tx.Save(&employee).Error; err != nil { + return fmt.Errorf("保存微信绑定信息失败: %v", err) + } + + // 2. 检查是否已存在作者记录(通过手机号和企业ID) + var existingAuthor models.Author + result := tx.Where("phone = ? AND enterprise_id = ?", employee.Phone, employee.EnterpriseID).First(&existingAuthor) + + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + // 作者记录不存在,创建新记录 + author := models.Author{ + EnterpriseID: employee.EnterpriseID, + CreatedUserID: employee.ID, + Phone: employee.Phone, + AuthorName: employee.RealName, + Department: employee.Department, + Status: "active", + Channel: 3, // 3=weixin (微信小程序) + } + + // 如果真实姓名为空,使用用户名 + if author.AuthorName == "" { + author.AuthorName = employee.Username + } + + if err := tx.Create(&author).Error; err != nil { + return fmt.Errorf("创建作者记录失败: %v", err) + } + + log.Printf("[微信登录] 创建作者记录成功: ID=%d, Name=%s", author.ID, author.AuthorName) + } else if result.Error != nil { + // 其他数据库错误 + return fmt.Errorf("检查作者记录失败: %v", result.Error) + } else { + log.Printf("[微信登录] 作者记录已存在: ID=%d", existingAuthor.ID) + } + + return nil + }) + + if err != nil { + return "", nil, err + } + + log.Printf("[微信登录] 用户 %s (ID:%d) 微信绑定成功", employee.Phone, employee.ID) } - // 3. 生成JWT token + // 4. 生成JWT token token, err := utils.GenerateToken(employee.ID) if err != nil { return "", nil, fmt.Errorf("生成token失败: %v", err) diff --git a/go_backend/service/employee_service.go b/go_backend/service/employee_service.go index de55704..0baf978 100644 --- a/go_backend/service/employee_service.go +++ b/go_backend/service/employee_service.go @@ -43,12 +43,12 @@ func (s *EmployeeService) SendXHSCode(phone string) error { // 执行命令 err := cmd.Run() - + // 打印Python脚本的日志输出(stderr) if stderr.Len() > 0 { log.Printf("[Python日志-发送验证码] %s", stderr.String()) } - + if err != nil { return fmt.Errorf("执行Python脚本失败: %w, stderr: %s", err, stderr.String()) } @@ -80,7 +80,7 @@ func (s *EmployeeService) GetProfile(employeeID int) (*models.User, error) { if err != nil { return nil, err } - + // 如果已绑定小红书且有Cookie,验证Cookie是否有效 if employee.IsBoundXHS == 1 && employee.XHSCookie != "" { // 检查绑定时间,刚绑定的30秒内不验证(避免与绑定操作冲突) @@ -96,7 +96,7 @@ func (s *EmployeeService) GetProfile(employeeID int) (*models.User, error) { log.Printf("GetProfile - 用户%d有Cookie,长度: %d(已跳过自动验证)", employeeID, len(employee.XHSCookie)) // go s.VerifyCookieAndClear(employeeID) } - + return &employee, nil } @@ -164,7 +164,7 @@ func (s *EmployeeService) BindXHS(employeeID int, xhsPhone, code string) (string } else { log.Printf("绑定小红书 - 用户%d - 警告: cookiesData为nil", employeeID) } - + if cookiesJSON == "" { log.Printf("绑定小红书 - 用户%d - 错误: 未能获取到Cookie数据", employeeID) return "", errors.New("登录成功但未能获取到Cookie数据,请重试") @@ -195,7 +195,7 @@ func (s *EmployeeService) BindXHS(employeeID int, xhsPhone, code string) (string log.Printf("绑定小红书 - 用户%d - 数据库更新失败: %v", employeeID, err) return "", fmt.Errorf("更新员工绑定状态失败: %w", err) } - + log.Printf("绑定小红书 - 用户%d - 数据库更新成功", employeeID) // 提交事务 @@ -203,7 +203,7 @@ func (s *EmployeeService) BindXHS(employeeID int, xhsPhone, code string) (string log.Printf("绑定小红书 - 用户%d - 事务提交失败: %v", employeeID, err) return "", fmt.Errorf("提交事务失败: %w", err) } - + log.Printf("绑定小红书 - 用户%d - 绑定成功 - 账号: %s", employeeID, xhsNickname) return xhsNickname, nil } @@ -381,7 +381,7 @@ func (s *EmployeeService) VerifyCookieAndClear(employeeID int) error { if employee.IsBoundXHS == 0 || employee.XHSCookie == "" { return nil // 没有绑定或已无Cookie,直接返回 } - + // 检查绑定时间,刚绑定的30秒内不验证(避免与绑定操作冲突) if employee.BoundAt != nil { timeSinceBound := time.Since(*employee.BoundAt) @@ -492,20 +492,37 @@ func (s *EmployeeService) clearXHSCookie(employeeID int) error { return nil } -// GetAvailableCopies 获取可领取的文案列表 +// GetAvailableCopies 获取可领取的文案列表(根据作者ID、产品ID和状态筛选) func (s *EmployeeService) GetAvailableCopies(employeeID int, productID int) (map[string]interface{}, error) { + // 获取当前用户信息,查找对应的作者ID + var employee models.User + if err := database.DB.First(&employee, employeeID).Error; err != nil { + return nil, fmt.Errorf("获取用户信息失败: %w", err) + } + + // 查找对应的作者记录 + var author models.Author + if err := database.DB.Where("phone = ? AND enterprise_id = ?", employee.Phone, employee.EnterpriseID).First(&author).Error; err != nil { + return nil, fmt.Errorf("未找到对应的作者记录: %w", err) + } + // 获取产品信息 var product models.Product if err := database.DB.First(&product, productID).Error; err != nil { return nil, err } - // 获取该产品下所有可用文案(注意:新数据库中status有更多状态) + // 根据产品ID、作者ID和状态筛选文案 + // status = 'assign_authors' 表示已分配作者的文案 var copies []models.Article - if err := database.DB.Where("product_id = ? AND status IN ?", productID, []string{"draft", "approved"}).Order("created_at DESC").Find(&copies).Error; err != nil { - return nil, err + query := database.DB.Where("product_id = ? AND author_id = ? AND status = ?", productID, author.ID, "assign_authors") + + if err := query.Order("created_at DESC").Find(&copies).Error; err != nil { + return nil, fmt.Errorf("查询文案列表失败: %w", err) } + log.Printf("[获取文案列表] 用户ID=%d, 作者ID=%d, 产品ID=%d, 筛选到 %d 条文案", employeeID, author.ID, productID, len(copies)) + return map[string]interface{}{ "product": map[string]interface{}{ "id": product.ID, @@ -513,14 +530,106 @@ func (s *EmployeeService) GetAvailableCopies(employeeID int, productID int) (map "image": product.ImageURL, }, "copies": copies, + "author": map[string]interface{}{ + "id": author.ID, + "name": author.AuthorName, + }, }, nil } +// UpdateArticleStatus 更新文案状态(通过/拒绝) +func (s *EmployeeService) UpdateArticleStatus(employeeID int, articleID int, status string) error { + // 获取当前用户信息 + var employee models.User + if err := database.DB.First(&employee, employeeID).Error; err != nil { + return fmt.Errorf("获取用户信息失败: %w", err) + } + + // 查找对应的作者记录 + var author models.Author + if err := database.DB.Where("phone = ? AND enterprise_id = ?", employee.Phone, employee.EnterpriseID).First(&author).Error; err != nil { + return fmt.Errorf("未找到对应的作者记录: %w", err) + } + + // 获取文案信息 + var article models.Article + if err := database.DB.First(&article, articleID).Error; err != nil { + return fmt.Errorf("获取文案信息失败: %w", err) + } + + // 验证文案是否属于当前作者且状态为assign_authors + if article.AuthorID == nil || *article.AuthorID != author.ID { + return fmt.Errorf("无权操作此文案") + } + + if article.Status != "assign_authors" { + return fmt.Errorf("文案当前状态为%s,无法操作", article.Status) + } + + // 开启事务 + tx := database.DB.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + now := time.Now() + + // 更新文案状态 + err := tx.Model(&article).Updates(map[string]interface{}{ + "status": status, + "review_user_id": employeeID, + }).Error + if err != nil { + tx.Rollback() + return fmt.Errorf("更新状态失败: %w", err) + } + + // 创建操作记录到 ai_article_published_records + actionType := "通过" + if status == "rejected" { + actionType = "拒绝" + } + + record := models.PublishRecord{ + ArticleID: &article.ID, + EnterpriseID: employee.EnterpriseID, + ProductID: article.ProductID, + Topic: article.Topic, + Title: article.Title, + CreatedUserID: article.CreatedUserID, + ReviewUserID: &employeeID, + Status: status, + PublishTime: &now, + WordCount: article.WordCount, + ImageCount: article.ImageCount, + Channel: article.Channel, + ReviewComment: fmt.Sprintf("作者%s于%s", actionType, now.Format("2006-01-02 15:04:05")), + } + + if err := tx.Create(&record).Error; err != nil { + tx.Rollback() + return fmt.Errorf("创建操作记录失败: %w", err) + } + + // 提交事务 + if err := tx.Commit().Error; err != nil { + return fmt.Errorf("提交事务失败: %w", err) + } + + log.Printf("[更新文案状态] 用户ID=%d, 作者ID=%d, 文案ID=%d, 状态: assign_authors => %s, 记录ID=%d", employeeID, author.ID, articleID, status, record.ID) + return nil +} + // ClaimCopy 领取文案(新版本:直接返回文案信息,不再创建领取记录) func (s *EmployeeService) ClaimCopy(employeeID int, copyID int, productID int) (map[string]interface{}, error) { // 检查文案是否存在且可用(注意:新数据库中status有更多状态) + // assign_authors: 已分配给作者,可以直接发布 + // draft: 草稿状态 + // approved: 已审核通过 var copy models.Article - if err := database.DB.Where("id = ? AND status IN ?", copyID, []string{"draft", "approved"}).First(©).Error; err != nil { + if err := database.DB.Where("id = ? AND status IN ?", copyID, []string{"draft", "approved", "assign_authors"}).First(©).Error; err != nil { return nil, errors.New("文案不存在或不可用") } @@ -542,7 +651,7 @@ func (s *EmployeeService) ClaimCopy(employeeID int, copyID int, productID int) ( func (s *EmployeeService) ClaimRandomCopy(employeeID int, productID int) (map[string]interface{}, error) { // 查询未领取的可用文案(注意:新数据库中status有更多状态) var copy models.Article - query := database.DB.Where("product_id = ? AND status IN ?", productID, []string{"draft", "approved"}) + query := database.DB.Where("product_id = ? AND status IN ?", productID, []string{"draft", "approved", "assign_authors"}) if err := query.Order("RAND()").First(©).Error; err != nil { return nil, errors.New("暂无可领取的文案") diff --git a/miniprogram/miniprogram/config/api.ts b/miniprogram/miniprogram/config/api.ts index 1711c60..944c548 100644 --- a/miniprogram/miniprogram/config/api.ts +++ b/miniprogram/miniprogram/config/api.ts @@ -25,8 +25,8 @@ interface EnvConfig { const API_CONFIG: Record = { // 开发环境 - 本地开发 dev: { - baseURL: 'http://localhost:8080', // 本地Go服务 - pythonURL: 'http://localhost:8000', // 本地Python服务 + baseURL: 'http://192.168.17.127:8080', // 本地Go服务 + pythonURL: 'http://192.168.17.127:8000', // 本地Python服务 timeout: 90000 }, diff --git a/miniprogram/miniprogram/pages/articles/articles.ts b/miniprogram/miniprogram/pages/articles/articles.ts index 0cd958b..25a24fe 100644 --- a/miniprogram/miniprogram/pages/articles/articles.ts +++ b/miniprogram/miniprogram/pages/articles/articles.ts @@ -12,7 +12,8 @@ Page({ currentCopy: null as any, // 当前显示的文案 loading: false, claiming: false, // 领取中 - publishing: false // 发布中 + publishing: false, // 发布中 + rejecting: false // 拒绝中 }, onLoad(options: any) { @@ -150,7 +151,7 @@ Page({ // 换一个文案 changeArticle() { - if (this.data.claiming || this.data.publishing) return; + if (this.data.claiming || this.data.publishing || this.data.rejecting) return; const { allCopies, currentIndex } = this.data; @@ -162,19 +163,25 @@ Page({ return; } - // 切换到下一个文案 - const nextIndex = (currentIndex + 1) % allCopies.length; - - this.setData({ - currentIndex: nextIndex, - currentCopy: allCopies[nextIndex] + // 显示加载动画 + wx.showLoading({ + title: '加载中...', + mask: true }); - wx.showToast({ - title: `${nextIndex + 1}/${allCopies.length}`, - icon: 'none', - duration: 1000 - }); + // 模拟加载效果,让切换更平滑 + setTimeout(() => { + // 切换到下一个文案 + const nextIndex = (currentIndex + 1) % allCopies.length; + + this.setData({ + currentIndex: nextIndex, + currentCopy: allCopies[nextIndex] + }); + + // 隐藏加载动画 + wx.hideLoading(); + }, 300); }, // 一键发布(先领取,再发布) @@ -281,6 +288,84 @@ Page({ }; }, + // 拒绝文案 + async rejectArticle() { + if (!this.data.currentCopy) { + wx.showToast({ + title: '请先选择文案', + icon: 'none' + }); + return; + } + + wx.showModal({ + title: '确认拒绝', + content: '确定要拒绝这篇文案吗?', + confirmText: '拒绝', + confirmColor: '#ff4d4f', + success: async (res) => { + if (res.confirm) { + await this.doRejectArticle(); + } + } + }); + }, + + // 执行拒绝操作 + async doRejectArticle() { + this.setData({ rejecting: true }); + + try { + const response = await EmployeeService.updateArticleStatus( + this.data.currentCopy.id, + 'rejected' + ); + + if (response.code === 200) { + wx.showToast({ + title: '已拒绝', + icon: 'success', + duration: 1500 + }); + + // 从列表中移除当前文案 + const { allCopies, currentIndex } = this.data; + allCopies.splice(currentIndex, 1); + + // 更新显示 + if (allCopies.length > 0) { + const newIndex = currentIndex >= allCopies.length ? 0 : currentIndex; + this.setData({ + allCopies, + currentIndex: newIndex, + currentCopy: allCopies[newIndex], + rejecting: false + }); + } else { + // 没有更多文案了 + this.setData({ + allCopies: [], + currentCopy: null, + rejecting: false + }); + + setTimeout(() => { + wx.navigateBack(); + }, 1500); + } + } else { + throw new Error(response.message || '拒绝失败'); + } + } catch (error: any) { + console.error('拒绝失败:', error); + this.setData({ rejecting: false }); + wx.showToast({ + title: error.message || '操作失败', + icon: 'none' + }); + } + }, + // 分享到朋友圈 onShareTimeline() { return { diff --git a/miniprogram/miniprogram/pages/articles/articles.wxml b/miniprogram/miniprogram/pages/articles/articles.wxml index 2f27b8f..0cffce8 100644 --- a/miniprogram/miniprogram/pages/articles/articles.wxml +++ b/miniprogram/miniprogram/pages/articles/articles.wxml @@ -53,16 +53,24 @@ +