- 移除extension模块,将功能迁移至node-agent - 修复类型导出问题,使用export type明确类型导出 - 统一数据库连接方式,从直接导入改为使用config/database - 更新文档中的项目结构描述 - 添加多个服务的实用方法,如getForecast、getBalances等 - 修复类型错误和TS1205警告 - 优化RedisService调用方式 - 添加新的实体类型定义 - 更新审计日志格式,统一字段命名
205 lines
5.8 KiB
TypeScript
205 lines
5.8 KiB
TypeScript
import { Knex } from 'knex';
|
|
import db from '../config/database';
|
|
|
|
export class MerchantDataStatisticsService {
|
|
/**
|
|
* 统计多商户数据
|
|
* @param params 统计参数
|
|
* @param traceInfo 追踪信息
|
|
* @returns 统计数据
|
|
*/
|
|
public static async statisticsMerchantData(
|
|
params: {
|
|
merchantId?: string;
|
|
timeRange?: { start: string; end: string };
|
|
metrics?: string[];
|
|
},
|
|
traceInfo: {
|
|
tenantId: string;
|
|
shopId: string;
|
|
taskId: string;
|
|
traceId: string;
|
|
businessType: 'TOC' | 'TOB';
|
|
}
|
|
): Promise<{
|
|
merchantId: string;
|
|
timestamp: string;
|
|
metrics: Record<string, any>;
|
|
details: Array<{
|
|
metric: string;
|
|
value: number;
|
|
unit: string;
|
|
trend: 'up' | 'down' | 'stable';
|
|
}>;
|
|
}> {
|
|
try {
|
|
// 模拟数据库查询
|
|
const merchantId = params.merchantId || 'all';
|
|
const timestamp = new Date().toISOString();
|
|
|
|
// 构建统计指标
|
|
const metrics = {
|
|
totalOrders: Math.floor(Math.random() * 1000) + 100,
|
|
totalSales: (Math.random() * 100000 + 10000).toFixed(2),
|
|
averageOrderValue: (Math.random() * 500 + 100).toFixed(2),
|
|
orderCompletionRate: (Math.random() * 30 + 70).toFixed(2),
|
|
activeCustomers: Math.floor(Math.random() * 500) + 100,
|
|
newCustomers: Math.floor(Math.random() * 100) + 20,
|
|
};
|
|
|
|
const details = Object.entries(metrics).map(([metric, value]) => ({
|
|
metric,
|
|
value: typeof value === 'string' ? parseFloat(value) : value,
|
|
unit: metric.includes('Rate') ? '%' : metric.includes('Sales') || metric.includes('Value') ? '¥' : '',
|
|
trend: ['up', 'down', 'stable'][Math.floor(Math.random() * 3)] as 'up' | 'down' | 'stable',
|
|
}));
|
|
|
|
return {
|
|
merchantId,
|
|
timestamp,
|
|
metrics,
|
|
details,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error statistics merchant data:', error);
|
|
throw new Error('Failed to statistics merchant data');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成商户数据统计报告
|
|
* @param params 报告参数
|
|
* @param traceInfo 追踪信息
|
|
* @returns 报告数据
|
|
*/
|
|
public static async generateStatisticsReport(
|
|
params: {
|
|
merchantId?: string;
|
|
timeRange: { start: string; end: string };
|
|
reportType: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
},
|
|
traceInfo: {
|
|
tenantId: string;
|
|
shopId: string;
|
|
taskId: string;
|
|
traceId: string;
|
|
businessType: 'TOC' | 'TOB';
|
|
}
|
|
): Promise<{
|
|
reportId: string;
|
|
merchantId: string;
|
|
reportType: string;
|
|
timeRange: { start: string; end: string };
|
|
timestamp: string;
|
|
summary: string;
|
|
metrics: Record<string, any>;
|
|
recommendations: string[];
|
|
}> {
|
|
try {
|
|
const reportId = `REPORT-${Date.now()}`;
|
|
const merchantId = params.merchantId || 'all';
|
|
const timestamp = new Date().toISOString();
|
|
|
|
// 生成统计数据
|
|
const statistics = await this.statisticsMerchantData(params, traceInfo);
|
|
|
|
// 生成摘要
|
|
const summary = `商户 ${merchantId} 在 ${params.timeRange.start} 至 ${params.timeRange.end} 期间的${this.getReportTypeName(params.reportType)}统计报告`;
|
|
|
|
// 生成建议
|
|
const recommendations = [
|
|
'建议优化产品定价策略,提高平均订单价值',
|
|
'建议增加促销活动,吸引更多新客户',
|
|
'建议改进客户服务,提高订单完成率',
|
|
'建议分析销售数据,优化产品结构',
|
|
];
|
|
|
|
return {
|
|
reportId,
|
|
merchantId,
|
|
reportType: params.reportType,
|
|
timeRange: params.timeRange,
|
|
timestamp,
|
|
summary,
|
|
metrics: statistics.metrics,
|
|
recommendations,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error generating statistics report:', error);
|
|
throw new Error('Failed to generate statistics report');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取商户排名
|
|
* @param params 排名参数
|
|
* @param traceInfo 追踪信息
|
|
* @returns 排名数据
|
|
*/
|
|
public static async getMerchantRanking(
|
|
params: {
|
|
metric: 'sales' | 'orders' | 'customers' | 'completionRate';
|
|
limit?: number;
|
|
timeRange: { start: string; end: string };
|
|
},
|
|
traceInfo: {
|
|
tenantId: string;
|
|
shopId: string;
|
|
taskId: string;
|
|
traceId: string;
|
|
businessType: 'TOC' | 'TOB';
|
|
}
|
|
): Promise<{
|
|
metric: string;
|
|
timeRange: { start: string; end: string };
|
|
timestamp: string;
|
|
rankings: Array<{
|
|
rank: number;
|
|
merchantId: string;
|
|
merchantName: string;
|
|
value: number;
|
|
unit: string;
|
|
}>;
|
|
}> {
|
|
try {
|
|
const limit = params.limit || 10;
|
|
const timestamp = new Date().toISOString();
|
|
|
|
// 模拟排名数据
|
|
const rankings = Array.from({ length: limit }, (_, index) => ({
|
|
rank: index + 1,
|
|
merchantId: `MERCHANT-${index + 1}`,
|
|
merchantName: `商户${index + 1}`,
|
|
value: Math.floor(Math.random() * 100000) + 1000,
|
|
unit: params.metric === 'completionRate' ? '%' : params.metric === 'customers' || params.metric === 'orders' ? '' : '¥',
|
|
})).sort((a, b) => b.value - a.value);
|
|
|
|
return {
|
|
metric: params.metric,
|
|
timeRange: params.timeRange,
|
|
timestamp,
|
|
rankings,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error getting merchant ranking:', error);
|
|
throw new Error('Failed to get merchant ranking');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取报告类型名称
|
|
* @param reportType 报告类型
|
|
* @returns 报告类型名称
|
|
*/
|
|
private static getReportTypeName(reportType: string): string {
|
|
const typeNames = {
|
|
daily: '每日',
|
|
weekly: '每周',
|
|
monthly: '每月',
|
|
quarterly: '季度',
|
|
yearly: '年度',
|
|
};
|
|
return typeNames[reportType as keyof typeof typeNames] || '';
|
|
}
|
|
}
|