feat(finance): show cross-app withdrawal sources

This commit is contained in:
zhx 2026-07-17 18:38:10 +08:00
parent 169ada93b5
commit 42e9eb0804
6 changed files with 137 additions and 2 deletions

View File

@ -4556,6 +4556,27 @@
]
}
},
"/integrations/withdrawal-applications": {
"post": {
"operationId": "createExternalWithdrawalApplication",
"description": "来源钱包使用 App 独立 Bearer token 幂等提交提现申请,不使用后台用户 JWT。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ExternalWithdrawalApplicationInput"
}
}
}
},
"responses": {
"201": {
"$ref": "#/components/responses/FinanceWithdrawalApplicationResponse"
}
}
}
},
"/admin/finance/withdrawal-applications": {
"get": {
"operationId": "listFinanceWithdrawalApplications",
@ -14288,6 +14309,14 @@
"appName": {
"type": "string"
},
"sourceSystem": {
"type": "string",
"description": "资金执行来源hyapp_wallet 为平台钱包likei 为 Aslan legacy 钱包"
},
"sourceApplicationId": {
"type": "string",
"description": "来源钱包的提现申请号,用于跨系统审计和幂等回调"
},
"userId": {
"type": "string"
},
@ -14362,6 +14391,32 @@
}
}
},
"ExternalWithdrawalApplicationInput": {
"type": "object",
"required": [
"appCode",
"sourceSystem",
"sourceApplicationId",
"userId",
"salaryAssetType",
"withdrawAmount",
"withdrawAmountMinor",
"withdrawMethod",
"withdrawAddress"
],
"properties": {
"appCode": { "type": "string" },
"sourceSystem": { "type": "string" },
"sourceApplicationId": { "type": "string" },
"userId": { "type": "string" },
"salaryAssetType": { "type": "string" },
"withdrawAmount": { "type": "string" },
"withdrawAmountMinor": { "type": "integer", "minimum": 1 },
"withdrawMethod": { "type": "string" },
"withdrawAddress": { "type": "string" },
"createdAtMs": { "type": "integer", "format": "int64" }
}
},
"FinanceCoinSellerRechargeOrderInput": {
"type": "object",
"required": ["appCode", "targetUserId", "usdAmount", "providerCode", "externalOrderNo"],

View File

@ -73,6 +73,7 @@ export function FinanceWithdrawalApplicationList({
<TableHead>
<TableRow>
<TableCell>APP</TableCell>
<TableCell>来源单号</TableCell>
<TableCell>用户ID</TableCell>
<TableCell align="right">提现金额</TableCell>
<TableCell>提现方式</TableCell>
@ -107,6 +108,12 @@ export function FinanceWithdrawalApplicationList({
size="small"
/>
</TableCell>
<TableCell>
<Stacked
primary={sourceSystemLabel(item.sourceSystem)}
secondary={item.sourceApplicationId || ""}
/>
</TableCell>
<TableCell>{item.userId || "-"}</TableCell>
<TableCell align="right">$ {formatAmount(item.withdrawAmount)}</TableCell>
<TableCell>{item.withdrawMethod || "-"}</TableCell>
@ -214,7 +221,7 @@ export function FinanceWithdrawalApplicationList({
))
) : (
<TableRow>
<TableCell align="center" colSpan={isOperationsReview ? 10 : 11}>
<TableCell align="center" colSpan={isOperationsReview ? 11 : 12}>
当前无数据
</TableCell>
</TableRow>
@ -240,6 +247,16 @@ export function FinanceWithdrawalApplicationList({
);
}
function sourceSystemLabel(value) {
if (!value || value === "hyapp_wallet") {
return "HY 钱包";
}
if (value === "likei") {
return "Likei";
}
return value;
}
function ReviewContext({ application }) {
return (
<span className="finance-review-context">

View File

@ -50,6 +50,8 @@ test("renders withdrawal application table with requested finance columns", () =
operationsReviewerName: "运营一",
operationsReviewerUserId: 11,
operationsStatus: "approved",
sourceApplicationId: "991001",
sourceSystem: "likei",
status: "approved",
userId: "10001",
withdrawAddress: "TRX-address",
@ -62,6 +64,7 @@ test("renders withdrawal application table with requested finance columns", () =
});
expect(screen.getByRole("columnheader", { name: "APP" })).toBeInTheDocument();
expect(screen.getByRole("columnheader", { name: "来源单号" })).toBeInTheDocument();
expect(screen.getByRole("columnheader", { name: "用户ID" })).toBeInTheDocument();
expect(screen.getByRole("columnheader", { name: "提现金额" })).toBeInTheDocument();
expect(screen.getByRole("columnheader", { name: "提现方式" })).toBeInTheDocument();
@ -73,6 +76,8 @@ test("renders withdrawal application table with requested finance columns", () =
expect(screen.getByRole("columnheader", { name: "财务审核原因" })).toBeInTheDocument();
expect(screen.getByRole("columnheader", { name: "操作" })).toBeInTheDocument();
expect(screen.getByText("lalu")).toBeInTheDocument();
expect(screen.getByText("Likei")).toBeInTheDocument();
expect(screen.getByText("991001")).toBeInTheDocument();
expect(screen.getByText("10001")).toBeInTheDocument();
expect(screen.getByText("$ 12.5")).toBeInTheDocument();
expect(screen.getByText("USDT-TRC20")).toBeInTheDocument();
@ -88,7 +93,7 @@ test("renders withdrawal application table with requested finance columns", () =
test("keeps empty withdrawal table colspan aligned with visible columns", () => {
const { container } = renderList(baseProps);
expect(container.querySelector("tbody td")?.colSpan).toBe(11);
expect(container.querySelector("tbody td")?.colSpan).toBe(12);
expect(screen.getByText("当前无数据")).toBeInTheDocument();
});

View File

@ -31,6 +31,8 @@ export function normalizeWithdrawalApplication(item = {}) {
operationsReviewerUserId: item.operationsReviewerUserId ?? item.operations_reviewer_user_id,
operationsStatus: stringValue(item.operationsStatus ?? item.operations_status),
salaryAssetType: stringValue(item.salaryAssetType ?? item.salary_asset_type),
sourceApplicationId: stringValue(item.sourceApplicationId ?? item.source_application_id),
sourceSystem: stringValue(item.sourceSystem ?? item.source_system),
status: stringValue(item.status),
updatedAtMs: numberOrNull(item.updatedAtMs ?? item.updated_at_ms),
userId: stringValue(item.userId ?? item.user_id),

View File

@ -54,6 +54,7 @@ export const API_OPERATIONS = {
createEmojiPack: "createEmojiPack",
createExploreTab: "createExploreTab",
createExternalAdminUser: "createExternalAdminUser",
createExternalWithdrawalApplication: "createExternalWithdrawalApplication",
createFinanceCoinSellerRechargeOrder: "createFinanceCoinSellerRechargeOrder",
createFullServerNoticeFanout: "createFullServerNoticeFanout",
createGift: "createGift",
@ -712,6 +713,11 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
permission: "external-admin-user:create",
permissions: ["external-admin-user:create","external-admin-user:permissions"]
},
createExternalWithdrawalApplication: {
method: "POST",
operationId: API_OPERATIONS.createExternalWithdrawalApplication,
path: "/v1/integrations/withdrawal-applications"
},
createFinanceCoinSellerRechargeOrder: {
method: "POST",
operationId: API_OPERATIONS.createFinanceCoinSellerRechargeOrder,

View File

@ -2636,6 +2636,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/integrations/withdrawal-applications": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** @description 来源钱包使用 App 独立 Bearer token 幂等提交提现申请,不使用后台用户 JWT。 */
post: operations["createExternalWithdrawalApplication"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/admin/finance/withdrawal-applications": {
parameters: {
query?: never;
@ -6652,6 +6669,10 @@ export interface components {
id: number;
appCode: string;
appName?: string;
/** @description 资金执行来源hyapp_wallet 为平台钱包likei 为 Aslan legacy 钱包 */
sourceSystem?: string;
/** @description 来源钱包的提现申请号,用于跨系统审计和幂等回调 */
sourceApplicationId?: string;
userId: string;
salaryAssetType?: string;
withdrawAmount: string;
@ -6679,6 +6700,19 @@ export interface components {
createdAtMs: number;
updatedAtMs?: number;
};
ExternalWithdrawalApplicationInput: {
appCode: string;
sourceSystem: string;
sourceApplicationId: string;
userId: string;
salaryAssetType: string;
withdrawAmount: string;
withdrawAmountMinor: number;
withdrawMethod: string;
withdrawAddress: string;
/** Format: int64 */
createdAtMs?: number;
};
FinanceCoinSellerRechargeOrderInput: {
appCode: string;
targetUserId: string;
@ -11482,6 +11516,22 @@ export interface operations {
200: components["responses"]["FinanceScopeCatalogResponse"];
};
};
createExternalWithdrawalApplication: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["ExternalWithdrawalApplicationInput"];
};
};
responses: {
201: components["responses"]["FinanceWithdrawalApplicationResponse"];
};
};
listFinanceWithdrawalApplications: {
parameters: {
query?: {