feat(wallet+admin-web): 系统账户流水增加来源用户账户和来源备注列

问题:系统账户(S0000000001等)、省/市区域/团队账户的流水明细
只显示 allocationType 英文标识,无法追溯是哪个用户的认种产生的。

解决方案:从 wallet_ledger_entries.payload_json.metadata 中提取
sourceAccountSequence 和 memo 字段,通过 API 返回给前端展示。

后端 wallet-service 改动:
- LedgerEntryDTO 新增 sourceAccountSequence / sourceMemo 两个可选字段
- 新增 extractPayloadInfo() 辅助函数统一从 payloadJson 提取信息
- 替换所有 5 处 LedgerEntryDTO 映射,使用 extractPayloadInfo()
- 向后兼容:旧记录无 metadata 时返回 null,不影响已有功能

前端 admin-web 改动:
- LedgerEntryDTO 类型新增 sourceAccountSequence / sourceMemo 字段
- 固定账户明细表格和分类账明细表格增加"来源账户"和"来源备注"列
- 新增 .sourceAccount 样式(等宽字体显示账户序列号)

数据来源说明:
- 正常认种:reward-service 传入 metadata 含完整中文 memo 和 sourceAccountSequence
- 预种:planting-service 传入 metadata 含 sourceAccountSequence 和中文 memo
- 历史记录(2026-01-04前):metadata 可能为空,显示为"-"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-28 20:22:40 -08:00
parent 90fad63fed
commit bf50810830
4 changed files with 42 additions and 5 deletions

View File

@ -64,6 +64,10 @@ export interface LedgerEntryDTO {
refTxHash: string | null;
memo: string | null;
allocationType: string | null;
/** 来源用户账户序列号(从 payloadJson.metadata 提取) */
sourceAccountSequence: string | null;
/** 来源中文备注(从 payloadJson.metadata.memo 提取) */
sourceMemo: string | null;
createdAt: string;
}
@ -75,6 +79,21 @@ export interface PaginatedLedgerDTO {
totalPages: number;
}
/** 从 payloadJson 中提取 metadata 信息用于 LedgerEntryDTO */
function extractPayloadInfo(payloadJson: unknown): {
allocationType: string | null;
sourceAccountSequence: string | null;
sourceMemo: string | null;
} {
const payload = payloadJson as Record<string, unknown> | null;
const metadata = payload?.metadata as Record<string, unknown> | null;
return {
allocationType: (payload?.allocationType as string) ?? null,
sourceAccountSequence: (metadata?.sourceAccountSequence as string) ?? null,
sourceMemo: (metadata?.memo as string) ?? null,
};
}
@Injectable()
export class WalletApplicationService {
private readonly logger = new Logger(WalletApplicationService.name);
@ -1991,7 +2010,7 @@ export class WalletApplicationService {
refOrderId: entry.refOrderId,
refTxHash: entry.refTxHash,
memo: entry.memo,
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
...extractPayloadInfo(entry.payloadJson),
createdAt: entry.createdAt.toISOString(),
})),
total: result.total,
@ -2036,7 +2055,7 @@ export class WalletApplicationService {
refOrderId: entry.refOrderId,
refTxHash: entry.refTxHash,
memo: entry.memo,
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
...extractPayloadInfo(entry.payloadJson),
createdAt: entry.createdAt.toISOString(),
})),
total: result.total,
@ -2103,7 +2122,7 @@ export class WalletApplicationService {
refOrderId: entry.refOrderId,
refTxHash: entry.refTxHash,
memo: entry.memo,
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
...extractPayloadInfo(entry.payloadJson),
createdAt: entry.createdAt.toISOString(),
})),
total: ledgerResult.total,
@ -2131,7 +2150,7 @@ export class WalletApplicationService {
refOrderId: entry.refOrderId,
refTxHash: entry.refTxHash,
memo: entry.memo,
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
...extractPayloadInfo(entry.payloadJson),
createdAt: entry.createdAt.toISOString(),
})),
total: ledgerResult.total,
@ -2159,7 +2178,7 @@ export class WalletApplicationService {
refOrderId: entry.refOrderId,
refTxHash: entry.refTxHash,
memo: entry.memo,
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
...extractPayloadInfo(entry.payloadJson),
createdAt: entry.createdAt.toISOString(),
})),
total: ledgerResult.total,

View File

@ -512,6 +512,12 @@
word-break: break-word;
}
.sourceAccount {
font-size: 13px;
font-family: monospace;
color: #374151;
}
/* [2026-01-06] 新增:详细明细列表样式 */
.detailsSection {
margin-top: 24px;

View File

@ -443,6 +443,8 @@ function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fix
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
@ -459,6 +461,8 @@ function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fix
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
</td>
<td>{entry.balanceAfter !== null ? formatAmount(entry.balanceAfter) : '-'}</td>
<td className={styles.sourceAccount}>{entry.sourceAccountSequence || '-'}</td>
<td className={styles.memo}>{entry.sourceMemo || '-'}</td>
<td className={styles.memo}>{entry.memo || entry.allocationType || '-'}</td>
</tr>
))}
@ -1110,6 +1114,8 @@ function LedgerAccountCard({
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
@ -1126,6 +1132,8 @@ function LedgerAccountCard({
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
</td>
<td>{entry.balanceAfter !== null ? formatAmount(entry.balanceAfter) : '-'}</td>
<td className={styles.sourceAccount}>{entry.sourceAccountSequence || '-'}</td>
<td className={styles.memo}>{entry.sourceMemo || '-'}</td>
<td className={styles.memo}>{entry.memo || entry.allocationType || '-'}</td>
</tr>
))}

View File

@ -195,6 +195,10 @@ export interface LedgerEntryDTO {
refTxHash: string | null;
memo: string | null;
allocationType: string | null;
/** 来源用户账户序列号 */
sourceAccountSequence: string | null;
/** 来源中文备注 */
sourceMemo: string | null;
createdAt: string;
}