package session import ( "context" "fmt" "gitea.com/red-future/common/beans" "gitea.com/red-future/common/utils" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" "prompts-core/common/util" "prompts-core/dao" "prompts-core/model/dto" "prompts-core/model/entity" ) // Callback 会话回调 func Callback(ctx context.Context, req *dto.SessionCallbackReq) (*dto.SessionCallbackRes, error) { req.Messages["role"] = "assistant" // 1) 更新 DB _, err := dao.ComposeSession.Update(ctx, &entity.ComposeSession{ SQLBaseDO: beans.SQLBaseDO{Id: req.EpicycleId}, ResponseContent: req.Messages, }) if err != nil { g.Log().Errorf(ctx, "[会话回调] 更新数据库失败 epicycleId=%d err=%v", req.EpicycleId, err) return nil, fmt.Errorf("更新数据库失败: %w", err) } // 2) 查询完整记录 session, err := dao.ComposeSession.Get(ctx, &entity.ComposeSession{ SQLBaseDO: beans.SQLBaseDO{Id: req.EpicycleId}, }) if err != nil || session == nil { return nil, fmt.Errorf("会话不存在: epicycleId=%d", req.EpicycleId) } // 3) 写入 Redis if err = SaveToRedis(ctx, session.TenantId, session.SessionId, &dto.HistoryRound{ Id: session.Id, User: session.RequestContent, Assistant: req.Messages, CreatedAt: gconv.String(session.CreatedAt), }); err != nil { return nil, fmt.Errorf("redis存储失败: %w", err) } // 4) 返回 g.Log().Infof(ctx, "[会话回调] 存储成功 sessionId=%s id=%d", session.SessionId, session.Id) return &dto.SessionCallbackRes{Status: true, SessionId: session.SessionId}, nil } // GetHistoryMessages 获取历史消息 func GetHistoryMessages(ctx context.Context, req *dto.GetHistoryMessagesReq) (*dto.GetHistoryMessagesRes, error) { user, err := utils.GetUserInfo(ctx) if err != nil { return nil, err } // 1) Redis redisRounds, err := GetFromRedis(ctx, user.TenantId, req.SessionId) if err == nil && len(redisRounds) > 0 { g.Log().Debugf(ctx, "[历史消息] Redis命中 sessionId=%s count=%d", req.SessionId, len(redisRounds)) return &dto.GetHistoryMessagesRes{Messages: parseHistoryRounds(redisRounds)}, nil } // 2) DB maxRounds := util.GetMaxRounds(ctx) sessions, _, err := dao.ComposeSession.List(ctx, &entity.ComposeSession{ SQLBaseDO: beans.SQLBaseDO{Creator: user.UserName}, SessionId: req.SessionId, }, 1, maxRounds) if err != nil { return nil, fmt.Errorf("DB获取历史失败: %w", err) } if len(sessions) == 0 { return &dto.GetHistoryMessagesRes{Messages: []dto.HistoryRound{}}, nil } // 3) 转换 + 异步回种 rounds := sessionsToHistoryRounds(sessions) go asyncCacheToRedis(context.WithoutCancel(ctx), user.TenantId, req.SessionId, sessions) return &dto.GetHistoryMessagesRes{Messages: rounds}, nil } // parseHistoryRounds Redis 数据转为 HistoryRound func parseHistoryRounds(redisRounds []map[string]any) []dto.HistoryRound { rounds := make([]dto.HistoryRound, 0, len(redisRounds)) for _, r := range redisRounds { round := dto.HistoryRound{ Id: gconv.Int64(r["id"]), CreatedAt: gconv.String(r["createdAt"]), } if user, ok := r["user"].(map[string]any); ok { round.User = user } if assistant, ok := r["assistant"].(map[string]any); ok { round.Assistant = assistant } rounds = append(rounds, round) } return rounds } // sessionsToHistoryRounds DB 数据转为 HistoryRound func sessionsToHistoryRounds(sessions []*entity.ComposeSession) []dto.HistoryRound { rounds := make([]dto.HistoryRound, 0, len(sessions)) for _, s := range sessions { reqMsgs := util.ConvertToMessages(s.RequestContent) respMsgs := util.ConvertToMessages(s.ResponseContent) round := dto.HistoryRound{ Id: s.Id, CreatedAt: gconv.String(s.CreatedAt), } if len(reqMsgs) > 0 { round.User = reqMsgs[0] } if len(respMsgs) > 0 { if respMsgs[0]["role"] == nil { respMsgs[0]["role"] = "assistant" } round.Assistant = respMsgs[0] } rounds = append(rounds, round) } return rounds } // DeleteSession 删除会话 func DeleteSession(ctx context.Context, req *dto.DeleteSessionReq) (*dto.DeleteSessionRes, error) { hasMsgID := len(req.MsgIds) > 0 && req.MsgIds[0] > 0 deleteReq := &entity.ComposeSession{ SessionId: req.SessionId, NodeId: req.NodeId, } if hasMsgID { deleteReq.Id = req.MsgIds[0] } if _, err := dao.ComposeSession.Delete(ctx, deleteReq); err != nil { return nil, fmt.Errorf("DB删除失败: %w", err) } if hasMsgID { if err := DeleteSingleMessage(ctx, req.TenantId, req.SessionId, req.MsgIds[0]); err != nil { g.Log().Warningf(ctx, "[删除会话] Redis删除单条失败 msgID=%d err=%v", req.MsgIds[0], err) } } else { if err := DeleteSessionHistory(ctx, req.TenantId, req.SessionId); err != nil { g.Log().Warningf(ctx, "[删除会话] Redis删除失败 sessionId=%s err=%v", req.SessionId, err) } } return &dto.DeleteSessionRes{Ok: true}, nil } // ============================================ // 内部方法 // ============================================ func extractMessagesFromSessions(sessions []*entity.ComposeSession) []map[string]any { var messages []map[string]any for i := len(sessions) - 1; i >= 0; i-- { appendRoleMessages(sessions[i].RequestContent, "user", &messages) appendRoleMessages(sessions[i].ResponseContent, "assistant", &messages) } return messages } func appendRoleMessages(content any, defaultRole string, messages *[]map[string]any) { msgs := util.ConvertToMessages(content) for _, m := range msgs { if m["role"] == nil || gconv.String(m["role"]) == "" { m["role"] = defaultRole } *messages = append(*messages, m) } } // asyncCacheToRedis 异步缓存会话数据到 Redis func asyncCacheToRedis(ctx context.Context, tenantID uint64, sessionID string, sessions []*entity.ComposeSession) { for _, s := range sessions { reqMsgs := util.ConvertToMessages(s.RequestContent) respMsgs := util.ConvertToMessages(s.ResponseContent) if len(reqMsgs) > 0 || len(respMsgs) > 0 { _ = SaveToRedis(ctx, tenantID, sessionID, &dto.HistoryRound{ Id: s.Id, User: s.RequestContent, Assistant: s.ResponseContent, CreatedAt: gconv.String(s.CreatedAt), }) } } }