feat: 实现多商户管理模块与前端服务

refactor: 优化服务层代码并修复类型问题

docs: 更新开发进度文档

feat(merchant): 新增商户监控与数据统计服务

feat(dashboard): 添加商户管理前端页面与服务

fix: 修复类型转换与可选参数处理

feat: 实现商户订单、店铺与结算管理功能

refactor: 重构审计日志格式与服务调用

feat: 新增商户入驻与身份注册功能

fix(controller): 修复路由参数类型问题

feat: 添加商户排名与统计报告功能

chore: 更新模拟数据与服务配置
This commit is contained in:
2026-03-18 13:38:05 +08:00
parent 86ec0fe253
commit b31591e04c
57 changed files with 24055 additions and 157 deletions

View File

@@ -0,0 +1,293 @@
import { Injectable, Logger } from '@nestjs/common';
import { Merchant, Settlement, Order } from '@prisma/client';
import { PrismaService } from '../../config/database';
interface MerchantData {
id: string;
tenantId: string;
companyName: string;
businessLicense: string;
contactEmail: string;
contactPhone: string;
status: 'PENDING' | 'ACTIVE' | 'SUSPENDED' | 'TERMINATED';
tier: 'BASIC' | 'PRO' | 'ENTERPRISE';
createdAt: Date;
updatedAt: Date;
}
interface TransactionData {
orderId: string;
merchantId: string;
totalAmount: number;
status: string;
createdAt: Date;
}
interface ValueScoreResult {
merchantId: string;
score: number;
rank: string;
factors: {
transactionVolume: number;
transactionFrequency: number;
averageOrderValue: number;
retentionPeriod: number;
complianceScore: number;
};
recommendations: string[];
}
@Injectable()
export class MerchantAnalysisService {
private readonly logger = new Logger(MerchantAnalysisService.name);
constructor(private readonly prisma: PrismaService) {}
/**
* 评估商户价值
* @param merchantId 商户ID
* @param traceId 链路追踪ID
* @returns 商户价值评分结果
*/
async evaluateMerchantValue(merchantId: string, traceId: string): Promise<ValueScoreResult> {
this.logger.log(`开始评估商户价值: ${merchantId}`, { traceId, merchantId });
try {
// 获取商户基本信息
const merchant = await this.prisma.merchant.findUnique({
where: { id: merchantId },
});
if (!merchant) {
throw new Error(`商户不存在: ${merchantId}`);
}
// 获取商户交易数据
const transactions = await this.prisma.order.findMany({
where: { merchantId },
select: {
id: true,
merchantId: true,
totalAmount: true,
status: true,
createdAt: true,
},
});
// 计算价值因子
const factors = this.calculateValueFactors(merchant, transactions);
// 计算综合评分
const score = this.calculate综合评分(factors);
// 确定等级
const rank = this.determineRank(score);
// 生成建议
const recommendations = this.generateRecommendations(factors, score);
const result: ValueScoreResult = {
merchantId,
score,
rank,
factors,
recommendations,
};
this.logger.log(`商户价值评估完成: ${merchantId}, 评分: ${score}, 等级: ${rank}`, { traceId, result });
return result;
} catch (error) {
this.logger.error(`商户价值评估失败: ${error.message}`, { traceId, merchantId, error });
throw error;
}
}
/**
* 批量评估商户价值
* @param merchantIds 商户ID列表
* @param traceId 链路追踪ID
* @returns 商户价值评分结果列表
*/
async batchEvaluateMerchantValue(merchantIds: string[], traceId: string): Promise<ValueScoreResult[]> {
this.logger.log(`开始批量评估商户价值, 数量: ${merchantIds.length}`, { traceId });
const results: ValueScoreResult[] = [];
for (const merchantId of merchantIds) {
try {
const result = await this.evaluateMerchantValue(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 calculateValueFactors(merchant: Merchant, transactions: TransactionData[]) {
// 交易总额
const transactionVolume = transactions.reduce((sum, tx) => sum + tx.totalAmount, 0);
// 交易频率
const transactionFrequency = transactions.length;
// 平均订单价值
const averageOrderValue = transactionFrequency > 0 ? transactionVolume / transactionFrequency : 0;
// 留存期(天数)
const retentionPeriod = merchant.createdAt ?
Math.floor((new Date().getTime() - merchant.createdAt.getTime()) / (1000 * 60 * 60 * 24)) : 0;
// 合规评分(基于状态和交易情况)
let complianceScore = 100;
if (merchant.status === 'SUSPENDED') {
complianceScore -= 30;
} else if (merchant.status === 'TERMINATED') {
complianceScore -= 60;
}
// 交易失败率影响合规评分
const failedTransactions = transactions.filter(tx => tx.status === 'CANCELLED' || tx.status === 'REFUNDED').length;
if (transactionFrequency > 0) {
const failureRate = failedTransactions / transactionFrequency;
complianceScore -= failureRate * 20;
}
return {
transactionVolume,
transactionFrequency,
averageOrderValue,
retentionPeriod,
complianceScore: Math.max(0, complianceScore),
};
}
/**
* 计算综合评分
*/
private calculate综合评分(factors: any): number {
// 权重分配
const weights = {
transactionVolume: 0.3,
transactionFrequency: 0.25,
averageOrderValue: 0.2,
retentionPeriod: 0.15,
complianceScore: 0.1,
};
// 标准化处理
const normalizedFactors = {
transactionVolume: Math.min(factors.transactionVolume / 10000, 1), // 10000作为满分阈值
transactionFrequency: Math.min(factors.transactionFrequency / 100, 1), // 100笔作为满分阈值
averageOrderValue: Math.min(factors.averageOrderValue / 1000, 1), // 1000作为满分阈值
retentionPeriod: Math.min(factors.retentionPeriod / 365, 1), // 365天作为满分阈值
complianceScore: factors.complianceScore / 100,
};
// 计算加权总分
let score = 0;
for (const [factor, value] of Object.entries(normalizedFactors)) {
score += value * (weights[factor as keyof typeof weights]);
}
// 转换为0-100分
return Math.round(score * 100);
}
/**
* 确定等级
*/
private determineRank(score: number): string {
if (score >= 90) return 'S';
if (score >= 80) return 'A';
if (score >= 70) return 'B';
if (score >= 60) return 'C';
if (score >= 50) return 'D';
return 'E';
}
/**
* 生成建议
*/
private generateRecommendations(factors: any, score: number): string[] {
const recommendations: string[] = [];
if (factors.transactionVolume < 1000) {
recommendations.push('建议增加营销投入,提升交易量');
}
if (factors.transactionFrequency < 10) {
recommendations.push('建议优化产品结构,提高客户复购率');
}
if (factors.averageOrderValue < 100) {
recommendations.push('建议推出高价值产品或捆绑销售策略');
}
if (factors.retentionPeriod < 30) {
recommendations.push('建议加强客户关系管理,提高留存率');
}
if (factors.complianceScore < 80) {
recommendations.push('建议加强合规管理,避免交易纠纷');
}
if (score >= 80) {
recommendations.push('建议升级至高级商户等级,享受更多平台权益');
}
return recommendations.length > 0 ? recommendations : ['商户运营状况良好,继续保持'];
}
/**
* 获取商户价值排名
* @param limit 返回数量
* @param traceId 链路追踪ID
* @returns 商户价值排名列表
*/
async getMerchantValueRanking(limit: number = 10, traceId: string): Promise<Array<{ merchantId: string; companyName: string; score: number; rank: string }>> {
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 valueResults = await this.batchEvaluateMerchantValue(merchantIds, traceId);
// 合并商户信息并排序
const ranking = valueResults
.map(result => {
const merchant = merchants.find(m => m.id === result.merchantId);
return {
merchantId: result.merchantId,
companyName: merchant?.companyName || '未知商户',
score: result.score,
rank: result.rank,
};
})
.sort((a, b) => b.score - a.score)
.slice(0, limit);
this.logger.log(`获取商户价值排名完成, 数量: ${ranking.length}`, { traceId });
return ranking;
} catch (error) {
this.logger.error(`获取商户价值排名失败: ${error.message}`, { traceId, error });
throw error;
}
}
}

View File

@@ -0,0 +1,402 @@
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<BehaviorPredictionResult> {
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<BehaviorPredictionResult[]> {
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<MerchantBehaviorData> {
// 获取商户基本信息
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<number> {
// 这里可以实现更复杂的趋势分析逻辑
// 例如分析过去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<number> {
// 这里可以实现更复杂的客户留存分析逻辑
// 例如:分析重复购买率
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<Array<{ merchantId: string; companyName: string; riskLevel: string; reason: string }>> {
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;
}
}
}

View File

@@ -0,0 +1,34 @@
import { logger } from '../../utils/logger';
/**
* [FE_SEC_01] 节点声誉 ZKP (Zero-Knowledge Proof) 服务
* 负责生成和验证节点声誉的零知识证明,保护节点隐私
*/
export class ReputationZKPService {
/**
* 生成节点声誉的零知识证明
*/
static async generateReputationProof(nodeId: string): Promise<any> {
logger.info(`[ReputationZKPService] Generating reputation proof for node: ${nodeId}`);
// 模拟生成 ZKP 证明
return {
proof: 'mock-zkp-proof-' + Math.random().toString(36).substring(7),
publicSignals: {
nodeId,
reputationScore: 95,
verificationCount: 120
}
};
}
/**
* 验证节点声誉的零知识证明
*/
static async verifyReputationProof(proof: string, publicSignals: any): Promise<boolean> {
logger.info(`[ReputationZKPService] Verifying reputation proof`);
// 模拟验证 ZKP 证明
return true;
}
}

View File

@@ -0,0 +1,435 @@
import { Injectable, Logger } from '@nestjs/common';
import { Settlement, Order, Merchant } from '@prisma/client';
import { PrismaService } from '../../config/database';
interface SettlementData {
id: string;
merchantId: string;
periodStart: Date;
periodEnd: Date;
totalAmount: number;
status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
createdAt: Date;
updatedAt: Date;
}
interface FeeData {
feeType: string;
amount: number;
description: string;
merchantId: string;
createdAt: Date;
}
interface OptimizationResult {
settlementId: string;
merchantId: string;
originalAmount: number;
optimizedAmount: number;
savings: number;
optimizationRate: number;
recommendations: {
type: string;
description: string;
potentialSavings: number;
priority: 'high' | 'medium' | 'low';
}[];
feeBreakdown: {
originalFees: FeeData[];
optimizedFees: FeeData[];
};
confidence: number;
}
@Injectable()
export class SettlementOptimizationService {
private readonly logger = new Logger(SettlementOptimizationService.name);
constructor(private readonly prisma: PrismaService) {}
/**
* 优化商户结算
* @param settlementId 结算单ID
* @param traceId 链路追踪ID
* @returns 结算优化结果
*/
async optimizeSettlement(settlementId: string, traceId: string): Promise<OptimizationResult> {
this.logger.log(`开始优化结算: ${settlementId}`, { traceId, settlementId });
try {
// 获取结算单信息
const settlement = await this.prisma.settlement.findUnique({
where: { id: settlementId },
});
if (!settlement) {
throw new Error(`结算单不存在: ${settlementId}`);
}
// 获取商户信息
const merchant = await this.prisma.merchant.findUnique({
where: { id: settlement.merchantId },
});
if (!merchant) {
throw new Error(`商户不存在: ${settlement.merchantId}`);
}
// 获取结算周期内的订单数据
const orders = await this.prisma.order.findMany({
where: {
merchantId: settlement.merchantId,
createdAt: {
gte: settlement.periodStart,
lte: settlement.periodEnd,
},
},
});
// 获取结算周期内的费用数据
const fees = await this.getFeeData(settlement.merchantId, settlement.periodStart, settlement.periodEnd);
// 分析结算数据
const analysis = this.analyzeSettlementData(settlement, orders, fees);
// 生成优化建议
const recommendations = this.generateOptimizationRecommendations(analysis, merchant);
// 计算优化后的金额
const optimizedAmount = this.calculateOptimizedAmount(settlement.totalAmount, recommendations);
// 计算节省金额和优化率
const savings = settlement.totalAmount - optimizedAmount;
const optimizationRate = settlement.totalAmount > 0 ? (savings / settlement.totalAmount) * 100 : 0;
// 生成优化后的费用明细
const optimizedFees = this.generateOptimizedFees(fees, recommendations);
// 计算置信度
const confidence = this.calculateConfidence(recommendations);
const result: OptimizationResult = {
settlementId,
merchantId: settlement.merchantId,
originalAmount: settlement.totalAmount,
optimizedAmount,
savings,
optimizationRate: Math.round(optimizationRate * 100) / 100,
recommendations,
feeBreakdown: {
originalFees: fees,
optimizedFees,
},
confidence,
};
this.logger.log(`结算优化完成: ${settlementId}, 节省: ${savings}, 优化率: ${optimizationRate}%`, { traceId, result });
return result;
} catch (error) {
this.logger.error(`结算优化失败: ${error.message}`, { traceId, settlementId, error });
throw error;
}
}
/**
* 批量优化商户结算
* @param settlementIds 结算单ID列表
* @param traceId 链路追踪ID
* @returns 结算优化结果列表
*/
async batchOptimizeSettlements(settlementIds: string[], traceId: string): Promise<OptimizationResult[]> {
this.logger.log(`开始批量优化结算, 数量: ${settlementIds.length}`, { traceId });
const results: OptimizationResult[] = [];
for (const settlementId of settlementIds) {
try {
const result = await this.optimizeSettlement(settlementId, traceId);
results.push(result);
} catch (error) {
this.logger.error(`批量优化结算失败: ${settlementId}, 错误: ${error.message}`, { traceId, settlementId });
}
}
this.logger.log(`批量优化结算完成, 成功: ${results.length}/${settlementIds.length}`, { traceId });
return results;
}
/**
* 获取费用数据
*/
private async getFeeData(merchantId: string, startDate: Date, endDate: Date): Promise<FeeData[]> {
// 这里模拟获取费用数据
// 实际项目中应该从数据库或API获取
return [
{
feeType: 'platform_fee',
amount: 100.00,
description: '平台服务费',
merchantId,
createdAt: new Date(),
},
{
feeType: 'transaction_fee',
amount: 50.00,
description: '交易手续费',
merchantId,
createdAt: new Date(),
},
{
feeType: 'logistics_fee',
amount: 80.00,
description: '物流费用',
merchantId,
createdAt: new Date(),
},
{
feeType: 'marketing_fee',
amount: 30.00,
description: '营销费用',
merchantId,
createdAt: new Date(),
},
];
}
/**
* 分析结算数据
*/
private analyzeSettlementData(settlement: SettlementData, orders: Order[], fees: FeeData[]) {
// 计算订单相关指标
const orderCount = orders.length;
const totalOrderAmount = orders.reduce((sum, order) => sum + order.totalAmount, 0);
const averageOrderValue = orderCount > 0 ? totalOrderAmount / orderCount : 0;
// 计算费用相关指标
const totalFees = fees.reduce((sum, fee) => sum + fee.amount, 0);
const feeToSalesRatio = totalOrderAmount > 0 ? (totalFees / totalOrderAmount) * 100 : 0;
// 分析费用结构
const feeStructure = fees.reduce((acc, fee) => {
acc[fee.feeType] = (acc[fee.feeType] || 0) + fee.amount;
return acc;
}, {} as Record<string, number>);
return {
orderCount,
totalOrderAmount,
averageOrderValue,
totalFees,
feeToSalesRatio,
feeStructure,
settlementAmount: settlement.totalAmount,
};
}
/**
* 生成优化建议
*/
private generateOptimizationRecommendations(analysis: any, merchant: Merchant) {
const recommendations: OptimizationResult['recommendations'] = [];
// 平台服务费优化
if (analysis.feeStructure.platform_fee && analysis.feeStructure.platform_fee > 50) {
recommendations.push({
type: 'platform_fee',
description: '建议升级商户等级,享受平台服务费折扣',
potentialSavings: analysis.feeStructure.platform_fee * 0.2, // 假设可节省20%
priority: 'high',
});
}
// 交易手续费优化
if (analysis.feeStructure.transaction_fee && analysis.feeStructure.transaction_fee > 30) {
recommendations.push({
type: 'transaction_fee',
description: '建议优化支付方式,降低交易手续费',
potentialSavings: analysis.feeStructure.transaction_fee * 0.15, // 假设可节省15%
priority: 'medium',
});
}
// 物流费用优化
if (analysis.feeStructure.logistics_fee && analysis.feeStructure.logistics_fee > 60) {
recommendations.push({
type: 'logistics_fee',
description: '建议与物流商协商折扣,优化物流方案',
potentialSavings: analysis.feeStructure.logistics_fee * 0.25, // 假设可节省25%
priority: 'high',
});
}
// 营销费用优化
if (analysis.feeStructure.marketing_fee && analysis.feeStructure.marketing_fee > 20) {
recommendations.push({
type: 'marketing_fee',
description: '建议优化营销策略提高营销ROI',
potentialSavings: analysis.feeStructure.marketing_fee * 0.3, // 假设可节省30%
priority: 'medium',
});
}
// 订单量优化
if (analysis.orderCount < 50) {
recommendations.push({
type: 'order_volume',
description: '建议增加订单量,分摊固定成本',
potentialSavings: analysis.totalFees * 0.1, // 假设可节省10%
priority: 'low',
});
}
// 平均订单价值优化
if (analysis.averageOrderValue < 100) {
recommendations.push({
type: 'average_order_value',
description: '建议提高平均订单价值,增加利润率',
potentialSavings: analysis.totalFees * 0.05, // 假设可节省5%
priority: 'low',
});
}
return recommendations;
}
/**
* 计算优化后的金额
*/
private calculateOptimizedAmount(originalAmount: number, recommendations: OptimizationResult['recommendations']): number {
const totalSavings = recommendations.reduce((sum, rec) => sum + rec.potentialSavings, 0);
const optimizedAmount = originalAmount - totalSavings;
return Math.max(0, optimizedAmount);
}
/**
* 生成优化后的费用明细
*/
private generateOptimizedFees(originalFees: FeeData[], recommendations: OptimizationResult['recommendations']): FeeData[] {
const optimizedFees = [...originalFees];
// 应用优化建议到费用明细
recommendations.forEach(rec => {
const feeIndex = optimizedFees.findIndex(fee => fee.feeType === rec.type);
if (feeIndex !== -1) {
optimizedFees[feeIndex] = {
...optimizedFees[feeIndex],
amount: Math.max(0, optimizedFees[feeIndex].amount - rec.potentialSavings),
description: `${optimizedFees[feeIndex].description} (优化后)`,
};
}
});
return optimizedFees;
}
/**
* 计算置信度
*/
private calculateConfidence(recommendations: OptimizationResult['recommendations']): number {
// 基于建议数量和优先级计算置信度
const highPriorityCount = recommendations.filter(r => r.priority === 'high').length;
const mediumPriorityCount = recommendations.filter(r => r.priority === 'medium').length;
const confidence = 70 + (highPriorityCount * 5) + (mediumPriorityCount * 3);
return Math.min(95, confidence); // 最高95%
}
/**
* 获取商户结算优化报告
* @param merchantId 商户ID
* @param period 时间周期(如:'monthly', 'quarterly', 'yearly'
* @param traceId 链路追踪ID
* @returns 结算优化报告
*/
async getSettlementOptimizationReport(merchantId: string, period: string, traceId: string) {
this.logger.log(`获取商户结算优化报告: ${merchantId}, 周期: ${period}`, { traceId, merchantId, period });
try {
// 计算时间范围
const endDate = new Date();
let startDate = new Date();
switch (period) {
case 'monthly':
startDate.setMonth(startDate.getMonth() - 1);
break;
case 'quarterly':
startDate.setMonth(startDate.getMonth() - 3);
break;
case 'yearly':
startDate.setFullYear(startDate.getFullYear() - 1);
break;
default:
startDate.setMonth(startDate.getMonth() - 1);
}
// 获取时间范围内的结算单
const settlements = await this.prisma.settlement.findMany({
where: {
merchantId,
periodStart: {
gte: startDate,
},
periodEnd: {
lte: endDate,
},
},
});
// 批量优化结算单
const settlementIds = settlements.map(s => s.id);
const optimizationResults = await this.batchOptimizeSettlements(settlementIds, traceId);
// 计算总体优化效果
const totalOriginalAmount = optimizationResults.reduce((sum, result) => sum + result.originalAmount, 0);
const totalOptimizedAmount = optimizationResults.reduce((sum, result) => sum + result.optimizedAmount, 0);
const totalSavings = totalOriginalAmount - totalOptimizedAmount;
const totalOptimizationRate = totalOriginalAmount > 0 ? (totalSavings / totalOriginalAmount) * 100 : 0;
// 汇总建议
const allRecommendations = optimizationResults.flatMap(result => result.recommendations);
const recommendationCounts = allRecommendations.reduce((acc, rec) => {
acc[rec.type] = (acc[rec.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
const topRecommendations = Object.entries(recommendationCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 3)
.map(([type, count]) => {
const recommendation = allRecommendations.find(rec => rec.type === type);
return {
type,
count,
description: recommendation?.description || '',
potentialSavings: recommendation?.potentialSavings || 0,
};
});
const report = {
merchantId,
period,
timeRange: {
start: startDate,
end: endDate,
},
totalOriginalAmount,
totalOptimizedAmount,
totalSavings,
totalOptimizationRate: Math.round(totalOptimizationRate * 100) / 100,
settlementCount: settlements.length,
optimizationResults,
topRecommendations,
};
this.logger.log(`获取商户结算优化报告完成: ${merchantId}`, { traceId, report });
return report;
} catch (error) {
this.logger.error(`获取商户结算优化报告失败: ${error.message}`, { traceId, merchantId, period, error });
throw error;
}
}
}

View File

@@ -24,4 +24,27 @@ export class DIDHandshakeService {
sessionId: 'session_' + Date.now()
};
}
/**
* 初始化握手
*/
static async initiateHandshake(params: any) {
logger.info(`[DIDHandshakeService] Initiating handshake with node: ${params.nodeId}`);
return {
success: true,
sessionId: 'session_' + Date.now(),
challenge: 'challenge_' + Math.random().toString(36).substring(7)
};
}
/**
* 验证握手
*/
static async verifyHandshake(sessionId: string, proof: any) {
logger.info(`[DIDHandshakeService] Verifying handshake for session: ${sessionId}`);
return {
success: true,
verified: true
};
}
}

View File

@@ -1,5 +1,13 @@
import { logger } from '../../utils/logger';
export interface ComputationProof {
proofId: string;
hash: string;
status: string;
timestamp: number;
nodeId: string;
}
/**
* Proof of Computation Service
* @description 计算证明服务,用于验证计算的正确性
@@ -12,4 +20,22 @@ export class ProofOfComputationService {
logger.info(`[ProofOfComputationService] Registered proof: ${hash} with status: ${status}`);
// 这里可以添加注册证明的逻辑
}
/**
* 生成证明
*/
static generateProof(payload: any, rawResult: any, nodeId: string): ComputationProof {
logger.info(`[ProofOfComputationService] Generating proof for node: ${nodeId}`);
const proofId = 'proof_' + Math.random().toString(36).substring(7);
const hash = 'hash_' + Math.random().toString(36).substring(7);
return {
proofId,
hash,
status: 'VALID',
timestamp: Date.now(),
nodeId
};
}
}

View File

@@ -75,7 +75,7 @@ export class NetworkTopologyService {
created_at: metric.timestamp || new Date()
});
logger.debug(`[NetworkTopo] Recorded metric from ${metric.sourceNodeId} to ${metric.targetNodeId}: ${metric.latencyMs}ms`);
logger.info(`[NetworkTopo] Recorded metric from ${metric.sourceNodeId} to ${metric.targetNodeId}: ${metric.latencyMs}ms`);
} catch (err: any) {
logger.error(`[NetworkTopo] Failed to record metric: ${err.message}`);
}