feat: 实现前端组件库和API服务基础架构

refactor: 移除废弃的AGI策略演进服务

fix: 修正磁盘I/O指标字段命名

chore: 更新项目依赖版本

test: 添加前后端集成测试用例

docs: 更新AI模块接口文档

style: 统一审计日志字段命名规范

perf: 优化Redis订阅连接错误处理

build: 配置多项目工作区结构

ci: 添加Vite开发服务器CORS支持
This commit is contained in:
2026-03-18 15:22:55 +08:00
parent b31591e04c
commit c932a67be2
96 changed files with 37748 additions and 16326 deletions

View File

@@ -0,0 +1,463 @@
import { PrismaClient } from '@prisma/client';
import { Logger } from 'winston';
import { ReturnEffectAnalysisResult, ReturnEffectMetrics, SKUImpactAnalysis } from '../../shared/types/return';
/**
* 退货效果分析服务
* 分析高退货率SKU对销售、利润的影响生成分析报告
*/
export class ReturnEffectAnalysisService {
private prisma: PrismaClient;
private logger: Logger;
constructor(prisma: PrismaClient, logger: Logger) {
this.prisma = prisma;
this.logger = logger;
}
/**
* 分析退货对SKU的影响
* @param skuId SKU ID
* @param timeRange 时间范围
* @param traceId 链路追踪ID
* @returns 退货效果分析结果
*/
async analyzeSKUReturnEffect(
skuId: string,
timeRange: {
start: Date;
end: Date;
},
traceId: string
): Promise<ReturnEffectAnalysisResult> {
this.logger.info(`开始分析SKU ${skuId} 的退货效果`, { traceId });
try {
// 获取SKU信息
const sku = await this.prisma.sku.findUnique({
where: { id: skuId },
include: {
product: true
}
});
if (!sku) {
throw new Error(`SKU ${skuId} 不存在`);
}
// 获取退货数据
const returns = await this.prisma.return.findMany({
where: {
skuId,
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
},
include: {
order: true
}
});
// 获取销售数据
const orders = await this.prisma.order.findMany({
where: {
items: {
some: {
skuId
}
},
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
},
include: {
items: {
where: {
skuId
}
}
}
});
// 计算基本指标
const totalOrders = orders.length;
const totalReturns = returns.length;
const returnRate = totalOrders > 0 ? (totalReturns / totalOrders) * 100 : 0;
// 计算销售影响
const totalSales = orders.reduce((sum, order) => {
const skuItem = order.items.find(item => item.skuId === skuId);
return sum + (skuItem ? skuItem.price * skuItem.quantity : 0);
}, 0);
const returnedSales = returns.reduce((sum, returnItem) => {
return sum + returnItem.refundAmount;
}, 0);
const salesImpact = (returnedSales / totalSales) * 100;
// 计算利润影响
const totalProfit = orders.reduce((sum, order) => {
const skuItem = order.items.find(item => item.skuId === skuId);
if (!skuItem) return sum;
const cost = sku.costPrice || 0;
return sum + (skuItem.price - cost) * skuItem.quantity;
}, 0);
const lostProfit = returns.reduce((sum, returnItem) => {
const cost = sku.costPrice || 0;
const order = orders.find(o => o.id === returnItem.orderId);
if (!order) return sum;
const skuItem = order.items.find(item => item.skuId === skuId);
if (!skuItem) return sum;
return sum + (skuItem.price - cost) * skuItem.quantity;
}, 0);
const profitImpact = (lostProfit / totalProfit) * 100;
// 分析退货原因分布
const reasonDistribution = this.analyzeReasonDistribution(returns);
// 生成改进建议
const recommendations = this.generateRecommendations({
returnRate,
salesImpact,
profitImpact,
reasonDistribution,
sku
});
// 生成分析报告
const analysisReport = this.generateAnalysisReport({
sku,
timeRange,
returnRate,
salesImpact,
profitImpact,
reasonDistribution,
recommendations
});
const result: ReturnEffectAnalysisResult = {
skuId,
productName: sku.product.name,
timeRange,
metrics: {
returnRate,
totalOrders,
totalReturns,
totalSales,
returnedSales,
salesImpact,
totalProfit,
lostProfit,
profitImpact
},
reasonDistribution,
recommendations,
analysisReport
};
this.logger.info(`SKU ${skuId} 退货效果分析完成`, { traceId, returnRate, salesImpact, profitImpact });
return result;
} catch (error) {
this.logger.error(`分析SKU ${skuId} 退货效果失败`, { traceId, error: (error as Error).message });
throw error;
}
}
/**
* 批量分析多个SKU的退货效果
* @param skuIds SKU ID列表
* @param timeRange 时间范围
* @param traceId 链路追踪ID
* @returns 多个SKU的退货效果分析结果
*/
async batchAnalyzeSKUReturnEffect(
skuIds: string[],
timeRange: {
start: Date;
end: Date;
},
traceId: string
): Promise<ReturnEffectAnalysisResult[]> {
this.logger.info(`开始批量分析 ${skuIds.length} 个SKU的退货效果`, { traceId });
try {
const results = await Promise.all(
skuIds.map(skuId => this.analyzeSKUReturnEffect(skuId, timeRange, traceId))
);
this.logger.info(`批量分析完成,成功分析 ${results.length} 个SKU`, { traceId });
return results;
} catch (error) {
this.logger.error(`批量分析SKU退货效果失败`, { traceId, error: (error as Error).message });
throw error;
}
}
/**
* 分析退货原因分布
* @param returns 退货数据
* @returns 退货原因分布
*/
private analyzeReasonDistribution(returns: any[]): Record<string, number> {
const distribution: Record<string, number> = {};
returns.forEach(returnItem => {
const reason = returnItem.reason || '其他';
distribution[reason] = (distribution[reason] || 0) + 1;
});
return distribution;
}
/**
* 生成改进建议
* @param analysisData 分析数据
* @returns 改进建议
*/
private generateRecommendations(analysisData: {
returnRate: number;
salesImpact: number;
profitImpact: number;
reasonDistribution: Record<string, number>;
sku: any;
}): string[] {
const { returnRate, salesImpact, profitImpact, reasonDistribution, sku } = analysisData;
const recommendations: string[] = [];
// 根据退货率生成建议
if (returnRate > 30) {
recommendations.push('考虑暂时下架该SKU进行全面质量检查');
recommendations.push('重新评估供应商,考虑更换供应商');
} else if (returnRate > 20) {
recommendations.push('加强质量控制,提高产品质量');
recommendations.push('优化产品描述,确保与实际产品一致');
} else if (returnRate > 10) {
recommendations.push('定期检查库存,确保产品存储条件良好');
recommendations.push('提供更详细的产品使用说明');
}
// 根据销售影响生成建议
if (salesImpact > 20) {
recommendations.push('调整定价策略,考虑降价以减少退货率');
recommendations.push('加强售前咨询,确保客户了解产品特性');
}
// 根据利润影响生成建议
if (profitImpact > 25) {
recommendations.push('重新评估成本结构,寻找降低成本的方法');
recommendations.push('考虑提高产品价格以抵消退货损失');
}
// 根据退货原因生成建议
const topReason = Object.entries(reasonDistribution)
.sort(([,a], [,b]) => b - a)[0];
if (topReason) {
const [reason, count] = topReason;
switch (reason) {
case '质量问题':
recommendations.push('加强产品质量检测,确保符合标准');
recommendations.push('与供应商沟通,要求提高产品质量');
break;
case '尺寸不合适':
recommendations.push('提供更详细的尺寸图表');
recommendations.push('考虑提供尺寸测量指南');
break;
case '描述不符':
recommendations.push('更新产品描述,确保与实际产品一致');
recommendations.push('增加产品实拍图片和视频');
break;
case '物流损坏':
recommendations.push('优化包装方式,减少运输损坏');
recommendations.push('与物流公司合作,改善运输条件');
break;
default:
recommendations.push(`针对"${reason}"原因,制定相应的改进措施`);
}
}
return recommendations;
}
/**
* 生成分析报告
* @param data 分析数据
* @returns 分析报告
*/
private generateAnalysisReport(data: {
sku: any;
timeRange: {
start: Date;
end: Date;
};
returnRate: number;
salesImpact: number;
profitImpact: number;
reasonDistribution: Record<string, number>;
recommendations: string[];
}): string {
const { sku, timeRange, returnRate, salesImpact, profitImpact, reasonDistribution, recommendations } = data;
let report = `# SKU 退货效果分析报告\n\n`;
report += `## 基本信息\n`;
report += `- SKU ID: ${sku.id}\n`;
report += `- 产品名称: ${sku.product.name}\n`;
report += `- 分析时间范围: ${timeRange.start.toISOString()}${timeRange.end.toISOString()}\n\n`;
report += `## 核心指标\n`;
report += `- 退货率: ${returnRate.toFixed(2)}%\n`;
report += `- 销售影响: ${salesImpact.toFixed(2)}%\n`;
report += `- 利润影响: ${profitImpact.toFixed(2)}%\n\n`;
report += `## 退货原因分布\n`;
Object.entries(reasonDistribution).forEach(([reason, count]) => {
report += `- ${reason}: ${count}\n`;
});
report += `\n`;
report += `## 改进建议\n`;
recommendations.forEach((recommendation, index) => {
report += `${index + 1}. ${recommendation}\n`;
});
return report;
}
/**
* 分析高退货率SKU的整体影响
* @param threshold 退货率阈值
* @param timeRange 时间范围
* @param traceId 链路追踪ID
* @returns 高退货率SKU的整体影响分析
*/
async analyzeHighReturnSKUsImpact(
threshold: number,
timeRange: {
start: Date;
end: Date;
},
traceId: string
): Promise<SKUImpactAnalysis> {
this.logger.info(`开始分析退货率超过 ${threshold}% 的SKU整体影响`, { traceId });
try {
// 获取所有SKU的退货数据
const skus = await this.prisma.sku.findMany({
include: {
product: true
}
});
// 分析每个SKU的退货率
const highReturnSKUs = [];
let totalImpact = 0;
for (const sku of skus) {
const returns = await this.prisma.return.count({
where: {
skuId: sku.id,
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
}
});
const orders = await this.prisma.order.count({
where: {
items: {
some: {
skuId: sku.id
}
},
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
}
});
const returnRate = orders > 0 ? (returns / orders) * 100 : 0;
if (returnRate > threshold) {
// 计算该SKU的销售和利润影响
const skuOrders = await this.prisma.order.findMany({
where: {
items: {
some: {
skuId: sku.id
}
},
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
},
include: {
items: {
where: {
skuId: sku.id
}
}
}
});
const totalSales = skuOrders.reduce((sum, order) => {
const skuItem = order.items.find(item => item.skuId === sku.id);
return sum + (skuItem ? skuItem.price * skuItem.quantity : 0);
}, 0);
const skuReturns = await this.prisma.return.findMany({
where: {
skuId: sku.id,
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
}
});
const returnedSales = skuReturns.reduce((sum, returnItem) => {
return sum + returnItem.refundAmount;
}, 0);
const salesImpact = (returnedSales / totalSales) * 100;
highReturnSKUs.push({
skuId: sku.id,
productName: sku.product.name,
returnRate,
salesImpact,
totalSales,
returnedSales
});
totalImpact += salesImpact;
}
}
// 按退货率排序
highReturnSKUs.sort((a, b) => b.returnRate - a.returnRate);
const analysis: SKUImpactAnalysis = {
threshold,
timeRange,
totalHighReturnSKUs: highReturnSKUs.length,
averageImpact: highReturnSKUs.length > 0 ? totalImpact / highReturnSKUs.length : 0,
highReturnSKUs,
top5SKUs: highReturnSKUs.slice(0, 5)
};
this.logger.info(`高退货率SKU整体影响分析完成共发现 ${highReturnSKUs.length} 个高退货率SKU`, { traceId });
return analysis;
} catch (error) {
this.logger.error(`分析高退货率SKU整体影响失败`, { traceId, error: (error as Error).message });
throw error;
}
}
}