#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 存量 Controller「就绪度」盘点:老门户的每个 Controller,它依赖的 `clouds.api` 服务在新栈**齐不齐**。 它回答什么(这是挑下一批迁移目标前的**第一步**,见 AGENTS.md §3.1 第 0 步): 一个老 Controller 里的每个 `import com.zteits.clouds.api.service.*`,是不是 (a) 接口在 `parking-module-api` 里存在,且 (b) 有一个 `class XxxImpl implements Xxx` 的实现类。 两条都满足才算"就绪"。**任一不满足 = 这个 Controller 搬过来就是 500**, 不管它的端点看上去多简单、多像"只读",所以**不要**靠读代码去猜,跑这个脚本。 全仓当前结果(2026-10-13 盘点,迁移进度同步到 **2026-10-26 第二百八十切片**,`zteits-bcp-portal` 一个 portal):老门户 **232 个 Controller**, 其中 **227 个有映射 / 5 个是死代码**(整类被注释掉,本脚本已跳过)。 有映射的 227 个里:**223 个 / 1180 端点就绪**(依赖服务齐全),**4 个 / 38 端点有缺口**。 就绪的 223 个中已迁 **223 个 / 1180 端点** → 就绪且未迁 **0 个 / 0 端点**; **再过坑 22 一刀:✔ 0 个 / 0 端点,⚠ 0 个 / 0 端点(⚠A 0/0 + ⚠B 0/0)。** ⛔ **2026-10-20 第一百六十八切片:`OrderExamineController` 判定不可迁(半合并壳)** 四个生产分支都只有 Controller + API 接口,**没有实现、没有 Dubbo provider、`examinePlNos` 全仓无赋值**; 实现只在未合入的 `origin/chifeng_dev_high_video_examine` ⇒ 机械缺口仍报,但单列 `gapAdjudicated*` / `gapOpen*` 两组数,让「可迁缺口」归零而「机械缺口」仍说真话。 详见代码里的 `GAP_ADJUDICATED` 表。 ★ **2026-10-20 第一百六十九切片:P3 ③ APP 端登录(`UserLoginAppController` 5 端点)** **本片不在本脚本口径内**:`DEFAULT_LEGACY` 只扫 `zteits-bcp-portal/.../web`, `zteits-pay-portal/.../web/app` 从来不在扫描根里(app 端 Controller 不消费 portal 的 `clouds.api` 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**(已判 1/2、可迁 1/9)、5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十切片:P3 ④ 登录桥补 `loginCode`(零端点)** 只改框架层(`LoginUser.info` 多一个 `username`)+ `LoginUserBridge`,**不新增也不迁移任何端点** ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 ★ **2026-10-20 第一百七十一切片:APP 端只读小接口(5 类 / 7 端点)** 搬的是 `zteits-pay-portal/.../web/app/{Activity,AppNews,CommonProblem,CustRule,AppCheck}`, 同 §169 一样**不在 `DEFAULT_LEGACY` 里** ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十二切片:APP 端码表/组织/反馈建议(3 类 / 8 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{SysCodeValueQueryController, AppOrgController, SuggestController(只读 4/5)}`, 同样**不在 `DEFAULT_LEGACY` 里**(这 3 个类只依赖 `AppOrgService`/`TdCCustPersonService`/ `TdCCustSuggestService`/`TdCCustSuggestDetailQueryService` 与缓存工具,不消费 portal 契约) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十三切片:APP 端对外车场信息/停车记录/ETC(3 类 / 5 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{outinterface/ParkInfoController, outinterface/ParkRecordController, newversion/ETCStateController}`,同样**不在 `DEFAULT_LEGACY` 里** (这 3 个类只依赖 `ParkFreeBerthQueryService`/`ParkingLotQueryService`/`BerthManageService`/ `ParkOrderQueryService`/`TdCCustPersonService`/`SysConfigCodeOrgIdService` 与 `AppTokenUtil`, 不消费 portal 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十四切片:APP 端接口平台/积分/时域卡/模板缓存/现金找零(5 类 / 9 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{ApiController, CustScoreController, newversion/VipTimeCardRuleController, MessageTempletCacheRefreshController, cashchange/KeyTopCashChangeController}`, 同样**不在 `DEFAULT_LEGACY` 里**(这 5 个类只依赖 `ApiService`/`TdCCustScoreDetailService`/ `TdCFileService`/`TVipAreaCardService`/`TdCMsgTemplateService` + `MessageTempletCacheUtil`, 不消费 portal 契约)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十五切片:APP 端初始化/无感支付/特殊车牌(3 类 / 4 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{pingkang/InitController, newversion/NonSensePaymentController, specialcar/SpecialCarnumberBillingController}`,同样**不在 `DEFAULT_LEGACY` 里** (只依赖 `SysCommonService`/`TdCAcctNoSecretService`/`SpecialCarnumberBillingService`/ `TdCAcctPersonService` + `RedisCacheUtil`/`ParkFreeBerthsCacheUtil`,不消费 portal 契约) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十六切片:APP 端用户签到/免密支付(2 类 / 9 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{UserSignController, AcctNoSecretController}`,同样**不在 `DEFAULT_LEGACY` 里** (只依赖 `TdCCustSignService`/`AcctNoSecretService`/`TdCCustPersonService`, 不消费 portal 契约、不新增缓存) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十七切片:APP 端年底活动/卡劵(2 类 / 8 端点)** 搬的是 `zteits-pay-portal/.../web/app/` `{YearActivityController, CouponRuleController}`,同样**不在 `DEFAULT_LEGACY` 里** (只依赖 `TdCCouponRuleService`/`TdCCouponPersonService`/`TdCCustScoreDetailService`/ `TdCCustPersonService`/`AppOrgService`,不消费 portal 契约) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ **2026-10-20 第一百七十八切片:APP 端车主车牌(1 类 / 3 端点)** 搬的是 `zteits-pay-portal/.../web/app/UserCarController`,同样**不在 `DEFAULT_LEGACY` 里** (只依赖 `TdCCarService`/`TdCCarBrandService`/`TdCCarCheckRecordQueryService`,不消费 portal 契约) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ 注意:同批原定的 `CustScoreController`(`/score/*` 4 条)新栈早已迁过 (`controller/app/score/CustScoreAppController`)⇒ 立项先 `grep` 路由,别只 grep 类名。 ★ **2026-10-20 第一百七十九切片:APP 端停车场信息查询(1 类 / 7 端点)** 搬的是 `zteits-pay-portal/.../web/app/TabParkingLotController`(老 `/park`),同样 **不在 `DEFAULT_LEGACY` 里**(只依赖 `ParkingLotQueryService`/`TBillingDescriptionQueryService`/ `ParkFreeBerthQueryService`/`TpPBerthsQueryService`/`BerthManageService`/`AppOrgService`, 全部是新栈既有 Bean;6 个门户 VO 落到 `parking-legacy-common/.../vo/app/parklot`) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ 注意:本片 7 条里只有 `query-park-berth-msg-res` 验签;带对象体的 5 条沿用老 `@Valid`, 所以 `{}` 会被 `AppBaseReq` 的 `@NotBlank` 挡在绑定层 400(不是 500)。 ★ **2026-10-20 第一百八十切片:APP 端小额免密支付(1 类 / 6 端点,4 条真写)** 搬的是 `zteits-pay-portal/.../web/app/PettyPaymentController`(老 `/pettyPayment`,新 `/pay/petty-payment`), 同样**不在 `DEFAULT_LEGACY` 里**(只依赖 `TdCCustPersonService`;2 个门户 VO 落在 `parking-legacy-common/.../vo/app/pettypayment`)⇒ 就绪 **225/1199**、已迁 **221/1195**、 未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。这里只记录口径,不改判定。 ★ 注意:本类**一次都没调** `validateSigin`/`checkParam`,`@NoAuth`/`⚠A` 那套启发式对它不适用; 取用户靠 `AppTokenUtil.getUserInfo` ⇒ 假 token 是 [存量错误码 -10000],不是 [4014]。 ★ 注意:`IRainParkingController`(9)整个类已注释掉 ⇒ 死代码,没有迁移目标。 ★ **2026-10-20 第一百八十一切片:反馈建议第 5 条 `saveFeedbackAndSuggest`(1 端点,真写)** 补的是第一百七十二切片留下的那条写端点,直接加进既有 `SuggestAppController` (老 `/suggest/saveFeedbackAndSuggest` → 新 `/pay/suggest/save-feedback-and-suggest`) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ 注意:`orgId` 非数字时 `Long.parseLong` 抛 NFE,位置在 token 校验**之前** ⇒ 500「系统异常」。 ★ 注意:新增分支的 `insertTdCCustSuggest` 返回值被丢弃(存量缺陷,照搬)。 ★ **2026-10-20 第一百八十二切片:APP 端个人卡券 `CouponPersonController`(1 类 / 8 端点,4 条真写)** 同样**不在 `DEFAULT_LEGACY` 里**(`zteits-pay-portal/.../web/app` 不在盘点范围)。依赖全是新栈既有 Bean: `TdCCouponPersonService`/`TdCCouponRuleService`/`TdCCustScoreDetailService`/`TdCAcctPersonService`/ `WhitelistService`/`AppOrgService`/`DiscountWrapper`/`TActivityThirdDiscountService`/`PayOrderQueryService`; 5 个门户 VO 落到 `parking-legacy-common/.../vo/app/coupon` + `.../vo/app/billquery` ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:同类里**三种老鉴权档位并存** —— `AppRsesultUtil.checkParam(String)`(回信封)与老 `AppBaseController#checkParam(AppBaseReq)`(抛 `BizException`)的 `{}` 都是 [4014]; 裸 `AppTokenUtil.getUserCustId` 是 [-10000];`deleteCouponPerson` 的 1003 文案校验在鉴权**之前**。 ★ 注意:本片把 `DiscountWrapper` 从 `parking-module-order` 移到 `parking-legacy-common` (**同 FQN `com.zteits.ipay.util`**),order 侧 import 不变、单进程仍只有一个 Bean。 ★ **2026-10-20 第一百八十三切片:APP 端账号信息 `UserController`(7 端点,4 条动库)** 默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`。老类共 25 条 POST,其中 6 条**早已搬过** (`sendverificode`/`checkverificode`/`login`/`alias`/`logout` → §169 `/pay/user-login/*`; `userCarsInfoEdit` → §178 `/pay/user-car/*`)⇒ 本片只搬 `queryUserInfo`/`shareInfo`/ `getUserPhoneByToken`/`updateCustPersonBaseInfo`/`queryCustPersonBaseInfo`/`updateUserChangeOrgId`/ `queryUserCars` 七条到 `/app-api/pay/user-info/*`。依赖全是既有 Bean;老门户的 `AppOrgCommonService` 复制进 `parking-legacy-common/.../pay/portal/util`(同 FQN) ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:`updateUserChangeOrgId` **没有任何签名校验**,`checkAppToken` 失败就 `success(true)` 早退 (回成功信封但一行不写);`queryUserInfo`/`queryCustPersonBaseInfo` 在读接口里 `updateByUserPhone` 写库。 ★ **2026-10-20 第一百八十四切片:APP 端注册/登录/改密一族 `UserController` 最后 10 端点(9 条动库)** 默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`;且工具是**按类**判已迁,`UserController` 早在 §169 就计入 221/1195 ⇒ 本片把老类剩下的 `userRegistration`/`passwordLogin`/`messCodeBeforeCheck`/ `verificationCodeLogin`/`userRecoverPasswordCheck`/`changePassword`/`setPassword`/`clearAccount`/ `oneClickLogin`/`uploadHeadPic` 十条搬到 `/app-api/pay/user-account/*` 后,计数**一字不动** ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:**multipart 端点缺参是 500 不是 400** —— `upload-head-pic`(`@RequestParam picFile + token`) 空 body 被全局兜底成「系统异常」⇒ 进不了 `LEGACY_WRITES` 的 400 断言,单钉在冒烟 §2fu (c)。 ★ 注意:`oneClickLogin` **不验签**,第一道是 `operatorToken` 非空(空 ⇒ [1003]),非空值会调**外部运营商接口** ⇒ 冒烟只发空串;其余 8 条走老私有校验(先必填、再验签),假签名一律 [4014]。 ★ 注意:`FastDFSClientWrapper` 在 **pay 模块**是活的 `@Component`(`com.zteits.pay.util`), order 模块那份整类被注释 ⇒ `uploadHeadPic` 直接注入即可,无需复制。 ★ **2026-10-20 第一百八十五切片:APP 端会员卡办理 / 区域卡规则 / 时域卡使用(3 类 / 9 端点,全只读)** 默认盘点口径同样**不含** `zteits-pay-portal/.../web/app`:`ApplyVipCardController`(2)→ `/app-api/pay/apply-vip-card/*`、`VipAreaCardRuleController`(6)→ `/app-api/pay/vip-area-card-rule/*`、 `VipTimeCardUseController`(1)→ `/app-api/pay/vip-time-card-use/*`。依赖全是既有 Bean ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:**老基类 `com.zteits.pay.portal.web.app.AppBaseController` 本片首次搬进 `parking-legacy-common`(同 FQN)** ⇒ 后续 APP 切片直接 `extends`,别再内联副本;同时搬了 `com.clouds.common.constants.ParkConstant` (parking-cloud 原先只有 `com.zteits.park.constant.ParkConstant`,pay 模块不依赖 park 模块)。 ★ 注意:`{}` 落点分两档 —— 带 `@Valid` 的 3 条 400「请求参数不正确」,`VipAreaCardRule` 的 6 条 **无 `@Valid`** ⇒ 进方法体被 `checkParam` 拦成 500 `[4014]`。 ★★ 注意:`@Valid` 多约束并存时校验报文**不稳定**(`@NotNull carNumber` 与 `@NotBlank` 同时命中, 遍历顺序随 HashMap 变)⇒ 冒烟只锁 400 + 「请求参数不正确」前缀。 ★ **2026-10-20 第一百八十六切片:APP 端充电桩充电(1 类 / 4 端点,3 读 + 1 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/battery/BatteryChargeController`: `queryEquipChargeStatus`→`/app-api/pay/battery-charge/query-equip-charge-status`、 `queryBatteryChargeInfo`→`/query-battery-charge-init-info`、 `queryAccountDetailsForPage`→`/query-order-charge-info-for-page`、`stopCharge`→`/stop-charge`。 依赖全是既有 Bean ⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。这里只记录口径,不改判定。 ★ 注意:APP 端老控制器**还有第二种鉴权形态** —— 这 4 条**都没有 `checkParam`**,只靠裸 `AppTokenUtil.getUserCustId(token)` ⇒ 无 token / 假签名的落点是 500 `[存量错误码 -10000]`, 不是 4014;`query-order-charge-info-for-page` **无 `@Valid`** ⇒ `{}` 也到 [-10000]。 ★ 注意:`stop-charge` 是**真写 + 出站**(`queryStopCharge` 下发停机 + `updateOrderChargeStatusStop`), 冒烟只发空 body;`query-battery-charge-init-info` 会做出站设备认证,探针只用必然查不到的假 `connectorCode`。 ★ 注意:`ChargeStateInfoDTO` 本片**补回老 api 的 3 个字段**(`feeInfoList`/`stationLng`/`stationLat`)—— parking-cloud 早先裁剪过,纯加字段、不改 `BizResult` 与接口签名。 ★ **2026-10-20 第一百八十七切片:APP 端停车场会员卡(1 类 / 12 端点,11 读 + 1 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/ParkVipCardController`(老 `/parkvip`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:本类是「三种鉴权形态并存」样本 —— 2 条 `@Valid` + `validateSigin`、2 条 `checkParam`、 1 条 `AppRsesultUtil.validateSigin(JSONObject)`、其余 7 条**零校验** ⇒ `{}` 落点分 400 / 500 [4014] / 老信封 1003 / 老信封 0 四档,写断言前必须逐条 grep。 ★ 注意:`query-parking-vip-card` 本地恒 500「系统异常」—— bill 的 `TVipCardParkSaleRelSmapper.querySaleVIPCardRuleForPage` 是存量 `GROUP BY` 写法, 本地 MySQL 8 `only_full_group_by` 直接拒 ⇒ 非回归、红线⑥不修、冒烟不断言。 ★ 注意:`VipCardAuthPersonBjxcService` 只有**嵌套包**(`...order.VipCardAuthPersonBjxc.`)那个接口 有实现;用扁平包注入会启动失败。⛔ `change-car-number-for-vip-card` 是真写,冒烟只发空 body。 ★ **2026-10-20 第一百八十八切片:APP 端发票申领(1 类 / 21 端点,11 读 + 10 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/InvoiceHelperController`(老 `/invoice`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。 ★ 注意:**`@Valid` 能否把 `{}` 挡成 400 取决于 VO 的父类** —— `ResendInvoiceMailReq extends AppBaseReq`(app 公共五件套 `@NotBlank`)⇒ 400; `QueryInvoiceHeaderReq extends AppBaseRequest`(无约束)⇒ `{}` 直接成功; 17 条 `@RequestBody String` 与 1 条**无 `@Valid`** 的 `query-rnt-pay-order-for-invoice` ⇒ `{}` 落到方法体里的 `checkParam`,500 `[4014]`。 ★ 注意:`query-invoic-header` / `query-invoic-order-date` 老代码把 `checkParam` **注释掉** ⇒ 无鉴权公开可读(照搬)。`query-invoic-order-date` 读 `SysCodeValueCacheUtil(APP_INVOICE_QUERY_DATE)`, 本机未配 ⇒ 空列表,别据此判"端点死了"。 ⛔ `open-ticket-for-cust-invoice-his-info`(真插开票历史 + 航信开票队列出站)与 `resend-invoice-mail`(真实发邮件)是真写,冒烟只发空 body;同片另有 8 条纯 CRUD 写端点。 ★ **2026-10-20 第一百八十九切片:APP 端账户充值(1 类 / 19 端点,15 读 + 4 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/AppAcctRechargeController`(老 `/appAcctRecharge`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 这里只记录口径,不改判定。(`zteits-minipay-portal` 里同 FQN 的另一份不搬。) ★ 注意:同一类里**三种鉴权形态混排** —— 7 条 `@Valid` + VO 继承 `AppBaseReq` ⇒ `{}` 400; 9 条无 `@Valid` 的 VO + 3 条 `@RequestBody String` ⇒ `{}` 落 `checkParam`,500 `[4014]`; `get-account-recharge-amount` **无入参、无鉴权**(公开可读码表,本机恒 `[{"code":"0","name":"0"}]`)。 ★ 注意:`get-park-card-by-park-card` / `account-recharge-by-park-card` 是**死端点**,恒抛 `[存量错误码 3017]`「该版本已经失效」(照搬,别当回归)。 ⛔ 真写 4 条:`get-accout-balance`(`checkParam` 后 `updateByUserPhone` 刷客户操作时间)、 `open-account`、`acct-deposit-refund`(建退款单)、`account-recharge-by-recharge-card` (`aliWxPayService` 建充值支付单);冒烟只发空 body。 ★ 负向探针必须**带 token**:不带 token 时安全层先回 401「账号未登录」,看不到 404。 ★ **2026-10-20 第一百九十切片:APP 端会员卡退款(1 类 / 2 端点,1 读 + 1 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/ParkVipCardRefundController`(老 `/parkviprefund`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 依赖只有既有 Bean `RefundOrderOperService`;新增 2 个门户 VO(`vo/app/vipcard/VipCardRefundReq` + `QueryVipCardRefundReq`)。 ★ 注意:两条都 `@Valid` + VO 继承 `AppBaseReq` ⇒ `{}` 统一 400;假签名统一 500 `[4014]`。 ⛔ `/refund` 是真写(建退款单,且先查重),冒烟只发空 body。 ★ **2026-10-20 第一百九十一切片:APP 端消息推送开关(1 类 / 1 端点,1 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/MsgPushController`(老 `/msgPush`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 依赖只有既有 Bean `TdCCustPersonService.openMsgPush`,零新增 VO/DTO。 ★★ 注意:该类**不继承 `AppBaseController`**、没有 `checkParam`/`validateSigin`;token 从 body JSON 取, 直接 `AppTokenUtil.getUserInfo` ⇒ 无效 token 是 **`-10000`「登录失效」**,不是 `4014`。 ⛔ `/open-or-close` 改客户推送状态,冒烟只发空 body。 ★ **2026-10-20 第一百九十二切片:APP 端极光消息推送(1 类 / 6 端点,4 读 + 2 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/JPushController`(老 `/push`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 依赖全是既有 Bean(`JPushService`/`ParkingLotQueryService`/`TdCMsgTemplateService`/`TdCCustPersonService`); 新增 3 个门户 VO(`vo/app/messagepush/*`)。 ★★ 注意:**同一类三种鉴权落点** —— 4 条 `String`+`checkParam`/`validateSigin` ⇒ `{}`/假签名 500 `[4014]`; `new-message-push-homepage` 假 token 抛 `[2003]`;`un-reads-total` 假 token **回 code "0" 全 0 成功** (存量契约,别当缺陷)。逐条看方法体,别整类套模板。 ⛔ 真写 2 条:`delete-pushed-message-details`(按 msgType 删)、`new-message-push-homepage`(读完置已读); 冒烟只发空 body。 ★ **2026-10-20 第一百九十三切片:APP 端与华软 ETC 结算对接(1 类 / 2 端点,2 写)+ 补回 QR 进出场链** 口径同样**不含** `zteits-pay-portal/.../web/app/etcinterface/ElectronicTollCollectionController` (老 `/etc/pay/`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、5 死代码**全部不变**。 本片补整条链:13 个 VO(etcinterface/qrcodeinout/billquery/ETCBaseReq)、 `portal/util/{ParkLotCommonService,DateUtils}`、`portal/config/TimedCacheFactory`、 `portal/web/app/qrcodeinoutparking/`(接口/路由/适配器/基类 + 4 个 impl + 科拓费用查询 InnerService)。 ★★ 注意:**返回值不是 `AppBaseRes` 信封** —— 签名失败回 `{"code":"5","message":"验证签名失败"}` (`QueryFeeVO`/`PayNoticeVO`),断言按 code 字符串比,别套 500/[4014] 模板。 ⛔ 两条都真写:`/query-fee` 改支付单 + 写 Redis 防重;`/notice` 走 `etcPayService.payNotify` 真结算。 冒烟只发空 body(两条也都进 `LEGACY_WRITES`)。 ★ `KeyTopQRcodeInOutParkController` 仍留(下一片搬;当时误以为它「真正使用 `BillQueryCommonService`」)。 ★ **2026-10-20 第一百九十四切片:科拓道闸扫码进出场(1 类 / 2 端点,2 写)** 口径同样**不含** `zteits-pay-portal/.../web/app/qrcodeinoutparking/KeyTopQRcodeInOutParkController` (老 `/keyTopQrCodeInOutPark`)⇒ 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。零新增 VO(`UseCouponQueryFeeReq/Res` 侦察阶段已补)。 ★★ 纠正上一片的结论:`BillQueryCommonService` 在本类**是死字段**(注入后两个方法体从未引用), 删掉即可,**不必**搬 `BillQueryService`/`BillQueryRouteService` 整条链。 ★★ 注意:老类 `// validateSigin(request);` **是注释掉的**,全类无签名校验 ⇒ `{}`/假字段都是 **400「请求参数不正确」**,不是 500/[4014]。别按签名失败模板写断言。 ⛔ 两条都真写:`QrCodeInPark`/`QrCodeOutPark`(补录进场、建/改待支付单)、 `updateWaitPayOrderFeeByPayOrderId`(改金额);冒烟只发空 body / `{}` / 假字段。 ★ **2026-10-21 第一百九十五切片:共享车位收益提现(1 类 / 2 端点,1 读 + 1 写)** 搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingTransferController` (老 `/sharedparking/transfer`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**, 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。新增 3 个门户 VO: `vo/app/sharedparking/{OperTransferReq,QueryTransferReq,QueryTransferLogRes}`。 ★★ 同一控制器里的**两个端点假签名码不同**:`oper-transfer` 的 `OperTransferReq` 自带 5 个 `@NotNull/@NotBlank`,`@Valid` **先于** `checkParam` 失败 ⇒ **400**; `query-transfer` 的 `QueryTransferReq` 无自有注解 ⇒ **500 [存量错误码 4014]**。别套模板。 ⛔⛔ `/oper-transfer` 是全仓最危险的写端点:`aliWxPayService.transfers` **真实发起 支付宝/微信提现**,微信分支还先调 `sns/oauth2/access_token` 换 openid。 冒烟**只发无 body**(进 `LEGACY_WRITES`),真机联调必须沙箱/测试账号。 ★ 存量缺陷照搬:`query-transfer` 按月**循环查库(N+1)**;`queryTransferLogResList` 在循环外创建 ⇒ 第 n 个月响应重复带上前 n-1 个月记录;`totalFee` 无 `@NotNull`,`getTotalFee()<=0` 会 NPE。 ★ **2026-10-21 第一百九十六切片:共享车位·我的共享车位(1 类 / 6 端点,全部读)** 搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingHomeController` (老 `sharedparking/sharedBerthHome`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**, 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。新增 10 个门户 VO(`vo/app/sharedparking/` 9 个 + `vo/app/billquery/QuerySharedParkFeeRes`)。 ★★ 与 §194 相反:`BillQueryCommonService` 在这里**不是死依赖**(3 处 `queryBillingForShared` 调用)。 新栈在 `com.zteits.pay.portal.util` 补回**只含两个重载**的版本(不需要 `BillQueryService`/ `BillQueryRouteService` 整条链)。 ★★ `{}` 在同一个类里有**三种落点**:`query-shared-page` 无 `checkParam` ⇒ `code="0"`(**字符串**); 两个走 `@Valid` 的 ⇒ 400「请求参数不正确」;其余三个 ⇒ 500 `[4014]`。 ★★ 400 文案**不稳定**(`不能为空` 与 `不能为null` 随机,取决于 Hibernate Validator 遍历顺序) ⇒ 断言只钉 `请求参数不正确` 前缀。 ★ 假签名三种:`query-shared-page` ⇒ 500 `[-10000]`(`AppTokenUtil` 认不出 token); `query-shared-by-berth-no` ⇒ 400(VO 自带 `@NotNull`);其余四个 ⇒ 500 `[4014]`。 ★ 6 条**全是读** ⇒ 零 `LEGACY_WRITES` 增量、零 `@OperateLog`。存量缺陷:分页里 N+1 查订单、 `isAllowDownLock` 查不到订单直接 NPE、`getPreActFeeForShareBerth` 的允许取消时长可能为负。 ★ **2026-10-21 第一百九十七切片:共享车位·预订(1 类 / 6 端点,2 读 + 4 写)** 搬的是 `zteits-pay-portal/.../web/app/sharedparking/SharedParkingReservationController` (老 `/sharedparking/reservation`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**, 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。新增 10 个门户 VO(全部继承 `QuerySharedByPlNoAndBerthNoReq`)。 ★★ 本类两个口径最干净的对照:6 个 VO 全**继承带 `@NotNull` 的基类** ⇒ `{}` 与假签名 **一律 400「请求参数不正确」**,`@Valid` 先于 `validateSigin`/`checkParam`,**零 500**。 ⇒ 判 `{}` 落点的唯一判据就是"VO 有没有自带注解",别按控制器套模板。 ★ 4 条真写(`reservation-submit`/`cancel-pre-order`/`in-parking`/`out-parking`)进 `LEGACY_WRITES`, 只发无 body;老类零 `@SysOpLog` ⇒ 新类零 `@OperateLog`。 ★ 存量缺陷:`queryPreInTimes` 第一个时间点 `psPreFee` 恒 0(后 3 个才取担保费); `inParking` 在"现在已过预约入场时间"时把进场时间回退成预约时间;`outParking` 在 `psInTime == null` 时 NPE;`reservationSubmit` 的 `Long.valueOf(preTotalFee)` 无 null 守卫。 ★★ **2026-10-21 第一百九十八切片:共享车位·我的共享车位(Rnt)(1 类 / 6 端点,4 读 + 2 写)** 搬的是 `zteits-pay-portal/.../web/app/sharedparking/MySharedBerthController` (老 `sharedparking/mySharedBerth`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**, 就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、机械缺口 **2/11**、 5 死代码**全部不变**。零新 VO(全走 `JSONObject.parseObject`)。 ★★ 本类**不继承 `AppBaseController`**,手写 `AppRsesultUtil.checkParam` ⇒ 响应 `{code, message}`(不是 `msg`)且 `code` 是**字符串**;`save-parking-berth-share` 把 5 个业务字段校验放在 `checkParam` 之前 ⇒ `{}` 直接回 `code="1003"` 「共享车位详细地址为空!」,其余 5 条才是 500 `[4014]`。 ★ 2 条真写(`save-parking-berth-share`/`update-park-share-berth-by-berth-no-and-cust-id`) 进 `LEGACY_WRITES`,只发无 body;老类零 `@SysOpLog` ⇒ 新类零 `@OperateLog`。 ★ 存量缺陷:`saveParkingBerthShare` 客户号为 null 时 NPE;两个费用接口逐条调计费(N+1); `updateParkShareBerth...` 不校验车位归属;6 条全 `catch(Exception)` 吞成业务码。 ★★ **2026-10-21 第一百九十九切片:自助停车(1 类 / 2 端点,1 读 + 1 写)** 搬的是 `zteits-pay-portal/.../web/app/AppSelfParkingController`(老 `/appSelfParking`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。新增 2 个门户 VO;补回 `SelfParkingCommonService`。 ★★ `@Valid` 才是 `{}` 落点的开关:本类继承 `AppBaseController`、`BerthParklotReq` 也有 `@NotNull berthNo`,但方法参数没加 `@Valid` ⇒ `{}` 与假签名**一律 500 `[4014]`** (落 `validateSigin`),与 §197 的 400 正好对照。 ★ `in-park-self` 是 `private` 方法但仍是真端点(Spring 反射调用);它是本片唯一真写 (白名单 → Redis 防重 → `insertParkOrder` 建单 + 推 PDA 消息)⇒ 只进 `LEGACY_WRITES`、只发无 body。 ★ 存量缺陷:`getIsSelfParking() == 1` 自动拆箱 NPE;`nightChargeDesc` 误取 `dayChargeDesc`; `regionAreaDTO`/`parkLotByPlNo` 无 null 守卫;`Integer.valueOf(carNumberColor)` 无守卫; `listBizResult.getData()` 前不判 `isSuccess`。 ★★ **2026-10-26 第二百八十一切片:`IncomeBerthReportController` +1(停车位日报 URL 型导出补齐 ⇒ 该类 3/3 全迁)** 同样补在**已迁类**里 ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;重测全部不变: 就绪 **223/1180**、已迁 **223/1180**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、 机械缺口 **4/38**(已判 **1/2** ⇒ 机械口径下可迁 **3/36**)、死代码 **5**、导出 **97**。 ★ 这是本脚本的**口径边界**:它按**类**计数,"同一个类里再补端点"永远显示 +0; 端点级差集靠 `api_smoke.py`(§2i27 等分块断言)与 `p2_audit_*` 兜住,别拿这里说"没变化 = 没干活"。 ★ 顺带销账一条**旧判定的错因**:`billing/UploadController.uploadPic` 早在第二百一十六切片就迁完了 (`POST /admin-api/pay/upload/upload-pic`,`pay:upload:create`);旧笔记里说的 「`FileStorageServiceImpl.uploadFile` 整段注释、`return null`」指的是 `com.zteits.pay.biz` 的 **Dubbo 服务**, 与本端点无关。⇒ 当前"上传类"队列只剩 4 条(`insertParkingBerthShare` / `upload-employee-head-pic` / `recharge-card-generate-pic` / `uploadParkLotPic`),本机都只验负向、正向走离线桩。 ★★ **2026-10-26 第二百八十切片:两条 URL 型导出补迁(`ParkInOutRecordController` +1 / `IncomeChargerCheckTaskController` +1)** 两条同样补在**已迁类**里 ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;重测: 就绪 **223/1180**、已迁 **223/1180**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、 机械缺口 **4/38**(已判 **1/2** ⇒ 机械口径下可迁 **3/36** = `ParkingRecordController` 15 + `InvoiceOperController` 12 + `ParkingAssistantController` 9,后一个 ⛔ 红线不动)、死代码 **5**。 ⚠️ **225/1199 → 223/1180 不是回归**:用户 2026-10-26 的三个提交(10:56 / 11:27 / 14:14)在老仓 `zteits-api` 新增 `order.parkorder.oper.ParkRecordCashService`、`pay.invoice.InvoiceRedFlushService`、 `pay.invoice.InvoiceFaceAuthService`,而 `parking-module-api` **都没有** ⇒ `ParkingRecordController`(15) 与 `InvoiceOperController`(12) 从「就绪」掉进「缺口」。缺口 4 类 = 15 + 12 + `ParkingAssistantController` 9(⛔) + `OrderExamineController` 2(已判不可迁)。 ★ 这两条**返回 URL 字符串而不是响应流** ⇒ 按既定口径保持 `POST`,不进「导出 97」的统计; 改 `GET /export-excel` 是契约变更(红线⑥)。 ★ 本脚本不受影响的真原因:它只比「老类依赖的 service 有没有实现」,URL 型导出的差别在 Controller 形状。 ★★ **2026-10-26 第二百七十九切片:`ArrearageStaticController` +3(欠费统计的 ajax 导出补齐)** 老类是「4 读 + 1 响应流导出已迁、3 条模板型 ajax 导出挂起」,本片补上后**该老类 8 端点全迁**。 3 条同样**补在已迁类里** ⇒ 本脚本看见的仍是 **+0 类 / +0 端点**;就绪口径 **225/1199、就绪且未迁 0/0、⚠A 0/0、⚠B 0/0、机械缺口 2/11(已判 1/2)、死代码 5** 全部不变。 ★ 本片顺带把「模板类导出」的挂起理由也销账了一条:卡点一直是**模板文件没随迁**, 不是存储、也不是依赖 ⇒ 打法 = 模板复制到模块 `resources` + `ClassPathResource` 同路径读 + 需要时加 `easyexcel`。 ★ 下一批只剩 `ParkInOutRecordController` −1、`IncomeChargerCheckTaskController` −1(两条模板导出), 见 AGENTS.md EOF §279 ⑤。 ★★ **2026-10-26 第二百七十八切片:`AppVersionCheckController` +2 / `InspectionLogController` +1(multipart 上传补迁)** **本片搬的 3 个端点全补在「已迁类」里** ⇒ 按**类**计数的本脚本看见的是 **+0 类 / +0 端点** (它只数老类与端点映射,不追"某个已迁类又补了几个方法")。 实测(⚠️ 本片在 **`beijing_master`** 上跑;用户 2026-10-26 从 `fangshan_master` 切过来 ⇒ **口径整体变了、不是回归**):就绪 **225/1199**、已迁 **225/1199**、**就绪且未迁 0/0**、 ⚠A **0/0**、⚠B **0/0**、机械缺口 **2/11**(已判 **1/2** ⇒ 可迁 **0/9**)、死代码 **5**。 ⚠️ 别再拿 §277 记的 **235/1267 / 死代码 6** 跟现在比:`beijing_master` 的老仓端点总数少 68 条、 死代码少 1 个(`fangshan_master` 口径)。换分支后**先重跑**再报数。 ★ 本片的价值不在本脚本:它是把「等文件存储抽象」这条挂起理由**彻底销账**—— 抽象 `OssClientBaseWrapper` 一直都在,真卡点是①强制 FastDFS 无 `fdfs.*` 配置②本机 `ali` 是真 bucket。 ★ 下一批:就绪池仍 **0/0**;剩 `ArrearageStaticController` −3(本片已备好 `uploadFileWithName` 的 wrapper) 与几条模板类导出(见 AGENTS.md EOF §278 ⑤)。 ★★ **2026-10-23 第二百七十七切片:`TdCCustBusinessParkController` 老类 3 端点全迁(商户-车场关联)★ 就绪池清零** 老路由 `/tdCCustBusinessParkManage`(`business/`),新路由 `/admin-api/business/td-c-cust-business-park/*`(1 读 + 2 写,无导出)。 实测:就绪 **235/1267**、已迁 **235/1267**、**就绪且未迁 0/0**、⚠A **0/0**、⚠B **0/0**、 缺口的 2 类 / 7 端点**已判不可迁** ⇒ 真正可迁 **0/0**、死代码 **6**。 ★ 3 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。 ★ 失败形状与 §276 相反:缺表 `td_c_cust_business_park` 结尾是 `park`(无下划线)⇒ 不命中 `park_` ⇒ **500** (§276 的 `…park_rel` 命中 ⇒ 501)。501/500 只取决于表名里的子串。 ★ 分页回填 `plName`:`isSuccess()` → `ParkingLotCacheUtil` 查缓存 → 按 `plNo` 双循环,照搬。 ⛔ `save` 服务层**先物理删除**该 businessId 下全部关联再逐车场插入(`{}` 会真删真插)。 ★ 零写基线仍 **+248/轮**(实测 238550 → 238798,变化表 1)。 ★ 下一批:**就绪池无待办**;只剩已判不可迁的 2 类 / 7 端点与端点级差集(见 AGENTS.md EOF §277 ⑤)。 ★★ **2026-10-23 第二百七十六切片:`TdCBusinessCouponRuleParkRelController` 老类 4 端点全迁(商户卡券规则-车场关联)** 老路由 `/tdCBusinessCouponRuleParkRelManage`(`business/`),新路由 `/admin-api/business/td-c-business-coupon-rule-park-rel/*`(1 读 + 3 写,无导出)。 实测:就绪 **235/1267**(不变)、已迁 **234/1264**(+1 类 / +4 端点)、就绪未迁 **1/3**、 缺口 **2/7 已判不可迁**、真正可迁 **0/0**、死代码 **6**。 ★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。 ★★ 501 提示被**子串匹配**成「停车场模块」:缺的表名 `td_c_business_coupon_rule_park_rel` 含 `park_`, 纯属表名撞词(§275 的 `td_c_business_acct` 不含 ⇒ 同一批里它是 500)。 ★★ 同一 `update` 端点两种形状:只带 `id` ⇒ `` 空 ⇒ SQL 语法错 **500**;带宽真实列 ⇒ **501**。 ⛔ `save` 无校验且 `parks` 非空时**逐车场各插一行**(`{}` 会真插);`update`/`bath-delete` 的 1003 闸门照搬。 ★ 零写基线仍 **+248/轮**(实测 236814 → 237062,变化表 1)。 ★ 下一批只剩 1 个类 / 3 端点(`TdCCustBusinessParkController`,清单见 AGENTS.md EOF §276 ⑤)。 ★★ **2026-10-23 第二百七十五切片:`TdCBusinessAcctController` 老类 4 端点全迁(商户账户)** 老路由 `/tdCBusinessAcctManage`(`business/`),新路由 `/admin-api/business/td-c-business-acct/*`(1 读 + 3 写,无导出)。 实测:就绪 **235/1267**(不变)、已迁 **233/1260**(+1 类 / +4 端点)、就绪未迁 **2/7**、 缺口 **2/7 已判不可迁**、真正可迁 **0/0**、死代码 **6**。 ★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。 ★★ **与 §273/§274 不同**:分页服务是**真实现**(`PageHelper` + `PageBeanUtil`),本机缺表 ⇒ **500**,不是恒空页。 ★ 本 DAO **没有**表存在性守卫 ⇒ `bath-delete {死主键}` 也是 **500**(§274 同形状端点回 501)——各自照搬。 ⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。 ★ 零写基线仍 **+248/轮**(实测 235326 → 235574,变化表 1)。 ★ 下一批剩 2 个类 / 7 端点(清单见 AGENTS.md EOF §275 ⑤)。 ★★ **2026-10-23 第二百七十四切片:`OrderBusinessCouponController` 老类 4 端点全迁(商户购券主单)+ 就绪工具修正** 老路由 `/orderBusinessCouponManage`(`business/`),新路由 `/admin-api/business/order-business-coupon/*`(1 读 + 3 写,无导出)。 ★★★ **本片先修工具再搬类**:原先 `impls` 扫描只做 `"implements" in src` 的文本预过滤, `CustPersonCreditServiceImpl.java`(整文件注释)因此被判「有实现」,让 `CustPersonCreditController` 长期挂在「就绪且未迁」;改为 `strip_comments` 后再套 `IMPL_PAT`。 ⇒ 该 Controller 落回缺口并登记 `GAP_ADJUDICATED` 判 **⛔ 不可迁**;就绪基数 **236/1272 → 235/1267**。 实测(修正后口径):就绪 **235/1267**、已迁 **232/1256**(+1 类 / +4 端点)、就绪未迁 **3/11**、 缺口 **2/7 已判不可迁**(OrderExamine 2 + CustPersonCredit 5)、真正可迁 **0/0**、死代码 **6**。 ★ 4 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。 ★★ `query-order-business-coupon-for-page` 服务实现体**整段被注释** ⇒ **恒返回空页、一次库都不查**。 ★ 本机缺表:`update` 死主键 **500**(MBG 直接抛)、`bath-delete` 死主键 **501**(DAO 守卫)——**不是回归**。 ⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。 ★ 零写基线仍 **+248/轮**(实测 234086 → 234334,变化表 1)。 ★ 下一批剩 3 个类 / 11 端点(清单见 AGENTS.md EOF §274 ⑤)。 ★★ **2026-10-23 第二百七十三切片:`OrderBusinessCouponSubController` 老类 5 端点全迁(商户购券子单)** 老路由 `/orderBusinessCouponSubManage`(`business/`),新路由 `/admin-api/business/order-business-coupon-sub/*`(2 读 + 3 写,无导出)。 实测:就绪 **236/1272**(不变)、已迁 **231/1252**(+1 类 / +5 端点)、就绪未迁 **5/20**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 5 条全抛 `AUTH_TOKEN_NOT_EXISTS`(照搬)且全 `@PreAuthorize`;老类**无** `@SysOpLog` ⇒ 新类**无** `@OperateLog`。 ★★ `query-order-business-coupon-sub-for-page` 服务实现体**整段被注释** ⇒ **恒返回空页、一次库都不查**。 ★ `query-order-business-coupon-list-for-page` 本机缺表 ⇒ **501「表结构未导入」**(DAO 有前置守卫,不是 500)。 ★ 零写基线仍 **+248/轮**(本片零 `@OperateLog`,实测 232598 → 232846,变化表 1)。 ⛔ `save` 服务层无校验(`{}` 真插一行);`update`/`bath-delete` 的 1003 闸门、`bath` 拼写照搬。 ★ 下一批剩 5 个类 / 20 端点(清单见 AGENTS.md EOF §273 ⑤)。 ★★ **2026-10-23 第二百七十二切片:`InspectionLogController` 老类 5 端点迁 4(巡检日志)** 老路由 `/inspectionlog`(`operationsmanage/inspectionlog`),新路由 `/admin-api/order/inspection-log/*`(1 读 + 3 写;第 5 个 multipart+FastDFS 上传**挂起不迁**)。 实测:就绪 **236/1272**(不变)、已迁 **230/1247**(+1 类 / +5 端点)、就绪未迁 **6/25**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类**没有一条**抛 `AUTH_TOKEN_NOT_EXISTS`(靠门户全局拦截器)⇒ 新栈 4 条全 `@PreAuthorize`。 ★ `@SysOpLog` 三条 → `@OperateLog(module="巡检日志")`;`query` 老代码没有 ⇒ 新代码也没有。 ★★ **零写基线从 +244 变 +248**:`OperateLogAspect` 在 `finally` 里落库、**业务 500 也记**; §2i18 里 `update`/`delete` 各两次穿透控制器 ⇒ 多 4 行(变化表仍是 1)。 ★ 坑 24 补充:全局 `spring.jackson.date-format: yyyy-MM-dd HH:mm:ss` ⇒ **请求体 `Date` 只认带时分秒**, `yyyy-MM-dd` 被 Jackson 拒 → 400「请求参数类型错误」。 ⚠️ 本机缺表 `inspection_log` ⇒ 读/改/删恒 500;⛔ `save` 服务层无校验(`{}` 真插一行)。 ★ 下一批剩 6 个类 / 25 端点(清单见 AGENTS.md EOF §272 ⑤)。 ★★ **2026-10-23 第二百七十一切片:`business/TdCBusinessCouponRuleController` 整类 6 端点首迁(卡券规则)** 老路由 `/tdCBusinessCouponRuleManage`,新路由 `/admin-api/business/business-coupon-rule/*` (2 读 + 1 导出 + 3 写)。 实测:就绪 **236/1272**(不变)、已迁 **229/1242**(+1 类 / +6 端点)、就绪未迁 **7/30**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类**零** `@SysOpLog` ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮);导出数 **96 → 97**。 ★ 新增 `com.zteits.business.util.SnowFlakeUtil`(business 模块第 5 份同源副本,生成 `cardRuleNo`); ⚠️ **别**换 `com.clouds.common.utils.SnowflakeIdWorker`(twepoch/datacenter 位数不同)。 ⚠️ 老导出是 `@RequestMapping`(**不限方法**)+ `@NoAuth` **且不判登录态**,新栈收紧为 `:export` 且**只收 GET**;`orgId`/`sysCode` 是**必填 query 参数**(缺 → 400)。 ⚠️ **导出不分页** ⇒ `BaseInfo` 默认 0/10 ⇒ 最多 10 行;**DAO 根本不按 orgId 过滤**(全组织同一份)。 ⚠️ 本机缺表 `td_c_business_coupon_rule` ⇒ `page`/`list`/导出/死主键写全为 500「系统异常」。 ★ 三个写端点的闸门**都在落库前**(save 卡名称/类型/优惠值、update 卡 id+三项、delete 卡 id); `delete` 是逻辑删除(`data_state=0`)且 `opName` 恒 null。 ★ 下一批剩 7 个类 / 30 端点(清单见 AGENTS.md EOF §271 ⑤)。 ★★ **2026-10-23 第二百七十切片:`business/TdCBusinessCouponController` 整类 6 端点首迁(商户卡券)** 老路由 `/tdCBusinessCouponManage`,新路由 `/admin-api/business/business-coupon/*` (2 读 + 1 导出 + 3 写)。 实测:就绪 **236/1272**(不变)、已迁 **228/1236**(+1 类 / +6 端点)、就绪未迁 **8/36**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类**零** `@SysOpLog` ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮);导出数 **95 → 96**。 ⚠️ 老导出是 `@RequestMapping`(**不限方法**)+ `@NoAuth`,新栈收紧为 `:export` 且**只收 GET** (POST → 405);⚠️ **导出不带 `orgId`**(跨组织全量,存量缺陷照搬)。 ⚠️ 本机缺表 `td_c_business_coupon` ⇒ `page`/`sum-sta`/导出/`update {死主键}` 全为 500「系统异常」。 ⚠️ 分页回填 `BusCardTypeEnum.getValue(int).desc()`:`cardType` null 拆箱 NPE、未知类型也 NPE。 ⛔ `save` 服务层无任何校验(`{}` 真插一行)⇒ 只发无 body。 ★ 下一批剩 8 个类 / 36 端点(清单见 AGENTS.md EOF §270 ⑤)。 ★★ **2026-10-23 第二百六十九切片:`share/CouponController` 整类 7 端点首迁(对外卡券接口)** 老路由 `share/coupon`(最江阴下发优惠券),新路由 `/admin-api/pay/share-coupon/*` (2 读 + 5 写,**无导出**)。 实测:就绪 **236/1272**(不变)、已迁 **227/1230**(+1 类 / +7 端点)、就绪未迁 **9/42**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类 `@SysOpLog` **全是注释掉的** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。 ★ 两条读端点查不到用户时走 `setErrorInfo(COMMON_ERROR)` ⇒ **500 `[存量错误码 1002] 账户不存在`**(`selectByUserPhone` 返回非 null 空 DTO,不是 NPE)。 ⚠️ 五条写端点的 `{}` 全是**只失败不落库**:`save`→flg NPE、`batch-insert`→`@Valid` 400、 `delete-coupon-rule`→MBG「Value for cardNo cannot be null」、 `user-registration`→1003「手机号不能为空」、`delete-coupon-info`→`Long.valueOf(null)` NFE。 ★ 下一批剩 9 个类 / 42 端点(清单见 AGENTS.md EOF §269 ⑤)。 ★★ **2026-10-23 第二百六十八切片:`TdCBusinessAcctPayoutController` 整类 7 端点首迁** 老路由 `/tdCBusinessAcctPayoutManage`(`business` 模块第三个),新路由 `/admin-api/business/business-acct-payout/*`(3 读 + 1 导出 + 3 写)。 实测:就绪 **236/1272**(不变)、已迁 **226/1223**(+1 类 / +7 端点)、就绪未迁 **10/49**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。 ★ 导出老 `@NoAuth` + 手工 `x-auth-token` ⇒ 按 §3.3 第 1 类收紧为 `business:business-acct-payout:export`,`GET /export-excel`,不再收 `token` 参数。 ⚠️ **坑 24**:`@RequestParam Date` 只认 JS `Date.toString()`,`yyyy-MM-dd` → 400。 ⚠️ **导出不带 `orgId` 过滤**(存量缺陷,跨组织全量,照搬)。 ⚠️ `td-c-business-acct-payout-detailfor-card` 的服务实现是**空壳 `return null`** ⇒ 控制器 `getErrCode()` NPE ⇒ **任何环境都 500**(老栈同样,照搬别补实现)。 ⚠️ **本机缺表 `td_c_business_acct_payout`** ⇒ `page`/`static`/导出/`update {死主键}`/ `bath-delete {ids:[死主键]}` 恒 500「系统异常」,冒烟断言按双收写 —— **这是本机 schema 缺口,不是迁移缺陷**。 ⛔ `save` 服务层无任何校验(`{}` 会真插一行)⇒ 冒烟只发无 body; ⚠️ `bathDelete` 写的 `opName` 服务读的是 `BaseRequest.sOpName`(**永为 null**,存量缺陷照搬)。 ★ 下一批剩 10 个类 / 49 端点(清单见 AGENTS.md EOF §268 ⑤)。 ★★ **2026-10-23 第二百六十七切片:`TdCBusinessAcctSourceController` 整类 7 端点首迁** 老路由 `/tdCBusinessAcctSourceManage`(`business` 模块第二个),新路由 `/admin-api/business/business-acct-source/*`(2 读 + 1 导出 + 4 写)。 实测:就绪 **236/1272**(不变)、已迁 **225/1216**(+1 类 / +7 端点)、就绪未迁 **11/56**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`(零写基线不变,+244/轮)。 ★ 导出老 `@NoAuth` + 手工 `x-auth-token` ⇒ 按 §3.3 第 1 类收紧为 `business:business-acct-source:export`, `GET /export-excel`,不再收 `token` 参数。⚠️ **坑 24**:`@RequestParam Date` 只认 JS `Date.toString()`,`yyyy-MM-dd` → 400。⚠️ **导出不带 `orgId` 过滤**(存量缺陷,跨组织全量,照搬)。 ⚠️ **本机缺表 `td_c_business_acct_source`** ⇒ `page`/`static`/两个导出/`update {死主键}`/ `bath-delete {ids:[死主键]}`/`examine` 恒 500「系统异常」,冒烟断言按双收写 —— **这是本机 schema 缺口,不是迁移缺陷**。 ⛔ `save` 服务层无任何校验(`{}` 会真插一行)⇒ 冒烟只发无 body; ⚠️ `td-c-business-acct-source-examine` 审批通过会**真加余额 + 建支付单**。 ★ 下一批剩 11 个类 / 56 端点(清单见 AGENTS.md EOF §267 ⑤)。 ★★ **2026-10-23 第二百六十六切片:`TdCBusinessUserController` 整类 7 端点首迁** 老路由 `/tdCBusinessUserManage`(`business` 模块第一个),新路由 `/admin-api/business/business-user/*`。 实测:就绪 **236/1272**(不变)、已迁 **224/1209**(+1 类 / +7 端点)、就绪未迁 **12/63**、 机械缺口 **1/2**(已判 1/2、可迁 **0/0**)、死代码 **6**。★ 本机仍为 **`fangshan_master`** 口径。 ★ 坑 22 安全(只写登录态 `orgId`/`sOpId`/操作人)。⚠️ **本机缺表 `td_c_business_user`** ⇒ `page`/`get-business-user-name-list`/`batch-delete {ids:[死主键]}` 恒 500「系统异常」, 冒烟断言按「500 或 0」双收 —— **这是本机 schema 缺口,不是迁移缺陷**。 ★ 下一批剩 12 个类 / 63 端点(清单见 AGENTS.md EOF §266 ⑤)。 ★★ **2026-10-23 第二百六十五切片(P3 桥回收第十二组):`BlackListRuleController` 补 `parkTree`** §94 只落 6/7(`parkTree` 读登录态 `plNos` 当树根,P3 搁置);P3 ① 落地后补迁 ⇒ **7/7**。 ★ **口径提示**:本片运行时本机已从 `beijing_master` 检出到 **`fangshan_master`** (2026-10-23 09:29;会话开始时是 `beijing_master`),老门户树不同 ⇒ 本脚本实测 **就绪 236/1272**、已迁 **223/1202**、就绪未迁 **13/70**、机械缺口 **1/2** (已判 1/2、可迁 0/0)、死代码 **6**。历史行记的 **225/1199 是 `beijing_master` 口径**, **两者不可直接比**;换回 `beijing_master` 后应重跑再记录。 ★ **本次重跑暴露的 13 个就绪且未迁类(70 端点,全部 ⚠B)** —— 下一批从这里取: `TdCBusinessAcctSourceController`(7)、`TdCBusinessAcctPayoutController`(7)、 `TdCBusinessUserController`(7)、`CouponController`(7)、`TdCBusinessCouponController`(6)、 `TdCBusinessCouponRuleController`(6)、`InspectionLogController`(5)、 `OrderBusinessCouponSubController`(5)、`CustPersonCreditController`(5)、 `OrderBusinessCouponController`(4)、`TdCBusinessAcctController`(4)、 `TdCBusinessCouponRuleParkRelController`(4)、`TdCCustBusinessParkController`(3)。 抽样两个(`TdCBusinessUserController` / `TdCBusinessAcctSourceController`)只读登录态 `orgId` ⇒ 坑 22 安全候选(逐个还应读源码再判)。 ★★ **2026-10-22 第二百六十四切片(P3 桥回收第十一组):`AnomalyStatisticsController` 补齐 2 条** 第一百四十八切片只落 2/4(`querySealedParkingReportForPage` / `querySealedParkingReportForLineChart` 在 `parkIds` 为空时回落 `userInfo.getPlNos()`, 且只留 `plType == 2`);P3 ① 落地后补迁 2 条,交付口径 2/4 → **4/4**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、 机械缺口 **2/11**、5 死代码不变;本工具按**类**判已迁 —— §148 起它就一直算「全迁」。 ★ 本机 `plNos=[]` ⇒ 回落得到空列表,两个存量 SQL 都是 `IN ` 且**没有空集合守卫** ⇒ 拼出 `IN ()` ⇒ Druid wall 判语法错误 ⇒ **500**(老门户同样 500,照搬)。 ★ 折线图**先快照时间串**:存量 `ParkOrderQueryServiceImpl#statisticOrderFeeByDay` 会就地 把入参拼成 `"… 00:00:00 00:00:00"`;老门户跨 JVM 远程调用不受影响,本栈 injvm 传引用 ⇒ 不快照就 `DateTimeParseException` ⇒ 500。`Period.getDays()` 只取日分量(存量缺陷)。 ★ 另记两个**假缺口**(不必再写代码):`GroundLockManageController` −3 已由 `GroundLockAppController` 落、`RealTimeMonitorController` −2 已由 `RealTimeMonitorAppController` 落(§3.3 的 `@NoAuth` 外部端点拆到 `controller/app/`)。 ★★ **2026-10-22 第二百六十三切片(P3 桥回收第十组):`VipCardRuleController` 补齐 3 条** 第九十五切片只落 4/7(`queryVIPCardRuleForPage` / `queryVIPCardRuleForPageNew` / `vipCardSelectParkTree` 都读登录态 `userInfo.getPlNos()`);P3 ① 落地后补迁 3 条, 交付口径 4/7 → **7/7**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、 机械缺口 **2/11**、5 死代码不变;本工具按**类**判已迁 —— §95 起它就一直算「全迁」。 ★ 本机 `plNos=[]` ⇒ 两个分页回落成空 `plNos`,存量 SQL **对空 plNos 不加过滤** ⇒ `{}` 回全量 364 条;`tree` 回空树 `[]`。★ 冒烟只发不存在的 `cardNo`:`{}` 会让 `queryParkName` 逐条回填时把「本地 DB 快照里没有的车场」的空 DTO 写进 Redis。 ★★ **2026-10-22 第二百六十二切片(P3 桥回收第九组):`IncomeCalculationNewController` 补齐 4 条** 第九十八切片只落 5/9(`getSummationIncomeInfoforPlTypeByTime` / `getAllIncomeCalculationDataOfAreaByTime` / `getSummationIncomeByTimeOfArea` / `getSummationIncomeByTimeOfPl` 都是「`req.plNos` 为空就回落 `userInfo.getPlNos()`」); P3 ① 落地后补迁 4 条,交付口径 5/9 → **9/9**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §98 起它就一直算「全迁」。 ★ `plNos=[]` ⇒ 不带 `plNos` 的 4 条全在**服务层**抛 1003(500);缓存内车场 `A150402000` 时「按类型拆三次」的两条问 i=1 空子集 ⇒ 同样 500 + 1003, 另两条回 `code=0` + `[]`;缓存外车场 ⇒ `getParkTypePlNos` 对 null 元素 NPE。 ★★ **2026-10-22 第二百六十一切片(P3 桥回收第八组):`ParkLotManageController` 补齐 2 条** 第九十七切片只落 14/18(`getParkLotNamesByUserId` 读登录 `plNos` 后逐车场查缓存/服务; `queryAreaTreeByOpIdAndOrgId` 最终用 `plNos` 过滤车场节点);P3 ① 落地后补迁 `get-park-lot-names-by-user-id` / `get-areatree-by-op-id`,交付口径 14/18 → **16/18**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §97 起它就一直算「全迁」。 ★ `refreshParkLotCache`(把 `plNos` 写回 `HttpSession`,JWT 栈无对应语义)与 `uploadParkLotPic`(FastDFS/OSS 挂起)仍不迁,故是 16/18 而不是 18/18。 ★ `plNos=[]` ⇒ 第一条回空数组(老门户「登录人无车场」同口径);第二条本机**恒 500**: 存量服务查到 92 个块、块→区县 0 条 ⇒ 地市查询空 id 列表 ⇒ `and id in and area_type=?` 被 Druid WallFilter 判语法错(红线⑥照搬,不是本片引入)。 ★★ **2026-10-22 第二百六十切片(P3 桥回收第七组):`SelectItemController` 补齐后 6 条** 第一百四十七切片只落 2/8(另 6 条读登录 `plNos`:5 条经 `GetParkLotNameListByUserId()` 查 `ParkingLotCacheUtil`,1 条直接喂 `queryVipCardParkSaleRelForListByPlNo`); P3 ① 落地后补迁 6 条,交付口径 2/8 → **8/8**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §147 起它就一直算「全迁」。 ★ `plNos=[]` ⇒ 前 5 条回空集(只剩「全部」);第 6 条 DAO 空集不过滤 ⇒ 105 张在售卡 (+ALL = 106,ALL 写 `value=ALL`/`value4=全部`、`label=null`);前 5 条缺 `noAll` 仍 NPE 500。 ★★ **2026-10-22 第二百五十九切片(P3 桥回收第六组):`SpecialCarManageController` 补齐后 2 条** 第八十八切片只落 6/8(`querySpecialCar`/`initSpecialCarTree` 读登录 `plNos`、 后者另固定 `sourceTypes=[17,9]`);P3 ① 落地后补迁 `query-special-car`/`init-special-car-tree`, 交付口径 6/8 → **8/8**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §88 起它就一直算「全迁」。 ★ `plNos=[]` ⇒ DAO 不加车场过滤(老门户「登录人无车场」同口径);两条本机**恒 500**: `query-special-car` 存量 `GROUP BY` 撞 MySQL 8 `only_full_group_by`; `init-special-car-tree` 区县查空 ⇒ `and id in ()` 非法 SQL(存量无空集守卫)。 ★★ **2026-10-22 第二百五十八切片(P3 桥回收第五组):`CloseCarParkController` 补齐前 2 条** 第八十六切片只落 17/19(`QueryParkingList`/`queryParkingParkList` 读登录 `plNos` + 固定 `sourceType=[17,9]`);P3 ① 落地后补迁 `query-parking-list`/`query-parking-park-list`, 交付口径 17/19 → **19/19**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §86 起它就一直算「全迁」。 ★ `plNos=[]` ⇒ DAO 不加 `pl_no` 过滤(本机 `total=2`);不分页那条 DAO 无 null 守卫, `{}` 让 `getPlProvinceId() != 0` 拆箱 **NPE ⇒ 500**(存量照搬,省市区显式 0 才正常)。 ★★ **2026-10-22 第二百五十七切片(P3 桥回收第四组):`TdCCustInvoiceSaleController` 补齐 `/relParkTree`** 第八十四切片只落 11/12(`/relParkTree` 用登录 `plNos` 裁掉已绑定车场);P3 ① 落地后补迁 `POST /rel-park-tree`,交付口径 11/12 → **12/12**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码不变;本工具按**类**判已迁 —— §84 起它就一直算「全迁」。 ★ 桥实测 `orgIds=[10003] userAreas=0 plNos=[]`(**空集合,不是 null**)⇒ `removeAll` 正常执行、 回空树;`{}` 不给 `saleTaxids` 时服务层 `saleTaxids.get(0)` **NPE ⇒ 500**(存量照搬)。 ★★ **2026-10-22 第二百五十六切片(P3 桥回收第三组):`KeyTopServiceController` 补齐 `/parkTree`** 第一百八十五切片只落 7/8(`/parkTree` 读登录 `plNos` 构树);P3 ① 落地后补迁 `POST /park-tree`(**无入参**、无 `@RequestBody`),交付口径 7/8 → **8/8**。 ★ 本类**在扫描根内** ⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、5 死代码不变; 但本工具按**类**判已迁 —— §185 起它就一直算「全迁」。 ★ 本机 admin 经桥换算 `orgIds=[10003]`、但本地 `sys_org_area` 无该组织行 ⇒ `plNos=[]`(空集合,不是 null)⇒ 恒回空树 `[]`(老门户「无车场」同口径)。 ★★ **2026-10-22 第二百五十五切片(P3 桥回收第二组):`BalanceManagementController` 补齐 2 个分页** 第一百五十切片只落 3/5(另 2 个在 `plNos` 空/`ALL` 时回落 `userInfo.getPlNos()`);P3 ① 落地后 补齐 `query-bill-fill-total-for-pay-type-new`/`query-blancacfor-day-detail-page`,交付口径 3/5 → **5/5**。 ★ 本类**在扫描根内** ⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、5 死代码不变; 但本工具按**类**判已迁 —— §150 起它就一直算「全迁」。 ★ 服务层 `BillReconciliationForQueryServiceImpl` 的 DAO 调用**全被注释掉**(存量遗留) ⇒ 两条端点恒 `{total:0,list:[]}`;无 `@Valid` ⇒ `{}` 不 400。 ★★ **2026-10-22 第二百五十四切片(P3 桥回收第一组):`ParkSourceController` 补齐 3 个 GET** 第一百四十九切片只落 3/6(另 3 个读 `userInfo.getPlNos()`);第一百五十九切片的 P3 ① 给 `LoginUserBridge` 补了懒推导 `plNos`/`userAreas` ⇒ 本片补齐 `query-park-source-statistics`/ `query-park-source-employee-total`/`query-park-source-device-total`,该类交付口径 3/6 → **6/6**。 ★ 本类**在扫描根内**(`bcp-portal`)⇒ 就绪 **225/1199**、已迁 **225/1199**、机械缺口 **2/11**、 5 死代码全部不变。★ 但本工具按**类**判已迁 —— §149 起它就一直算「全迁」, 本片只是把当初的交付口径补成名副其实。 ★ `sysCode` 是死参数(只进日志,真正给服务的是 `BasicEnum.SYS_CODE`)但老签名 `@RequestParam` 必填 ⇒ 缺它 **400「请求参数缺失:sysCode」**(照搬)。 ★ 本机 admin 经桥换算 `orgIds=[10003]`、但本地 `sys_org_area` 无该组织行 ⇒ `plNos=[]`(空集合,不是 null)⇒ 三条都走空值早退(统计全 null、两个 total 回 0), 与老门户「登录人无车场」同口径。 ★★ **2026-10-22 第二百五十三切片(批 7 第二十七切片):PDA 收费员登录 9 端点** `PdaUserAppController`:`/app-api/pda/user/*` 9 个,**写 4 个**(login/sign-out/modify-pass/sign-in) 全进 `LEGACY_WRITES`(`modify-pass` 还会写 `td_b_api_log`)。 ★★ 通用口径:`PDABaseReq` 的 6 个公共参数是 `@NotBlank`,但 `data` **没有 `@Valid` 级联** ⇒ `{}`/仅 token 一律 400「不能为空」;齐参无 data 一律 500「系统异常」; 齐参 + `data={}` 落 500「系统异常」/ 500「-10001」/ 400 三档。 ★ 新增 4 个 VO(`ChargerInfoRes`/`ChargerLoginRes`/`SignInReq`/`account/ChargeAccountIsRechargeRes`)。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百五十二切片(批 7 第二十六切片):PDA 图片上传 2 端点** `UploadPictureAppController`:`upload-pic`、`sf-pda-upload-pic`,**都是 multipart + OSS 真写** ⇒ 不进 `LEGACY_WRITES`(那张表要求无 body 时 400),零写守卫写在 §2i10。 ★ `validateToken` 在取文件名 / 加水印 / 上传之前 ⇒ 零字节 `picFile` + 假 token 必落 500「-10001」, 绝无 OSS 写;JSON 侧(无 `@RequestBody`)一律 500「系统异常」。 ★ 落地映射:老 `uploadCarPic`/`uploadXCPic`/`uploadFile(file,plNo)` 新栈都没有 ⇒ 统一 `uploadFile(in,size,ext)`/`uploadFile(file)`;字体用 `reportpdf/Microsoft Yahei.ttf`。 ★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百五十一切片(批 7 第二十五切片):PDA 停车记录 5 端点** `ParkRecordAppController`:`query-berth-info`/`query-park-record`/`query-park-record-by-order-id` 只读;`modify-order-info`/`out-park-nullify` **写** ⇒ 进 `LEGACY_WRITES`。 ★ 同类两种 `data={}` 落点:`query-berth-info` 的 data 是 `?` ⇒ 齐参 + `data={}` 落 **500「-10001」**; 其余 4 个 data 类带 `@NotBlank` ⇒ 落 **400「不能为空」**。 ★ 启动坑复现:`@Resource private CommonService` 会撞 park 模块同名 bean ⇒ 必须 `@Resource(name = "pdaCommonService")`(§238 已记)。 ★ 新增 5 个 VO + `pda.portal.constant.BerthConstant`(抄自老门户)。 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百五十切片(批 7 第二十四切片):PDA 排班服务 2 端点** `DutyAppController`:`query-latest-week-duty`、`query-latest-week-duty-by-charge-code`,**都只读** ⇒ 不进 `LEGACY_WRITES`。 ★ 两套校验并存:前者 `@Valid` + `checkParam` ⇒ 齐参 + `data={}` 落 **500「-10001」**; 后者 **`@RequestBody` 上没有 `@Valid`**、也不调 `checkParam`,只调 `BeanValidatorsUtils.validateWithException(request.getData())` ⇒ **`{}` 落 500「系统异常」** (data 为 null 时 NPE)、齐参 + `data={}` 落 **400「不能为空」**。 ⚠️ 后者**完全不校验 token**(假 token + 真 `chargerCode` 直接 8888 成功),存量缺陷照搬(红线⑥)。 ★ 新增 1 个 VO:`QueryWeekDutysReq`(抄自老门户 `vo/pda`)。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十九切片(批 7 第二十三切片):收费员考核任务 1 端点** `ChargerCheckTaskAppController`:`query-charger-check-task`,**只读** ⇒ 不进 `LEGACY_WRITES`。 ★★ **新样板:没有 `checkParam` 的端点,`data={}` 不会落 -10001** —— 只 `@Valid` 校验公共参数 + 直接 `PdaTokenCacheUtil.getPDAInfoByToken` ⇒ 假 token 得 null ⇒ `.getChargerInfo()` NPE ⇒ **齐参缺 data 与齐参 + `data={}` 都是 500「系统异常」**,两者无差别。 ★ 存量缺陷照搬:两处 `setSysCode("sysCode")` 是**字面量字符串**。 ★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十八切片(批 7 第二十二切片):PDA收费员充值/支出记录 2 端点** `ChargerAccountAppController`:`query-acct-recharge-for-page`、`query-acct-pay-out-for-page`, **两个都只读** ⇒ 不进 `LEGACY_WRITES`;`custId` 取自 PDA token,不从入参取。 ★ 类名带 `App`:`admin/chargeraccount/ChargerAccountController` 已占 bean 名。 ★ 落点第三档:`PDABaseReq` 而 `BaseInfo` **无任何约束** ⇒ 齐参 + `data={}` 落 **500「-10001」**。 ★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十七切片(批 7 第二十一切片):车牌识别激活码 2 端点** `CarNumberIdentifyAppController`:`get-register-code`(只读 `tp_p_pda`)、 `report-register-result`(⚠️ 真写 `tp_p_pda`:`updatePDAByPdaNo`)⇒ 只有后者进 `LEGACY_WRITES`。 ★ 两端点都是 `@Valid` + `checkParam`、data 类都有约束 ⇒ 齐参 + `data={}` 一律 400; `ReportRegisterResultRequest` 的 `@NotBlank` + `@NotNull` 并存 ⇒ 报文**不稳定** ⇒ 断言只认 400 + 二者之一。 ★ 老方法名 `queryFreeBerths` 是复制粘贴残留(实际是「获取激活码」),新栈按语义改名 `getRegisterCode`。 ★ 本片**零新增 VO**。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十六切片(批 7 第二十切片):违章车辆上报 2 端点** `PdaCarBreakRuleAppController`(★ 类名带 `Pda`:`controller.app.carbreak.CarBreakRuleAppController` 已占 bean 名 `carBreakRuleAppController`,同名会 `ConflictingBeanDefinitionException` 启动即死): `upload-pic`(multipart,OSS + `td_c_file`)、`report-car-break-rule`(真插 `tp_c_car_break_rule`)。 ★ 只有 `report-car-break-rule` 进 `LEGACY_WRITES`;`upload-pic` 无 body 是 500「系统异常」⇒ 守卫写在 §2i4。 ★★ **multipart 零写冒烟规矩**:缺 `picFile` part ⇒ 500;**零字节空 part + 假 token ⇒ 500「-10001」** (`validateToken` 在 `getSize()`/`uploadParkFile` 之前 ⇒ 既不传 OSS 也不写 `td_c_file`); 缺 `token` part ⇒ 400「请求参数缺失:token」。⛔ 永不发真字节 + 有效 token。 ★ `upload-pic` 的 JSON 侧(空 body / `{}` / 坏 JSON)**一律 500「系统异常」**,不是 bug。 ★ 本片新增 1 个 VO(`CarBreakRuleReq`)。本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十五切片(批 7 第十九切片):车辆出场 8 端点** `CarOutParkAppController`:4 条真写进 `LEGACY_WRITES`(两个现金出场 `outParkPayedByCash`、 `batch-out-park` 推 MQ、`set-park-order-recosting` 真重算),其余 4 个只读。 ★ 落点三档:两个无约束 data 类 ⇒ 500「-10001」;`PayOutFeeByCashReq` ⇒ 400; 其余 4 个 ⇒ 400「不能为空」;缺 data ⇒ 八个一致 500「系统异常」。 ★★ `PayOutFeeByCashReq` 的 400 报文在「不能为空」/「不能为null」之间**不稳定** (`ConstraintViolation` 是 HashSet)⇒ 断言只认 code=400 + 二者之一。 ★ 本片新增 8 个 VO(`QueryHaveOrderBerthsRes`/`CarOutParkReq`/`QueryOutParkInfoRes`/ `PayOutFeeByCashReq`/`BatchOutParkReq`/`VehicleLeavingReq`/`RecostingByOrderIdReq`/ `VehicleLeavingResForTicket`)。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十四切片(批 7 第十八切片):车辆入场 6 端点** `CarInParkAppController`:`in-park`(`insertParkOrder` 真建停车单)与 `pay-pre-fee-by-cash` (真现金预付)进 `LEGACY_WRITES`,其余 4 个只读。 ★★ 本片**三种 `data={}` 落点并存**:`query-free-berths`(`CarInOutParkReq` 无约束)⇒ 500「-10001」; `get-vehicle-entering-data`(**`checkParam` 被注释** ⇒ 进业务,服务端 `@NotNull`)⇒ 400「不能为null」; 其余 4 个 ⇒ 400「不能为空」。缺 data ⇒ 六个一致 500「系统异常」。 ★ 本片新增 8 个 VO(`QueryFreeBerthsRes`/`CarInOutParkReq`/`CarInParkRes`/`CarInParkReq`/ `QueryPreInParkFeeRes`/`QueryPreInParkFeeReq`/`PayPreFeeByCashReq`/`VehicleEnteringReq`)。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十三切片(批 7 第十七切片):欠费补交 6 端点** `ArrearageAppController`:`pay-arrearage-by-cash` 是**真现金补缴**(`LEGACY_WRITES`),其余 5 个只读。 ★ 六个端点都 `@Valid` + `checkParam`,data 类都有 `@NotBlank` ⇒ `data={}` 一律 400;缺 data ⇒ 500。 ★ 存量缺陷照搬:一键补缴取 orderIds 用 `getRecordInfos()` 而非局部 `recordInfos`。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十二切片(批 7 第十六切片):个人账户支付 1 端点** `PersonAccountPayAppController`:`pre-or-out-pay-by-account` 是**真扣个人账户余额**(`LEGACY_WRITES`)。 ★ `@Valid` + `checkParam` + data 类 `@NotBlank/@NotNull` ⇒ `data={}` 一律 400;缺 data ⇒ 500。 ★ `saveCallInterfaceLog` 在 `checkParam` 之后 ⇒ `data={}` 时**不写 `td_b_api_log`**。 ★ 方法名沿老代码误写 `preOrOutPayByBarcode`(红线⑥不改)。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十一切片(批 7 第十五切片):退款 2 端点** `RefundAppController`:`pre-fee-refund` 是**真退款**(`LEGACY_WRITES`);`query-allow-refund-order` 只读。 ★ 两个端点都 `@Valid` 且方法体都走 `checkParam`,两个 data 类都有 `@NotBlank` ⇒ `data={}` 一律 400「不能为空」(PDA 口径里最简单的情形);缺 data ⇒ 500「系统异常」。 ★ `saveCallInterfaceLog` 在 `checkParam` 之后 ⇒ `data={}` 时**不写 `td_b_api_log`**。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百四十切片(批 7 第十四切片):条码支付 4 端点 + 基类 PDABassController** `BarcodePayAppController`:**3 个真扣款/充值**(`LEGACY_WRITES`),只发空 body / `{}` / `data={}`。 ★★ 修正 §238 的过度概括:`@Valid` 打在 `PDABaseReq` 上,`data` 字段没有 `@Valid` ⇒ **只校验公共参数,不下钻 data**;`data={}` 的落点取决于方法体走不走 `checkParam`。 ★ 本片三种落点:checkParam ⇒ 400;只 validateSigin + 查库 ⇒ 500;只 validateSigin + 服务 校验失败 ⇒ 8888 信封 + 4000(account-recharge,未扣款)。 ★ 随迁基类 `PDABassController` 到 `com.zteits.pay.portal.web.pda`(扫描包内)。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百三十九切片(批 7 第十三切片):停车公共查询 13 端点** `ParkSourceQueryAppController`:**13 个全是纯读**(零写,`LEGACY_WRITES` 无新增)。 ★ 都没有 `@Valid`、都走 `checkParam` ⇒ `{}`/缺 data 都是 500(HV000116); `data={}` 按 data 类有无约束分两组:无约束 ⇒ 500(齐参时 -10001),有约束 ⇒ 400。 ★ 存量照搬:`getEqpIsOnline() == "0"` 字符串引用比较(onlineEqpNum 恒 0)、 `queryNearbyParkLotInfo` 的循环内查收费员(N+1),均已注释、不批量化。 ★ 本工具只扫 `bcp-portal` ⇒ **pda-portal 是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百三十八切片(批 7 第十二切片):巡查 登录/签退/改密 4 端点** `PatrolUserAppController`:**4 个全是真写**(Redis token / 改密码),`LEGACY_WRITES`,只发空 body。 ★ 四个都有 `@Valid`、`data` 类也都有约束 ⇒ 空 body/`{}`/`data={}` 全是 400,**够不到写服务**。 ★ 坑:`@Resource private CommonService commonService` 撞 park 模块同名 bean ⇒ 必须 `@Resource(name = "pdaCommonService")`。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-22 第二百三十七切片(批 7 第十一切片):巡查任务 6 端点** `PatrolTaskAppController`:4 读 + `update-task-state`/`apply-help`(⚠️写,`LEGACY_WRITES`,只发空 body)。 ★★ 总口径:`PDARsesultUtil.checkParam` 第一句就是 HV 校验 `data` ⇒ `data={}` 是 400 还是 500 **取决于 `data` 类上有没有 Bean Validation 约束**,与 `@Valid` 无关。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十六切片(批 7 第十切片):巡查 设备巡查 4 端点** `EqpInspectAppController`:3 读 + `save-inspect-record`(⚠️写,`LEGACY_WRITES`,只发空 body)。 ★★ 与 §235「5 端点整齐一致」相反:同类的 4 个端点 `data={}` 落点各不相同(设备类型 200+`"8888"`、设备属性/异常上报 500、工单明细 500+存量码 1003)。 ★ 无 `@Valid` + 只 `validateSigin` ⇒ 除空 body/坏 JSON 外没有 400。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十五切片(批 7 第九切片):巡查 设备维护 5 端点** `EqpMaintenanceAppController`:4 读 + `update-record-detail`(⚠️写,`LEGACY_WRITES`,只发空 body)。 ★★ **参数上没有 `@Valid`** ⇒ `{}` 不是 400 而是 500「系统异常」(进方法体 `validateSigin` 里 NPE)。 ★ 只 `validateSigin`(不校验 token),随后 `getPDAInfoByToken` ⇒ 假 token 也是 500 而**不是** `-10001`。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十四切片(批 7 第八切片):巡查 情况上报 3 端点** `PatrolInspectAppController`:`save-inspect-situation`(⚠️写)+ `get-service-rule-list`(读)+ `to-save-service-record`(⚠️写)。 ★ 三端点落点完全一致(`checkParam` **不吞异常**)⇒ 假 token 一律 500「-10001」;与同目录的巡查心跳(异常全吞)相反。 ★ 老方法名 `updateTaskState` 与语义不符,是复制粘贴残留 ⇒ 照搬不改。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十三切片(批 7 第七切片):巡查 版本检测 + 心跳 2 端点** `PatrolCheckAppController`(纯读)+ `PatrolHeartBeatAppController`(⚠️ 真写,`LEGACY_WRITES`,只发空 body)。 ★★ 同名端点、**不同老版本**:PDA 心跳给 signId 加了 loginRecordId 兜底并设 signDetailid,巡查心跳两样都没有 ⇒ 各搬各的。 ★ 巡查版本检测同 PDA 口径:**不校验 token**,`data` 非空即真查版本表,`appType=5`。 ★ 本工具只扫 `bcp-portal` ⇒ **巡查端是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十二切片(批 7 第六切片):PDA 推送消息 1 端点** `JPushMsgAppController`(老 `pda-portal` `web/pda/JPushMsgController`),纯读。 ★ 老类 `extends PDABassController`(`@Component`,带 MQ/HttpServletRequest),但本端点没调用任何继承方法 ⇒ 新类不继承。 ★ 鉴权同 §2hm:`checkParam`(签名 + token,不吞)⇒ 假 token 500「-10001」。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十一切片(批 7 第五切片):PDA 版本检测 1 端点** `PDACheckAppController`(老 `pda-portal` `web/pda/PDACheckController`),纯读。 ★★ **PDA 鉴权强度不一致**:本端点只调 `validateSigin`、**不校验 token** ⇒ `data` 非空就真查版本表、返回真数据。 ★ 返回老 `PDABaseRes` 信封,`code` 是**字符串 `"8888"`**(断言按字符串比)。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百三十切片(批 7 第四切片):PDA 自助停车车牌上报 1 端点** `CarSelfParkingAppController`(老 `pda-portal` `web/pda/CarSelfParkingController`),**真写**(文件表)⇒ 已进 `LEGACY_WRITES`。 ★ 三种分界:`{}`/`data={}` ⇒ 400(`ConstraintViolationException` 走 400 分支);`data` **整个缺失** ⇒ 500 系统异常;假 token ⇒ 500 -10001。 ★ 老类硬编码 `sysCode="ZTEITS-ORDER"`;hutool3 `CollectionUtil` → Spring `CollectionUtils`。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百二十九切片(批 7 第三切片):PDA 码表查询 1 端点** `SysMsgQueryAppController`(老 `pda-portal` `web/common/SysMsgQueryController`),纯读、走本地码表缓存。 ★ 新坑:老方法**没有 `@Valid`** ⇒ `{}` 不落 400 而落 500「系统异常」(`validateSigin` NPE)。 ★ 同一个 `checkParam`:带 `@Valid` 时 `{}`=400(§2hm),不带时 `{}`=500(本片)。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百二十八切片(批 7 第二切片):PDA 今日统计 1 端点 + 门户 CommonService** `PdaTodayStatisticsAppController`(老 `pda-portal` `web/pda/TodayStatisticsController`),纯读。 ★ 伴迁 `web/pda/CommonService`(543 行)→ `com.zteits.pay.portal.web.pda`(**必须改包名**:basePackages 没扫 `com.zteits.pda`)。 ★ 新坑:bean 名/字段名都必须是 `pdaCommonService`,否则撞 `park...inoutpark.CommonService`(@Resource 先按名再按类型)。 ★ 同基座不同形态:本端点不吞异常 ⇒ 假 token 500「-10001 登录失效」;`data` 缺失 ⇒ NPE 500「系统异常」。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百二十七切片(批 7 第一切片):PDA 门户基座 + 心跳 1 端点** `PdaHeartBeatAppController`(老 `pda-portal` `web/pda/HeartBeatController`):收费员实时位置上报告。 ★ 顺带把 PDA 门户基座搬进 `parking-legacy-common`(逐字):`com.zteits.pda.portal.common.PDARsesultUtil`、 `vo/base/{PDABaseReq,PDABaseRes}`、`vo/pda/GpsResportReq`。 ★ PDA 鉴权 = 自己的签名(`PDARsesultUtil.checkParam`)+ `PdaTokenCacheUtil` token ⇒ 全 `@PermitAll`。 ★ 返回值是**裸 `Boolean`**(响应体 `true`),冒烟要 `raw=True`;老方法**吞掉所有异常**并 return true。 ★ 新坑:`@Valid` 多约束同时失败时报哪条是随机的(「不能为空」/「不能为null」)⇒ 断言锁 code、文案两种都收。 ★ 本工具只扫 `bcp-portal` ⇒ **PDA 是盲区**,225/1199 不变。 ★★ **2026-10-21 第二百二十六切片(P5 第十批):违章车辆上报 3 端点** `CarBreakRuleAppController`(老 `app/carbreak/CarBreakRuleController`):违章照片上传(multipart+水印)、 违章车辆上报、违章道路分页。搬了两个门户 VO(`CarBreakRuleReq`、`RoadNameReq`,都逐字)。 ★ 三条都靠**老 APP token**(`checkAppToken` / `checkParam`)⇒ 全 `@PermitAll`;三条都 `@PostMapping` ⇒ GET 401/405。 ⚠️ **`/upload-pic` 是 multipart + OSS 直连 ali 的真写**:空 body 落 500「系统异常」(不是 400) ⇒ **不能进 `LEGACY_WRITES`**(同 `/app-api/pay/file-upload/upload-pic`);冒烟只发不带文件分片 / 0 字节假分片。 ★ `/query-...-for-page` 的 `{}` 落 500「[存量错误码 4014] appId不存在」(验签在方法体里抛)。 ★ 水印字体类路径 `MicrosoftYahei.ttf` → `reportpdf/Microsoft Yahei.ttf`(同 md5)。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十五切片(P5 第九批):微信公众号 4 端点** `AliWeixinPublicAppController`(老 `weixinpublic/AliWeixinPublicController`):公众号换 openId / 小程序 code2session / 小程序手机号登录 / JS-SDK 扫码签名。搬了两个门户 VO(`GetPhoneNumberReq`、 `WxChatScanReq`,都逐字)。 ★ 四个方法都没验签 ⇒ 全 `@PermitAll`;四条都 `@PostMapping` ⇒ GET 401/405。 ⚠️ ⭐ **四条都会出站调微信,URL 里带解密后的 appSecret** ⇒ 冒烟只用**不存在的 appId**(落 1001、不出站)。 ★ `{}` 落点不一致:前三个老信封 1003;`/get-wechat-scan` 服务层抛 ⇒ 500(存量照搬)。 ★ `/small-routine-login` 是**真写**(建 `td_c_cust_person` + Redis 写 token)⇒ 只发空 body。 ★ 老类 `@Value("${local.base.url}")` 字段源码零引用 ⇒ 不搬(坑 16)。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十四切片(P5 第八批):农行支付 5 端点** `ABCBankAppController`(老 `bank/ABCBankController`):钱包充值下单 / app 内嵌下单 / H5 下单 / 银行异步通知 / 银行订单状态查询。搬了门户 `XMLDocument`(逐字)。 ★ 五条老代码都写了 `method = RequestMethod.POST` ⇒ 只收 POST(GET 401/405), 与上一片「未写 method」口径**相反**。 ★ ★ **三个端点在老代码里是 private**(`bankH5Pay`/`bankPayForH5`/`queryBankOrderState`): Spring 反射注册 handler 不过滤可见性 ⇒ 真能通,迁移照搬 private。 ⚠️ 风险最高的一批:3 条真下单(`appPay`)+ 1 条真结算(`payNotify`)⇒ 只测绑定层/第一道闸。 ★ `/pay-notify-url` 没有 `@RequestBody` ⇒ 空 body 是 500,**不进** `LEGACY_WRITES`。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十三切片(P5 第七批):北京西城用户认证 3 端点** `BJXCUserAuthAppController`(老 `wxpublic4bjxc/BJXCUserAuthController`):认证状态 / 可购卡列表 / 线下转线上专用。搬了两个门户 VO(`UserAuthRes` 逐字、`AvailableVipCardInfoVO` 仅换 import)。 ★ 三个方法都没验签 ⇒ `@PermitAll`;★ **老 mapping 未写 method** ⇒ 任意方法可进, GET 无 token 也落 400(不是 401/405)。 ★ `/auth-user` 是读接口带写(`buy_state` 10→20)⇒ 进 `LEGACY_WRITES`。 ★ 本地认证表缺列 `examine_state` ⇒ 三个端点的 `{}` 恒 500(环境漂移)。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十二切片(P5 第六批):北京西城会员卡 3 端点** `BJXCVipCardAppController`(老 `wxpublic4bjxc/BJXCVipCardController`):可办车场 / 新增认证 / 下单。 ★ 三个端点全部匿名(`save` 令牌失效只降级成「未登录支付」,不拦)⇒ `@PermitAll`。 ★★ 坑:`...service.order` 下有两个同名 `VipCardAuthPersonBjxcService`,只有**子包**那份有实现; 导错包 ⇒ 启动 `required a bean ... could not be found`。 ★ 本地无 `VIP_OPEN_PARKING` 字典 ⇒ `query` 的 `{}` 恒 500(NPE);认证表缺列 `examine_state` ⇒ `save` 落库路径 500。两者老栈同样复现 ⇒ 环境漂移,不是迁移缺陷。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十一切片(P5 第五批):北京西城会员卡开票 2 端点** `BJXCVipCardInvoiceAppController`(老 `wxpublic4bjxc/BJXCVipCardInvoiceController`)。 搬了两个门户 VO(`UserAuthReq` 逐字、`HasBuyVipCardInfoVO` 仅换 `StringUtils` import)。 ★ 老类继承 `AppBaseController` 但**两个方法都没验签** ⇒ 新类 `@PermitAll`。 ★ 本地表 `vip_card_auth_person_bjxc` 缺列 `examine_state` ⇒ 业务路径在本地恒 500, **老栈同样会 500** ⇒ 环境漂移,不是迁移缺陷。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百二十切片(P5 第四批):公众号用户 2 端点** `WXPublicUserAppController`(老 `weixinpublic/WXPublicUserController`):openId 换 App token、 openId 绑手机号。**整片无出站**,依赖全在新栈(无需搬类)。 ★ 响应是第三套协议:`AppRsesultUtil` 的 `{"code":"1003","message":...}` —— `code` 字符串、键是 `message`,别按 `CommonResult` 的 `msg` 写断言。 ★ 仍是本工具盲区(`pay-portal`)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百一十九切片(P5 第三批):电池(充电)微信下单 1 端点** `BatteryWeixinPayAppController#unifiedOrder`(老 `weixin/BatteryWeixinPayController`)。 老门户 VO `BatteryAliWxPayRequest` 本片才搬进 `parking-legacy-common` —— 注意:**「包目录已存在」≠「类已存在」**,按类名 `find`。 ★ 仍是本工具盲区(`pay-portal` 不在 `DEFAULT_LEGACY` 口径内)⇒ 225/1199 不变。 ★★ **2026-10-21 第二百一十八切片(P5 第二批):4 个真下单端点,两个类补齐 3/3** `AliPayAppController` 的 `/app-pay`、`/ali-h5-pay` 与 `WeixinPayAppController` 的 `/unified-order`、`/public-unified-order`。 ★ 订正:上一片写的"下单端点依赖未迁的门户本地类"**是误判** —— `BillQueryCommonService`(pay 模块) / `EmployeeChargerValidateUtil` / `BillQueryRes` / `QueryFeePublicReq`(legacy-common) 早就在新栈。**判定缺口前先 `find` 一遍新栈。** ★ 仍是本工具盲区:`pay-portal` 不在 `DEFAULT_LEGACY` 口径内 ⇒ 225/1199 不会变。 ★★ **2026-10-21 第二百一十七切片(P5 第一批):第三方支付回调 2 端点 —— 本工具的盲区** 搬的是 `zteits-pay-portal` 的 `alipay/AliPayController#aliPayNotify` 与 `weixin/WeixinPayController#payNotifyUrl`(各 1/3 端点;**下单端点不是缺口**, 第二百一十八切片已补齐 3/3 —— 当时写的"依赖未迁的门户本地类"是误判)。 ★★ **P5 的进度本工具完全看不见**:`DEFAULT_LEGACY` 写死 `zteits-bcp-portal/.../web`, `pay-portal` / `minipay-portal` 全在口径外 ⇒ 搬完 P5 后 225/1199、未迁 0/0 **都不会变**。 要统计 P5,必须传 `--legacy-root` 或另写工具;**别拿本工具的数字当全站进度**。 ★ 回调是 `@PermitAll` + 协议原文返回(支付宝 `success`、微信 XML),不是 `CommonResult`; 冒烟绝不发 `result_code=SUCCESS`(会走真结算)。 ★★ **2026-10-21 第二百一十六切片:老门户最后 4 个管理后台 Controller(4 类 / 4 端点)** 搬的是 `zteits-bcp-portal` 的 `HomeController`、`aliyunecs/AliyuncsEcsController`、 `billing/UploadController`、`personpc/PersonOrderBackWxPayController` ⇒ ★★ **就绪与已迁首次对齐:就绪 225/1199、已迁 225/1199、未迁 0/0**(此前长期 4/4)。 机械缺口 2/11 不变(`OrderExamineController` 2 已判不可迁 + `ParkingAssistantController` 9 ⛔ 不碰), 5 个死代码不变。 ★ `HomeController` **必须**落在 `parking-module-*` 的 `controller` 包(`controller.admin` 的同级): 本工具只扫 `parking-module-*`,放 `parking-server` 会让本类重新变回「未迁」(本片实测踩过)。 ★ `aliyunecs` 端点带 token 会**真出站调阿里云**(实现在无配置时回落到写死的 key)⇒ 冒烟不带 token, 只验「匿名 401 / 带 token GET 405 / 老路由 404」;`upload` 老 `@NoAuth` 按 §3.3 第 1 类收敛为 `pay:upload:create`(缺文件是 500,不进 `LEGACY_WRITES`);`wxpay` 老 `@SysOpLog` → `@OperateLog`。 ★★ **2026-10-21 第二百一十五切片:时域月卡购买/续费(1 类 / 2 端点,全写,老类已弃用)** 搬的是 `zteits-pay-portal/.../web/app/viparea/TimeVipCardController`(老 `/timeVipCard`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 老类注释已标**弃用**,但映射仍在门户 ⇒ 照搬;2 条全真写,进 `LEGACY_WRITES`、只发空 body。 ★ 与 §214 的差别:**没有** `isJudgeArreage` 前置,`checkParam` 是第一道业务闸,结构完整+appId 假直接 `[4014]`。 ★ 复用 §214 补搬的两个 VO,零新 VO、零新依赖;存量缺陷:`createVipTimeCardOrder` 调 `createVipAreaCardOrder`。 ★★ **2026-10-21 第二百一十四切片:区域月卡购买/续费(1 类 / 2 端点,全写)** 搬的是 `zteits-pay-portal/.../web/app/viparea/AreaVipCardController`(老 `/areaVipCard`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 2 条全是真写(建卡 + `order_vipcard` + 明细 / 续费同四步)⇒ 进 `LEGACY_WRITES`、只发空 body。 ★ ⚠️ **`checkParam` 不是第一道闸**:先跑 `isJudgeArreage(carNumber, effDate, cardNo)`(只读,查不到卡 return false), 所以要证明 `checkParam` 在链路上得给"结构完整 + appId 假 + cardNo 不存在"的 body(落 `[4014]`); 只给 `AppBaseReq` 5 个字段的假签名会先被 `@Valid` 业务必填挡成 400。日期只认 `yyyy-MM-dd HH:mm:ss`。 ★ 存量缺陷:`getIsJudgeArreargeVipcard()`/`getType()` 拆箱 NPE;`Long.valueOf(orderFee)`;两分支逐行重复。 ★★ **2026-10-21 第二百一十三切片:错峰共享信息验证(1 类 / 3 端点,1 读 + 2 写)** 搬的是 `zteits-pay-portal/.../web/app/newversion/SharedAuthenticationController`(老 `/sharedAuthentication`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 1 读 + 2 写(`driver-license-review`/`idcard-review` 改 `td_c_car`)⇒ 两条写进 `LEGACY_WRITES`、只发空 body。 ★ 3 条都 `@Valid` ⇒ `{}`/空 body 都是 400;假签名**两种落点并存**:读走 `checkParam` ⇒ `[4014]`, 两条写没有 `checkParam`、第一道闸是 `AppTokenUtil.getUserCustId` ⇒ `[-10000]`。 ★ 存量缺陷:逐车 N+1 查审核记录;`ExamineStateEnum.of(Integer)` 拆箱 NPE;日志占位符与字符串拼接混用。 ★★ **2026-10-21 第二百一十二切片:新版 APP 区域卡(1 类 / 3 端点,全读)** 搬的是 `zteits-pay-portal/.../web/app/newversion/VipAreaCardController`(老 `/vipAreaCard`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 3 条全读;⚠️ `valid-vip-area-card-buy-or-renew` 在 `checkParam` **之前** `getType() == 1` 拆箱 ⇒ `{}`/假签名是 500「系统异常」,带 `type` 才落 `[4014]`。复用 §211 补搬的两个 VO。 ★★ **2026-10-21 第二百一十一切片:新版 APP 时域卡(1 类 / 3 端点,全读)** 搬的是 `zteits-pay-portal/.../web/app/newversion/VipTimeCardController`(老 `/vipTimeCard`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ `query-vip-time-card-info` 带 `@Valid`(`AppBaseReq` 5 个 `@NotBlank`)⇒ `{}` 400; 另两条老代码已标弃用、无 `@Valid` ⇒ `{}`/假签名都 500 `[4014]`。 ★ `validVipTimeCardBuyOrRenew` **只查不写**(次数/时长校验)⇒ 3 条全读、不进 `LEGACY_WRITES`。 ★ 补搬 `vo.app.vipareacard.QueryVipAreaCardRequest`、`web.app.viparea.query.TVipAreaCardValidRequest`。 ★★ **2026-10-21 第二百一十切片:ETC 德亚追缴通知(1 类 / 1 端点,真写)** 搬的是 `zteits-pay-portal/.../web/app/etcinterface/ETCDeYaController`(老 `/etc/deya`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 第三方回调不吃老 token;裸 VO 入参(无 `@RequestBody`)⇒ 任何空 payload 都 500「系统异常」 (`bizParam`=null 的 NPE 在 try 之外)。★ 真写:`arrearsPayNotify` 欠费追缴结算。 ⚠️ 空 body 是 500 ⇒ **不能进 `LEGACY_WRITES`**(那张表要求 400)。 ★★ **2026-10-21 第二百零九切片:第三方车场查询(1 类 / 1 端点,纯缓存读)** 搬的是 `zteits-pay-portal/.../web/app/thirdservice/ParkingLotForThirdController`(老 `/thirdParkQuery`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 不吃老 token:`@Valid @RequestBody String` 取 `appId` 查 `td_c_third_part_app_authorization`。 空 body ⇒ 400;`{}`/假签名 ⇒ 500「系统异常」(appId=null 存量路径);空白串 ⇒ 200 + 空列表。 ⚠️ 存量缺陷:该 Service 查不到也返回**空壳 DTO** ⇒ `THIRDPART_APPID_NOT_EXISTS` 不可达。 ★ `sysCode` 传进了 `GetAuthorizationByAppIdRequest` 但实现类**只按 appId 查** ⇒ 不必另立独立键。 ★★ **2026-10-21 第二百零八切片:丹阳公共自行车(1 类 / 4 端点,纯出站代理)** 搬的是 `zteits-pay-portal/.../web/app/danyang/DanYangBikeQueryController`(老 `/danYangBike`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 4 条全是 `@RequestParam` 代理到 danyangbike.com:缺必填参数 ⇒ 400「请求参数缺失:」。 ⛔ `/baseinfo/get-batch` 参数全 optional,空参数也出站 ⇒ 冒烟只验 GET 405/401,绝不 POST。 ★ 零落库、零写 ⇒ 不进 `LEGACY_WRITES`。 ★★ **2026-10-21 第二百零七切片:商户信息(1 类 / 4 端点,2 写 + 2 读)** 搬的是 `zteits-pay-portal/.../web/app/business/CustBusinessController`(老 `/custBusiness`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ `@Valid` 先于 `checkParam`:两条写 `{}` ⇒ 400「请求参数不正确」;假签名带 `token` ⇒ 500 `[4014]`。 两条读无 `@Valid` ⇒ `{}`/假签名都 500 `[4014]`。 ★ 2 条写(落 `td_c_cust_business`、改商户并重置审核状态)进 `LEGACY_WRITES`,只发无 body。 ★★ **2026-10-21 第二百零六切片:车位预约(1 类 / 6 端点,2 写 + 4 读)** 搬的是 `zteits-pay-portal/.../web/app/appointment/AppointMentBerthController`(老 `/appointmentBerth`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 6 条全 `@RequestBody`(无 `@Valid`):空 body 400;`{}`/假签名**三落点** —— `checkParam` ×4 ⇒ 500 `[4014]`;手写 `checkAppToken` ×1 ⇒ 500 `[-10000]`; 不验签 ×1 ⇒ 500「系统异常」。 ★ 2 条写(建 `order_appointment`、改预约日期)进 `LEGACY_WRITES`,只发无 body。 ★★ **2026-10-21 第二百零五切片:提现(1 类 / 1 端点,存量空壳)** 搬的是 `zteits-pay-portal/.../web/app/TransferController`(老 `/parkInfo`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ `@Valid @RequestBody String`:空 body 400;非空 body 直接 **`return null`** ⇒ HTTP 200 + 空体。 ★ 零副作用(`orderTransferService` 从未调用)⇒ **不进 `LEGACY_WRITES`**。 ★ 至此老 `web/app/` 顶层类全部处理完(`IRainParkingController` 整类注释掉、无需迁)。 ★★ **2026-10-21 第二百零四切片:APP 图片上传(1 类 / 1 端点,写)** 搬的是 `zteits-pay-portal/.../web/app/SysFileUploadController`(老 `/pic`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 入参是 **multipart/form**(`@RequestParam MultipartFile picFile + token + fileType`): 空 body/`{}` 因缺必填参数被兜底成 **500「系统异常」**(不是 400) ⇒ **不能进 `LEGACY_WRITES`**,零写入守卫单独钉在 §2go。 ★ 会落 FastDFS/OSS + `td_c_file` ⇒ 冒烟**永不发真文件**。 ★★ **2026-10-21 第二百零三切片:会员卡购买(1 类 / 1 端点,写)** 搬的是 `zteits-pay-portal/.../web/app/VipCardController`(老 `/vip`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ `@RequestBody String` + 手写 `checkParam` ⇒ `{}`/假签名 500 `[4014]`。 ★ 真写 `order_vipcard` + `order_vipcard_detail` ⇒ `LEGACY_WRITES` 只发无 body。 ★★ **2026-10-21 第二百零二切片:我的首页(1 类 / 2 端点,全读)** 搬的是 `zteits-pay-portal/.../web/app/MyHomeController`(老 `/home`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 两种落点:`home-page`(`@RequestBody String` + `validateSigin`)⇒ 500 `[4014]`; `query-arrearage-is-exist` **不验签名** ⇒ 成功信封(`code` 字符串 `"0"`、`isLogin=0`)。 ★ 全读,`LEGACY_WRITES` 不加。 ★★ **2026-10-21 第二百零一切片:停车订单查询(1 类 / 13 端点,12 读 + 1 写)** 搬的是 `zteits-pay-portal/.../web/app/ParkOrderQueryNewController`(老 `/queryParkOrder`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★ 补回 8 个老 VO(`parklot/` 5 个 + `billquery/` 3 个)到 legacy-common; jfinal `JsonUtils.toJson` 唯一一处(日志)换 fastjson `JSON.toJSONString`。 ★ 鉴权有**三类落点**:8 条无 `@Valid` ⇒ 500 `[4014]`;4 条 `@Valid` ⇒ 400; `query-arrearage-sta-for-wx-publc` 因 `getOrgId() == 10003` 拆箱 null ⇒ 500「系统异常」。 ★ 唯一写 `appDeleteParkOrder`(`delType=0` 按 custId 全部车牌批量软删)⇒ `LEGACY_WRITES` 只发无 body; `openOrClose` 名字像写、实际只读。 ★★ **2026-10-21 第二百切片:个人账户支付(1 类 / 4 端点,全写)+ 补回 billquery 路由链** 搬的是 `zteits-pay-portal/.../web/app/AppAccoutPayController`(老 `/appAccountPay`)⇒ 口径同样**不在 `DEFAULT_LEGACY` 里**,就绪 **225/1199**、已迁 **221/1195**、未迁 **4/4**、 机械缺口 **2/11**、5 死代码**全部不变**。 ★★ 补链路是本片主体:整条 `com.zteits.pay.portal.billquery`(11 文件)+ `HttpClientTutorial` + 5 个 billquery VO + `ZeroPayReq` + `EmployeeChargerValidateUtil`,并把 §196 精简版 `BillQueryCommonService` 换成 964 行全量版。 ⚠️ 这些类放在 **`parking-module-pay`**:`KeyTopBillQueryServiceImpl` 依赖 module-pay 的 `KeyTopBillQueryInnerService`,而 `parking-legacy-common` 不能反向依赖模块。 `DiscountWrapper` 复用 `com.zteits.ipay.util.DiscountWrapper`(同签名)。 ★ 同一控制器两种鉴权:`account-pay` 手写 `checkParam` ⇒ `{}` 500 `[4014]`; 其余 3 条 `@Valid` + `ZeroPayReq` ⇒ `{}` 400(文案随机)。 ★ 4 条全是钱/账 ⇒ 全进 `LEGACY_WRITES`、只发无 body。 ★★ **2026-10-20 第一百六十七切片:补回月卡合同整条链路 + 搬完车场运营·月卡合同管理 6 端点** (`VipCardContractController`,路由 `/admin-api/order/vip-card-contract`) ★ 缺口原因:`VipCardContractService` 整条链路都不在新栈(接口 + 4 个 DTO/入参 + 域对象 + 两套 Mapper/XML + Word/Excel 模板)。本次按老仓逐字搬回,再补 3 个编排服务 (归档 / Word 渲染 / 四川营台账导出)。 ⇒ 就绪 **224/1193 → 225/1199**、已迁 **220/1189 → 221/1195**、未迁 **4/4**、 缺口 **3/17 → 2/11**(剩 `OrderExamineController` 2、`ParkingAssistantController` 9[不碰])、 死代码 5 不变。 ★ 老类零 `@NoAuth`、零 `@SysOpLog` ⇒ 6 条全 `@PreAuthorize`、新类零 `@OperateLog`。 ★ ⛔ `archive-now` 是真归档 + 真传 OSS:冒烟只允许**空 body**(绑定层 400),⛔ 不发 `{}`。 每日 03:23 的 `VipCardContractTask` 定时任务**未搬**(会自行上传真实 OSS)。 ★ 冒烟 34 条(+22 §2ff + 6 §5b + 6 `LEGACY_WRITES`),6262 → 6296;零写 `system_operate_log` **+244/轮**(§166 的 5 条 `@OperateLog`,本片零新增),`td_b_vip_card_contract` 恒 2 行。 ★ POI 4.1.2:`Cell.CELL_TYPE_NUMERIC` 已删,台账导出用 `CellType.NUMERIC`。 ★★ **2026-10-20 第一百六十六切片:补回 `BjxcPushService` + 搬完停车订单管理 14 端点** (`ParkingRecordController`,路由 `/admin-api/order/park-record`) ★ 缺口原因只有一条:12 个服务依赖都在,**只差 `BjxcPushService`**(接口 + 2 DTO + 太极响应 DTO + 实现类都不在新栈)。本次按老仓逐行搬回 `BjxcPushServiceImpl`, 并给 `ParkOrderDTO` 补 `isUploadThird`(老 DTO 有、新栈缺,导出侧 `getParkInfo` 要回填)。 ⇒ 就绪 **223/1179 → 224/1193**、已迁 **219/1175 → 220/1189**、未迁 **4/4**、 缺口 **4/31 → 3/17**(剩 `ParkingAssistantController` 9[不碰]、`VipCardContractController` 6、 `OrderExamineController` 2)、死代码 5 不变。 ★ `@NoAuth` 两条:导出收紧为 `GET /export-excel` + `:export`;`queryParkOrderInOutImgNew` 是上一方法的逐字副本、老前端 0 调用 ⇒ 收紧为 `:query`。两条都不进 `@PermitAll` 白名单。 ★ 冒烟 46 条(+26 §2fe + 14 §5b + 6 `LEGACY_WRITES`);零写 `system_operate_log` **+244/轮** (本片 5 条 `@OperateLog`),其余 52 张表 0 变化。 ★ 编译踩坑:`OperateLog`/`OperateTypeEnum` 在 `com.zteits.parking.framework.operatelog.core`。 ★ `ParkOrderDTO` 加字段 ⇒ §2u 的 RSN 全键断言 46 → **47**,`RSN_KEYS` 已同步。 ★★ **2026-10-19 第一百六十五切片:修第二类实现误判 + 搬完进出场上报 3 端点** (`InOutParkStatisticController` 2 读 + 1 导出,路由 `/admin-api/park/in-out-park-statistic`) ★★ **工具修正**:实现扫描此前只 glob `*Impl.java` / `*Ipml.java`,漏掉**不叫 Impl 的实现类** (典型:桥接空实现 `NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService`) ⇒ 已迁的 `GroundLockManageController` 6 端点一直被误挂在缺口档。现改为扫全部 `*.java` (先 `"implements" in src` 预过滤,再套 `IMPL_PAT`),彻底不依赖命名约定 ⇒ 就绪 **221/1170 → 223/1179**、缺口 **6/40 → 4/31**。 ★ `InOutParkStatisticService` 接口一直在 `parking-module-api`,但**无实现**(存量实现整类被注释、 Dubbo provider 早已注释);本次按 `git show 550cba464e^` 搬回实现, 并补回它依赖、同样被删干净的 `TdACardCouponsService`(接口 + `queryCardCouponsByCarNumbers` 单方法 + pay 模块实现;DB 访问留在 pay 内,不在 park 跨库直连)。 ⇒ 已迁 **217/1166 → 219/1175**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、死代码 5 不变。 ★ 冒烟 17 条(+3 §5b):两个读端点空 body 停在 1003;chart 跨天横轴 19 格、同一天横轴 **25 格(00:00 … 24:00,存量 `j <= hour + 1` 多排一格)**;导出缺参停在绑定层 400。 ★ 本机 `parking_cloud` **没有 `inpark_log` / `outpark_log`** ⇒ list/export 的 SQL 抛 `Table … doesn't exist`,被兜底成 501「表结构未导入」;断言写成 `0 或 501`,表导入后自然变 0。 ★ 零写一轮:`system_operate_log` 两次计数均 101348(本片零 `@OperateLog`),其余表 0 变化。 ★★ **2026-10-19 第一百六十四切片:修 DTO 误判 + 搬完会员卡购买 15 端点** (`TVipTimeCardCustInfoController` 6 + `TVipAreaCardCustInfoController` 9) ★★ **工具修正**:`RenewVipAreaCardOperReq` 位于 `api.service.pay.viparea.oper`,是**入参 DTO 类**、 不是服务契约。旧版只看 `import com.zteits.clouds.api.service.*` 就判"接口不在新栈", 把这两个 Controller 误挂进缺口档。新增 `API_CLASS_PAT` 收集 `api.service.*` 下的类, `scan_legacy` 命中即跳过 ⇒ 就绪 **219/1155 → 221/1170**、缺口 **8/55 → 6/40**。 ★ 15 个端点全 `@PreAuthorize('bill:vip-{time,area}-card-cust-info:*')`、全 `@PostMapping`; 7 个写端点挂 `@OperateLog`(时域卡 3 + 区域卡 4),其余读/校验端点零注解。 ⇒ 已迁 **215/1151 → 217/1166**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、死代码 5 不变。 ★ 冒烟 15 条全发空 body:create 停在 1003(时域卡编码/区域卡编码)、renew 停在存量 `Value for cardNo cannot be null`(500「系统异常」,红线⑥照搬)、delete 停在 `@Valid`、 update/delete-list 停在控制器 1003、push 停在 `@NotNull id`、statistic 停在参数校验。 ★ 零写一轮:仅 `system_operate_log` +239(7 个 `@OperateLog` 里 delete 两条在绑定层就 400、 到不了切面,其余 5 条真正写入;另 234 条来自全脚本既有端点);新增监控表 `t_vip_area_card_cust_info` / `t_vip_area_card_cust_info_rel` 均 0 变化。 ★★ **2026-10-19 第一百六十三切片落地 P3 ②(操作员 id 换算表)** (`LoginUserBridge` + `LegacyOpIdMappingConfig`,配置键 `parking.legacy.op-id-mapping`) ⇒ 本片**不搬 Controller、不加端点**,上面所有计数**逐位不变**: **215/1151**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、缺口 **8/55**、死代码 5。 ★ `opId` 现在与 `orgId` 同一手法:`新system_users.id:老sys_op.id` 查表换算; **默认空表 = 恒等映射**(不配与改造前完全一致),非法格式启动即失败。 ★ 本机 `sys_op` 0 行 ⇒ 代码里**零猜测映射**;本地 yaml 的 `1:10001` 仅为验证链路。 ★ 端到端实测:`save-black-list-rule` 写入 `create_empid=10001`(探查行已清理); 本片零写,基线仍 **+234/轮**。 ★★ **2026-10-19 第一百六十二切片补全两个既有类的 session 端点** (`PersonPCIndexController` +3/3:首页卡片 / 停车记录 / 欠费记录; `PersonPCCarNumController` +1/1:车牌读取) ⇒ 两个类此前已计入"已迁",本片只是补齐端点 ⇒ 就绪/已迁数**不变**: **215/1151**、未迁 **4/4**、✔ **3/3**、⚠A **0/0**、⚠B **1/1**、缺口 **8/55**、死代码 5。 ★ 4 条都经私有 `getOwnerCarNumbers()` 或直接读 `getUserInfoForPersonPC()`, 带 token 无 session 时依次撞存量 1003(`custId为空!` / `组织id为空!` / `获取客户信息失败`,顺序见 §162 ⑥)。 ★ 新增 4 条 `§5b` 无 token 断言;本片零写,基线仍 **+234/轮**。 ★★ **2026-10-19 第一百六十一切片借 §160 登录桥搬走 ⚠B 的 `PersonPCCouponController` 1/1** (`/admin-api/pay/person-pc-coupon/get-coupon-person`,1 读、零导出、零 `@OperateLog`) ⇒ 已迁 **214/1150 → 215/1151**、未迁 **5/5 → 4/4**、✔ **3/3** 不变、 ⚠B **2/2 → 1/1**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ 唯一闸门是 `getUserInfoForPersonPC()`:session `USER_INFO.custId` 为空 ⇒ 存量 1003 「获取客户信息失败」;带 token 但无个人客户 session 时冒烟就停在这里。 ★ 查询条件照搬:`cardTypes=[1,2,3]`、`day=15`、`dataState` 有效;回填 `orgName` 的 N+1 与 `getData()` 空时 NPE 是存量缺陷,照搬。 ★ ⚠️ 本地 `td_c_coupon_person` **缺 `car_num` 列**(parking-cloud 存量 mapper 多列漂移, 老 `zteits-pay` 同名 SQL 并不查它)⇒ 正样本探针临时加列、跑完删列; 冒烟只走 1003,不执行 SQL,不受影响。⛔ 别为此改 mapper/schema。 ★ 本片零写:基线仍 **+234/轮**;`td_c_coupon_person` / `td_c_coupon_rule` 已在监控内。 ★★ **2026-10-19 第一百六十切片落地 P3 ③(个人客户登录桥)并搬走 `PersonPCAuthController` 4/4** (`/app-api/pay/person-pc-auth`,4 条 `@PermitAll`:发码 / 登录 / 取用户信息 / 登出) ⇒ 已迁 **213/1146 → 214/1150**、未迁 **6/9 → 5/5**、✔ **4/7 → 3/3**、 ⚠B **2/2** 不变(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ 桥的原理:登录端点把 `LOGIN_USER_TOKEN` / `USER_INFO` 写进 **HTTP session**, 于是 `SessionCommUtil#getUserInfoForPersonPC()` 在新栈恢复可用 ⇒ `PersonPCCouponController` 已按原样搬走,其余「只差 session」的类仍待补。 ★ ⛔ 冒烟**绝不**用已注册手机号打 `send-validator-code`(会真实发短信); 正样本用 Redis 种 `person-pc:validator_code_` 单独验证(见 §160 ⑥)。 ★★ **2026-10-19 第一百五十九切片落地 P3 ①(数据权限桥)并搬走 ⚠B 的 `ParkLotStatisticController` 1/1** ⇒ 已迁 **212/1145 → 213/1146**、未迁 **7/10 → 6/9**、✔ **4/7** 不变、 ⚠B **3/3 → 2/2**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ `LoginUserBridge` 现在按老登录口径**懒推导** `userAreas`/`plNos`: `queryAreaListByOrgIds` → `sys_org_area`;`sys_code=1001` 取 `areaType=60` 走 `queryParkInfoByAreaBlockIds`,`sys_code=1002` 取 `areaType=40` 走 `queryParkListByPark`; 没有区域时 `plNos` 为空集合(不回退「空即可见」);`resIds`/`roleIds` 仍 null。 ★ 该类唯一端点只读登录 `plNos`,为空抛 `AUTH_TOKEN_NOT_EXISTS`(存量 2003), 本机 org 10003 无区域 ⇒ 冒烟断言 500/2003;正样本另用 `LEGACY_ORG_ID_MAPPING=1:10038` 验证(恰好 2 个车场,与 SQL 一致)。 ★ 本片零写:基线仍 **+234/轮**;新增只读表 `sys_org_area`、`sys_org` 已进零写监控。 ★★ **2026-10-19 第一百五十八切片搬走 ⚠B 的 1 个类 / 1 端点** (`RemoteControlDzController` 1/1) ⇒ 已迁 **211/1144 → 212/1145**、未迁 **8/11 → 7/10**、✔ **4/7** 不变、 ⚠B **4/4 → 3/3**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ 同片判定:`PersonPCCouponController` 读 `getUserInfoForPersonPC().getCustId()` ⇒ 个人客户登录桥(P3 ③)未落地,**不迁**;`AliyuncsEcsController` 虽只读 `orgId`, 但会带存量 AK/SK **真实外呼阿里云**(且 service 会把配置对象打进 info 日志) ⇒ 继续挂起,不做正测。 ★★ **2026-10-19 第一百五十七切片搬走 ⚠B 的 2 个类 / 8 端点** (`StoreCarManageController` 7 / `AppointmentManageController` 1) ⇒ 已迁 **209/1136 → 211/1144**、未迁 **10/19 → 8/11**、✔ **4/7** 不变、 ⚠B **6/12 → 4/4**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ 同片判定:`ParkLotStatisticController` 读登录 `plNos`(**不是** `orgId`)⇒ P3 未落地前不可搬。 ★★ **2026-10-19 第一百五十六切片搬走 ⚠B 的 2 个类 / 12 端点** (`OrderRefundController` 5 / `AccountRechargeStatisController` 7) ⇒ 已迁 **207/1124 → 209/1136**、未迁 **12/31 → 10/19**、✔ **4/7** 不变、 ⚠B **8/24 → 6/12**(⚠A 仍 0/0)、缺口 **8/55**、死代码 5。 ★ **2026-10-19 第一百五十四切片修正实现识别**(`extends Base implements X` 与 `RefundServiceIpml.java`) ⇒ 就绪 211/1118 → **219/1155**、缺口 16/92 → **8/55**、未迁 8/11 → **15/42**、 ✔ 4/7 → **6/15**、⚠ 4/4 → **⚠A 1/3 + ⚠B 8/24**。总数 232/227/5 不变。 ★ **2026-10-19 第一百六十五切片修正实现识别(第二刀)**:上一条只解决了「文件名拼错」, 仍按 `*Impl.java`/`*Ipml.java` 命名约定找实现 ⇒ **不叫 Impl 的实现类**一律漏判。 实证:`NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService`(桥接空实现) 被当成「无实现」,让**早已迁完**的 `GroundLockManageController` 6 端点一直挂在缺口队列。 现改为 glob 全部 `*.java`(`"implements" in src` 预过滤后再套 `IMPL_PAT`),与命名约定解耦 ⇒ 就绪 **221/1170 → 223/1179**、缺口 **6/40 → 4/31**、已迁 **217/1166 → 219/1175**。 ★★ **2026-10-19 第一百五十五切片把上片新进 ✔ 池/⚠A 池的 3 个类一次搬完** (`PersonPCAcctController` 3 / `UserRechargeRefundController` 5 / `AppUserAccountController` 3) ⇒ 已迁 **204/1113 → 207/1124**、未迁 **15/42 → 12/31**、✔ **6/15 → 4/7**、 ⚠A **1/3 → 0/0(再次清零)**、⚠B **8/24** 不变、缺口 **8/55**、死代码 5。 ★ **2026-10-16 第三十四切片:✔ 那 5 个已逐个读源码判定**(人工结论见下面 ADJUDICATED 表,脚本只做标注、不改机械判定) ⇒ ⏸ 4 个 / 10 端点(待定:共享密钥来源 1 个、个人客户登录桥 2 个、文件存储 1 个)、 ⛔ 1 个 / 1 端点不适用(HomeController 不是 JSON 接口) ⇒ **✔ 池当前 0 个可直接开搬,别按 4/7 排期。** ★ 2026-10-17 第四十六切片:`RefreshCacheController` 已按 (B) 配置化方案搬走 ⇒ ✔ 池从 5/11 降到 **4/7**。 ★★ **2026-10-16 第三十五切片:⚠ 那 124 个再切一刀**(`LEGACY_ID_PAT` 不知道该 `get*()` 打在谁身上, 把前端**请求体**一起抓了)⇒ **⚠A 12 个 / 61 端点**整类**没有一处**读登录态 = **纯机械误报**、 **⚠B 113 个 / 700 端点**才是真读了登录态的那批。A 组逐类核完命中行(全落在 `request.getPlNos()` / 局部入参 `req.getOrgId()` / 下游 DTO 上),结论见 `IDSPACE_FALSE_POSITIVE` 表; 其中 `EqpIncomeStatisticController`(3 端点)本片已搬 ⇒ **⚠A 余 11 个 / 58 端点**。 ★★ **2026-10-16 第三十六切片:⚠A 里再搬 2 个**(`BerthStatisticController` 2 端点、 `WxcpMessageController` 2 端点)⇒ **⚠A 余 9 个 / 54 端点**。本片的价值在于把 §3.3 「老 `@NoAuth` 分两类」**在同一批次里两侧都钉住**:berth 落 `/admin-api` + `@PreAuthorize` (无 token **401**),wxcp 落 `/app-api` + `@PermitAll` + 白名单登记(无 token **进业务**)。 ★★ **2026-10-16 第三十七切片:⚠A 里的 `IndexParkSituationController`(6 端点)搬走** ⇒ **⚠A 余 8 个 / 48 端点**。它一搬走,⚠A 的 6 端点整档就**自动从「未迁」里消失** (下面的 85/359 与 ⚠A 8/48 是**同一次移动的两个投影**,别当两件事重复计数)。 ★ 本片是「云平台首页」两个 tab 的**收尾**:兄弟类 `IndexParkIncomeController` 早在 批 6 第三十X 切片就搬了,本片把 `IndexParkSituationController` 补上 ⇒ 该页面在 `/admin-api` 下已无遗留。★ 坑 22 侧:老类 5 处命中**全是请求体**(3 处 `CollectionUtils.isEmpty(request.getPlNos())` + 2 处 `setPlNos(request.getPlNos())` 转投下游), 全类 **0 处** `getUserInfo(` / `SessionCommUtil` ⇒ ⚠A 机械误报,逐行核销见 `IDSPACE_FALSE_POSITIVE`。 ★★ **2026-10-16 第三十八切片:⚠A 里的 `IncomeCalculationController`(5 端点,4 读 + 1 导出)搬走** ⇒ **⚠A 余 7 个 / 43 端点**(`86/364` 与 `7/43` 同一次移动的两个投影)。 ★ 导出的 `@NoAuth` 按 §3.3 **第 1 类**(管理页面)收紧;★★ **本片是「坑 25」的第二处修点**—— 两条走 `PageHelper.startPage` 的 select 原是 `statementType="STATEMENT"`,老 PageHelper 5.0.0 拼字面量 `LIMIT 3` 能跑、新栈 6.1.1 拼 `LIMIT ?` 绑不上 ⇒ 真分页必 500;修法与回归守卫 见 §2ah 抬头 ① 与 `TIncomeCalculationStaticNumSMapper.xml` 的 `⚠️ 手改` 注释。 ★★ **2026-10-16 第三十九切片:⚠A 里的 `VehicleFlowController`(5 端点,3 读 + 2 导出)搬走** ⇒ **⚠A 余 6 个 / 38 端点**(`87/369` 与 `6/38` 同一次移动的两个投影)。 ★ 两条老 `@NoAuth` 都按 §3.3 **第 1 类**收紧(一条 `GET /export-excel`、一条 `GET /export-pdf`, 老 `carFlowPrint` 的 `@RequestMapping` **没写 method**);★ 本片**新建了 1 个老门户独有 VO** (`com.zteits.bcp.portal.vo.report.runtime.VehicleFlowVO`,只喂 PDF、不进响应体)—— ⚠️ 上两片那条"先看返回类型在不在新栈"的省力规律**要连 VO 一起数**,本片就漏了一个。 ★★ **2026-10-16 第四十切片:⚠A 里的 `IncomeAllStatisticController`(5 端点,5 读)搬走** ⇒ **⚠A 余 5 个 / 33 端点**(`88/374` 与 `5/33` 同一次移动的两个投影)。 ★ 本片**又新建了 3 个老门户独有 VO**(`com.zteits.bcp.portal.vo.report.income` 下 `IncomeStatisticTrentReportVO` / `IncomeStatistic2MonthFeeVO` / `IncomeStatisticDayFeeVO`)—— 第三十九切片留下那条"返回类型要连 VO 一起数"的教训本片照做了,**先数再开搬**。 ⚠️ **本片 5 个端点里 4 个在本机恒 500**,原因**不在迁移**:老 SQL(`IncomeFinanceBillSMapper.xml` 3 处**在本片路径上** + 同族的 `IncomeStatisticDaySMapper.xml` 1 处)写的是 `GROUP BY x DESC`,**这个语法在 MySQL 8.0.13 已被移除**, 本机 MySQL 是 8.x ⇒ 老栈同样 500。按红线⑥(存量缺陷照搬不修)**不动 SQL**,改为**离线桩测**证明Controller 逻辑等价 (剥 fat jar + `Proxy` 桩 + 反射注入,见 AGENTS.md 第四十切片 ④)。⇒ 这 4 条端点的"可交付"以 **目标库若不是 MySQL 8 才成立**为准,是**非阻塞决策项**,已上报。 ★★ **2026-10-16 第四十一切片:⚠A 里的 `OrderParkStatisticController`(4 端点,4 读)搬走** ⇒ **⚠A 余 4 个 / 29 端点**(`89/378` 与 `4/29` 同一次移动的两个投影)。 ★ 本片**一个新类都没新建**(4 个返回类型全在新栈)——"返回类型要连 VO 一起数"这条第 3 次生效。 ★ 老类 **3 条端点是大屏用 `@NoAuth`** ⇒ 按 §3.3 **第 1 类**(管理页面)收紧成 `/admin-api` + `@PreAuthorize('park:order-park-statistic:query')` ⇒ 4 条无 token 全 **401**。 ⚠️ **本片两条端点在本机恒空、但都不是迁移回归**:EP2 走的 SQL 打 `order_park.modfiy_date` (该表 `max = 2018-10-16`、`> yesterday` 的行 0 条);EP3/EP4 的表 `parking_running_statistic` 全量 500 行停在 `2026-01-31`,而窗口由代码**按今天算** ⇒ 恒空。 ★ 判定手法:**同一条 SQL 直连、手动换窗口**(`2026-01-24~01-31` 回 1 组 15188/15073) ⇒ 证明 SQL 活着,"空"只来自窗口。**先怀疑样本、再怀疑代码**。 ★★ **2026-10-16 第四十二切片:⚠A 里的 `ReconciliationManagementController`(7 端点,5 读 + 2 导出)搬走** ⇒ **⚠A 余 3 个 / 22 端点**(`90/385` 与 `3/22` 同一次移动的两个投影)。 ★ 本片**一个新类都没新建**(7 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 4 次生效。 ★ 老类 `extends ReconciliationBaseController`(140 行、只有一个 `getPayOrderTypeName(Integer,Integer)`、自己零映射)⇒ 它躺在本脚本的"5 个死代码"清单里,但**它是活基类,不是死代码**;新栈没有基类可继承 ⇒ 那段 switch **原地抄进新控制器**。 ★ 两条导出老 `@NoAuth` 按 §3.3 **第 1 类**(管理页面)收紧:一个类两条导出 ⇒ 主导出占 `GET /export-excel`、另一条落 `GET /export-detail-excel`(§3.3 第 3 条)。 ⚠️ **本片两条源表样本都停在过去、且采到的列组正好错开**:`td_b_pay_blncac_result` 的 `report_date` ∈ 2018-01-01~01-16 而 `pl_no` **500 行全是空串**(⇒ 传真 plNo 恒空);`pay_order` 全表停在 `pay_finish_time = 2017-09-25` 且 `pay_type = 6`(⇒ 写死 `pay_type IN (1,2,4,5)` 的 EP2 **恒全 0**)。**又一条「先怀疑样本」**。 ⚠️ 导出端点的日期是 `@RequestParam Date` ⇒ **只吃 JS `Date.toString()`**(`yyyy-MM-dd HH:mm:ss` → 400)——**第 4 种日期契约**,⛔ 别顺手统一。 ★★ **2026-10-16 第四十三切片:⚠A 里的 `BerthsStatisticController`(9 端点,5 读 + 4 导出)搬走** ⇒ **⚠A 余 2 个 / 13 端点**(`91/394` 与 `2/13` 同一次移动的两个投影)。 ⚠️ **别与 `BerthStatisticController`(第三十六切片已搬)混** —— 只差一个 `s`, 那个在新路由 `/park/berth-statistic`,本片这个在 `/park/berths-statistic`。 ★ 本片**一个新类都没新建**(9 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 5 次生效。 ★ 4 条导出**全是老 `@NoAuth`**(老前端 `unit.js:877~890` 逐条有入口)⇒ 按 §3.3 **第 1 类**收紧 ⇒ 9 条端点无 token 全 **401**;一个类 4 条导出 ⇒ 第 3 条命名:主导出占 `GET /export-excel`, 另三条落 `-detail` / `-month` / `-month-detail` 后缀。 ★★ **本片新增一条样本判据**:`parking_lot_use_statistic` 500 行**只有一行车场** (`A150402003`,且它**不在** `parking_lot` 里)⇒ 多车场断言全废;要验 N+1 回填车场名, 得另找 `parking_lot` 里有、统计表里没有的 `B1504020C7`。挑正样本前先 `select count(distinct <业务键>)`。 ★★ **两条"全小写连写"的老路由值 `getstatisticsummary` / `getstatisticlist` 逐字保留**—— kebab 只对驼峰切词,对全小写连写是恒等映射,加了连字符就回推不出来。 ★★ **2026-10-17 第四十四切片:⚠A 里的 `RealTimeMonitorController`(10 端点,8 读 + 1 导出 + 2 免认证读)搬走** ⇒ **⚠A 余 1 个 / 3 端点**(`92/404` 与 `1/3` 同一次移动的两个投影)。 ★★ **本片第一次把「一个老类 = 两个新类」落成事实**:本栈 URL 前缀由包路径决定 (`**.controller.admin.**` → `/admin-api`、`**.controller.app.**` → `/app-api`), 一个类不能两栖 ⇒ 管理端 8 条进 `park/real-time-monitor`(全 `@PreAuthorize`), 外部网站 2 条进 `/app-api/park/real-time-monitor` + `@PermitAll`(先例 `AppVersionCheck*`)。 ★ 老类 3 条 `@NoAuth` **不是同一类**:导出是第 1 类(老前端三处 `unit.js` 调用)、 两条按组织查车场是第 2 类(`git grep` 两个前端分支**都搜不到** + 老注释写明"黄石官网") ⇒ **口径更正**:第四十三切片 ⑫ 里"只有黄石那条才是第 2 类"的预判是错的,两条都是。 ★ 本片**零新建 VO**(10 个返回类型全在新栈)——"返回类型要连 VO 一起数"第 6 次生效。 ★ 第五种日期契约:导出是 `@RequestParam String` + hutool `DateUtil.parseDateTime`(受检异常)。 ★★ **本片新增一条缓存判据**:`getParkLotByPlNo` 会把"查不到"的空 DTO 写进 Redis hash `zteits-park:parking_lot:plNo_parkLotInfo`,之后 `QueryParkingLotByPkNos` 在 `BerthManageServiceImpl:123` 对 null `plNo` **NPE** ⇒ 500。本地 `tp_p_berths` 的 6 个 plNo **全部**在 `query-park-berth-occupancy-rate` 的 `{}` 返回里 ⇒ 冒烟里 ep5 只发带 `plNo` 的过滤调用, ep3 断言写成"缓存干净/被污染"两支(红线⑥ 不修)。 ★★ **2026-10-17 第四十五切片:⚠A 最后一个类 `VipCardQueryController`(3 端点)搬走 ⇒ ⚠A 清零(0 个 / 0 端点)** (`93/407` 与 `0/0` 同一次移动的两个投影)。★ 本片推翻了两条预测: ① 第四十三切片 ⑫ 说「§3.2 要另建 2 个老门户独有 VO」——**不用**:`VipCardStatisticIndexDTO` / `AuthCarNumberDTO` 本来就已在 `parking-module-api`,`VipCardStatisticFeeVO` 也早已随 `parking-legacy-common` 整包搬入(只是用不上); ② 更要紧的是**第 3 条端点的签名在说谎**:声明 `BizResultVO`(6 字段), 方法体却是裸 `new BizResultVO(incomeStatisticFeeVipCardDTOBizResult)` ⇒ 实际线格式是 **21 字段**的 `IncomeStatisticFeeVipCardDTO`,那个 VO **一次都没被 new 过**。 ⇒ 新契约按实际线格式声明;照签名搬会**静默丢 15 个字段**(编译期不报)。 **新判据:§3.2 数 VO 之前先看方法体 new 的是谁,别只看泛型声明。** ★ 本片**零新建 VO/DTO**("返回类型要连 VO 一起数"第 7 次生效,且这次连"要不要建"都省了)。 ★ 老类 3 条端点**都没有** `@NoAuth`、没有写端点、没有 `@SysOpLog` ⇒ `LEGACY_WRITES` 与 `system_operate_log` 的 +16 常量都不动。 ⚠️ 第 2 条端点老 `@RequestMapping` **没写 method**(老栈 GET/POST 都进),新栈吃 `@RequestBody` 一律 `@PostMapping` ⇒ 带 token GET 是 405(§2ao (d) 钉住)。 ★★ **2026-10-17 第四十六切片:✔ 池里的 `RefreshCacheController`(4 端点)搬走 ⇒ ✔ 池 5/11 → 4/7** (`94/411` 与 `4/7` 同一次移动的两个投影)。★ 本片按第三十四切片 ④ 的 **(B) 配置化**方案落地: 老类 4 条 `@NoAuth` + **写死的 32 位共享密钥**不搬(红线 11),改成 `parking.refresh-cache.token` 配置项(本地 profile 给测试值,生产不配即 fail-closed);调用方不登录 ⇒ §3.3 **第 2 类**: 包 `**.controller.app.**` → `/app-api` + `@PermitAll` + 注解自动进白名单。 ★ 一个**新行为**:`refresh-sys-cache?type=1` 会把 `sys_code_value` 缓存**热起来**(跨轮次生效), 于是 §2ak ep1 的首条图片会从 null 变成 `LZ_DZSP_*` 配置的 URL ⇒ 那条断言已改成「只有首条可能 非 null、值只能是 null 或 http URL」,两种缓存态都合法。 ★ 零新建 VO(4 个返回类型全是 `CommonResult`);`LEGACY_WRITES` 不加(4 条只写 Redis、 不写 DB,零写复查扫 51 张表只有 `system_operate_log` +16)。 ★★ **2026-10-17 第四十七切片:⚠B 里的 `ApiCallLogController`(1 端点)搬走,同时落地 P3 组织 id 换算表** ⇒ **⚠B 113/700 → 112/699**(`95/412` 与 `112/699` 同一次移动的两个投影)。 ★ 本片**不再「等 P3」**:`LoginUserBridge` 新增 `parking.legacy.org-id-mapping` (`新id:老id,...`,**默认空 = 恒等映射**、行为不变;本地 profile 配 `1:10003`), 把登录人的新 `system_org.id` 换算成存量业务表认的老 `sys_org.id`。 ★ 判据是**数据量而不是形状**:本接口在本地从 `total=0` 变成 `total=498` (`td_b_api_log` 里 org=10003 的行数);同时 `AreaController` 的区域树从 494 行变成 586 行(省市区 494 + 区块 92)——后者是**已迁端点**,说明这张表是全局生效的, 不是给单个 Controller 打补丁。 ⚠️ 仍然没打通的是 `plNos` / `userAreas` / `resIds`(桥依旧留 null),所以 ⚠B 里 **按这些字段过滤的类还不能直接开搬**;本片只解锁了「只读 orgId/orgIds」的那批。 ★★ **2026-10-17 第四十八切片:⚠B 里的 `SmsTemplateConfigController`(5 端点)搬走** ⇒ **⚠B 112/699 → 111/694、已迁 95/412 → 96/417、未迁 116/706 → 115/701**。 ★ 它是换算表落地后的**第二个**消费者(第一个是第四十七切片的 `ApiCallLogController`), 证明「只读 orgId/orgIds」这条解禁路径可复制:本类 5 条端点只读 `userInfo.getOrgId()`, 没有任何 `plNos`/`userAreas`/`resIds` 读取。 ★ 判据依旧是**数据量**:`/page {}` 在本地 `sms_template_config` 上回 **total=1** (org=10003 且 `data_status=1` 的正好 1 行);映射不生效时是 `org_id = 1` ⇒ total=0(不报错)。 ⚠️ 本类还有 4 个写端点,其中 `/delete-list` 的老 DAO 是 `return 0;` **桩**(点了从没删掉过任何行) ——红线⑥照搬,别说它「坏了」。 ★★ **2026-10-17 第四十九切片:⚠B 里的 `ParkGroupController`(6 端点)搬走** ⇒ **⚠B 111/694 → 110/688、已迁 96/417 → 97/423、未迁 115/701 → 114/695**。 ★ 它是换算表落地后的**第三个**消费者。`/page`、`/list` 只读 `userInfo.getOrgId()`, 4 个写端点只读 `userInfo.getOpId()`(写 `create_empid`/`modfiy_empid`), 没有任何 `plNos`/`userAreas`/`resIds` 读取。 ★ 判据依旧是**数据量**:`/page {}` 在本地 `parking_lot_group` 上回 **total=13** (org=10003 且 `data_status=1`、`park_num>0` 的正好 13 行);映射不生效时是 `org_id = 1` ⇒ total=0。 ⚠️ 老 smapper 的 `plGroupName` 分支**少一个 `and`** ⇒ 一传过滤条件就 SQL 语法错、恒 500 ——红线⑥照搬,别说它「坏了」。 ★★ **2026-10-17 第五十切片:⚠B 里的 `DiscountController`(7 端点)搬走** ⇒ **⚠B 110/688 → 109/681、已迁 97/423 → 98/430、未迁 114/695 → 113/688**。 ★ 它是换算表落地后的**第四个**消费者,也是**最新的一类解禁形态**: **整类不读 `orgId`**(按请求体 `plNos` 过滤),三个写端点只拿 `userInfo.getOpId()`/`getName()` 写审计列 ⇒ 连组织换算都不涉及。别再认为"⚠B 命中 = 必须等 P3 orgId 换算"。 ★ 判据是**过滤后的数据量**:`/page {}` 在本地 `t_activity_discount` 上回 **total=21** (表里 500 行,smapper 硬编码 `data_state=1` 只剩 21 行)——拿表行数当期望值会一跑就红。 ⚠️ 老 SQL 的 `disc_name like '%${discName}%'` 是**拼串**(注入面)、`queryParkName` 是 **N+1 且不判空**、`/create` 的规则列表**不判空就是 NPE** —— 红线⑥全部照搬。 ★★ **2026-10-17 第五十一切片:⚠B 里的 `TActivityAbcbankDiscountController`(5 端点)搬走** ⇒ **⚠B 109/681 → 108/676、已迁 98/430 → 99/435、未迁 113/688 → 112/683**。 ★ 它是换算表落地后的**第五个**消费者,也是最"轻"的一类:`/page`、`/query` 只读 `userInfo.getOrgId()`,三个写端点只拿 `userInfo.getOpId()`/`getName()` 写审计列, 没有 `plNos`/`userAreas`/`resIds` 读取。 ★ 判据同样是**过滤后的数据量**:`/page {}` 在本地 `t_activity_abcbank_discount` 上回 **total=1** (表里 15 行,`data_state=1 AND org_id=10003` 只剩 1 行 id=68);映射不生效是 `org_id=1` ⇒ 0 行。 ⚠️⚠️ 本片最危险的一条:`/create` 在调服务层**之前**先调 `setTActivityAbcbankDiscountFalse`, 那条 SQL **不带 `org_id`** ⇒ 会把**全库所有组织**的生效中配置(`disc_state in (0,1)`) 一起下架。冒烟**只能发空 body**(绑定层 400),⛔ 绝不能发 `{}`(会先执行这条 update、之后才 NPE)。 ⚠️ 另三处存量缺陷照搬:`disc_name like ?` **绑定不过滤通配符** ⇒ `discName=农行` 查不到 `农行充值`(用户得自己传 `%`)、`/query` 查不到时**服务层 NPE**、`/delete-list` **逐 id 先查后删** (N+1)且 id 不存在时先 NPE。 ★★ **2026-10-17 第五十二切片:⚠B 里的 `TActivityThirdDiscountController`(5 端点)搬走** ⇒ **⚠B 108/676 → 107/671、已迁 99/435 → 100/440、未迁 112/683 → 111/678**。 ★ 它与第五十一切片是同一包(`discount/`)的孪生类,也是最"轻"的一类:`/page`、`/query` 只读 `orgId`,`/update` **连 `orgId` 都不 set**,三个写端点只拿 `opId`/`getName()` 写审计列。 ★ 判据同样是**过滤后的数据量**:`/page {}` 在本地 `t_activity_third_discount` 上回 **total=3** (表里 9 行,`data_state=1 AND org_id=10003` 正好 3 行 id=7/8/9);映射不生效是 `org_id=1` ⇒ 0 行。 ★ 与孪生类**行为不同的两处**(别按上个类抄断言):`/query` 查不到回 **code=0、data=null** (服务层把 null 装进 BizResult,`getDataOrThrow` 允许透传,**不 NPE**)、`/delete-list` 的 id 不存在回 **data=false**(逻辑删 0 行,**不 NPE**)。 ⚠️ `/create` 的 `{}` 会**真插一行**(`disc_no` 由 `SequenceUtil` 生成;本类**没有**全库下架那一步) ⇒ 冒烟只能发空 body(绑定层 400),⛔ 绝不能发 `{}`。 ⚠️ 与孪生类相同的坑:`andDiscNameLike` **不补 `%`**(`农行` → 0 行、`农行%` → 2 行)、 `PageUtil.pageSizeValidate` 校验 1..1000。 ★★ **2026-10-17 第五十三切片:⚠B 里的 `TpPNetworkCardController`(11 端点)搬走** ⇒ **⚠B 107/671 → 106/660、已迁 100/440 → 101/451、未迁 111/678 → 110/667**。 ★ 本类落在 `park` 域(`POST /admin-api/park/network-card/*`),7 读 + 4 写、**无导出**; 读端点只读 `orgId` **且带超管豁免**(超管不覆盖请求体 `orgId`),写端点只拿 `opId`/`getName()`。 `/list` **完全不读登录态**(老 DAO 是 `//TODO` + `return null` ⇒ 恒空)。 ★ 判据:`/page {}` 在本地 `tp_p_network_card` 上回 **total=500** (表里 500 行,全部 `data_state=1 AND org_id=10003`);映射不生效是 `org_id=1` ⇒ 0 行。 ★ **本类没有 `PageUtil.pageSizeValidate`** ⇒ `pageSize=0` 不分页(回全量 500 行)、`1001` 照过 —— 别把折扣类的「0 → 1002 / 1001 → 500」断言抄过来。 ★ 存量缺陷照搬:`/delete` 与 `/delete-list` **都不置 `data_state=0`**(只改审计列); `/statistic` 查不到回 **code=0、data=null**;`/query-eqp-for-page {}` 与 `/query-pda-for-page {eqpType:…}` 都因 tk `and*EqualTo(null)` 抛异常 ⇒ **500**。 ⚠️ `/create` 的 `{}` 会**真插一行**(`insertSelective`,`create_date` 由 DAO 补 now) ⇒ 冒烟只能发空 body(绑定层 400),⛔ 绝不能发 `{}`。 ★★ **2026-10-17 第五十四切片:⚠B 里的 `ParkingBindingChargingController`(10 端点)搬走** ⇒ **⚠B 106/660 → 105/650、已迁 101/451 → 102/461、未迁 110/667 → 109/657**。 ★ 落在 `bill` 域(`POST /admin-api/bill/parking-binding-charging/*`),7 读 + 3 写、**无导出**、 **无 `@NoAuth`、无 `@SysOpLog`** ⇒ 新类没有 `@OperateLog`。 ★ 整类只有 2 处读登录态(`queryParkingBindingChargingRules`、`initCloseParkingBindTree`), 都是**无条件覆写 `orgId`、没有超管豁免**(与第五十三切片网络卡类**相反**); `orgId` 经 `parking.legacy.org-id-mapping` 换算 ⇒ `areas {plNo=P15040214Z}` 回 1 行即映射生效。 ★ **本类两个分页端点都有 `PageUtil.pageSizeValidate`**(min=1/max=1000)⇒ `pageSize=0`/`1001` 都 500。 ★ 本地恒 500 的四条端点**不是回归**:`query-parking-binding-charging-rules` 与 `query-binding-rules-list` 撞 MySQL 8 `only_full_group_by`(老 SQL `GROUP BY r.pl_no` 却 select 非聚合列); `areas {}` 撞 Druid wall `where 1=1`;`areas {plNo=不存在}` 空结果拼出 `in ()` 撞 wall。 ★ 空结果语义两分:`areas {plNo=NOPE}` **500**、`query-binding-area-rules-list {plNo=NOPE}` 回 **`[]`**。 ★ `initCloseParkingBindTree` 必须调 **park 模块**的同名服务(bill 服务接口上那个实现 `return null`)。 ⚠️ 3 个写端点:`/save` 的 `{}` NPE、`/update` 缺 `areaId`、`/delete` 缺 `plNo` 都在 DAO 前 500; **`/delete` 是物理删除**(`deleteByExample`)、`/update` 不存在则 `insertSelective` ⇒ ⛔ 冒烟只能发空 body(400),绝不能发带真实 `areaId`/`plNo`/`parkDtoList` 的 body。 ★★ **2026-10-17 第五十五切片:⚠B 里的 `TbAreaController`(2 端点)搬走** ⇒ **⚠B 105/650 → 104/648、已迁 102/461 → 103/463、未迁 109/657 → 108/655**。 ★ 落在 `park` 域(`POST /admin-api/park/tb-area/*`),**全只读**、无导出、无 `@NoAuth`、 无 `@SysOpLog` ⇒ 新类没有 `@OperateLog`。同目录 `AreaManageController`/`RegionAreaController` 早已迁完,本类是「区域管理」页的**最后一块**。 ★ `/query-next-lower-lever-by-id` **不查库**:数据源是 Redis 字典 (`GOV_CLOUDS_LBS_AREA_` + cityId,`SysCodeValueCacheUtil`),`cityId` 为空先抛 `PARAMM_NULL「区域ID」`;字典里没有该城市 ⇒ **code=0 + `[]`**(不是 500)。 ★ `/get-berth-area-tree` 读 `orgId` 且**无条件覆写、没有超管豁免**;`orgId` 经 `parking.legacy.org-id-mapping` 换算 ⇒ 本地回 588 行(32 省 + 342 市 + 120 区 + **92 区块** + 2 停车场),92 个区块即映射生效的判据。本地这 2 个车场没有泊位区间样本 ⇒ **无 areaType=1000 行**(数据事实,不是代码丢段)。 ★ `TpPRegionAreaService#queryTpPRegionAreaForPage` 接口声明 `throws Exception` ⇒ 控制器方法必须保留 `throws Exception`(同 `RegionAreaController`)。 ★★ **2026-10-17 第五十六切片:⚠B 里的 `CarBreakRuleController`(3 端点)搬走** ⇒ **⚠B 104/648 → 103/645、已迁 103/463 → 104/466、未迁 108/655 → 107/652**。 ★ 落在 `park` 域(`POST /admin-api/park/car-break-rule/*`),1 读 + 2 写、**无导出**; 读端点**无条件覆写 `orgId`、没有超管豁免**(同第五十四/五十五切片),写端点只拿 `opId`/`name` 写审计列;两个写端点老类都有 `@SysOpLog` ⇒ 新类有 `@OperateLog`。 ★ 读端点 **不带 `plNos` 时老 DAO 走 `andPlNoIsNull()`** ⇒ `{}` 只回「未挂车场」的记录 (本地 `org_id=10003` 有效行 52、其中 `pl_no IS NULL` **35** 行;显式 `plNos` 才查得到另 17 行)。 ★ 两条写端点在控制器校验关口就 500(「主键信息」/「审核状态」),**服务层都碰不到**; ⛔ 冒烟只能发空 body 或这些炸关口样本,真 `id`/`ids` 会插审核记录或把 `upload_type` 标成 1。 ★★ **2026-10-17 第五十七切片:⚠B 里的 `TBillingDescriptionController`(3 端点)搬走** ⇒ **⚠B 103/645 → 102/642、已迁 104/466 → 105/469、未迁 107/652 → 106/649**。 ★ 落在 `bill` 域(`POST /admin-api/bill/billing-description/*`),2 读 + 1 写、**无导出**; 两个读端点**完全不读登录态**,写端点只拿 `opId`/`name`/`orgId` 写审计/组织列、不参与过滤; 只有写端点老类有 `@SysOpLog` ⇒ 新类只在该端点带 `@OperateLog`。 ★ 按计费集合查不到时返回**空 DTO 且 code=0**(不是 null、不是 500)。 ★ 按车场查在本机**恒 500「系统异常」**:存量 join SQL 撞两表 **collation 不一致** (`t_billing_rule_collect` = utf8mb4_unicode_ci,另两张 = utf8mb4_general_ci), MySQL 回 **1267**;直连同一条 SQL 同样报错 ⇒ 非迁移回归,按红线⑥不改 SQL/库。 ★ ⛔ `/save-or-update` 只能发空 body 或炸服务层校验关口的样本(缺 `ruleCollectNo` / 缺 `descText`); 真发两者会把同集合旧描述置失效、插新描述,并清空两个 Redis key。 ★★ **2026-10-17 第五十八切片:⚠B 里的 `DutyRosterGroupController`(4 端点)搬走** ⇒ **⚠B 102/642 → 101/638、已迁 105/469 → 106/473、未迁 106/649 → 105/645**。 ★ 落在 `park` 域(`POST /admin-api/park/duty-roster-group/*`),1 读 + 3 写、**无导出**; 查询/修改/删除三个端点**完全不读登录态**(`orgId`/`sOpId` 都来自请求体), 只有保存端点读 `opId`(审计列)与 `orgId`(新行组织列 + 按计划物理删的过滤条件)。 ★ 保存端点的顺序是「校验列表非空 → 按 `orgId+planId+dutyType` **物理删** → 逐条插」; 列表为空时 1003 在删除**之前**抛出 ⇒ 冒烟只发列表为空的样本。 ★ 修改 `{}` 返回 `code=0 + false`(`where id = null` 命中 0 行); ⛔ 本库**真有一行 `id=0`**,冒烟绝不能发 `id=0`(会改它的 `modify_time`)。 ★ 删除是逻辑删(`data_status=0`),`ids` 为空 → 1003「值班班次ids」。 ★★ **2026-10-18 第五十九切片:⚠B 里的 `ChargerAssessTargetController`(4 端点)搬走** ⇒ **⚠B 101/638 → 100/634、已迁 106/473 → 107/477、未迁 105/645 → 104/641**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-assess-target/*`),1 读 + 3 写、**无导出**; **四个端点全部读登录态**:查询把登录 `orgId` **无条件**覆盖请求体并作为 SQL 过滤条件 (`andOrgIdEqualTo`,**无超管豁免**),保存/修改/删除只拿 `opId`/`name` 写审计列。 ★ ★ 删除端点的老拼写是 **`bath-`**(少一个 c),路由照抄,别"修正"成 `batch-`。 ★ `targetNo` 的自动生成在 DAO 里是**注释掉的**(「考核指标不生成,在码表里配置」); 修改缺 id → 控制器 1003「id为空!」;删除 `ids` 为空 → 1003「id为空!」。 ★ ⛔ 冒烟写样本只用「空白 targetNo / 不存在 id / 空 ids」;非空 targetNo 会真插行。 ★★ **2026-10-18 第六十切片:⚠B 里的 `ChargerAssessRuleController`(4 端点)搬走** ⇒ **⚠B 100/634 → 99/630、已迁 107/477 → 108/481、未迁 104/641 → 103/637**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-assess-rule/*`),1 读 + 3 写、**无导出**; 查询把登录 `orgId` **无条件**覆盖请求体并作为 SQL 过滤条件(**无超管豁免**), 保存/修改/删除只拿 `opId`/`name` 写审计列。 ★ 与兄弟类 `ChargerAssessTargetController` 的三处不同:查询**有 `order by id asc`**; 请求体是**列表包装类**(`chargerAssessRuleRequests`,空/null 被 `ListCopyUtil` 安全吞掉 ⇒ `false`); 修改的 id 判空在**服务层循环内**(`{}`→false、`[{}]`→1003)。 ★ `ruleNo` 由 DAO `queryNextRuleNoByOrgId` **自动生成**(默认 `R1000001`,存量 N+1); 删除端点老拼写是 **`bath-`**(单数 rule);删除是逻辑删(`data_state=0`)。 ★ ⛔ 冒烟写样本只用「空列表 / 缺 id / 不存在 id / 空 ids」;非空列表会真插行。 ★★ **2026-10-18 第六十一切片:⚠B 里的 `ChargerAssessTemplateController`(5 端点)搬走** ⇒ **⚠B 99/630 → 98/625、已迁 108/481 → 109/486、未迁 103/637 → 102/632**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-assess-template/*`),2 读 + 3 写、**无导出**; 老类里 `/saveChargerAssessTemplateRel` **整段被注释** ⇒ 只迁 5 条。 ★ 两个读端点把登录 `orgId` 用于主表 WHERE 与配置过滤(**无超管豁免**); `by-id` **不**按组织/`data_state` 过滤主表,查不到回全 null DTO(不是 null、不是 500)。 ★ 保存先插主表再校验配置列表(空列表 1003;`@Transactional` 实测回滚); 修改 id 判空在**控制器**;不存在的 id 回 1002「保存模板信息错误!」。 ★ 删除的 `modify_emp_name` 因控制器 `setOpName` 与服务层 `getsOpName` **字段错位恒为 null** —— 存量缺陷照搬。 ★ ⛔ 冒烟写样本只用「空 body / `{}`(缺 id)/ 不存在 id / 空 ids」;非空配置列表会真插行。 ★★ **2026-10-18 第六十二切片:⚠B 里的 `ChargerAssessController`(5 端点)搬走** ⇒ **⚠B 98/625 → 97/620、已迁 109/486 → 110/491、未迁 102/632 → 101/627**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-assess/*`),**5 端点全部只读**、无导出; 老类无 `@NoAuth`、无 `@SysOpLog` ⇒ 新类**不挂** `@OperateLog`,冒烟跑一轮审计仍只 +59。 ★ 五个端点都把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE,**无超管豁免** (请求体传 `orgId=10007`,结果仍是登录组织 `10003` 的)。 ★ 结果分页的必填项:`beginTime`/`endTime` 缺 → 1003;`templateNo` 缺 → tk.mybatis Example 抛 `Value for templateNo cannot be null` → 500「系统异常」; `chargerCodes` 缺 → 服务层 for 循环 **NPE** → 500「系统异常」(存量缺陷照搬)。 ★ 模板配置查询**只看配置表自身 `data_state=1`**,不看模板主表 `data_state` (主表 `data_state=0` 的 `M100000001` 仍出 1 条配置)。 ★ ⚠ 本机 MySQL 8 上结果分页**恒 500**:存量聚合 SQL `GROUP BY charger_code,org_id` 却 select `charger_name`/`person_type`,撞 `only_full_group_by`;**老栈同 SQL 同样 500** ⇒ 不是迁移回归,按红线⑥不改。 ★★ **2026-10-18 第六十三切片:⚠B 里的 `ChargerAssessResultController`(7 端点)搬走** ⇒ **⚠B 97/620 → 96/613、已迁 110/491 → 111/498、未迁 101/627 → 100/620**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-assess-result/*` + `GET /export-excel`), **2 读 + 4 写 + 1 导出**;老类 6 条 POST 都有 `@SysOpLog` ⇒ 4 个写端点带 `@OperateLog`。 ★ 两个读端点把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE,**无超管豁免** (传 `orgId=10007` 仍回登录组织 `10003` 的 18 / 206 条)。 ★★ **导出是唯一例外**:老 `@NoAuth`,`orgId` 由前端 query 参数传入并原样使用, **不覆盖**;缺 `orgId` → 500「系统异常」(Example 拒 null),`orgId=10007` → 1002, 证明真按传入值查。按 §3.3 第 1 类收敛为需登录 + `park:charger-assess-result:export`。 ★ 明细分页 `recordNo` 必填(服务层 1003「记录编码」),`order_num` 升序; 结果分页 `id desc`、`pageSize=0` 回全部(18 行)。 ★ 写端点校验顺序:`save` 先插主表再校验(缺开始时间 1003、`@Transactional` 回滚); `update` 控制器先判 id(1003「id为空!」)、服务层判审核状态/记录编码后**先插日志再改主表**; `bathDelete` 不存在的 id 回 **`code=0 + data=false`**(不是错误码); `updateDetail` 不存在 id 回 1002「…**为**查询到相关记录」(错别字照搬)。 ★ ⚠ 本机没有 `charger_assess_result_log` 表 ⇒ `update` 恒 500「系统异常」 (本地 schema 漂移、老栈同依赖);`save` 的聚合仍撞 MySQL 8 `only_full_group_by`。 ★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在 id / 空 ids」。 ★★ **2026-10-18 第六十四切片:⚠B 里的 `TdCCustSuggestDetailController`(7 端点)搬走** ⇒ **⚠B 96/613 → 95/606、已迁 111/498 → 112/505、未迁 100/620 → 99/613**。 ★ 落在 `pay` 域(`POST /admin-api/pay/cust-suggest-detail/*`),**3 读 + 4 写**、无导出; 老类 4 条写都有 `@SysOpLog` ⇒ 新类带 `@OperateLog`,3 条读不带。 ★ **只有建议分页读登录组织,且有超管豁免**(`SUPER_ORG_ID` 时**不**覆盖、看全部); 两个回复读端点**完全不读登录态**。本机 `td_c_cust_suggest.org_id` 全 NULL + 登录组织覆盖 ⇒ 建议分页 6 种入参都 `total=0`。 ★ **回复分页是存量死读**(服务层直接返回空 `PageBean`、DAO 实现整段注释)⇒ 恒 `total=0`,照搬。 ★ `save` 先插回复再置主表 `is_answer=1` 再推极光;`suggestId` 空的 1003 在写审计字段**之后**, 且 `@Transactional` 内抛 `BizException` ⇒ `@OperateLog` **不记账**。 ★ `update` 只写 `sOpId`,空 SET 段被 druid 判语法错 ⇒ 空 body/id 不存在都 500。 ★ `bath-delete` 两条 500:`{}` → `Value for id cannot be null`、`{ids:[]}` → `id in ()` 语法错; `{ids:[不存在]}` → `code=0 + data=false`。路由拼写是 **`bath-`**(照抄)。 ★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在 id / 空 ids」。 ★★ **2026-10-18 第六十五切片:⚠B 里的 `PdaManageMentController`(12 端点)搬走** ⇒ **⚠B 95/606 → 94/594、已迁 112/505 → 113/517、未迁 99/613 → 98/601**。 ★ 落在 `park` 域(`POST /admin-api/park/pda/*` + 3 条 `GET ...excel`), **4 读 + 5 写 + 3 导出**;3 条导出与 1 条导入老类 `@NoAuth`。 ★ 老类 4 条写有 `@SysOpLog` ⇒ 新类带 `@OperateLog`;`import` 不带。 ★ 两个分页读端点的登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**; 本机按 `orgId` 过滤不缩小结果(229 → 229)。统计端点登录 null 时回退 `orgId=99999`。 ★ 存量缺陷照搬:`/export-berth-info-excel` 判的是刚 `new` 出来的空 `ArrayList` ⇒ **恒空文件**;`/query-passedca4pabe` 在本地因 `NID`(32 位 hex)映射 `Integer` 抛异常被 `catch` 吞掉 ⇒ `data=null`;`/import-pda` 的 `list` 初值为 null 后直接 `for` ⇒ NPE。 ★ `insert-pda` 的 `{}` 会**真插一行**(全列可空)⇒ 冒烟只能用已存在 `pdaNo`; `update {}` → 1007 / 空 SET、`delete {}`/`{ids:[]}`、`batch-delete {}`/`{padList:[]}` 均 500。 ★ `updateByPrimaryKeySelective` 空 SET 段、`andIdIn(null)`、空 `IN ()` 都是存量行为,不修。 ★ ⛔ 冒烟写样本只用「已存在 `pdaNo`(回 3003)/ 空 body / 不存在 id / 空 ids」。 ★★ **2026-10-18 第六十六切片:⚠B 里的 `ChargerManageController`(32 端点)搬走** ⇒ **⚠B 94/594 → 93/561、已迁 113/517 → 114/550、未迁 98/601 → 97/568**。 ★ 落在 `park` 域(`POST /admin-api/park/charger/*` + 3 条 `GET ...export...`), **16 读 + 13 写 + 3 导出**;3 条导出老类 `@NoAuth` 全按 §3.3 第 1 类收紧。 ★ 老类 33 个端点,本片只迁 32:`upload-employee-head-pic` 走 FastDFS/存储后端, 等 `FileClient` 抽象拍板后再补。脚本按整类计 ⇒ 机械计数 **+33** 而实际迁 **32**。 ★ 11 个写端点老类有 `@SysOpLog` ⇒ 新类带 `@OperateLog`; `update-bind-regions`、`get-employee-tail` 不带。 ★ 分页/签到类读端点的登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**; `queryEmployerByPlNo`/`queryInspectSituationForPage`/`querySignRecordByChargerCode`/ 3 导出/`getEmployeeTail` **完全不读登录态**。 ★ 存量缺陷照搬:老 GPS 查询/导出**恒 500**(存量 SQL 末尾 `WHERE 1=1` 撞新栈 druid `wall`;老 portal 无 wall);`/query-lot-region-tree` 无「事」节点时 `rootDTO.getChildren()` 回 **null** ⇒ `data=null` 不是 `[]`;`resetpwd {}` 与 `acct-status {不存在编码}` 均回 `true`(0 行也算成功);`getEmployeeTail` 只回最后一次 循环的 `BizResult`。 ★ `insert {}`/`update {}` 是 **400**(`@Valid`),不写审计;其余写端点的 `{}` 多落在 服务层 NPE / tk.mapper null 守卫 ⇒ 500。 ★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 不存在编码 / 空列表」。 ★★ **2026-10-18 第六十七切片:⚠B 里的 `DutyRosterController`(25 端点)搬走** ⇒ **⚠B 93/561 → 92/536、已迁 114/550 → 115/575、未迁 97/568 → 96/543**。 ★ 落在 `park` 域(`POST /admin-api/park/duty-roster/*` + 1 条 `GET ...-list`), **12 读 + 12 写 + 1 导出**;导出老类 `@NoAuth` 按 §3.3 第 1 类收紧。 ★ 12 个读端点把登录 `orgId` **无条件覆盖**进请求体并用于 WHERE、**无超管豁免**; `queryDutyRosterPlanForPage` 覆盖的是 `List orgId`。 `updateDutyRoster`/`exchangeDutyRoster`/两个验证码端点/导出**完全不读登录态**。 ★ 4 个写端点老类有 `@SysOpLog` ⇒ 新类带 `@OperateLog` (保存调休记录 / 删除排班计划 / 正式提交排班计划 / 调班)。 ★ 存量缺陷照搬:`queryNoDutyRosterParkTree` 老实现只 set `orgId/sysCode/plType`、 **从不 set `plNos`**,而底层 `queryParkLotByOrgIdAndPlNos(orgId, plNos, plType)` 三参版要求 `plNos` 非空 ⇒ **恒 1003「停车场编号」**(老栈同样炸); `deleteParkByPlanIdAndPlNos {}` 未判空,直落服务层 ⇒ **500「系统异常」**。 ★ `queryDutyPersonNumAndRecessNum` 老类 `@Validated` ⇒ 新类 `@Valid`,`{}` 是 **400**。 ★ `generatorDutyRosterPlanId` 不收 body(空 body 回 code=0),**不能**进 `LEGACY_WRITES`。 ★ ⛔ 冒烟写样本只用「空 body / `{}`(缺必填)/ 假验证码」:**绝不发真实手机号** (`sendVerifyCodeByPhoneNumber` 会真发短信)。 ★ POI 4.1.2 已移除 `XSSFCellStyle.ALIGN_CENTER`/`XSSFCell.CELL_TYPE_STRING` 等 int 常量, 照搬存量导出必须先换 `HorizontalAlignment`/`VerticalAlignment`/`BorderStyle`/`CellType`。 ★★ **2026-10-18 第六十八切片:⚠B 里的 `TemporarySettlementController`(18 端点)搬走** ⇒ **⚠B 92/536 → 91/518、已迁 115/575 → 116/593、未迁 96/543 → 95/525**。 ★ 落在 `order` 域(`POST /admin-api/order/temporary-settlement/*` + 2 条 `GET …excel`), **12 读 + 4 写 + 2 导出**;老路由 `/TemporarySettlement`, 老类在 `financialmanagement/clearsettlement/`。 ★ 登录态只在 8 处把**当前登录人组织**写进 `fromOrgId`/`toOrgIds`(打款方/收款方), **无 `plNos`/`userAreas`/`resIds`、无超管豁免**;`querySettlementRecordByStatusForOrgId`/ `querySettlementRecordListBySettleNO`/`querySettlementInfoByNo` 与 2 条导出**完全不读登录态**。 ★ 3 个写端点老类有 `@SysOpLog` ⇒ `@OperateLog`(`confirmReceiveMoney` 没有); 第 4 处 `@SysOpLog` 挂在**读端点** `querySettlementRecordSumforOrgId` 上, 按第六十三切片口径新类**不挂**。 ★ 两条导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `GET` + `order:temporary-settlement:export`。 ★ 存量缺陷照搬:`orgIds` 为空时把 `new ArrayList<>()` 塞回请求体(**不是**加 orgId,恒查空集合); `Boolean flag` 直接拆箱 ⇒ 缺 `flag` 是 **500 NPE**(`fee-summary`/`list-page`); 导出 `getData().getDataList()` 无判空;`fenChangeToyuan` 用 `Double.toString()` (`100`→`"1.0"`);主导出 `new BaseInfo(1, 0)` 吃全局 `pagehelper.page-size-zero: true`。 ★ ⛔ `update-settlement-record-check-status-by-org-id-and-time` 只要过校验就**必插一行 `td_b_settlement_serial`**(0 行可改也插)⇒ 冒烟只发 `{}` 与空 body。 ★ 导出日期走 `com.clouds.common.utils.DateUtil.strDate`(严格 `yyyy-MM-dd HH:mm:ss`), 只给 `yyyy-MM-dd` 是 **500「系统异常」**,不是 400。 ★★ **2026-10-18 第六十九切片:⚠B 里的 `ChargerAuditController`(13 端点)搬走** ⇒ **⚠B 91/518 → 90/505、已迁 116/593 → 117/606、未迁 95/525 → 94/512**。 ★ 落在 `park` 域(`POST /admin-api/park/charger-audit/*` + 4 条 `GET …excel`), **9 读 + 0 写 + 4 导出**;老路由 `/pda/input`, 老类在 `operationsmanage/chargemanage/`。**零写**、无 `@SysOpLog` ⇒ 无 `@OperateLog`。 ★ 登录态只出现在 4 个收入报表端点:把**当前登录人组织** `orgId` **无条件覆盖**进请求体 (无超管豁免);前 5 个稽核读端点与 4 条导出**完全不读登录态**。 ★ 4 条导出老 `@NoAuth`(任何人可下载全部收费员/收益数据)按 §3.3 第 1 类收紧为 需登录 + `park:charger-audit:export`;一个 Controller 四个导出 ⇒ 主导出 `/export-excel`(两张 sheet:收费员收益稽核 + 停车场收益稽核),其余 `/export-input-nums-excel`、`/export-report-time-excel`、`/export-dy-excel`。 ★ 5 个稽核读端点在 DAO 层**无条件拆箱** `inOutState`(`queryEqpAndPdaTimesForLine` 另拆 `timeType`)⇒ 带时间但缺这两个字段是 **500「系统异常」**;冒烟正样本都带上。 ★ 存量缺陷照搬:收入报表 3 条列表 + 主导出/大元导出在本机撞 **MySQL 8 `only_full_group_by`**(smapper `GROUP BY charger_code, pl_no, ...` 却 SELECT `charger_name`),**两栈同坏、不改 SQL**(红线⑥),冒烟断言 500; `queryChargerIncomeStatisticTotal` 的 SQL 不分组 ⇒ 本机 code=0 可正向。 ★ `getAreaName` 在停车场 sheet 里是**每行一次查询的 N+1**,按红线⑥照搬并加注释。 ★★ **2026-10-18 第七十切片:⚠B 里的 `SubCardInfoManageController`(9 端点)搬走** ⇒ **⚠B 90/505 → 89/496、已迁 117/606 → 118/615、未迁 94/512 → 93/503**。 ★ 落在 `bill` 域(`POST /admin-api/bill/sub-card-info-manage/*`), **5 读 + 4 写 + 0 导出**;老路由 `/subCardInfoManage`, 老类在 `parkinglotcloudplatform/closecarpark/`。 **无 `@NoAuth`、无导出、无 `@SysOpLog`** ⇒ 新类无 `@OperateLog`。 ★ 登录态只出现在 `query` / `save` / `check-sub-custmoer-bind` 三条: 「`userInfo != null && userInfo.getOrgId() != null` 才覆盖」、**无超管豁免**; 其余 6 条**完全不读登录态**(无 `plNos`/`userAreas`/`resIds`)。 ★ 存量缺陷照搬(新栈 DAO 原本就这样):`{}` 打在 update/delete/check/view/ viewCar/flow 全是 **500「系统异常」**(tk.mybatis 条件值 null 抛 IllegalArgumentException / 查回 null 后继续解引用);只有 `query {}` 回空页; `query` 的 `pageSize` 走 `PageUtil.pageSizeValidate` ⇒ 0/1001 是 500 存量错误码 1002。 ★ ⛔ **两个写端点绝不能用 `{}`**:`save {}` 会在事务内**真插 3 张表**; `save-sub-flow-record {}` **先插一行 `td_cl_sub_flow_record`**、再在 `chargeType` 拆箱处 NPE,而该 inner 方法**没有 `@Transactional`** ⇒ 不回滚。 冒烟只用**无 body**(绑定层 400),四张表行数已实测不变。 ★★ **2026-10-18 第七十一切片:⚠B 里的 `VipCardAuthPersonBjxcController`(12 端点)搬走** ⇒ **⚠B 89/496 → 88/484、已迁 118/615 → 119/627、未迁 93/503 → 92/491**。 ★ 落在 `order` 域(`POST /admin-api/order/vip-card-auth-person-bjxc/*` + 2 条 `GET …excel`), **4 读/导入(3 读 + 1 导入)+ 6 写 + 2 导出**;老路由 `/vipCardAuthPersonBjxcManage`, 老类在 `vipCardAuthPersonBjxc/`(北京西城「一位多车卡」授权人)。 ★ 登录态出现在 8 条端点(9 处调用,`batch-inset` 里两处),全部只覆盖 `orgId` 或写审计列 (`sOpId`/`createEmpid`/`modfiyEmpid`/`opId`/`opName`), **无 `plNos`/`userAreas`/`resIds`**;另 4 条(`query-cust-invoice-info-by-id`、两个导出、导入)完全不读登录态 (`export-excel` 连组织过滤都不加,导出的是全部授权人)。 ★ 三个老 `@NoAuth` 端点按 §3.3 收紧:两个导出 → `:export`(主 `GET /export-excel`、 次 `GET /export-invoice-excel`,26 列)、导入 → `:import`(multipart, `POST /batch-import-bjxc-vipcard-info`,与 `OrderModifyController#read-excel` 同款)。 ★ 唯一的 `@SysOpLog`(`batchInsetBjxcVIPcardList`「优惠月卡-批量保存」) ⇒ `@OperateLog(create, 优惠月卡, 批量保存)`。 ★ **本地 schema 缺口(新旧两栈 mapper XML 逐字相同,不是迁移引入)**: `VipCardAuthPersonBjxcMapper.xml` select 带 `examine_state`/`examine_date`/`picture_path`, 而本机 `vip_card_auth_person_bjxc` 只建到 `invoice_apply_time` ⇒ `selectByExample`/ `selectByPrimaryKey` 恒 `BadSqlGrammarException` ⇒ `query-pay-order-for-vip-card-invoicebjxc`、 `export-invoice-excel`、`examine` 三条**本机恒 500「系统异常」**;分页查询走另一条 SQL,正常。 ★ 存量缺陷照搬:`save {}` 的 `vipAuthType` 拆箱 NPE;`examine {}` 的 `getExamineState()+""` 得到字符串 `"null"`(非空串)⇒ 校验放行后拆箱 NPE; `batchInsetBjxcVIPcardList` 的 `list.size()` 在空判**之前**(`{}` NPE、`[]` 才是 1003「数据为空」); `ExcelReadUtil.readUploadExl` 对假文件**不抛 IOException** 而是回空表 ⇒ 导入的 `BIZ_ERROR` 分支实际走不到(冒烟断言 `code=0 data=null`)。 ★ ⛔ **写端点绝不能发业务形状**:`save {vipAuthType:1}` 且手机号不存在时会 **真插一条 `td_c_cust_person`**。冒烟只用**无 body**(绑定层 400)与上述炸关口样本, `vip_card_auth_person_bjxc`、`td_c_cust_invoiceinfo` 行数已实测不变。 ★★ **2026-10-18 第七十二切片:⚠B 里的 `GeomagneticManagementController`(10 端点)搬走** ⇒ **⚠B 88/484 → 87/474、已迁 119/627 → 120/637、未迁 92/491 → 91/481**。 ★ 落在 `park` 域(`POST /admin-api/park/geomagnetic/*` + 1 条 `GET …export-excel`), 老路由 `/geomagnetic`(老类在 `devicemanagement/`,地磁设备管理); **3 读 + 1 导入 + 1 导出 + 5 写**。 ★ 登录态只在 4 条端点(`statistic` / `records` / `insertone` / `batch-inset`), 都是把当前登录人 `orgId` 写进查询条件或库,**无 `plNos`/`userAreas`/`resIds`**; 其余 6 条完全不读登录态。 ★ 两个老 `@NoAuth` 端点按 §3.3 收紧:导出 → `:export`(`GET /export-excel`,11 列, `sysCode` 硬编码 `"1001"`、`new BaseInfo(1, 10000)` 照搬,空结果也出文件); 导入 → `:import`(`POST /batch-import-eqp-info`,multipart `excelFile`)。 ★ **本类 `@Valid` 是真生效的**(老类逐字相同):`DeleteEqpsRequest.eqps` @NotEmpty、 `NewEqpRequest` 的 eqpNo/createOp/usedType 必填、`EqpHostRequest.ids` @NotEmpty、 `QueryParkLotInfoByPkNoRequest.pklNo` @NotNull ⇒ 这些端点的 `{}` / 空集合是 **400**; `QueryEqpRequest` 无约束 ⇒ `statistic` / `records` 的 `{}` 能到 service(空条件查询)。 ★ 存量缺陷照搬:`insertone` 缺 `joinMethod` 在拆箱处 NPE(发生在 insert 之前,安全)、 `batch-inset-excle-list` 的 `list.size()` 在空判**之前**(`{}` NPE、`[]` 才是 1003「数据为空」)、 `export-excel` 的 `SimpleDateFormat.parse` 遇到坏日期抛 ParseException ⇒ 500, `readUploadExl` 对假文件回空表而不抛 IOException ⇒ 导入断言 `code=0 data=null`。 ★ ⛔ **`insertone` 带 `joinMethod=1`(设备直连)+ 不存在的 `eqpNo` 会真插一行 `tp_p_eqp`**; `deleteall` 会逻辑删除 `eqps` 里列出的设备、`updateone` 改主数据、 `bind-eqp-host-by-ids` 改 `gateway_no`。冒烟一律用**无 body**(绑定层 400) 或带 `id:0` 的 0 行 no-op 样本,`tp_p_eqp` 行数已实测不变。 ★★ **2026-10-18 第七十三切片:⚠B 里的 `TdCRechargeCardController`(10/11 端点)搬走** ⇒ **⚠B 87/474 → 86/463、已迁 120/637 → 121/648、未迁 91/481 → 90/470**。 ★ 落在 `pay` 域(`POST /admin-api/pay/recharge-card/*` + 1 条 `GET …export-excel`), 老路由 `/tdCRechargeCardManage`(老类在 `appservice/`,充值卡管理); **4 读 + 5 写 + 1 导出**。 ★ **唯一未迁端点 `rechargeCardGeneratePic`**:本地 `./cardpic` 生成二维码 zip + `FastDFSClientWrapper.uploadFile` + 落 `td_c_recharge_card_pic_zip`, 卡在「文件存储收敛到 `FileClient` 抽象」的独立决策(与 `/upload-employee-head-pic`、 停车场车位共享同款)。 ★ 登录态在 7 条端点(page / zip-page / save / update / delete / bath-delete / batch-update),全部只碰 `orgId` 或 `opId`/审计列,**无 `plNos`/`userAreas`/`resIds`**; list / statistic / 导出完全不读登录态。 ★ 导出老 `@NoAuth` 按 §3.3 收紧为 `:export`:13 列、`sysCode` 硬编码 `"1001"`、 入参全 `required=false`、`orgId` 取**请求参数**不取登录组织、空表也出文件。 ★ 存量死读:`queryTdCRechargeCardList` 的 DAO 方法整段注释、`return null` ⇒ 恒 `data=[]`。 ★ 存量缺陷照搬:`save {}` 的 `cardNum` 拆箱 NPE、`save cardNum=0` 空 `` 坏 SQL、 `bath-delete {}` / `batch-update {}` 的 `andIdIn(null)` 坏 SQL(均 500「系统异常」); `delete` 只写 `modfiy_empid`/`modfiy_date`、**不改 `data_state`**; `zip-page` 的 `andCreateDateBetween(null,null)` 抛 RuntimeException ⇒ 两个日期是事实必填; `export` 的 `getCardType()==1` / `getEndTime().getTime()` 拆箱,坏日期走 hutool `parse`。 ★ `delete`/`bath-delete`/`batch-update` 里的 `setsOpId(...)` 是存量死赋值 (DTO 自己声明 `String opId` 并覆盖 `getOpId()`,服务读的是那个 String)。 ★ ⛔ **`save` 带 `cardNum>0` 会真插 N 行 `td_c_recharge_card`**;冒烟只发 `{}` / `{cardNum:0}` / `id:0` / `ids:[0]`,`td_c_recharge_card` 与 `td_c_recharge_card_pic_zip` 行数已实测不变。 ★★ **2026-10-18 第七十四切片:⚠B 里的 `VipCardCustInfoController`(迁 14/15 端点)搬走** ⇒ **⚠B 86/463 → 85/448、已迁 121/648 → 122/663、未迁 90/470 → 89/455**。 ★ 落在 `order` 域(`POST /admin-api/order/vip-card-cust-info/*` + 1 条 `GET …export-excel` + 1 条 multipart 导入),老路由 `/vipCardCustInfo` (老类在 `operationsmanage/vipcardmanage/`,个人购买会员卡记录); **6 读 + 7 写 + 1 导入 + 1 导出**。 ★ **唯一未迁端点 `exportVipCardWordDoc`**:输出目录硬编码 `/Users/wangbiao/Downloads/四川营协议/`、模板用 `ClassPathResource.getFile()` (打 jar 后不可用)、`XWPFTemplate`(deepoove poi-tl)新栈**没有依赖** ⇒ 与 `rechargeCardGeneratePic` 同归「文件存储/外部模板」决策。 ★ 登录态只碰 `orgId`/`createEmpid`/`modifyEmpid`/`name`, **无 `plNos`/`userAreas`/`resIds`**;读端点与导出完全不读登录态。 ★ 老 `@NoAuth` **三处**,三种处置:multipart 导入 → `:import`;导出 → `:export` (`GET /export-excel`);`checkPhoneAndCarNumber`(老前端只有登录后的 `cardrule.js`/`xcvipcard.js` 在调)→ `:query`,老 `@SysOpLog` 保留成 `@OperateLog(QUERY, …)`。 ★ 存量缺陷照搬:`update-*-by-id` 服务层 `detailList.iterator()` NPE、 `update-*-by-cust-card-no` 空指针、`change-*-car-number`/`delete-*` 的 1003 判空、 `check {}` 的 `DateUtil.parse(null)` NPE;`check` 里「每个别人的车牌都覆盖一次结果」 的循环与 `getCustId().equals(...)` 裸调用一律保留。 ★ 导出 `@RequestParam Date beginTime/endTime` 走 §3c 契约(坏日期 400, 与充值卡导出的 hutool 解析 500 不同);`pageSize=0` 靠全局 `page-size-zero`。 ★ ⛔ **`save-vip-card-cust-info-and-person` 会建客户/车牌/订单/授权多表插入**; `batch-save-vip-card-buy` 批量建卡订单;`check-phone-and-car-number` 在 「手机号存在、车牌不存在」时**真插一条 `td_c_car`**。冒烟只发**无 body** 与 `checkFlg=2` + 假车场样本,`t_vip_card_cust_info` 等表行数已实测不变。 ★★ **2026-10-18 第七十五切片:⚠B 里的 `WorkOrderManageController`(迁 8/8 端点)搬走** ⇒ **⚠B 85/448 → 84/440、已迁 122/663 → 123/671、未迁 89/455 → 88/447**。 ★ 落在 `order` 域(`POST /admin-api/order/work-order/*`),老路由 `/workorder` (老类在 `operationsmanage/workordermanage/`,工单管理);**4 读 + 4 写、无导出**。 ★ 登录态只在 `save-order-work` / `save-order-work-for-inspection` 两处读, 且**只取 `orgId`**(`userInfo != null && orgId != null` 才覆盖); 其余 6 条完全不读登录态,**无 `plNos`/`userAreas`/`resIds`**。 ★ `query-order-work-track-by-work-id` 的 `workId` 为空抛 1003「入参为空」; 轨迹列表走老 `returnJqGridDataByList` 口径(`total = list.size()`)→ `toPageResultOfList`(同 `ParkingFlowController` 的写法)。 ★ 存量错拼 `Histoty` 保留在路由里(`…-for-page-for-histoty`)。 ★ 老 `@SysOpLog` 5 处 → `@OperateLog`(CREATE×3、UPDATE×1、DELETE×1); `save-order-work-for-publish` 底层是 `updateOrderWorkAndOrderWorkTrack` ⇒ 记 UPDATE。 ★ ⛔ **`save-order-work-for-inspection` 与 `save-order-work-for-publish` 传 `{}` 会真插 `order_work`/`order_work_track`**;冒烟只发无 body,两张表行数已实测不变。 ★★ **2026-10-18 第七十六切片:⚠B 里的 `TdBPayProductController`(迁 7/7 端点)搬走** ⇒ **⚠B 84/440 → 83/433、已迁 123/671 → 124/678、未迁 88/447 → 87/440**。 ★ 落在 `order` 域(`POST /admin-api/order/pay-product/*`),老路由 `/tdBPayProductManage`(支付产品管理);**4 读 + 3 写 + 1 批量删、无导出**。 ★ 6 处读登录态但**只碰 `orgId`/`opId`/`name`**:page/save/appNameList 在 `userInfo == null || orgId == null` 时抛 `AUTH_TOKEN_NOT_EXISTS`, 非超管(`orgId != -1`)才用登录组织覆盖请求;`opId` 只作审计字段写库, **无 `plNos`/`userAreas`/`resIds`**。 ★ `bath` 是存量错拼,路由保留(`/bath-delete-td-b-pay-product`)。 ★ 存量缺陷照搬:`delete-td-b-pay-product` 写 `request.setsOpId(...)` (`BaseRequest` 字段),服务层却读 `getOpId()`(子类字段)⇒ 操作人恒 null; `batchDelete` 才写 `setOpId`。`update` 的请求 DTO 没有 id,`{}` 直接撞空主键 500。 ★ ⛔ **`save-td-b-pay-product` 传 `channelEnum` 会真插 `td_b_pay_product` + 渠道配置 + `sys_org_app_id`**;冒烟只发 `{}` 与无 body,三张表行数已实测不变。 ★★ **2026-10-18 第七十八切片:⚠B 里的 `IncomeChargerCheckTaskSettingController`(迁 7/7 端点)搬走** ⇒ **⚠B 82/426 → 81/419、已迁 125/685 → 126/692、未迁 86/433 → 85/426**。 ★ 落在 `order` 域(`POST /admin-api/order/charger-check-task-setting/*`),老路由 `/incomeChargerCheckTaskSettingManage`(收费员考核任务设置); **2 读 + 1 导出 + 1 导入 + 3 写**;导出数 64 → 65。 ★ 导出按 §3.3 第 1 类收紧:老 `@NoAuth` → 登录 + `:export`,路由固定 `GET /export-excel`;导出里 `orgId` 硬编码 10003、`plBusiType` 硬编码 `"1"`,照搬。 ★ 5 处读登录态但**只碰 `orgId`/`sOpId`/`name`**;**无 `plNos`/`userAreas`/`resIds`**。 ★ `bath` 是存量错拼,路由保留(`/bath-delete-income-charger-check-task-setting`)。 ★ page/sumFee 的 `staticDate` 判空 + beginOfDay/beginOfMonth 是存量 Controller 层处理,照搬;`{}` 是 500「统计时间不能为空」。 ★ `update {"id":-1}` 其余字段全 null ⇒ tk mapper 空 SET ⇒ Druid 报 `expect SET, actual WHERE` ⇒ 500(存量同样炸)。 ★ 导入 multipart 先解析 `statisticDateStr`:缺月份是 400「请求参数缺失」, 只发月份缺文件才是 `MissingServletRequestPartException` ⇒ 500。 ★ ⛔ **`save-income-charger-check-task-setting` 有 body 就会真插 `income_charger_check_task_setting`**(无任何必填校验);冒烟只发无 body。 ★ ⛔ 导入**合法 Excel** 会走 `batchSaveChargerCheckTask`(按月份先删后插)—— 冒烟只发缺参 multipart,服务层未被调用,该表行数已实测不变。 ★★ **2026-10-18 第七十九切片:⚠B 里的 `CustomerManagementController`(迁 7/7 端点)搬走** ⇒ **⚠B 81/419 → 80/412、已迁 126/692 → 127/699、未迁 85/426 → 84/419**。 ★ 落在 `pay` 域(`POST /admin-api/pay/customer-manage/*`),老路由 `customerManage`(★ 老 `@RequestMapping` **没有前导斜杠**); **4 读 + 3 写、无导出**。 ★ 3 处读登录态但**只碰 `orgId`/`orgName`**,老写法是宽松守卫 `if (userInfo != null && null != userInfo.getOrgId())`(不抛 `AUTH_TOKEN_NOT_EXISTS`);**无 `plNos`/`userAreas`/`resIds`**。 ★ 老类无 `@SysOpLog` ⇒ 不加 `@OperateLog`。 ★ 存量差异照搬:`nameByNo {}` 对 null 列表 for-each NPE ⇒ 500; `view {}` 撞 `BeanUtils.copyProperties(null, dto)` ⇒ 500; `update {}` 的 DAO 是 `updateByPrimaryKey`(非 selective)⇒ SET 全列、 `where id = null` 命中 0 行(code=0 false)。 ★ ⛔ **`save-customer-manage-info` 带 `carInfoDTOList` 会真插 `td_cl_customer_management` + `td_cl_customer_car_info`**; 冒烟只发无 body 与 `update {}`,两张表行数已实测不变。 ★★ **2026-10-18 第八十切片:⚠B 里的 `TdCPresentRuleController`(迁 7/7 端点)搬走** ⇒ **⚠B 80/412 → 79/405、已迁 127/699 → 128/706、未迁 84/419 → 83/412**。 ★ 落在 `pay` 域(`POST /admin-api/pay/present-rule/*`),老路由 `/tdCPresentRuleManage`;**3 读 + 4 写、无导出**。 ★ 类路由按「去掉表前缀 `TdC` 再 kebab」定为 `present-rule` (先例 `TdCRechargeCardController` → `/pay/recharge-card`); 方法段 kebab 老 mapping 逐字,`bath` 存量错拼保留。 ★ 5 处读登录态但**只碰 `orgId`/`sOpId`/`createEmpid`/操作人**; **无 `plNos`/`userAreas`/`resIds`**。老类 4 处 `@SysOpLog` ⇒ 新类 4 处 `@OperateLog`。 ★ 存量差异照搬:`save {}` 撞 `getPayType().contains(...)` 的 NPE ⇒ 500 (**在任何 DB 调用之前**);`update {}` 的全 null selective update 在 SQL 层炸 ⇒ 500; `bath-delete {}` 对 null ids 直接 `.size()` NPE ⇒ 500;`delete {}` 命中 0 行 ⇒ code=0 false。 ★ ⛔ **`save-td-c-present-rule` 只要 `payType` 有值就会真插 1~2 行 `td_c_present_rule`**(`3`/`1,3`/`2,3` 还会先查农行 5 折活动); 冒烟只发无 body 与炸关口样本,该表行数已实测不变。 ★★ **2026-10-18 第九十六切片:⚠B 里的 `ArrearageStaticController`(迁 5/8 端点,3 个「模板 + FastDFS 回 URL」导出挂起)搬走** ⇒ **⚠B 64/281 → 63/273、已迁 143/830 → 144/838、未迁 68/288 → 67/280**。 ★ 落在 `order` 域(`POST /admin-api/order/arrearage-static/*` + `GET /export-excel`), 老路由 `/arrearageStatic`(欠费统计);**4 读 + 1 响应流导出 + 3 挂起**。 ★ 坑 22 安全:4 读 + 导出的登录态**只读 `orgId`**(并无条件覆盖进请求体), 请求体 `plNos` 是前端下发的,**不读** `userInfo.getPlNos()`/`userAreas`/`resIds`。 ★ 老类 8 个端点**一条 `@SysOpLog` 都没有** ⇒ 新类无 `@OperateLog`。 ★ 老 `@NoAuth` 的响应流导出按 §3.3 第 1 类**收紧**为 `order:arrearage-static:export`(无 token 401)。 ★ ⏸ 3 个 `...ForAjax[New]`/`...ForParkAjax` 导出挂起:模板 `ArrearageReportTemplate.xlsx` / `ArrearageReportTemplate4Park.xlsx` 未随迁, 形状是「写临时文件 → FastDFS 上传 → 回 URL」,属 §4.3 待定端点; **别硬搬成响应流**(会改掉老契约)。 ★ 存量语义照搬:两个列表端点(含 New 的非 vip/非来源分支)走 `queryArrearListForParkOrCarNumber`,其 SQL 撞本地 MySQL 8 `only_full_group_by` ⇒ **500「系统异常」**(老栈同 SQL 同样 500,红线⑥不修); New 的 vip/parkSource 分支走 `...WithVipCard`,本地活着(窗口内 0 行)。 ★ ⛔ 全类只读/导出,无写端点;`system_operate_log` 零写基线不变。 ★★ **2026-10-19 第一百三十二切片:⚠B 里的 `TVipTimeCardController`(3 端点整类迁完)搬走** ⇒ **⚠B 28/102 → 27/99、已迁 179/1009 → 180/1012、未迁 32/109 → 31/106**。 ★ 落在 `bill` 域(`/admin-api/bill/vip-time-card/*`),老类在 `portal.web.viptime` 包、 老路由 `/tVipTimeCardManage`;**2 读 + 1 写**、无导出、无 `@NoAuth`。 ★ 与已迁的 `/bill/vip-area-card` 共用 `TVipAreaCardService`,本类强制 `type=2`。 ★ 坑 22 安全:只写 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 真写风险:`create` 服务层无校验、`{}` 会真插一行 `t_vip_area_card` ⇒ 冒烟只发无 body(400)。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百五十五切片:上片新进池的 3 个类一次搬完(11 端点)** ⇒ **已迁 204/1113 → 207/1124、未迁 15/42 → 12/31、✔ 6/15 → 4/7、 ⚠A 1/3 → 0/0(再次清零)、⚠B 8/24 不变、缺口 8/55、死代码 5。** ★ `PersonPCAcctController` 3/3(`/admin-api/pay/person-pc-acct/*`): ⚠A **机械误报核销** —— 第 93 行 `acctRequest.setOrgId(request.getOrgId())` 打在**请求体**上,全类不读登录态;新类不注入 `SessionCommUtil`。 ★ `AppUserAccountController` 3/3(`/admin-api/pay/app-user-account/*`): 1 读 + 2 写;老 `@SysOpLog` ×2 ⇒ `@OperateLog` ×2。`delectUserCar`(老拼写) → `delete-user-car`,按主键软删;`updateAccountFreezeState` 的冻结流水插入 **无条件**(不看 update 结果)⇒ 冒烟只能发零字节/坏 JSON/GET,**不能发 `{}`**。 ★ `UserRechargeRefundController` 5/5(`/admin-api/order/user-recharge-refund/*`): 3 读 + 1 写 + 1 导出;导出老方法名误称 `excleImport…`、老 `@NoAuth` 按 §3.3 第 1 类收紧为 `:export`;`sysCode` 硬编码 `"1001"`、`pageSize=0`、 空对象兜底、provider 空 data 会 NPE 等存量缺陷照搬。 ★ 坑 22:读端点/导出零登录态;写端点只拿 `opId`/`name` 写审计列。 ★ 零写基线上移 **+224/轮 → +226/轮**(新增 2 条 `@OperateLog` 留痕); 新增监控表 `order_refund` / `td_c_cust_person_freeze`(均一行不动)。 ★★ **2026-10-19 第一百五十六切片:⚠B 的 2 个类一次搬完(12 端点)** ⇒ **已迁 207/1124 → 209/1136、未迁 12/31 → 10/19、✔ 4/7 不变、 ⚠B 8/24 → 6/12(⚠A 仍 0/0)、缺口 8/55、死代码 5。** ★ `OrderRefundController` 5/5(`/admin-api/order/order-refund/*`): 3 读 + 1 写 + 1 导出;两个读端点把登录 `orgId` 写进查询(null 抛 `AUTH_TOKEN_NOT_EXISTS`),`plNos` 只出现在导出 query 参数里(不是登录态); 导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `:export`。 存量缺陷照搬:补缴分支 `split("-")[1]` AIOOBE、`payOrderid == null` 时 `.toString()` NPE;`{}` 打写端点先炸 1003。 ★ `AccountRechargeStatisController` 7/7(`/admin-api/pay/account-recharge-statis/*`): 6 读 + 1 导出、**零写**、老类零 `@SysOpLog`;前两个读端点 `null != userInfo` 才设 `orgId`(null 不抛错,与 OrderRefund 相反);导出老 `@NoAuth` 收紧。 本机两条端点恒 500 = 存量 `group by ... desc` 撞 MySQL 8(老栈同坏)。 ★ 坑 22:两类命中都只读登录 `orgId`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 零写基线上移 **+226/轮 → +228/轮**(新增 1 个写端点 `@OperateLog` ×2 条留痕); 本片没有新增被写表。 ★★ **2026-10-19 第一百五十七切片:⚠B 的 2 个类一次搬完(8 端点)** ⇒ **已迁 209/1136 → 211/1144、未迁 10/19 → 8/11、✔ 4/7 不变、 ⚠B 6/12 → 4/4(⚠A 仍 0/0)、缺口 8/55、死代码 5。** ★ `StoreCarManageController` 7/7(`/admin-api/pay/store-car-manage/*`): 4 读 + 3 写(开通/销户/现金充值)、零导出;老类 3 处 `@SysOpLog` ⇒ 3 条 `@OperateLog`(CREATE / DELETE / UPDATE)。 读端点 `null == userInfo` 抛 `AUTH_TOKEN_NOT_EXISTS`;开通/销户/充值只写 `orgId`/`sOpId`/`sOpName` 等审计列。 ★ 真写风险:`open-store-car-acctount {}` 会**真插**个人账户(custId=null)⇒ 探针/冒烟只发无 body/坏 JSON/GET;`close-store-car-account` 只发不存在的 `custId`;`account-store-car-recharge-by-xj {}` 在取值处 NPE,早于任何写入。 ★ 本机 `query-acct-person-for-page` 恒 500 = 存量 `TdCAcctPersonSMapper.queryAcctPersonStoreCarForPage` 的 `GROUP BY a.cust_id` 却 SELECT 非聚合列 `b.acct_id` 等,撞 MySQL 8 `only_full_group_by`(老栈同坏, 照红线⑥不修)⇒ 冒烟断言 500 并留注释。 ★ `AppointmentManageController` 1/1(`/admin-api/order/appointment-manage/*`): 1 读、零写零导出;老方法名 `queryAcctPersonForPage`(复制残留)、mapping 才是 `queryOrderAppointmentForBcpPage` ⇒ 新方法名按 mapping 恢复。 ★ 坑 22:两类只读登录 `orgId`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 同片判定 `ParkLotStatisticController` **不可搬**:它读登录 `plNos` 且为空抛 `AUTH_TOKEN_NOT_EXISTS`,属 P3 未落地 ⇒ 继续挂起。 ★ 零写基线 **+228/轮 → +231/轮**(新增 3 个写端点埋点,`system_operate_log` 每轮各多 1 行);本片没有新增被写表。 ★★ **2026-10-19 第一百五十八切片:⚠B 的 `RemoteControlDzController` 1/1 搬走** ⇒ **已迁 211/1144 → 212/1145、未迁 8/11 → 7/10、✔ 4/7 不变、 ⚠B 4/4 → 3/3(⚠A 仍 0/0)、缺口 8/55、死代码 5。** ★ 老类 `dzcontroller/RemoteControlDzController`(老路由 `/dzcontroller`), 新路由 `/admin-api/park/remote-control-dz/*`;老类 1 处 `@SysOpLog` ⇒ 新类 1 条 `@OperateLog`(UPDATE)。 ★ 坑 22:只读登录 `orgId`/`loginCode`/`opId` 写 `api_log_request`, **不读** `plNos`/`userAreas`/`resIds`。 ★ ⛔ 真副作用:`sourceType == 7`(红门)会**真实开闸 + 插 API 调用日志**; 安全样本 `P15040214Z` 在本机 Redis 缓存里 `sourceType=17` ⇒ 走 else, 在**外呼与写库之前**抛「该车场暂不支持远程开闸」(存量错误码 1007)。 ★ 存量 NPE 照搬:`getParkLotByPlNo(null/不存在)` 返回 null 后直接 `parkLot.getSourceType()` ⇒ `{}` / 假 plNo 都是 500「系统异常」(红线⑥)。 ★ HTTP 工具改用 park 模块已有的 `com.zteits.park.util.HttpClientTutorial` (与老门户同名同签名);两份 portal VO 原样搬进 `controller/admin/remotecontroldz/vo/`。 ★ 零写基线 **+231/轮 → +234/轮**(3 条带 token 的 §2ex 调用触发 `@OperateLog`); 本片没有新增被写表(红门分支会写 `td_b_api_log`,该表历批已在监控内,本片不可测)。 ★★ **2026-10-19 第一百五十四切片:先修实现识别,再搬 `ChargerAccountController` 6 端点** ⇒ **已迁 203/1107 → 204/1113、未迁 8/11 → 15/42、✔ 4/7 → 6/15、 ⚠A 0/0 → 1/3、⚠B 4/4 → 8/24、缺口 16/92 → 8/55、就绪 211/1118 → 219/1155。** ★ 本片**先修工具再搬类**:`IMPL_PAT` 只认 `class X implements Y`、漏掉 `class X extends Base implements Y`;新版实现类只 glob `*Impl.java`、漏掉 `RefundServiceIpml.java`(存量把 Impl 拼成 Ipml)⇒ 8 个 Controller / 37 端点 被误判成「无实现」挡在缺口档:StoreCarManage(7)、AccountRechargeStatis(7)、 ChargerAccount(6)、UserRechargeRefund(5)、OrderRefund(5)、PersonPCAcct(3)、 AppUserAccount(3)、AppointmentManage(1)。总数 232/227/5 不变。 ★ `ChargerAccountController` 落在 `pay` 域(`/admin-api/pay/charger-account/*`), 老类在 `operationsmanage/chargemanage` 包、老路由 `/chargerAccount`; **4 读 + 2 导出**,零写、零 `@SysOpLog`。 ★ 坑 22 安全:6 个端点**零登录态**(`custIds` 由前端下发);新类不注入 `SessionCommUtil`。 ★ 两条导出老 `@NoAuth` 按 §3.3 第 1 类收紧为 `:export`;一个类两个导出 ⇒ `/export-excel` + `/export-pay-out-excel`。 ★ 照搬:`acctTypeList` 无条件覆写、双重循环回填收费员名且不判空、 支出回填两次且退费单命中时 `berthNo=""`、停车查询用 `0001_SYS` 而退费/收费员用请求 `sysCode`、 导出 `BaseInfo(1, MAX)` + `DateUtil.to_date` + `rechargeType` 1/2/5/6/未知 + `/100` ROUND_HALF_UP。 ★ 本机样本:`acct_type=6` 充值 2 行(统计 110000/2/0/0)、支出 0 行、 `tp_e_employee_charge` 0 行 ⇒ `custName` 恒 null;支出两次回填无正样本。 ★ 零写库;零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百五十三切片:⚠B 里的 `RealTimeInfoController`(3 端点只落 2 个)搬走** ⇒ **⚠B 5/7 → 4/4、已迁 202/1104 → 203/1107、未迁 9/14 → 8/11**。 ★ 落在 `park` 域(`/admin-api/park/real-time-info/*`),老类在 `parkinglotcloudplatform/monitor` 包、老路由 `/rtinfo`;**2 读**、零导出、零 `@SysOpLog`。 ★ 未迁 `berthinfo`:真读 `userInfo.getUserAreas()` 做数据权限过滤(P3)。 ★ 已迁两条**零登录态**:`escapePaymentInfo` 把请求原样透传;`warninginfo` 里的 `//userInfo.getUserAreas()` 在存量源码里就是注释掉的(「权宜之策构造」按 `cityId` 现造 `UserArea`)⇒ 不算登录态读取,别"顺手恢复"。 ★ `escape-payment-info` 本机**恒 500**:`RealtimeInfoSMapper.xml` 的 namespace 写成 `com.zteits.park.mapper.smapper...`、接口在 `com.zteits.pay.mapper.smapper...` ⇒ `Invalid bound statement`;**老仓同一份 XML 同样写错**(两栈同坏、红线⑥照搬不修), `p2_audit_mapper_bind.py` 归 B 类「存量就断」。 ★ `warning-info` 本机 `free_berth=0` 与 `parking_lot_geo(geo_type=1)` 的连接结果为 0 ⇒ 恒返回空数组(样本缺口,不是过滤错)。 ★ 零写库;零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百五十二切片:⚠B 里的 `ChargerTicketController` 3 端点全迁** ⇒ **⚠B 6/10 → 5/7、已迁 201/1101 → 202/1104、未迁 10/17 → 9/14**。 ★ 落在 `pay` 域(`/admin-api/pay/charger-ticket/*`),老类在 `web.ticket` 包、 老路由 `/chargerTicket`;**1 读 + 2 写**、零导出、零 `@SysOpLog`。 ★ 坑 22 安全:只写 `orgId`/操作人,不读 `plNos`/`userAreas`/`resIds`; 老代码三处**都不判** `userInfo == null`,新栈由安全过滤器先挡(照搬不补判空)。 ★ 两处预览都用**写死的样例 DTO**(收费员 0050001/张三、京A88888 等), 渲染后 `replaceAll("null","-")` 并把 `&&&&` 切成 `ticketTextUp/Down` + `hasPic`, 但正文**仍保留 `&&&&`**(只切不删)。 ★ 照搬顺序:`save` 先雪花号(内存)再五项判空;`open` 先按条件把旧模板 `ticket_state=0`、再启用当前模板(返回第二步结果)。 ★ 真写风险:`save` 带齐 `configRequests` 会真插配置+预览模板; `open` 带真实 `ticketNo` 会真改两批行(本地 `charger_ticket_template` 81 行)。 冒烟只发无 body / `{}` / 标题-only / 不存在模板号;零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百五十一切片:⚠B 里的 `ArrearageCallConfigController` 4 端点全迁** ⇒ **⚠B 7/14 → 6/10、已迁 200/1097 → 201/1101、未迁 11/21 → 10/17**。 ★ 落在 `order` 域(`/admin-api/order/arrearage-call-config/*`),老类在 `web.arrearangecall` 包、老路由 `/arrearageCallConfig`; **1 读 + 3 写**、零导出、零 `@SysOpLog`。 ★ 坑 22 安全:分页条件用的是**请求体** `plNos`(`sql.andPlNoIn(request.getPlNos())`), 控制器写的 `orgId` 在 DAO 里**根本没被使用**;save/update 只写操作人,delete 老代码不读登录态。 ★ 存量形状:`plNos=null` ⇒ Example `addCriterion` 抛 RuntimeException; `plNos=[]` ⇒ `and pl_no in ()` SQL 语法错 ⇒ 两条都是 500「系统异常」。 分页**必须显式带非空 plNos**,别顺手改成"不传就查全部"。 ★ 其它照搬:`callMaxValue` 条件错用 `getCallMinValue`;update 先写操作人/时间再判 id; delete 是**物理删除**(不是 `data_state=0`)。 ★ 真写风险:`save` 带非空 `parkLontDTOs` 会真插行(本地 239 行) ⇒ 冒烟只发无 body / `{}` / `{"parkLontDTOs":[]}`;零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百五十切片:⚠B 里的 `BalanceManagementController`(5 端点只落 3 个)搬走** ⇒ **⚠B 8/19 → 7/14、已迁 199/1092 → 200/1097、未迁 12/26 → 11/21**。 ★ 落在 `order` 域(`/admin-api/order/balance-management/*`),老类在 `web.financialmanagement.clearsettlement` 包、老路由 `/BalanceManagement`; **1 写 + 2 导出**;另 2 个分页端点**有意不迁** (`plNos` 空或 `"ALL"` 时回落 `userInfo.getPlNos()` = P3 数据权限)。 ★ 坑 22:已迁 3 条**都不读登录态**(写只吃 body、导出只吃 7 个 query 参数)⇒ 可开搬。 ★ 两条导出老 `@NoAuth`,按 §3.3 第 1 类收紧为需登录; 权限 `order:balance-management:update` / `order:balance-management:export`。 ★ **本地服务层是空壳**(`BillReconciliationFor*ServiceImpl` 的 DAO 调用全注释) ⇒ 导出只有表头、写恒回 `false`;换环境接回 DAO 后冒烟 `dataRows == []` 会红, 那是功能恢复、不是回归。 ★ 写端点冒烟只发空 body / 坏 JSON(绑定层 400),`@OperateLog` 不触发; 零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十九切片:⚠B 里的 `ParkSourceController`(6 端点只落 3 个)搬走** ⇒ **⚠B 9/25 → 8/19、已迁 198/1086 → 199/1092、未迁 13/32 → 12/26**。 ★ 落在 `park` 域(`/admin-api/park/park-source/*`),老类在 `parkinglotcloudplatform.parklotmanage` 包、老路由 `parkSource`; **3 读、零写、零导出、零 `@SysOpLog`**;另 3 个端点**有意不迁** (统计 / 员工总数 / 设备总数都读 `userInfo.getPlNos()` = P3 数据权限)。 ★ 坑 22:三个已迁端点**都不读登录态**(两条分页只吃请求体 `plNos`、 单车场只吃 `plNo`)⇒ 可开搬。 ★ 三个端点统一挂 `park:park-source:query`;老类无 `@NoAuth`。 ★ `plNos=null` 时存量 SQL 的 `` 全不生效 ⇒ 车场扫全部 `data_status=1` 行 (本机 4 行);每行 `deviceList` 恒 3 项(PDA/地磁/地锁)、`deviceTotal` 走 `Integer` 拆箱;单车场端点查不到车场时服务层回**非 null 空 DTO**。 ★ 单车场端点 `{}` → **500「系统异常」**:`plNo=null` 时缓存工具直接炸 (存量缺陷照搬);员工统计本机 `tp_e_employee_charge` 0 行 ⇒ 恒空页。 ★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 6 个端点都记进"已迁"、⚠B 整类核销, 但**交付口径是 3/6**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十八切片:⚠B 里的 `AnomalyStatisticsController`(4 端点只落 2 个)搬走** ⇒ **⚠B 10/29 → 9/25、已迁 197/1082 → 198/1086、未迁 14/36 → 13/32**。 ★ 落在 `order` 域(`/admin-api/order/anomaly-statistics/*`),老类在 `portal.web.report.income` 包、老路由 `income/anomalyStatistics`; **2 读/导出、零写、零 `@SysOpLog`**;另 2 个端点**有意不迁** (`parkIds` 为空时回落 `userInfo.getPlNos()` = P3 数据权限)。 ★ 坑 22:两个已迁端点**都不读登录态**(明细只吃请求体;导出只吃 4 个 `@RequestParam`) ⇒ 可开搬。 ★ 老导出 `@NoAuth` 按 §3.3 第 1 类收紧为 `GET /export-excel` + `:export`; 明细分页 `POST /query-sealed-parking-detail-report-for-page` + `:query`。 ★ 明细 `QuerySealedParkDetailReportRequest` 自带 `@NotBlank parkId` / `@NotBlank paymentTime` / `@NotNull queryType` ⇒ `{}` 是绑定层 400。 ★ 老导出整个方法体包在 `try/catch` → 只 `printStackTrace`(新栈 `log.error`): `queryType=0` 撞本机 MySQL 8 `only_full_group_by` 时是 **HTTP 200 + 空 body**, 不是新栈 500;文件名/sheet/10 列表头逐字照搬,`sysCode="1001"`、 `BaseInfo(1,0)`、金额 ÷100、null → `""`、「封顶放行(次)」恒空串。 ★ 月口径入参必须是 `yyyy-MM`(服务层补 `-01`);本机 `P1101020Z7` + `2018-02-01` 有 10 单、汇总 9400 分 ⇒ xlsx 行 `94.0/94.0`。 ★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 4 个端点都记进"已迁"、⚠B 整类核销, 但**交付口径是 2/4**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十七切片:⚠B 里的 `SelectItemController`(8 端点只落 2 个)搬走** ⇒ **⚠B 11/37 → 10/29、已迁 196/1074 → 197/1082、未迁 15/44 → 14/36**。 ★ 落在 `bill` 域(`/admin-api/bill/select-item/*`),老类在 `parkinglotcloudplatform.datastatistic` 包、老路由 `/selectItem`; **2 读、零写、零导出**;另 6 个端点**有意不迁**(都读 `userInfo.getPlNos()`)。 ★ 坑 22:两个已迁端点**都不读登录态**(会员卡全量分页 + 前端参数 `noAll`)⇒ 可开搬。 ★ 老接口是 `void` + `response.getOutputStream().write(JSONArray…)` 裸数组; 新栈统一 `CommonResult>`,`SelectItemVO` 与老 `com.zteits.bcp.portal.common.SelectItem` 字段一一对应,`equals`/`hashCode` 只比 `code`/`label`/`userDefined`/`value`(老代码 `list.contains` 去重)。 ★ `noAll` 缺失时照搬老代码 `noAll.equals("yes")` 的 **NPE → 500**; `pageSize=0` 取全量靠 `page-size-zero: true`;`sysCode` 是字面量 `"1001"`。 ★ 本机 `t_vip_card where data_state=1` **427 行**(card_type 1~7 全非空)⇒ `noAll=yes` 返回 8 条类型 / 428 张卡;`noAll=no` 返回 7 / 427。 ★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 8 个端点都记进"已迁"、⚠B 整类核销, 但**交付口径是 2/8**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十六切片:⚠B 里的 `PersonPCIndexController`(5 端点只落 2 个)搬走** ⇒ **⚠B 12/42 → 11/37、已迁 195/1069 → 196/1074、未迁 16/49 → 15/44**。 ★ 落在 `pay` 域(`/admin-api/pay/person-pc-index/*`),老类在 `portal.web.personpc` 包、 老路由 `/personPc/index`;**2 读、零写、零导出**;另 3 个端点**有意不迁**。 ★ 坑 22:两个已迁端点**都不读登录态**(`custId` 来自请求体、组织查询不读身份)⇒ 可开搬。 ★ 不迁的 3 个(`getCustIndexBaseInfo` / `queryOwnerParkRecord` / `pageQueryArrearRecordByCondition`)都走 `getUserInfoForPersonPC()` 读 HTTP session, 新栈不建 session ⇒ 恒 1003,记为「等个人客户登录桥」 (与 `PersonPCCarNumController` 同口径)。 ★ `query-owner-pay-out-by-month`:控制器不校验 `custId`(服务层抛 1003); `beginTime`/`endTime` 缺一则改「去年今天 ~ 本月月末」并补满 13 个月零值。 ★ `select-all-app-org`:**无 `@RequestBody`**,DAO 过滤 `data_state=1` (本机 14 行 → 12 行),服务层 Redis 缓存优先(读端点里的 Redis 写)。 ★ ⚠️ 盘点脚本按**类**判已迁 ⇒ 本片 5 个端点都记进"已迁"、⚠B 整类核销, 但**交付口径是 2/5**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十五切片:⚠B 里的 `IndexDeviceController`(3 端点整类迁完)搬走** ⇒ **⚠B 13/45 → 12/42、已迁 194/1066 → 195/1069、未迁 17/52 → 16/49**。 ★ 落在 `park` 域(`/admin-api/park/index-device/*`),老类在 `portal.web.index` 包、 老路由 `/index/device`;**3 读、零写、零导出**。 ★ 坑 22:**只有第 1 个端点**读登录态回填 `orgId`(`getUserInfo()` 为空抛 `AUTH_TOKEN_NOT_EXISTS`);第 2、3 个老代码不读登录态,照搬。 `plNos` 全来自前端,`userAreas`/`resIds` 不碰 ⇒ 可开搬。 ★ 第 1 个 `eqpTypes=[ALL(-1)]` 由服务层展开成 GEO+PDA,每类型回 3 个 VO (在线 0 / 离线 1 / 故障 2),`map` 空回**空列表**。 ★ 第 2 个 `eqpType` **无校验**(`{}` ⇒ `eqpTypes=[null]` 空结果),4 个计数 `Integer` 拆箱累加。 ★ 第 3 个 `@Valid`(`plNos` `@NotEmpty`、`eqpType` `@NotNull` ⇒ 400;两字段同违时 Bean Validation **不保证先报哪个**);`PageResult.total` = 地磁分页 total + PDA 分页 total, `id` 左补零 10 位;两个分支各自 `pageBean==null` 提前返回空页(PDA 分支会丢弃已收集的地磁行)。 ★ **本地样本**:`tp_p_eqp` 与 `parking_lot` **零 JOIN** ⇒ GEO 分支统计/分页恒空(样本缺口, 已直连 SQL 复核);PDA 正样本 `P150402254`(34 行),登录人会话 `orgId=10003`。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十四切片:⚠B 里的 `IncomeBerthReportController`(3 端点,2 迁 + 1 挂起)搬走** ⇒ **⚠B 14/48 → 13/45、已迁 193/1063 → 194/1066、未迁 18/55 → 17/52**。 ★ 落在 `order` 域(`/admin-api/order/income-berth-report/*`),老类在 `portal.web.berthreport` 包、老路由 `/incomeBerthReport`;**2 读 + 1 导出**。 ★ 坑 22 安全:两个读端点都照搬 `getUserInfo().getOrgId()` 且**不判空** (新栈 `@PreAuthorize` 先挡无 token),`plNos` 全来自前端,`userAreas`/`resIds` 不碰。 ★ 请求 DTO **无 `@Valid`** ⇒ 三条手工校验:空 `plNos` → 1003、开始/结束时间缺失 → 1003、 跨度 >31 天 → 1003;日期字段只吃 `yyyy-MM-dd HH:mm:ss`(`yyyy-MM-dd` 是 400)。 ★ 分页老 `returnJqGridData` → `PageResult`;汇总 SQL 全列 `ifnull(sum(...),0)` ⇒ 假车场回**全 0 DTO**。 ★ **DAO 除零风险照搬**:守卫只看 `parkDurationPay==0`,`0` 到本机 Redis,**无 TTL、跨轮次常驻**,会把 §2ak「DTO 字段全 null」与 `park-detail-info-by-org-ids → 500` 两条老断言永久打红。冒烟只准用不存在的 plNo (本片 `ZZZNOPE`,预热在 `getParkLotByPlNo` 返回 null 处 NPE 被 catch)。 ★ 车场概况:`@Valid @NotEmpty` 先于老手写判空 ⇒ 空 body **400**(1003 不可达); 有效 ⇒ `total=plNos.size()`;不存在车场 ⇒ `total=0, list=[]`(老 `data=null` 口径)。 ★ 老导出 `@NoAuth`(`/exportParkinglotBasicFactsByPlNos`,不限方法)按 §3.3 收紧为 `GET /export-excel` + `park:index-real-time:export`。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百四十切片:⚠B 里的 `InvoiceController`(5 端点整类迁完)搬走** ⇒ **⚠B 18/66 → 17/61、已迁 189/1045 → 190/1050、未迁 22/73 → 21/68**。 ★ 落在 `pay` 域(`/admin-api/pay/invoice/*`),老类在 `portal.web.invoice` 包、老路由 `/invoice`; **4 读 + 1 导出、零写端点**。 ★ 坑 22 安全:4 读 + 导出只写 `orgId`(统计端点保留「非超管才覆盖」); **不读** `plNos`/`userAreas`/`resIds`。 ★ ★ `query-invioce-by-vo`(老 `queryInvioceByVo` 的 `Invioce` 是存量错拼,路由逐字 kebab) 与 `query-cust-invoice-his-for-page` 都是真分页:`data_state=1` + 登录 orgId=10003 ⇒ 本机 406 行; `custName=赤峰` → 168。 ★ ★ 分页**逐行 N+1 照搬**(`selectByCustId` + `queryCustInvoiceHisDetailList`): 本机 `td_c_cust_person` 0 行 ⇒ `recipientsPhone=""`;明细未挂 org=10003 且 `car_number` 全 NULL ⇒ `carNumbers=null`。别改批量。 ★ 详情死主键 / `id=null` ⇒ `code=0 + data=null`。 ★ 老导出 `@NoAuth`(`/exportCustInvoiceHisList`,不限方法)按 §3.3 收紧为 `GET /export-excel` + `pay:invoice:export`;坏日期 `ParseException` ⇒ 500(存量无包装)。 ★ 老统计端点 `@NoAuth` 但方法内自校验登录 + 超管组织不覆盖 orgId;新栈外层加 `:query`, 保留老 bean 绑定(不加 `@RequestBody`);custName/saleName 空 ⇒ 1003; 两者都给 ⇒ 存量 `selectRateData` 撞 MySQL 8 `only_full_group_by` ⇒ 500(红线⑥不修)。 ★ 补齐 `parking-module-api` 的 `TdCCustInvoiceHisQueryRequest`:老仓有 `carNumbers`/`custPhone`/ `custEmail` 三字段,新栈漏搬,本片补回(纯追加)。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十九切片:⚠B 里的 3 个类一次搬完** (`TdBSmsRecordController` 4 + `OrderParkBjxcController` 4 + `OrderParkBjxcSuccessController` 4) ⇒ **⚠B 21/78 → 18/66、已迁 186/1033 → 189/1045、未迁 25/85 → 22/73**。 ★ 全部落在 `order` 域: `/admin-api/order/sms-record/*`(老 `/tdBSmsRecordManage`,类路由去 `TdB` 前缀,先例 `TdBPayApp`)、 `/admin-api/order/order-park-bjxc/*`(老 `/orderParkBjxcManage`)、 `/admin-api/order/order-park-bjxc-success/*`(老 `/orderParkBjxcSuccessManage`); 三个类都是 **1 读 + 3 写**、无导出、无 `@NoAuth`、**零** `@SysOpLog` ⇒ 零 `@OperateLog`。 ★ 坑 22 安全:只写 `orgId`/`sOpId`/`createEmp*`/`modifyEmp*`/`opId`/`opName`, **不读** `plNos`/`userAreas`/`resIds`。 ★ `TdBSmsRecord` 分页**真查询**:DAO 按登录组织(admin 会话 orgId=10003)+ `data_state=1` 过滤 ⇒ 本机 total=500;`carNumber=蒙D7Y077` → 1、`sendType=10,sendState=20` → 466。 ★ 北西城两个类的分页服务层把 DAO 调用整段注释 ⇒ **恒空页**(存量 TODO,别补 SQL); 本机也**没有** `order_park_bjxc_success` 表(delete → 501)。 ★ ★ `OrderParkBjxc` 批量删除 DAO 的 `record.set*` 全被注释 ⇒ `updateByExampleSelective` 生成空 SET ⇒ **删除恒 500**;`OrderParkBjxcRequest` 无 `id` 字段 ⇒ 按主键改恒 `where id=null`。 `OrderParkBjxcSuccessRequest` 与 domain 只共享 `id` ⇒ update 恒空 SET(500)。均照搬。 ★ 真写风险:三个 save 都无校验 ⇒ 冒烟只发无 body(400);其余只发手写 1003 / 0 行分支。 ★ 零写基线 **+224/轮** 不变(`td_b_sms_record` 500、`order_park_bjxc` 0 实测不动)。 ★★ **2026-10-19 第一百三十八切片:⚠B 里的 `TdCAcctPersonChangeController`(4 端点整类迁完)搬走** ⇒ **⚠B 22/82 → 21/78、已迁 185/1029 → 186/1033、未迁 26/89 → 25/85**。 ★ 落在 `pay` 域(`/admin-api/pay/acct-person-change/*`),老类在 `portal.web.appservice` 包、 老路由 `/tdCAcctPersonChangeManage`;**1 读 + 3 写**、无导出、无 `@NoAuth`。 ★ 坑 22 安全:只写 `orgId`/`sOpId`/`createEmp*`/`modifyEmp*`/`opId`/`opName`, **不读** `plNos`/`userAreas`/`resIds`。 ★ ★★ 分页**恒空页**:老 DAO 的 `queryTdCAcctPersonChangeForPage` 整段被注释 (`//TODO 编写smapper实现`),服务层直接 `return new PageBean<>()` ⇒ 任何入参都是 `total=0` + `list=[]`,本机表里有 5 行也看不到(存量形状,别补 SQL)。 ★ ★ `update` 入参 DTO 只有 `id/orgId/create*/modify*`;拷到 domain 后能进 `` 的 只有 `orgId` ⇒ `{"id":999999999}` 生成空 SET(500 系统异常), `{"id":999999999,"orgId":10003}` 才返回 `0 + false`。存量缺陷照搬。 ★ 真写风险:`save` 无校验、`{}` 会走 insert(本机三列 NOT NULL 兜底)⇒ 冒烟只发无 body。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十七切片:⚠B 里的 `TdCStoreCarController`(4 端点整类迁完)搬走** ⇒ **⚠B 23/86 → 22/82、已迁 184/1025 → 185/1029、未迁 27/93 → 26/89**。 ★ 落在 `pay` 域(`/admin-api/pay/store-car/*`),老类在 `portal.web.appservice` 包、 老路由 `/tdCStoreCarManage`;**1 读 + 3 写**、无导出、无 `@NoAuth`。 类路由按「去掉表前缀 `TdC` 再 kebab」定(先例 `TdCPresentRule` → `/pay/present-rule`)。 ★ 坑 22 安全:只写 `orgId`/`sOpId`/`modfiyEmpid`/`opId`/`opName`, **不读** `plNos`/`userAreas`/`resIds`。 ★ ★ 分页 DAO **不按 `orgId` 过滤**(只认 `carNumber`/`custId`/`data_state`)—— 控制器里的 `setOrgId` 不生效,本机 total=3、能看到 org=10005 的行;存量事实照搬。 ★ 真写风险:`save` 无校验、`{}` 会真走 insert(本机 `car_number NOT NULL` 兜底) ⇒ 冒烟只发无 body;update/delete 只发 0 行 / 手写 1003 分支。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十六切片:⚠B 里的 `TbWorkOrderController`(4 端点整类迁完)搬走** ⇒ **⚠B 24/90 → 23/86、已迁 183/1021 → 184/1025、未迁 28/97 → 27/93**。 ★ 落在 `system` 域(`/admin-api/system/tb-work-order/*`),老类在 `portal.web.workorder` 包、老路由 `/tbWorkOrderManage`;**1 读 + 3 写** (分页 / 新增 / 按主键改 / 批量删),无导出、无 `@NoAuth`。 ★ 坑 22 安全:只写登录态 `orgId`/`sOpId`/`createEmpId`/`modifyEmpId`/`opId`/`opName`, **不读** `plNos`/`userAreas`/`resIds`。 ★ ⛔ 真写风险:`saveTbWorkOrder` 服务层无任何校验 ⇒ `{}` 也真插一行 `tb_work_order` ⇒ 冒烟只发无 body(400)。update/delete 的 1003「id为空!」是 Controller 手写闸门 ⇒ 真零写。 ★ 本机**没有** `tb_work_order` 表:走到 SQL 的分支都是 500「系统异常」 (表名不含 `order_` ⇒ 不吃 `tb_work_order_process` 的 501 兜底);冒烟断言写成 0/500/501 三选一,表导进来后自动转绿。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十五切片:⚠B 里的 `ReconciliationController`(3 端点整类迁完)搬走** ⇒ **⚠B 25/93 → 24/90、已迁 182/1018 → 183/1021、未迁 29/100 → 28/97**。 ★ 落在 `order` 域(`/admin-api/order/reconciliation/*`),老类在 `portal.web.financialmanagement.reconciliation` 包、老路由 `/new/reconciliation`; **2 读 + 1 导出(xlsx)**、无写、导出老 `@NoAuth` 已收紧为需登录 + `:export`。 ★ 坑 22 安全:汇总/分页只判登录后写 `orgId`,超管再扩 `SUPER_OP_ID` 配置组织; 导出沿用老入参 `opId`/`orgId`/`sysCode`,**不读** `plNos`/`userAreas`/`resIds`。 基类 `ReconciliationBaseController` 仅 `getPayOrderTypeName`,整段照搬进新类。 ★ 存量缺陷照搬:缺起止时间 ⇒ 200 + code=500 + `[存量错误码 1003]`; 只给时间不给 `dateType` ⇒ `request.getDateType()` 拆箱 NPE ⇒ 500; 导出不传 `opId` ⇒ `isSuperOpId` 内 `opId.longValue()` NPE ⇒ 500。 ★ 导出是程序化 13 列 `SXSSFWorkbook`,无模板依赖;导出端点数 **79 → 80**。 日期参数保持存量 `@RequestParam Date`(坑 24),只吃 JS `Date.toString()` 格式。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十四切片:⚠B 里的 `EqpOnlineRateController`(3 端点整类迁完)搬走** ⇒ **⚠B 26/96 → 25/93、已迁 181/1015 → 182/1018、未迁 30/103 → 29/100**。 ★ 落在 `park` 域(`/admin-api/park/eqp-online-rate/*`),老类在 `portal.web.report.equipment` 包、老路由 `/eqpOnlineRate`; **2 读 + 1 导出(PDF)**、无写、导出老 `@NoAuth` 已收紧为需登录 + `:export`。 ★ 坑 22 安全:分页/合计只判登录(合计另用 `sysOrg` 回填 `orgName`), 导出沿用老入参 `orgId`/`orgName`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量缺陷照搬:`pl_no in ()` 在 `plNos` 为 null/空列表时拼成 `in ()` ⇒ MySQL 语法错 ⇒ 500;导出 `plNos=` 空串更早抛 `BizException(1003)` ⇒ 200 + code=500 + [存量错误码 1003](取参校验在 try/catch 外)。 ★ 分页/合计按 `reportTabTag` 1/2/3 走日/周/月,未知 tab 回空页 / 数值全 null(只填 `orgName`)。 ★ 导出是程序化 PDF(`PdfCreatTableUtil` 双子报表),无模板依赖;导出端点数 **78 → 79**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十三切片:⚠B 里的 `IncomeOnlineBillController`(3 端点整类迁完)搬走** ⇒ **⚠B 27/99 → 26/96、已迁 180/1012 → 181/1015、未迁 31/106 → 30/103**。 ★ 落在 `order` 域(`/admin-api/order/income-online-bill/*`),老类在 `portal.web.financialmanagement.clearsettlement` 包、老路由 `/incomeOnline`; **2 读 + 1 导出**、无写、无 `@NoAuth`(导出老门户是 `@NoAuth`,已收紧为需登录 + `:export`)。 ★ 坑 22 安全:两读判登录后写 `orgId`,导出沿用老入参 `orgId`/`sysCode`, **不读** `plNos`/`userAreas`/`resIds`。 ★ 存量 SQL 两种面貌:汇总带聚合却无 `GROUP BY`、又 select `DATE_FORMAT(check_date)` ⇒ 给时间窗口在 MySQL 8 恒 500(`only_full_group_by`,红线⑥不修),`{}` 命中空集合回全 0; 分页 `GROUP BY check_date, org_id` 正常(本机 total=121)。 ★ 导出是程序化 15 列(`SXSSFWorkbook` + `Layouter` + `ExcleFillDateManager`),无模板依赖; 导出端点数 **77 → 78**。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十一切片:⚠B 里的 `TpPChargingPileController`(3 端点整类迁完)搬走** ⇒ **⚠B 29/105 → 28/102、已迁 178/1006 → 179/1009、未迁 33/112 → 32/109**。 ★ 落在 `park` 域(`/admin-api/park/charging-pile/*`),老类在 `portal.web.chargingpile` 包、 老路由 `/chargingPile`;**1 读 + 2 写**、无导出、无 `@NoAuth`。 ★ 坑 22 安全:三端点判登录后写 `orgId`/`opId`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 真写风险:保存是 tk `insert`(`charging_pile_no` NOT NULL)⇒ `{}` 500 不落行; 更新 `{}` 主键为空 500、`{id:999999999}` 0 行 false。 ★ 老类**零** `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线 **+224/轮** 不变。 ★★ **2026-10-19 第一百三十切片:⚠B 里的 `TdCCouponPersonController`(1 端点整类迁完)搬走** ⇒ **⚠B 30/106 → 29/105、已迁 177/1005 → 178/1006、未迁 34/113 → 33/112**。 ★ 落在 `pay` 域(`POST /admin-api/pay/coupon-person/batch-insert-coupon-person`), 老类在 `portal.web.coupon` 包、老路由 `/couponPerson`;**1 写**、无导出、无 `@NoAuth`。 ★ 坑 22 安全:判登录后写 `opId`/`orgId`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 真写风险:`custIds` 非空会逐条插 `td_c_coupon_person`;冒烟只发 「无 body / `{}` ⇒ 400」与「必填齐全 + `custIds` 缺省/`[]`/`[""]` ⇒ 零插入 false」。 ★ 老类 1 处 `@SysOpLog` ⇒ 1 个 `@OperateLog` ⇒ 零写基线 **+221 → +224/轮**。 ★★ **2026-10-19 第一百二十九切片:⚠B 里的 `OrderRechargerController`(2 端点整类迁完)搬走** ⇒ **⚠B 31/108 → 30/106、已迁 176/1003 → 177/1005、未迁 35/115 → 34/113**。 ★ 落在 `order` 域(`POST /admin-api/order/order-recharger/...`),老类在 `portal.web.acctsource` 包、老路由 `/orderRecharger`;**1 读 + 1 写**、无导出、无 `@NoAuth`。 ★ 坑 22 安全:读端点判登录并写 `orgId` + **字面量** `sysCode="10001"`; 写端点老代码不判登录(新栈过滤器兜住),只取 `opId`/`name`/`orgId`。 ★ 存量语义照搬:读端点的 `...SMapper` 聚合 SQL `group by` 选非聚合列 ⇒ 本地 MySQL 8 `only_full_group_by` **恒 500**(红线⑥不修); 写端点是**真充值**(充值单 + 支付单 + 加余额),查不到账户还会**自动建户**。 ★ 冒烟只发零写入样本:无 body **400**、已存在 `custId` 缺 `acctGiveFee` **NPE 500**(落库前)。 ★ 老类 1 处 `@SysOpLog` ⇒ 1 个 `@OperateLog` ⇒ 零写基线 **+220 → +221/轮**。 ★★ **2026-10-19 第一百二十八切片:⚠B 里的 `StationInfoController`(1 个有效端点整类迁完)搬走** ⇒ **⚠B 32/109 → 31/108、已迁 175/1002 → 176/1003、未迁 36/116 → 35/115**。 ★ 落在 `charge` 域(`POST /admin-api/charge/station-info/query-station-info-for-page`), 老类在 `portal.web.battery` 包、老路由 `/stationInfoManage`; 老类 4 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读、零写**、无导出。 ★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`; **不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:分页是 `station_info ⋈ operator_info` 两表内连接, 本地 `operator_info` **0 行** ⇒ 恒空页(`station_info` 3 行都不起作用)。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十七切片:⚠B 里的 `EquipmentInfoController`(1 个有效端点整类迁完)搬走** ⇒ **⚠B 33/110 → 32/109、已迁 174/1001 → 175/1002、未迁 37/117 → 36/116**。 ★ 落在 `charge` 域(`POST /admin-api/charge/equipment-info/query-equipment-info-for-page`), 老类在 `portal.web.battery` 包、老路由 `/equipmentInfoManage`; 老类 4 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读、零写**、无导出。 ★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`; **不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:分页是 `equipment_info ⋈ operator_info ⋈ station_info` 三表内连接, 本地 `operator_info` **0 行** ⇒ 恒空页(`equipment_info` 7 行、`station_info` 3 行都进不来)。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十六切片:⚠B 里的 `ConnectorInfoController`(2 个有效端点整类迁完)搬走** ⇒ **⚠B 34/112 → 33/110、已迁 173/999 → 174/1001、未迁 38/119 → 37/117**。 ★ 落在 `charge` 域(`POST /admin-api/charge/connector-info/*`), 老类在 `portal.web.battery` 包、老路由 `/connectorInfoManage`; 老类 5 个 mapping 里 save/update/bathDelete **整段被注释** ⇒ **1 读 + 1 写**、无导出。 ★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`, generator 写 `sysCode`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:分页是四表内连接,本地 `operator_info` **0 行** ⇒ 恒空页; `queryConnectorInfoByPrimaryKey` 查不到 ⇒ 服务层 `copyProperties(null)` 500, 「设备id不存在」分支是死代码;本地 7 行都有 `qr_code_url` ⇒ `generator {id:1}` 停在 1002「太阳码已经存在」,不调微信、不写库。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十五切片:⚠B 里的 `OperatorInfoController`(2 个有效端点整类迁完)搬走** ⇒ **⚠B 35/114 → 34/112、已迁 172/997 → 173/999、未迁 39/121 → 38/119**。 ★ 落在 `charge` 域(`POST /admin-api/charge/operator-info/*`), 老类在 `portal.web.battery` 包、老路由 `/operatorInfoManage`; 老类 4 个 mapping 里 save/bathDelete **整段被注释** ⇒ **1 读 + 1 写**、无导出。 ★ 坑 22 安全:page 无条件写登录 `orgId` + 注入 `${project.syscode}`, update 写 `sOpId`/`sOpName`/`modifyEmpId`/`modifyEmpName`/`modifyTime`/`sysCode`; **不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:`PageBeanUtil` 只在 list 非空时 copy,本地 `operator_info` **0 行** ⇒ page 恒空页;update 走 selective,不存在的 id 命中 0 行回 `false`; `sOpId`/`sOpName` 域对象没有对应字段 ⇒ 拷不过去。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十四切片:⚠B 里的 `ChargeRecordController`(2 端点整类迁完)搬走** ⇒ **⚠B 36/116 → 35/114、已迁 171/995 → 172/997、未迁 40/123 → 39/121**。 ★ 落在 `order` 域(`POST /admin-api/order/charge-record/*`), 老类在 `financialmanagement/chargerecord` 包、老路由 `/ChargeRecord`,类级 `@NoAuth` ⇒ 按 §3.3 第 1 类收紧为登录 + `order:charge-record:query`。 **2 端点 = 2 读、零写、无导出**,整类 2/2 迁完。 ★ 坑 22 安全:page 判 `userInfo`/`orgId` 并有超管豁免(非超管强制覆盖 orgId); pay-detail **完全不读登录态**(照搬);只碰 `orgId`。 ★ 存量语义照搬:日期只收 `yyyy-MM-dd HH:mm:ss` / 毫秒时间戳,纯 `yyyy-MM-dd` 是 400; `parkOutTime=null` ⇒ hutool `beginOfDay` NPE 500;`orderState` 拆箱 ⇒ `{}` 500; `orderId` 为 null ⇒ tk mapper `Value for rltOrderId cannot be null` 500。 ★ 本地 org 10003 `2017-09-25` 真读 **41 行**(首行 id=2048609 / state=50)、 `2017-09-28` **1 行**、9/2 归一末页首行 2048479;`orgId=10007` 被强制回 10003 仍 41 行。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十三切片:⚠B 里的 `TdCCommonProblemController`(4 端点整类迁完)搬走** ⇒ **⚠B 37/120 → 36/116、已迁 170/991 → 171/995、未迁 41/127 → 40/123**。 ★ 落在 `pay` 域(`POST /admin-api/pay/common-problem/*`), 老类在 `portal.web.commonproblem` 包、老路由 `/tdCCommonProblemManage`; **1 读 + 3 写**,无导出,整类 4/4 迁完。 ★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈 `@RestController` + `CommonResult.success` + `BizResultAdapter`。 ★ 坑 22 安全:4 个端点**全部**判登录;page/save 写 `orgId`、save 写 `sOpId`/`createEmpId`/`createEmpName`、update 写 `sOpId`/`modifyEmpId`/`modifyEmpName`、 batchDelete 写 `opId`/`opName`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:**分页服务层 `PageInfo` 整段被注释 ⇒ page 恒空页**(本地 13 行活数据也不读); save 无校验、任何 body(含 `{}`)都真插一行;`update {}` ⇒ 1003「id为空!」、 `update id=不存在` 空 SET 被 Druid 拒 500;bathDelete 是软删(`data_state=0`)、 `{}`/`ids=[]` ⇒ 1003、不存在 id 回 false。 ★ `TdCCommonProblemRequest` 无 `question`/`answer` 字段、`createEmpId` 与域对象 `createEmpid` 大小写不同 ⇒ 拷不过去,save 实际只落 `org_id`+`create_date`。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+220/轮)。 ★★ **2026-10-19 第一百二十二切片:⚠B 里的 `TdCouponRuleController`(5 端点整类迁完)搬走** ⇒ **⚠B 38/125 → 37/120、已迁 169/986 → 170/991、未迁 42/132 → 41/127**。 ★ 落在 `pay` 域(`POST /admin-api/pay/coupon-rule/*` + `GET /export-excel`), 老类在 `pay.controller.coupon` 包、老路由 `/tdCoupon`;**1 读 + 3 写 + 1 导出**,整类 5/5 迁完。 ★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈 `@RestController` + `CommonResult.success` + `BizResultAdapter`。 ★ 坑 22 安全:save/query 读 `orgId`/`opId`/`name`,delete/deleteBatch **完全不读登录态**; **不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:save 由 `flg` 分支(`Integer` 拆箱 ⇒ `flg=null` NPE 500), 服务层另按 `cardType==2` 分叉;delete 只在 `id != null` 时软删 + 按 `card_no` 删关系表; deleteBatch `andIdIn(ids)` 空 ⇒ tk mapper 500;导出 `BaseInfo(1, MAX)` 一次取全量, `couponsType`/`isUesed` 拆箱、时间列判空缺失 ⇒ 存量缺陷。 ★ 本地 org 10003 真读 **46 行**(首行 id=10169),`pageNum=9&pageSize=2` 被 reasonable 归一成末页 1 行(id=10132)。 ★ 导出老 `@NoAuth` → 登录 + `:export`,10 列、参数全可选,真出 20KB+ xlsx。 ★ 老类 3 个写端点带 `@SysOpLog` ⇒ 3 个 `@OperateLog` ⇒ 零写基线 **+214 → +220/轮**(冒烟发不落库样本,只多记 3 条日志)。 ★★ **2026-10-19 第一百二十一切片:⚠B 里的 `ParkBerthAppointmentConfigRelController`(3 端点整类迁完)搬走** ⇒ **⚠B 39/128 → 38/125、已迁 168/983 → 169/986、未迁 43/135 → 42/132**。 ★ 落在 `park` 域(`POST /admin-api/park/park-berth-appointment-config-rel/*`), 老类在 `parkberthappointmentconfig` 包、老路由 `/parkBerthAppointmentConfigRle` (★ 存量拼写 `Rle`);**1 读 + 2 写**,整类 3/3 迁完。 ★ 老类 `@Controller` + 每方法 `@ResponseBody`、`extends BizController` ⇒ 新栈 `@RestController` + `CommonResult.success` + `BizResultAdapter`。 ★ 坑 22 安全:3 个端点**全部**判登录;page 写 `orgId`、save 写 `sOpId`/`createEmpId`/ `createEmpName`、batchDelete 什么都不写;**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:page 内联 `park_berth_appointment_config`、两边 `data_state=1`、 按 `b.org_id` 过滤、`order by a.id desc`;本地 org 10003 真读 **3 行**(首行 id=7), plName 由 Dubbo 回填;不传 `baseRequest` 不炸(默认 10 行); `pageNum=9&pageSize=2` 被 reasonable **归一成末页 1 行(id=2)**; `appointmentConfigName` 的 like 过滤「医院」命中 2 行; ⛔ save **先按 `pl_noList` 物理删再逐条插**(空列表不删不插回 false); ⛔ batchDelete 物理删、`ids` 空 ⇒ 1003「id为空!」。 ★ ⛔ 两个写端点:save 传真 `plNoList` 会真删真插、batchDelete 会真删, 冒烟只发无 body(绑定层 400)+ `plNoList=[]`;本地 3 行一行不动。 ★ 老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变(+214/轮)。 ★★ **2026-10-19 第一百二十切片:⚠B 里的 `EtcSettlementController`(4 端点整类迁完)搬走** ⇒ **⚠B 40/132 → 39/128、已迁 167/979 → 168/983、未迁 44/139 → 43/135**。 ★ 落在 `order` 域(`POST /admin-api/order/etc-settlement/*` + `GET /export-excel`), 老类在 `financialmanagement/clearsettlement` 包、老路由 `/EtcSettlement`; **2 读 + 1 分页 + 1 导出**,整类 4/4 迁完。 ★ 老类 `extends BizController` ⇒ 只借道 `BizResultVO`/`returnJqGridData`, 新栈改 `CommonResult.success` + `BizResultAdapter.getDataOrThrow/toPageResult`。 ★ 坑 22 安全:3 个 POST 判 `userInfo == null`、写 `orgIds` = 登录 `orgId`、 `opId` 只用于 `isSuperOpId` 超管判定;导出**不读登录态**(老方法里的登录判定 整段被注释),读**请求参数** `optId`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:**`queryMchInfoList` 完全忽略入参**(服务层全表查 `td_b_etc_config` + 回填 appId/mchName),本地 0 行 ⇒ `code=0 + []`;`appIds` 的补齐在**服务层** (空则按 `orgIds + payType=32` 查 `sys_org_app_id`,查不到 ⇒ `BIZ_ERROR "appId为空!"` ⇒ 500/1002);**`baseRequest` 不会 NPE**(字段初始化 + 懒 getter + `reasonable=true`) ⇒ 不传 `baseRequest` 实测 total 500 / 默认 10 行;导出 `getCheckResultType()==1`、 `getSettlementState()==0` 是 `Integer` 拆箱,`DateUtil.format` 不判空; `isSuperOpId` 对 null map `forEach` ⇒ NPE。照搬。 ★ 本地 org 10003 绑 ETC 商户 1504005 ⇒ `td_b_etc_pay_order_check_result` 500 行 (2020 年)是真读:2020 窗口 `totalFee=217134、totalCount=500、已结算 494、未结算 6`。 ★ 导出老 `@NoAuth` → 登录 + `:export`,5 必填 / `appId` 可选;先查一次拿 pages 再把 pageSize 改成 **1000** 逐页取,18 列、起始行 `pageSize*(num-1)+2`。 ★ ⛔ 全类只读/导出,无写端点;老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变。 ★★ **2026-10-19 第一百一十九切片:⚠B 里的 `EtcReconciliationController`(5 端点整类迁完)搬走** ⇒ **⚠B 41/137 → 40/132、已迁 166/974 → 167/979、未迁 45/144 → 44/139**。 ★ 落在 `order` 域(`POST /admin-api/order/etc-reconciliation/*` + `GET /export-excel`), 老类在 `financialmanagement/reconciliation` 包、老路由 `/etc/reconciliation`; **2 对账读 + 2 异常流水动作 + 1 导出**,整类 5/5 迁完。 ★ 老类 `extends ReconciliationBaseController`(基类只有一个 `getPayOrderTypeName`、 类上无 mapping)⇒ 新类**整段照搬**那一个方法,不继承、不抽公共类。 ★ 坑 22 安全:total/page 读 `orgId`(进 `orgIds`)+ `opId`(只用于超管判定); feedBack 读 `opId`/`name`(**不判空**);异常流水查询**完全不读登录态**; 导出读**请求参数** `optId`/`orgId`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 存量语义照搬:`dateType=null` 时 page 控制器拆箱 NPE、total 服务层 `.equals(2)` NPE, 都是 **500**;page **不传 `baseRequest` 不炸**(服务层 `PageBeanUtil.copyProperties` 补默认 BaseInfo ⇒ total 500 / 默认 10 行);异常流水 `{}` 500、假 appId 4014, 均停在外部调用/回写之前;导出老 `@NoAuth` → 登录 + `:export`,6 个必填 `@RequestParam`。 ★ 本地 org 10003 绑 ETC 商户 1504005 ⇒ `td_b_etc_pay_order_check_result` 500 行 (2020 年)是真读:日口径汇总 217134、8 月月口径 35680/124 行。 ★ ⛔ 两个异常流水端点**可能落库**(回写 `check_state` / 改平账), 冒烟只发炸关口样本;老类零 `@SysOpLog` ⇒ 零 `@OperateLog`,零写基线不变。 ★★ **2026-10-19 第一百一十八切片:⚠B 里的 `OrderChargeInfoController`(3 端点整类迁完)搬走** ⇒ **⚠B 42/140 → 41/137、已迁 165/971 → 166/974、未迁 46/147 → 45/144**。 ★ 落在 `order` 域(`POST /admin-api/order/order-charge-info/*`),老类在 `battery` 包、 老路由 `/orderChargeInfoManage`;**2 读 + 1 退款动作**,整类 3/3 迁完。 ★ 类级去 `Manage` 后 kebab;方法段按老 mapping 逐字 kebab。 ★ 坑 22 安全:3 个端点**全部**判 `userInfo == null`;page 写 `orgId`(★ DAO 的 smapper **确实用它过滤**)与写死 `sysCode=10001`;退款/日志查询写 `sOpId`/`sOpName` (日志查询其实没用这两列,照搬);**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+214/轮 不变**。 ★ 存量语义照搬:page 固定 `data_state=1 and order_state > 30` + 登录人 `orgId` ⇒ 本地 org 10003 **0 行**(有数据时还会经 Dubbo 查充电站位置,走不到); `platformManualRefund` 先 `queryByOrderId(operatorId, orderId)` ⇒ `{}` 因 null 条件在 tk-mapper 直接抛异常 **500**,给不存在编号 ⇒ **5010 没有找到充电订单**, 两条都在任何退款/外部调用之前;`queryOperatorLog` 的 DAO 直接 `andOrderIdEqualTo(null)` ⇒ `{}` **500「系统异常」**,条件齐了回 `[]`。 ★ ⛔ `platformManualRefund` 带**真实已支付 orderId + operatorId** 会真发起退款并调 「停止充电」外部接口 ⇒ 冒烟只发 `{}`/不存在编号/无 body,业务表行数已实测不变。 ★ 该三张表(`order_charge_info`/`order_charge_log`/`order_charge_operate_log`)已并入 零写计数脚本(`/tmp/zwcount66.py`)。 ★★ **2026-10-19 第一百一十七切片:⚠B 里的 `InvoiceOperController`(5 端点整类迁完)搬走** ⇒ **⚠B 43/145 → 42/140、已迁 164/966 → 165/971、未迁 47/152 → 46/147**。 ★ 落在 `pay` 域(`POST /admin-api/pay/invoice-oper/*`),老类在 `invoice` 包、 老路由 `/invoice/oper`;**1 改状态 + 3 开票/下载动作 + 1 个 `@NoAuth` 测试端点**, 整类 5/5 迁完。 ★ 坑 22 安全:`updateInvoiceStateById` **不判空**直接取 `opId`/`name`(照搬); `openInvoiceRecord` **完全不读登录态**;另两条写 `createEmpid`/`modifiyEmpid` (`openInvoiceListByOrderIds` 另写 `orgId`,`downTheInvoice` **不写**); **不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 3 处 `@SysOpLog` ⇒ 新类 3 处 `@OperateLog` ⇒ 零写基线 **+209 → +214/轮**。 ★ 存量语义照搬:`updateInvoiceStateById` 的 `@Valid`(id/invoiceState)挡成 400; 给不存在 id ⇒ 0 行但**无条件回 true**;`openRecord {}` 在 `saleTaxid.substring` 处 NPE 500;`openList` 先判 orderIds(1003)再判 orgId,订单不存在 1002; `downInvoice` **不设 orgId** ⇒ 销方查询 null ⇒ 1002。 ★ `openInvoiceRecordTest` 老 `@NoAuth` 按 §3.3 收紧,且服务层会调**外部金税 WebService** ⇒ ⛔ 冒烟只发无 body。 ★ ⛔ `openRecord` 带 `saleTaxid` 会真插历史+条目、`openList`/`downInvoice` 会调 外部发票服务并改状态 ⇒ 冒烟只发炸关口样本,业务表行数已实测不变。 ★★ **2026-10-19 第一百一十六切片:⚠B 里的 `ParkingChargingRulesController`(5 端点整类迁完)搬走** ⇒ **⚠B 44/150 → 43/145、已迁 163/961 → 164/966、未迁 48/157 → 47/152**。 ★ 落在 `bill` 域(`POST /admin-api/bill/parking-charging-rules/*`),老类在 `parkinglotcloudplatform/closecarpark` 包、老路由 `/parkingChargingRules`; **1 读 + 4 写**,整类 5/5 迁完。 ★ 类级与方法段均按老名字 kebab 逐字搬。 ★ 坑 22 安全:query/save/update **判** `userInfo == null`;delete/view **老代码根本不取登录态**(照搬,不补校验);只碰 `orgId`/`createBy`, **不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**分页参数在 `baseRequest` 里**(直接传 pageNum/pageSize 被忽略, 默认 0/10);page 按 `orgId`+`data_flg='1'`+`fee_type=1` 过滤(本地 8 行); `save` **先插主表再遍历 detailList** ⇒ `{}` 会先落一行再 NPE ⇒ ⛔ 绝不发; `update` 的 `andRuleCollectIdEqualTo(null)` 直接抛异常 ⇒ **500,异常在写之前**; `delete` 先 `checkBindPark`(空 ruleCollectId ⇒ 500),不存在 id ⇒ 0 行返回 0; `view` 对结果 `.get(0)` ⇒ 查不到 **IndexOutOfBounds 500**。 ★ ⛔ `save`/`update` 带 detailList 会真写 `td_cl_rule_collect`/`td_cl_rule`/ `td_cl_rule_detail` ⇒ 冒烟只发无 body 与炸关口样本,三表行数已实测不变。 ★★ **2026-10-19 第一百一十五切片:⚠B 里的 `SpecialCarnumberBillingController`(5 端点整类迁完)搬走** ⇒ **⚠B 45/155 → 44/150、已迁 162/956 → 163/961、未迁 49/162 → 48/157**。 ★ 落在 `pay` 域(`POST /admin-api/pay/special-carnumber-billing/*`),老类在 `specialcar` 包、 老路由 `/specialCarnumberBillingManage`;**1 读 + 4 写**,整类 5/5 迁完。 ★ 类级去 `Manage` 后 kebab;方法段按老 mapping 逐字 kebab(`bath-delete` 保留拼写)。 ★ 坑 22 安全:5 个端点都判 `userInfo == null`;page 只写 `orgId`(★ DAO 没用它), save 另写 `createEmpId`/`createEmpName`,update 写 `modifyEmpId`/`modifyEmpName`, batchDelete/del 写 `opId`/`opName`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**page 不按组织过滤**(本地 220 行活数据全可见,默认 0/10 ⇒ `page {}` = total 220 / 10 条);`save` 手写判手机/车牌非空再查重; `update`/`del` 先 `queryByPrimaryKey().get(0)` ⇒ 不存在 id **IndexOutOfBounds 500** (异常在写之前);`del` 还会关闭 `acct_type=5` 的免密;`bathDelete` 逻辑删, 传 `getsOpName()` 的存量错位照搬。 ★ ⛔ `save {phoneNum,carNumber}` 会真插主表并可能建客户/账户/车牌 ⇒ 冒烟只发 `{}`(判空 1003)与无 body。 ★★ **2026-10-19 第一百一十四切片:⚠B 里的 `TdCCustBusinessController`(5 端点整类迁完)搬走** ⇒ **⚠B 46/160 → 45/155、已迁 161/951 → 162/956、未迁 50/167 → 49/162**。 ★ 落在 `pay` 域(`POST /admin-api/pay/cust-business/*`),老类在 `custperson` 包、 老路由 `/tdCCustBusinessManage`;**1 读 + 3 写 + 1 审核动作**,整类 5/5 迁完。 ★ 类级与方法段都去 `TdC` 前缀后按 kebab 逐字搬;`examine` 审核动作复用 `:update` 权限。 ★ 坑 22 安全:5 个端点都判 `userInfo == null`;page 只写 `orgId`, save/update 另写 `opId`/`name`,batchDelete 写 `opId`/`opName`,examine 写 `createEmpid`/`createEmpname`/`createDate`/`examineDate`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:本地三表全 **0 行** ⇒ page 恒 `total=0 / list=[]`; `update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`;域对象无 `sOpId` ⇒ `update {id}` 空 SET 被 Druid 拒 ⇒ **500 且不改行**;`examine {}` 在 `getExamineState().equals(2)` 处 **NPE 500**(DAO 之前,不落库); `bathDelete` 是逻辑删;`batchDelete` 传 `getsOpName()` 的存量错位照搬。 ★ ⛔ `save {}` 会真插一行、⛔ `examine {examineState}` 会改主表 + 插审核记录 ⇒ 冒烟只发无 body 与 `examine {}`(NPE)。 ★★ **2026-10-19 第一百一十三切片:⚠B 里的 `TdCCustBusinessCheckTypeController`(4 端点整类迁完)搬走** ⇒ **⚠B 47/164 → 46/160、已迁 160/947 → 161/951、未迁 51/171 → 50/167**。 ★ 落在 `pay` 域(`POST /admin-api/pay/cust-business-check-type/*`), 老类在 `custperson` 包、老路由 `/tdCCustBusinessCheckTypeManage`; **1 读 + 3 写**,**整类 4/4 全部迁完,无 P3 尾巴**。 ★ 类级与方法段都去 `TdC` 前缀后按 kebab 逐字搬(沿同包 `TdCCustBusinessCheckRecordController` → `/pay/cust-business-check-record` 的风格)。 ★ 坑 22 安全:4 个端点都判 `userInfo == null`;page 只写 `orgId`, save/update 另写 `opId`/`name`,batchDelete 写 `opId`/`opName`;**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**page 服务层整段被注释 ⇒ 恒 `total=0 / list=[]`**(DAO 也没有分页方法); `queryTdCCustBusinessCheckType` 的 DAO 死方法 `return null`(老门户没调用); Request 的 `createEmpId`/`modifyEmpId` 与域对象 `createEmpid`/`modfiyEmpid` 拼写不同 ⇒ `update {id}` 空 SET 被 Druid 拒 ⇒ **500「系统异常」且不改行**; `bathDelete` 是**逻辑删除**(`data_state=0`,`where id in (?)`);`batchDelete` 传 `getsOpName()` 的存量错位照搬。 ★ 本地 `td_c_cust_business_check_type` **0 行** ⇒ 读断言钉 `total=0 / []`; ⛔ `save {}` 会真插一行(DAO 补 `createDate`),冒烟只发无 body(绑定层 400)。 ★★ **2026-10-19 第一百一十二切片:⚠B 里的 `ParkBerthAppointmentConfigController`(5 端点整类迁完)搬走** ⇒ **⚠B 48/169 → 47/164、已迁 159/942 → 160/947、未迁 52/176 → 51/171**。 ★ 落在 `park` 域(`POST /admin-api/park/park-berth-appointment-config/*`), 老类在 `parkberthappointmentconfig` 包、老路由 `/parkBerthAppointmentConfigManage`; **2 读 + 3 写**,**整类 5/5 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab(`/bath-delete-…` 保留存量拼写);类级按类名 kebab。 ★ 坑 22 安全:5 个端点都判 `userInfo == null`;page/list 只写 `orgId`, save/update 另写 `opId`/`name`,batchDelete 什么都不写;**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**page** 过滤 `data_state=1` + `org_id`;**list** 额外叠 `rule_state=1`;`update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`; ⛔ **`bathDelete` 是物理删除**(先删主表、再删 `park_berth_appointment_park_rel`, 影响行数相加),不是逻辑删。 ★ ★ 本地两表都有活数据:`park_berth_appointment_config` **2 行**(id=2、3 / org 10003)、 `park_berth_appointment_park_rel` **3 行** ⇒ page/list 是**活读**,冒烟按真实数据钉 `total=2 / 2 条`;⛔ 绝不发真实 id、save 只发无 body。实测两表前后不变(2→2、3→3)。 ★★ **2026-10-19 第一百一十一切片:⚠B 里的 `ArrearageSmsAutoSendConfigController`(5 端点整类迁完)搬走** ⇒ **⚠B 49/174 → 48/169、已迁 158/937 → 159/942、未迁 53/181 → 52/176**。 ★ 落在 `order` 域(`POST /admin-api/order/arrearage-sms-auto-send-config/*`), 老类在 `arrautosendconfig` 包、老路由 `/arrearageSmsAutoSendConfigManage`;**2 读 + 3 写**, **整类 5/5 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab(`/bath-delete-…` 保留存量拼写);类级按类名 kebab。 ★ 坑 22 安全:5 个端点都判 `userInfo == null`,但只写 `orgId`/`opId`/`name`, **不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有一处 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:`queryArrearageSmsAutoSendConfig` 只取 list `get(0)`; `handelCronStr` 在判空之前执行(空 ⇒ "";不足 6 段 ⇒ `BIZ_ERROR「cron 表达式错误!」` ⇒ code 500);老 Request 的 `modifyEmpId` 与域对象 `modfiyEmpId` **拼写不同**、`BeanUtils.copyProperties` 拷不过去 ⇒ selective update 的 SET 只剩 `cron_str`;`bathDelete` 是逻辑删除(`data_state=0`)。 ★ ★ 与最近几片不同:本地 `arrearage_sms_auto_send_config` **有 1 行活数据** (id=1 / org 10003 / data_state=1,id=2 已删)⇒ page/query 是**活读**, 冒烟按真实数据钉 `total=1`、`id=1`;⛔ 别给 ids 传 1、别给 save 传 `{}`。 ★ 实测冒烟前后该表 **2 → 2** 未变,零写差分只动 `system_operate_log`。 ★★ **2026-10-19 第一百一十切片:⚠B 里的 `TdCCustBusinessCheckRecordController`(5 端点整类迁完)搬走** ⇒ **⚠B 50/179 → 49/174、已迁 157/932 → 158/937、未迁 54/186 → 53/181**。 ★ 落在 `pay` 域(`POST /admin-api/pay/cust-business-check-record/*`), 老类在 `custperson` 包、老路由 `/tdCCustBusinessCheckRecordManage`;**2 读 + 3 写**, **整类 5/5 全部迁完,无 P3 尾巴**。 ★ 类级与方法段**都把 `TdC` 前缀去掉后按 kebab 逐字搬**(沿 `TdCCustSuggestDetailController` / `TdCCustBusinessPicController` 的既有风格)。 ★ 坑 22 安全:`query-cust-business-check-record` **完全不读登录态**;其余 4 个端点 只写 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**page 服务层整段被注释** ⇒ 恒空页;list 的 `andBusinessIdEqualTo(null)` 抛 ⇒ **500**;`save {}` / `update id=1` 的 selective SET 为空被 Druid 拒(`expect SET, actual WHERE`)⇒ **500 且不落库**; `update {}` / `bathDelete {}` ⇒ 控制器 `PARAMM_NULL`;`bathDelete ids=[999999999]` 命中 0 行 ⇒ false(逻辑删 `data_state=0`)。 ★ ⛔ `save` 仍只发无 body(绑定层 400);本地 `td_c_cust_business_check_record` 0 行, 实测前后不变。 ★★ **2026-10-19 第一百零九切片:⚠B 里的 `SmsTemplateConfigRltParkingController`(5 端点整类迁完)搬走** ⇒ **⚠B 51/184 → 50/179、已迁 156/927 → 157/932、未迁 55/191 → 54/186**。 ★ 落在 `park` 域(`POST /admin-api/park/sms-template-config-rlt-parking/*`), 老类在 `msg` 包、老路由 `/smsTemplateConfigRltParkingManage`;**2 读 + 3 写**, **整类 5/5 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab(`/query-…-for-page`、`/query-…-for-list`、 `/save-…`、`/update-…-by-id`、`/bath-delete-…`);类级按类名 kebab。 ★ 坑 22 安全:5 个端点**全部**读登录态判空,但只碰 `orgId`/`opId`/`name`, **不读** `plNos`/`userAreas`/`resIds`;page/list 的 `orgId` **不判空直接覆盖**。 ★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:**page 服务层整段被注释** ⇒ 恒空页(`total=0 / list=[]`); list 按登录 org=10003 查,本地 46 行全 `org_id=NULL` ⇒ 恒 `[]`; `save` 缺 smsId / `update` 缺 id / `bathDelete` 缺 ids ⇒ 控制器抛 `PARAMM_NULL` (HTTP 200 + `code=500` + 存量错误码 1003); `update id=1` / `bathDelete ids=[1]` 因 DAO 是 TODO 空实现 ⇒ 恒 `false`。 ★ ⛔ `save` 给**已存在的 smsId** 会先 `deleteByExample` **真删**该 smsId 下所有关联、 再按 `templateConfigRltParkList` 插新 ⇒ 冒烟只发无 body / `{}` / 空 smsId, `sms_template_config_rlt_parking` 行数实测不变(46 → 46)。 ★★ **2026-10-18 第一百零八切片:⚠B 里的 `CardRuleManageController`(6 端点整类迁完)搬走** ⇒ **⚠B 52/190 → 51/184、已迁 155/921 → 156/927、未迁 56/197 → 55/191**。 ★ 落在 `bill` 域(`POST /admin-api/bill/card-rule-manage/*`),老类在 `closecarpark` 包、老路由 `/cardRuleManage`;**2 读 + 3 写 + 1 查看**, **整类 6/6 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab;类级按类名 kebab,沿同目录 `SpecialCarManageController` / `SubCardInfoManageController` 的风格。 ★ 坑 22 安全:只读 `orgId`,**不读** `plNos`/`userAreas`/`resIds`; `query-card-rule-list-manage` / `update` / `delete` / `view` **完全不读登录态**。 ★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:本地 `td_cl_sub_card_rule` 0 行 ⇒ page total 0 / list []; page 按 `data_status = 1 + org_id`,list **只按 data_status = 1、不按组织**(两条口径不同); `view {}` / `view id=1` 都查不到 ⇒ `BeanUtils.copyProperties(null, dto)` 抛 `Source must not be null` ⇒ **500**;`update {}` / `delete {}` 主键 null ⇒ 更新 0 行、 回 false(delete 是逻辑删除 `data_status=0`)。 ★ ⛔ `save` 服务层无校验、给 `{}` 会真插一行 `td_cl_sub_card_rule` ⇒ 冒烟只发无 body(绑定层 400);该表行数实测不变。 ★★ **2026-10-18 第一百零七切片:⚠B 里的 `CompanyStoreAccountController`(6 端点整类迁完)搬走** ⇒ **⚠B 53/196 → 52/190、已迁 154/915 → 155/921、未迁 57/203 → 56/197**。 ★ 落在 `pay` 域(`POST /admin-api/pay/company-store-account/*`),老类在 `closecarpark` 包、老路由 `/companyAccount`;**3 读 + 3 写**, **整类 6/6 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab(`/query-company-account`、`/save-company-account-info`、 `/update-company-account-info`、`/view-company-account`、`/operate-company-account`、 `/query-company-account-flow`);类级按类名 kebab。 ★ 坑 22 安全:登录态只读 `orgId`/`name`,**不读** `plNos`/`userAreas`/`resIds`; `view` / `query-company-account-flow` **完全不读登录态**。 ★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 存量语义照搬:分页 SMapper 的 SQL 以 `where 1=1` 结尾,本地 Druid `filters=stat,wall` 直接拒(`select alway true condition not allow`)⇒ **page 恒 500**; 老 `zteits-pay` 的 `druid.filters=stat,wall` 是同一份配置 ⇒ 老栈同样 500,不是迁移回归。 `view {}` 同一条 SQL ⇒ 500;`view accCode=不存在` 查询回 null、 `BeanUtils.copyProperties(null, dto)` 抛 `Source must not be null` ⇒ 500。 `save {}` 撞 `andCustomerNoEqualTo(null)` ⇒ 500;`update {}` 因空 `parkList` 的 `record.get(0)` 越界 ⇒ 500;flow 本地 0 行 ⇒ total 0 / list []。 ★ ⛔ `operate` 给 `{}` 仍会调 `operateCompanyAccountFlow` **真插一行** `td_cl_company_acc_flow` ⇒ 冒烟只发无 body(绑定层 400);三张 `td_cl_company_acc*` 表行数实测不变。 ★★ **2026-10-18 第一百零六切片:⚠B 里的 `ParkingSegmentChargingController`(6 端点整类迁完)搬走** ⇒ **⚠B 54/202 → 53/196、已迁 153/909 → 154/915、未迁 58/209 → 57/203**。 ★ 落在 `bill` 域(`POST /admin-api/bill/parking-segment-charging/*`),老类在 `closecarpark` 包、老路由 `/parkingSegmentCharging`(**无前导斜杠**); **2 读 + 3 写 + 1 查看**,**整类 6/6 全部迁完,无 P3 尾巴**。 ★ 方法段按老 mapping 逐字 kebab(`/query-parking-segment-charging`、 `/query-parking-charging-rules-list`、`/save-…`、`/update-…`、`/delete-…`、`/view-…`), 沿同目录 `SpecialCarManageController` / `SubCardInfoManageController` 的风格。 ★ 坑 22 安全:登录态只读 `orgId`/`name`,**不读** `plNos`/`userAreas`/`resIds`; `delete` / `view` **完全不读登录态**。 ★ 登录 `orgId` 是「非 null 才覆盖」、**没有超管分支**(`-1` 原样写入)——与其它类不同,照搬。 ★ 老类 **没有 `@SysOpLog`** ⇒ 新类无 `@OperateLog` ⇒ 零写基线 **+209/轮 不变**。 ★ 两条读端点口径不同:分页 SQL 只查 `fee_type = 2 and data_flg = 1`(本地 6 行); 规则列表走 `selectByExample`,只按 org + `data_flg = 1`、**不过滤 fee_type**(本地 14 行)。 ★ 存量语义照搬:`update {}` / `delete {}` 均因 Example criteria `Value for ruleCollectId cannot be null` ⇒ **500**;`view {}` 的 id 是原始类型 `long` 默认 0 ⇒ 查不到回 null;`save` 返回**最后一条详情**的插入行数,无详情恒 `false`。 ★ ⛔ `save` 给 `{}` 会真插一行 `td_cl_rule_collect`(列全可空、ID 雪花生成)却仍回 false ⇒ 冒烟只发无 body(绑定层 400);三张 `td_cl_*` 表行数实测不变。 ★★ **2026-10-18 第一百零五切片:⚠B 里的 `TBlackRuleListController`(6 端点整类迁完)搬走** ⇒ **⚠B 55/208 → 54/202、已迁 152/903 → 153/909、未迁 59/215 → 58/209**。 ★ 落在 `bill` 域(`POST /admin-api/bill/black-rule-list/*`),老路由 `/tBlackRuleListManage` (黑名单规则列表管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。 ★ ★ 与已迁的 `BlackListRuleController`(`/bill/black-list-rule`,表 `t_black_list_rule`) **不是同一个类**:本类操作 `t_black_rule_list`,路由/权限前缀都不同,别混。 ★ CRUD 段沿同模块 `TVipAreaCardController` 的语义命名(`/list`、`/page`、`/create`、 `/update`、`/delete`、`/delete-list`);老方法名 `bathDelete…` 的错拼只留在方法名。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 两个读端点老代码直接 `getUserInfo().getOrgId()`、**不判空**(无 token 先被过滤器拦); save 判 `userInfo == null` 后写 `sOpId`/`sOpName`/`orgId`;其余三个只判 `userInfo == null`。 ★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(create/update/delete/delete-list); 冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+206 → +209/轮**。 ★ 存量语义照搬:SMapper 真 SQL 是 `data_state = 1 and org_id = #{orgId}`(可加 carNum/ ruleType/ruleState/carNums),本地 org=10003 有 **3 行**;`update {}` 动态 SQL 无 SET 段 ⇒ **500**;`delete-list {}` 因 `andIdIn(null)` ⇒ **500**;`delete {}` 主键 null ⇒ 更新 0 行、 回 false;两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人 ID 恒 null(照搬)。 ★ ⛔ `create` 服务层只强制 `auditorStatus=2`/`ruleState=1`、`ruleNo` 由 SequenceUtil 生成, 给合法 body 会真插一行 ⇒ 冒烟只发无 body(绑定层 400)。 ★★ **2026-10-18 第一百零四切片:⚠B 里的 `TdBPayAppController`(6 端点整类迁完)搬走** ⇒ **⚠B 56/214 → 55/208、已迁 151/897 → 152/903、未迁 60/221 → 59/215**。 ★ 落在 `order` 域(`POST /admin-api/order/pay-app/*`),老路由 `/tdBPayAppManage` (支付APP应用配置管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。 ★ 路由风格照同模块同族 `TdBPayProductController`(`/order/pay-product`): 方法段逐字 kebab(`/query-td-b-pay-app-list` 等),不改成语义 CRUD 段。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 `queryTdBPayAppList` **完全不读登录态**;page/save 判 `userInfo == null || orgId == null`, 非超管(`orgId != -1`)才覆盖 `orgId`;其余三个写端点只判 `userInfo == null`。 ★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(save/update/delete/bathDelete); 冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+203 → +206/轮**。 ★ 存量语义照搬:列表 DAO 是 **TODO 空实现、恒 return null** ⇒ 端点**永远 `[]`**; 分页按登录 `orgId` + `data_state=1` 过滤(本地样本那行属 10079)⇒ 0 行; `update {}` / `delete {}` 主键 null ⇒ 更新 0 行、回 `code=0 + data=false`; `bath {}` 因 `andPayAppIdIn(null)` 抛 RuntimeException ⇒ **500**(老栈同); 两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人 ID 恒 null; DAO 的 `save` 第二处判空仍判 `createTime`(本意 `modifyTime`)——全部照搬。 ★ ⛔ `save` 服务层无校验,给 body 会真插一行 `td_b_pay_app` ⇒ 冒烟只发无 body(绑定层 400)。 ★★ **2026-10-18 第一百零三切片:⚠B 里的 `TdBPayChannelController`(6 端点整类迁完)搬走** ⇒ **⚠B 57/220 → 56/214、已迁 150/891 → 151/897、未迁 61/227 → 60/221**。 ★ 落在 `order` 域(`POST /admin-api/order/pay-channel/*`),老路由 `/tdBPayChannelManage` (支付渠道管理);**2 读 + 4 写**,**整类 6/6 全部迁完,无 P3 尾巴**。 ★ 路由风格照同模块同族 `TdBPayProductController`(`/order/pay-product`): 方法段逐字 kebab(`/query-td-b-pay-channel-list` 等),不改成语义 CRUD 段。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 `queryTdBPayChannelList` **完全不读登录态**;page/save 判 `userInfo == null || orgId == null`, 非超管(`orgId != -1`)才覆盖 `orgId`;其余三个写端点只判 `userInfo == null`。 ★ 老类 **4 处 `@SysOpLog`** ⇒ 4 个 `@OperateLog`(save/update/delete/bathDelete); 冒烟每轮有 3 条带 `{}` 的写请求进控制器 ⇒ 零写基线 **+200 → +203/轮**。 ★ 存量语义照搬:列表 DAO 是 **TODO 空实现、恒 return null** ⇒ 端点**永远 `[]`**; 分页只按登录 `orgId` 过滤;`update {}` 动态 SQL 无 SET 段 ⇒ **500**; `bath {}` 的 `andChannelIdIn(null)` ⇒ **500**;`delete {}` 更新 0 行; 两个删除端点写 `setsOpId`、服务读 `getOpId()` ⇒ 操作人恒 null。 ★ ⛔ 写样本红线:`saveTdBPayChannel` 服务层**无任何校验**、`{}` 会真插一行 `td_b_pay_channel` ⇒ 冒烟只发无 body(绑定层 400);该表已纳入零写计数表做守卫。 ★★ **2026-10-18 第一百零二切片:⚠B 里的 `TdCPoliceCarInfoController`(6 端点整类迁完)搬走** ⇒ **⚠B 58/226 → 57/220、已迁 149/885 → 150/891、未迁 62/233 → 61/227**。 ★ 落在 `pay` 域(`POST /admin-api/pay/police-car-info/*`),老路由 `/tdCPoliceCarInfoManage`(公安车辆信息);**2 读 + 4 写**(含 1 条 multipart Excel 解析),**整类 6/6 全部迁完,无 P3 尾巴**。 ★ 类名以老表前缀 `TdC` 开头,按本仓既有做法(`TdCActivityController` → `/pay/activity`、 `TdCCarCheckRecordController` → `/pay/car-check-record`)**去掉 `TdC`** 落 `/pay/police-car-info`;CRUD 段语义命名,非 CRUD 保留原方法 kebab(`4` 原样保留)。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 ★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变**。 ★ 存量语义照搬:两个 `parse` 端点**不判** `userInfo == null`(有 token 不受影响); `parsePoliceCarInfoByCarNumber` 外部失败逐条吞掉仍 `code=0`,服务层拿到图片会往 `td_c_police_car_info` **插一行**(`@Async` 副作用);`parsePoliceCarInfo4Excel` 逐行落库(循环内 N+1 存量违例)、`sysCode` 写字面量、`>200` 行 500、 列数不足 500、车牌空/手机号非数字静默跳过;`update {}` / `delete-list {}` 停在 控制器 1003。 ★ ⛔ 写样本红线:`saveTdCPoliceCarInfo` 服务层**无任何校验**、`{}` 会真插一行 `td_c_police_car_info` ⇒ 冒烟只发无 body(绑定层 400);该表已纳入零写计数表做守卫。 ★★ **2026-10-18 第一百零一切片:⚠B 里的 `TVipAreaCardController`(6 端点整类迁完)搬走** ⇒ **⚠B 59/232 → 58/226、已迁 148/879 → 149/885、未迁 63/239 → 62/233**。 ★ 落在 `bill` 域(`POST /admin-api/bill/vip-area-card/*`),老路由 `/tVipAreaCardManage` (区域卡管理);**3 读 + 3 写**,**整类 6/6 全部迁完,无 P3 尾巴**。 ★ 类名以老表前缀 `T` 开头,按本仓既有做法(`TBillingDescriptionController` → `/bill/billing-description`、`TBlackRuleParkRelController` → `/bill/black-rule-park-rel`) **去掉 `T`** 落 `/bill/vip-area-card`;CRUD 段沿用语义命名 `/list`、`/page`、`/create`、`/update`、`/delete-list`。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`; `queryParkListByAreaCardNo` **完全不读登录态**。 ★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变** (`update {}` / `delete-list {}` 停在控制器 1003;`create` 冒烟只发无 body)。 ★ 存量语义照搬:列表走 `getDataOrThrow`、分页走 `toPageResult`; `updateTVipAreaCardById` **先写操作人再判 id**;`bathDelete` 的 `ids` 判空在写 opId 之前; `queryParkListByAreaCardNo` 查不到卡规则回 `code=0 + []`(不报错)、查到取第一条的 `plGroupNo` 再查组下车场;`@Valid` 保留。 ★ ⛔ 写样本红线:`saveTVipAreaCard` 服务层**无任何校验**、`{}` 会**真插一行** `t_vip_area_card` ⇒ 冒烟只发无 body(绑定层 400);`t_vip_area_card` 已纳入零写计数表做守卫。 ★★ **2026-10-18 第一百切片:⚠B 里的 `BlackListController`(6 端点整类迁完)搬走** ⇒ **⚠B 60/238 → 59/232、已迁 147/873 → 148/879、未迁 64/245 → 63/239**。 ★ 落在 `bill` 域(`POST /admin-api/bill/black-list/*` + `GET /export-excel`), 老路由 `/blackListMng`(黑名单车辆管理);**3 读 + 3 写**(含 1 响应流导出 + 1 multipart 导入), **整类 6/6 全部迁完,无 P3 尾巴**。 ★ 与已迁的 `/admin-api/bill/black-list-rule`(第九十四切片)前缀不同,**不冲突、不合并**。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`,不读 `plNos`/`userAreas`/`resIds`。 两处例外照搬:导出老代码把登录态判断整段注释掉、`orgId` 由前端下发(缺了 1003); 导入 `sOpId` 取前端 query `userId`。 ★ 老类**零 `@SysOpLog`** ⇒ 新类零 `@OperateLog`,零写基线 **+200/轮 不变** (`addArr {}`/`save {}` 在服务层前抛 500;`invalid {}` 主键 null ⇒ 0 行)。 ★ 老 `@NoAuth` 导出按 §3.3 第 1 类收紧为 `bill:black-list:export` 且方法收紧为 GET; 导入挂 `bill:black-list:import`。 ★ 存量语义照搬:导出**不判** `beanBizResult.getData()` ⇒ data 为 null 时 NPE 500; 缺 `orgId` 是 **500 + 1003**(POJO 绑定无必填,不是 400);`invalidBlackListCarForPage` 无前置校验、`QueryBlackCarLimitRequest` 是死变量;导入 `HttpServletRequest` 是死参数、 不校验列数(列少即 IndexOutOfBounds 500)、忽略服务返回值恒回 true。 ★★ **2026-10-18 第九十九切片:⚠B 里的 `WhitelistController`(8 端点整类迁完)搬走** ⇒ **⚠B 61/246 → 60/238、已迁 146/865 → 147/873、未迁 65/253 → 64/245**。 ★ 落在 `bill` 域(`POST /admin-api/bill/whitelist/*` + `GET /export-excel`), 老路由 `/whitelist`(白名单规则);**3 读 + 3 写 + 1 响应流导出 + 1 导入解析**, **整类 8/8 全部迁完,无 P3 尾巴**。 ★ 与已有的 `/admin-api/bill/whitelist-rule-car-number` 前缀不同,**不冲突、不合并**。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`; `orgId` 用于分页过滤与新增组织列,`opId`/`name` 只写审计列。 ★ 老类 3 处 `@SysOpLog` ⇒ 3 个 `@OperateLog`(add/update/delete,module=白名单规则); `OperateLogAspect` 抛异常也记账 ⇒ 冒烟 5 次写请求 ⇒ 零写基线 +195 → **+200/轮**。 ★ 老 `@NoAuth` 两条按 §3.3 第 1 类收紧:导出 → `bill:whitelist:export`、 multipart 导入 → `bill:whitelist:import`,**不进 `@PermitAll`**。 ★ 存量语义照搬:`queryWhiteRuleById` 的 DAO 对不存在/`data_state=0` 的 id 返回**非 null 全 null DTO** ⇒ `code=0` 而不是 404;`checkWhiteUnique {}` 无必填校验直接进 SQL ⇒ 500;导出对 `result.getData()` 不判空 ⇒ NPE 500; `delete` 的 `setModifyDate` 写在 `userInfo != null` 判断之外;导入保留死变量 `parkingLotList`、`whiteRNo` 取两次、严格 4 列校验, 四条失败分支 = 500 / `1019` / `1003` / `1020`。 ★★ **2026-10-18 第九十八切片:⚠B 里的 `IncomeCalculationNewController`(迁 5/9 端点,4 个读 `plNos` 留 P3)搬走** ⇒ **⚠B 62/255 → 61/246、已迁 145/856 → 146/865、未迁 66/262 → 65/253**。 ★ 落在 `order` 域(`POST /admin-api/order/income-calculation-new/*` + `GET /export-excel` + `GET /export-calculation-excel`),老路由 `/incomeCalculation`(收益测算·新); **3 读 + 2 响应流导出 + 4 未迁**。 ★ 与第三十八切片的 `IncomeCalculationController`(老 `/income/incomeCalculation`)**同类不同文件**, 新路由按类名 kebab 落到 `income-calculation-new`,**不合并**。 ★ 坑 22:4 个未迁端点都读登录态 `userInfo.getPlNos()`(`getSummationIncomeInfoforPlTypeByTime` / `getAllIncomeCalculationDataOfAreaByTime` / `getSummationIncomeByTimeOfArea` / `getSummationIncomeByTimeOfPl`)⇒ **P3 阻塞**;已迁 5 条只读前端 `plNos`。 ★ 老类 9 个端点**一条 `@SysOpLog` 都没有** ⇒ 新类无 `@OperateLog`,**零写**。 ★ 老 `@NoAuth` 两条导出按 §3.3 第 1 类收紧为 `order:income-calculation-new:export`; 一个类两条导出 ⇒ 主导出(三 sheet)占 `/export-excel`,另一条(两 sheet)落 `/export-calculation-excel`(§3.3 第 3 条)。 ★ 存量缺陷照搬:缺 8 个金额参数(`dayMinAmount`…`nightMaxAmountAfter`)时 XML 的 IF 不成立 ⇒ SQL 渲染成 `t.day_1_hour_num * +`(空表达式)⇒ 500「系统异常」; `getInitDateList` 的 `timeType`、`queryIncomeCalculationGroupByPlNo` 的 `billType` 都是 `Integer` 拆箱 ⇒ 不传即 500;`getInitDateList` 按周分支写成 end−end, 差值恒 0 ⇒ 永远只出 1 行;导出 ① 先按 `plType` 拆 `plNos`: 缓存里「存在但全 null」的车场让两个子集都空 ⇒ 服务层 1003, **完全不在缓存**的车场让 `getParkTypePlNos` 撞 null 元素 ⇒ NPE 500; 导出 ② 整体 `try/catch(Exception)` ⇒ 失败是 **200 + 空 body**。 ★ ⛔ 全类只读/导出,无写端点;`system_operate_log` 零写基线不变。 ★★ **2026-10-18 第九十七切片:⚠B 里的 `ParkLotManageController`(迁 14/18 端点,3 个读 `plNos` 留 P3 + 1 个上传挂起)搬走** ⇒ **⚠B 63/273 → 62/255、已迁 144/838 → 145/856、未迁 67/280 → 66/262**。 ★ 落在 `park` 域(`POST /admin-api/park/park-lot-manage/*` + `GET /export-excel`), 老路由 `/parkLotManage`(停车场管理);**5 读 + 7 写 + 1 响应流导出 + 1 导入解析 + 4 未迁**。 ★ 坑 22 安全:登录态只读 `orgId`/`opId`;`plNos` 相关 3 条未迁 (`getParkLotNamesByUserId`/`refreshParkLotCache`/`getAreatreeByOpId`)⇒ **P3 阻塞**。 ★ 老类 5 处 `@SysOpLog` ⇒ 新类 5 个 `@OperateLog`;`updateBlockForParkLot` 与 `deleteParkLotPic` 老类**没有** `@SysOpLog` ⇒ 新类也不带(照搬,不"补上")。 ★ 老 `@NoAuth` 三条按 §3.3 收紧:响应流导出 → `:export`、JSON 读 → `:query`、 multipart 导入解析 → `:import`(无 token 401)。 ★ ⏸ `uploadParkLotPic` 挂起:`fastDFSClientWrapper.uploadFile(...)` 运行期依赖 阿里云 OSS/FastDFS 后端,属文件存储抽象(与 `ParkShareController` 同因)。 ★ `queryParkingLotByPkNo` / `queryParkLotByCountryAndParkName` 与 `ParkingLbsController` (老 `monitor/ParkingLBSController` 的迁移)同义,**按本类老路由另挂一份**; 两边去重留 P6 门户对账时统一决定,双跑期不先删。 ★ ⛔ 7 个 `@RequestBody` 写端点:冒烟只发无 body(绑定层 400); 本片 5 个 `@OperateLog` ⇒ `system_operate_log` **+195/轮**(原 +190 + 5)。 ★★ **2026-10-18 第九十五切片:⚠B 里的 `VipCardRuleController`(迁 4/7 端点,3 个读 `plNos` 留 P3)搬走** ⇒ **⚠B 65/288 → 64/281、已迁 142/823 → 143/830、未迁 69/295 → 68/288**。 ★ 落在 `bill` 域(`POST /admin-api/bill/vip-card-rule/*`),老路由 `/vipCardRule` (会员卡规则管理);**1 读 + 3 写 + 3 未迁**。 ★ 3 个未迁端点都读登录态 `plNos`:两个分页在请求体 `plNos` 为空时回填、 `vipCardSelectParkTree` 直接用 `plNos` 当树根 ⇒ **P3 阻塞**; 两个分页还带 `queryParkName` 回填(缓存优先 + 循环 N+1)。 ★ 写端点只写 `opId`/`name`/`orgId`,**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全;老代码**不判** `userInfo == null`(无 token 时框架层已拦) ⇒ 新类照搬不判,不补 `AUTH_TOKEN_NOT_EXISTS`。 ★ 3 处老 `@SysOpLog` ⇒ 3 个 `@OperateLog`(module `会员卡规则`); `checkVipCardPark` 老类无 `@SysOpLog` ⇒ 不加。 ★ 存量语义照搬:`insert {}` 撞服务层 `BeanValidatorsUtils` ⇒ **code=400「请求参数不正确:xxx必填」**;`update` 的 `id`/`cardNo` 不全 ⇒ DAO 提前 `return 0`(0 行、不碰库);`delete` 是**下架**(改 `card_status`/`data_state`),不存在 id 回 `0 + false`; `check` 缺 `plNos` ⇒ `` 展成空 `in` ⇒ **500**。 ★ ⛔ 冒烟对写端点只用 `{}`、不存在 id(`999999`/`NOPE_95`)与无 body(400), `t_vip_card` / `t_vip_card_park_sale_rel` / `t_vip_card_cust_info` 行数已实测不变。 ★★ **2026-10-18 第九十四切片:⚠B 里的 `BlackListRuleController`(迁 6/7 端点,`parkTree` 留 P3)搬走** ⇒ **⚠B 66/295 → 65/288、已迁 141/816 → 142/823、未迁 70/302 → 69/295**。 ★ 落在 `bill` 域(`POST /admin-api/bill/black-list-rule/*`),老路由 `/blackListRuleMng` (黑名单规则管理);**3 读 + 3 写 + 1 未迁**(`parkTree`)。 ★ 登录态只读 `orgId`/`opId`,**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全; `parkTree` 从登录态取 `plNos` 当车场树根 ⇒ **P3 阻塞、整条不迁**(本机路由 404)。 ★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。 ★ `@Value("${project.syscode}")` 照搬(老门户值 `zteits-bcp-portal`), **不要**硬编码 `"1001"`(那是另一个类的老写法)。 ★ 控制器闸门照搬:`invalid` 的 `ruleNo` 判空(1030)在写操作人**之前**; `update` 的 `ruleNo`/`ruleName`/`actionType` 三字段判空(1030); `actionType` 在 update 是 **String**、save 是 Integer(存量不一致,照搬)。 ★ 存量缺陷照搬:`queryBlackListRule` 查不到规则时 `BeanUtils.copyProperties(null, dto)` 后继续 `getStartTime()` ⇒ **NPE 500**;`save {}` 先撞服务层 1003「结束时间不能早于当前时间」,`save` 只给 `endTime` 撞 `now.after(startTime)` 拆箱 **NPE 500**;`invalid` 不存在的 `ruleNo` 是 0 行更新 `code=0 + true`(存量不校验命中行)。 ★ ⛔ 冒烟对三个写端点只用「无 body(绑定层 400)」与「不存在 ruleNo(0 行)」样本, `t_black_list_rule` / `t_black_list_park_rel` / `t_black_list` 行数已实测不变。 ★★ **2026-10-18 第九十三切片:⚠B 里的 `IncomeParkAndVipController`(迁 7/7 端点)搬走** ⇒ **⚠B 67/302 → 66/295、已迁 140/809 → 141/816、未迁 71/309 → 70/302**。 ★ 落在 `order` 域(`POST|GET /admin-api/order/income-park-and-vip/*`), 老路由 `/income/incomeParkAndVip`(统计报表·临停车、会员卡收入); **5 读 + 2 响应流导出**(xlsx + PDF),无写端点、无挂起。 ★ 登录态只读 `orgId`(仅 total 端点回填组织名),**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全,整类可迁、不涉及 P3。 ★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。 ★ 两条导出都是**直接写 `HttpServletResponse`**(不落临时文件、不过 FastDFS/模板) ⇒ 可直接迁;但异常语义相反:`/export-excel` 无 try/catch ⇒ 500 JSON、 `/export-pdf` 整段 try/catch ⇒ 200 + 0 字节。`days` 只在 PDF 必填。 ★ pay-type 两个端点的 DTO 无必填校验,`{}` 撞 `1 == null` 拆箱 NPE ⇒ 500; `reportTabTag` 只有 1/2/3 分支,4/9 是静默空结果。 ★ 本机 `ParkingLotCacheUtil` 未命中这三个车场 ⇒ pay-type 三行全 null, 冒烟只断言 code=0 与条数 3。 ★★ **2026-10-18 第九十二切片:⚠B 里的 `IncomeChargerCheckTaskController`(迁 5/6 端点,导出挂起)搬走** ⇒ **⚠B 68/308 → 67/302、已迁 139/803 → 140/809、未迁 72/315 → 71/309**。 ★ 落在 `order` 域(`POST /admin-api/order/charger-check-task/*`), 老路由 `/incomeChargerCheckTaskManage`(收费员任务完成情况);**2 读 + 3 写 + 1 导出**。 ★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全,整类可迁、不涉及 P3。 ★ 老类**没有** `@SysOpLog` ⇒ 新类**不加** `@OperateLog`。 ★ 本脚本按老类计数 ⇒ 挂起的导出也计入「已迁」的 6 个端点(同 AppVersionCheck 先例)。 ★ ⏸ `exportIncomeChargerCheckTask` **挂起**:EasyExcel 模板写临时文件 → `fastDFSClientWrapper.uploadFileWithName(...)` → 回**文件 URL 字符串**; 本机运行期无存储后端,且模板 `excelTemplate/ChargerCheckTask.xlsx` 未随迁。 ★ 日期契约只吃 `yyyy-MM-dd HH:mm:ss`;`page` 缺起止日 → DAO generator 空值守卫抛 RuntimeException ⇒ 500(存量缺陷,红线⑥不修)。 ★ ⛔ `save` 的 DTO 无必填校验 ⇒ 冒烟绝不 POST;`bathDelete` 的 DAO 是 `return 0` 死代码,回 false 不是安全保证。 ★★ **2026-10-18 第九十一切片:⚠B 里的 `TdCActivityController`(迁 6/6 端点)搬走** ⇒ **⚠B 69/314 → 68/308、已迁 138/797 → 139/803、未迁 73/321 → 72/315**。 ★ 落在 `pay` 域(`POST /admin-api/pay/activity/*`),老路由 `/tdCActivityManage`; **2 读 + 4 写**,无导出;4 处 `@SysOpLog` → 4 个 `@OperateLog`。 ★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds`。 ★ `save`/`update` 是 **multipart/form**(不是 `@RequestBody`):空 body 会真进业务, `save` 无必填校验会真插 `td_c_activity` ⇒ 冒烟**只发 GET 验 405**; `update`/`bath` 停在服务层 1003「id不能為空!」,`delete {不存在}` 是 0 行更新。 ★★ **2026-10-18 第九十切片:⚠B 里的 `TpPEqpErrorRecordController`(迁 6/6 端点)搬走** ⇒ **⚠B 70/320 → 69/314、已迁 137/791 → 138/797、未迁 74/327 → 73/321**。 ★ 落在 `park` 域(`POST /admin-api/park/eqp-error-record/*`), 老路由 `/tpPEqpErrorRecordManage`(设备异常记录管理);**2 读 + 4 写**,无导出。 ★ 登录态只读 `orgId`(读端点查询条件)/`opId`(写端点操作人), **不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全。 ★ 老类**没有** `@SysOpLog` ⇒ 新类**没有** `@OperateLog`。 ★ `save {}` 走到 `insertSelective` 被 `error_name` NOT NULL 挡回; `update {}` / `delete {id}` / `bath {ids:[]}` 生成无 SET 或 `id in ()` 的 SQL ⇒ 本机 500 但**一行不落**;`bath {ids:[不存在]}` 是 0 行 ⇒ `data=false`。 ★★ **2026-10-18 第八十九切片:⚠B 里的 `BillingRuleController`(迁 9/9 端点)搬走** ⇒ **⚠B 71/329 → 70/320、已迁 136/782 → 137/791、未迁 75/336 → 74/327**。 ★ 落在 `bill` 域(`POST /admin-api/bill/billing-rule/*` + `GET /export-excel`), 老路由 `/billingRule`(计费规则服务);**5 读 + 3 写 + 1 导出**。 ★ 登录态只读 `orgId`/`opId`/`name`,**不读** `plNos`/`userAreas`/`resIds` ⇒ 坑 22 安全,整类可迁、不涉及 P3。 ★ 3 处 `@SysOpLog` → 3 个 `@OperateLog`(`module = "计费规则服务"`); 老导出 `@NoAuth` 按 §3.3 收敛为 `bill:billing-rule:export`。 ★ 老方法名 `queryBilingRuleShowInfoByCollectNo` 少个 `l`,新路由段照抄 `query-biling-rule-show-info-by-collect-no`。 ★ ⚠️ 存量库排序规则冲突:`t_billing_rule_collect`(unicode_ci)与 `t_billing_rule` / `t_billing_park_rule_rel`(general_ci)JOIN ⇒ MySQL 1267 `Illegal mix of collations` ⇒ 本机 5 读 + 分页 + 导出**恒 500** (`sql/.dump/zt_bill.sql` 确认是源库真实形态;红线⑥不改 SQL、不改库)。 ★ ⛔ 写端点冒烟只发无 body / 停在闸门或服务前置校验的样本; `t_billing_rule` / `t_billing_rule_config` / `t_billing_rule_collect` / `t_billing_park_rule_rel` / `t_billing_description` 实测一行不动。 ★★ **2026-10-18 第八十八切片:⚠B 里的 `SpecialCarManageController`(迁 6/8 端点)搬走** ⇒ **⚠B 72/337 → 71/329、已迁 135/774 → 136/782、未迁 76/344 → 75/336**。 ★ 落在 `bill` 域(`POST /admin-api/bill/special-car-manage/*`),老路由 `/specialCarManage`(特殊车辆管理);**4 读 + 2 写、无导出**。 ★ ⛔ 前 2 条 `/querySpecialCar`、`/initSpecialCarTree` **未迁**: 老代码 `request.setPlNos(userInfo.getPlNos())` + 固定 `sourceTypes=[17,9]` ⇒ P3 `plNos` 数据权限模型。其余 4 条只写 `orgId`、2 条完全不读登录态 ⇒ 坑 22 安全。 ★ 老类**无 `@SysOpLog`** ⇒ 新类**无 `@OperateLog`**(照搬,不补)。 ★ `saveSpecialCar` 按 `plNo` 逗号拆行 insert;`updateSpecialCar` 按 `carNumber` 先删后插;`deleteSpecialCar` 是软删(`data_state='0'`)。 ★ ⚠️ 存量 SQL 撞 MySQL 8:`viewSpecialCar` 的 `GROUP BY car_number` 选了 非聚合列 `remarks` ⇒ `only_full_group_by` ⇒ 本机**恒 500** (老栈 5.7 正常;红线⑥不修,冒烟钉成已知行为)。 ★ ⛔ 冒烟只用零写入样本:`save {specialCarList:[]}` 循环 0 次、 `update {carNumber:ZZZ_NO_88}` 删 0 行不插、`delete {id:999999999}` 软删 0 行; `td_cl_special_car` 实测 500 行不变。 ★★ **2026-10-18 第八十七切片:⚠B 里的 `ParkIllegalAreaBaseController`(整类 8/8 端点)搬走** ⇒ **⚠B 73/345 → 72/337、已迁 134/766 → 135/774、未迁 77/352 → 76/344**。 ★ 落在 `park` 域(`POST /admin-api/park/park-illegal-area-base/*`),老路由 `/parkIllegalAreaBaseManage`(停车违章区域基础信息管理);**3 读 + 5 写、无导出**。 ★ 坑 22 **全类安全**:8 个端点只从登录态读 `orgId`/`opId`/`name`;唯一一处 `getPlNos()` 是 `request.getPlNos()`(`saveParkIllegalAreaRel` 的**请求参数**, 不是登录数据权限)⇒ 不需要等 P3。 ★ 老类 `@NoAuth` 0 处;5 处 `@SysOpLog` ⇒ 5 处 `@OperateLog`。 ★ `saveParkIllegalAreaRel` 的两道控制器前置校验照搬(`parkIllegalBaseId` 为空、 `plNos` 为空 ⇒ `BizException(PARAMM_NULL, …)`),⛔ 都在 Service「先删后插」之前。 ★ ⛔ 5 个写端点会**真插/真删主数据**(`save-base` 无校验注解;`save-rel` 先删旧关系) ⇒ 冒烟只发**无 body**(绑定层 400),`save-rel` 另发两道校验样本(控制器内 500)。 零写实测 `parking_lot`/`parking_lot_areas`/`parking_lot_enclosed`/ `parking_lot_geo`/`park_free_berth` 500/53/46/500/500 不变。 ★★ **2026-10-18 第八十六切片:⚠B 里的 `CloseCarParkController`(迁 17/19 端点)搬走** ⇒ **⚠B 74/364 → 73/345、已迁 133/747 → 134/766、未迁 78/371 → 77/352**。 ★ 落在 `park` 域(`POST /admin-api/park/close-car-park/*`),老路由 `/parkListManage`(封闭车场管理);**10 读 + 7 写、无导出**。 ★ ⛔ 前 2 条 `/QueryParkingList`、`/queryParkingParkList` **未迁**: 老代码 `request.setPlNos(userInfo.getPlNos())` + 固定 `sourceType=[17,9]` ⇒ P3 `plNos` 数据权限模型。其余 17 条中只有 `areaBlockByAreaId` 读登录态且只写 `orgId` ⇒ 坑 22 安全。 ★ 老类 `@NoAuth` 0 处;3 处 `@SysOpLog` ⇒ 3 处 `@OperateLog` (`saveParkingListInfo` / `saveParkingAreaInfo` / `updateFreeBerthEnclosed`; 另有 4 处老代码本就是注释状态,不补)。 ★ 存量缺陷照搬(红线⑥不修):`view-parking-area {id:0}`(BeanUtils copy null)、 `view-parking-area-by-id {ids:[]}`(空 `in ()`)、`init-area {}`(id 拆箱 NPE)、 `view-free-berth-enclosed` 无封闭扩展行(`list.get(0)` 越界)、 `view-parking-list-info` 车场不存在 均 500。 ★ ⛔ 7 个写端点会**真插/真改/真软删车场与区域**(`saveParkingListInfo` 无校验注解,`{}` 会生成新编码并真插一行)⇒ 冒烟只发 **无 body**(400); 零写实测 `parking_lot`/`parking_lot_areas`/`parking_lot_enclosed`/ `parking_lot_geo`/`park_free_berth` 500/53/46/500/500 不变, 只有 `system_operate_log` +169/轮。 ★★ **2026-10-18 第八十五切片:⚠B 里的 `KeyTopServiceController`(迁 7/8 端点)搬走** ⇒ **⚠B 75/372 → 74/364、已迁 132/739 → 133/747、未迁 79/379 → 78/371**。 ★ 落在 `order` 域(`POST /admin-api/order/key-top-service/*`),老路由 `/keyTopService`(科拓小管家);**4 读 + 3 写、无导出**。 ★ ⛔ 第 8 条 `/parkTree` **未迁**:老 `parkTree()` 直接 `plNos = userInfo.getPlNos();` 再构树(只留 KEYTOP/CLOUD_BILL) ⇒ P3 `plNos` 数据权限模型。其余 7 条只写 `orgId`/`opId`/`opName` ⇒ 坑 22 安全。 ★ 老类 `@NoAuth` 0 处;4 处 `@SysOpLog` ⇒ 4 处 `@OperateLog`; `sysCode` 走 `@Value("${project.syscode}")`(已配 `zteits-bcp-portal`)。 ★ `queryOrder` 按 §3.2 直接透传 `ParkOrderDTO`(老栈多拷一层 `OrderParkForPayTypeDTO`,多出的 21 字段本就恒 null);分页参数照搬。 ★ ⚠️ 存量服务层缺陷照搬:`int carType = request.getCarType()` 对 null 拆箱 NPE ⇒ `query {}` 恒 500(老栈同款,红线⑥不修);冒烟正样本必须带 carType。 ★ ⛔ 4 个写端点会**真出场 / 真补录并进 MQ / 真控闸机** ⇒ 冒烟只发 **无 body**(400)与炸关口样本;零写实测 `order_park` / `park_in_out_record` / `order_modify_record` 500/500/0 不变,只有 `system_operate_log` +169/轮。 ★★ **2026-10-18 第八十四切片:⚠B 里的 `TdCCustInvoiceSaleController`(迁 11/12 端点)搬走** ⇒ **⚠B 76/384 → 75/372、已迁 131/727 → 132/739、未迁 80/391 → 79/379**。 ★ 落在 `pay` 域(`POST /admin-api/pay/invoice-sale/*`),老路由 `/invoiceSaleManage`(发票-销方管理);**6 读 + 5 写、无导出**。 ★ ⛔ 第 12 条 `/relParkTree` **未迁**:老 `vipCardSelectParkTree` 直接 `plNos = userInfo.getPlNos(); plNos.removeAll(relPlNos);` 裁剪可选车场树 ⇒ P3 `plNos` 数据权限模型(先例 `VipCardCustInfoController` 14/15)。 ★ 其余 11 条只写 `orgId`/`opId`/`createEmpid`/`modfiyEmpid` ⇒ 坑 22 安全。 ★ 老类 `@NoAuth` 0 处;4 处 `@SysOpLog` ⇒ 4 处 `@OperateLog`; `saveOrUpdateInvoiceRelPlNo` 老类没有 ⇒ 不加。 ★ 照搬:`queryRelParks` 空 `sysCode` 补 `"1001"`、`relplno` **无条件** `"1001"`;`bath-delete` 老 mapping 错拼逐字保留; `delete`/`bath-delete` 同时写父类 `sOpId(Long)` 与 `opId(String)`。 ★ ⛔ **`save {}` 会真插一行销方**(服务层无任何必填校验)⇒ 冒烟只发 **无 body**(绑定层 400);`relplno` 只用不存在的税号(删 0 行、不插)。 零写实测:只有 `system_operate_log` +161/轮,两张发票表 4/4/4 不变。 ★★ **2026-10-18 第八十三切片:⚠B 里的 `ManualInvoiceController`(迁 7/7 端点)搬走** ⇒ **⚠B 77/391 → 76/384、已迁 130/720 → 131/727、未迁 81/398 → 80/391**。 ★ 落在 `pay` 域(`POST /admin-api/pay/manual-invoice/*`),老路由 `/manualInvoice`(人工开具电子发票);**4 读 + 3 写、无导出**。 ★ 登录态只碰 `orgId`/`loginCode`;`queryInvoiceSaleInfo` 的 `plNos` 从结果集 `PayOrderParkingInvoiceDTO::getPlNo` 现算,不是数据权限字段。 ★ 老类 `@NoAuth` 1 处(`queryPayOrderForParkingInvoice`)——★ 方法体自己就抛 `AUTH_TOKEN_NOT_EXISTS`,调用方 `fillinvoice.js` 走 `sysAjax`(带 token) ⇒ §3.3 第一类,收敛为需登录;老类无 `@SysOpLog` ⇒ 无 `@OperateLog`。 ★ 存量差异照搬:`park {}` 撞本机 MySQL 8 `only_full_group_by` ⇒ 500 (存量 SQL,老栈 5.7 正常,红线⑥不修);`ticket` 缺 `invoiceType` 拆箱 NPE ⇒ 500;`resend {}` 模板缺失 ⇒ `-100020`;`open {}` ⇒ 「BizResult 为 null」(老类 catch 里塞 null)。 ★ ⛔ 三个写端点有 body 会**真发邮件 / 真落开票历史 / 真调航信开票队列**; 冒烟只发无 body 与炸关口样本,`td_c_cust_invoice_his` / `_sale` / `_sale_park_rel` 行数已实测不变。 ★★ **2026-10-18 第八十二切片:⚠B 里的 `GuideScreenController`(迁 7/7 端点)搬走** ⇒ **⚠B 78/398 → 77/391、已迁 129/713 → 130/720、未迁 82/405 → 81/398**。 ★ 落在 `park` 域(`POST /admin-api/park/guide-screen/*`),老路由 `/guideScreen`(诱导屏管理);**5 读 + 2 写、无导出**。 ★ 4 处读登录态但**只碰 `orgId`/`opId`/`opName`**;`plNos` 来自 `guideScreenQueryService.getGuideScreenInfo` 的结果集,不是数据权限字段。 ★ 老类 `@NoAuth` 1 处(`updateGuideScreenStates`)——★ 有**管理页面**调用方 (`screenmanage.js:415` 走 `sysAjax`,带 token)⇒ §3.3 第一类,收敛为需登录; 老类 `@SysOpLog` 1 处 ⇒ `@OperateLog` 1 处(UPDATE)。 ★ 存量差异照搬:`statistics {}` 的 `WHERE 1=1` 被本机 Druid wall 拦 ⇒ 500; `get-info` 不存在的 guideNo 拆箱 NPE ⇒ 500;`update-states {}` 撞 Example `andGuideNoEqualTo(null)` ⇒ 500。 ★ ⛔ **`syn-all-guide-info` 有 body(连 `{}` 都算)会先打青岛三组 HTTP, 再进 `@Transactional` 的先删后插**;冒烟只发无 body (`td_g_guide_screen` 97 行、`td_g_guide_park_rel` 320 行已实测不变)。 ★★ **2026-10-18 第八十一切片:⚠B 里的 `BatteryRechargeCardController`(迁 7/7 端点)搬走** ⇒ **⚠B 79/405 → 78/398、已迁 128/706 → 129/713、未迁 83/412 → 82/405**。 ★ 落在 `pay` 域(`POST /admin-api/pay/battery-recharge-card/*`),老路由 `/batteryRechargeCardManage`;**3 读 + 4 写、无导出**。 ★ 6 处读登录态但**只碰 `orgId`/`sOpId`/操作人**;**无 `plNos`/`userAreas`/`resIds`**。 ★ 老类 `@NoAuth` 1 处(分页查询)——但方法体自己就抛 `AUTH_TOKEN_NOT_EXISTS`,新栈收敛为需登录;老类无 `@SysOpLog` ⇒ 不加 `@OperateLog`。 ★ `save` 与 `account-charge-wx-pay` 保留 `@Valid`(`{}` → 400); `update {}` / `bath-delete {}` ⇒ 500「存量错误码 1003 / id为空!」。 ★ `query-account-details-for-page` 撞本机 MySQL 8 `only_full_group_by` ⇒ 500 (存量 SQL,老栈同样炸,红线⑥不修)。 ★ ⛔ **`account-charge-wx-pay` 有完整 body 会真建充值单并调微信下单**; 冒烟只发无 body 与 `{}`,两张电池卡表行数已实测不变。 ★★ **2026-10-18 第七十七切片:⚠B 里的 `TpPEqpGatewayController`(迁 7/7 端点)搬走** ⇒ **⚠B 83/433 → 82/426、已迁 124/678 → 125/685、未迁 87/440 → 86/433**。 ★ 落在 `park` 域(`POST /admin-api/park/eqp-gateway/*`),老路由 `/tpPEqpGatewayManage`(道闸网关管理);**3 读 + 4 写、无导出**。 ★ 类路由按「去掉表前缀 `TpP` 再 kebab」定为 `eqp-gateway` (先例 `TpPNetworkCardController` → `/park/network-card`); 方法段 kebab 老 mapping 逐字(同第七十五/七十六切片),`bath` 存量错拼保留。 ★ 7 处读登录态但**只碰 `orgId`/`sOpId`/`sOpName`**:statistic/list/page/save/update 写 `orgId`,delete/bathDelete 只写操作人;**无 `plNos`/`userAreas`/`resIds`**。 ★ 存量差异照搬:`delete` 同时写父类 `setsOpId` 与子类 `setOpId`(服务读 `getOpId()`); `batchDelete` 只写 `setsOpId` ⇒ 操作人名称恒 null; `bathDelete {}` 撞 `TpPEqpGatewayExample.andIdIn(null)` 的 `Value for id cannot be null` ⇒ 500。 ★ ⛔ **`save-tp-p-eqp-gateway` 有 body(哪怕 `{}`)就会真插 `tp_p_eqp_gateway`** (`TpPEqpGatewayRequest` 无任何校验注解);冒烟只发无 body,该表行数已实测不变。 ⚠️ **排期用「✔ 3/3(0 个可直接开搬,全是外部副作用/文件存储前置)+ ⚠A 0/0(已清零)+ ⚠B 1/1(orgId 部分已解禁,剩余均带硬避让)」,别用 4/4**—— "就绪"只回答了"服务齐不齐",不回答"搬过来数据对不对"。端点数**不能**当"好迁"的信号: `ChargerManageController` 与 `DutyRosterController` 是端点最多的两个,也是坑 22 命中最多的两个(各 22 处), 且这两个都在 **⚠B**(真读登录态)——**两者已分别在第六十六/第六十七切片搬走**; 当前队列里端点最多的是 `ParkLotManageController`(18,⛔ 读登录 `plNos` 三处 = P3 数据权限),其后 `VipCardCustInfoController`(15,**第七十四切片已搬走 14 个**,余 1 条「下载月卡购买协议」⛔ 依赖 poi-tl + classpath 下 docx 模板 + 本机硬编码输出目录)、 `VipCardAuthPersonBjxcController`(12,**已于第七十一切片搬走**)、`TdCCustInvoiceSaleController`(12,⛔ 读登录 `plNos` = P3 数据权限)。 "ids 大"才是危险信号,"endpoints 大"只是工量大。 退出码: 0 = 盘点本身是可信的(扫到了 Controller、也扫到了新栈的接口与实现) 1 = **盘点不可信**:老门户目录不存在、或新栈一个接口/一个实现都没扫到。 ⚠️ 退出码**不是**"有没有缺口"——缺口是这份报告的**结论**,不是它的失败。 (把"有缺口"当失败的话,这个脚本会恒返回 1,等于没有退出码。) ⚠️ 四个必须保留的做法,别"顺手简化": 1. **统计端点前先剥注释 —— 而且必须把老写法 `@RequestMapping` 一起算进来。** 两个独立的坑,**第一版两个都栽了**,数字差一倍: - *剥注释*:`invoice/InvoiceController` 看着 18 端点 / 0 会话 / 0 `@NoAuth`, 实际整个类是注释掉的(`//@RequestMapping("/invoice")`)。不剥就会把死代码排进待迁清单。 - *算 `@RequestMapping`*:老工程的**主流写法**是 `@RequestMapping(value = "/getFeedbackType", method = RequestMethod.POST)`, **不是** `@PostMapping`。只匹配 `@(Get|Post|Put|Delete)Mapping` 会漏掉一大半端点, 并把**活着的控制器误判成死代码**(当时把整个 `personpc/**` 判成死代码, 实际 `PersonPCSuggestController:57`、`PersonPCAcctController:79` 都是活的)。 - *别把类级映射算成端点*:`@RequestMapping("equipmentManagement")` 和 `@PostMapping("/saveChannelInfo")` 在正则眼里是一个东西,于是**每个带类级映射的 Controller 都多算 1 个端点**(`EquipmentManagementController` 数出 11,实际 10)。 现在 `count_endpoints()` 按"类声明之前 = 类级、之后 = 方法级"分开数。 - 教训:**"剥注释"只解决"假活",还要再解决"假死"和"数多了"**。任何结论性的数字 ("X 个是死代码"、"某类有 N 个端点")**写进文档前一定要拿一个真实文件手数核对** —— 这三个 bug 全是这么发现的:`personpc` 是随手 grep 一个方法签名发现的, 端点数是对着 `closecarpark` 那 5 个类逐行数出来的。 2. **判定"某个 Controller 是否已迁"不靠文件名,也不靠新类名。** 两条都不可靠: - 新类普遍按路由约定**改名**(老 `TpPInspectRecordController` → 新 `InspectRecordController`), 按新类名反查会把已迁的**全判成未迁**(只认出 13 个,实际 23 个); - 老工程大小写不统一(老 `monitor/ParkingLBSController` → 新 `ParkingLbsController`), 逐字符比对同样漏。 本脚本用「新类 javadoc 抬头里提到老类名」+「类名忽略大小写相同」两路合并 (见 `provenance_mentions` / `PROVENANCE_PAT`,含"必须在小标题之前截断"的原因—— 不截断会把 `ParkLotExtendController` 里那句"与老 `ParkLotManageController` 的端点不混在一起" 当成已迁证据,把 19 个端点的 `ParkLotManageController` 从待办队列里藏掉)。 **但这是启发式**:真要确认某个类迁没迁,`grep` 那个老名字,别信计数。 3. **"接口不在新栈"和"无实现"要分开报。** 前者要补 `parking-module-api` 的契约, 后者要搬一个 ServiceImpl 进某个 module——是两种完全不同的工。 4. **实现类是按简单类名匹配的**(`class XxxImpl implements Xxx`)。 老工程存在同名不同包的重复类,所以这是**宽松匹配**:它只证明"有一个实现", **不证明**实现落在正确的 module 里、也不证明它被 Spring 扫到了。 就绪度是**必要不充分**条件——真迁的时候仍然要起 Jar 打端点(`tools/api_smoke.py`)。 5. **"服务就绪"之外必须再报坑 22**(`idSpaceHits`)。只报就绪会给人一个 189 个的"待办队列", 实际能开搬的只有三分之一——**把半个队列说成可做,比不说更糟**(会被当成排期依据)。 判据是 `LEGACY_ID_PAT`(从登录态读 `getOrgId()/getPlNos()/getUserAreas()`), 保守但机械;`setOpId(...)` 这类只写审计列的**不**算命中(见 §3.1 第 1 步的判据)。 6. **坑 22 必须切两刀,只切一刀会漏掉 12 个可搬的类。** 第一刀(`LEGACY_ID_PAT`)的 正则**没有接收者概念**:`request.getPlNos()`(前端请求体,与登录态无关)与 `userInfo.getPlNos()`(登录态)命中一模一样。于是 ⚠ 档里混进了 12 个 / 61 端点 "整类一处都没读登录态"的**纯误报**(第三十五切片实测)。第二刀 `IDENT_READ_PAT` (`getUserInfo\\s*\\(|SessionCommUtil|getUserInfoForPersonPC\\s*\\(`)把它们摘出来成 ⚠A。 ⚠️ ⚠A **不等于"已核销"**:脚本只证明"整类没读登录态",仍要人肉看一眼命中行落在谁身上 (`idSpaceLines` 已经把行号+原文打出来了,几秒的事),核完登记进 `IDSPACE_FALSE_POSITIVE`。 用法: python3 tools/p2_ready_check.py # 从仓库根目录跑(parking-cloud/ 的父目录) python3 tools/p2_ready_check.py --json # 机器可读,供挑选下一批用 python3 tools/p2_ready_check.py --top 40 # 只看端点最多的前 40 个就绪项 python3 tools/p2_ready_check.py --legacy-root /path/to/zteits-clouds \\ --new-root /path/to/parking-cloud """ import argparse import glob import json import os import re import sys DEFAULT_LEGACY = "zteits-clouds/zteits-bcp-portal/src/main/java/com/zteits/bcp/portal/web" DEFAULT_NEW = "parking-cloud" # ⚠️ 第一百五十四切片修正(2026-10-19):老正则只认 `class Xxx implements Yyy`, # 漏掉「`class Xxx extends Base implements Yyy`」这种**先继承再实现**的形状。 # 新栈里有 6 个接口就是因此被误判成「无实现」(`PubParkOrderQueryService`、 # `RefundService` 等,共 8 个 Controller / 37 端点被错挡在「缺口」档)。 # 另外老新版扫描只 glob `*Impl.java`,漏掉 `RefundServiceIpml.java`(文件名把 Impl 拼成 Ipml)。 IMPL_PAT = re.compile(r'class\s+(\w+)(?:\s+extends[^{]*?)?\s+implements[^{]*?(\w+)') IFACE_PAT = re.compile(r'public\s+interface\s+(\w+)') # 新栈 `api.service.*` 包下的**类**(不是接口)。存量 Controller 会 import 它们当入参 # (典型:`RenewVipAreaCardOperReq` 放在 `api.service.pay.viparea.oper` 下但 `extends BaseRequest`)。 # 这些 import 只说明"传参用到这个 DTO",不构成"缺服务",不能算进 gaps。 API_CLASS_PAT = re.compile(r'\b(?:public\s+|final\s+|abstract\s+)*class\s+(\w+)') SVC_IMPORT_PAT = re.compile(r'import\s+(com\.zteits\.clouds\.api\.service\.[\w.]+);') ENDPOINT_PAT = re.compile(r'@(?:Get|Post|Put|Delete|Request)Mapping\(') # 坑 22 的机械信号:老 Controller **从登录态读**这三个身份字段。它们在新栈里一个是新 id # (`system_org.id`,seed 只有 1 行「总公司」)、一个是空集合,拿去**过滤结果集**就静默少数据。 # 只认 `get*`(读登录态)——`setOpId(...)` 写审计列不在此列,而 # `request.setOrgId(userInfo.getOrgId())` 这种"读完就喂给下游当过滤条件"的写法因为含 # `getOrgId()` 仍会被抓到。== 0 才认为"坑 22 安全"(见 §3.1 第 1 步)。 LEGACY_ID_PAT = re.compile(r'get(?:OrgId|OrgIdList|PlNos|UserAreas|UserAreaList)\s*\(') # ⚠️⚠️ `LEGACY_ID_PAT` **不知道 `get*()` 是打在谁身上的**——它连**请求体**一起抓。 # 老 Controller 里 `request.getPlNos()`(`request` 是前端传进来的请求体)、 # `dto.getOrgId()`(下游返回的 DTO)都会命中,**与登录态毫无关系**。 # ⇒ 它是个"宁可多报"的**筛子**,不是判据。第三十五切片实测:⚠ 档 124 个里有 # **12 个 / 61 端点**整类**没有任何一处**读登录身份(`IDENT_READ_PAT` 命中 0), # 全部是机械误报——所以本脚本在 ⚠ 档内部再切一刀成 ⚠A / ⚠B(见 main()): # ⚠A(`hasIdentitySource == False`)= **机械误报候选**,可开搬,但每个类仍要 # 人肉核对命中行落在谁身上(`idSpaceLines` 已经把行号和原文打出来了,几秒的事); # ⚠B(`hasIdentitySource == True`)= 真读了登录态,还要再往下追"读到的值有没有进 SQL" # (第三十三切片:`ParkInOutRecordController` 的 `setTopOrgId` 是**注释掉的** INERT)。 IDENT_READ_PAT = re.compile(r'getUserInfo\s*\(|SessionCommUtil|getUserInfoForPersonPC\s*\(') # 新类注释里对老类的指认:反引号或 `/` 紧跟的 `XxxController`(见 collect_new_stack 注释②)。 # 只在**类 javadoc 的第一段**里找(`provenance_mentions` 负责切段),因为约定是 # 「管理后台 - XXX Controller(老门户 `path/XxxController`,路由 …)」。 # ⚠️ **不切段会误判**:`ParkLotExtendController` 在后面的说明里写了 # 「与老 `ParkLotManageController` 的端点不混在一起」——那是在**划清界限**, # 结果把 19 个端点的 `ParkLotManageController` 误标成"已迁"从待办队列里藏掉了 # (而它恰恰是 §4.3 高风险批次里那个不能漏的)。 # ⚠️ 它仍是启发式:切段后仍可能在某类的第一段里提到**另一个**老类名。 # 反过来的错(把已迁的算进待办)只浪费时间,这个方向的错(把未迁的藏起来)会**漏活**, # 所以确认某个类迁没迁,一律 `grep` 那个老名字,别信这里的计数。 PROVENANCE_PAT = re.compile(r'[`/]([A-Za-z0-9_]+Controller)\b') def strip_comments(src): """剥掉块注释与整行行注释——见模块注释第 1 条。""" s = re.sub(r'/\*.*?\*/', '', src, flags=re.S) return re.sub(r'^\s*//.*$', '', s, flags=re.M) def strip_comments_keep_lines(src): """同 `strip_comments`,但块注释**只清空字符、保留换行**。 为什么需要它:上面那个 `re.S` 替换会把多行注释整段删掉、**连带删掉里面的换行**, 于是"剥注释后的第 N 行"和**文件里真正的第 N 行**就不是一回事了。剥注释后打行号 (`idSpaceLines`,给人核对 `getPlNos()` 打在谁身上用)必须对得上编辑器里看到的行号, 否则人肉核对要重新数一遍——那这一步就白做了。 两者**非换行字符完全一致**,所以 `LEGACY_ID_PAT` 在两个视图上命中数相同。 """ s = re.sub(r'/\*.*?\*/', lambda m: re.sub(r'[^\n]', '', m.group(0)), src, flags=re.S) return re.sub(r'^\s*//.*$', '', s, flags=re.M) CLASS_PAT = re.compile(r'\b(?:public\s+|final\s+|abstract\s+)*class\s+\w+') # 类级 `@RequestMapping("channelManagement")` 的路径。把它也当"指认",因为**第三种** # provenance 写法认的是**路由**不是类名:`(老门户 \`channelManagement\`)`。 ROUTE_PAT = re.compile(r'@RequestMapping\s*\(\s*(?:value\s*=\s*)?"([^"]+)"') # 注释抬头里的反引号词(`` `channelManagement` `` 这种)。见 new_stack_mentions。 BACKTICK_PAT = re.compile(r'`([A-Za-z0-9_./\-]+)`') # ── 已判定表(★ 人工读源码的结论,按**老类名**索引;脚本只做标注,不改 ✔ 的机械判定) ── # 为什么要有这张表:✔ 的判据只有「服务齐 + 没读身份字段」两条,**不回答能不能交付** # (第三十四切片实测:✔ 5 个 / 11 端点**没有一个是能直接开搬的**)。 # 规矩:**每判定一个类就登记在这里**,否则下一批还会把同一批类当「可开搬」重走一遍。 ADJUDICATED = { "RefreshCacheController": ( u"✅ 已搬(第四十六切片)", u"4 个端点全 `@NoAuth` + 老代码写死的 32 位共享密钥(照搬违红线 11)⇒ 按第三十四切片 ④ 的" u" **(B) 配置化**落地:期望值走 `parking.refresh-cache.token`(本地 profile 给测试值、生产不配即" u" fail-closed),代码里零密钥;调用方不登录 ⇒ §3.3 第 2 类:`**.controller.app.**` → `/app-api`" u" + `@PermitAll`(注解扫描自动进白名单)。落 `parking-module-system`,路由 `/system/refresh-cache`。" u"★ 4 条只写 Redis、不写 DB;token 失败的「成功码 + data=token不正确」存量形状照搬"), "PersonPCAuthController": ( u"⏸ 等个人客户登录桥", u"类级 `@NoAuth`;`login` 把 `request.getSession().getId()` 当 token 写 `PersonSessionEnum`," u"`getUserInfo`/`logout` 读同一 session —— 新栈 Redis Token + Spring Security **不建 session**," u"且全仓**没有任何地方写** `PersonSessionEnum.USER_INFO`(只有 3 处读)⇒ 照搬即恒失败;" u"`sendValidatorCode` 还会走 `MessageCodeUtil.sendSms` **真发短信**;" u"`uploadUserPic` 的 `@RequestMapping` **被注释掉** ⇒ 它不是端点"), "PersonOrderBackWxPayController": ( u"⏸ 等个人客户登录桥", u"`sessionCommUtil.getUserInfoForPersonPC()` **只读 HTTP session、没有 LoginBridge 兜底** " u"⇒ 照搬恒抛 1003「获取客户信息失败」(先例 `PersonPCCarNumController#getPersonCarPC` 已如此记账);" u"且它会**真建微信支付订单**(`aliWxPayService.wxJSAPIPay`)"), "UploadController": ( u"⏸ 等文件存储抽象", u"`/upload/uploadPic` 是 `@NoAuth` + `FastDFSClientWrapper.uploadFile`;新栈里 " u"`FileStorageServiceImpl.uploadFile` **整段被注释掉、直接 return null** " u"(先例 `ParkShareController#insertParkingBerthShare`、`AppVersionCheckController#saveAppVersion`)。" u"⚠️ 同栈里那些 `MultipartFile` 的 excel 导入端点**不算**这个卡点——卡的是上传到存储后端"), "HomeController": ( u"⛔ 不适用", u"14 行 `@Controller`:`@RequestMapping(根路径)` → `return redirect:swagger-ui.html`," u"**不是 JSON 接口**;新栈 springdoc 的 UI 在 `/swagger-ui`、knife4j 在 `/doc.html`," u"根路径也没有别的映射 ⇒ 判定「不迁」,不是「等」(要么落成文档页,要么不交)"), } # ── ⚠A 档(机械误报)核销表 ── # 与 `ADJUDICATED` 分开两张表,因为**回答的是不同问题**: # `ADJUDICATED` 管 ✔ 档("没读身份字段"的类到底能不能交付); # `IDSPACE_FALSE_POSITIVE` 管 ⚠A 档("`LEGACY_ID_PAT` 命中了,但整类没读登录态"的类, # 命中行究竟打在谁身上)。规矩同样是**每核销一个类就登记在这里**,否则下一批会重走一遍。 # ★ 2026-10-16 第三十五切片:⚠A 候选 12 个 / 61 端点逐类读完命中行,**结论全是误报** # (命中行无一例外落在请求体 `request.getPlNos()` / 局部变量 `req.getOrgId()` / # 下游 DTO `dto.getOrgId()` 上);其中 `EqpIncomeStatisticController` 本片已搬走 ⇒ 余 11 个 / 58 端点。 IDSPACE_FALSE_POSITIVE = { "EqpIncomeStatisticController": ( u"✅ 已搬(第三十五切片)", u"3 端点搬进 `park/eqp-income-statistic`;命中行 `recordRequest.setPlNos(request.getPlNos())`" u"(`request` = 前端请求体 `IncomeRecentStatisticRequest`)"), "RealTimeMonitorController": ( u"✅ 已搬(第四十四切片)", u"2 处全是 `null == req.getOrgId()` —— `req` 是**方法入参**(前端请求体 `ParkDutyInfoQueryReq`)," u"只做「没传就不查」的守卫,不是登录态;全类 **0 处** `getUserInfo(`/`SessionCommUtil`。" u"10 端点拆成**两个新类**(本栈前缀由包路径决定,一个类不能两栖):管理端 8 条进 " u"`park/real-time-monitor`(+ 导出收紧为 `park:real-time-monitor:export`);" u"`queryParkLotInfoByOrgId` / `...ForHS` 两条**老 `@NoAuth` 是第 2 类**(外部黄石官网," u"`parkingcloud` 的两个分支都搜不到调用方)⇒ `/app-api` + `@PermitAll` + 白名单。" u"★ 精确口径更正第四十三切片 ⑫ 的预判:**两条都是第 2 类**,不是只有 `ForHS`。"), "BerthsStatisticController": ( u"✅ 已搬(第四十三切片)", u"9 端点进 `park/berths-statistic`(⚠️ 不是 `BerthStatisticController`);" u"`request.getPlNos()` 两处(老文件 `:362`/`:363`)里 `request` 是**方法入参**" u"`ParkingLotUseStatisticForPageRequest` = 前端请求体,一处比 size、一处 for 遍历," u"全类 **0 处** `getUserInfo(`/`SessionCommUtil`;" u"4 条导出全是老 `@NoAuth` ⇒ 按 §3.3 **第 1 类**收紧(一个类 4 条导出 ⇒ 主导出占 " u"`GET /export-excel`,余三条给 `-detail`/`-month`/`-month-detail` 后缀)"), "ReconciliationManagementController": ( u"✅ 已搬(第四十二切片)", u"两处 `CollectionUtils.isEmpty(request.getPlNos())` / `req.getPlNos()`,均为请求体空判" u"(`request`/`req` 是**方法入参**),全类 **0 处** `getUserInfo(`/`SessionCommUtil`;" u"7 端点(5 读 + 2 导出)搬进 `order/reconciliation-management`。" u"★ 老类 `extends ReconciliationBaseController`(那个只有 `getPayOrderTypeName(Integer,Integer)`" u"的 140 行基类)——新栈没有基类可继承,那段 switch **原地抄进新控制器**(⛔ 别改成枚举/工具类)。" u"★ 两条导出在老门户上都是 `@NoAuth` ⇒ 按 §3.3 **第 1 类**(管理页面导出)收紧成 " u"`GET /export-excel` + `GET /export-detail-excel` + `@PreAuthorize('order:reconciliation-management:export')`;" u"5 条读用 `order:reconciliation-management:query` ⇒ **7 条无 token 全 401**。" u"⚠️ 两条源表样本都停在过去(`td_b_pay_blncac_result` 2018-01 且 `pl_no` 全空串、" u"`pay_order` 停在 2017-09-25 且 `pay_type=6`)⇒ 正样本窗口得手动指定,**不是迁移回归**"), "IndexParkSituationController": ( u"✅ 已搬(第三十七切片)", u"5 处全打在 `request.getPlNos()` 上:3 处空判(老 `:100`/`:230`/`:286`)+ " u"2 处 `setPlNos(request.getPlNos())` 转投下游请求(老 `:111`/`:117`)——" u"`request` = 前端请求体,全类 **0 处** `getUserInfo(`/`SessionCommUtil`;" u"6 端点搬进 `order/index-park-situation`(老类**没有** `@NoAuth` ⇒ 一律 `@PreAuthorize`)"), "VehicleFlowController": ( u"✅ 已搬(第三十九切片)", u"3 处 `request/parkingRunningStatisticForPageRequest.getPlNos()`,均为请求体;" u"⚠️ 本类**两条端点在老门户上是 `@NoAuth`**(`exportCarFlowForList` 与 " u"`carFlowPrint`——后者 `@RequestMapping` 连 `method` 都没写)⇒ 已按 §3.3 **第 1 类**" u"(管理页面导出)收紧成 `GET /export-excel` + `GET /export-pdf`,同一权限点 " u"`park:vehicle-flow:export`;5 端点搬进 `park/vehicle-flow`。★ 本片**新建 1 个老门户独有 VO**" u"(`com.zteits.bcp.portal.vo.report.runtime.VehicleFlowVO`,只喂 PDF、不进响应体)"), "IncomeCalculationController": ( u"✅ 已搬(第三十八切片)", u"1 处 `CollectionUtils.isEmpty(req.getPlNos())`(请求体,控制器里那条是服务层同句的复制粘贴);" u"⚠️ 本类另带 `@NoAuth` 导出端点(`/exportToIncomeCalculationSummaryNewOfllwTwo`," u"`@RequestMapping` 无 `method`)⇒ 已按 §3.3 **第 1 类**(管理页面导出)收紧成 " u"`GET /export-excel` + `@PreAuthorize('order:income-calculation:export')`;" u"5 端点搬进 `order/income-calculation`。★ 本片同时是**坑 25 的第二处修点**" u"(两条走 `PageHelper.startPage` 的 select 去掉 `statementType=\"STATEMENT\"`)"), "IncomeAllStatisticController": ( u"✅ 已搬(第四十切片)", u"5 处 `request/incomeStatisticRequest.getPlNos()==null`,全是请求体空判(⚠A 机械误报);" u"5 端点(5 读,无 `@NoAuth`、无 `@SysOpLog`)搬进 `order/income-all-statistic`。" u"★ 本片**新建 3 个老门户独有 VO**(`IncomeStatisticTrentReportVO` / " u"`IncomeStatistic2MonthFeeVO` / `IncomeStatisticDayFeeVO`)。" u"⚠️ 其中 4 条端点的老 SQL 用 `GROUP BY x DESC`(MySQL 8.0.13 已移除的语法)⇒ " u"本机(MySQL 8.x)恒 500,**老栈同样 500**,按红线⑥ **不改 SQL**," u"改以离线桩测(剥 jar + `Proxy` 桩 + 反射注入,66 断言)证明控制器逻辑等价"), "OrderParkStatisticController": ( u"✅ 已搬(第四十一切片)", u"1 处 `request.getOrgIds()/getPlNos()` 空判(请求体,⚠A 机械误报);" u"4 端点(4 读、无导出、无 `@SysOpLog`)搬进 `park/order-park-statistic`。" u"★ 老类 3 条大屏 `@NoAuth`(`queryLastOrderPark` / `queryLastOrderParkByPlNos` / " u"`queryOrderParkNumForLastWeek`)按 §3.3 **第 1 类**(老门户自己的管理页面)收紧成 " u"`/admin-api` + `@PreAuthorize('park:order-park-statistic:query')`(第 4 条原本就没有 " u"`@NoAuth`,也纳入同一个安全档)⇒ **4 条端点无 token 全 401**。" u"⚠️ 别与 `RealTimeMonitorController` 里**外部黄石官网**那条 `@NoAuth` 互推——" u"那个才是第 2 类(`/app-api` + `@PermitAll` + 白名单)。" u"⚠️ EP2 / EP3 / EP4 在本机**恒空**(样本表停在过去 + 窗口按今天算)," u"**不是迁移回归**:判定靠「同一条 SQL 直连换窗口」,见 AGENTS.md 第四十一切片 ⑤"), "VipCardQueryController": ( u"✅ 已搬(第四十五切片)", u"1 处 `whitelistRuleForPageRequest.setPlNos(request.getPlNos())`(请求体转投下游;" u"全类 0 处 `getUserInfo(`/`SessionCommUtil`)⇒ 3 端点(3 读、无导出、无 `@NoAuth`、无写)" u"搬进 `order/vip-card-query`。★ 更正旧结论:**没有**「要另建 2 个老门户独有 VO」这回事——" u"`VipCardStatisticIndexDTO`/`AuthCarNumberDTO` 早已在 `parking-module-api`," u"`VipCardStatisticFeeVO` 早已随 `parking-legacy-common` 搬入且**从未被 new 过**" u"(第 3 条端点的真实线格式是 21 字段的 `IncomeStatisticFeeVipCardDTO`,老签名是假的)"), "WxcpMessageController": ( u"✅ 已搬(第三十六切片)", u"2 处 `request.getOrgId() == null` 空判(请求体)⇒ 落 `/app-api` + `@PermitAll`" u"(§3.3 **第 2 类**:企微推送 H5 真免认证,已进白名单登记表 + §2af (b) 正面钉住)"), "BerthStatisticController": ( u"✅ 已搬(第三十六切片)", u"1 处 `appOrgRequest.setOrgId(dto.getOrgId() + \"\")` —— `dto` 是**上游服务返回的 DTO**" u"(全类 0 处登录态读取);两个端点的 `@NoAuth` 已按 §3.3 **第 1 类**收紧成 `@PreAuthorize`"), } # 缺口档的人工结论(**不可迁**):机械判定仍是「缺实现」,但人读完源码判定**补不了**。 # 与 ADJUDICATED 的区别:那张表管"就绪且未迁",这张表管"缺实现",两者都只做**标注**。 GAP_ADJUDICATED = { "OrderExamineController": ( u"⛔ 不可迁:半合并壳(实现只在未合入的特性分支)", u"老门户 `orderexamine/OrderExamineController`(2021-08 高位订单数据检查,2 端点)在" u" `beijing_master` / `beijing_dev` / `chifeng_master` / `chifeng_dev` **四个生产分支上只有" u" Controller + `clouds.api` 接口与 DTO,没有任何实现**:" u"① 全仓(HEAD)`implements OrderExamineService` = 0;② Dubbo 只有 consumer,**没有 provider**;" u"③ `UserInfo.examinePlNos` **全仓无一处 set**(登录时不填)⇒ `queryLists` 恒抛 `AUTH_PL_NOS_EMPTY`," u"`updateStatus` 恒「no provider available」;④ 仓库里也没有 `order_examine_data` / `sys_op_examine_rel` 的 DDL。" u"实现(`OrderExamineServiceImpl` + `OrderExamineData(+Example)` + 两套 Mapper/XML + " u"`SysOpExamineRelService`)**只存在于未合入的 `origin/chifeng_dev_high_video_examine`**(最后提交 2021-08)。" u"⇒ 照搬 Controller 在新栈必因缺 Bean 启动失败;补实现 = 把这个未发布特性整块搬进新栈(约 3000 行生成代码 +" u" `examinePlNos` 登录桥 + 两张表),**属 P4 特性移植,不是 P2 Controller 迁移**。" u"→ 若要上线「高位视频审核」,先由产品确认,再从该分支整体移植。"), "CustPersonCreditController": ( u"⛔ 不可迁:服务实现整文件被注释(两栈同款)", u"老门户 `custperson/CustPersonCreditController`(5 端点:分页/新增/审核/修改/批量删)。" u"`CustPersonCreditServiceImpl.java` 在**老仓和新栈里都是从第 1 行 `//package` 起整文件注释**," u"全仓 `implements CustPersonCreditService` 的有效代码 = 0(包 `com.zteits.pay.biz.custperson.oper.impl`);" u"`CustCreDitDicJob` 里对它的注入同样被注释。⇒ 老栈走 Dubbo **没有 provider**(端点本来就恒失败)," u"新栈若照搬 `@Resource CustPersonCreditService` 会**没有任何 Bean**,整个应用启动即失败。" u"补实现 = 从被注释的 200 行里恢复 Service + DAO + `td_c_cust_person_credit` 表 + 征信 WebService 调用," u"**属功能复活,不是 P2 Controller 迁移**(红线⑥:存量缺陷照搬不修)。" u"★ 同一分支下 `custperson/TdCCustBusinessPicController` / `TdCCustBusinessController` 等**有活实现**," u"只有这一类是死的。"), } def gap_adjudicated(controller): """返回缺口档的人工结论 (标记, 依据);没登记过就是 (None, None)。""" hit = GAP_ADJUDICATED.get(controller) if hit is None: return None, None return hit def idspace_adjudicated(controller): """返回 ⚠A 档的核销结论 (标记, 依据);没登记过就是 (None, None)。""" hit = IDSPACE_FALSE_POSITIVE.get(controller) if hit is None: return None, None return hit def adjudicated(controller): """返回 (标记, 依据);没登记过就是 (None, None)。""" hit = ADJUDICATED.get(controller) if hit is None: return None, None return hit def count_endpoints(src): """数**方法级**映射。⚠️ 类级 `@RequestMapping("equipmentManagement")` 不算端点, 但 `ENDPOINT_PAT` 认不出两者——它就是一个 `@RequestMapping(`。 第一版因此把**每个带类级映射的 Controller 都多算 1 个端点** (`EquipmentManagementController` 数出 11,实际 10——对着文件数一遍才发现)。 做法:类声明之前的映射是类级的,之后的才是方法级的。 """ m = CLASS_PAT.search(src) return len(ENDPOINT_PAT.findall(src[m.end():] if m else src)) def javadoc_head(src): """类 javadoc 的**抬头**:首行标题 + 之后的普通段落, 到第一个 `

`/`

` 小标题或 `@author` 为止。 `

` 不算分界——三种 provenance 写法并存(反引号里是实际内容): ① 标题行里:老门户 `inspect/TpPInspectRecordController`,路由 ... → 反引号里是**类名**(前面还可能带老包路径) ② 第二段:来源:老门户 `zteits-bcp-portal` 的 `AreaManageController` → 反引号里是**类名** ③ 标题行里:老门户 `channelManagement` → 反引号里是**路由**,不是类名!这一种光看类名认不出来(见 scan_legacy 第二遍) 但 `

`/`

` 里全是"与老代码的差异"「不要碰」之类的**对照性**提及,必须排除—— 否则 `ParkLotExtendController` 里那句"与老 `ParkLotManageController` 的端点不混在一起" 会把 19 个端点的 `ParkLotManageController` 当成已迁(见 PROVENANCE_PAT 注释)。 """ m = re.search(r'/\*\*(.*?)\*/', src, flags=re.S) if not m: return "" para = [] for line in m.group(1).splitlines(): body = line.strip().lstrip('*').strip() if body.startswith(' args.top: print(" … 另有 %d 个(用 --top 调整,或 --json 取全量)" % (len(clean) - args.top)) print() print("=" * 72) print("⚠A 机械误报候选(没读登录态)——**可开搬,但逐类核对下面这几行**:**%d 个 / %d 端点**" % (len(hazA), sum(r["endpoints"] for r in hazA))) print("=" * 72) _ida = [r for r in hazA if r["controller"] in IDSPACE_FALSE_POSITIVE] print(" (已核销 %d / %d;核销结论见代码里的 `IDSPACE_FALSE_POSITIVE` 表)" % (len(_ida), len(hazA))) for r in hazA[:args.top]: _mark, _why = idspace_adjudicated(r["controller"]) print(" eps=%-3d ids=%-3d %-40s %s [%s]" % (r["endpoints"], r["idSpaceHits"], r["controller"], r["path"], _mark or u"❓ 未核销")) for n, line in r["idSpaceLines"][:6]: print(" %5d| %s" % (n, line[:110])) if len(r["idSpaceLines"]) > 6: print(" … 另有 %d 行命中" % (len(r["idSpaceLines"]) - 6)) if _why: print(" └ %s" % _why) if len(hazA) > args.top: print(" … 另有 %d 个(用 --top 调整,或 --json 取全量)" % (len(hazA) - args.top)) print() print("=" * 72) print("⚠B 真读了登录态 → 等 P3:**%d 个 / %d 端点**" % (len(hazB), sum(r["endpoints"] for r in hazB))) print("=" * 72) for r in hazB[:args.top]: print(" eps=%-3d ids=%-3d %-40s %s" % (r["endpoints"], r["idSpaceHits"], r["controller"], r["path"])) print() print("=" * 72) print("有缺口(搬过来就是 500,先补服务):**%d 个 Controller / %d 个端点**" % (len(gap), sum(r["endpoints"] for r in gap))) print("=" * 72) for r in sorted(gap, key=lambda r: -r["endpoints"]): detail = ", ".join("%s/%s" % (g["service"], g["reason"]) for g in r["gaps"]) _mark, _why = gap_adjudicated(r["controller"]) print(" eps=%-3d %-40s 缺: %s%s" % (r["endpoints"], r["controller"], detail, (" [%s]" % _mark) if _mark else "")) if _why: print(" └ %s" % _why) if _gap_adj: print(" ★ 其中 %d 个 / %d 端点**已判定不可迁**(见代码里的 `GAP_ADJUDICATED` 表)" "⇒ 真正可迁的缺口是 **%d 个 / %d 端点**" % (len(_gap_adj), sum(r["endpoints"] for r in _gap_adj), len(_gap_open), sum(r["endpoints"] for r in _gap_open))) print() print("=" * 72) print("死代码(整个类被注释掉 / 零映射,**不迁**):**%d 个** " "(老门户 Controller 合计 %d 个,其中 %d 个有映射)" % (len(dead), len(rows) + len(dead), len(rows))) print("=" * 72) for d in sorted(dead, key=lambda d: d["controller"]): print(" %-44s %s" % (d["controller"], d["path"])) if ambiguous: print() print("=" * 72) print("⚠ **一个类级路由下挂了多个 Controller**,仅凭路由认不出谁迁了——" "**别把没迁的当已迁,去 grep**:") print("=" * 72) for a in sorted(ambiguous, key=lambda a: a["route"]): print(" 老路由 %s:" % a["route"]) for c in a["controllers"]: print(" %s" % c) print() print("⚠️ 就绪 ≠ 可迁。**还要过坑 22**(拿 `orgId`/`plNos`/`userAreas` 过滤数据的不能先迁)" "与死代码判定——本脚本只回答「服务齐不齐」这一个问题。") print("⚠️ 而且坑 22 的判据是**机械的**,两个方向都会错:" "① 它**认不出接收者**——`request.getPlNos()`(前端请求体)和 `userInfo.getPlNos()`(登录态)" "在正则眼里一样(第三十五切片:⚠ 档里 12 个类整类没读登录态,是**纯误报**);" "② 就算命中的是登录态,它也**不说那行读到的值有没有真的进 SQL**" "(第三十三切片就栽在这:`ParkInOutRecordController` 的 `setTopOrgId` 在老代码里本来就是注释掉的)" "⇒ 每个候选仍要读源码/smapper(⚠A 档看 `idSpaceLines`,⚠B 档看 smapper 的 `WHERE`)。") print("★ ✔ 池的人工结论在上面 `ADJUDICATED` 表里(脚本只标注、不改判定);**每判定一个类都要登记进去**。") return 0 if __name__ == "__main__": sys.exit(main())