Files
platforms/audit/CODE_AUDIT_REPORT.md

641 lines
27 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-07-31
审计对象:`apps/*``backend/*``frontend/*`
审计方式:只读代码审计、静态检查、单元测试和本地构建
审计基线:仅以实际代码、路由、数据模型、客户端调用和测试为依据,未将 `docs/*` 作为需求或验收依据
## 1. 结论
当前代码**不具备生产发布条件**。
本次确认:
- P02 项
- P17 项
- P28 项
- P33 项
最先需要阻断发布的事项是:
1. 仓库历史中存在明文的外部数据库、Redis 等敏感配置;固定验证码又可直接进入注册、登录和重置密码流程。
2. 钱包的 `balance``withdrawal_balance` 记账口径不闭合,可产生重复支出、拒绝提现后余额凭空增加、提现完成后总余额不减少等资金错误。
3. Worker、IoT 和上传存储仍明确是 Mock实际异步与设备链路不存在。
4. 工作人员“作业前置检查”仅用于展示,业务接口没有服务端强制执行。
因此,测试和构建全部通过并不能推导出业务可上线;现有测试主要验证路由、模型和少量纯函数,没有覆盖上述关键资金、鉴权、幂等和跨端流程。
### 等级定义
| 等级 | 含义 |
|---|---|
| P0 | 可直接造成账户接管、资金损失、核心数据破坏或必须立即处置的密钥泄露 |
| P1 | 核心流程不可用、可绕过关键业务控制或上线即产生严重错误 |
| P2 | 在异常、并发、重试或特定数据下产生错误,或形成明显维护/兼容风险 |
| P3 | 低风险冗余、局部质量或性能问题 |
置信度“高”表示从当前代码可以完整证明;“中”表示代码证据明确,但删除字段或迁移前仍需核对线上数据及外部消费者。
## 2. P0发布阻断
### P0-01 仓库内存在敏感连接配置,固定验证码可形成账户接管链路
置信度:高
**证据**
- `backend/api/etc/platform_dev.yaml:7` 包含公网 PostgreSQL 连接地址、账号和明文密码。
- `backend/api/etc/platform_dev.yaml:10` 包含公网 Redis 地址和明文凭据。
- `backend/api/etc/platform_dev.yaml:12,18,24` 分别包含固定 JWT 密钥/固定验证码/字段加密密钥性质的配置。
- 该文件由 Git 跟踪,且至少出现在多个历史提交中,单纯修改当前文件不能消除历史泄露。
- `backend/api/internal/logic/common/client_auth.go:60-67` 将全局固定验证码写入 Redis。
- `backend/api/internal/logic/common/client_auth.go:73-85` 使用该固定值完成验证和消费。
- 用户与工作人员的注册、验证码登录、重置密码、支付密码设置均复用该验证能力。
**影响**
- 外部数据库和 Redis 可能被直接访问、篡改或拖库。
- 已知固定验证码的人可为目标手机号申请新的 `request_identity`,随后尝试注册、验证码登录或重置密码。
- 字段加密密钥一旦与密文数据同时泄露,敏感字段的静态加密失去保护。
**根因**
- 开发配置被当作可提交配置管理。
- Mock 验证码能力直接复用了真实身份流程,没有环境级硬隔离。
**最小整改**
1. 立即轮换数据库、Redis、JWT、字段加密等所有已提交凭据先吊销旧值再更新部署。
2. 核查相关服务的访问日志、异常登录、密码重置和数据导出记录。
3. 将敏感值迁移到环境变量或密钥管理系统,仓库只保留无效示例。
4. 清理 Git 历史中的敏感内容,并要求所有已有克隆重新同步。
5. Mock 验证码只允许在不可访问生产数据的本地环境启动;生产启动时发现 Mock 开关或固定码应直接失败。
**迁移风险**
- 字段加密密钥轮换需要双密钥读取或批量重加密方案,不能直接替换后让历史数据失效。
**复测**
- 对仓库当前树和完整 Git 历史执行密钥扫描。
- 在非本地环境验证 Mock 验证码无法启动。
- 使用旧密钥、旧数据库凭据和旧 Redis 凭据验证均已失效。
- 覆盖目标手机号的验证码登录和重置密码攻击用例。
### P0-02 钱包双余额账本不闭合,可重复支出并制造余额
置信度:高
**证据**
1. 平台可提现充值同时增加两列:
- `backend/api/internal/logic/platform/wallet/wallet.go:232-239`
2. 商城支付只扣 `balance`,不扣 `withdrawal_balance`
- `backend/api/internal/logic/client/user/shop.go:167-189`
3. 用户提现申请只扣 `withdrawal_balance`,不扣 `balance`
- `backend/api/internal/logic/common/client_wallet.go:372-398`
4. 提现完成只更新申请状态和外部交易号,不扣 `balance`
- `backend/api/internal/logic/platform/wallet/wallet.go:336-366`
5. 配送后台创建提现申请时不预扣 `withdrawal_balance`,仅在查询时减去待处理申请:
- `backend/api/internal/logic/delivery/finance.go:145-188`
6. 平台驳回任何提现申请都会把申请金额加回 `withdrawal_balance`
- `backend/api/internal/logic/platform/wallet/wallet.go:313-323`
**可复现场景**
- 钱包充值 100 元且标记可提现后:`balance=100``withdrawal_balance=100`
- 商城消费 80 元后:`balance=20``withdrawal_balance=100`
- 再申请提现 100 元会通过,形成 80 元消费加 100 元提现。
- 配送后台申请提现时未预扣;若平台驳回,现有代码仍加回金额,可将 100 元可提现余额变成 200 元。
- 提现最终完成也不会减少总余额,余额和实际资金继续背离。
**影响**
- 直接资金损失。
- 钱包余额、可提现余额、提现申请和流水无法对账。
- 客户可在正常 API 流程内触发,不需要数据库权限。
**根因**
- `withdrawal_balance``balance` 的可提现子集,但各交易没有在同一事务内维护该不变量。
- 用户提现和配送后台提现采用了两套互不兼容的预扣策略。
- 提现没有完整的冻结额、解冻、出账和不可变流水模型。
**最小整改**
1. 立即关闭充值、余额支付和提现写入口,先冻结风险窗口。
2. 明确统一不变量,例如 `0 <= available_withdrawable <= available_balance`,并增加“冻结余额/冻结可提现余额”。
3. 支付、提现申请、驳回、完成必须在事务和行锁内同时更新余额、冻结额、流水。
4. 合并用户与配送后台提现逻辑,禁止各自实现不同扣减策略。
5. 对历史钱包、流水、提现、商城订单做全量对账和差异修复。
**迁移风险**
- 不能只改代码;历史余额已经可能不可信。
- 修复前需以外部支付记录、订单、提现回执和不可变流水重建余额,避免把现有错误余额作为初始事实。
**复测**
- 覆盖充值→支付→提现、提现→驳回、提现→完成、重复请求、并发支付/提现。
- 对每一步断言余额、可提现余额、冻结额和流水守恒。
- 增加基于随机交易序列的账本不变量测试。
## 3. P1核心流程和安全控制
### P1-01 Worker 与 IoT 仅为阻塞式 Mock 进程
置信度:高
**证据**
- `backend/worker/cmd/main/main.go:14-20` 只初始化后等待退出信号,并明确输出 Redis Streams consumer 未启用。
- `backend/iot/cmd/main/main.go:14-20` 只初始化后等待退出信号,并明确输出 MQTT Broker 未连接。
- 两模块合计约 150 行 Go 代码没有消费者、重试、Outbox 投递、MQTT 会话、命令回执或业务测试。
**影响**
- 异步通知、对账、超时处理等依赖 Worker 的流程不会执行。
- 设备遥测、远程命令和回执链路不存在。
- 进程可以成功构建和启动,但只会制造“服务在线”的假象。
**最小整改**
- 发布清单中明确排除这两个能力,或在发布前实现真实适配、健康检查、失败重试、幂等和可观测性。
- 健康检查必须区分“进程存活”和“已连接 Redis Streams/MQTT”。
### P1-02 关闭 Mock 后验证码功能全部失效,代码中没有真实发送适配器
置信度:高
**证据**
- `backend/api/internal/logic/common/client_auth.go:60-63` 无条件保存 `MockVerificationCode`,没有生成随机码或调用短信渠道。
- `backend/api/internal/logic/common/client_auth.go:73-76``MockVerificationEnabled=false` 时直接拒绝所有验证码。
- 未发现短信发送接口、供应商适配器或发送结果处理。
**影响**
- 为安全而关闭 Mock 后,验证码登录、注册、密码重置和支付密码相关流程全部不可用。
**最小整改**
- 建立真实验证码生成、散列保存、发送、频率限制、失败处理和审计链路。
- Mock 与真实实现应通过依赖注入隔离,禁止在业务函数内用全局开关混用。
### P1-03 工作人员作业预检可以绕过
置信度:高
**证据**
- `backend/api/internal/logic/client/staff/auth.go:67-108` 计算机构归属、资质有效性、在岗状态和 `can_work`
- `backend/api/internal/logic/client/staff/auth.go:20-52` 的登录实现只校验账户状态、角色和密码,注释所称“资质有效”未被执行。
- `backend/api/internal/logic/client/staff/work.go:72-85``backend/api/internal/logic/client/staff/delivery.go:55-230` 的开始、轨迹、到达、异常、恢复、签收等接口没有校验同一套预检条件。
- `apps/service_app/lib/app/router.dart:12-98` 只按是否有 Token 路由;可直接进入 `/work``/tasks/:identity`,没有强制 `can_work`
**影响**
- 离岗、资质过期或缺少机构归属的人员仍可绕过页面直接调用作业 API。
**最小整改**
- 将作业资格校验提取为服务端中间件/领域守卫,挂载在所有会改变工单、配送、轨迹、证据的接口上。
- 客户端预检仅负责展示,不作为可信控制。
### P1-04 三套支付事实互相割裂,真实支付不会进入看板支付统计
置信度:高
**证据**
- 重叠模型:
- `backend/api/internal/models/fin_payment.go:8-16`
- `backend/api/internal/models/wallet_payment.go:5-20`
- `backend/api/internal/models/gasorder_payment.go:5-12`
- 除 Mock seed 外,未发现业务代码创建 `FinPayment``WalletPayment``GasorderPayment`
- 商城实际支付只更新 `EcOrder` 并创建 `WalletRecord`
- `backend/api/internal/logic/client/user/shop.go:180-191`
- 平台支付金额和渠道统计读取 `WalletPayment`
- `backend/api/internal/logic/platform/dashboard/statistics.go:106-132`
- 气体订单的金额调整又以 `GasorderPayment` 是否存在作为“已支付”判断:
- `backend/api/internal/logic/delivery/order.go:369-375`
**影响**
- 真实商城支付成功后,看板支付金额仍可能为零。
- 支付页面、财务支付、钱包支付、气体订单支付显示不同事实。
- 气体订单代码具备“已支付后禁止改价”判断,但当前流程没有形成对应支付记录。
**最小整改**
- 选定唯一支付主事实和订单支付关联模型。
- 所有支付渠道在同一事务/事件链路写入统一支付事实和钱包流水。
- 看板、财务、气站、配送后台统一读取同一事实或受控聚合。
**迁移风险**
- 三张表不能直接删;需先核对线上数据和外部消费者,建立字段映射与去重规则。
### P1-05 用户 App 的商城流程在“请前往订单页支付”后中断
置信度:高
**证据**
- 下单成功明确提示前往订单页支付:
- `apps/user_app/lib/ui/features/shop/shop_page.dart:59-70`
- `ClientRepository` 只有创建和列表方法,没有调用后端已有的支付、取消、确认收货接口:
- `apps/user_app/lib/data/repositories/client_repository.dart:43-117`
- 订单页只是三个只读列表:
- `apps/user_app/lib/ui/features/orders/orders_page.dart:7-47`
- 后端实际提供 `/shop/orders/:identity/pay``cancel``confirm-receipt`
- `backend/api/internal/routers/client.go:47-51`
**影响**
- 用户可以下单并占用库存,但不能在客户端完成支付、取消或确认收货。
**最小整改**
- 增加订单详情及基于服务端状态的支付/取消/确认动作。
- 支付请求必须持久化并复用幂等号,不能每次点击生成新值。
### P1-06 冻结或停用的钱包仍可在客户端执行资金操作
置信度:高
**证据**
- 平台允许把钱包改为启用、停用或冻结:
- `backend/api/internal/logic/platform/wallet/wallet.go:191-200`
- 客户端 `ensureWallet` 对已存在钱包直接返回,不检查状态:
- `backend/api/internal/logic/common/client_wallet.go:41-57`
- 商城支付和用户提现查询钱包时也未限制 `status`
- `backend/api/internal/logic/client/user/shop.go:167-177`
- `backend/api/internal/logic/common/client_wallet.go:366-398`
**影响**
- 风控冻结不能阻止支付和提现。
**最小整改**
- 所有资金写操作在事务内按 `status=enable` 锁定钱包;冻结后禁止新交易,仅允许受控冲正/退款。
### P1-07 上传能力仍是本地 Mock且只按扩展名判定文件类型
置信度:高
**证据**
- `backend/api/internal/logic/upload/upload.go:36-37,90-95` 明确为本地 Mock 存储。
- `backend/api/internal/logic/upload/upload.go:45-49` 只检查文件名扩展名和大小。
- `backend/api/internal/logic/upload/upload.go:79-83` 将客户端声明的 Content-Type 原样返回,没有校验文件签名或实际 MIME。
**影响**
- 伪装成图片/PDF/视频的任意内容可进入存储。
- 单机本地目录无法支持多实例、一致备份、受控下载或恶意文件隔离。
**最小整改**
- 校验魔数和解码结果,重编码图片,对视频/PDF进行独立扫描。
- 使用私有对象存储、短期授权访问、病毒扫描、审计和生命周期策略。
## 4. P2一致性、重试与维护风险
### P2-01 多处“幂等”只处理唯一键冲突,没有验证请求归属和载荷
置信度:高
**证据**
- 签收回执按全局 `request_no` 返回任意既有确认记录,没有校验订单和当前工作人员:
- `backend/api/internal/logic/client/staff/delivery.go:226-231`
- 商城支付成功后使用相同 `request_no` 重试,会先因订单状态不再是待支付而失败,无法返回原结果:
- `backend/api/internal/logic/client/user/shop.go:161-195`
- 创建工单遇到重复 `request_no` 直接返回数据库错误:
- `backend/api/internal/logic/client/user/address_ticket.go:98-109`
- 打卡重复请求返回时,响应中的 `work_status` 根据本次请求重新计算,而不是根据既有记录:
- `backend/api/internal/logic/client/staff/work.go:46-69`
**影响**
- 网络超时后的安全重试可能变成失败、误报成功,甚至返回另一订单的结果。
**最小整改**
- 幂等记录至少绑定:主体、资源、动作、请求载荷摘要和最终响应。
- 相同幂等键但载荷不同必须返回稳定冲突错误;相同载荷返回已保存结果。
### P2-02 服务 App 保存了签收幂等号,但提交时重新生成
置信度:高
**证据**
- 草稿保存 `request_no`
- `apps/service_app/lib/ui/features/work/work_detail_page.dart:114-123`
- 页面没有把该值传给仓库:
- `apps/service_app/lib/ui/features/work/work_detail_page.dart:124-136`
- 仓库在每次提交时重新生成 UUID
- `apps/service_app/lib/data/repositories/service_repository.dart:92-108`
**影响**
- 上传或请求响应丢失后,用户重试会使用新幂等键,可能重复形成签收动作。
**最小整改**
- `submitDeliveryReceipt` 必须接收并复用草稿中的 `requestNo`;删除草稿前保留服务端最终结果。
### P2-03 商城订单的组织字段在真实创建流程中永远为零
置信度:高
**证据**
- `EcOrder` 声明 `gas_station_id``delivery_point_id`
- `backend/api/internal/models/ec_order.go:15-16`
- 真实下单创建 `EcOrder` 时未赋值:
- `backend/api/internal/logic/client/user/shop.go:54-59`
- 仅 Mock seed 为这两个字段赋值。
**影响**
- 按气站/配送点统计、权限范围、履约分派或结算会得到空组织。
**最小整改**
- 若商城订单必须归属机构,应在服务端从服务关系或商品归属中确定并写入快照。
- 若业务确实为平台统一商城,应迁移后移除这两个误导字段及相关索引/展示。
### P2-04 三个 Web 管理端以复制方式维护,且配送端包含整段不可达配置
置信度:高
**证据**
- 三个 `src + scripts` 各约 1.9 万行。
- 平台端与气站端有 80 个逐字节相同文件,约 18,280 行;平台端与配送端有 79 个相同文件,约 18,199 行。
- 三端的 `CrudListPage.vue` 均为 1,175 行且内容相同。
- `frontend/delivery_admin/src/api/resources.ts:287-384` 复制了完整平台资源定义。
- `frontend/delivery_admin/src/api/resources.ts:386-426` 定义 `gasOverrides`,但导出只使用 `deliveryOverrides`
- `frontend/delivery_admin/src/api/resources.ts:428-470`
**影响**
- 一个通用缺陷需要在三处同步修复,极易发生漂移。
- 配送包携带与本端无关的大量资源、字段和动作配置。
**最小整改**
- 将 API 客户端、会话、通用 CRUD、字段渲染、权限和契约检查提取到工作区共享包。
- 各管理端只维护入口、主题和本端资源覆盖。
- 直接删除前先用引用检查确认不可达;当前 `gasOverrides` 已可由导出链证明不可达。
### P2-05 数据库自增 `id` 被作为公共 API 字段并在三端展示
置信度:高
**证据**
- 公共实体把内部主键序列化为 `id`
- `backend/api/internal/models/entity.go:10-16`
- 公共资源响应显式保留记录自身 `id`
- `backend/api/internal/logic/common/resource.go:450-486`
- 三端通用列表固定显示 `ID` 列:
- `frontend/platform_admin/src/views/shared/CrudListPage.vue:23-26`
**影响**
- `id``identity` 成为两个公开身份字段,增加前端误用和外部耦合。
- 连续主键还会泄露记录规模和创建顺序。
**最小整改**
- 数据库 `id` 本身不是冗余字段,不应删除;应从 HTTP 响应和前端移除。
- 迁移前检查外部调用方是否仍使用 `id`,提供兼容窗口。
### P2-06 两个 Flutter API 客户端缺少超时和统一的 401 会话失效处理
置信度:高
**证据**
- `apps/user_app/lib/data/services/api_client.dart:47-76`
- `apps/service_app/lib/data/services/api_client.dart:41-80`
- 请求直接等待 `_client.send`/上传结果,没有连接、读取或总超时。
- 401 只抛异常,没有清理安全存储中的 Token。
- 两个路由都仅以“Token 字符串非空”判断已登录:
- `apps/user_app/lib/app/router.dart:14-21`
- `apps/service_app/lib/app/router.dart:12-19`
**影响**
- 弱网下页面可能长期挂起。
- Token 过期或被撤销后,用户会停留在已登录路由并持续收到请求错误。
**最小整改**
- 建立共享客户端层统一超时、有限重试、取消、401 清会话和重新登录。
- 资金写操作只能依赖幂等键重试,不能盲目重放。
### P2-07 前后端契约检查只比较资源名、模式和路由字符串
置信度:高
**证据**
- `frontend/platform_admin/scripts/check-backend-contract.mjs:12-37` 通过正则读取 `define(name, mode)`,再检查资源路径和路由源码是否包含字符串。
- 未校验 HTTP 方法、动作路径、请求字段、必填项、枚举、响应字段和错误码。
- 气站、配送端脚本采用同类实现。
**影响**
- `contract:check` 通过不能发现支付动作缺失、字段漂移或错误 HTTP 方法。
**最小整改**
- 使用结构化 OpenAPI/Schema 生成客户端与字段类型。
- 至少对每个动作校验方法、路径、请求/响应结构,并增加真实路由契约测试。
### P2-08 钱包创建存在并发竞态,银行卡敏感字段加密错误被忽略
置信度:高
**证据**
- `ensureWallet` 采用先查后创建,唯一键冲突时不回查已有钱包:
- `backend/api/internal/logic/common/client_wallet.go:41-57`
- 首次并发请求时,一个请求可能收到数据库唯一约束错误。
- 绑卡时只处理卡号加密错误,身份证和手机号加密错误被丢弃:
- `backend/api/internal/logic/common/client_wallet.go:287-300`
**影响**
- 新用户并发访问钱包时出现偶发失败。
- 加密异常可能生成空密文但仍写入银行卡记录,造成不可恢复的数据缺失。
**最小整改**
- 使用 `ON CONFLICT DO NOTHING` 后回查,或在事务中锁定所有者。
- 每个敏感字段的加密错误都必须中止事务。
## 5. P3已证实或疑似冗余
### P3-01 钱包第三方账号字段只有 Mock 数据写入
置信度:中
**证据**
- `backend/api/internal/models/wallet_basic.go:11-14` 定义支付宝、微信账号及姓名四个字段。
- 生产业务中未发现写入入口;仅 seed 使用。
- Web 资源仍展示这些字段。
**判断**
- 当前属于“疑似冗余/未完成字段”,不能仅凭静态引用直接删除。
**建议**
- 核对线上非空率、导出消费者和未来渠道设计;确认不用后再做带回滚方案的迁移。
### P3-02 Web 与状态 Store 保留多组本端不可达能力和模板字段
置信度:中
**证据**
- 气站/配送端的通用平台 API 保留角色创建、角色菜单、平台账号等调用,但本端路由和资源未暴露这些页面。
- 三端用户 Store 保留 `job``organization``location``email``introduction``personalWebsite` 等模板字段,实际登录资料只填充账号、名称、头像、角色和菜单。
**建议**
- 先用 TypeScript 引用分析和运行时埋点确认不可达,再删除本端无关方法和状态字段。
- 共享 Store 只保留跨端最小会话模型,本端扩展单独声明。
### P3-03 离线草稿读取重复解析同一加密文件
置信度:高
**证据**
- `apps/service_app/lib/data/offline/encrypted_draft_store.dart:69-77` 先读取并 `jsonDecode` 外层载荷,随后再次读取文件并在 `_decrypt` 内再次解析。
**影响**
- 每次读草稿多一次文件读取和 JSON 解析,且前后两次读取理论上可能看到不同内容。
**最小整改**
- 读取一次字符串,完成结构校验后把同一值传给解密函数。
## 6. 字段与表冗余结论
| 对象 | 结论 | 依据 | 处理方式 |
|---|---|---|---|
| 数据库 `id` | 数据库内部必需,公共 API/UI 冗余 | API 同时提供 `identity`UI仍显示 `id` | 保留数据库列;移除公开序列化和 UI 展示 |
| `FinPayment` / `WalletPayment` / `GasorderPayment` | 已证实事实重叠,但不能直接删表 | 实际支付不写三表,看板却读其中一表 | 先确定唯一支付事实并迁移、对账 |
| `EcOrder.GasStationID/DeliveryPointID` | 当前生产流程未填充 | 仅 seed 写入 | 明确归属规则后补写,或迁移删除 |
| `WalletBasic` 支付宝/微信四字段 | 疑似冗余 | 仅 seed 写入 | 查线上非空率及外部消费者后决定 |
| 配送端 `gasOverrides` | 已证实代码冗余 | 定义后未进入导出链 | 可删除,并用静态检查防回归 |
| Web 会话模板字段 | 疑似冗余 | 未进入实际资料映射 | 引用和运行时确认后删除 |
没有将“只在响应、展示或 seed 中出现”的字段直接判定为可删。数据库删字段、删表前必须补充:
1. 线上非空率和取值分布;
2. API 网关/访问日志中的字段消费者;
3. 报表、导出、脚本和第三方集成引用;
4. 双写/回填/回滚方案。
## 7. 测试、静态检查与构建结果
已实际执行:
```text
backend/api:
go test ./... 通过
go vet ./... 通过
go build ./cmd/main/main.go 通过
backend/worker:
go test ./... 通过(无测试文件)
go build ./cmd/main/main.go 通过
backend/iot:
go test ./... 通过(无测试文件)
go build ./cmd/main/main.go 通过
apps/user_app:
flutter analyze 通过
flutter test 通过2 个测试)
apps/service_app:
flutter analyze 通过
flutter test 通过3 个测试)
frontend/platform_admin:
pnpm type:check 通过
pnpm lint 退出码 0112 warnings / 12 infos
pnpm contract:check 通过47 个资源)
pnpm build 通过
frontend/gas_admin:
pnpm type:check 通过
pnpm lint 退出码 0105 warnings / 12 infos
pnpm contract:check 通过20 个资源)
pnpm build 通过
frontend/delivery_admin:
pnpm type:check 通过
pnpm lint 退出码 0106 warnings / 12 infos
pnpm contract:check 通过18 个资源)
pnpm build 通过
```
说明:
- Web lint 的相当一部分告警来自工具无法识别 Vue 模板对 `script setup` 变量的使用,不能全部视为真实死代码;本报告仅列出能够从导出/调用链证明的冗余。
- 后端虽有若干测试文件,但没有覆盖用户钱包支付、用户提现、配送提现、验证码完整流程、工作人员预检强制执行等高风险路径。
- 两个 Flutter App 的测试主要覆盖模型和登录页面不覆盖商城支付、离线签收重试、401 会话失效或完整作业流程。
## 8. 建议整改顺序
### 立即处置
1. 关闭相关公网凭据并轮换全部已泄露密钥。
2. 关闭生产资金写入口,审计历史余额和提现。
3. 禁止生产环境启用固定验证码和 Mock 支付。
### 第一阶段:资金与身份
1. 重建钱包不变量、冻结额、统一流水和提现状态机。
2. 合并支付事实,完成历史数据对账。
3. 接入真实验证码并增加限流、审计和攻击测试。
4. 强制服务端工作人员作业资格守卫。
### 第二阶段:闭环与可靠性
1. 完成用户 App 支付/取消/收货闭环。
2. 修复所有幂等键的归属和载荷校验。
3. 修复服务 App 草稿幂等号传递、401 会话和网络超时。
4. 上线真实 Worker、IoT、对象存储及健康检查。
### 第三阶段:去重与契约
1. 抽取三个 Web 端共享包,删除不可达配置。
2. 用结构化契约替换正则和源码字符串检查。
3. 在确认线上数据和消费者后迁移疑似冗余字段/表。
## 9. 审计限制
- 未连接公网数据库、Redis、短信、支付、MQTT 或对象存储,未验证仓库中凭据是否仍有效。
- 未启动依赖外部数据库的完整 E2E 流程,结论来自当前代码可证明的控制流、数据写入和客户端调用链。
- 未使用 `docs/*` 推导功能缺口或业务要求。
- 未修改任何业务源代码;只新增本报告。