security_policy_repo.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package repository
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "strings"
  7. "time"
  8. dbent "github.com/Wei-Shaw/sub2api/ent"
  9. dbsecpolkw "github.com/Wei-Shaw/sub2api/ent/securitypolicykeyword"
  10. "github.com/Wei-Shaw/sub2api/internal/service"
  11. )
  12. // securityPolicyRepository 实现 service.SecurityPolicyRepository。
  13. type securityPolicyRepository struct {
  14. client *dbent.Client
  15. sql sqlExecutor
  16. }
  17. func NewSecurityPolicyRepository(client *dbent.Client, sqlDB *sql.DB) service.SecurityPolicyRepository {
  18. return &securityPolicyRepository{client: client, sql: sqlDB}
  19. }
  20. // ListEffectiveKeywords 返回对指定分组生效的自定义词:全局词(group_id IS NULL)
  21. // 与该分组词的并集,仅含 enabled 且未删除。内置 seed 由 service 层合并。
  22. func (r *securityPolicyRepository) ListEffectiveKeywords(ctx context.Context, groupID *int64) ([]service.SecurityPolicyKeyword, error) {
  23. q := r.client.SecurityPolicyKeyword.Query().
  24. Where(
  25. dbsecpolkw.Enabled(true),
  26. dbsecpolkw.DeletedAtIsNil(),
  27. )
  28. if groupID != nil && *groupID > 0 {
  29. q = q.Where(dbsecpolkw.Or(
  30. dbsecpolkw.GroupIDIsNil(),
  31. dbsecpolkw.GroupIDEQ(*groupID),
  32. ))
  33. } else {
  34. q = q.Where(dbsecpolkw.GroupIDIsNil())
  35. }
  36. rows, err := q.
  37. Order(dbent.Asc(dbsecpolkw.FieldID)).
  38. Limit(10000).
  39. All(ctx)
  40. if err != nil {
  41. return nil, err
  42. }
  43. out := make([]service.SecurityPolicyKeyword, 0, len(rows))
  44. for _, row := range rows {
  45. out = append(out, service.SecurityPolicyKeyword{
  46. ID: row.ID,
  47. GroupID: row.GroupID,
  48. Keyword: row.Keyword,
  49. Category: row.Category,
  50. Enabled: row.Enabled,
  51. CreatedAt: row.CreatedAt,
  52. UpdatedAt: row.UpdatedAt,
  53. })
  54. }
  55. return out, nil
  56. }
  57. func (r *securityPolicyRepository) CreateKeyword(ctx context.Context, in service.SecurityPolicyKeywordInput) (*service.SecurityPolicyKeyword, error) {
  58. keyword := strings.TrimSpace(in.Keyword)
  59. if keyword == "" {
  60. return nil, fmt.Errorf("keyword must not be empty")
  61. }
  62. if len([]rune(keyword)) > 200 {
  63. return nil, fmt.Errorf("keyword must not exceed 200 runes")
  64. }
  65. category := strings.TrimSpace(in.Category)
  66. if category == "" {
  67. category = service.SecurityPolicyCategoryCustom
  68. }
  69. builder := r.client.SecurityPolicyKeyword.Create().
  70. SetKeyword(keyword).
  71. SetCategory(category).
  72. SetEnabled(true)
  73. if in.GroupID != nil && *in.GroupID > 0 {
  74. builder = builder.SetGroupID(*in.GroupID)
  75. }
  76. row, err := builder.Save(ctx)
  77. if err != nil {
  78. return nil, translatePersistenceError(err, nil, service.ErrSecurityPolicyKeywordExists)
  79. }
  80. return &service.SecurityPolicyKeyword{
  81. ID: row.ID,
  82. GroupID: row.GroupID,
  83. Keyword: row.Keyword,
  84. Category: row.Category,
  85. Enabled: row.Enabled,
  86. CreatedAt: row.CreatedAt,
  87. UpdatedAt: row.UpdatedAt,
  88. }, nil
  89. }
  90. func (r *securityPolicyRepository) UpdateKeyword(ctx context.Context, id int64, in service.SecurityPolicyKeywordUpdate) (*service.SecurityPolicyKeyword, error) {
  91. builder := r.client.SecurityPolicyKeyword.UpdateOneID(id)
  92. updated := false
  93. if in.Keyword != nil {
  94. keyword := strings.TrimSpace(*in.Keyword)
  95. if keyword == "" {
  96. return nil, fmt.Errorf("keyword must not be empty")
  97. }
  98. if len([]rune(keyword)) > 200 {
  99. return nil, fmt.Errorf("keyword must not exceed 200 runes")
  100. }
  101. builder = builder.SetKeyword(keyword)
  102. updated = true
  103. }
  104. if in.Category != nil {
  105. builder = builder.SetCategory(strings.TrimSpace(*in.Category))
  106. updated = true
  107. }
  108. if in.Enabled != nil {
  109. builder = builder.SetEnabled(*in.Enabled)
  110. updated = true
  111. }
  112. if !updated {
  113. return nil, fmt.Errorf("nothing to update")
  114. }
  115. builder = builder.SetUpdatedAt(time.Now())
  116. row, err := builder.Save(ctx)
  117. if err != nil {
  118. return nil, translatePersistenceError(err, service.ErrSecurityPolicyKeywordNotFound, service.ErrSecurityPolicyKeywordExists)
  119. }
  120. return &service.SecurityPolicyKeyword{
  121. ID: row.ID,
  122. GroupID: row.GroupID,
  123. Keyword: row.Keyword,
  124. Category: row.Category,
  125. Enabled: row.Enabled,
  126. CreatedAt: row.CreatedAt,
  127. UpdatedAt: row.UpdatedAt,
  128. }, nil
  129. }
  130. func (r *securityPolicyRepository) DeleteKeyword(ctx context.Context, id int64) error {
  131. // 软删除:保留审计痕迹,匹配查询自动过滤 deleted_at。
  132. if err := r.client.SecurityPolicyKeyword.DeleteOneID(id).Exec(ctx); err != nil {
  133. return translatePersistenceError(err, service.ErrSecurityPolicyKeywordNotFound, nil)
  134. }
  135. return nil
  136. }
  137. func (r *securityPolicyRepository) ListKeywords(ctx context.Context, groupID *int64, includeDisabled bool) ([]service.SecurityPolicyKeyword, error) {
  138. q := r.client.SecurityPolicyKeyword.Query().
  139. Where(dbsecpolkw.DeletedAtIsNil())
  140. if groupID != nil {
  141. if *groupID == 0 {
  142. q = q.Where(dbsecpolkw.GroupIDIsNil())
  143. } else {
  144. q = q.Where(dbsecpolkw.GroupIDEQ(*groupID))
  145. }
  146. }
  147. if !includeDisabled {
  148. q = q.Where(dbsecpolkw.Enabled(true))
  149. }
  150. rows, err := q.
  151. Order(dbent.Desc(dbsecpolkw.FieldID)).
  152. Limit(10000).
  153. All(ctx)
  154. if err != nil {
  155. return nil, err
  156. }
  157. out := make([]service.SecurityPolicyKeyword, 0, len(rows))
  158. for _, row := range rows {
  159. out = append(out, service.SecurityPolicyKeyword{
  160. ID: row.ID,
  161. GroupID: row.GroupID,
  162. Keyword: row.Keyword,
  163. Category: row.Category,
  164. Enabled: row.Enabled,
  165. CreatedAt: row.CreatedAt,
  166. UpdatedAt: row.UpdatedAt,
  167. })
  168. }
  169. return out, nil
  170. }