优化api密钥
This commit is contained in:
@@ -302,6 +302,16 @@ func (h *MerchantHandler) UpdateAPIClientStatus(c *gin.Context) {
|
|||||||
response.OK(c, nil)
|
response.OK(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAPIClient DELETE /api/merchant/api-clients/:id
|
||||||
|
func (h *MerchantHandler) DeleteAPIClient(c *gin.Context) {
|
||||||
|
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||||
|
if err := h.merchantSvc.DeleteAPIClient(middleware.GetMerchantID(c), uint(id), middleware.GetUserID(c)); err != nil {
|
||||||
|
response.BadRequest(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OK(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *MerchantHandler) ListCallbacks(c *gin.Context) {
|
func (h *MerchantHandler) ListCallbacks(c *gin.Context) {
|
||||||
list, err := h.callbackSvc.ListSubscriptions(middleware.GetMerchantID(c))
|
list, err := h.callbackSvc.ListSubscriptions(middleware.GetMerchantID(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -113,9 +113,10 @@ func Setup(h *Handlers) *gin.Engine {
|
|||||||
merchant.GET("/wallet", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator, model.MemberRoleFinance), h.Merchant.GetWallet)
|
merchant.GET("/wallet", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator, model.MemberRoleFinance), h.Merchant.GetWallet)
|
||||||
merchant.GET("/wallet/ledger", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleFinance), h.Merchant.ListWalletLedger)
|
merchant.GET("/wallet/ledger", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleFinance), h.Merchant.ListWalletLedger)
|
||||||
merchant.POST("/wallet/adjust", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleFinance), h.Merchant.AdjustWallet)
|
merchant.POST("/wallet/adjust", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureWallet), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleFinance), h.Merchant.AdjustWallet)
|
||||||
merchant.GET("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.ListAPIClients)
|
merchant.GET("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), h.Merchant.ListAPIClients)
|
||||||
merchant.POST("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.CreateAPIClient)
|
merchant.POST("/api-clients", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.CreateAPIClient)
|
||||||
merchant.PATCH("/api-clients/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.UpdateAPIClientStatus)
|
merchant.PATCH("/api-clients/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.UpdateAPIClientStatus)
|
||||||
|
merchant.DELETE("/api-clients/:id", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureAPI), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.DeleteAPIClient)
|
||||||
merchant.GET("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.ListCallbacks)
|
merchant.GET("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.ListCallbacks)
|
||||||
merchant.POST("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.CreateCallback)
|
merchant.POST("/callbacks", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.CreateCallback)
|
||||||
merchant.PATCH("/callbacks/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.UpdateCallbackStatus)
|
merchant.PATCH("/callbacks/:id/status", middleware.RequireMerchantFeature(h.OpenDB, model.MerchantFeatureCallbacks), middleware.RequireMerchantRole(model.MemberRoleOwner, model.MemberRoleOperator), h.Merchant.UpdateCallbackStatus)
|
||||||
|
|||||||
@@ -506,7 +506,9 @@ func (s *MerchantService) CreateAPIClient(merchantID uint, in CreateAPIClientInp
|
|||||||
return errors.New("商户不存在或已禁用")
|
return errors.New("商户不存在或已禁用")
|
||||||
}
|
}
|
||||||
var clientCount int64
|
var clientCount int64
|
||||||
if err := tx.Model(&model.APIClient{}).Where("merchant_id = ?", merchantID).Count(&clientCount).Error; err != nil {
|
if err := tx.Model(&model.APIClient{}).
|
||||||
|
Where("merchant_id = ? AND status = ?", merchantID, model.APIClientStatusActive).
|
||||||
|
Count(&clientCount).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if clientCount >= MaxAPIClientsPerMerchant {
|
if clientCount >= MaxAPIClientsPerMerchant {
|
||||||
@@ -545,6 +547,27 @@ func (s *MerchantService) UpdateAPIClientStatus(merchantID, id uint, status stri
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAPIClient 删除商户的 API 密钥(物理删除,不可恢复)。
|
||||||
|
// 删除前要求先停用,避免在用的密钥被误删。
|
||||||
|
func (s *MerchantService) DeleteAPIClient(merchantID, id uint, actorUserID uint) error {
|
||||||
|
return s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
var client model.APIClient
|
||||||
|
if err := tx.Where("id = ? AND merchant_id = ?", id, merchantID).First(&client).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return errors.New("API 客户端不存在")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if client.Status == model.APIClientStatusActive {
|
||||||
|
return errors.New("请先停用该密钥再删除")
|
||||||
|
}
|
||||||
|
if err := tx.Delete(&client).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeAudit(tx, &merchantID, &actorUserID, nil, "api_client.delete", "api_client", fmt.Sprint(id), map[string]string{"name": client.Name, "app_key": client.AppKey})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ProductCatalogItem 是商品目录(自营商户可售商品)的展示项,供平台管理员分配商品时勾选。
|
// ProductCatalogItem 是商品目录(自营商户可售商品)的展示项,供平台管理员分配商品时勾选。
|
||||||
type ProductCatalogItem struct {
|
type ProductCatalogItem struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ export const merchantApi = {
|
|||||||
}) => request.post('/merchant/api-clients', data).then((r) => r.data.data as ApiCredential),
|
}) => request.post('/merchant/api-clients', data).then((r) => r.data.data as ApiCredential),
|
||||||
updateApiClientStatus: (id: number, status: ApiClient['status']) =>
|
updateApiClientStatus: (id: number, status: ApiClient['status']) =>
|
||||||
request.patch(`/merchant/api-clients/${id}/status`, { status }).then((r) => r.data.data),
|
request.patch(`/merchant/api-clients/${id}/status`, { status }).then((r) => r.data.data),
|
||||||
|
deleteApiClient: (id: number) =>
|
||||||
|
request.delete(`/merchant/api-clients/${id}`).then((r) => r.data.data),
|
||||||
callbacks: () =>
|
callbacks: () =>
|
||||||
request.get('/merchant/callbacks').then((r) => r.data.data as CallbackSubscription[]),
|
request.get('/merchant/callbacks').then((r) => r.data.data as CallbackSubscription[]),
|
||||||
createCallback: (data: { name: string; url: string; events: string }) =>
|
createCallback: (data: { name: string; url: string; events: string }) =>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const adminSections: SidebarSection[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'funds',
|
key: 'funds',
|
||||||
label: '资金',
|
label: '积分',
|
||||||
icon: <WalletOutlined />,
|
icon: <WalletOutlined />,
|
||||||
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
||||||
},
|
},
|
||||||
@@ -114,7 +114,7 @@ const merchantSections: SidebarSection[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'funds',
|
key: 'funds',
|
||||||
label: '资金',
|
label: '积分',
|
||||||
icon: <WalletOutlined />,
|
icon: <WalletOutlined />,
|
||||||
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Modal,
|
Modal,
|
||||||
|
Popconfirm,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
@@ -206,14 +207,10 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
}
|
}
|
||||||
}, [ledger.page, ledger.size, merchantRole])
|
}, [ledger.page, ledger.size, merchantRole])
|
||||||
|
|
||||||
const loadAPIClients = useCallback(async (role = merchantRole) => {
|
const loadAPIClients = useCallback(async () => {
|
||||||
if (role !== 'owner' && role !== 'operator') {
|
|
||||||
setApiClients([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const clientData = await merchantApi.apiClients()
|
const clientData = await merchantApi.apiClients()
|
||||||
setApiClients(clientData || [])
|
setApiClients(clientData || [])
|
||||||
}, [merchantRole])
|
}, [])
|
||||||
|
|
||||||
const loadCallbacks = useCallback(async (role = merchantRole) => {
|
const loadCallbacks = useCallback(async (role = merchantRole) => {
|
||||||
if (role !== 'owner' && role !== 'operator') {
|
if (role !== 'owner' && role !== 'operator') {
|
||||||
@@ -245,7 +242,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
await loadWallet(undefined, undefined, role)
|
await loadWallet(undefined, undefined, role)
|
||||||
return
|
return
|
||||||
case 'api':
|
case 'api':
|
||||||
await loadAPIClients(role)
|
await loadAPIClients()
|
||||||
return
|
return
|
||||||
case 'callbacks':
|
case 'callbacks':
|
||||||
await loadCallbacks(role)
|
await loadCallbacks(role)
|
||||||
@@ -457,6 +454,16 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteAPIClient = async (record: ApiClient) => {
|
||||||
|
try {
|
||||||
|
await merchantApi.deleteApiClient(record.id)
|
||||||
|
message.success('密钥已删除')
|
||||||
|
loadAPIClients()
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e instanceof Error ? e.message : '删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const toggleCallback = async (record: CallbackSubscription) => {
|
const toggleCallback = async (record: CallbackSubscription) => {
|
||||||
try {
|
try {
|
||||||
await merchantApi.updateCallbackStatus(record.id, record.status === 'active' ? 'disabled' : 'active')
|
await merchantApi.updateCallbackStatus(record.id, record.status === 'active' ? 'disabled' : 'active')
|
||||||
@@ -550,11 +557,24 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 100,
|
width: 150,
|
||||||
render: (_, record) => canManage ? (
|
render: (_, record) => canManage ? (
|
||||||
|
<Space size={0}>
|
||||||
<Button type="link" size="small" onClick={() => toggleAPIClient(record)}>
|
<Button type="link" size="small" onClick={() => toggleAPIClient(record)}>
|
||||||
{record.status === 'active' ? '禁用' : '启用'}
|
{record.status === 'active' ? '禁用' : '启用'}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Popconfirm
|
||||||
|
title="删除该密钥?"
|
||||||
|
description={record.status === 'active' ? '请先停用该密钥再删除' : '删除后不可恢复,使用该密钥的对接方将无法调用开放接口。'}
|
||||||
|
okText="删除"
|
||||||
|
okButtonProps={{ danger: true }}
|
||||||
|
onConfirm={() => deleteAPIClient(record)}
|
||||||
|
>
|
||||||
|
<Button type="link" size="small" danger disabled={record.status === 'active'}>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
) : '-',
|
) : '-',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user