feat: manage merchant members
This commit is contained in:
@@ -23,6 +23,12 @@ type AddMemberInput struct {
|
||||
IsDefault bool
|
||||
}
|
||||
|
||||
type UpdateMemberInput struct {
|
||||
Role string
|
||||
Status int
|
||||
IsDefault bool
|
||||
}
|
||||
|
||||
type MerchantRoleInput struct {
|
||||
Code string
|
||||
Name string
|
||||
@@ -224,3 +230,67 @@ func (s *MerchantService) ListMembers(merchantID uint) ([]model.MerchantMember,
|
||||
err := s.db.Preload("User").Where("merchant_id = ?", merchantID).Order("id ASC").Find(&members).Error
|
||||
return members, err
|
||||
}
|
||||
|
||||
// UpdateMember updates a member's relationship with one merchant without changing the login account.
|
||||
func (s *MerchantService) UpdateMember(merchantID, memberID uint, in UpdateMemberInput, actorUserID uint) (*model.MerchantMember, error) {
|
||||
in.Role = strings.TrimSpace(in.Role)
|
||||
if in.Role == "" {
|
||||
return nil, errors.New("商户成员角色不能为空")
|
||||
}
|
||||
if in.Status != 0 && in.Status != 1 {
|
||||
return nil, errors.New("成员状态无效")
|
||||
}
|
||||
var member model.MerchantMember
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Where("id = ? AND merchant_id = ?", memberID, merchantID).First(&member).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("成员不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if member.Role == model.MemberRoleOwner {
|
||||
return errors.New("负责人不能在成员管理中修改")
|
||||
}
|
||||
if member.UserID == actorUserID {
|
||||
return errors.New("不能修改当前登录账号")
|
||||
}
|
||||
if _, err := (&TenantService{db: tx}).MerchantRolePermissions(merchantID, in.Role); err != nil {
|
||||
return err
|
||||
}
|
||||
updates := map[string]interface{}{"role": in.Role, "status": in.Status, "is_default": in.IsDefault}
|
||||
if err := tx.Model(&member).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return writeAudit(tx, &merchantID, &actorUserID, nil, "merchant.member.update", "merchant_member", fmt.Sprint(memberID), map[string]string{"role": in.Role, "status": fmt.Sprint(in.Status)})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.db.Preload("User").First(&member, member.ID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &member, nil
|
||||
}
|
||||
|
||||
// RemoveMember removes only the selected merchant relationship and retains the account for audit and other merchants.
|
||||
func (s *MerchantService) RemoveMember(merchantID, memberID, actorUserID uint) error {
|
||||
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||
var member model.MerchantMember
|
||||
if err := tx.Where("id = ? AND merchant_id = ?", memberID, merchantID).First(&member).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("成员不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if member.Role == model.MemberRoleOwner {
|
||||
return errors.New("负责人不能移除")
|
||||
}
|
||||
if member.UserID == actorUserID {
|
||||
return errors.New("不能移除当前登录账号")
|
||||
}
|
||||
if err := tx.Delete(&member).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return writeAudit(tx, &merchantID, &actorUserID, nil, "merchant.member.remove", "merchant_member", fmt.Sprint(memberID), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,3 +129,48 @@ func TestDefaultMerchantRolesCanBeEdited(t *testing.T) {
|
||||
t.Fatalf("unexpected updated role: %+v", updated)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMemberCanBeUpdatedOrRemovedWithoutDeletingAccount(t *testing.T) {
|
||||
db := newServiceTestDB(t)
|
||||
merchant := model.Merchant{Code: "member-crud", Name: "成员管理商户", Status: model.MerchantStatusActive}
|
||||
actor := model.User{Username: "member-crud-owner", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1}
|
||||
employee := model.User{Username: "member-crud-employee", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1}
|
||||
if err := db.Create(&merchant).Error; err != nil {
|
||||
t.Fatalf("create merchant: %v", err)
|
||||
}
|
||||
if err := db.Create(&actor).Error; err != nil {
|
||||
t.Fatalf("create actor: %v", err)
|
||||
}
|
||||
if err := db.Create(&employee).Error; err != nil {
|
||||
t.Fatalf("create employee: %v", err)
|
||||
}
|
||||
if err := createDefaultMerchantRoles(db, merchant.ID); err != nil {
|
||||
t.Fatalf("create default roles: %v", err)
|
||||
}
|
||||
member := model.MerchantMember{MerchantID: merchant.ID, UserID: employee.ID, Role: "support", Status: 1}
|
||||
if err := db.Create(&member).Error; err != nil {
|
||||
t.Fatalf("create member: %v", err)
|
||||
}
|
||||
|
||||
svc := NewMerchantService(db, nil, NewTenantService(db))
|
||||
updated, err := svc.UpdateMember(merchant.ID, member.ID, UpdateMemberInput{
|
||||
Role: model.MemberRoleFinance, Status: 0, IsDefault: true,
|
||||
}, actor.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("update member: %v", err)
|
||||
}
|
||||
if updated.Role != model.MemberRoleFinance || updated.Status != 0 || !updated.IsDefault {
|
||||
t.Fatalf("unexpected updated member: %+v", updated)
|
||||
}
|
||||
if err := svc.RemoveMember(merchant.ID, member.ID, actor.ID); err != nil {
|
||||
t.Fatalf("remove member: %v", err)
|
||||
}
|
||||
var user model.User
|
||||
if err := db.First(&user, employee.ID).Error; err != nil {
|
||||
t.Fatalf("employee account should remain: %v", err)
|
||||
}
|
||||
var count int64
|
||||
if err := db.Model(&model.MerchantMember{}).Where("id = ?", member.ID).Count(&count).Error; err != nil || count != 0 {
|
||||
t.Fatalf("member relationship should be removed, count=%d err=%v", count, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user