优化:平台管理员账号独立管理

This commit is contained in:
yml2213
2026-08-13 13:31:06 +08:00
parent c928c5b8e8
commit cfdc12cf94
7 changed files with 250 additions and 379 deletions
+30 -57
View File
@@ -206,90 +206,63 @@ func TestAuthServiceChangePasswordVerifiesCurrentPassword(t *testing.T) {
func TestUserServiceCannotDeleteOrDisableLastAdmin(t *testing.T) {
db := newServiceTestDB(t)
svc := NewUserService(db, nil)
admin, err := svc.Create("only-admin", "password123", "唯一管理员", model.RoleAdmin, 0)
admin, err := svc.CreatePlatformAdmin("only-admin", "password123", "唯一管理员")
if err != nil {
t.Fatalf("create admin: %v", err)
}
if err := svc.UpdateStatus(admin.ID, 0); err == nil {
if _, err := svc.UpdatePlatformAdmin(admin.ID, UpdatePlatformAdminInput{
Username: admin.Username, Nickname: admin.Nickname, Status: 0,
}, 0); err == nil {
t.Fatal("should not disable the last enabled admin")
}
other, err := svc.Create("other-admin", "password123", "另一管理员", model.RoleAdmin, 0)
other, err := svc.CreatePlatformAdmin("other-admin", "password123", "另一管理员")
if err != nil {
t.Fatalf("create second admin: %v", err)
}
if err := svc.Delete(other.ID, admin.ID); err != nil {
if err := svc.DeletePlatformAdmin(other.ID, admin.ID); err != nil {
t.Fatalf("delete non-current admin: %v", err)
}
if err := svc.Delete(admin.ID, other.ID); err == nil {
if err := svc.DeletePlatformAdmin(admin.ID, other.ID); err == nil {
t.Fatal("should not delete the last enabled admin")
}
}
func TestUserServicePromotesMerchantAccountToPlatformAdmin(t *testing.T) {
func TestPlatformAdminCRUDDoesNotCreateMerchantMembership(t *testing.T) {
db := newServiceTestDB(t)
var merchant model.Merchant
if err := db.Where("code = ?", model.MerchantCodeSelfOperated).First(&merchant).Error; err != nil {
t.Fatalf("load self-operated merchant: %v", err)
}
svc := NewUserService(db, NewTenantService(db))
actor, err := svc.Create("existing-admin", "password123", "现有管理员", model.RoleAdmin, 0)
actor, err := svc.CreatePlatformAdmin("existing-admin", "password123", "现有管理员")
if err != nil {
t.Fatalf("create actor: %v", err)
}
member, err := svc.Create("skin_nh_admin", "password123", "管理员", model.RoleMerchant, 0)
admin, err := svc.CreatePlatformAdmin("platform-admin", "password123", "平台管理员")
if err != nil {
t.Fatalf("create merchant user: %v", err)
t.Fatalf("create platform admin: %v", err)
}
if err := svc.UpdateRole(member.ID, model.RoleAdmin, actor.ID); err != nil {
t.Fatalf("promote account: %v", err)
var membershipCount int64
if err := db.Model(&model.MerchantMember{}).Where("user_id = ?", admin.ID).Count(&membershipCount).Error; err != nil {
t.Fatalf("count merchant memberships: %v", err)
}
var promoted model.User
if err := db.First(&promoted, member.ID).Error; err != nil || promoted.Role != model.RoleAdmin {
t.Fatalf("account should be admin, user=%+v err=%v", promoted, err)
if membershipCount != 0 {
t.Fatalf("platform admin must not have merchant memberships, count=%d", membershipCount)
}
var membership model.MerchantMember
if err := db.Where("merchant_id = ? AND user_id = ?", merchant.ID, member.ID).First(&membership).Error; err != nil {
t.Fatalf("promoted account should join self-operated merchant: %v", err)
}
if membership.Role != model.MemberRoleOwner || !membership.IsDefault {
t.Fatalf("unexpected promoted membership: %+v", membership)
}
}
func TestListMerchantMembersGroupsEmployeesByMerchant(t *testing.T) {
db := newServiceTestDB(t)
merchantA := model.Merchant{Code: "grouped-members-a", Name: "分组商户 A", Status: model.MerchantStatusActive}
merchantB := model.Merchant{Code: "grouped-members-b", Name: "分组商户 B", Status: model.MerchantStatusActive}
employeeA := model.User{Username: "grouped-employee-a", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1}
employeeB := model.User{Username: "grouped-employee-b", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1}
admin := model.User{Username: "grouped-admin", PasswordHash: "hash", Role: model.RoleAdmin, Status: 1}
for _, entity := range []interface{}{&merchantA, &merchantB, &employeeA, &employeeB, &admin} {
if err := db.Create(entity).Error; err != nil {
t.Fatalf("create fixture: %v", err)
}
}
for _, member := range []model.MerchantMember{
{MerchantID: merchantA.ID, UserID: employeeA.ID, Role: model.MemberRoleOperator, Status: 1},
{MerchantID: merchantA.ID, UserID: employeeB.ID, Role: model.MemberRoleFinance, Status: 1},
{MerchantID: merchantB.ID, UserID: admin.ID, Role: model.MemberRoleOwner, Status: 1},
} {
if err := db.Create(&member).Error; err != nil {
t.Fatalf("create membership: %v", err)
}
}
groups, total, err := NewUserService(db, nil).ListMerchantMembers(1, 20)
updated, err := svc.UpdatePlatformAdmin(admin.ID, UpdatePlatformAdminInput{
Username: "platform-admin-updated", Nickname: "更新后管理员", Password: "new-password", Status: 1,
}, actor.ID)
if err != nil {
t.Fatalf("list grouped members: %v", err)
t.Fatalf("update platform admin: %v", err)
}
if total != 1 || len(groups) != 1 {
t.Fatalf("expected one merchant group, total=%d groups=%+v", total, groups)
if updated.Username != "platform-admin-updated" || updated.Nickname != "更新后管理员" {
t.Fatalf("unexpected updated admin: %+v", updated)
}
if groups[0].MerchantID != merchantA.ID || groups[0].Merchant == nil || groups[0].Merchant.Name != merchantA.Name {
t.Fatalf("unexpected merchant group: %+v", groups[0])
var saved model.User
if err := db.First(&saved, admin.ID).Error; err != nil {
t.Fatalf("load updated admin: %v", err)
}
if len(groups[0].Members) != 2 {
t.Fatalf("expected two employees in the merchant group, got %+v", groups[0].Members)
if err := bcrypt.CompareHashAndPassword([]byte(saved.PasswordHash), []byte("new-password")); err != nil {
t.Fatalf("updated password should match: %v", err)
}
if err := svc.DeletePlatformAdmin(admin.ID, actor.ID); err != nil {
t.Fatalf("delete platform admin: %v", err)
}
}