fix: 同步原生账号启停与商务审核状态
This commit is contained in:
@@ -342,7 +342,7 @@ func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *system.U
|
||||
user.NickName = "系统用户"
|
||||
}
|
||||
now := time.Now()
|
||||
po := userPO{UUID: user.UUID, Username: user.Username, Password: user.Password, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Phone: user.Phone, Email: user.Email, Enable: user.Enable, PasswordUpdatedAt: &now, MustChangePassword: user.MustChangePassword}
|
||||
po := userPO{UUID: user.UUID, Username: user.Username, Password: user.Password, NickName: user.NickName, HeaderImg: user.HeaderImg, AuthorityID: user.AuthorityID, Phone: user.Phone, Email: user.Email, Enable: user.Enable, BusinessStatus: user.BusinessStatus, PasswordUpdatedAt: &now, MustChangePassword: user.MustChangePassword}
|
||||
var createdAuthorities []system.Authority
|
||||
if err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// The compatible registration rejects an existing non-deleted username before creating the
|
||||
@@ -397,6 +397,34 @@ func (r *userRepo) UpdateUser(ctx context.Context, user *system.User) error {
|
||||
return r.updateUser(r.data.DB().WithContext(ctx), user)
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateUserWithStatus(ctx context.Context, user *system.User, updateStatus func(int) (int, error)) error {
|
||||
if err := r.checkUserUpdateAuth(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.updateUserAndStatus(ctx, user.ID, userUpdateFields(user), updateStatus)
|
||||
}
|
||||
|
||||
func (r *userRepo) updateUserAndStatus(ctx context.Context, userID uint, updates map[string]any, updateStatus func(int) (int, error)) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var current userPO
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Select("id,business_status").First(¤t, userID).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return system.ErrUserNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
// 使用锁内的最新审核状态运行领域策略,与 enable 在同一次更新中提交。
|
||||
if updateStatus != nil {
|
||||
status, err := updateStatus(current.BusinessStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updates["business_status"] = status
|
||||
}
|
||||
return tx.Model(&userPO{}).Where("id = ?", userID).Updates(updates).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (r *userRepo) checkUserUpdateAuth(ctx context.Context, user *system.User) error {
|
||||
access := &authorityAccessRepo{data: r.data}
|
||||
if err := access.checkUserIDAuth(ctx, user.ID, false); err != nil {
|
||||
@@ -409,6 +437,18 @@ func (r *userRepo) checkUserUpdateAuth(ctx context.Context, user *system.User) e
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateSelfUser(ctx context.Context, user *system.User) error {
|
||||
updates := selfUserUpdateFields(user)
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateSelfUserWithStatus(ctx context.Context, user *system.User, updateStatus func(int) (int, error)) error {
|
||||
return r.updateUserAndStatus(ctx, user.ID, selfUserUpdateFields(user), updateStatus)
|
||||
}
|
||||
|
||||
func selfUserUpdateFields(user *system.User) map[string]any {
|
||||
updates := make(map[string]any)
|
||||
if user.NickName != "" {
|
||||
updates["nick_name"] = user.NickName
|
||||
@@ -425,10 +465,7 @@ func (r *userRepo) UpdateSelfUser(ctx context.Context, user *system.User) error
|
||||
if user.Enable != 0 {
|
||||
updates["enable"] = user.Enable
|
||||
}
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
return updates
|
||||
}
|
||||
|
||||
func (r *userRepo) FillDepartmentNamePaths(ctx context.Context, user *system.User) error {
|
||||
@@ -473,11 +510,15 @@ func (r *userRepo) FillDepartmentNamePaths(ctx context.Context, user *system.Use
|
||||
}
|
||||
|
||||
func (r *userRepo) updateUser(tx *gorm.DB, user *system.User) error {
|
||||
return tx.Model(&userPO{}).Where("id = ?", user.ID).Updates(userUpdateFields(user)).Error
|
||||
}
|
||||
|
||||
func userUpdateFields(user *system.User) map[string]any {
|
||||
updates := map[string]any{"nick_name": user.NickName, "header_img": user.HeaderImg, "phone": user.Phone, "email": user.Email, "enable": user.Enable}
|
||||
if user.AuthorityID != 0 {
|
||||
updates["authority_id"] = user.AuthorityID
|
||||
}
|
||||
return tx.Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
return updates
|
||||
}
|
||||
|
||||
func (r *userRepo) DeleteUser(ctx context.Context, id uint) error {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"kra/internal/biz/system"
|
||||
)
|
||||
|
||||
func TestUserStatusPolicyReadsLockedStateAndWritesTogether(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
db := data.gormDB.WithContext(context.Background())
|
||||
user := userPO{Username: "status-user", AuthorityID: 888, Enable: 0, BusinessStatus: 3, NickName: "old"}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repo := &userRepo{data: data}
|
||||
seen := -1
|
||||
err := repo.UpdateUserWithStatus(context.Background(), &system.User{ID: user.ID, NickName: "new", Enable: 1}, func(current int) (int, error) { seen = current; return 0, nil })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var stored userPO
|
||||
if err := db.First(&stored, user.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if seen != 3 || stored.Enable != 1 || stored.BusinessStatus != 0 || stored.NickName != "new" {
|
||||
t.Fatalf("状态策略和更新不同步: %+v seen=%d", stored, seen)
|
||||
}
|
||||
if err := db.Exec("CREATE TRIGGER fail_status_update BEFORE UPDATE OF business_status ON sys_users BEGIN SELECT RAISE(ABORT, 'test rejection'); END").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = repo.UpdateUserWithStatus(context.Background(), &system.User{ID: user.ID, NickName: "failed", Enable: 2}, func(int) (int, error) { return 1, nil })
|
||||
if err == nil {
|
||||
t.Fatal("失败的状态更新应回滚")
|
||||
}
|
||||
if err := db.First(&stored, user.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stored.Enable != 1 || stored.BusinessStatus != 0 || stored.NickName != "new" {
|
||||
t.Fatal("留下部分状态或资料更新")
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,9 @@ func TestStrictUserMutationsRejectOutsideTargetAndAllowSelfPassword(t *testing.T
|
||||
if err := repo.UpdateUser(ctx, &system.User{ID: users[2].ID, NickName: "changed"}); err == nil {
|
||||
t.Fatal("UpdateUser() accepted an out-of-scope target")
|
||||
}
|
||||
if err := repo.UpdateUserWithStatus(ctx, &system.User{ID: users[2].ID, Enable: 1}, func(int) (int, error) { t.Fatal("越权时不应执行状态策略"); return 0, nil }); err == nil {
|
||||
t.Fatal("启停同步接口放行了角色范围外用户")
|
||||
}
|
||||
if err := repo.DeleteUser(ctx, users[2].ID); err == nil {
|
||||
t.Fatal("DeleteUser() accepted an out-of-scope target")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user