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

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

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

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

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

510 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码审查报告
> **审查日期**: 2026-03-19
> **审查范围**: 全代码库深度检查
> **审查目标**: 逻辑统一性、冗余代码、潜在冲突
---
## 📊 审查概览
| 类别 | 问题数量 | 严重程度 | 状态 |
|------|----------|----------|------|
| 🔴 严重问题 | 3 | 高 | 需立即修复 |
| 🟡 中等问题 | 5 | 中 | 需计划修复 |
| 🟢 轻微问题 | 4 | 低 | 建议优化 |
---
## 🔴 严重问题
### 1. 服务层逻辑重复与不一致
**问题描述**:
`ArbitrageService``PriceComparisonService` 存在高度重复的逻辑,但实现细节不一致。
**涉及文件**:
- `server/src/services/ArbitrageService.ts`
- `server/src/services/PriceComparisonService.ts`
**具体问题**:
#### 1.1 汇率数据不一致
```typescript
// 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 机会评分算法不一致
```typescript
// 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` (有自己的检查逻辑)
**现有实现**:
```typescript
// 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' };
}
```
**问题代码**:
```typescript
// 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()
**问题代码**:
```typescript
// 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`
**重复模式**:
```typescript
// 每个文件都有相同的结构
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`
**示例**:
```typescript
// 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. 日志格式不统一
**问题描述**:
不同服务的日志前缀格式不一致。
```typescript
// ArbitrageService.ts
logger.info(`[ArbitrageService] ...`);
// PriceComparisonService.ts
logger.info(`[PriceComparisonService] ...`);
// DynamicPricingService.ts
logger.info(`[DynamicPricing] ...`); // 缩写
```
---
### 10. 类型导出方式不一致
**问题描述**:
类型导出方式混用。
```typescript
// 方式1: 先定义后导出
export interface ArbitrageOpportunity { ... }
// 方式2: 定义时导出
interface PriceComparison { ... }
export interface PriceComparison { ... } // 重复声明
```
---
### 11. 错误处理不一致
**问题描述**:
部分服务有详细的错误处理,部分直接抛出异常。
---
### 12. 注释语言混用
**问题描述**:
代码注释中中英文混用,不统一。
---
## 📋 修复方案
### 方案一:统一汇率服务
**优先级**: P0
**预计工时**: 4h
**实施步骤**:
1. 创建 `ExchangeRateService.ts` 统一管理汇率
2. 从外部 API 或配置获取实时汇率
3. 修改 `ArbitrageService``PriceComparisonService` 调用统一服务
```typescript
// 新建 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. 修改 `ArbitrageService``PriceComparisonService` 调用统一方法
```typescript
// 在 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` 使用统一检查
```typescript
// 修改 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. 修改各服务引用统一枚举
```typescript
// 新建 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
- 风险:低
---
*本报告由代码审查工具生成,建议按优先级逐步修复。*