docs: 补充项目分析并更新 README 目录结构
- 新增 docs/项目分析.md:项目定位、架构、数据模型、业务流、工程质量、 两轮优化记录(含验证证据)与遗留问题清单 - README 目录结构同步现有包(cmd/*、internal/database、testdb、store 等), 并说明 service 包按 delivery_*/rock_*/merchant_*/provider_* 前缀成组
This commit is contained in:
@@ -194,21 +194,39 @@ POST /api/open/v1/orders/ship-notify
|
||||
|
||||
```
|
||||
├── backend/
|
||||
│ ├── cmd/server/main.go # 入口
|
||||
│ ├── cmd/
|
||||
│ │ ├── server/ # 服务入口
|
||||
│ │ ├── reencrypt_secrets/ # 密钥轮换:重加密落库密文
|
||||
│ │ ├── cache_images/ # 商品图本地缓存工具
|
||||
│ │ └── sync_product_images/ # 商品图同步工具
|
||||
│ └── internal/
|
||||
│ ├── config/ # 配置加载
|
||||
│ ├── handler/ # HTTP 处理器
|
||||
│ ├── middleware/ # JWT 认证、签名验证
|
||||
│ ├── model/ # 数据模型
|
||||
│ ├── pkg/ # 工具包
|
||||
│ ├── router/ # 路由
|
||||
│ └── service/ # 业务逻辑
|
||||
│ ├── config/ # 配置加载 + 启动安全体检
|
||||
│ ├── database/ # 显式 SQL 迁移(embed + checksum)
|
||||
│ │ └── migrations/ # 001~012 迁移脚本,已执行版本不可修改
|
||||
│ ├── handler/ # HTTP 处理器:merchant_* / open_* / recharge / rock …
|
||||
│ ├── middleware/ # JWT / 商户签名 / 源头签名 / 功能与权限门禁
|
||||
│ ├── model/ # 数据模型:merchant / recharge / rock / models
|
||||
│ ├── pkg/ # 工具包:applog / openlog / jwt / response / timeutil / lewan
|
||||
│ ├── router/ # 路由注册与请求日志
|
||||
│ ├── service/ # 业务逻辑
|
||||
│ │ ├── fulfillment* # 订单状态机、下单幂等、超时巡检
|
||||
│ │ ├── delivery* # 提货页 BFF:查询 / 绑定 / 提交 / 链接
|
||||
│ │ ├── provider* # 履约渠道接口与路由(DLC / 乐玩 / Rock)
|
||||
│ │ ├── rock_* # Rock 库存池、UID 强一致门禁、绑错追溯
|
||||
│ │ ├── merchant* # 商户、商品目录、成员与角色
|
||||
│ │ └── recharge / callback / user / tenant
|
||||
│ └── testdb/ # 集成测试用 PostgreSQL 临时 schema
|
||||
├── frontend/src/
|
||||
│ ├── api/ # API 调用
|
||||
│ ├── pages/ # 页面组件
|
||||
│ ├── layouts/ # 布局
|
||||
│ └── types/ # 类型定义
|
||||
├── docker-compose.yml # 容器编排
|
||||
├── Makefile # 常用命令
|
||||
└── start.sh # 开发一键启动
|
||||
│ ├── api/ # API 调用
|
||||
│ ├── pages/ # 页面组件(platformRock/ 为 Rock 大屏拆出的子组件)
|
||||
│ ├── layouts/ # 布局
|
||||
│ ├── store/ # 登录态与租户上下文
|
||||
│ └── types/ # 类型定义
|
||||
├── docs/ # 对接文档、实施方案与项目分析
|
||||
├── docker-compose.yml # 容器编排
|
||||
├── Makefile # 常用命令(dev / test / lint / fmt / docker-*)
|
||||
└── start.sh # 开发一键启动
|
||||
```
|
||||
|
||||
> `service` 包按 `delivery_*` / `rock_*` / `merchant_*` / `provider_*` 前缀成组,
|
||||
> 单个文件保持在 300 行以内,便于按业务域定位。
|
||||
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
# 游戏皮肤分销平台(affiliate_dash)项目分析
|
||||
|
||||
> 分析日期:2026-10-06
|
||||
> 分析范围:`backend/`、`frontend/`、`docs/`、构建与部署配置
|
||||
> 验证方式:`go build` / `go vet` / `go test ./...` / `tsc + vite build` / `oxlint` 实测,非静态阅读推断
|
||||
|
||||
---
|
||||
|
||||
## 一、项目定位
|
||||
|
||||
**游戏皮肤(和平精英)分销供货 SaaS 平台**。平台方自行对接上游货源,向下游商户提供
|
||||
「下单 → 发货」能力,并同时承载平台管理、商户自助与终端买家提货三类使用者。
|
||||
|
||||
| 角色 | 入口 | 职责 |
|
||||
|------|------|------|
|
||||
| 平台管理员 | `/platform-*` 后台 | 管理商户、商品目录、渠道路由、Rock 库存池 |
|
||||
| 下游商户 | `/merchant-*` 与 `/api/client/v1` | 卖皮肤、查钱包、收支回调 |
|
||||
| 终端买家 | `/delivery/:channel/:orderNo` | 只填/确认游戏 UID,扫码绑定角色,领取凭证 |
|
||||
| 上游货源 | `/api/open/v1` 与 Webhook | 三个履约渠道:DLC(第一平台 BFF)、乐玩开放平台、Rock(0666.pro) |
|
||||
|
||||
核心闭环:**商户下单扣钱包 → 渠道路由 → 买家提货绑定 → 上游发货 → 回写状态 → 回调商户**。
|
||||
|
||||
---
|
||||
|
||||
## 二、技术栈与规模
|
||||
|
||||
| 层 | 技术 |
|
||||
|----|------|
|
||||
| 后端 | Go 1.26 + Gin 1.12 + GORM + PostgreSQL 16 |
|
||||
| 前端 | React 19 + TypeScript 6 + Ant Design 6 + Vite 8 |
|
||||
| 部署 | Docker Compose + Caddy(统一入口 + SPA + API 反代) |
|
||||
|
||||
代码规模(2026-10-06 统计):
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| 后端 | 90 个 `.go` / 22,150 行 |
|
||||
| 前端 `src/` | 33 个文件 / 12,129 行 |
|
||||
| HTTP 路由 | 96 条 |
|
||||
| SQL 迁移 | 12 个 |
|
||||
| 测试函数 | 106 个(顶层) |
|
||||
| 提交历史 | 154 次(2026-07-20 → 2026-10-06) |
|
||||
|
||||
---
|
||||
|
||||
## 三、系统架构
|
||||
|
||||
### 3.1 分层
|
||||
|
||||
```
|
||||
cmd/server/main.go 组装依赖、注册 Provider、启动常驻 goroutine
|
||||
└ internal/router 96 条路由 + 结构化请求日志(高频轮询降级 DEBUG)
|
||||
└ internal/middleware 4 套鉴权:
|
||||
JWT+Tenant / OpenAuth(商户) / SourceOpenAuth(源头)
|
||||
/ RequireMerchantFeature + RequireMerchantPermissions
|
||||
└ internal/handler 薄层:参数校验与响应封装
|
||||
└ internal/service 业务主体
|
||||
└ internal/database 显式 SQL 迁移(embed + checksum + advisory lock)
|
||||
```
|
||||
|
||||
### 3.2 履约策略抽象(架构上最有价值的部分)
|
||||
|
||||
`backend/internal/service/provider.go` 定义统一接口:
|
||||
|
||||
```
|
||||
FulfillmentProvider → PrepareDelivery / Bind / CheckBind / Submit / SyncStatus / Cancel
|
||||
├ DLCProvider 第一平台 BFF(sign-proxy 签名代理)
|
||||
├ LewanProvider 乐玩开放平台
|
||||
└ RockProvider 0666.pro(本地 Token 库存池 + UID 强一致门禁)
|
||||
```
|
||||
|
||||
`FulfillmentRouter.ResolveProviders()` 按 `ProductRoutingConfig` 选择渠道,支持
|
||||
`priority / lewan / dlc / rock` 四种模式。两个务实细节:
|
||||
|
||||
1. 订单 `result_data` 一旦落了 `provider` 即**锁定渠道**,避免重试时跨渠道双发;
|
||||
2. 解析为空时仍保留 DLC 兜底,避免配置缺失导致订单无法发货。
|
||||
|
||||
新增渠道只需实现接口 + `RegisterProvider`,扩展成本低。
|
||||
|
||||
### 3.3 三条外部链路(务必区分,签名算法不通用)
|
||||
|
||||
| 链路 | 调用方 → 接收方 | 入口 | 鉴权 |
|
||||
|------|----------------|------|------|
|
||||
| 商户侧开放 API | 商户系统 → 本平台 | `/api/client/v1` | `X-App-Key` + HMAC + `body_sha256` |
|
||||
| 源头侧发货 API | 上游平台 → 本平台 | `/api/open/v1` | `X-Api-Key` + HMAC + 原始 body |
|
||||
| 商户回调 | 本平台 → 商户 URL | 商户后台配置 | `X-Event-ID` + `X-Timestamp` + `X-Sign` |
|
||||
|
||||
详见 [`API对接关系.md`](API对接关系.md) 与 [`发货通知约定.md`](发货通知约定.md)。
|
||||
|
||||
### 3.4 数据模型
|
||||
|
||||
- `Merchant` 是所有业务数据的租户隔离边界;`MerchantMember` 把「系统账号」与「商户权限」解耦,
|
||||
一个账号可属于多个商户。
|
||||
- 钱包用 `int64` 最小货币单位记账,**明确不使用 float64**;`WalletLedgerEntry` 带
|
||||
`balance_after` 与幂等键唯一索引,便于审计对账。
|
||||
- `CallbackDelivery` 是标准**事务性 outbox**,`event_id` 唯一索引供接收方幂等。
|
||||
- `APIRequestNonce` / `SourceAPINonce` 把重放保护落库,**跨进程、跨重启生效**。
|
||||
- `RockToken` 是本地通用库存池:`expected_uid` / `actual_role_id` / `mismatch_at` 支撑
|
||||
「绑定错误独立统计与追溯」。
|
||||
- `AuditLog` 记录操作者(用户或 API Client)与请求 ID。
|
||||
|
||||
---
|
||||
|
||||
## 四、关键业务流
|
||||
|
||||
1. **下单幂等**:`(merchant_id, client_order_no)` 联合唯一索引 + `request_fingerprint` 比对,
|
||||
重复提交返回原单,参数不一致则报错。
|
||||
2. **Rock UID 强一致风控**:方案见
|
||||
[`Rock发货平台代理接入与库存风控实施方案.md`](Rock发货平台代理接入与库存风控实施方案.md)。
|
||||
落地要点:本地 Token 池、基线快照比对防上游历史绑定误判、UID 不符硬阻断并记 `mismatch`、
|
||||
全局 kill switch、5 分钟水位巡检与自动补货。
|
||||
3. **可靠性护栏**:回调微信式重试(默认 16 次、退避可配)、发货超时巡检(30 分钟自动置失败)、
|
||||
发货队列防重试死锁、上游 BFF 频控治理(缓存 + 轮询降频)。
|
||||
4. **提货页**:`bind → 轮询 bind-result → submit`,轮询接口在路由层被识别为高频读并降级日志级别。
|
||||
|
||||
---
|
||||
|
||||
## 五、工程质量(优点)
|
||||
|
||||
1. **显式 SQL 迁移**而非 AutoMigrate,SHA256 checksum 防篡改(改动已执行版本会启动失败),
|
||||
`pg_advisory_xact_lock` 防并发迁移。
|
||||
2. **测试使用真实 PostgreSQL 临时 schema 隔离**(`internal/testdb`),刻意不用 SQLite,
|
||||
避免方言行为差异。
|
||||
3. **密钥 AES-256-GCM 加密落库**(`service/secret_codec.go`),API Client / 回调密钥不明文存储。
|
||||
4. **CORS 显式拒绝 `*`**,并在注释中写明与 `AllowCredentials` 组合的风险。
|
||||
5. **日志体系完整**:结构化、`X-Request-ID` 全链路透传、高频轮询降级 DEBUG、签名打码。
|
||||
6. 所有出站 HTTP 客户端均设置超时;无 `TODO/FIXME/HACK` 残留。
|
||||
|
||||
---
|
||||
|
||||
## 六、2026-10-06 优化记录
|
||||
|
||||
本轮针对分析中发现的 P0/P1 问题做了修复,全部经过实测验证。
|
||||
|
||||
### 6.1 密钥配置修复(P0)
|
||||
|
||||
**问题**:`.env` 中 `JWT_SECRET` 与 `.env.example` 逐字节相同(公开的示例值),
|
||||
且 `DATA_ENCRYPTION_KEY` 未设置,按 `config.go` 的回退逻辑实际等于 `JWT_SECRET`。
|
||||
后果是任何见过仓库的人都能伪造管理员 JWT,并解密库中全部 API/回调密钥密文。
|
||||
|
||||
**处理**:
|
||||
|
||||
| 项 | 内容 |
|
||||
|----|------|
|
||||
| 生成新密钥 | `JWT_SECRET` 96 位十六进制(384-bit)、`DATA_ENCRYPTION_KEY` 64 位十六进制(256-bit),两者独立 |
|
||||
| 历史密文迁移 | 开发库 11 条 `api_clients` + 10 条 `callback_subscriptions` 已用 `cmd/reencrypt_secrets` 重加密,0 失败 |
|
||||
| 新增防护 | `Config.SecurityWarnings()` 启动体检,示例密钥 / 未分离 / release 下开调试日志均打印 `[CONFIG] 安全告警` |
|
||||
| 新增工具 | `backend/cmd/reencrypt_secrets`(默认预演,`-apply` 才写库;幂等,坏数据不中断整轮) |
|
||||
| 新增文档 | [`密钥轮换说明.md`](密钥轮换说明.md) |
|
||||
| 其他 | `.env` 中 `OPEN_API_DEBUG` 由 `true` 改为 `false`;`.gitignore` 忽略 `.env.bak*` |
|
||||
|
||||
**验证证据**:
|
||||
|
||||
```
|
||||
# 负例:故意使用示例密钥 → 告警如期触发,/health 仍 200
|
||||
[WARN] [CONFIG] 安全告警: JWT_SECRET 仍是 .env.example 里的示例值,任何见过仓库的人都能伪造管理员 Token
|
||||
[WARN] [CONFIG] 安全告警: DATA_ENCRYPTION_KEY 未显式设置,已回退到 JWT_SECRET;…
|
||||
[WARN] [CONFIG] 安全告警: DATA_ENCRYPTION_KEY 与 JWT_SECRET 相同,建议分离以便独立轮换
|
||||
|
||||
# 正例:使用新密钥 → 0 条告警
|
||||
游戏皮肤供货平台 API 启动: http://localhost:18080
|
||||
|
||||
# 重加密:预演 → 写入 → 复跑幂等
|
||||
api_clients 扫描=11 重加密=11 已是新密钥=0 失败=0
|
||||
callback_subscriptions 扫描=10 重加密=10 已是新密钥=0 失败=0
|
||||
# 复跑(预演)
|
||||
api_clients 扫描=11 重加密=0 已是新密钥=11 失败=0
|
||||
callback_subscriptions 扫描=10 重加密=0 已是新密钥=10 失败=0
|
||||
```
|
||||
|
||||
### 6.2 测试可运行性修复(P1)
|
||||
|
||||
**问题 1**:仓库没有 `make test`,集成测试硬依赖 `127.0.0.1:15432` 的 PostgreSQL,
|
||||
未启动数据库时 60+ 用例直接 FAIL,README 也没有测试说明。
|
||||
|
||||
**问题 2(本次暴露的真实缺陷)**:`internal/testdb.withSearchPath` 使用
|
||||
`url.Values.Encode()` 重新编码整个查询串,把 `TimeZone=Asia/Shanghai` 变成
|
||||
`Asia%2FShanghai`,pgx 原样传给服务端后报 `unknown time zone Asia%2FShanghai`。
|
||||
**数据库起来之后测试依然全挂**,这才是此前「测试跑不起来」的第二层原因。
|
||||
|
||||
**处理**:
|
||||
|
||||
- `withSearchPath` 改为只**追加** `search_path`,不再重编码已有参数,
|
||||
并补充注释说明原因;
|
||||
- 新增 `internal/testdb/testdb_test.go` 锁定该行为,防止回归;
|
||||
- Makefile 新增 `make test`(自动拉起并等待 PostgreSQL 就绪)、
|
||||
`make test-db`、`make lint`、`make fmt`。
|
||||
|
||||
**验证证据**:
|
||||
|
||||
```
|
||||
$ make test
|
||||
ok affiliate_dash/internal/config ok affiliate_dash/internal/middleware
|
||||
ok affiliate_dash/internal/pkg/applog ok affiliate_dash/internal/pkg/lewan
|
||||
ok affiliate_dash/internal/pkg/openlog ok affiliate_dash/internal/pkg/response
|
||||
ok affiliate_dash/internal/pkg/timeutil ok affiliate_dash/internal/router
|
||||
ok affiliate_dash/internal/service ok affiliate_dash/internal/testdb
|
||||
```
|
||||
|
||||
顶层用例通过数:**106 / 106**。
|
||||
|
||||
### 6.3 前端体积与 HMR 修复(P1)
|
||||
|
||||
| 项 | 改造前 | 改造后 |
|
||||
|----|--------|--------|
|
||||
| 首屏静态 JS+CSS | 1,606.9 kB(gzip 497.7 kB) | 821.8 kB(gzip 268.4 kB) |
|
||||
| gzip 降幅 | — | **−46.1%** |
|
||||
| 构建体积警告 | 有(>500 kB) | 无 |
|
||||
| oxlint | 5 warnings | **0 warnings** |
|
||||
|
||||
**处理**:
|
||||
|
||||
1. **路由级懒加载**:`App.tsx` 中 11 个页面改为 `React.lazy()`,
|
||||
`Login` 与 `MainLayout` 保持同步加载(它们是未登录用户与登录后首帧的必经路径);
|
||||
新增统一占位 `RouteFallback`,同时替换了权限校验处重复的占位 div。
|
||||
各页面成为独立 chunk(如 `PlatformRock` 39.8 kB、`MerchantCenter` 43.3 kB)。
|
||||
2. **修复失效的 `useMemo`**:`PlatformUsers.tsx` 中 `columns` / `memberColumns` /
|
||||
`merchantColumns` 每次渲染都会重建,导致依赖它们的 `items` memo 完全失效。
|
||||
已将三个回调(`openEdit` / `remove` / `removeMerchantAccount`)`useCallback` 化,
|
||||
并把三个列定义 `useMemo` 化,使 memo 真正命中。
|
||||
3. **拆分 auth 模块**:`useAuth` 与 `AuthContext` 移入新增的 `store/authContext.ts`,
|
||||
使 `store/auth.tsx` 只导出 `AuthProvider` 组件,消除 `react(only-export-components)`
|
||||
导致的 HMR 整页刷新(同步更新 7 处引用)。
|
||||
|
||||
### 6.4 格式基线
|
||||
|
||||
新增 `make fmt` 并清理了 6 个文件的既有 gofmt 偏差(纯对齐与行尾空行)。
|
||||
现 `gofmt -l` 输出为空,可将格式检查直接纳入 CI。
|
||||
|
||||
---
|
||||
|
||||
## 七、遗留问题与建议
|
||||
|
||||
### P1-1 Rock BaseURL 双来源 —— 已修复(确认是真 Bug)
|
||||
|
||||
**根因确认**:`cmd/server/main.go` 把 `https://0666.pro` 硬编码进 `NewRockClient`,
|
||||
而 `RockService.GetConfig` **从不把 `rock_configs.base_url` 回写到客户端**。
|
||||
`UpdateConfig` 只改内存字段,因此后台改了上游地址会「保存那一刻生效、进程重启后失效」——
|
||||
DB 里的值被静默忽略,且没有任何日志提示。
|
||||
|
||||
**修复**:
|
||||
|
||||
| 项 | 内容 |
|
||||
|----|------|
|
||||
| 单一默认值 | 新增 `model.RockDefaultBaseURL` 常量,`main.go` / `RockClient` 兜底 / `GetConfig` 初始化三处共用 |
|
||||
| 启动同步 | 新增 `RockService.ApplyConfigToClient(ctx)`,启动时把 DB 地址同步到客户端;`main.go` 调用 |
|
||||
| 立即生效 | `UpdateConfig` 改用 `client.SetBaseURL()`,不再直接写字段 |
|
||||
| 并发安全 | `RockClient.baseURL` 原为裸字段,被 HTTP 请求与库存巡检 goroutine 并发读写(数据竞争)。改为 `sync.RWMutex` 保护,新增 `BaseURL()` / `SetBaseURL()` |
|
||||
| 请求头跟随 | `ClaimTokens` / `RefreshToken` / `RedeemSkin` 的 `Origin`、`Referer` 原为硬编码 `https://0666.pro`,改为跟随配置地址 |
|
||||
| 边界处理 | 空值/纯空白回落默认地址,末尾斜杠自动裁剪 |
|
||||
|
||||
**不修改迁移文件**:`012_rock_fulfillment.sql` 里的默认值受 checksum 保护,
|
||||
改动会让已有部署因 `migration 012 checksum mismatch` 启动失败,故保持原样并在常量处注释说明。
|
||||
|
||||
**验证**:新增 `rock_baseurl_test.go`(4 个用例),其中
|
||||
`TestRockServiceBaseURLFromConfig` 完整模拟「后台改地址 → 立即生效 → 进程重启 → 仍然生效」;
|
||||
`TestRockClientBaseURLConcurrentAccess` 在 `-race` 下验证无数据竞争。全部通过。
|
||||
|
||||
### P1-2 巨型文件 —— 已拆分(纯文件级移动,零行为变更)
|
||||
|
||||
四个文件按业务域拆分,全部为同 package 内移动,未改动任何逻辑、签名或文案:
|
||||
|
||||
| 原文件 | 行数 | 拆分结果 |
|
||||
|--------|------|----------|
|
||||
| `service/delivery.go` | 1,162 | → `delivery.go` 113、`delivery_types.go` 86、`delivery_provider.go` 136、`delivery_query.go` 248、`delivery_bind.go` 208、`delivery_submit.go` 249(`deliveryGoodID` 并入 `delivery_helpers.go`,链接管理并入 `delivery_link.go`) |
|
||||
| `handler/merchant.go` | 847 | → `merchant.go` 59、`merchant_product.go` 182、`merchant_order.go` 152、`merchant_wallet.go` 96、`merchant_api_client.go` 121、`merchant_member.go` 134、`merchant_platform.go` 169 |
|
||||
| `service/rock_service.go` | 1,177 | → `rock_service.go` 221、`rock_bindlink.go` 77、`rock_stock.go` 177、`rock_assign.go` 158、`rock_bindcheck.go` 213、`rock_redeem.go` 168、`rock_recover.go` 127、`rock_snapshot.go` 116 |
|
||||
| `pages/PlatformRock.tsx` | 1,306 | → `PlatformRock.tsx` 335(容器:state / 数据加载 / 顶层布局)+ `platformRock/` 子目录 9 个文件:`tokenColumns.tsx` 329、`RockStrategyCard.tsx` 267、`PurchaseTokenModal.tsx` 183、`RockTokenTable.tsx` 156、`RockMetricCards.tsx` 124、`VoucherPreviewModal.tsx` 95、`RockTokenFilterBar.tsx` 76、`UidEditModal.tsx` 47、`types.ts` 6 |
|
||||
|
||||
拆分后后端 `service` 包最大非测试文件从 1,177 行降到 640 行(`fulfillment.go`),
|
||||
`handler` 包最大从 847 行降到 182 行,前端 `PlatformRock.tsx` 从 1,306 行降到 335 行
|
||||
(最大子组件 329 行)。`App.tsx` 的 `lazy(() => import('./pages/PlatformRock'))` 与默认导出名未变。
|
||||
|
||||
**前端拆分的额外行为验证**(拆分 JSX 不改变渲染结果需要证据,不能只看编译通过):
|
||||
|
||||
1. SSR 快照:`renderToString(<PlatformRock/>)` 在拆分前后输出**字节一致**(40,920 bytes);
|
||||
2. jsdom 交互差分:用 mock 的 `rockApi` 真实挂载并点击,拆分前后 **23 个检查点的 DOM 快照与 API 调用日志完全一致**
|
||||
(覆盖采购弹窗、UID 修正、解冻、释放、熔断确认、筛选/搜索/翻页、保存配置、连通性测试);
|
||||
原始文件先跑两遍以证明探针本身可复现;
|
||||
3. 原文件应保留的 230 行代码逐字在新文件中命中(唯一差异是 `uidEdit` 的内联类型被抽成共享 `UidEditState`,纯类型无运行时影响)。
|
||||
4. 临时探针文件已清理,`package.json` / `package-lock.json` 无改动。
|
||||
|
||||
**验证方式**(不依赖子代理自述,独立复核):
|
||||
|
||||
- 声明级并集比对:`delivery.go` 拆分前后 55 个 `func/type/const/var` 声明完全一致,无缺失、无多出、无重复;
|
||||
- `handler` 拆分前后声明行 `diff` 为空,`MerchantHandler` 方法数 41 → 41;
|
||||
- `rock_service.go` 35 个声明块(26 func + 9 type/const)全部有归属,无遗漏无重复;
|
||||
- `gofmt -l` 全部为空,`go build ./...` / `go vet ./...` 通过;
|
||||
- 全量测试 `go test ./... -count=1` 通过,Rock 相关用例额外在 `-race` 下通过。
|
||||
|
||||
### P1-3 `service` 包按 provider 拆成子包 —— 评估后建议暂缓
|
||||
|
||||
**结论:本轮不做包级拆分,改用文件级组织替代。** 理由是包级拆分的前提成本远高于收益:
|
||||
|
||||
1. **存在硬性循环依赖**。`provider_dlc.go` / `provider_lewan.go` / `provider_rock.go` 依赖
|
||||
`OpenOrderQuery`、`DeliveryOrderState`、`ProductRoutingConfig`、`DeliveryProduct`、
|
||||
`ProviderBindResult` 等类型,而这些类型全部定义在 `service` 包内(`provider.go`、`delivery_types.go`)。
|
||||
要让 provider 变成子包,必须先把这些共享类型抽到第三个包,再让 `service` 反向导入它。
|
||||
2. **RockProvider 与 RockService 强耦合**:前者是后者的薄包装,拆包会把 `RockService`(及 `RockClient`)一并拖走,
|
||||
而 `RockService` 又被 `handler/rock.go`、`DeliveryService` 与发货超时巡检引用。
|
||||
3. **测试随之迁移**:`service` 包内约 4,500 行测试全部是 `package service` 内部测试,
|
||||
直接引用 `NewRockProvider`、`deliveryGoodID` 等非导出符号,拆包需要同步重构测试。
|
||||
4. **原始痛点是「文件太大难导航」,不是「包边界错了」**。文件级拆分已经把这个痛点解决:
|
||||
`service` 包内 46 个文件现已按 `delivery_*` / `rock_*` / `merchant_*` / `provider_*` 前缀清晰成组。
|
||||
|
||||
在没有独立测试环境、且线上有真实订单与上游凭据的前提下,做这种三包重排的回归风险不成比例。
|
||||
若后续确有需要,建议作为独立迭代推进,并先补一层端到端的订单回归测试再动手。
|
||||
|
||||
### P1-4 其余(未变)
|
||||
|
||||
| 问题 | 位置 | 建议 |
|
||||
|------|------|------|
|
||||
| `fulfillment.go` 640 行 | `service/fulfillment.go` | 本次未拆;如需可继续按「下单 / 状态机 / 查询 / 超时」拆分 |
|
||||
| `provider_dlc.go` 598 行、`provider_lewan.go` 565 行 | `service/` | 同上,可各拆为「接口适配」与「协议映射」两部分 |
|
||||
|
||||
### P2
|
||||
|
||||
| 问题 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| SQLite 时代残留 | `backend/data/affiliate.db`(0 字节)、`app.db`(90 KB) | 已无用途,可删除 |
|
||||
| 三套 HMAC 签名实现独立 | `middleware/open_auth.go`、`source_open_auth.go`、`pkg/lewan/signer.go` | 重复度高易漂移,可抽出公共签名工具 |
|
||||
| 告警渠道配置外泄面 | `AlertNotifyChannel.Config` 带 `json:"config"`,内含 webhook token | 对具备 `recharge:manage` 的商户成员可见;建议按需脱敏 |
|
||||
| 全局改写时区 | `cmd/server/main.go` 中 `time.Local = timeutil.Location()` | 影响进程内所有时间逻辑,建议改用显式 loc |
|
||||
| 前端仍有较大公共 chunk | `jsx-runtime` 479.7 kB(gzip 157.8 kB) | React + AntD 公共运行时,如需进一步优化可评估 antd 按需引入策略 |
|
||||
|
||||
---
|
||||
|
||||
## 八、本地开发与验证
|
||||
|
||||
```bash
|
||||
make install # 安装前后端依赖
|
||||
make dev # 一键启动(含 PostgreSQL),前端 :15173 / 后端 :18080
|
||||
make test # 全量测试(自动拉起 PostgreSQL,测试用临时 schema 隔离)
|
||||
make lint # go vet + oxlint
|
||||
make fmt # gofmt 格式化
|
||||
```
|
||||
|
||||
首次配置必须为 `JWT_SECRET` 与 `DATA_ENCRYPTION_KEY` 各自生成随机值,
|
||||
详见 [`密钥轮换说明.md`](密钥轮换说明.md)。
|
||||
Reference in New Issue
Block a user