优化商户管理, 增加商户名重置密码等等
This commit is contained in:
@@ -294,3 +294,154 @@ func (s *MerchantService) RemoveMember(merchantID, memberID, actorUserID uint) e
|
||||
return writeAudit(tx, &merchantID, &actorUserID, nil, "merchant.member.remove", "merchant_member", fmt.Sprint(memberID), nil)
|
||||
})
|
||||
}
|
||||
|
||||
// MerchantOwnerAccount 商户负责人登录账号(平台管理员视角)。
|
||||
type MerchantOwnerAccount struct {
|
||||
UserID uint `json:"user_id"`
|
||||
MemberID uint `json:"member_id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Status int `json:"status"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
// ListOwnerAccounts 返回指定商户的全部负责人登录账号(通常一个)。
|
||||
func (s *MerchantService) ListOwnerAccounts(merchantID uint) ([]MerchantOwnerAccount, error) {
|
||||
if merchantID == 0 {
|
||||
return nil, errors.New("无效的商户")
|
||||
}
|
||||
var members []model.MerchantMember
|
||||
if err := s.db.Preload("User").
|
||||
Where("merchant_id = ? AND role = ?", merchantID, model.MemberRoleOwner).
|
||||
Order("id ASC").
|
||||
Find(&members).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]MerchantOwnerAccount, 0, len(members))
|
||||
for _, member := range members {
|
||||
item := MerchantOwnerAccount{
|
||||
UserID: member.UserID,
|
||||
MemberID: member.ID,
|
||||
Status: member.Status,
|
||||
IsDefault: member.IsDefault,
|
||||
}
|
||||
if member.User != nil {
|
||||
item.Username = member.User.Username
|
||||
item.Nickname = member.User.Nickname
|
||||
item.Status = member.User.Status
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UpdateMerchantOwnerAccountInput 平台管理员维护商户负责人登录账号的输入。
|
||||
type UpdateMerchantOwnerAccountInput struct {
|
||||
UserID uint
|
||||
Username string
|
||||
Nickname string
|
||||
Password string
|
||||
Status *int
|
||||
}
|
||||
|
||||
// UpdateMerchantOwnerAccount 平台管理员修改商户负责人登录账号:
|
||||
// 改用户名 / 昵称 / 重置密码 / 启用停用。负责人账号受保护:
|
||||
// 用户名全局唯一,至少保留一个启用的负责人账号,防止商户被锁死。
|
||||
func (s *MerchantService) UpdateMerchantOwnerAccount(merchantID uint, in UpdateMerchantOwnerAccountInput, actorUserID uint) (*MerchantOwnerAccount, error) {
|
||||
if merchantID == 0 || in.UserID == 0 {
|
||||
return nil, errors.New("无效的商户或账号")
|
||||
}
|
||||
in.Username = strings.TrimSpace(in.Username)
|
||||
in.Nickname = strings.TrimSpace(in.Nickname)
|
||||
if len(in.Username) < 3 || len(in.Username) > 64 {
|
||||
return nil, errors.New("登录用户名需为 3-64 位")
|
||||
}
|
||||
if in.Password != "" && len(in.Password) < 6 {
|
||||
return nil, errors.New("密码至少 6 位")
|
||||
}
|
||||
if in.Status != nil && *in.Status != 0 && *in.Status != 1 {
|
||||
return nil, errors.New("账号状态无效")
|
||||
}
|
||||
var out MerchantOwnerAccount
|
||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||
var member model.MerchantMember
|
||||
if err := tx.Preload("User").
|
||||
Where("merchant_id = ? AND user_id = ? AND role = ?", merchantID, in.UserID, model.MemberRoleOwner).
|
||||
First(&member).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("该账号不是该商户的负责人账号")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if member.User == nil {
|
||||
return errors.New("负责人账号不存在")
|
||||
}
|
||||
user := member.User
|
||||
if in.Username != user.Username {
|
||||
var count int64
|
||||
if err := tx.Model(&model.User{}).Where("username = ? AND id <> ?", in.Username, user.ID).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("用户名已存在")
|
||||
}
|
||||
}
|
||||
// 防锁死:停用最后一个启用的负责人账号会被拒绝。
|
||||
if in.Status != nil && *in.Status == 0 && user.Status == 1 {
|
||||
var enabledOwners int64
|
||||
if err := tx.Model(&model.MerchantMember{}).
|
||||
Joins("JOIN users ON users.id = merchant_members.user_id AND users.deleted_at IS NULL").
|
||||
Where("merchant_members.merchant_id = ? AND merchant_members.role = ? AND users.status = 1",
|
||||
merchantID, model.MemberRoleOwner).
|
||||
Count(&enabledOwners).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if enabledOwners <= 1 {
|
||||
return errors.New("至少保留一个启用的商户负责人账号")
|
||||
}
|
||||
}
|
||||
updates := map[string]interface{}{"username": in.Username, "nickname": in.Nickname}
|
||||
if in.Password != "" {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updates["password_hash"] = string(hash)
|
||||
}
|
||||
if in.Status != nil {
|
||||
updates["status"] = *in.Status
|
||||
}
|
||||
if err := tx.Model(&model.User{}).Where("id = ?", user.ID).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
metadata := map[string]interface{}{
|
||||
"username": in.Username,
|
||||
"nickname": in.Nickname,
|
||||
}
|
||||
if in.Password != "" {
|
||||
metadata["password_reset"] = true
|
||||
}
|
||||
if in.Status != nil {
|
||||
metadata["status"] = *in.Status
|
||||
}
|
||||
if err := writeAudit(tx, &merchantID, &actorUserID, nil, "merchant.account.update", "user", fmt.Sprint(user.ID), metadata); err != nil {
|
||||
return err
|
||||
}
|
||||
out = MerchantOwnerAccount{
|
||||
UserID: user.ID,
|
||||
MemberID: member.ID,
|
||||
Username: in.Username,
|
||||
Nickname: in.Nickname,
|
||||
Status: user.Status,
|
||||
IsDefault: member.IsDefault,
|
||||
}
|
||||
if in.Status != nil {
|
||||
out.Status = *in.Status
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user