v0.1.42572cb5e5fd4e73c9fdebe994391522aeda32c25f → 9d7540590e3520e09dfff0526926853cc7e40730v0.1.42;项目:huanyan/servercodescan_issues_diff,未删改、未编造)本版是一次较大的「员工端(mp)波 2 + D61 收尾」合并批,变更面覆盖生产代码与测试:
| 类别 | 主要落点 |
|---|---|
| 新增员工端端点 | GET /app/orders/{id}(单据详情)、GET /app/performance/by-order(我的业绩·按单)、GET /app/debts/repayments(还款记录)、GET /app/stock/product-flow(入出库合并流水)、GET /app/stock/transfers/{id}、GET /app/stock/doc-detail、`GET |
| 后台运维口 | `GET |
| 安全/风控 | 首绑失败限流(新业务码 2009 BindRateLimited)、mask_openid 脱敏 |
| 数据模型 | 迁移 m20261017_000026(stock_in_order.image_urls TEXT[] 1–9 张)、m20261017_000027(staff.wx_bound_at / app_last_login_at) |
| 业务口径 | kind 由「可选+兼容」改为必传(order_doc_series/needs_treatment 签名同步收紧);建单 0 元单建单即成立(draft 同事务空支付调 settle_core);/stock-outs 增 excludeAuto 与 type;库存/调拨展示串(transit_*_display、) |
风险面(鉴权/金额/库存/隐私)相关代码集中在 src/modules/app/*、src/modules/sale/*、src/modules/inventory/*、src/modules/fund/debt.rs、src/modules/org/wx_binding.rs,下文第 4 节为人工重点复核结论。
codescan_issues_diff)修复项以「被重构/收口触点上的历史坏味道」为主,绝大多数为 Low/Medium 的复杂度与重复代码,另有真实「待办标记」被关闭:
src/modules/sale/create.rs:1445 TODO(波B/C 收口) 删除——kind 兼容分支收口为必传;tests/order_split.rs:2925 同源 TODO 删除,用例改为断言「缺 kind → 1001」。src/modules/inventory/stock_in.rs(圈复杂度、嵌套、函数过长)、src/modules/inventory/stock_transfer.rs、src/modules/inventory/stocks.rs、src/modules/sale/order_draft.rs、src/router.rs 等原问题点被拆分/抽象(RowSources、assemble_dto 公开复用时同步降复杂度)。Low 重复块与超长行随重写消除。tests/app_employee.rs、tests/order.rs、tests/m6_ops.rs 等旧问题(unwrap 无注释、SQL 字符串拼接、函数过长)在对应位置被「重排/移动」,从 base 有 → head 无 记为修复;其中大量是同文件的「位置漂移」,非真正治理(例如 tests/m6_ops.rs 同类告警只是行号平移)。结论:385 条修复里,真正有工程意义的仅「TODO 收口」与
src/侧复杂度/重复消除;测试文件的行号漂移类「修复」不应视为质量提升。
codescan_issues_diff)src/ 生产代码约 90 条:以 Medium/Complexity(函数过长、圈复杂度、嵌套过深)、Medium/Dependency(高耦合、扇入扇出异常)、Low/Maintainability(重复块、行过长)为主;Medium/Security 在 src/ 内无新增(安全类新增全部落在 tests/)。tests/ 约 500 条:主体是
SQL 注入风险(tests/* 大量 fixture 用字符串拼接 SQL 造数,如 format!("... {var} ..."))——属测试夹具噪声,非生产注入面;无安全注释的 unwrap(测试断言 as_i64().unwrap() 等)——测试代码语境下可接受;函数过长(新增端到端用例普遍 150–600 行)。建议:将测试侧
SQL 注入/unwrap归入 基线噪声,不计入门禁;否则 591 条会淹没真正需要处理的生产代码复杂度问题。
src/ 新增(代表性)| 文件 | 问题 | 说明 |
|---|---|---|
src/modules/report/performance.rs:511 | by_order 函数过长(255 行)、扇出 132、高耦合 | 新增「按单业绩」装配,取数逻辑重且集中 |
src/modules/app/stock_flow.rs:156 | product_flow 168 行、9 参、扇出 98、高耦合 | 合并流水装配;参数过多建议入参结构体化 |
src/modules/app/stock_detail.rs:135 | transfer_detail 164 行、圈复杂度 14、扇出 59 | 记录时间线 + 权限 + 明细装配同函数 |
src/modules/app/order_detail.rs:152 | order_detail 157 行、扇出 87、高耦合 | 头/行/收款/分摊/治疗单/服务人一次装配 |
src/modules/sale/order_draft.rs:422 | build_draft_allocs 圈复杂度 16、82 行 | 建议拆分销售单整单/服务单按行两分支 |
src/router.rs:349 | business_routes 727 行 | 持续膨胀,建议按域拆 子函数 |
上述均为可维护性问题,不直接构成功能缺陷;但 by_order / product_flow / transfer_detail 的高耦合叠加大扇出,是后续改动易引入错漏的高风险区(与下文第 4.1 的正确性风险相互印证)。
以下为对 diff 与源码逐点核对后,判断需要关注的问题(按影响排序)。
/app/stock/product-flow 带筛选时可能「丢行」,total 与 list 不一致src/modules/app/stock_flow.rs fetch_in(约 :405)/ fetch_out(约 :519)与 count_in(约 :389)/ count_out(约 :503)。count_* 用完整条件(门店+时间窗+productId+keyword)计数;但 fetch_* 先按门店+时间窗取最新 limit = page*page_size 张单据(in_order_cond(f) 不含 productId/keyword),再在这些单据的行上按产品/关键词过滤。fetch_* 返回空/不足,而 count_* 仍给出正确 total → 首页/前几页出现「total>0 但列表缺失/为空」。
page=1&page_size=20 取最新 20 张(均不含 X)→ 列表 0 行,total=1。productId/keyword 折进「单据候选」选择(EXISTS(匹配行) 或用行级 UNION 直接分页),或改成 WHERE 单据存在匹配行 后再 limit;否则注释里「取候选不丢行」的结论在带筛选时不成立。productId/keyword 缺省)分页是成立的(每张单据至少 1 行、行随单据一起全量取),问题仅在筛选态。ensure_flow_funcssrc/modules/sale/order_draft.rs draft_inner(0 元分支调 settle_core);对比 src/modules/sale/create.rs::create_inner 在结算前显式 ensure_flow_funcs(ctx, &req)?。POST /orders/draft 路由仅挂 FuncGuard(order.create)(src/router.rs:748);draft_inner 未调用 ensure_flow_funcs;0 元分支直接 settle_core,会实际执行划卡扣次(card_usage_txn)/ 自动出库扣库存(stock_out_order(+item))。order.create、但未被授予 card.usage / product.sale / order.pay / performance.alloc 的员工,可通过「0 元服务单(划卡行)」或「0 元销售单(产品行)」实际消耗客户卡次数/扣减库存,绕过独立功能点。WHERE status='待支付',0 行 → 1005)与并发回滚在 settle_core 内成立,单测亦覆盖「卡仅剩 1 次并发两张恰一张成立」。INCR 与 EXPIRE 非原子,异常时可能「永久锁号」src/modules/app/auth.rs record_bind_failure(INCR 成功后单独发 EXPIRE,仅 count==1 时设 TTL)。INCR 成功而 EXPIRE 前连接中断/进程退出,或该 key 因历史异常无 TTL,则计数永不过期;达到阈值后该 (openid, mobile) 将永久返回 2009。SET key 1 EX 600 NX + INCR,或 INCR 后无条件 EXPIRE(每次续期到固定窗上界亦可,注意不要无限延长窗口),并做一次「无 TTL 补设」兜底。src/modules/app/auth.rs ensure_bind_allowed:tracing::warn!(openid = %mask_openid(openid), mobile, "...")。mobile 以明文进日志;与全项目「手机号脱敏」口径不一致(mask_mobile 已存在)。mobile = %mask_mobile(mobile)。src/modules/inventory/stocks.rs warning_rows_for_store(约 :610)新增 transit_in_display/transit_out_display 时把在途量硬编码为 Decimal::ZERO(该聚合行本身不含在途),展示串恒为「待收 0 次 / 发出 0 次」。若消息中心前端复用该字段会误显示,建议该上下文置 None 或省略字段。src/modules/app/stock_detail.rs transfer_detail 记录时间线:photo_urls: vec![dto.out_image_url.clone()]——当 out_image_url 为空串时下发 [""] 而非 [],建议 .filter(|s| !s.is_empty()) 归一。kind 必传:ensure_lines_match_kind 对未知单据类型显式 Err(1001)(src/modules/sale/create.rs),order_doc_series/needs_treatment 只在两值间分派,无「未知 kind 落到销售单」的空洞。common::web::resolve 已把 page 钳到 ≥1、page_size 钳 1–100,各新端点 (page-1)*page_size 不会下溢。app/orders/{id} 用 customerMobileMasked;wx-binding 用 mask_openid;审计 detail 记录的是脱敏 openid。/app/debts/repayments 与后台 ?view=repayments 复用 repayment_page,settle_status/remaining 同源,测试断言双端同值。/app/stock/product-flow 带 productId/keyword 的分页丢行——这是唯一会导致「真实数据缺失/前后端对不上」的功能缺陷,建议本版修复并补一条「匹配单据早于候选批次」的回归用例。ensure_flow_funcs 的豁免——需产品/权限负责人确认;如保留豁免,请写入权限矩阵文档。tests/ 的 SQL 拼接/unwrap/长函数噪声,建议纳入基线忽略;真正需治理的是 src/ 中 by_order / product_flow / transfer_detail / order_detail 的高复杂度高耦合(与 4.1 风险点重合,优先重构)。TODO 收口 与 src/ 复杂度下降有实际意义,其余多为行号漂移,不作为质量提升依据。transit_text| 复用重构 | stocks::RowSources 抽出、stock_transfer::assemble_dto/load_dto 公开化、debt::repayment_page 公开化并补 settle_status/remaining、query::OrderDetailDto 增 draft_allocs |
Routersrc/modules/inventory/stock_out.rs:526 | index 圈复杂度 12 | 新增 excludeAuto 过滤叠加类型/日期/用途筛选 |
src/modules/app/workbench.rs、src/modules/org/wx_binding.rs | 高耦合(19/16 依赖) | 读/写装配依赖偏多,可归并辅助函数 |