积分充值全流程落地:申请审核入账、凭证上传压缩转webp、管理员调账收权

- 新增积分充值申请与审核:商户提交充值申请(凭证图片必填,1元=100积分),
  管理员审核通过后事务内自动入账,幂等键防重复
- 新增通用图片上传组件 ImageUploader:canvas 压缩转 webp、预览、删除,
  后端按魔数校验图片格式并持久化到 data/uploads
- 管理员钱包调整改为按商户维度:移除商户侧调账接口,新增管理员
  平台商户积分调整与钱包流水查看
- 低余额 Webhook 告警配置持久化,充值入账/调账后自动检查推送
- 平台商户操作列移除进入/成员,积分充值页接入真实接口并修复金额单位换算
This commit is contained in:
yml2213
2026-08-04 13:03:29 +08:00
parent 54f05c15a4
commit 941e7ad79a
22 changed files with 1658 additions and 18 deletions
+26
View File
@@ -12,11 +12,13 @@ import type {
DeliverySubmitResult,
FulfillmentOrder,
LoginResult,
LowBalanceAlertConfig,
Merchant,
MerchantMember,
MerchantProduct,
PageResult,
ProductCatalogItem,
RechargeApplication,
User,
WalletAccount,
WalletLedgerEntry,
@@ -99,6 +101,26 @@ export const merchantApi = {
request.get('/merchant/members').then((r) => r.data.data as MerchantMember[]),
addMember: (data: { user_id: number; role: MerchantMember['role']; is_default?: boolean }) =>
request.post('/merchant/members', data).then((r) => r.data.data as MerchantMember),
rechargeApplications: (params?: Record<string, unknown>) =>
request.get('/merchant/recharge/applications', { params }).then((r) => r.data.data as PageResult<RechargeApplication>),
createRechargeApplication: (data: { amount_cny: number; vouchers: string[]; note?: string }) =>
// amount_cny 单位:分(1元 = 100分),积分按 1元 = 100积分 折算
request.post('/merchant/recharge/applications', data).then((r) => r.data.data as RechargeApplication),
alertConfig: () =>
request.get('/merchant/recharge/alert-config').then((r) => r.data.data as LowBalanceAlertConfig),
saveAlertConfig: (data: LowBalanceAlertConfig) =>
request.put('/merchant/recharge/alert-config', data).then((r) => r.data.data as LowBalanceAlertConfig),
}
export const uploadApi = {
file: (file: File) => {
const form = new FormData()
form.append('file', file)
return request.post('/upload', form, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 60000,
}).then((r) => r.data.data as { url: string })
},
}
export const deliveryApi = {
@@ -158,4 +180,8 @@ export const platformApi = {
request.get(`/platform/merchants/${merchantId}/wallet/ledger`, { params }).then((r) => r.data.data as PageResult<WalletLedgerEntry>),
adjustWallet: (merchantId: number, data: { amount: number; idempotency_key: string; note?: string }) =>
request.post(`/platform/merchants/${merchantId}/wallet/adjust`, data).then((r) => r.data.data as WalletAccount),
rechargeApplications: (params?: Record<string, unknown>) =>
request.get('/platform/recharge/applications', { params }).then((r) => r.data.data as PageResult<RechargeApplication>),
reviewRechargeApplication: (id: number, data: { approved: boolean; review_note?: string }) =>
request.post(`/platform/recharge/applications/${id}/review`, data).then((r) => r.data.data as RechargeApplication),
}