refactor: 统一全项目金额精度为角(0.1元)
## 变更概述 将全项目金额处理从整元四舍五入统一为角精度(0.1元),提高金额计算准确性和显示一致性。 ## 后端改动 - 新增 pkg/money/format.go 统一金额处理包 - Round(): 角精度四舍五入 - Min/Max(): 金额比较 - Format(): 格式化字符串 - 更新业务模块使用统一金额函数 - internal/modules/order: 订单结算改为角精度 - internal/modules/dispute: 纠纷金额处理 - internal/modules/listing: 商品定价和存储 - 更新测试用例期望值为角精度 ## 前端改动 - 新增 shared/utils/money.ts 金额工具函数 - roundMoney(): 角精度四舍五入 - formatMoney(): 格式化为字符串(保留1位小数) - formatMoneyWithSymbol(): 添加¥符号 - 更新金额计算和显示逻辑 - shared/utils/pricing.ts: 定价计算 - shared/utils/listingDisplay.ts: 商品显示 - shared/composables/useMoney.ts: 组合式函数 - 修复视图文件导入声明 - features/listings/views: 商品详情页 - features/seller/views: 卖家管理页 ## 效果 - 金额显示:¥123.0(统一保留1位小数) - 计算精度:12.45 -> 12.5(角精度) - 减少误差:避免整元四舍五入损失 - 显示一致:全项目统一格式 ## 测试 - ✅ 后端单元测试通过 - ✅ TypeScript 类型检查通过 - ✅ 开发环境正常运行 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
e41d3a923a
commit
4ebf7f75fe
@@ -0,0 +1,472 @@
|
||||
# HFB 租号平台项目分析报告
|
||||
|
||||
生成时间:2026-06-04
|
||||
|
||||
## 项目概况
|
||||
|
||||
**项目定位**:《三角洲行动》游戏账号租赁平台,面向 C2C 租号场景
|
||||
**技术栈**:Go + Gin + GORM + MySQL + Redis / Vue 3 + TypeScript + Vite + Element Plus
|
||||
**代码规模**:
|
||||
- 后端:~18,000 行 Go 代码,121 个文件
|
||||
- 前端:~32,000 行 TypeScript/Vue 代码,158 个文件
|
||||
- 测试覆盖:后端 5 个测试文件(覆盖率极低)
|
||||
- 最近活跃度:近两周 179 次提交(开发活跃)
|
||||
|
||||
**架构特点**:
|
||||
- 前后端分离,RESTful API 设计
|
||||
- 模块化设计(19 个业务模块)
|
||||
- Docker Compose 本地开发环境
|
||||
- 支持 Mock 和真实服务切换
|
||||
|
||||
---
|
||||
|
||||
## 一、优势亮点 ✅
|
||||
|
||||
### 1.1 架构设计合理
|
||||
- **清晰的模块化**:按业务领域拆分(auth、listing、order、wallet、dispute 等),职责清晰
|
||||
- **三层架构**:Repository → Service → Handler 分层明确
|
||||
- **依赖注入**:通过构造函数注入,便于测试和扩展
|
||||
- **中间件设计**:request_id、logger、recovery、auth、permission 等职责分离
|
||||
|
||||
### 1.2 工程化完善
|
||||
- **一键启动脚本**:`./scripts/dev.sh` 自动化所有启动流程
|
||||
- **健康检查机制**:Docker 容器和 HTTP 服务都有完善的健康检查
|
||||
- **日志管理**:使用 Zap 结构化日志,支持按天切分
|
||||
- **配置管理**:支持环境变量和 .env 文件,开发/生产环境隔离
|
||||
- **数据库迁移**:自动检测并执行 SQL 迁移脚本
|
||||
|
||||
### 1.3 业务功能完整
|
||||
- 核心交易流程:发布 → 审核 → 下单 → 交接 → 归还 → 结算
|
||||
- 风控体系:实名认证、信用分、冻结机制
|
||||
- 纠纷处理:申诉仲裁、证据上传、客服介入
|
||||
- 通知系统:站内信 + 订单群聊
|
||||
- 后台管理:用户、订单、商品、审核、审计日志
|
||||
|
||||
### 1.4 开发体验良好
|
||||
- **类型安全**:前端 TypeScript 严格模式,后端 Go 强类型
|
||||
- **组件化**:前端按 features 组织,共享组件复用
|
||||
- **自动化构建**:Vite 热更新 + Docker 多阶段构建优化镜像体积
|
||||
- **代码整洁**:无 TODO/FIXME 残留,console.log 极少
|
||||
|
||||
---
|
||||
|
||||
## 二、待优化问题 ⚠️
|
||||
|
||||
### 2.1 测试覆盖严重不足 🔴
|
||||
|
||||
**现状**:
|
||||
- 后端仅 5 个测试文件,覆盖率不足 5%
|
||||
- 前端配置了 Vitest 但无测试用例
|
||||
- 缺少集成测试、E2E 测试
|
||||
|
||||
**风险**:
|
||||
- 核心金融逻辑(钱包、订单、押金)无测试保障
|
||||
- 重构时容易引入 Bug
|
||||
- 交付质量完全依赖手工测试
|
||||
|
||||
**建议**:
|
||||
```
|
||||
优先级 P0:
|
||||
1. 钱包服务单元测试(余额计算、流水记录、并发安全)
|
||||
2. 订单状态机测试(状态转换、超时处理)
|
||||
3. 支付回调测试(幂等性、签名校验)
|
||||
|
||||
优先级 P1:
|
||||
4. 实名认证集成测试
|
||||
5. 权限中间件测试
|
||||
6. 前端核心流程 E2E 测试(发布-下单-交接)
|
||||
```
|
||||
|
||||
### 2.2 错误处理不一致
|
||||
|
||||
**现状**:
|
||||
- 部分模块返回自定义错误(如 `auth.ErrInvalidPhone`)
|
||||
- 部分模块直接返回 `fmt.Errorf`
|
||||
- 缺少统一的错误码体系
|
||||
- 前端错误处理分散在各组件
|
||||
|
||||
**建议**:
|
||||
```go
|
||||
// 统一错误定义
|
||||
package errors
|
||||
|
||||
type BizError struct {
|
||||
Code string // "AUTH_INVALID_PHONE"
|
||||
Message string // "手机号格式错误"
|
||||
HTTPCode int // 400
|
||||
}
|
||||
|
||||
// 错误注册表
|
||||
var (
|
||||
ErrInvalidPhone = &BizError{"AUTH_INVALID_PHONE", "手机号格式错误", 400}
|
||||
ErrInsufficientBalance = &BizError{"WALLET_INSUFFICIENT", "余额不足", 400}
|
||||
// ...
|
||||
)
|
||||
|
||||
// 中间件统一处理
|
||||
func ErrorHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
if len(c.Errors) > 0 {
|
||||
err := c.Errors.Last().Err
|
||||
if bizErr, ok := err.(*BizError); ok {
|
||||
c.JSON(bizErr.HTTPCode, gin.H{"code": bizErr.Code, "message": bizErr.Message})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 性能瓶颈隐患
|
||||
|
||||
**问题点**:
|
||||
|
||||
1. **N+1 查询风险**
|
||||
```go
|
||||
// 潜在问题:循环中查询用户信息
|
||||
for _, order := range orders {
|
||||
user := getUserByID(order.UserID) // N+1 查询
|
||||
}
|
||||
|
||||
// 优化方案:使用 GORM Preload
|
||||
db.Preload("Owner").Preload("Renter").Find(&orders)
|
||||
```
|
||||
|
||||
2. **缺少缓存层**
|
||||
- 系统配置每次查询数据库
|
||||
- 用户实名状态高频读取无缓存
|
||||
- 商品列表无 Redis 缓存
|
||||
|
||||
3. **Redis 连接未复用**
|
||||
- 每个请求都创建新连接(检查 `redis.Client` 是否全局单例)
|
||||
|
||||
**建议**:
|
||||
```go
|
||||
// 系统配置缓存
|
||||
func (s *SystemConfigService) GetConfig(key string) (string, error) {
|
||||
cacheKey := "config:" + key
|
||||
val, err := s.redis.Get(ctx, cacheKey).Result()
|
||||
if err == redis.Nil {
|
||||
val, err = s.repo.GetConfig(key)
|
||||
if err == nil {
|
||||
s.redis.Set(ctx, cacheKey, val, 5*time.Minute)
|
||||
}
|
||||
}
|
||||
return val, err
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 安全加固建议
|
||||
|
||||
**现状问题**:
|
||||
|
||||
1. **JWT Secret 弱密钥**
|
||||
```env
|
||||
JWT_SECRET=change-me # 开发环境默认值风险
|
||||
```
|
||||
|
||||
2. **缺少 Rate Limiting**
|
||||
- 登录接口无防暴力破解
|
||||
- 短信验证码虽有冷却但无 IP 级限流
|
||||
|
||||
3. **文件上传安全**
|
||||
- 虽限制文件类型,但未检测文件内容(MIME 伪造风险)
|
||||
- 无文件大小限制(潜在 DoS)
|
||||
|
||||
4. **SQL 注入风险低但需注意**
|
||||
- GORM 参数化查询保护较好
|
||||
- 但存在 `db.Where("status = ?", status)` 手动拼接风险
|
||||
|
||||
**建议**:
|
||||
```go
|
||||
// 1. 强制生产环境强密钥
|
||||
if cfg.AppEnv == "production" && cfg.JWTSecret == "change-me" {
|
||||
log.Fatal("生产环境必须设置强 JWT_SECRET")
|
||||
}
|
||||
|
||||
// 2. 添加限流中间件
|
||||
func RateLimitMiddleware(redis *redis.Client) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
key := "rate:" + c.ClientIP() + ":" + c.Request.URL.Path
|
||||
count, _ := redis.Incr(c, key).Result()
|
||||
if count == 1 {
|
||||
redis.Expire(c, key, time.Minute)
|
||||
}
|
||||
if count > 100 { // 每分钟 100 次
|
||||
c.AbortWithStatusJSON(429, gin.H{"error": "请求过于频繁"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 文件内容检测
|
||||
func validateFileContent(file []byte, allowedTypes []string) error {
|
||||
mimeType := http.DetectContentType(file)
|
||||
for _, allowed := range allowedTypes {
|
||||
if mimeType == allowed {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("文件类型不允许")
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 数据库设计可优化
|
||||
|
||||
**问题点**:
|
||||
|
||||
1. **索引缺失**
|
||||
```sql
|
||||
-- 缺少复合索引
|
||||
SELECT * FROM rental_orders
|
||||
WHERE renter_id = ? AND status = ?
|
||||
ORDER BY created_at DESC;
|
||||
-- 建议添加:KEY idx_orders_renter_status_time (renter_id, status, created_at)
|
||||
```
|
||||
|
||||
2. **JSON 字段查询效率低**
|
||||
```sql
|
||||
-- asset_summary、season_tags 使用 JSON 存储
|
||||
-- 如需频繁按标签查询,建议改为关联表
|
||||
CREATE TABLE listing_tags (
|
||||
listing_id BIGINT,
|
||||
tag_type VARCHAR(32),
|
||||
tag_value VARCHAR(64),
|
||||
INDEX(listing_id),
|
||||
INDEX(tag_type, tag_value)
|
||||
);
|
||||
```
|
||||
|
||||
3. **大字段分离不足**
|
||||
- `description TEXT` 与主查询字段混在一起
|
||||
- 建议分离到 `listing_details` 表
|
||||
|
||||
**建议**:
|
||||
```sql
|
||||
-- 优化热点查询索引
|
||||
ALTER TABLE rental_orders
|
||||
ADD INDEX idx_orders_renter_status_time (renter_id, status, created_at);
|
||||
|
||||
ALTER TABLE rental_orders
|
||||
ADD INDEX idx_orders_owner_status_time (owner_id, status, created_at);
|
||||
|
||||
-- 钱包流水查询优化
|
||||
ALTER TABLE wallet_ledger
|
||||
ADD INDEX idx_ledger_user_time (user_id, created_at DESC);
|
||||
```
|
||||
|
||||
### 2.6 前端优化空间
|
||||
|
||||
**问题点**:
|
||||
|
||||
1. **代码分割可优化**
|
||||
- 虽有 `manualChunks`,但 Element Plus 和 Vant 同时使用导致体积臃肿
|
||||
- 建议按桌面/移动端路由懒加载
|
||||
|
||||
2. **API 调用缺少取消机制**
|
||||
```typescript
|
||||
// 问题:用户快速切换页面时,旧请求未取消
|
||||
const fetchData = async () => {
|
||||
const data = await api.get('/listings');
|
||||
}
|
||||
|
||||
// 建议:使用 AbortController
|
||||
const controller = new AbortController();
|
||||
const data = await api.get('/listings', { signal: controller.signal });
|
||||
onUnmounted(() => controller.abort());
|
||||
```
|
||||
|
||||
3. **状态管理可简化**
|
||||
- 部分简单状态用 Pinia 过度设计
|
||||
- 可用 `provide/inject` 或 `localStorage` 简化
|
||||
|
||||
4. **TypeScript `any` 残留**
|
||||
- 虽然很少(1 处),但建议完全消除
|
||||
|
||||
**建议**:
|
||||
```typescript
|
||||
// 1. 路由懒加载 + 预加载
|
||||
const routes = [
|
||||
{
|
||||
path: '/admin',
|
||||
component: () => import('@/layouts/AdminLayout.vue'),
|
||||
children: [
|
||||
{
|
||||
path: 'users',
|
||||
component: () => import('@/features/admin/views/UsersView.vue'),
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// 2. API 取消封装
|
||||
export const useCancelableRequest = () => {
|
||||
const controller = ref(new AbortController());
|
||||
|
||||
onUnmounted(() => controller.value.abort());
|
||||
|
||||
const request = async (url: string, options = {}) => {
|
||||
return axios.get(url, {
|
||||
...options,
|
||||
signal: controller.value.signal
|
||||
});
|
||||
};
|
||||
|
||||
return { request };
|
||||
};
|
||||
```
|
||||
|
||||
### 2.7 监控和运维缺失 🔴
|
||||
|
||||
**现状**:
|
||||
- 无性能监控(APM)
|
||||
- 无错误追踪(Sentry)
|
||||
- 无业务指标监控(Prometheus + Grafana)
|
||||
- 日志仅存储本地,无集中采集
|
||||
|
||||
**建议**:
|
||||
```yaml
|
||||
# docker-compose.prod.yml 添加监控栈
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
||||
loki:
|
||||
image: grafana/loki
|
||||
|
||||
promtail:
|
||||
image: grafana/promtail
|
||||
volumes:
|
||||
- ./backend/logs:/logs
|
||||
- ./promtail.yml:/etc/promtail/config.yml
|
||||
```
|
||||
|
||||
```go
|
||||
// 后端添加 Prometheus 指标
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
var (
|
||||
httpRequestDuration = prometheus.NewHistogramVec(...)
|
||||
orderCreated = prometheus.NewCounterVec(...)
|
||||
walletBalance = prometheus.NewGaugeVec(...)
|
||||
)
|
||||
```
|
||||
|
||||
### 2.8 文档维护问题
|
||||
|
||||
**现状**:
|
||||
- API 文档手动维护(`docs/api.md`),易过期
|
||||
- 缺少 Swagger/OpenAPI 自动生成
|
||||
- 缺少架构图、流程图
|
||||
- 开发规范未文档化
|
||||
|
||||
**建议**:
|
||||
```go
|
||||
// 使用 swaggo 自动生成 API 文档
|
||||
// @title HFB 租号平台 API
|
||||
// @version 1.0
|
||||
// @host localhost:8080
|
||||
// @BasePath /api
|
||||
|
||||
// @Summary 发送登录验证码
|
||||
// @Tags 认证
|
||||
// @Param phone body string true "手机号"
|
||||
// @Success 200 {object} response.Success
|
||||
// @Router /auth/send-code [post]
|
||||
func (h *Handler) SendCode(c *gin.Context) { ... }
|
||||
|
||||
// 启动时访问 /swagger/index.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、优化优先级建议
|
||||
|
||||
### P0 - 立即修复(影响生产安全)
|
||||
|
||||
1. **补充核心业务单元测试**(钱包、订单、支付)
|
||||
2. **生产环境安全加固**(强密钥、限流、文件校验)
|
||||
3. **添加监控告警**(至少日志采集 + 错误告警)
|
||||
4. **数据库热点索引优化**
|
||||
|
||||
### P1 - 近期优化(提升质量)
|
||||
|
||||
5. **统一错误处理体系**
|
||||
6. **系统配置缓存层**
|
||||
7. **N+1 查询优化**
|
||||
8. **前端代码分割优化**
|
||||
9. **API 文档自动化**
|
||||
|
||||
### P2 - 长期改进(提升体验)
|
||||
|
||||
10. **引入 APM 性能监控**
|
||||
11. **前端错误边界和离线缓存**
|
||||
12. **数据库读写分离(如果流量增长)**
|
||||
13. **CI/CD 流水线完善**
|
||||
|
||||
---
|
||||
|
||||
## 四、技术债务清单
|
||||
|
||||
| 类别 | 问题 | 影响 | 工作量估算 |
|
||||
|------|------|------|-----------|
|
||||
| 测试 | 缺少单元测试 | 高 | 3-5 人天 |
|
||||
| 安全 | 限流机制缺失 | 高 | 1 人天 |
|
||||
| 性能 | 缺少缓存层 | 中 | 2 人天 |
|
||||
| 监控 | 无 APM 和告警 | 高 | 3 人天 |
|
||||
| 文档 | API 文档手动维护 | 低 | 1 人天 |
|
||||
| 数据库 | 索引优化 | 中 | 0.5 人天 |
|
||||
|
||||
**总估算**:10-15 人天可完成 P0+P1 优化
|
||||
|
||||
---
|
||||
|
||||
## 五、架构演进建议
|
||||
|
||||
### 5.1 短期(3 个月内)
|
||||
- 完善测试覆盖到 60%+
|
||||
- 接入 Sentry 错误追踪
|
||||
- 添加 Redis 缓存层
|
||||
- 补充核心业务监控指标
|
||||
|
||||
### 5.2 中期(6-12 个月)
|
||||
- 考虑微服务拆分(订单服务独立)
|
||||
- 引入消息队列(RabbitMQ/Kafka)处理异步任务
|
||||
- 实施数据库分库分表(按用户 ID 哈希)
|
||||
- WebSocket 优化为独立长连接服务
|
||||
|
||||
### 5.3 长期(1 年以上)
|
||||
- 多租户改造(支持多游戏品类)
|
||||
- 智能定价和反欺诈模型
|
||||
- 区块链存证(订单不可篡改)
|
||||
- 海外市场国际化
|
||||
|
||||
---
|
||||
|
||||
## 六、总结
|
||||
|
||||
**整体评价**:⭐⭐⭐⭐☆ (4/5)
|
||||
|
||||
这是一个**架构清晰、工程化良好**的商业项目,核心业务逻辑完整,代码质量整体优秀。主要短板在**测试覆盖和监控体系**,这在快速迭代期可以理解,但在生产上线前必须补齐。
|
||||
|
||||
**最紧迫的 3 件事**:
|
||||
1. 补充核心金融逻辑单元测试
|
||||
2. 生产环境安全加固(限流 + 强密钥 + 文件校验)
|
||||
3. 接入基础监控(日志采集 + 错误告警)
|
||||
|
||||
完成这 3 项后,项目就具备了生产级可靠性。后续可按优先级逐步优化性能和体验。
|
||||
|
||||
---
|
||||
|
||||
**分析人**:Claude Code
|
||||
**项目规模**:中型(5 万行代码)
|
||||
**技术栈成熟度**:高(Go + Vue 主流栈)
|
||||
**团队建议规模**:3-5 人(2 后端 + 2 前端 + 1 测试/运维)
|
||||
Reference in New Issue
Block a user