package service import ( "context" "errors" "time" "git.x2erp.com/qdy/go-base/logger" "git.x2erp.com/qdy/go-db/factory/mongodb" "git.x2erp.com/qdy/go-svc-code/internal/model" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) // SessionStore 会话存储服务 type SessionStore struct { mongoFactory *mongodb.MongoDBFactory } // NewSessionStore 创建新的会话存储服务 func NewSessionStore(factory *mongodb.MongoDBFactory) *SessionStore { return &SessionStore{mongoFactory: factory} } // Create 创建会话 func (s *SessionStore) Create(ctx context.Context, session *model.Session) error { if session.ID == "" { return errors.New("session ID cannot be empty") } if session.Title == "" { return errors.New("session title cannot be empty") } if session.AgentName == "" { return errors.New("agent name cannot be empty") } // 设置时间戳 now := time.Now() if session.CreatedAt.IsZero() { session.CreatedAt = now } if session.UpdatedAt.IsZero() { session.UpdatedAt = now } // 设置默认状态为需求文档 if session.Status == "" { session.Status = model.StatusRequirementDocument } // 插入到MongoDB _, success := s.mongoFactory.InsertOneWithResult(model.Session{}.CollectionName(), session) if !success { logger.Error("创建会话失败", "session_id", session.ID, "title", session.Title) return errors.New("failed to create session") } logger.Debug("创建会话成功", "session_id", session.ID, "title", session.Title) return nil } // GetByID 根据ID获取会话 func (s *SessionStore) GetByID(ctx context.Context, id string) (*model.Session, error) { if id == "" { return nil, errors.New("session ID cannot be empty") } filter := bson.M{"_id": id} var result model.Session err := s.mongoFactory.FindOne(model.Session{}.CollectionName(), filter, &result) if err != nil { if err.Error() == "mongo: no documents in result" { return nil, nil // 未找到会话 } logger.Error("根据ID查询会话失败", "session_id", id, "error", err) return nil, err } return &result, nil } // GetByUser 根据用户ID获取会话 func (s *SessionStore) GetByUser(ctx context.Context, userID string) ([]*model.Session, error) { if userID == "" { return nil, errors.New("user ID cannot be empty") } filter := bson.M{"user_id": userID} var sessions []*model.Session err := s.mongoFactory.Find(model.Session{}.CollectionName(), filter, &sessions) if err != nil { logger.Error("根据用户查询会话失败", "user_id", userID, "error", err) return nil, err } return sessions, nil } // Update 更新会话(状态和标题) func (s *SessionStore) Update(ctx context.Context, id string, title, status string) error { if id == "" { return errors.New("session ID cannot be empty") } // 检查会话是否存在 session, err := s.GetByID(ctx, id) if err != nil { return err } if session == nil { return errors.New("session not found") } // 已发布的会话不能修改 if session.Status == model.StatusRelease { return errors.New("released session cannot be modified") } // 构建更新内容 update := bson.M{ "updated_at": time.Now(), } if title != "" { update["title"] = title } if status != "" { // 验证状态值 if !model.IsValidStatus(status) { return errors.New("invalid status value") } update["status"] = status } filter := bson.M{"_id": id} success, _ := s.mongoFactory.UpdateOne(model.Session{}.CollectionName(), filter, update) if !success { logger.Error("更新会话失败", "session_id", id) return errors.New("failed to update session") } logger.Debug("更新会话成功", "session_id", id) return nil } // Delete 删除会话 func (s *SessionStore) Delete(ctx context.Context, id string) error { if id == "" { return errors.New("session ID cannot be empty") } // 检查会话是否存在 session, err := s.GetByID(ctx, id) if err != nil { return err } if session == nil { return errors.New("session not found") } // 已发布的会话不能删除 if session.Status == model.StatusRelease { return errors.New("released session cannot be deleted") } filter := bson.M{"_id": id} success, deletedCount := s.mongoFactory.DeleteOne(model.Session{}.CollectionName(), filter) if !success { logger.Error("删除会话失败", "session_id", id) return errors.New("failed to delete session") } logger.Debug("删除会话成功", "session_id", id, "deleted_count", deletedCount) return nil } // ListQuery 列表查询参数 type ListQuery struct { Page int `form:"page" binding:"min=1"` // 页码,从1开始 PageSize int `form:"pageSize" binding:"min=1,max=100"` // 每页大小,最大100 Title string `form:"title,omitempty"` // 标题搜索(模糊匹配) Status string `form:"status,omitempty"` // 状态筛选 UserID string // 用户ID(从上下文中获取) TenantID string // 租户ID(从上下文中获取) } // ListResult 列表查询结果 type ListResult struct { Sessions []*model.Session `json:"sessions"` TotalCount int64 `json:"total_count"` Page int `json:"page"` PageSize int `json:"page_size"` TotalPages int `json:"total_pages"` } // List 分页查询会话列表 func (s *SessionStore) List(ctx context.Context, query *ListQuery) (*ListResult, error) { // 构建查询条件 filter := bson.M{ "user_id": query.UserID, "tenant_id": query.TenantID, } // 标题模糊匹配 if query.Title != "" { filter["title"] = bson.M{"$regex": primitive.Regex{Pattern: query.Title, Options: "i"}} } // 状态筛选 if query.Status != "" { filter["status"] = query.Status } // 查询所有匹配的记录(TODO: 优化为分页查询) var allSessions []*model.Session err := s.mongoFactory.Find(model.Session{}.CollectionName(), filter, &allSessions) if err != nil { logger.Error("查询会话列表失败", "error", err) return nil, err } // 按创建时间倒序排序 // 由于MongoDB驱动可能已经按_id排序,我们需要手动排序 // 这里简单反转切片(假设Find返回的顺序是插入顺序) // 实际应该使用聚合管道排序,但当前MongoDB工厂不支持 logger.Warn("会话列表查询使用内存分页,数据量大时性能可能受影响", "count", len(allSessions)) // 计算分页 if query.Page < 1 { query.Page = 1 } if query.PageSize < 1 { query.PageSize = 20 } else if query.PageSize > 100 { query.PageSize = 100 } totalCount := int64(len(allSessions)) skip := (query.Page - 1) * query.PageSize // 计算分页切片 var sessions []*model.Session if skip < len(allSessions) { end := skip + query.PageSize if end > len(allSessions) { end = len(allSessions) } sessions = allSessions[skip:end] } // 计算总页数 totalPages := int(totalCount) / query.PageSize if int(totalCount)%query.PageSize > 0 { totalPages++ } result := &ListResult{ Sessions: sessions, TotalCount: totalCount, Page: query.Page, PageSize: query.PageSize, TotalPages: totalPages, } return result, nil } // EnsureIndexes 确保集合索引 func (s *SessionStore) EnsureIndexes(ctx context.Context) error { // 主键索引(_id)已自动创建 // 用户ID索引(常用查询条件) userIDIndexKeys := bson.D{{Key: "user_id", Value: 1}} userIDSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), userIDIndexKeys) if !userIDSuccess { logger.Error("创建用户ID索引失败") return errors.New("failed to create user_id index") } // 租户ID索引 tenantIDIndexKeys := bson.D{{Key: "tenant_id", Value: 1}} tenantIDSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), tenantIDIndexKeys) if !tenantIDSuccess { logger.Error("创建租户ID索引失败") return errors.New("failed to create tenant_id index") } // 状态索引(用于筛选) statusIndexKeys := bson.D{{Key: "status", Value: 1}} statusSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), statusIndexKeys) if !statusSuccess { logger.Error("创建状态索引失败") return errors.New("failed to create status index") } // 创建时间索引(用于排序) createdAtIndexKeys := bson.D{{Key: "created_at", Value: -1}} createdAtSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), createdAtIndexKeys) if !createdAtSuccess { logger.Error("创建创建时间索引失败") return errors.New("failed to create created_at index") } // 复合索引:用户ID + 创建时间(常用查询组合) userCreatedIndexKeys := bson.D{ {Key: "user_id", Value: 1}, {Key: "created_at", Value: -1}, } userCreatedSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), userCreatedIndexKeys) if !userCreatedSuccess { logger.Error("创建用户-创建时间复合索引失败") return errors.New("failed to create user_id-created_at index") } logger.Debug("会话集合索引创建成功") return nil }