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:
parent
90fad63fed
commit
bf50810830
|
|
@ -64,6 +64,10 @@ export interface LedgerEntryDTO {
|
||||||
refTxHash: string | null;
|
refTxHash: string | null;
|
||||||
memo: string | null;
|
memo: string | null;
|
||||||
allocationType: string | null;
|
allocationType: string | null;
|
||||||
|
/** 来源用户账户序列号(从 payloadJson.metadata 提取) */
|
||||||
|
sourceAccountSequence: string | null;
|
||||||
|
/** 来源中文备注(从 payloadJson.metadata.memo 提取) */
|
||||||
|
sourceMemo: string | null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +79,21 @@ export interface PaginatedLedgerDTO {
|
||||||
totalPages: number;
|
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()
|
@Injectable()
|
||||||
export class WalletApplicationService {
|
export class WalletApplicationService {
|
||||||
private readonly logger = new Logger(WalletApplicationService.name);
|
private readonly logger = new Logger(WalletApplicationService.name);
|
||||||
|
|
@ -1991,7 +2010,7 @@ export class WalletApplicationService {
|
||||||
refOrderId: entry.refOrderId,
|
refOrderId: entry.refOrderId,
|
||||||
refTxHash: entry.refTxHash,
|
refTxHash: entry.refTxHash,
|
||||||
memo: entry.memo,
|
memo: entry.memo,
|
||||||
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
|
...extractPayloadInfo(entry.payloadJson),
|
||||||
createdAt: entry.createdAt.toISOString(),
|
createdAt: entry.createdAt.toISOString(),
|
||||||
})),
|
})),
|
||||||
total: result.total,
|
total: result.total,
|
||||||
|
|
@ -2036,7 +2055,7 @@ export class WalletApplicationService {
|
||||||
refOrderId: entry.refOrderId,
|
refOrderId: entry.refOrderId,
|
||||||
refTxHash: entry.refTxHash,
|
refTxHash: entry.refTxHash,
|
||||||
memo: entry.memo,
|
memo: entry.memo,
|
||||||
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
|
...extractPayloadInfo(entry.payloadJson),
|
||||||
createdAt: entry.createdAt.toISOString(),
|
createdAt: entry.createdAt.toISOString(),
|
||||||
})),
|
})),
|
||||||
total: result.total,
|
total: result.total,
|
||||||
|
|
@ -2103,7 +2122,7 @@ export class WalletApplicationService {
|
||||||
refOrderId: entry.refOrderId,
|
refOrderId: entry.refOrderId,
|
||||||
refTxHash: entry.refTxHash,
|
refTxHash: entry.refTxHash,
|
||||||
memo: entry.memo,
|
memo: entry.memo,
|
||||||
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
|
...extractPayloadInfo(entry.payloadJson),
|
||||||
createdAt: entry.createdAt.toISOString(),
|
createdAt: entry.createdAt.toISOString(),
|
||||||
})),
|
})),
|
||||||
total: ledgerResult.total,
|
total: ledgerResult.total,
|
||||||
|
|
@ -2131,7 +2150,7 @@ export class WalletApplicationService {
|
||||||
refOrderId: entry.refOrderId,
|
refOrderId: entry.refOrderId,
|
||||||
refTxHash: entry.refTxHash,
|
refTxHash: entry.refTxHash,
|
||||||
memo: entry.memo,
|
memo: entry.memo,
|
||||||
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
|
...extractPayloadInfo(entry.payloadJson),
|
||||||
createdAt: entry.createdAt.toISOString(),
|
createdAt: entry.createdAt.toISOString(),
|
||||||
})),
|
})),
|
||||||
total: ledgerResult.total,
|
total: ledgerResult.total,
|
||||||
|
|
@ -2159,7 +2178,7 @@ export class WalletApplicationService {
|
||||||
refOrderId: entry.refOrderId,
|
refOrderId: entry.refOrderId,
|
||||||
refTxHash: entry.refTxHash,
|
refTxHash: entry.refTxHash,
|
||||||
memo: entry.memo,
|
memo: entry.memo,
|
||||||
allocationType: (entry.payloadJson as Record<string, unknown>)?.allocationType as string ?? null,
|
...extractPayloadInfo(entry.payloadJson),
|
||||||
createdAt: entry.createdAt.toISOString(),
|
createdAt: entry.createdAt.toISOString(),
|
||||||
})),
|
})),
|
||||||
total: ledgerResult.total,
|
total: ledgerResult.total,
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,12 @@
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sourceAccount {
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: monospace;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
/* [2026-01-06] 新增:详细明细列表样式 */
|
/* [2026-01-06] 新增:详细明细列表样式 */
|
||||||
.detailsSection {
|
.detailsSection {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
|
|
|
||||||
|
|
@ -443,6 +443,8 @@ function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fix
|
||||||
<th>类型</th>
|
<th>类型</th>
|
||||||
<th>金额</th>
|
<th>金额</th>
|
||||||
<th>余额</th>
|
<th>余额</th>
|
||||||
|
<th>来源账户</th>
|
||||||
|
<th>来源备注</th>
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -459,6 +461,8 @@ function FixedAccountsSection({ data }: { data: SystemAccountReportResponse['fix
|
||||||
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
|
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
|
||||||
</td>
|
</td>
|
||||||
<td>{entry.balanceAfter !== null ? formatAmount(entry.balanceAfter) : '-'}</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>
|
<td className={styles.memo}>{entry.memo || entry.allocationType || '-'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
@ -1110,6 +1114,8 @@ function LedgerAccountCard({
|
||||||
<th>类型</th>
|
<th>类型</th>
|
||||||
<th>金额</th>
|
<th>金额</th>
|
||||||
<th>余额</th>
|
<th>余额</th>
|
||||||
|
<th>来源账户</th>
|
||||||
|
<th>来源备注</th>
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -1126,6 +1132,8 @@ function LedgerAccountCard({
|
||||||
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
|
{entry.amount >= 0 ? '+' : ''}{formatAmount(entry.amount)} {getAssetTypeLabel(entry.assetType)}
|
||||||
</td>
|
</td>
|
||||||
<td>{entry.balanceAfter !== null ? formatAmount(entry.balanceAfter) : '-'}</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>
|
<td className={styles.memo}>{entry.memo || entry.allocationType || '-'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,10 @@ export interface LedgerEntryDTO {
|
||||||
refTxHash: string | null;
|
refTxHash: string | null;
|
||||||
memo: string | null;
|
memo: string | null;
|
||||||
allocationType: string | null;
|
allocationType: string | null;
|
||||||
|
/** 来源用户账户序列号 */
|
||||||
|
sourceAccountSequence: string | null;
|
||||||
|
/** 来源中文备注 */
|
||||||
|
sourceMemo: string | null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue