feat: 生成接口支持场景(occasion,默认通勤,评分/LLM 依据)
This commit is contained in:
@@ -9,6 +9,7 @@ type OutfitGenerateReq struct {
|
||||
StartDate string `v:"required|date" json:"start_date"`
|
||||
EndDate string `v:"required|date" json:"end_date"`
|
||||
Location string `v:"required" json:"location"`
|
||||
Occasion string `v:"in:通勤,约会,聚会,运动" json:"occasion"`
|
||||
}
|
||||
|
||||
type OutfitGenerateRes struct {
|
||||
|
||||
@@ -41,10 +41,18 @@ func (s *outfitService) Generate(ctx context.Context, userId int64, req *dto.Out
|
||||
return 0, err
|
||||
}
|
||||
// 异步执行:传入独立 ctx(请求结束不中断任务)
|
||||
go runGenerateTask(gctx.New(), taskId, userId)
|
||||
go runGenerateTask(gctx.New(), taskId, userId, normalizeOccasion(req.Occasion))
|
||||
return taskId, nil
|
||||
}
|
||||
|
||||
// normalizeOccasion 空场景默认通勤(评分引擎按 通勤/约会/聚会/运动 匹配)
|
||||
func normalizeOccasion(o string) string {
|
||||
if o == "" {
|
||||
return "通勤"
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// StartWorker 服务启动时恢复未完成任务(标记失败,避免重启后重复消耗 LLM 费用)
|
||||
func (s *outfitService) StartWorker(ctx context.Context) {
|
||||
tasks, err := dao.OutfitGenTask.ListUnfinished(ctx)
|
||||
@@ -59,7 +67,7 @@ func (s *outfitService) StartWorker(ctx context.Context) {
|
||||
}
|
||||
|
||||
// runGenerateTask 任务核心流程:planning → scoring → done/failed
|
||||
func runGenerateTask(ctx context.Context, taskId, userId int64) {
|
||||
func runGenerateTask(ctx context.Context, taskId, userId int64, occasion string) {
|
||||
setTask := func(status, msg string) {
|
||||
_ = dao.OutfitGenTask.UpdateStatus(ctx, taskId, status, msg)
|
||||
}
|
||||
@@ -109,7 +117,6 @@ func runGenerateTask(ctx context.Context, taskId, userId int64) {
|
||||
// 4. LLM 规划(1 次调用)
|
||||
hairstyles := hairstyleListText(ctx)
|
||||
bodyDesc := bodyDescText(ctx, userId)
|
||||
occasion := "通勤"
|
||||
candidates := make([]agent.CandidateData, 0, len(sets)*3)
|
||||
for si, set := range sets {
|
||||
for _, it := range set.Items {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"slogan-agent/styleagent/model/dto"
|
||||
)
|
||||
|
||||
func TestGenerateReqOccasionValidation(t *testing.T) {
|
||||
valid := []string{"", "通勤", "约会", "聚会", "运动"}
|
||||
for _, v := range valid {
|
||||
req := &dto.OutfitGenerateReq{
|
||||
StartDate: "2026-08-01", EndDate: "2026-08-02",
|
||||
Location: "上海", Occasion: v,
|
||||
}
|
||||
if err := g.Validator().Data(req).Run(context.Background()); err != nil {
|
||||
t.Fatalf("occasion %q 应通过校验: %v", v, err)
|
||||
}
|
||||
}
|
||||
req := &dto.OutfitGenerateReq{
|
||||
StartDate: "2026-08-01", EndDate: "2026-08-02",
|
||||
Location: "上海", Occasion: "随便",
|
||||
}
|
||||
if err := g.Validator().Data(req).Run(context.Background()); err == nil {
|
||||
t.Fatal("非法 occasion 应被拒绝")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeOccasion(t *testing.T) {
|
||||
if got := normalizeOccasion(""); got != "通勤" {
|
||||
t.Fatalf("空场景应默认通勤, got %q", got)
|
||||
}
|
||||
if got := normalizeOccasion("约会"); got != "约会" {
|
||||
t.Fatalf("非空场景应原样返回, got %q", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user