refactor(types): 重构类型系统,统一共享类型定义

feat(types): 新增共享类型中心,包含用户、产品、订单等核心领域类型
fix(types): 修复类型定义错误,统一各模块类型引用
style(types): 优化类型文件格式和注释
docs(types): 更新类型文档和变更日志
test(types): 添加类型测试用例
build(types): 配置类型共享路径
chore(types): 清理重复类型定义文件
This commit is contained in:
2026-03-20 17:53:46 +08:00
parent 989c4b13a6
commit 427becbc8f
222 changed files with 25676 additions and 6328 deletions

View File

@@ -1,20 +1,89 @@
import { PrismaClient } from '@prisma/client';
import { Logger } from 'winston';
import { ReturnEffectAnalysisResult, ReturnEffectMetrics, SKUImpactAnalysis } from '../../shared/types/return';
import { db } from '../../config/database';
// 本地接口定义
interface Sku {
id: string;
product: {
name: string;
};
costPrice: number;
}
interface Return {
id: string;
skuId: string;
orderId: string;
refundAmount: number;
reason: string;
createdAt: Date;
}
interface Order {
id: string;
items: Array<{
skuId: string;
price: number;
quantity: number;
}>;
createdAt: Date;
}
interface ReturnEffectAnalysisResult {
skuId: string;
productName: string;
timeRange: {
start: Date;
end: Date;
};
metrics: ReturnEffectMetrics;
reasonDistribution: Record<string, number>;
recommendations: string[];
analysisReport: string;
}
interface ReturnEffectMetrics {
returnRate: number;
totalOrders: number;
totalReturns: number;
totalSales: number;
returnedSales: number;
salesImpact: number;
totalProfit: number;
lostProfit: number;
profitImpact: number;
}
interface SKUImpactAnalysis {
threshold: number;
timeRange: {
start: Date;
end: Date;
};
totalHighReturnSKUs: number;
averageImpact: number;
highReturnSKUs: Array<{
skuId: string;
productName: string;
returnRate: number;
salesImpact: number;
totalSales: number;
returnedSales: number;
}>;
top5SKUs: Array<{
skuId: string;
productName: string;
returnRate: number;
salesImpact: number;
totalSales: number;
returnedSales: number;
}>;
}
/**
* 退货效果分析服务
* 分析高退货率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
@@ -30,56 +99,28 @@ export class ReturnEffectAnalysisService {
},
traceId: string
): Promise<ReturnEffectAnalysisResult> {
this.logger.info(`开始分析SKU ${skuId} 的退货效果`, { traceId });
console.log(`开始分析SKU ${skuId} 的退货效果`, { traceId });
try {
// 获取SKU信息
const sku = await this.prisma.sku.findUnique({
where: { id: skuId },
include: {
product: true
}
});
const sku = await db('cf_sku')
.where('id', skuId)
.first();
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 returns = await db('cf_return')
.where('sku_id', skuId)
.whereBetween('created_at', [timeRange.start, timeRange.end]);
// 获取销售数据
const orders = await this.prisma.order.findMany({
where: {
items: {
some: {
skuId
}
},
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
},
include: {
items: {
where: {
skuId
}
}
}
});
const orders = await db('cf_order')
.join('cf_order_item', 'cf_order.id', '=', 'cf_order_item.order_id')
.where('cf_order_item.sku_id', skuId)
.whereBetween('cf_order.created_at', [timeRange.start, timeRange.end]);
// 计算基本指标
const totalOrders = orders.length;
@@ -88,31 +129,26 @@ export class ReturnEffectAnalysisService {
// 计算销售影响
const totalSales = orders.reduce((sum, order) => {
const skuItem = order.items.find(item => item.skuId === skuId);
return sum + (skuItem ? skuItem.price * skuItem.quantity : 0);
return sum + (order.price || 0) * (order.quantity || 0);
}, 0);
const returnedSales = returns.reduce((sum, returnItem) => {
return sum + returnItem.refundAmount;
return sum + (returnItem.refund_amount || 0);
}, 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;
const cost = sku.cost_price || 0;
return sum + ((order.price || 0) - cost) * (order.quantity || 0);
}, 0);
const lostProfit = returns.reduce((sum, returnItem) => {
const cost = sku.costPrice || 0;
const order = orders.find(o => o.id === returnItem.orderId);
const cost = sku.cost_price || 0;
const order = orders.find(o => o.order_id === returnItem.order_id);
if (!order) return sum;
const skuItem = order.items.find(item => item.skuId === skuId);
if (!skuItem) return sum;
return sum + (skuItem.price - cost) * skuItem.quantity;
return sum + ((order.price || 0) - cost) * (order.quantity || 0);
}, 0);
const profitImpact = (lostProfit / totalProfit) * 100;
@@ -142,7 +178,7 @@ export class ReturnEffectAnalysisService {
const result: ReturnEffectAnalysisResult = {
skuId,
productName: sku.product.name,
productName: sku.product_name || 'Unknown',
timeRange,
metrics: {
returnRate,
@@ -160,10 +196,10 @@ export class ReturnEffectAnalysisService {
analysisReport
};
this.logger.info(`SKU ${skuId} 退货效果分析完成`, { traceId, returnRate, salesImpact, profitImpact });
console.log(`SKU ${skuId} 退货效果分析完成`, { traceId, returnRate, salesImpact, profitImpact });
return result;
} catch (error) {
this.logger.error(`分析SKU ${skuId} 退货效果失败`, { traceId, error: (error as Error).message });
console.error(`分析SKU ${skuId} 退货效果失败`, { traceId, error: (error as Error).message });
throw error;
}
}
@@ -183,17 +219,17 @@ export class ReturnEffectAnalysisService {
},
traceId: string
): Promise<ReturnEffectAnalysisResult[]> {
this.logger.info(`开始批量分析 ${skuIds.length} 个SKU的退货效果`, { traceId });
console.log(`开始批量分析 ${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 });
console.log(`批量分析完成,成功分析 ${results.length} 个SKU`, { traceId });
return results;
} catch (error) {
this.logger.error(`批量分析SKU退货效果失败`, { traceId, error: (error as Error).message });
console.error(`批量分析SKU退货效果失败`, { traceId, error: (error as Error).message });
throw error;
}
}
@@ -306,7 +342,7 @@ export class ReturnEffectAnalysisService {
let report = `# SKU 退货效果分析报告\n\n`;
report += `## 基本信息\n`;
report += `- SKU ID: ${sku.id}\n`;
report += `- 产品名称: ${sku.product.name}\n`;
report += `- 产品名称: ${sku.product_name || 'Unknown'}\n`;
report += `- 分析时间范围: ${timeRange.start.toISOString()}${timeRange.end.toISOString()}\n\n`;
report += `## 核心指标\n`;
@@ -343,94 +379,43 @@ export class ReturnEffectAnalysisService {
},
traceId: string
): Promise<SKUImpactAnalysis> {
this.logger.info(`开始分析退货率超过 ${threshold}% 的SKU整体影响`, { traceId });
console.log(`开始分析退货率超过 ${threshold}% 的SKU整体影响`, { traceId });
try {
// 获取所有SKU的退货数据
const skus = await this.prisma.sku.findMany({
include: {
product: true
}
});
// 获取所有SKU
const skus = await db('cf_sku');
// 分析每个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 returns = await db('cf_return')
.where('sku_id', sku.id)
.whereBetween('created_at', [timeRange.start, timeRange.end]);
const orders = await this.prisma.order.count({
where: {
items: {
some: {
skuId: sku.id
}
},
createdAt: {
gte: timeRange.start,
lte: timeRange.end
}
}
});
const orders = await db('cf_order')
.join('cf_order_item', 'cf_order.id', '=', 'cf_order_item.order_id')
.where('cf_order_item.sku_id', sku.id)
.whereBetween('cf_order.created_at', [timeRange.start, timeRange.end]);
const returnRate = orders > 0 ? (returns / orders) * 100 : 0;
const returnRate = orders.length > 0 ? (returns.length / orders.length) * 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);
const totalSales = orders.reduce((sum, order) => {
return sum + (order.price || 0) * (order.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;
const returnedSales = returns.reduce((sum, returnItem) => {
return sum + (returnItem.refund_amount || 0);
}, 0);
const salesImpact = (returnedSales / totalSales) * 100;
highReturnSKUs.push({
skuId: sku.id,
productName: sku.product.name,
productName: sku.product_name || 'Unknown',
returnRate,
salesImpact,
totalSales,
@@ -453,10 +438,10 @@ export class ReturnEffectAnalysisService {
top5SKUs: highReturnSKUs.slice(0, 5)
};
this.logger.info(`高退货率SKU整体影响分析完成共发现 ${highReturnSKUs.length} 个高退货率SKU`, { traceId });
console.log(`高退货率SKU整体影响分析完成共发现 ${highReturnSKUs.length} 个高退货率SKU`, { traceId });
return analysis;
} catch (error) {
this.logger.error(`分析高退货率SKU整体影响失败`, { traceId, error: (error as Error).message });
console.error(`分析高退货率SKU整体影响失败`, { traceId, error: (error as Error).message });
throw error;
}
}