Files
makemd/docs/Code_Review_Report.md
wurenzhi aa2cf560c6 feat: 添加汇率服务和缓存服务,优化数据源和日志服务
refactor: 重构数据源工厂和类型定义,提升代码可维护性

fix: 修复类型转换和状态机文档中的错误

docs: 更新服务架构文档,添加新的服务闭环流程

test: 添加汇率服务单元测试

chore: 清理无用代码和注释,优化代码结构
2026-03-19 14:19:01 +08:00

13 KiB
Raw Blame History

代码审查报告

审查日期: 2026-03-19 审查范围: 全代码库深度检查 审查目标: 逻辑统一性、冗余代码、潜在冲突


📊 审查概览

类别 问题数量 严重程度 状态
🔴 严重问题 3 需立即修复
🟡 中等问题 5 需计划修复
🟢 轻微问题 4 建议优化

🔴 严重问题

1. 服务层逻辑重复与不一致

问题描述: ArbitrageServicePriceComparisonService 存在高度重复的逻辑,但实现细节不一致。

涉及文件:

  • server/src/services/ArbitrageService.ts
  • server/src/services/PriceComparisonService.ts

具体问题:

1.1 汇率数据不一致

// ArbitrageService.ts (第62行)
private static readonly EXCHANGE_RATE = 7.2;  // 硬编码单一汇率

// PriceComparisonService.ts (第65-71行)
private static readonly EXCHANGE_RATES: Record<string, number> = {
  'CNY-USD': 0.14,
  'USD-CNY': 7.2,
  'CNY-EUR': 0.13,
  'EUR-CNY': 7.7,
  'USD-EUR': 0.92,
  'EUR-USD': 1.09
};

影响: 两个服务对相同货币对可能计算出不同的汇率结果。

1.2 机会评分算法不一致

// ArbitrageService.ts (第465-470行)
private calculateOpportunityScore(profitSnapshot: ProfitSnapshot): number {
  const profitRateScore = Math.min(profitSnapshot.profitRate * 200, 40);
  const roiScore = Math.min(profitSnapshot.roi * 50, 30);
  const netProfitScore = Math.min(profitSnapshot.netProfit / 10, 30);
  return Math.round(profitRateScore + roiScore + netProfitScore);
}

// PriceComparisonService.ts (第381-386行)
private calculateOpportunityScore(profitSnapshot: ProfitSnapshot, priceDiffPercent: number): number {
  const profitRateScore = Math.min(profitSnapshot.profitRate * 200, 40);
  const roiScore = Math.min(profitSnapshot.roi * 50, 30);
  const priceDiffScore = Math.min(priceDiffPercent, 30);  // 不同!
  return Math.round(profitRateScore + roiScore + priceDiffScore);
}

影响: 相同的利润数据可能产生不同的机会评分。


2. 利润红线检查未统一调用

问题描述: PricingService.checkRisk() 已实现完整的利润红线检查逻辑,但其他服务未调用此方法。

涉及文件:

  • server/src/services/PricingService.ts (定义了 checkRisk)
  • server/src/services/ArbitrageService.ts (未调用)
  • server/src/services/PriceComparisonService.ts (未调用)
  • server/src/services/DynamicPricingService.ts (有自己的检查逻辑)

现有实现:

// PricingService.ts (第116-135行) - 正确实现
static checkRisk(snapshot: ProfitSnapshot, isB2B: boolean): { 
  isRisk: boolean; 
  message?: string; 
  level: 'BLOCK' | 'WARN' | 'PASS' 
} {
  const profitRate = snapshot.profitRate;
  
  if (isB2B) {
    if (profitRate < 0.15) {  // B2B < 15% 阻止
      return { isRisk: true, message: `...`, level: 'BLOCK' };
    }
  } else {
    if (profitRate < 0.20) {  // B2C < 20% 预警
      return { isRisk: true, message: `...`, level: 'WARN' };
    }
  }
  return { isRisk: false, level: 'PASS' };
}

问题代码:

// ArbitrageService.ts (第107-111行) - 自己实现的检查
if (profitSnapshot.profitRate < 0.05) {
  riskLevel = 'BLOCK';
} else if (profitSnapshot.profitRate < 0.15 || profitSnapshot.roi < 0.2) {
  riskLevel = 'HIGH';
} else if (profitSnapshot.profitRate < 0.25) {
  riskLevel = 'MEDIUM';
}

影响:

  • 违反项目规则中的利润红线约束
  • 不同服务的风险判断标准不一致

3. 数据库表定义分散

问题描述: 数据库表定义分散在两个地方,违反单一职责原则。

涉及文件:

  • server/src/database/DatabaseSchema.ts - 主数据库架构
  • server/src/services/DynamicPricingService.ts - 内部定义 initTables()

问题代码:

// DynamicPricingService.ts (第216-299行)
static async initTables() {
  // 在服务内部创建表定义
  const hasConfigTable = await db.schema.hasTable(this.CONFIG_TABLE);
  if (!hasConfigTable) {
    await db.schema.createTable(this.CONFIG_TABLE, (table) => { ... });
  }
  // ...
}

影响:

  • 表定义难以统一管理
  • 初始化顺序可能出问题
  • 违反项目架构规范

🟡 中等问题

4. 文档与代码不一致

问题描述: Business_ClosedLoops.md 中提到的表名与实际代码不匹配。

涉及文件:

  • docs/00_Business/Business_ClosedLoops.md

不一致内容:

文档中的表名 实际代码中的表名 状态
cf_arbitrage_products 不存在 需补充或删除文档
cf_arbitrage_orders 不存在 需补充或删除文档
cf_arbitrage_profits 不存在 需补充或删除文档
cf_pricing_metrics 不存在 需补充或删除文档

5. 前端 DataSource 模式重复

问题描述: 多个 DataSource 文件实现了相同的 Mock/API 切换模式。

涉及文件:

  • dashboard/src/services/dynamicPricingDataSource.ts
  • dashboard/src/services/arbitrageDataSource.ts
  • dashboard/src/services/shopReportDataSource.ts

重复模式:

// 每个文件都有相同的结构
interface IXxxDataSource { ... }

class MockXxxDataSource implements IXxxDataSource { ... }

class ApiXxxDataSource implements IXxxDataSource { ... }

export const xxxDataSource: IXxxDataSource = USE_MOCK
  ? new MockXxxDataSource()
  : new ApiXxxDataSource();

建议: 抽取为通用的 DataSource 工厂模式。


6. 状态枚举定义分散

问题描述: 相同业务概念的状态枚举在多处重复定义。

涉及文件:

  • server/src/services/ArbitrageService.ts
  • server/src/services/DynamicPricingService.ts
  • server/src/database/DatabaseSchema.ts

示例:

// ArbitrageService.ts
status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'EXECUTING' | 'EXECUTED' | 'FAILED';

// DynamicPricingService.ts  
status: 'PENDING' | 'EXECUTED' | 'REJECTED' | 'EXPIRED';

// DatabaseSchema.ts (cf_arbitrage_opportunities)
table.enum('status', ['PENDING', 'APPROVED', 'REJECTED', 'EXECUTING', 'EXECUTED', 'FAILED']);

建议: 统一定义在 shared/types/ 目录下。


7. 服务实例化模式不一致

问题描述: 部分服务使用单例模式,部分不使用。

涉及文件:

  • server/src/services/ArbitrageService.ts - 使用单例
  • server/src/services/PriceComparisonService.ts - 使用单例
  • server/src/services/DynamicPricingService.ts - 不使用单例
  • server/src/services/PricingService.ts - 静态方法,无实例

影响: 服务调用方式不统一,增加理解成本。


8. 缓存策略分散

问题描述: 各服务独立定义缓存策略,未统一管理。

涉及文件:

  • server/src/services/DynamicPricingService.ts - CACHE_PREFIX, CACHE_TTL
  • server/src/services/CompetitorPriceService.ts - CACHE_PREFIX, CACHE_TTL

建议: 统一缓存配置管理。


🟢 轻微问题

9. 日志格式不统一

问题描述: 不同服务的日志前缀格式不一致。

// ArbitrageService.ts
logger.info(`[ArbitrageService] ...`);

// PriceComparisonService.ts
logger.info(`[PriceComparisonService] ...`);

// DynamicPricingService.ts
logger.info(`[DynamicPricing] ...`);  // 缩写

10. 类型导出方式不一致

问题描述: 类型导出方式混用。

// 方式1: 先定义后导出
export interface ArbitrageOpportunity { ... }

// 方式2: 定义时导出
interface PriceComparison { ... }
export interface PriceComparison { ... }  // 重复声明

11. 错误处理不一致

问题描述: 部分服务有详细的错误处理,部分直接抛出异常。


12. 注释语言混用

问题描述: 代码注释中中英文混用,不统一。


📋 修复方案

方案一:统一汇率服务

优先级: P0 预计工时: 4h

实施步骤:

  1. 创建 ExchangeRateService.ts 统一管理汇率
  2. 从外部 API 或配置获取实时汇率
  3. 修改 ArbitrageServicePriceComparisonService 调用统一服务
// 新建 server/src/services/ExchangeRateService.ts
export class ExchangeRateService {
  private static rates: Record<string, number> = { ... };
  private static lastUpdate: Date;
  
  static async getRate(from: string, to: string): Promise<number> {
    await this.ensureFresh();
    return this.rates[`${from}-${to}`] || 1;
  }
  
  private static async ensureFresh(): Promise<void> {
    // 每小时更新一次
    if (!this.lastUpdate || Date.now() - this.lastUpdate.getTime() > 3600000) {
      await this.fetchLatestRates();
    }
  }
}

方案二:统一机会评分算法

优先级: P0 预计工时: 2h

实施步骤:

  1. PricingService 中添加 calculateOpportunityScore 方法
  2. 修改 ArbitrageServicePriceComparisonService 调用统一方法
// 在 PricingService.ts 中添加
static calculateOpportunityScore(
  profitSnapshot: ProfitSnapshot,
  options?: { priceDiffPercent?: number; netProfit?: number }
): number {
  const profitRateScore = Math.min(profitSnapshot.profitRate * 200, 40);
  const roiScore = Math.min(profitSnapshot.roi * 50, 30);
  
  // 优先使用价格差异,其次使用净利润
  const thirdScore = options?.priceDiffPercent 
    ? Math.min(options.priceDiffPercent, 30)
    : Math.min((options?.netProfit || profitSnapshot.netProfit) / 10, 30);
  
  return Math.round(profitRateScore + roiScore + thirdScore);
}

方案三:统一利润红线检查

优先级: P0 预计工时: 3h

实施步骤:

  1. 修改 ArbitrageService 调用 PricingService.checkRisk()
  2. 修改 PriceComparisonService 调用 PricingService.checkRisk()
  3. 修改 DynamicPricingService 使用统一检查
// 修改 ArbitrageService.ts
import { PricingService } from './PricingService';

async createOpportunity(...): Promise<ArbitrageOpportunity> {
  // ... 计算利润
  
  // 使用统一的利润红线检查
  const riskCheck = PricingService.checkRisk(profitSnapshot, options?.isB2B || false);
  
  let riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'BLOCK';
  switch (riskCheck.level) {
    case 'BLOCK':
      riskLevel = 'BLOCK';
      break;
    case 'WARN':
      riskLevel = 'HIGH';
      break;
    default:
      riskLevel = profitSnapshot.profitRate >= 0.25 ? 'LOW' : 'MEDIUM';
  }
  
  // ...
}

方案四:集中数据库表定义

优先级: P1 预计工时: 4h

实施步骤:

  1. DynamicPricingService.initTables() 中的表定义移至 DatabaseSchema.ts
  2. DatabaseSchema.initializeAll() 中统一调用
  3. 删除服务中的表创建代码

方案五:统一状态枚举定义

优先级: P1 预计工时: 2h

实施步骤:

  1. 创建 server/src/shared/types/status.ts
  2. 定义所有业务状态枚举
  3. 修改各服务引用统一枚举
// 新建 server/src/shared/types/status.ts
export enum ArbitrageStatus {
  PENDING = 'PENDING',
  APPROVED = 'APPROVED',
  REJECTED = 'REJECTED',
  EXECUTING = 'EXECUTING',
  EXECUTED = 'EXECUTED',
  FAILED = 'FAILED'
}

export enum PricingDecisionStatus {
  PENDING = 'PENDING',
  EXECUTED = 'EXECUTED',
  REJECTED = 'REJECTED',
  EXPIRED = 'EXPIRED'
}

方案六:统一 DataSource 工厂

优先级: P2 预计工时: 3h

实施步骤:

  1. 创建 dashboard/src/services/DataSourceFactory.ts
  2. 抽取公共 Mock/API 切换逻辑
  3. 重构现有 DataSource 文件

📊 修复优先级矩阵

方案 优先级 影响范围 风险 预计工时
方案一:统一汇率服务 P0 4h
方案二:统一机会评分 P0 2h
方案三:统一利润红线 P0 3h
方案四:集中表定义 P1 4h
方案五:统一状态枚举 P1 2h
方案六DataSource工厂 P2 3h

总预计工时: 18h


验收标准

修复后需满足:

  1. 汇率统一: 所有服务使用同一汇率源
  2. 评分一致: 相同输入产生相同的机会评分
  3. 红线统一: 所有利润检查调用 PricingService.checkRisk()
  4. 表定义集中: 所有表定义在 DatabaseSchema.ts
  5. 类型统一: 状态枚举在 shared/types/ 统一定义
  6. 文档同步: 文档与代码一致

📝 执行建议

阶段一:紧急修复 (P0)

  • 先修复汇率、评分、利润红线三个核心问题
  • 预计工时9h
  • 风险:低

阶段二:架构优化 (P1)

  • 集中表定义、统一状态枚举
  • 预计工时6h
  • 风险:中

阶段三:代码规范 (P2)

  • DataSource 工厂、日志格式、注释语言
  • 预计工时3h
  • 风险:低

本报告由代码审查工具生成,建议按优先级逐步修复。