feat: 实现多商户管理模块与前端服务
refactor: 优化服务层代码并修复类型问题 docs: 更新开发进度文档 feat(merchant): 新增商户监控与数据统计服务 feat(dashboard): 添加商户管理前端页面与服务 fix: 修复类型转换与可选参数处理 feat: 实现商户订单、店铺与结算管理功能 refactor: 重构审计日志格式与服务调用 feat: 新增商户入驻与身份注册功能 fix(controller): 修复路由参数类型问题 feat: 添加商户排名与统计报告功能 chore: 更新模拟数据与服务配置
This commit is contained in:
293
server/src/core/ai/MerchantAnalysisService.ts
Normal file
293
server/src/core/ai/MerchantAnalysisService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
402
server/src/core/ai/MerchantPredictionService.ts
Normal file
402
server/src/core/ai/MerchantPredictionService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
34
server/src/core/ai/ReputationZKPService.ts
Normal file
34
server/src/core/ai/ReputationZKPService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
435
server/src/core/ai/SettlementOptimizationService.ts
Normal file
435
server/src/core/ai/SettlementOptimizationService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user