补齐相关日志日志
This commit is contained in:
@@ -67,13 +67,17 @@ func (s *AIRecommendService) Submit(ctx context.Context, userID int64, request *
|
||||
// 1. 抢占活跃位:抢不到说明该用户已有任务在跑,幂等返回它。
|
||||
acquired, runningTaskID, err := redisdao.AIRecommendTaskAcquire(ctx, userID, taskID, aiRecommendSlotTTL)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "ai recommend submit: acquire slot failed", "user_id", userID, "error", err)
|
||||
return nil, errcode.CacheError
|
||||
}
|
||||
if !acquired {
|
||||
if runningTaskID == "" {
|
||||
// 活跃位恰好在两次调用之间过期,让前端稍后重试即可。
|
||||
slog.InfoContext(ctx, "ai recommend submit: slot expired between calls, ask retry", "user_id", userID)
|
||||
return nil, errcode.TooManyRequests
|
||||
}
|
||||
slog.InfoContext(ctx, "ai recommend submit: task already running, return existing",
|
||||
"user_id", userID, "task_id", runningTaskID)
|
||||
return &dto.AIRecommendSubmitResponse{
|
||||
TaskID: runningTaskID,
|
||||
Status: AIRecommendTaskPending,
|
||||
@@ -84,8 +88,10 @@ func (s *AIRecommendService) Submit(ctx context.Context, userID int64, request *
|
||||
if qerr := consumeDailyQuota(ctx, userID, QuotaTypeAI, 1, "AI 推荐消耗"); qerr != nil {
|
||||
s.releaseSlot(context.WithoutCancel(ctx), userID, taskID)
|
||||
if qerr == errcode.QuotaInsufficient {
|
||||
slog.InfoContext(ctx, "ai recommend submit: quota insufficient", "user_id", userID)
|
||||
return nil, errcode.AIRecommendQuotaLimit
|
||||
}
|
||||
slog.ErrorContext(ctx, "ai recommend submit: consume quota failed", "user_id", userID, "error", qerr)
|
||||
return nil, qerr
|
||||
}
|
||||
|
||||
@@ -96,6 +102,8 @@ func (s *AIRecommendService) Submit(ctx context.Context, userID int64, request *
|
||||
Status: AIRecommendTaskPending,
|
||||
CreateTime: nowUnix(),
|
||||
}); serr != nil {
|
||||
slog.ErrorContext(ctx, "ai recommend submit: save task snapshot failed",
|
||||
"user_id", userID, "task_id", taskID, "error", serr)
|
||||
cleanupCtx := context.WithoutCancel(ctx)
|
||||
refundDailyQuota(cleanupCtx, userID, QuotaTypeAI, 1)
|
||||
s.releaseSlot(cleanupCtx, userID, taskID)
|
||||
@@ -106,6 +114,8 @@ func (s *AIRecommendService) Submit(ctx context.Context, userID int64, request *
|
||||
reqCopy := *request
|
||||
go s.runTask(taskID, userID, &reqCopy)
|
||||
|
||||
slog.InfoContext(ctx, "ai recommend submitted", "user_id", userID, "task_id", taskID)
|
||||
|
||||
return &dto.AIRecommendSubmitResponse{
|
||||
TaskID: taskID,
|
||||
Status: AIRecommendTaskPending,
|
||||
@@ -153,26 +163,39 @@ func (s *AIRecommendService) Save(ctx context.Context, userID int64, request *dt
|
||||
outfit, err := s.outfitDao.OutfitGetByUUID(ctx, request.OutfitUUID)
|
||||
if err != nil {
|
||||
if isNotFound(err) {
|
||||
slog.WarnContext(ctx, "ai recommend save: outfit not found",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID)
|
||||
return errcode.OutfitNotFound
|
||||
}
|
||||
slog.ErrorContext(ctx, "ai recommend save: query outfit failed",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID, "error", err)
|
||||
return errcode.DatabaseError
|
||||
}
|
||||
if outfit.UserID != userID {
|
||||
slog.InfoContext(ctx, "ai recommend save: permission denied",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID)
|
||||
return errcode.OutfitNoPermission
|
||||
}
|
||||
// 仅待保存状态可保存;已正常则幂等返回。
|
||||
if outfit.Status == OutfitStatusNormal {
|
||||
slog.DebugContext(ctx, "ai recommend save: outfit already saved, skip",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID)
|
||||
return nil
|
||||
}
|
||||
if outfit.Status != OutfitStatusUnsaved {
|
||||
slog.WarnContext(ctx, "ai recommend save: outfit status not saveable",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID, "status", outfit.Status)
|
||||
return errcode.OutfitUpdateFailed
|
||||
}
|
||||
|
||||
if uerr := s.outfitDao.OutfitUpdateByUUID(ctx, request.OutfitUUID, map[string]any{
|
||||
"status": OutfitStatusNormal,
|
||||
}); uerr != nil {
|
||||
slog.ErrorContext(ctx, "ai recommend save: update outfit failed",
|
||||
"user_id", userID, "outfit_uuid", request.OutfitUUID, "error", uerr)
|
||||
return errcode.OutfitUpdateFailed
|
||||
}
|
||||
slog.InfoContext(ctx, "ai recommend outfit saved", "user_id", userID, "outfit_uuid", request.OutfitUUID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -196,7 +219,7 @@ func (s *AIRecommendService) runTask(taskID string, userID int64, request *dto.A
|
||||
s.releaseSlot(context.WithoutCancel(ctx), userID, taskID)
|
||||
}()
|
||||
|
||||
out, bizErr := s.generate(ctx, userID, request)
|
||||
out, bizErr := s.generate(ctx, taskID, userID, request)
|
||||
if bizErr != nil {
|
||||
s.finishFailed(ctx, taskID, userID, bizErr.Code(), formatBizErr(bizErr))
|
||||
return
|
||||
@@ -206,11 +229,11 @@ func (s *AIRecommendService) runTask(taskID string, userID int64, request *dto.A
|
||||
TaskID: taskID,
|
||||
UserID: userID,
|
||||
Status: AIRecommendTaskSuccess,
|
||||
Result: mustMarshal(out),
|
||||
Result: mustMarshal(ctx, out),
|
||||
CreateTime: nowUnix(),
|
||||
}); serr != nil {
|
||||
// 结果已落库为主,快照写失败只影响前端轮询展示,记录即可。
|
||||
slog.ErrorContext(ctx, "save ai recommend task result failed",
|
||||
// 结果已落库为主,快照写失败只影响前端轮询展示,降为 Warn。
|
||||
slog.WarnContext(ctx, "save ai recommend task result failed",
|
||||
"task_id", taskID, "user_id", userID, "error", serr)
|
||||
}
|
||||
}
|
||||
@@ -249,13 +272,16 @@ func (s *AIRecommendService) releaseSlot(ctx context.Context, userID int64, task
|
||||
}
|
||||
|
||||
// generate 执行一次完整的推荐生成:查衣橱 → 取运势 → 调大模型 → 校验 → 落库。
|
||||
func (s *AIRecommendService) generate(ctx context.Context, userID int64, request *dto.AIRecommendCreateRequest) (*dto.AIRecommendCreateResponse, *errcode.Error) {
|
||||
func (s *AIRecommendService) generate(ctx context.Context, taskID string, userID int64, request *dto.AIRecommendCreateRequest) (*dto.AIRecommendCreateResponse, *errcode.Error) {
|
||||
// 1. 加载用户可用衣物(已成功处理)。
|
||||
clothes, cerr := s.processedClothes(ctx, userID)
|
||||
if cerr != nil {
|
||||
slog.ErrorContext(ctx, "ai recommend generate: load clothes failed",
|
||||
"user_id", userID, "task_id", taskID, "error", cerr)
|
||||
return nil, errcode.DatabaseError
|
||||
}
|
||||
if len(clothes) == 0 {
|
||||
slog.InfoContext(ctx, "ai recommend generate: no usable clothes", "user_id", userID, "task_id", taskID)
|
||||
return nil, errcode.AIRecommendFailed.WithDetails("衣橱暂无可用衣物")
|
||||
}
|
||||
|
||||
@@ -267,11 +293,13 @@ func (s *AIRecommendService) generate(ctx context.Context, userID int64, request
|
||||
client := tokenhub.NewClient()
|
||||
result, gerr := client.GenerateOutfitRecommendation(ctx, zodiac, fortune, clothes, request.Scene)
|
||||
if gerr != nil {
|
||||
slog.ErrorContext(ctx, "generate outfit recommendation failed", "user_id", userID, "error", gerr)
|
||||
slog.ErrorContext(ctx, "generate outfit recommendation failed", "user_id", userID, "task_id", taskID, "error", gerr)
|
||||
return nil, errcode.AIRecommendFailed.WithDetails("大模型调用失败")
|
||||
}
|
||||
result.ValidateClothesIDs(clothes)
|
||||
if len(result.Items) == 0 {
|
||||
slog.WarnContext(ctx, "ai recommend generate: no valid items after validation",
|
||||
"user_id", userID, "task_id", taskID)
|
||||
return nil, errcode.AIRecommendFailed.WithDetails("未生成有效搭配")
|
||||
}
|
||||
|
||||
@@ -302,6 +330,8 @@ func (s *AIRecommendService) generate(ctx context.Context, userID int64, request
|
||||
}
|
||||
return tx.Create(&items).Error
|
||||
}); err != nil {
|
||||
slog.ErrorContext(ctx, "ai recommend generate: save outfit failed",
|
||||
"user_id", userID, "task_id", taskID, "error", err)
|
||||
return nil, errcode.OutfitCreateFailed
|
||||
}
|
||||
|
||||
@@ -365,6 +395,9 @@ func (s *AIRecommendService) userFortune(ctx context.Context, userID int64) (str
|
||||
zodiac := ""
|
||||
if user, err := s.userDao.UserGetByID(ctx, userID); err == nil && user.BirthDay != 0 {
|
||||
zodiac = zodiacByUnix(user.BirthDay)
|
||||
} else if err != nil {
|
||||
slog.WarnContext(ctx, "ai recommend generate: get user for fortune failed, degrade",
|
||||
"user_id", userID, "error", err)
|
||||
}
|
||||
|
||||
fortune := &tokenhub.FortuneItem{Zodiac: zodiac}
|
||||
@@ -380,6 +413,9 @@ func (s *AIRecommendService) userFortune(ctx context.Context, userID int64) (str
|
||||
LuckyNumber: h.LuckyNumber,
|
||||
Description: h.DailyText,
|
||||
}
|
||||
} else if err != nil {
|
||||
slog.WarnContext(ctx, "ai recommend generate: get horoscope failed, degrade",
|
||||
"user_id", userID, "zodiac", zodiac, "error", err)
|
||||
}
|
||||
}
|
||||
return zodiac, fortune
|
||||
@@ -411,10 +447,10 @@ func formatBizErr(e *errcode.Error) string {
|
||||
}
|
||||
|
||||
// mustMarshal 序列化任务结果;失败时返回 nil(任务仍标记成功,前端可去「我的搭配」查看)。
|
||||
func mustMarshal(v any) json.RawMessage {
|
||||
func mustMarshal(ctx context.Context, v any) json.RawMessage {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
slog.Error("marshal ai recommend task result failed", "error", err)
|
||||
slog.ErrorContext(ctx, "marshal ai recommend task result failed", "error", err)
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
|
||||
Reference in New Issue
Block a user