修复提现详情关联名称展示
This commit is contained in:
51
docs/操作日志_提现详情关联名称展示修复_20260816.md
Normal file
51
docs/操作日志_提现详情关联名称展示修复_20260816.md
Normal 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 个模块的生产构建。
|
||||||
|
|
||||||
|
## 风险评估
|
||||||
|
|
||||||
|
- 本次仅改变提现详情的关联展示,不修改资金事实、权限、状态流转或后端接口。
|
||||||
|
- 钱包没有独立业务名称,因此使用归属主体类型作为稳定摘要;完整钱包标识仍可复制并可进入钱包详情核验。
|
||||||
@@ -68,3 +68,5 @@ frontend/platform_admin/
|
|||||||
- v1.4:商城订单统一展示用户名称及空值业务文案;Mock 订单改用商城独立交易状态,并补齐金额、支付时间和下单快照。
|
- v1.4:商城订单统一展示用户名称及空值业务文案;Mock 订单改用商城独立交易状态,并补齐金额、支付时间和下单快照。
|
||||||
- v1.5:商品评价列表的商城订单、商品和用户关系统一展示可读业务名称,UUID 降级为复制信息。
|
- v1.5:商品评价列表的商城订单、商品和用户关系统一展示可读业务名称,UUID 降级为复制信息。
|
||||||
- v1.6:商品分类的技术标识列与其他电商列表统一为“系统唯一标识”,显示截断值并保留复制能力。
|
- v1.6:商品分类的技术标识列与其他电商列表统一为“系统唯一标识”,显示截断值并保留复制能力。
|
||||||
|
- v1.7:提现详情的钱包关系改为可读归属类型摘要,审核人合并展示姓名快照与可复制标识,移除“名称加载失败”和重复裸 UUID。
|
||||||
|
- v1.7.1:提现列表的审核人改为直接使用审核姓名快照,并保留审核人标识复制与详情跳转,避免历史账户关系加载失败影响列表展示。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 功能描述:校验平台48类资源均具备中文展示字段,并防止列表、详情退回数据库ID或动态英文列。
|
* 功能描述:校验平台48类资源均具备中文展示字段,并防止列表、详情退回数据库ID或动态英文列。
|
||||||
* 版本:v1.2.1
|
* 版本:v1.3.0
|
||||||
*/
|
*/
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
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仅作为复制与排障信息保留。
|
// 购物车关联列必须展示用户和商品名称,UUID仅作为复制与排障信息保留。
|
||||||
const cartDefinition = resources.match(
|
const cartDefinition = resources.match(
|
||||||
/define\('ec_cart',[\s\S]*?\n\s*\]\),/,
|
/define\('ec_cart',[\s\S]*?\n\s*\]\),/,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 功能描述:声明平台资源详情的字段顺序、固定关联表列和 JSON 展示规则。
|
* 功能描述:声明平台资源详情的字段顺序、固定关联表列和 JSON 展示规则。
|
||||||
* 版本:v1.0.0
|
* 版本:v1.1.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type ResourceCollectionContract = {
|
export type ResourceCollectionContract = {
|
||||||
@@ -132,6 +132,14 @@ const contracts: Record<string, ResourceDetailContract> = {
|
|||||||
'wallet_basic_identity', 'identity',
|
'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: {
|
ec_order: {
|
||||||
leadingKeys: [
|
leadingKeys: [
|
||||||
'order_no', 'order_status', 'request_no', 'user_account_identity',
|
'order_no', 'order_status', 'request_no', 'user_account_identity',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* 功能:统一资源列表与详情页面的字段名称、关系、金额、状态和时间展示。
|
* 功能:统一资源列表与详情页面的字段名称、关系、金额、状态和时间展示。
|
||||||
* 版本:v1.6.0
|
* 版本:v1.7.0
|
||||||
*/
|
*/
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { RecordPageMode, ResourceRow } from './resource-page-rules';
|
import type { RecordPageMode, ResourceRow } from './resource-page-rules';
|
||||||
@@ -277,6 +277,9 @@ export function relationOptionLabel(
|
|||||||
if (field.relationOptionDisplay === 'product-name-type') {
|
if (field.relationOptionDisplay === 'product-name-type') {
|
||||||
return productNameTypeLabel(option, options);
|
return productNameTypeLabel(option, options);
|
||||||
}
|
}
|
||||||
|
if (field.relationOptionDisplay === 'wallet-owner') {
|
||||||
|
return walletOwnerLabel(option);
|
||||||
|
}
|
||||||
const configuredLabel = field.relationOptionLabelKey
|
const configuredLabel = field.relationOptionLabelKey
|
||||||
? option[field.relationOptionLabelKey]
|
? option[field.relationOptionLabelKey]
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -295,6 +298,20 @@ export function relationOptionLabel(
|
|||||||
: label;
|
: label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 钱包没有独立业务名称时,按归属主体类型提供稳定可读摘要。 */
|
||||||
|
function walletOwnerLabel(option: ResourceRow) {
|
||||||
|
const ownerType = String(option.owner_type ?? '');
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
user: '用户钱包',
|
||||||
|
staff: '工作人员钱包',
|
||||||
|
gas: '气站钱包',
|
||||||
|
delivery: '配送点钱包',
|
||||||
|
platform: '平台钱包',
|
||||||
|
}[ownerType] ?? '钱包账户'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** 合同气瓶优先显示具体设备名称和绑定时类型;同名同类型时追加编码以便区分。 */
|
/** 合同气瓶优先显示具体设备名称和绑定时类型;同名同类型时追加编码以便区分。 */
|
||||||
function productNameTypeLabel(option: ResourceRow, options: ResourceRow[]) {
|
function productNameTypeLabel(option: ResourceRow, options: ResourceRow[]) {
|
||||||
const name = String(option.product_name ?? '').trim();
|
const name = String(option.product_name ?? '').trim();
|
||||||
@@ -326,14 +343,7 @@ function relationLabel(
|
|||||||
(option) => String(option.identity) === identity,
|
(option) => String(option.identity) === identity,
|
||||||
);
|
);
|
||||||
if (!match) return '名称加载失败';
|
if (!match) return '名称加载失败';
|
||||||
if (field.relation === '/staff_account') {
|
return relationOptionLabel(field, match, relationOptions[resource] ?? []);
|
||||||
return relationOptionLabel(
|
|
||||||
field,
|
|
||||||
match,
|
|
||||||
relationOptions[field.relation ?? ''] ?? [],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return optionLabel(match);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 根据当前记录的主体类型解析静态或动态关系资源。 */
|
/** 根据当前记录的主体类型解析静态或动态关系资源。 */
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ export type ResourceField = {
|
|||||||
dynamicRelation?: ResourceDynamicRelation;
|
dynamicRelation?: ResourceDynamicRelation;
|
||||||
/** 关联选项优先使用的业务展示字段。 */
|
/** 关联选项优先使用的业务展示字段。 */
|
||||||
relationOptionLabelKey?: string;
|
relationOptionLabelKey?: string;
|
||||||
/** 关联选项的组合展示方式;合同气瓶使用设备名称、绑定类型快照和编码消歧。 */
|
/** 关联选项的组合展示方式;合同气瓶和钱包分别使用对应的可读业务摘要。 */
|
||||||
relationOptionDisplay?: 'product-name-type';
|
relationOptionDisplay?: 'product-name-type' | 'wallet-owner';
|
||||||
/** 该字段为真时,在关联选项后追加“默认地址”。 */
|
/** 该字段为真时,在关联选项后追加“默认地址”。 */
|
||||||
relationOptionDefaultKey?: string;
|
relationOptionDefaultKey?: string;
|
||||||
/** 候选加载完成后自动选中该布尔字段为真的选项。 */
|
/** 候选加载完成后自动选中该布尔字段为真的选项。 */
|
||||||
@@ -784,7 +784,12 @@ export const resources: ResourceUiDefinition[] = [
|
|||||||
], { canCreate: false, canEdit: false, canChangeStatus: false, canArchive: false }),
|
], { canCreate: false, canEdit: false, canChangeStatus: false, canArchive: false }),
|
||||||
define('wallet_apply_cash', '提现记录', 'readonly', [
|
define('wallet_apply_cash', '提现记录', 'readonly', [
|
||||||
f('cash_no'),
|
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('amount'),
|
||||||
f('apply_status', { type: 'select', options: [
|
f('apply_status', { type: 'select', options: [
|
||||||
{ label: '待处理', value: 10 },
|
{ label: '待处理', value: 10 },
|
||||||
@@ -792,7 +797,14 @@ export const resources: ResourceUiDefinition[] = [
|
|||||||
{ label: '已驳回', value: 26 },
|
{ label: '已驳回', value: 26 },
|
||||||
{ label: '已完成', value: 23 },
|
{ 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('review_reason'),
|
||||||
f('reviewed_at'),
|
f('reviewed_at'),
|
||||||
f('completed_at'),
|
f('completed_at'),
|
||||||
|
|||||||
Reference in New Issue
Block a user