sysDeptModel.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package dept
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "fmt"
  7. "github.com/zeromicro/go-zero/core/stores/cache"
  8. "github.com/zeromicro/go-zero/core/stores/sqlx"
  9. )
  10. var ErrUpdateConflict = errors.New("update conflict: data has been modified by another operation")
  11. var _ SysDeptModel = (*customSysDeptModel)(nil)
  12. type (
  13. SysDeptModel interface {
  14. sysDeptModel
  15. FindAll(ctx context.Context) ([]*SysDept, error)
  16. UpdateWithOptLock(ctx context.Context, data *SysDept, expectedUpdateTime int64) error
  17. // FindOneForShareTx 在当前事务里对 sys_dept 目标行加 S 锁(SELECT ... LOCK IN SHARE MODE),
  18. // 用于"UpdateUser 改 deptId 到 X"与"DeleteDept 删除 X"之间的 write skew 闭环(审计 M-R11-3)。
  19. // DeleteDept 会先对 sys_dept[X] 取 X 锁——被本 S 锁阻塞;等 UpdateUser 提交后 DeleteDept 再
  20. // 去 FOR SHARE sys_user WHERE deptId=X 时能看到新行,改删除为 400,整链路不产生 orphan deptId。
  21. // 本方法不走缓存,必须在 TransactCtx / Session 下调用。
  22. FindOneForShareTx(ctx context.Context, session sqlx.Session, id int64) (*SysDept, error)
  23. }
  24. customSysDeptModel struct {
  25. *defaultSysDeptModel
  26. }
  27. )
  28. func NewSysDeptModel(conn sqlx.SqlConn, c cache.CacheConf, cachePrefix string, opts ...cache.Option) SysDeptModel {
  29. return &customSysDeptModel{
  30. defaultSysDeptModel: newSysDeptModel(conn, c, cachePrefix, opts...),
  31. }
  32. }
  33. func (m *customSysDeptModel) FindAll(ctx context.Context) ([]*SysDept, error) {
  34. var list []*SysDept
  35. query := fmt.Sprintf("SELECT %s FROM %s ORDER BY `sort` ASC, `id` ASC", sysDeptRows, m.table)
  36. if err := m.QueryRowsNoCacheCtx(ctx, &list, query); err != nil {
  37. return nil, err
  38. }
  39. return list, nil
  40. }
  41. func (m *customSysDeptModel) FindOneForShareTx(ctx context.Context, session sqlx.Session, id int64) (*SysDept, error) {
  42. var data SysDept
  43. query := fmt.Sprintf("SELECT %s FROM %s WHERE `id` = ? LIMIT 1 LOCK IN SHARE MODE", sysDeptRows, m.table)
  44. if err := session.QueryRowCtx(ctx, &data, query, id); err != nil {
  45. return nil, err
  46. }
  47. return &data, nil
  48. }
  49. func (m *customSysDeptModel) UpdateWithOptLock(ctx context.Context, data *SysDept, expectedUpdateTime int64) error {
  50. sysDeptIdKey := fmt.Sprintf("%s%v", cacheSysDeptIdPrefix, data.Id)
  51. res, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error) {
  52. query := fmt.Sprintf("UPDATE %s SET `name`=?, `sort`=?, `deptType`=?, `remark`=?, `status`=?, `updateTime`=? WHERE `id`=? AND `updateTime`=?", m.table)
  53. return conn.ExecCtx(ctx, query, data.Name, data.Sort, data.DeptType, data.Remark, data.Status, data.UpdateTime, data.Id, expectedUpdateTime)
  54. }, sysDeptIdKey)
  55. if err != nil {
  56. return err
  57. }
  58. affected, _ := res.RowsAffected()
  59. if affected == 0 {
  60. return ErrUpdateConflict
  61. }
  62. return nil
  63. }