/** * 批量生成分润索引表记录脚本 * * 功能: * 1. 如果 commissionDetails 有数据,直接使用 * 2. 如果 commissionDetails 没有数据,根据字段生成2层分润: * - 第1层:agentId 和 incomeAmount * - 第2层:personalAgentId 和 personalIncomeAmount */ import { DataSource } from 'typeorm' import { IncomeRecords } from '../src/entities/income-records.entity' import { AgentCommissionIndex } from '../src/entities/agent-commission-index.entity' import { CommissionDetail } from '../src/services/multi-level-commission.service' import { createApp } from '../src/app' interface MigrationStats { total: number processed: number success: number failed: number skipped: number withDetails: number withoutDetails: number } class CommissionIndexGenerator { private dataSource: DataSource private stats: MigrationStats = { total: 0, processed: 0, success: 0, failed: 0, skipped: 0, withDetails: 0, withoutDetails: 0 } constructor(dataSource: DataSource) { this.dataSource = dataSource } /** * 从 commissionDetails JSON 字符串解析分润明细 */ private parseCommissionDetails(commissionDetails: string | null): CommissionDetail[] | null { if (!commissionDetails) { return null } try { const details = JSON.parse(commissionDetails) if (Array.isArray(details) && details.length > 0) { return details as CommissionDetail[] } return null } catch (error) { console.error('解析 commissionDetails 失败:', error) return null } } /** * 根据字段生成2层分润明细 */ private generateCommissionDetailsFromFields( record: IncomeRecords ): CommissionDetail[] { const details: CommissionDetail[] = [] // 第1层:agentId 和 incomeAmount if (record.agentId && record.agentId > 0 && record.incomeAmount && Number(record.incomeAmount) > 0) { const orderPrice = Number(record.orderPrice) || 0 const incomeAmount = Number(record.incomeAmount) // 计算分润比例 const rate = orderPrice > 0 ? (incomeAmount / orderPrice) * 100 : 0 details.push({ level: 1, agentId: record.agentId, rate: Number(rate.toFixed(2)), amount: incomeAmount }) } // 第2层:personalAgentId 和 personalIncomeAmount if (record.personalAgentId && record.personalAgentId > 0 && record.personalIncomeAmount && Number(record.personalIncomeAmount) > 0) { const orderPrice = Number(record.orderPrice) || 0 const personalIncomeAmount = Number(record.personalIncomeAmount) // 计算分润比例 const rate = orderPrice > 0 ? (personalIncomeAmount / orderPrice) * 100 : 0 details.push({ level: 2, agentId: record.personalAgentId, rate: Number(rate.toFixed(2)), amount: personalIncomeAmount }) } return details } /** * 检查是否已存在索引记录 */ private async hasIndexRecords(incomeRecordId: number): Promise { const indexRepository = this.dataSource.getRepository(AgentCommissionIndex) const count = await indexRepository.count({ where: { incomeRecordId } }) return count > 0 } /** * 处理单条记录 */ private async processSingleRecord(record: IncomeRecords, skipExisting: boolean = true): Promise { try { // 检查是否已存在索引记录 if (skipExisting) { const exists = await this.hasIndexRecords(record.id) if (exists) { this.stats.skipped++ return } } // 解析 commissionDetails let commissionDetails: CommissionDetail[] | null = null if (record.commissionDetails) { commissionDetails = this.parseCommissionDetails(record.commissionDetails) if (commissionDetails) { this.stats.withDetails++ } } // 如果没有 commissionDetails,根据字段生成 if (!commissionDetails || commissionDetails.length === 0) { commissionDetails = this.generateCommissionDetailsFromFields(record) this.stats.withoutDetails++ } // 如果没有分润明细,跳过 if (!commissionDetails || commissionDetails.length === 0) { this.stats.skipped++ return } // 创建索引记录 // 注意:createdAt 使用订单的创建时间(record.createdAt),而不是生成脚本时的时间 const indexRepository = this.dataSource.getRepository(AgentCommissionIndex) const indexRecords = commissionDetails.map(detail => indexRepository.create({ incomeRecordId: record.id, agentId: detail.agentId, level: detail.level, commissionRate: detail.rate, commissionAmount: detail.amount, orderNo: record.orderNo, orderPrice: Number(record.orderPrice), userId: record.userId, status: record.status, createdAt: record.createdAt // 使用订单的创建时间 }) ) await indexRepository.save(indexRecords) this.stats.success++ if (this.stats.processed % 100 === 0) { console.log(`已处理: ${this.stats.processed} | 成功: ${this.stats.success} | 失败: ${this.stats.failed} | 跳过: ${this.stats.skipped} | 有明细: ${this.stats.withDetails} | 无明细: ${this.stats.withoutDetails}`) } } catch (error) { this.stats.failed++ console.error(`处理记录失败 (ID: ${record.id}, OrderNo: ${record.orderNo}):`, error) } } /** * 批量处理订单记录 */ async generateIndex(batchSize: number = 100, startId?: number, skipExisting: boolean = true): Promise { const incomeRecordsRepository = this.dataSource.getRepository(IncomeRecords) const indexRepository = this.dataSource.getRepository(AgentCommissionIndex) console.log('开始生成分润索引表记录...') console.log(`批次大小: ${batchSize}`) console.log(`起始ID: ${startId || '无'}`) console.log(`跳过已存在: ${skipExisting}`) // 先统计总数 let countQuery = incomeRecordsRepository .createQueryBuilder('record') .where('record.delFlag = :delFlag', { delFlag: false }) if (startId) { countQuery = countQuery.andWhere('record.id >= :startId', { startId }) } const total = await countQuery.getCount() this.stats.total = total console.log(`总记录数: ${total}`) let offset = 0 let hasMore = true while (hasMore) { const queryBuilder = incomeRecordsRepository .createQueryBuilder('record') .where('record.delFlag = :delFlag', { delFlag: false }) .orderBy('record.id', 'ASC') .limit(batchSize) .offset(offset) if (startId) { queryBuilder.andWhere('record.id >= :startId', { startId }) } const records = await queryBuilder.getMany() if (records.length === 0) { hasMore = false break } for (const record of records) { await this.processSingleRecord(record, skipExisting) this.stats.processed++ } offset += batchSize // 如果这批数据少于批次大小,说明已经处理完所有数据 if (records.length < batchSize) { hasMore = false } } console.log('\n生成完成!') console.log(`总计: ${this.stats.total}`) console.log(`已处理: ${this.stats.processed}`) console.log(`成功: ${this.stats.success}`) console.log(`失败: ${this.stats.failed}`) console.log(`跳过: ${this.stats.skipped}`) console.log(`有明细: ${this.stats.withDetails}`) console.log(`无明细: ${this.stats.withoutDetails}`) } } // 主函数 async function main() { const app = await createApp() const dataSource = (app as any).dataSource if (!dataSource) { console.error('无法获取数据库连接') process.exit(1) } try { const generator = new CommissionIndexGenerator(dataSource) // 从命令行参数获取配置 const args = process.argv.slice(2) const batchSize = parseInt(args[0]) || 100 const startId = args[1] ? parseInt(args[1]) : undefined const skipExisting = args[2] !== 'false' console.log('配置参数:') console.log(` 批次大小: ${batchSize}`) console.log(` 起始ID: ${startId || '无'}`) console.log(` 跳过已存在: ${skipExisting}`) console.log('') await generator.generateIndex(batchSize, startId, skipExisting) } catch (error) { console.error('执行失败:', error) process.exit(1) } finally { await app.close() } } // 运行脚本 if (require.main === module) { main().catch(console.error) } export { CommissionIndexGenerator }