修复提现详情关联名称展示

This commit is contained in:
czl231
2026-08-16 23:33:38 +08:00
parent 043ba8df77
commit 27a4e62a43
6 changed files with 131 additions and 15 deletions

View File

@@ -0,0 +1,51 @@
# 提现详情关联名称展示修复操作日志
操作时间2026-08-16
操作类型:修改
影响模块:平台总后台、财务管理、提现记录详情
## 操作前状态
- 钱包唯一标识被声明为无目标资源的身份字段,详情页无法加载关系名称,固定显示“名称加载失败”。
- 审核人姓名快照已经展示,但响应中的审核人唯一标识又作为独立字段显示完整 UUID信息重复且不符合统一展示规范。
## 具体操作
1. 将提现记录的钱包字段声明为 `/wallet_basic` 关系,按钱包归属类型显示“用户钱包”“工作人员钱包”“气站钱包”“配送点钱包”或“平台钱包”。
2. 钱包关系继续保留详情跳转和完整 UUID 复制能力。
3. 将审核人唯一标识声明为 `/platform_account` 关系,优先使用接口已有的 `reviewer_name` 姓名快照展示。
4. 在提现详情专属契约中隐藏独立的审核人姓名快照字段,避免姓名与 UUID 分成两项重复展示。
5. 增加静态展示契约断言,防止钱包关系退回“名称加载失败”或审核人退回裸 UUID。
6. 提现列表直接使用 `reviewer_name` 审核姓名快照作为主文案,同时保留审核人标识复制和详情跳转。
## 行为变化
- 修改前:钱包显示“名称加载失败”;审核人姓名与完整审核人 UUID 分开显示。
- 修改后:钱包显示可读归属类型摘要;审核人显示姓名并附带可复制标识,可点击进入对应详情。
- 列表中的历史审核人不再依赖实时账户名称加载,避免显示“名称加载失败”。
- 提现金额、状态机、审核动作、接口入参和数据库数据均未改变。
## 代码变更
- `frontend/platform_admin/src/api/resources.ts`:补齐钱包及审核人的关系展示配置。
- `frontend/platform_admin/src/api/resource-display.ts`:新增钱包归属类型摘要格式化,并统一复用关系选项格式化器。
- `frontend/platform_admin/src/api/resource-detail-contract.ts`:增加提现详情字段顺序和姓名快照隐藏规则。
- `frontend/platform_admin/scripts/check-resource-display-contracts.mjs`:增加提现关系展示回归断言。
- `docs/项目文档_平台资源中文展示统一_v1.0.md`:记录 v1.7 变更。
## 验证结果
- `pnpm resource-display-contracts:check`:通过。
- `pnpm contract:check`通过48 个资源契约一致。
- `pnpm type:check`:通过。
- `pnpm exec prettier --check ...`:未执行,项目未安装 Prettier 命令;不影响既有质量脚本。
- `pnpm biome lint src/api/resources.ts src/api/resource-display.ts src/api/resource-detail-contract.ts scripts/check-resource-display-contracts.mjs`:通过;仅报告一项修改前已存在的 `Object.hasOwn` 建议警告。
- `pnpm lint`:未通过;全仓既有组件存在 2 个错误、188 个警告和 12 项提示,本次修改文件的定向 lint 未产生错误。
- `pnpm build`通过Vite 成功完成 2621 个模块的生产构建。
## 风险评估
- 本次仅改变提现详情的关联展示,不修改资金事实、权限、状态流转或后端接口。
- 钱包没有独立业务名称,因此使用归属主体类型作为稳定摘要;完整钱包标识仍可复制并可进入钱包详情核验。

View File

@@ -68,3 +68,5 @@ frontend/platform_admin/
- v1.4商城订单统一展示用户名称及空值业务文案Mock 订单改用商城独立交易状态,并补齐金额、支付时间和下单快照。
- v1.5商品评价列表的商城订单、商品和用户关系统一展示可读业务名称UUID 降级为复制信息。
- v1.6:商品分类的技术标识列与其他电商列表统一为“系统唯一标识”,显示截断值并保留复制能力。
- v1.7:提现详情的钱包关系改为可读归属类型摘要,审核人合并展示姓名快照与可复制标识,移除“名称加载失败”和重复裸 UUID。
- v1.7.1:提现列表的审核人改为直接使用审核姓名快照,并保留审核人标识复制与详情跳转,避免历史账户关系加载失败影响列表展示。

View File

@@ -1,6 +1,6 @@
/**
* 功能描述校验平台48类资源均具备中文展示字段并防止列表、详情退回数据库ID或动态英文列。
* 版本v1.2.1
* 版本v1.3.0
*/
import fs from 'node:fs';
import path from 'node:path';
@@ -79,6 +79,39 @@ for (const [key, label] of [
}
}
// 提现详情必须展示可读钱包摘要和审核人姓名UUID仅作为复制信息保留。
const withdrawalDefinition = resources.match(
/define\('wallet_apply_cash',[\s\S]*?\n\s*\], 'list',/,
)?.[0];
if (!withdrawalDefinition) throw new Error('无法读取提现记录资源定义');
for (const contract of [
"relation('wallet_basic_identity', '/wallet_basic'",
"relationOptionDisplay: 'wallet-owner'",
"relation('reviewer_identity', '/platform_account'",
"listDisplayKey: 'reviewer_name'",
'listDisplayIdentityCopy: true',
"detailDisplayKey: 'reviewer_name'",
]) {
if (!withdrawalDefinition.includes(contract)) {
throw new Error(`提现详情缺少可读关系展示契约:${contract}`);
}
}
assertIncludes(
detailContract,
"wallet_apply_cash: {",
'提现详情必须声明专属字段顺序与审核姓名快照隐藏规则',
);
assertIncludes(
detailContract,
"hiddenKeys: ['reviewer_name']",
'提现详情不得重复展示审核人姓名快照和审核人唯一标识',
);
assertIncludes(
display,
"field.relationOptionDisplay === 'wallet-owner'",
'钱包关系必须提供按归属类型生成的可读摘要',
);
// 购物车关联列必须展示用户和商品名称UUID仅作为复制与排障信息保留。
const cartDefinition = resources.match(
/define\('ec_cart',[\s\S]*?\n\s*\]\),/,

View File

@@ -1,6 +1,6 @@
/**
* 功能描述:声明平台资源详情的字段顺序、固定关联表列和 JSON 展示规则。
* 版本v1.0.0
* 版本v1.1.0
*/
export type ResourceCollectionContract = {
@@ -132,6 +132,14 @@ const contracts: Record<string, ResourceDetailContract> = {
'wallet_basic_identity', 'identity',
],
},
wallet_apply_cash: {
leadingKeys: [
'cash_no', 'apply_status', 'wallet_basic_identity', 'amount',
'reviewer_identity', 'review_reason', 'reviewed_at', 'identity',
],
// 审核姓名作为审核人关系的可读快照使用,不再重复渲染独立字段。
hiddenKeys: ['reviewer_name'],
},
ec_order: {
leadingKeys: [
'order_no', 'order_status', 'request_no', 'user_account_identity',

View File

@@ -1,6 +1,6 @@
/**
* 功能:统一资源列表与详情页面的字段名称、关系、金额、状态和时间展示。
* 版本v1.6.0
* 版本v1.7.0
*/
import dayjs from 'dayjs';
import type { RecordPageMode, ResourceRow } from './resource-page-rules';
@@ -277,6 +277,9 @@ export function relationOptionLabel(
if (field.relationOptionDisplay === 'product-name-type') {
return productNameTypeLabel(option, options);
}
if (field.relationOptionDisplay === 'wallet-owner') {
return walletOwnerLabel(option);
}
const configuredLabel = field.relationOptionLabelKey
? option[field.relationOptionLabelKey]
: undefined;
@@ -295,6 +298,20 @@ export function relationOptionLabel(
: label;
}
/** 钱包没有独立业务名称时,按归属主体类型提供稳定可读摘要。 */
function walletOwnerLabel(option: ResourceRow) {
const ownerType = String(option.owner_type ?? '');
return (
{
user: '用户钱包',
staff: '工作人员钱包',
gas: '气站钱包',
delivery: '配送点钱包',
platform: '平台钱包',
}[ownerType] ?? '钱包账户'
);
}
/** 合同气瓶优先显示具体设备名称和绑定时类型;同名同类型时追加编码以便区分。 */
function productNameTypeLabel(option: ResourceRow, options: ResourceRow[]) {
const name = String(option.product_name ?? '').trim();
@@ -326,14 +343,7 @@ function relationLabel(
(option) => String(option.identity) === identity,
);
if (!match) return '名称加载失败';
if (field.relation === '/staff_account') {
return relationOptionLabel(
field,
match,
relationOptions[field.relation ?? ''] ?? [],
);
}
return optionLabel(match);
return relationOptionLabel(field, match, relationOptions[resource] ?? []);
}
/** 根据当前记录的主体类型解析静态或动态关系资源。 */

View File

@@ -95,8 +95,8 @@ export type ResourceField = {
dynamicRelation?: ResourceDynamicRelation;
/** 关联选项优先使用的业务展示字段。 */
relationOptionLabelKey?: string;
/** 关联选项的组合展示方式;合同气瓶使用设备名称、绑定类型快照和编码消歧。 */
relationOptionDisplay?: 'product-name-type';
/** 关联选项的组合展示方式;合同气瓶和钱包分别使用对应的可读业务摘要。 */
relationOptionDisplay?: 'product-name-type' | 'wallet-owner';
/** 该字段为真时,在关联选项后追加“默认地址”。 */
relationOptionDefaultKey?: string;
/** 候选加载完成后自动选中该布尔字段为真的选项。 */
@@ -784,7 +784,12 @@ export const resources: ResourceUiDefinition[] = [
], { canCreate: false, canEdit: false, canChangeStatus: false, canArchive: false }),
define('wallet_apply_cash', '提现记录', 'readonly', [
f('cash_no'),
f('wallet_basic_identity', { type: 'identity' }),
relation('wallet_basic_identity', '/wallet_basic', false, {
label: '钱包',
relationOptionDisplay: 'wallet-owner',
displayRelationLabel: true,
showIdentityCopy: true,
}),
f('amount'),
f('apply_status', { type: 'select', options: [
{ label: '待处理', value: 10 },
@@ -792,7 +797,14 @@ export const resources: ResourceUiDefinition[] = [
{ label: '已驳回', value: 26 },
{ label: '已完成', value: 23 },
] }),
f('reviewer_name'),
relation('reviewer_identity', '/platform_account', false, {
label: '审核人',
listDisplayKey: 'reviewer_name',
listDisplayIdentityCopy: true,
detailDisplayKey: 'reviewer_name',
displayRelationLabel: true,
showIdentityCopy: true,
}),
f('review_reason'),
f('reviewed_at'),
f('completed_at'),