功能:平台管理员删除商户账号
This commit is contained in:
@@ -95,3 +95,12 @@ func (h *UserHandler) DeletePlatformAdmin(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
response.OK(c, nil)
|
response.OK(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *UserHandler) DeleteMerchantAccount(c *gin.Context) {
|
||||||
|
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||||
|
if err := h.svc.DeleteMerchantAccount(uint(id), middleware.GetUserID(c)); err != nil {
|
||||||
|
response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OK(c, nil)
|
||||||
|
}
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ func Setup(h *Handlers) *gin.Engine {
|
|||||||
{
|
{
|
||||||
admin.GET("/platform/admins", h.User.ListPlatformAdmins)
|
admin.GET("/platform/admins", h.User.ListPlatformAdmins)
|
||||||
admin.GET("/platform/merchant-accounts", h.User.ListMerchantAccountGroups)
|
admin.GET("/platform/merchant-accounts", h.User.ListMerchantAccountGroups)
|
||||||
|
admin.DELETE("/platform/merchant-accounts/:id", h.User.DeleteMerchantAccount)
|
||||||
admin.POST("/platform/admins", h.User.CreatePlatformAdmin)
|
admin.POST("/platform/admins", h.User.CreatePlatformAdmin)
|
||||||
admin.PATCH("/platform/admins/:id", h.User.UpdatePlatformAdmin)
|
admin.PATCH("/platform/admins/:id", h.User.UpdatePlatformAdmin)
|
||||||
admin.DELETE("/platform/admins/:id", h.User.DeletePlatformAdmin)
|
admin.DELETE("/platform/admins/:id", h.User.DeletePlatformAdmin)
|
||||||
|
|||||||
@@ -336,3 +336,40 @@ func TestListMerchantAccountGroupsExcludesPlatformAdmins(t *testing.T) {
|
|||||||
t.Fatalf("unexpected merchant account group: %+v", groups[0])
|
t.Fatalf("unexpected merchant account group: %+v", groups[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteMerchantAccountRemovesAllMemberships(t *testing.T) {
|
||||||
|
db := newServiceTestDB(t)
|
||||||
|
merchantA := model.Merchant{Code: "delete-account-a", Name: "删除账号商户 A", Status: model.MerchantStatusActive}
|
||||||
|
merchantB := model.Merchant{Code: "delete-account-b", Name: "删除账号商户 B", Status: model.MerchantStatusActive}
|
||||||
|
merchantUser := model.User{Username: "delete-merchant-account", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1}
|
||||||
|
platformAdmin := model.User{Username: "delete-account-admin", PasswordHash: "hash", Role: model.RoleAdmin, Status: 1}
|
||||||
|
for _, entity := range []interface{}{&merchantA, &merchantB, &merchantUser, &platformAdmin} {
|
||||||
|
if err := db.Create(entity).Error; err != nil {
|
||||||
|
t.Fatalf("create fixture: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, member := range []model.MerchantMember{
|
||||||
|
{MerchantID: merchantA.ID, UserID: merchantUser.ID, Role: model.MemberRoleOperator, Status: 1},
|
||||||
|
{MerchantID: merchantB.ID, UserID: merchantUser.ID, Role: "support", Status: 1},
|
||||||
|
} {
|
||||||
|
if err := db.Create(&member).Error; err != nil {
|
||||||
|
t.Fatalf("create membership: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc := NewUserService(db, nil)
|
||||||
|
if err := svc.DeleteMerchantAccount(merchantUser.ID, platformAdmin.ID); err != nil {
|
||||||
|
t.Fatalf("delete merchant account: %v", err)
|
||||||
|
}
|
||||||
|
var memberships int64
|
||||||
|
if err := db.Model(&model.MerchantMember{}).Where("user_id = ?", merchantUser.ID).Count(&memberships).Error; err != nil || memberships != 0 {
|
||||||
|
t.Fatalf("merchant memberships should be removed, count=%d err=%v", memberships, err)
|
||||||
|
}
|
||||||
|
var deleted model.User
|
||||||
|
if err := db.Unscoped().First(&deleted, merchantUser.ID).Error; err != nil || !deleted.DeletedAt.Valid {
|
||||||
|
t.Fatalf("merchant account should be soft deleted, user=%+v err=%v", deleted, err)
|
||||||
|
}
|
||||||
|
if err := svc.DeleteMerchantAccount(platformAdmin.ID, merchantUser.ID); err == nil {
|
||||||
|
t.Fatal("platform administrator should not be deletable from merchant account endpoint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -316,3 +316,31 @@ func (s *UserService) DeletePlatformAdmin(id, actorUserID uint) error {
|
|||||||
return tx.Delete(&user).Error
|
return tx.Delete(&user).Error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteMerchantAccount permanently removes a merchant login account and all
|
||||||
|
// of its merchant memberships. Platform administrators must be managed from
|
||||||
|
// the dedicated platform administrator view instead.
|
||||||
|
func (s *UserService) DeleteMerchantAccount(id, actorUserID uint) error {
|
||||||
|
if id == 0 {
|
||||||
|
return errors.New("用户不存在")
|
||||||
|
}
|
||||||
|
if id == actorUserID {
|
||||||
|
return errors.New("不能删除当前登录账号")
|
||||||
|
}
|
||||||
|
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
var user model.User
|
||||||
|
if err := tx.First(&user, id).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return errors.New("用户不存在")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if user.Role != model.RoleMerchant {
|
||||||
|
return errors.New("平台管理员请在平台管理员列表中管理")
|
||||||
|
}
|
||||||
|
if err := tx.Where("user_id = ?", user.ID).Delete(&model.MerchantMember{}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Delete(&user).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ export const deliveryApi = {
|
|||||||
export const platformApi = {
|
export const platformApi = {
|
||||||
merchantAccounts: (params?: Record<string, unknown>) =>
|
merchantAccounts: (params?: Record<string, unknown>) =>
|
||||||
request.get('/platform/merchant-accounts', { params }).then((r) => r.data.data as PageResult<MerchantMemberGroup>),
|
request.get('/platform/merchant-accounts', { params }).then((r) => r.data.data as PageResult<MerchantMemberGroup>),
|
||||||
|
deleteMerchantAccount: (userId: number) =>
|
||||||
|
request.delete(`/platform/merchant-accounts/${userId}`).then((r) => r.data.data),
|
||||||
merchants: (params?: Record<string, unknown>) =>
|
merchants: (params?: Record<string, unknown>) =>
|
||||||
request.get('/platform/merchants', { params }).then((r) => r.data.data as PageResult<Merchant>),
|
request.get('/platform/merchants', { params }).then((r) => r.data.data as PageResult<Merchant>),
|
||||||
createMerchant: (data: {
|
createMerchant: (data: {
|
||||||
|
|||||||
@@ -104,6 +104,16 @@ export default function PlatformUsers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeMerchantAccount = async (record: MerchantMember) => {
|
||||||
|
try {
|
||||||
|
await platformApi.deleteMerchantAccount(record.user_id)
|
||||||
|
message.success('商户账号已删除')
|
||||||
|
void loadMerchantAccounts()
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e instanceof Error ? e.message : '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns: ColumnsType<User> = [
|
const columns: ColumnsType<User> = [
|
||||||
{ title: '用户名', dataIndex: 'username', width: 260, render: (value) => <Typography.Text strong>{value}</Typography.Text> },
|
{ title: '用户名', dataIndex: 'username', width: 260, render: (value) => <Typography.Text strong>{value}</Typography.Text> },
|
||||||
{ title: '昵称', dataIndex: 'nickname', width: 220, render: (value) => value || '-' },
|
{ title: '昵称', dataIndex: 'nickname', width: 220, render: (value) => value || '-' },
|
||||||
@@ -128,6 +138,18 @@ export default function PlatformUsers() {
|
|||||||
{ title: '昵称', dataIndex: ['user', 'nickname'], width: 220, render: (_, record) => record.user?.nickname || '-' },
|
{ title: '昵称', dataIndex: ['user', 'nickname'], width: 220, render: (_, record) => record.user?.nickname || '-' },
|
||||||
{ title: '商户角色', dataIndex: 'role', width: 160, render: (role) => <Tag color="blue">{roleText(role)}</Tag> },
|
{ title: '商户角色', dataIndex: 'role', width: 160, render: (role) => <Tag color="blue">{roleText(role)}</Tag> },
|
||||||
{ title: '成员状态', dataIndex: 'status', width: 130, render: (status) => status === 1 ? <Tag color="green">启用</Tag> : <Tag>停用</Tag> },
|
{ title: '成员状态', dataIndex: 'status', width: 130, render: (status) => status === 1 ? <Tag color="green">启用</Tag> : <Tag>停用</Tag> },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 120,
|
||||||
|
render: (_, record) => <Popconfirm
|
||||||
|
title={`确认删除商户账号「${record.user?.username || `#${record.user_id}`}」?`}
|
||||||
|
description="该账号会被永久删除,并移除其在所有商户下的成员关系。"
|
||||||
|
onConfirm={() => void removeMerchantAccount(record)}
|
||||||
|
okButtonProps={{ danger: true }}
|
||||||
|
>
|
||||||
|
<Button type="link" danger size="small" icon={<DeleteOutlined />}>删除账号</Button>
|
||||||
|
</Popconfirm>,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const merchantColumns: ColumnsType<MerchantMemberGroup> = [
|
const merchantColumns: ColumnsType<MerchantMemberGroup> = [
|
||||||
|
|||||||
Reference in New Issue
Block a user