fix(pre-planting): 修复预种权益资金分配字段名错误(allocationType 丢失)

原 executeAllocations 使用了错误的 FundAllocationItem 字段名:
- targetAccountId → 应为 targetId
- 缺少 allocationType 字段
- targetType 错误地使用了 rightType 值而非 'USER'|'SYSTEM'

导致所有预种订单的 SHARE_RIGHT/COMMUNITY_RIGHT 等权益分配静默失败,
资金未能分配到推荐人/社区/省市账户,同时流水明细中也不显示预种记录。

修复内容:
1. WalletServiceClient 新增 allocatePrePlantingFunds 方法(使用正确格式)
2. executeAllocations 改用新方法,正确设置 targetType/targetId/allocationType
3. InternalPrePlantingController 新增 POST /admin/retry-rewards 历史数据修复端点

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-28 12:29:03 -08:00
parent 62bbbca609
commit 1d0e4352df
3 changed files with 127 additions and 3 deletions

View File

@ -357,6 +357,55 @@ export class WalletServiceClient {
} }
} }
/**
* 使 reward-service FundAllocationItem
*
* allocateFunds
* - targetType: 'USER' | 'SYSTEM' FundAllocationTargetType
* - targetId: 账户序列号 targetAccountId
* - allocationType: 权益类型字符串 wallet-service ledger
*
* S SYSTEM USER
*/
async allocatePrePlantingFunds(request: {
orderId: string;
allocations: Array<{
targetType: 'USER' | 'SYSTEM';
targetId: string;
allocationType: string;
amount: number;
metadata?: Record<string, unknown>;
}>;
}): Promise<boolean> {
try {
return await this.withRetry(
`allocatePrePlantingFunds(${request.orderId})`,
async () => {
const response = await firstValueFrom(
this.httpService.post(
`${this.baseUrl}/api/v1/wallets/allocate-funds`,
request,
),
);
// wallet-service 使用 TransformInterceptor响应格式为
// { success: true, data: { success: bool, allocatedCount: N, ... }, timestamp: "..." }
const data = response.data?.data ?? response.data;
return data?.success === true;
},
);
} catch (error) {
this.logger.error(
`Failed to allocate pre-planting funds for order: ${request.orderId}`,
error,
);
if (this.configService.get('NODE_ENV') === 'development') {
this.logger.warn('Development mode: simulating successful pre-planting allocation');
return true;
}
throw error;
}
}
/** /**
* *
*/ */

View File

@ -1,6 +1,7 @@
import { import {
Controller, Controller,
Get, Get,
Post,
Param, Param,
Query, Query,
HttpStatus, HttpStatus,
@ -15,6 +16,7 @@ import {
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { PrePlantingApplicationService } from '../../application/services/pre-planting-application.service'; import { PrePlantingApplicationService } from '../../application/services/pre-planting-application.service';
import { PrismaService } from '../../../infrastructure/persistence/prisma/prisma.service'; import { PrismaService } from '../../../infrastructure/persistence/prisma/prisma.service';
import { WalletServiceClient } from '../../../infrastructure/external/wallet-service.client';
@ApiTags('预种计划-内部API') @ApiTags('预种计划-内部API')
@Controller('internal/pre-planting') @Controller('internal/pre-planting')
@ -24,6 +26,7 @@ export class InternalPrePlantingController {
constructor( constructor(
private readonly prePlantingService: PrePlantingApplicationService, private readonly prePlantingService: PrePlantingApplicationService,
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly walletClient: WalletServiceClient,
) {} ) {}
@Get('eligibility/:accountSequence') @Get('eligibility/:accountSequence')
@ -234,4 +237,69 @@ export class InternalPrePlantingController {
pendingContracts, pendingContracts,
}; };
} }
/**
* [2026-02-28]
*
* executeAllocations 使 FundAllocationItem targetAccountIdtargetId
* SHARE_RIGHT/COMMUNITY_RIGHT
*
* pre_planting_reward_entries
* wallet-service /allocate-fundswallet-service
*/
@Post('admin/retry-rewards')
@ApiOperation({ summary: '重新执行预种权益资金分配(历史数据修复)' })
@ApiResponse({ status: HttpStatus.OK, description: '修复结果' })
async retryRewards() {
this.logger.log('[PRE-PLANTING RETRY] Starting rewards retry for all historical orders...');
// 1. 查询所有有 SETTLED 分配记录的订单号
const distinctOrders = await this.prisma.prePlantingRewardEntry.findMany({
where: { rewardStatus: 'SETTLED' },
select: { sourceOrderNo: true },
distinct: ['sourceOrderNo'],
});
const orderNos = distinctOrders.map((r) => r.sourceOrderNo);
this.logger.log(`[PRE-PLANTING RETRY] Found ${orderNos.length} orders to retry`);
let successCount = 0;
let failCount = 0;
const failedOrders: string[] = [];
// 2. 逐笔订单重新调用 wallet-service allocate-funds
for (const orderNo of orderNos) {
try {
const entries = await this.prisma.prePlantingRewardEntry.findMany({
where: { sourceOrderNo: orderNo, rewardStatus: 'SETTLED' },
});
if (entries.length === 0) continue;
await this.walletClient.allocatePrePlantingFunds({
orderId: orderNo,
allocations: entries.map((e) => ({
targetType: e.recipientAccountSequence.startsWith('S') ? 'SYSTEM' : 'USER',
targetId: e.recipientAccountSequence,
allocationType: e.rightType,
amount: Number(e.usdtAmount),
})),
});
successCount++;
this.logger.log(`[PRE-PLANTING RETRY] OK: ${orderNo} (${entries.length} entries)`);
} catch (err) {
failCount++;
failedOrders.push(orderNo);
this.logger.error(`[PRE-PLANTING RETRY] FAILED: ${orderNo}`, err);
}
}
return {
total: orderNos.length,
success: successCount,
failed: failCount,
failedOrders,
};
}
} }

View File

@ -86,6 +86,11 @@ export class PrePlantingRewardService {
/** /**
* Step 5: 事务提交后执行资金转账HTTP wallet-service * Step 5: 事务提交后执行资金转账HTTP wallet-service
*
* [2026-02-28] 使 FundAllocationItem
* - targetType: 'USER' | 'SYSTEM' S SYSTEM USER
* - targetId: 账户序列号 targetAccountId
* - allocationType: 权益类型字符串 wallet-service ledger
*/ */
async executeAllocations( async executeAllocations(
orderNo: string, orderNo: string,
@ -96,12 +101,14 @@ export class PrePlantingRewardService {
(a) => a.rewardStatus === PrePlantingRewardStatus.SETTLED, (a) => a.rewardStatus === PrePlantingRewardStatus.SETTLED,
); );
await this.walletClient.allocateFunds({ await this.walletClient.allocatePrePlantingFunds({
orderId: orderNo, orderId: orderNo,
allocations: settledAllocations.map((a) => ({ allocations: settledAllocations.map((a) => ({
targetType: a.rightType as unknown as import('../../../domain/value-objects/fund-allocation-target-type.enum').FundAllocationTargetType, // 账户序列以 S 开头 → 系统账户 SYSTEM其余D/9/8/7/6 开头)→ 用户账户 USER
targetType: a.recipientAccountSequence.startsWith('S') ? 'SYSTEM' : 'USER',
targetId: a.recipientAccountSequence,
allocationType: a.rightType,
amount: a.amount, amount: a.amount,
targetAccountId: a.recipientAccountSequence,
})), })),
}); });