import { Injectable, Logger } from '@nestjs/common'; import { Merchant, Order, Settlement } from '@prisma/client'; import { PrismaService } from '../../config/database'; interface MerchantBehaviorData { merchantId: string; orderCount: number; totalSales: number; averageOrderValue: number; orderFrequency: number; // 日均订单数 refundRate: number; activeDays: number; // 活跃天数 lastOrderDate: Date; createdAt: Date; } interface BehaviorPredictionResult { merchantId: string; predictions: { futureSales: number; // 预测未来30天销售额 orderTrend: 'increasing' | 'stable' | 'decreasing'; // 订单趋势 churnRisk: 'low' | 'medium' | 'high'; // 流失风险 growthPotential: 'high' | 'medium' | 'low'; // 增长潜力 }; confidence: number; // 预测置信度 factors: { historicalTrend: number; recentActivity: number; customerRetention: number; marketSeasonality: number; }; recommendations: string[]; } @Injectable() export class MerchantPredictionService { private readonly logger = new Logger(MerchantPredictionService.name); constructor(private readonly prisma: PrismaService) {} /** * 预测商户行为 * @param merchantId 商户ID * @param traceId 链路追踪ID * @returns 商户行为预测结果 */ async predictMerchantBehavior(merchantId: string, traceId: string): Promise { this.logger.log(`开始预测商户行为: ${merchantId}`, { traceId, merchantId }); try { // 获取商户历史行为数据 const behaviorData = await this.getMerchantBehaviorData(merchantId); // 分析历史趋势 const historicalTrend = this.analyzeHistoricalTrend(merchantId); // 分析最近活动 const recentActivity = this.analyzeRecentActivity(behaviorData); // 分析客户留存 const customerRetention = this.analyzeCustomerRetention(merchantId); // 分析市场季节性 const marketSeasonality = this.analyzeMarketSeasonality(); const factors = { historicalTrend, recentActivity, customerRetention, marketSeasonality, }; // 预测未来销售额 const futureSales = this.predictFutureSales(behaviorData, factors); // 预测订单趋势 const orderTrend = this.predictOrderTrend(historicalTrend, recentActivity); // 预测流失风险 const churnRisk = this.predictChurnRisk(behaviorData, recentActivity); // 评估增长潜力 const growthPotential = this.assessGrowthPotential(factors); // 计算预测置信度 const confidence = this.calculateConfidence(factors); // 生成建议 const recommendations = this.generateRecommendations({ ...behaviorData, ...factors, predictions: { futureSales, orderTrend, churnRisk, growthPotential, }, }); const result: BehaviorPredictionResult = { merchantId, predictions: { futureSales, orderTrend, churnRisk, growthPotential, }, confidence, factors, recommendations, }; this.logger.log(`商户行为预测完成: ${merchantId}`, { traceId, result }); return result; } catch (error) { this.logger.error(`商户行为预测失败: ${error.message}`, { traceId, merchantId, error }); throw error; } } /** * 批量预测商户行为 * @param merchantIds 商户ID列表 * @param traceId 链路追踪ID * @returns 商户行为预测结果列表 */ async batchPredictMerchantBehavior(merchantIds: string[], traceId: string): Promise { this.logger.log(`开始批量预测商户行为, 数量: ${merchantIds.length}`, { traceId }); const results: BehaviorPredictionResult[] = []; for (const merchantId of merchantIds) { try { const result = await this.predictMerchantBehavior(merchantId, traceId); results.push(result); } catch (error) { this.logger.error(`批量预测商户行为失败: ${merchantId}, 错误: ${error.message}`, { traceId, merchantId }); } } this.logger.log(`批量预测商户行为完成, 成功: ${results.length}/${merchantIds.length}`, { traceId }); return results; } /** * 获取商户行为数据 */ private async getMerchantBehaviorData(merchantId: string): Promise { // 获取商户基本信息 const merchant = await this.prisma.merchant.findUnique({ where: { id: merchantId }, }); if (!merchant) { throw new Error(`商户不存在: ${merchantId}`); } // 获取商户订单数据 const orders = await this.prisma.order.findMany({ where: { merchantId }, select: { id: true, totalAmount: true, status: true, createdAt: true, }, }); // 计算基本统计数据 const orderCount = orders.length; const totalSales = orders.reduce((sum, order) => sum + order.totalAmount, 0); const averageOrderValue = orderCount > 0 ? totalSales / orderCount : 0; // 计算活跃天数 const activeDays = new Set(orders.map(order => order.createdAt.toISOString().split('T')[0])).size; // 计算日均订单数 const daysSinceCreation = merchant.createdAt ? Math.max(1, Math.floor((new Date().getTime() - merchant.createdAt.getTime()) / (1000 * 60 * 60 * 24))) : 1; const orderFrequency = orderCount / daysSinceCreation; // 计算退款率 const refundedOrders = orders.filter(order => order.status === 'REFUNDED').length; const refundRate = orderCount > 0 ? refundedOrders / orderCount : 0; // 获取最后订单日期 const lastOrder = orders.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]; const lastOrderDate = lastOrder ? lastOrder.createdAt : merchant.createdAt; return { merchantId, orderCount, totalSales, averageOrderValue, orderFrequency, refundRate, activeDays, lastOrderDate, createdAt: merchant.createdAt, }; } /** * 分析历史趋势 */ private async analyzeHistoricalTrend(merchantId: string): Promise { // 这里可以实现更复杂的趋势分析逻辑 // 例如:分析过去3个月的订单增长趋势 return Math.random() * 0.5 + 0.5; // 模拟趋势值 (0.5-1.0) } /** * 分析最近活动 */ private analyzeRecentActivity(behaviorData: MerchantBehaviorData): number { // 计算最近30天的活跃程度 const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); const daysSinceLastOrder = Math.floor((new Date().getTime() - behaviorData.lastOrderDate.getTime()) / (1000 * 60 * 60 * 24)); const recentActivity = Math.max(0, 1 - (daysSinceLastOrder / 30)); return recentActivity; } /** * 分析客户留存 */ private async analyzeCustomerRetention(merchantId: string): Promise { // 这里可以实现更复杂的客户留存分析逻辑 // 例如:分析重复购买率 return Math.random() * 0.3 + 0.7; // 模拟留存率 (0.7-1.0) } /** * 分析市场季节性 */ private analyzeMarketSeasonality(): number { // 这里可以实现更复杂的季节性分析逻辑 // 例如:根据当前月份分析季节性因素 const month = new Date().getMonth() + 1; // 假设Q4是销售旺季 if (month >= 10 || month <= 2) { return 1.2; // 旺季 } else if (month >= 3 && month <= 5) { return 0.9; // 淡季 } else { return 1.0; // 正常 } } /** * 预测未来销售额 */ private predictFutureSales(behaviorData: MerchantBehaviorData, factors: any): number { // 基于历史数据和因素预测未来30天销售额 const dailySales = behaviorData.totalSales / Math.max(1, behaviorData.activeDays); const basePrediction = dailySales * 30; // 应用各种因素的影响 const adjustedPrediction = basePrediction * (1 + (factors.historicalTrend - 0.5) * 0.3) * (1 + (factors.recentActivity - 0.5) * 0.2) * factors.marketSeasonality; return Math.max(0, Math.round(adjustedPrediction)); } /** * 预测订单趋势 */ private predictOrderTrend(historicalTrend: number, recentActivity: number): 'increasing' | 'stable' | 'decreasing' { const combinedScore = (historicalTrend + recentActivity) / 2; if (combinedScore > 0.7) { return 'increasing'; } else if (combinedScore > 0.4) { return 'stable'; } else { return 'decreasing'; } } /** * 预测流失风险 */ private predictChurnRisk(behaviorData: MerchantBehaviorData, recentActivity: number): 'low' | 'medium' | 'high' { const daysSinceLastOrder = Math.floor((new Date().getTime() - behaviorData.lastOrderDate.getTime()) / (1000 * 60 * 60 * 24)); const activityScore = recentActivity; if (daysSinceLastOrder > 60 || activityScore < 0.3) { return 'high'; } else if (daysSinceLastOrder > 30 || activityScore < 0.6) { return 'medium'; } else { return 'low'; } } /** * 评估增长潜力 */ private assessGrowthPotential(factors: any): 'high' | 'medium' | 'low' { const combinedScore = (factors.historicalTrend + factors.customerRetention) / 2; if (combinedScore > 0.8) { return 'high'; } else if (combinedScore > 0.6) { return 'medium'; } else { return 'low'; } } /** * 计算预测置信度 */ private calculateConfidence(factors: any): number { // 基于数据完整性和因素稳定性计算置信度 const dataCompleteness = 0.9; // 假设数据完整性较好 const factorConsistency = (factors.historicalTrend + factors.recentActivity + factors.customerRetention) / 3; const confidence = dataCompleteness * factorConsistency; return Math.round(confidence * 100); } /** * 生成建议 */ private generateRecommendations(data: any): string[] { const recommendations: string[] = []; if (data.predictions.churnRisk === 'high') { recommendations.push('建议主动联系商户,了解经营情况并提供支持'); } if (data.predictions.orderTrend === 'decreasing') { recommendations.push('建议优化产品展示和营销策略,提升订单量'); } if (data.predictions.growthPotential === 'high') { recommendations.push('建议为商户提供高级功能和增值服务,支持其业务增长'); } if (data.refundRate > 0.1) { recommendations.push('建议帮助商户优化产品质量和客户服务,降低退款率'); } if (data.averageOrderValue < 100) { recommendations.push('建议引导商户增加高价值产品或实施捆绑销售策略'); } return recommendations.length > 0 ? recommendations : ['商户经营状况稳定,继续保持']; } /** * 获取高风险商户列表 * @param limit 返回数量 * @param traceId 链路追踪ID * @returns 高风险商户列表 */ async getHighRiskMerchants(limit: number = 10, traceId: string): Promise> { this.logger.log(`获取高风险商户列表, 限制: ${limit}`, { traceId }); try { // 获取所有活跃商户 const merchants = await this.prisma.merchant.findMany({ where: { status: 'ACTIVE' }, select: { id: true, companyName: true, }, }); const merchantIds = merchants.map(m => m.id); const predictions = await this.batchPredictMerchantBehavior(merchantIds, traceId); // 筛选高风险商户 const highRiskMerchants = predictions .filter(p => p.predictions.churnRisk === 'high') .map(prediction => { const merchant = merchants.find(m => m.id === prediction.merchantId); return { merchantId: prediction.merchantId, companyName: merchant?.companyName || '未知商户', riskLevel: prediction.predictions.churnRisk, reason: prediction.recommendations[0] || '商户活动度低,存在流失风险', }; }) .slice(0, limit); this.logger.log(`获取高风险商户列表完成, 数量: ${highRiskMerchants.length}`, { traceId }); return highRiskMerchants; } catch (error) { this.logger.error(`获取高风险商户列表失败: ${error.message}`, { traceId, error }); throw error; } } }