No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

session_store.go 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. package service
  2. import (
  3. "context"
  4. "errors"
  5. "time"
  6. "git.x2erp.com/qdy/go-base/logger"
  7. "git.x2erp.com/qdy/go-db/factory/mongodb"
  8. "git.x2erp.com/qdy/go-svc-code/internal/model"
  9. "go.mongodb.org/mongo-driver/bson"
  10. "go.mongodb.org/mongo-driver/bson/primitive"
  11. )
  12. // SessionStore 会话存储服务
  13. type SessionStore struct {
  14. mongoFactory *mongodb.MongoDBFactory
  15. }
  16. // NewSessionStore 创建新的会话存储服务
  17. func NewSessionStore(factory *mongodb.MongoDBFactory) *SessionStore {
  18. return &SessionStore{mongoFactory: factory}
  19. }
  20. // Create 创建会话
  21. func (s *SessionStore) Create(ctx context.Context, session *model.Session) error {
  22. if session.ID == "" {
  23. return errors.New("session ID cannot be empty")
  24. }
  25. if session.Title == "" {
  26. return errors.New("session title cannot be empty")
  27. }
  28. if session.AgentName == "" {
  29. return errors.New("agent name cannot be empty")
  30. }
  31. // 设置时间戳
  32. now := time.Now()
  33. if session.CreatedAt.IsZero() {
  34. session.CreatedAt = now
  35. }
  36. if session.UpdatedAt.IsZero() {
  37. session.UpdatedAt = now
  38. }
  39. // 设置默认状态为需求文档
  40. if session.Status == "" {
  41. session.Status = model.StatusRequirementDocument
  42. }
  43. // 插入到MongoDB
  44. _, success := s.mongoFactory.InsertOneWithResult(model.Session{}.CollectionName(), session)
  45. if !success {
  46. logger.Error("创建会话失败", "session_id", session.ID, "title", session.Title)
  47. return errors.New("failed to create session")
  48. }
  49. logger.Debug("创建会话成功", "session_id", session.ID, "title", session.Title)
  50. return nil
  51. }
  52. // GetByID 根据ID获取会话
  53. func (s *SessionStore) GetByID(ctx context.Context, id string) (*model.Session, error) {
  54. if id == "" {
  55. return nil, errors.New("session ID cannot be empty")
  56. }
  57. filter := bson.M{"_id": id}
  58. var result model.Session
  59. err := s.mongoFactory.FindOne(model.Session{}.CollectionName(), filter, &result)
  60. if err != nil {
  61. if err.Error() == "mongo: no documents in result" {
  62. return nil, nil // 未找到会话
  63. }
  64. logger.Error("根据ID查询会话失败", "session_id", id, "error", err)
  65. return nil, err
  66. }
  67. return &result, nil
  68. }
  69. // GetByUser 根据用户ID获取会话
  70. func (s *SessionStore) GetByUser(ctx context.Context, userID string) ([]*model.Session, error) {
  71. if userID == "" {
  72. return nil, errors.New("user ID cannot be empty")
  73. }
  74. filter := bson.M{"user_id": userID}
  75. var sessions []*model.Session
  76. err := s.mongoFactory.Find(model.Session{}.CollectionName(), filter, &sessions)
  77. if err != nil {
  78. logger.Error("根据用户查询会话失败", "user_id", userID, "error", err)
  79. return nil, err
  80. }
  81. return sessions, nil
  82. }
  83. // Update 更新会话(状态和标题)
  84. func (s *SessionStore) Update(ctx context.Context, id string, title, status string) error {
  85. if id == "" {
  86. return errors.New("session ID cannot be empty")
  87. }
  88. // 检查会话是否存在
  89. session, err := s.GetByID(ctx, id)
  90. if err != nil {
  91. return err
  92. }
  93. if session == nil {
  94. return errors.New("session not found")
  95. }
  96. // 已发布的会话不能修改
  97. if session.Status == model.StatusRelease {
  98. return errors.New("released session cannot be modified")
  99. }
  100. // 构建更新内容
  101. update := bson.M{
  102. "updated_at": time.Now(),
  103. }
  104. if title != "" {
  105. update["title"] = title
  106. }
  107. if status != "" {
  108. // 验证状态值
  109. if !model.IsValidStatus(status) {
  110. return errors.New("invalid status value")
  111. }
  112. update["status"] = status
  113. }
  114. filter := bson.M{"_id": id}
  115. success, _ := s.mongoFactory.UpdateOne(model.Session{}.CollectionName(), filter, update)
  116. if !success {
  117. logger.Error("更新会话失败", "session_id", id)
  118. return errors.New("failed to update session")
  119. }
  120. logger.Debug("更新会话成功", "session_id", id)
  121. return nil
  122. }
  123. // Delete 删除会话
  124. func (s *SessionStore) Delete(ctx context.Context, id string) error {
  125. if id == "" {
  126. return errors.New("session ID cannot be empty")
  127. }
  128. // 检查会话是否存在
  129. session, err := s.GetByID(ctx, id)
  130. if err != nil {
  131. return err
  132. }
  133. if session == nil {
  134. return errors.New("session not found")
  135. }
  136. // 已发布的会话不能删除
  137. if session.Status == model.StatusRelease {
  138. return errors.New("released session cannot be deleted")
  139. }
  140. filter := bson.M{"_id": id}
  141. success, deletedCount := s.mongoFactory.DeleteOne(model.Session{}.CollectionName(), filter)
  142. if !success {
  143. logger.Error("删除会话失败", "session_id", id)
  144. return errors.New("failed to delete session")
  145. }
  146. logger.Debug("删除会话成功", "session_id", id, "deleted_count", deletedCount)
  147. return nil
  148. }
  149. // ListQuery 列表查询参数
  150. type ListQuery struct {
  151. Page int `form:"page" binding:"min=1"` // 页码,从1开始
  152. PageSize int `form:"pageSize" binding:"min=1,max=100"` // 每页大小,最大100
  153. Title string `form:"title,omitempty"` // 标题搜索(模糊匹配)
  154. Status string `form:"status,omitempty"` // 状态筛选
  155. UserID string // 用户ID(从上下文中获取)
  156. TenantID string // 租户ID(从上下文中获取)
  157. }
  158. // ListResult 列表查询结果
  159. type ListResult struct {
  160. Sessions []*model.Session `json:"sessions"`
  161. TotalCount int64 `json:"total_count"`
  162. Page int `json:"page"`
  163. PageSize int `json:"page_size"`
  164. TotalPages int `json:"total_pages"`
  165. }
  166. // List 分页查询会话列表
  167. func (s *SessionStore) List(ctx context.Context, query *ListQuery) (*ListResult, error) {
  168. // 构建查询条件
  169. filter := bson.M{
  170. "user_id": query.UserID,
  171. "tenant_id": query.TenantID,
  172. }
  173. // 标题模糊匹配
  174. if query.Title != "" {
  175. filter["title"] = bson.M{"$regex": primitive.Regex{Pattern: query.Title, Options: "i"}}
  176. }
  177. // 状态筛选
  178. if query.Status != "" {
  179. filter["status"] = query.Status
  180. }
  181. // 查询所有匹配的记录(TODO: 优化为分页查询)
  182. var allSessions []*model.Session
  183. err := s.mongoFactory.Find(model.Session{}.CollectionName(), filter, &allSessions)
  184. if err != nil {
  185. logger.Error("查询会话列表失败", "error", err)
  186. return nil, err
  187. }
  188. // 按创建时间倒序排序
  189. // 由于MongoDB驱动可能已经按_id排序,我们需要手动排序
  190. // 这里简单反转切片(假设Find返回的顺序是插入顺序)
  191. // 实际应该使用聚合管道排序,但当前MongoDB工厂不支持
  192. logger.Warn("会话列表查询使用内存分页,数据量大时性能可能受影响", "count", len(allSessions))
  193. // 计算分页
  194. if query.Page < 1 {
  195. query.Page = 1
  196. }
  197. if query.PageSize < 1 {
  198. query.PageSize = 20
  199. } else if query.PageSize > 100 {
  200. query.PageSize = 100
  201. }
  202. totalCount := int64(len(allSessions))
  203. skip := (query.Page - 1) * query.PageSize
  204. // 计算分页切片
  205. var sessions []*model.Session
  206. if skip < len(allSessions) {
  207. end := skip + query.PageSize
  208. if end > len(allSessions) {
  209. end = len(allSessions)
  210. }
  211. sessions = allSessions[skip:end]
  212. }
  213. // 计算总页数
  214. totalPages := int(totalCount) / query.PageSize
  215. if int(totalCount)%query.PageSize > 0 {
  216. totalPages++
  217. }
  218. result := &ListResult{
  219. Sessions: sessions,
  220. TotalCount: totalCount,
  221. Page: query.Page,
  222. PageSize: query.PageSize,
  223. TotalPages: totalPages,
  224. }
  225. return result, nil
  226. }
  227. // EnsureIndexes 确保集合索引
  228. func (s *SessionStore) EnsureIndexes(ctx context.Context) error {
  229. // 主键索引(_id)已自动创建
  230. // 用户ID索引(常用查询条件)
  231. userIDIndexKeys := bson.D{{Key: "user_id", Value: 1}}
  232. userIDSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), userIDIndexKeys)
  233. if !userIDSuccess {
  234. logger.Error("创建用户ID索引失败")
  235. return errors.New("failed to create user_id index")
  236. }
  237. // 租户ID索引
  238. tenantIDIndexKeys := bson.D{{Key: "tenant_id", Value: 1}}
  239. tenantIDSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), tenantIDIndexKeys)
  240. if !tenantIDSuccess {
  241. logger.Error("创建租户ID索引失败")
  242. return errors.New("failed to create tenant_id index")
  243. }
  244. // 状态索引(用于筛选)
  245. statusIndexKeys := bson.D{{Key: "status", Value: 1}}
  246. statusSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), statusIndexKeys)
  247. if !statusSuccess {
  248. logger.Error("创建状态索引失败")
  249. return errors.New("failed to create status index")
  250. }
  251. // 创建时间索引(用于排序)
  252. createdAtIndexKeys := bson.D{{Key: "created_at", Value: -1}}
  253. createdAtSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), createdAtIndexKeys)
  254. if !createdAtSuccess {
  255. logger.Error("创建创建时间索引失败")
  256. return errors.New("failed to create created_at index")
  257. }
  258. // 复合索引:用户ID + 创建时间(常用查询组合)
  259. userCreatedIndexKeys := bson.D{
  260. {Key: "user_id", Value: 1},
  261. {Key: "created_at", Value: -1},
  262. }
  263. userCreatedSuccess := s.mongoFactory.CreateIndex(model.Session{}.CollectionName(), userCreatedIndexKeys)
  264. if !userCreatedSuccess {
  265. logger.Error("创建用户-创建时间复合索引失败")
  266. return errors.New("failed to create user_id-created_at index")
  267. }
  268. logger.Debug("会话集合索引创建成功")
  269. return nil
  270. }