sysRolePermModel.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package roleperm
  2. import (
  3. "context"
  4. "database/sql"
  5. "fmt"
  6. "strings"
  7. "github.com/zeromicro/go-zero/core/stores/cache"
  8. "github.com/zeromicro/go-zero/core/stores/sqlx"
  9. )
  10. var _ SysRolePermModel = (*customSysRolePermModel)(nil)
  11. type (
  12. SysRolePermModel interface {
  13. sysRolePermModel
  14. FindPermIdsByRoleId(ctx context.Context, roleId int64) ([]int64, error)
  15. FindPermIdsByRoleIds(ctx context.Context, roleIds []int64) ([]int64, error)
  16. // FindPermIdsByRoleIdTx 是 FindPermIdsByRoleId 的事务内变体:上游 BindRolePerms 在
  17. // LockByIdTx(role) 之后,需要在同一事务里读取当前 existing permIds 再做 diff,否则
  18. // "existing 读-外部 / diff 写-内部" 的窗口会产生第三态(见审计 M-R10-2)。
  19. FindPermIdsByRoleIdTx(ctx context.Context, session sqlx.Session, roleId int64) ([]int64, error)
  20. DeleteByRoleIdTx(ctx context.Context, session sqlx.Session, roleId int64) error
  21. DeleteByRoleIdAndPermIdsTx(ctx context.Context, session sqlx.Session, roleId int64, permIds []int64) error
  22. }
  23. customSysRolePermModel struct {
  24. *defaultSysRolePermModel
  25. }
  26. )
  27. func NewSysRolePermModel(conn sqlx.SqlConn, c cache.CacheConf, cachePrefix string, opts ...cache.Option) SysRolePermModel {
  28. return &customSysRolePermModel{
  29. defaultSysRolePermModel: newSysRolePermModel(conn, c, cachePrefix, opts...),
  30. }
  31. }
  32. func (m *customSysRolePermModel) FindPermIdsByRoleId(ctx context.Context, roleId int64) ([]int64, error) {
  33. var ids []int64
  34. query := fmt.Sprintf("SELECT `permId` FROM %s WHERE `roleId` = ?", m.table)
  35. if err := m.QueryRowsNoCacheCtx(ctx, &ids, query, roleId); err != nil {
  36. return nil, err
  37. }
  38. return ids, nil
  39. }
  40. func (m *customSysRolePermModel) FindPermIdsByRoleIdTx(ctx context.Context, session sqlx.Session, roleId int64) ([]int64, error) {
  41. var ids []int64
  42. query := fmt.Sprintf("SELECT `permId` FROM %s WHERE `roleId` = ?", m.table)
  43. if err := session.QueryRowsCtx(ctx, &ids, query, roleId); err != nil {
  44. return nil, err
  45. }
  46. return ids, nil
  47. }
  48. func (m *customSysRolePermModel) FindPermIdsByRoleIds(ctx context.Context, roleIds []int64) ([]int64, error) {
  49. if len(roleIds) == 0 {
  50. return nil, nil
  51. }
  52. placeholders := make([]string, len(roleIds))
  53. args := make([]interface{}, len(roleIds))
  54. for i, id := range roleIds {
  55. placeholders[i] = "?"
  56. args[i] = id
  57. }
  58. var ids []int64
  59. query := fmt.Sprintf("SELECT DISTINCT `permId` FROM %s WHERE `roleId` IN (%s)", m.table, strings.Join(placeholders, ","))
  60. if err := m.QueryRowsNoCacheCtx(ctx, &ids, query, args...); err != nil {
  61. return nil, err
  62. }
  63. return ids, nil
  64. }
  65. // rolePermKey 仅包含构造缓存键所需的列(审计 L-R11-2)。
  66. type rolePermKey struct {
  67. Id int64 `db:"id"`
  68. RoleId int64 `db:"roleId"`
  69. PermId int64 `db:"permId"`
  70. }
  71. func (m *customSysRolePermModel) buildCacheKeysFromKeys(list []rolePermKey) []string {
  72. keys := make([]string, 0, len(list)*2)
  73. for _, data := range list {
  74. keys = append(keys,
  75. fmt.Sprintf("%s%v", cacheSysRolePermIdPrefix, data.Id),
  76. fmt.Sprintf("%s%v:%v", cacheSysRolePermRoleIdPermIdPrefix, data.RoleId, data.PermId),
  77. )
  78. }
  79. return keys
  80. }
  81. func (m *customSysRolePermModel) DeleteByRoleIdTx(ctx context.Context, session sqlx.Session, roleId int64) error {
  82. var list []rolePermKey
  83. findQuery := fmt.Sprintf("SELECT `id`, `roleId`, `permId` FROM %s WHERE `roleId` = ? FOR UPDATE", m.table)
  84. if err := session.QueryRowsCtx(ctx, &list, findQuery, roleId); err != nil {
  85. return err
  86. }
  87. if len(list) == 0 {
  88. return nil
  89. }
  90. keys := m.buildCacheKeysFromKeys(list)
  91. _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error) {
  92. query := fmt.Sprintf("DELETE FROM %s WHERE `roleId` = ?", m.table)
  93. return session.ExecCtx(ctx, query, roleId)
  94. }, keys...)
  95. return err
  96. }
  97. func (m *customSysRolePermModel) DeleteByRoleIdAndPermIdsTx(ctx context.Context, session sqlx.Session, roleId int64, permIds []int64) error {
  98. if len(permIds) == 0 {
  99. return nil
  100. }
  101. placeholders := make([]string, len(permIds))
  102. args := make([]interface{}, 0, len(permIds)+1)
  103. args = append(args, roleId)
  104. for i, id := range permIds {
  105. placeholders[i] = "?"
  106. args = append(args, id)
  107. }
  108. inClause := strings.Join(placeholders, ",")
  109. var list []rolePermKey
  110. findQuery := fmt.Sprintf("SELECT `id`, `roleId`, `permId` FROM %s WHERE `roleId` = ? AND `permId` IN (%s) FOR UPDATE", m.table, inClause)
  111. if err := session.QueryRowsCtx(ctx, &list, findQuery, args...); err != nil {
  112. return err
  113. }
  114. if len(list) == 0 {
  115. return nil
  116. }
  117. keys := m.buildCacheKeysFromKeys(list)
  118. _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error) {
  119. query := fmt.Sprintf("DELETE FROM %s WHERE `roleId` = ? AND `permId` IN (%s)", m.table, inClause)
  120. return session.ExecCtx(ctx, query, args...)
  121. }, keys...)
  122. return err
  123. }