weicw-dshsa
|
7c4c887d74
|
🐛 修认证流程清空司机车辆绑定,导致车辆列表消失
Driver.drivingVehicle 标了 @TableField(updateStrategy = FieldStrategy.ALWAYS),
该策略忽略 null 直接写:任何一次 updateById 只要 patch 里没带这个字段,
就会把库里的 driving_vehicle 写成 NULL。而司机端的车辆列表正是按这一列
的车牌匹配出来的(DriverAppServiceImpl.myVehicles 开头就判空返回空列表),
清空即页面显示「暂无绑定车辆」,且司机在界面上无法自行恢复。
两条链路都会触发:
- 提交认证:updateDriverDisplayCache 只 patch id/状态/驳回原因;
- 审核通过:本次改动新增的快照写回,同样只 patch 证件字段。
已在两处 updateById 前把该车的原值搬进 patch,并补回归测试:
先确认去掉修复后测试确实失败(expected 桂A12345,桂B67890 but was null)。
保留 ALWAYS 策略不动——它保证了「能显式清空车辆绑定」这一能力,
问题在于 patch 式更新必须自己带上原值,而不是删掉策略。
|
2026-10-04 23:24:22 +08:00 |
|
weicw-dshsa
|
4fa6d4c430
|
🐛 个人认证通过后写回司机档案,并补齐资格证字段
个人认证通过时只切了生效指针与认证状态,证件内容一个字都没落到
blade_transport_driver:认证状态显示「已通过」,司机档案里却仍是
管理端当初录入的旧值(甚至为空),管理端与司机端各看各的。
车辆侧早有 applySnapshotToVehicle 做这件事,司机侧一直没有对应实现。
- 新增 applySnapshotToDriver,审核通过时与指针合并在同一次 UPDATE 写回。
只搬司机申报的证件内容,不碰身份标识(姓名/身份证号,由管理端建档确定)
与内部管理归属(司机类型/岗位/驾驶车辆/所属组织)。
- 快照缺失、解析失败或字段对不上时只跳过写回,不阻断审核:审核是独立于
写回的业务动作,不该因为一份读不懂的快照而失败,否则历史快照对应的
对象再也审不过去。
- PersonCertSubmitRequest 补 qualificationType / qualificationBack /
qualificationValidStart / qualificationLongTerm 四个字段。
- qualificationUrl 改名为 qualificationFront,与司机档案列
qualification_front 及管理端用词统一;改名前的记录存的仍是旧键,
写回时回退读一次旧键,避免已填的封面页在审核通过时被丢掉。
- 补测试:证件内容写回、不覆盖身份字段、驳回不写回、历史快照跳过写回
但仍照常通过审核。
qualificationValidStart 只进快照、不写档案:司机档案表没有对应的开始日期列,
管理端的「从业资格证有效期起」同样是只收不存。此处保持一致,不新增列。
|
2026-10-04 22:11:10 +08:00 |
|
weicw-dshsa
|
17ae367100
|
🐛 认证审核菜单名改为「司机审核 / 车辆审核」
原名「认证审核-司机 / 认证审核-车辆」与需求文档的表述不一致,
后台菜单显示为「司机审核」「车辆审核」——与 .scratch 里
审核页分页签那轮需求的措辞对齐。
只改 blade_menu 的 name 字段,code / alias / path / 权限码均不变,
故不影响权限校验与前端路由。
注意:该 INSERT 是 ON DUPLICATE KEY UPDATE,改脚本本身不会改库里已有的行,
要在已有环境生效需重跑本脚本(新环境按脚本建库则直接是新名)。
|
2026-10-04 19:16:38 +08:00 |
|
weicw-dshsa
|
ee135cd150
|
🐛 重新认证也要过内容校验,不再只校验新建车辆
起先 validateCertContent 只在 createVehicleForDriver 里调用,走 request.id 的重新认证路径
一句都不校验:待审内容虽然要等审核通过才写进车辆档案,但它已经是一条不可变记录,
并会在通过时按字段写回车辆行,于是「内容校验」对重新认证形同虚设。
- 内容校验提到 submitVehicleCert 里、resolveBoundVehicle 之前,两条路径共用;
- 「所属组织非空」拆成 requireOrganizationForNewVehicle,仍只在新车建档时要求:
该列只在 INSERT 时需要,它属于内部管理归属,拿它拦住重新认证只会让司机无从下手。
- 补 2 个用例:重新认证传空「所有人」必须被拒;司机档案没有所属组织时重新认证仍应放行。
- 顺带把 7 个此前只传车牌/ID 的用例改用字段齐全的请求:否则它们会因为「车牌颜色不能为空」
提前失败,看着还绿、其实已经不再验证原本要验证的越权与作废逻辑。
|
2026-10-03 17:56:56 +08:00 |
|
weicw-dshsa
|
11e62c172c
|
🐛 修车辆重新认证长期有效字段回显,有效期不再要求起期
- 卡片接口不再把「长期」当日期下发,也不在长期有效时下发上一版证件残留的止期:
两者都会被「重新认证」表单原样回填再原样提交,前者进日期框后按 LocalDate 反序列化直接报错,
后者会让一辆长期有效的车带着一个早已作废的止期一路写回车辆行。
- 车辆有效期校验收敛为「止期必填、起期可空」,和管理端车辆表单同一口径:
起期只在两端都有值时才比较先后。司机端表单本来也不采集起期。
- 补 6 个测试:3 个盯卡片回显(含长期有效 + 残留止期、无止期两种),
3 个盯有效期校验(起期可空、止期必填、长期有效时止期可空)。
|
2026-10-03 17:46:53 +08:00 |
|
weicw-dshsa
|
04a7846328
|
✨ 车辆认证扩到6张证件图,字段与车辆档案对齐;补齐建车必填列
司机端车辆认证此前只有 3 张图、5 个字段,且语义与车辆管理不一致
(「副页」字段里装的其实是主页反面)。本次把证件内容整体对齐车辆表列:
- 车辆表新增 owner_name(所有人:行驶证登记的单位名称,不是司机)与 vin;
- 提交请求与司机端车辆卡片全量对齐表列,行驶证四张图各自独立、
不再把「主页反面」与「副页正面」挤在同一个字段;
- 业务关系新增为司机自选(自有/管理挂靠/业务合作),不再由后端替用户猜。
顺带修掉三处会直接让功能不可用的缺陷:
1. 司机建车必失败:organization_name、approved_load_kg、business_relation、
energy_type、registration_no 五列 NOT NULL 且无默认值,而建车链路一个都不设置,
严格模式下 INSERT 直接报「doesn't have a default value」。已补齐并加服务端校验
(司机提交链路此前一句都不查,全靠前端)。
2. OCR 自动填充从未生效:后端按契约透传百度原始结果(中文键),
前端读的却是扁平字段名,永远 undefined 且不报错。新增 OcrFieldSupport
做归一化(键名别名容错、数值/日期/「长期」归一化、外廓尺寸拆三段、
有效期区间拆起止),响应的 fields 即前端直接回填的字段。
3. 审核通过不写回车辆档案:状态已通过、内容却还是旧的,自相矛盾。
现把生效快照的证件内容与指针合并成同一次 UPDATE 写回;快照是改造前
旧格式时明确报错,而不是静默写一堆空值。
|
2026-10-03 16:34:47 +08:00 |
|
weicw-dshsa
|
214ff38ef8
|
🐛 修复道路运输证OCR恒失败:上游不得替它补默认正反面
OcrTypeMapping.normalizeSide 对空值恒返回 front,控制器把这个 front 传给下游,
而道路运输证没有正副面参数(supportsSide 为 false),下游于是判
「传了 side 但该证件不支持」并直接抛错——小程序不传 side、传 front、
传空串三种方式实测全部失败,该证件 OCR 从来没能用过。
改为未指定时返回 null,把「默认取正面」交给知晓证件类型的一侧决定,
并补测试钉住这条契约。
|
2026-10-03 15:35:22 +08:00 |
|
weicw-dshsa
|
cc997c1c0c
|
✨ 审核列表新增司机姓名与手机号,并修正驾驶车辆绑定校验
审核页的司机姓名与手机号取司机档案的**当前值**,不是提交时的历史值:
手机号从未进入过认证快照(只有司机档案这一个来源),而队列的用途是
「找得到人、联系得上」。司机侧的认证对象就是记录本身;车辆侧记录不含任何
司机信息,取提交该记录的司机。存量订正记录里没有系统账号的那些司机解析不到,
两列为空属正常状态。
筛选必须发生在分页之前,故列表改为自定义 SQL;在 Java 层补字段再过滤会变成
「先分页再过滤」,每页条数与总数都不准。姓名搜索同时匹配档案当前姓名与提交时的
旧名,改名后按新名、旧名都搜得到。
顺带修掉两处既有缺陷:
1. 管理端保存绑定多辆车的司机会误报「驾驶车辆不存在」——小程序把车牌逗号并列
写入 driving_vehicle,管理端却拿整串去车牌表精确查,两边口径不一致。
2. 「一个司机最多 5 辆车」后端从未校验,且 driving_vehicle 原为 varchar(30),
5 个车牌连分隔符要 39 字符,超长写入会被静默截断成半个车牌(还会被当成一辆车)。
列扩到 varchar(100) 并在写入前校验数量与车牌格式。
|
2026-10-03 14:35:59 +08:00 |
|
weicw-dshsa
|
bd01122409
|
🐛 修复认证记录提交人记成司机档案ID
提交时把 driver.getId()(司机档案ID)当提交人写进了 submit_user,
而该字段要靠 UserCache.getUserRealName(userId) 翻译成姓名,
两处ID都是雪花ID、类型也相同,传错不报任何错,
只在审核页表现为「提交人」显示成一串数字。
改为 driver.getUserId(),并补回归测试锁住(用两个不同的ID常量,
传错必然断言失败)。
|
2026-10-02 19:58:11 +08:00 |
|
weicw-dshsa
|
2983ef78af
|
✨ 认证记录列表支持按审核状态查询
审核页要按「待审核 / 已通过 / 已驳回」分页签展示,而原接口把状态写死成
审核中(status=0),已通过/已驳回的记录根本查不出来。
新增可选的 status 参数;不传时仍按审核中处理,保持原有调用方行为不变。
|
2026-10-02 19:58:06 +08:00 |
|
weicw-dshsa
|
fbced4e6a0
|
✨ 支持司机新增未登记车辆(原实现会让小程序「+ 新增车辆」必然失败)
小程序的车辆录入页允许司机直接填车牌提交,且空列表时提示
「暂无绑定车辆,点击「+ 新增车辆」添加」。但原实现只按「已完成绑定」查找车辆,
未登记的车牌一律抛「车辆不存在或未绑定」——而司机名下还没有这辆车,也就永远无从绑定,
形成死结:该入口必然提交失败(属票 07 的验收点)。
改为按车牌区分三种情形:
- 未登记 → 就地建档(写入识别信息与证件内容)并把车牌追加到司机名下;
- 已登记且属于本人 → 复用,不重复建档(容忍车牌写法差异);
- 已登记但不在本人名下 → 拒绝,**不顺手改归属**(避免抢车)。
关键约束:新建车辆**不带生效快照指针**,故建档本身不会让一辆未审核通过的车变得可用
——是否已认证仍只由审核通过决定。
4 个新用例覆盖上述三种情形与空车牌拒绝。全模块 89 个测试通过。
|
2026-10-02 17:35:33 +08:00 |
|
weicw-dshsa
|
c66af4c162
|
✨ 补车辆认证删除接口(契约与小程序都已在调用,后端却缺失)
api-contract.md 承诺 DELETE /driver/auth/vehicle/{id},小程序
api/auth.ts 的 deleteVehicleAuth 也从 subpackages/auth/vehicle.vue 真实调用它,
但后端一直没实现——该操作此前必然 404。
实现要点:
- 对车辆做**逻辑删除**(deleteLogic,is_deleted=1),认证记录**一律保留**:
记录是不可变快照,也是「曾通过即永久可用」这一判定的依据,物理删除会让历史无从追溯;
- 越权校验复用同一套车牌绑定判定,写法差异(间隔符/大小写)不影响结果。
3 个用例:逻辑删除而非物理删除(显式断言从不调用 removeById/removeIds)、
越权拒绝、脏车牌仍可识别。全模块 85 个测试通过。
|
2026-10-02 17:32:07 +08:00 |
|
weicw-dshsa
|
4de1ea5209
|
🐛 修 code-review 指出的三处问题:车牌匹配、提交人姓名为空、分页写法
1) **车牌匹配漏了规范化兜底(最严重)**
DriverWaybillServiceImpl.assertCertified 把 splitPlates 返回的**规范化**车牌
直接与 plate_no 原始列做精确 IN。库中存在带间隔符/横线/大小写差异的车牌时
会命中 0 行,导致「名下无可用车辆」→ **已认证的司机被误判为未认证而拒绝接单**,
与本次改造的核心验收「已认证的车重新提交后仍可正常接单」相悖。
DriverCertServiceImpl.findByPlateWithinBound 同样问题:先精确 IN 取候选再规范化
filter,而 filter 只能收窄不能放宽,漏掉的候选永远找不回来(等于没做规范化匹配)。
改法:把「精确 IN 或 规范化后相等」抽成 VehiclePlateSupport.applyPlateMatch,
三处调用点(含原先自己实现的 DriverAppServiceImpl.myVehicles)统一使用。
并加测试断言 SQL 表达式覆盖 Java 侧剔除的全部字符、且必须带 OR 兜底,
防止两边日后悄悄分叉。
2) **审核详情的提交人姓名恒为空**:CertAuditDetailVO.submitUserName 声明了却从未赋值,
违反「VO 展示字段须由服务端用 UserCache 填好」的规范。已补。
3) **分页未走项目统一写法**:原用 Page.of 手搓,全模块无先例。改为
Controller 收 Query + Condition.getPage(query),与 AccidentRecordController 等一致。
Query 绑定的正是 current/size,故三端契约不受影响。
顺带删除无任何调用者的 ICertAuditService.getRecordOrNull(推测性抽象 + 纯转发)。
82 个测试通过。
|
2026-10-02 17:30:48 +08:00 |
|
weicw-dshsa
|
0309d26184
|
✅ 权限表达式抽为共享常量并补可求值性测试
@PreAuth 里的 SpEL 字符串由框架在**请求时**求值,root object 是 AuthFun。
函数名写错既不过编译期、也不在启动期暴露,只会在真实调用时抛异常
(表现为接口 500)——正好是最难在开发阶段发现的一类错。
故把两个表达式抽到 CertAuditPermissions,并在测试中用与框架相同的
SpelExpressionParser + AuthFun 作为 root object 实际解析求值,覆盖:
- 只授司机菜单 / 只授车辆菜单 / 两个都没有;
- 只有查看权限、没有审核按钮权限时必须拒绝(能看≠能审);
- 表达式必须引用票 01 建的真实菜单码,且不得再出现角色名判断。
顺带消除控制器里与共享常量重复的两份字符串。
6 个新用例;全模块 79 个测试通过。
|
2026-10-02 17:08:22 +08:00 |
|
weicw-dshsa
|
60c1d626a2
|
🐛 认证审核接口改用菜单权限码,并补回被漏掉的审核岗位
原写法 @PreAuth("hasAnyRole('administrator','admin')") 有两个实际缺陷:
1. 库中**并不存在 admin 角色**(实查 blade_role:超级管理员 administrator、
运管 yunguan、财务 caiwu、录单员 ludan…),该分支永远不成立;
2. 真正承担认证审核的「运管」岗位**被排除在外**——即审核功能上线后没人能审。
改为按本项目的权限模型走菜单码(票 01 已建好这四个码):
cert_certification_audit_driver / _vehicle(列表与详情)
cert_certification_audit_driver_audit / _vehicle_audit(审核动作)
用 SpEL 而非 @PreAuth(menu = "..."),是因为同一组接口由 bizType 区分同时服务
司机与车辆两侧,单值 menu 表达不了「二者之一」。
已核对框架实现:AuthAspect 把 value 交给 SpEL 求值,root object 即 AuthFun,
而 hasMenu 是它的公开方法,故该表达式可用;menu() 非空时才会先行校验。
73 个测试通过。
|
2026-10-02 17:05:42 +08:00 |
|
weicw-dshsa
|
d61f1bcae4
|
♻️ 门槛里的启用判断改用 BladeConstant.DB_STATUS_NORMAL
原先写死 Integer.valueOf(1),虽然值与常量一致,但读代码的人无从判断
「1 到底是启用还是别的什么」。改用框架常量后语义自明,
将来若有人改动状态取值也不会遗漏此处。
已核对:车辆表 status 实际取值为 1(18 辆)与 2(2 辆),
BladeConstant.DB_STATUS_NORMAL 确为 1,判断逻辑未变。
73 个测试通过。
|
2026-10-02 17:01:34 +08:00 |
|
weicw-dshsa
|
f351bad545
|
✨ 接单叠加认证可用性门槛,带开关可快速回退
在原有运营状态(启用/停用)之上叠加一层「是否已认证」:
- 车辆:启用 且 存在生效快照;司机:存在生效快照。
判据是**生效快照指针**而非「最新提交状态」展示缓存,这一区别正是本改造的核心:
司机重新提交认证会把展示缓存变成「审核中」,若照它判定,司机一提交就接不了单
——那恰恰是本次改造要消除的问题。生效快照只在审核通过时切换,故曾通过即持续可用。
两个开关默认开启,可通过配置关闭以便线上快速回退到改造前行为:
transport.certification.vehicle-gate-enabled / driver-gate-enabled
门槛落在 DriverWaybillServiceImpl.accept():司机本人与名下**任一**车辆满足即可。
运营状态优先于认证——停用的车即便已认证也不可用(认证只叠加不覆盖)。
顺带消除第三份重复的车牌匹配实现:DriverWaybillServiceImpl 里的
splitPlates/normalizePlate 与 VehiclePlateSupport 逐行相同,改为委托同一实现。
10 个门槛用例(含「重新提交后仍可用」「停用车即便已认证也不可用」)
与全模块 73 个测试通过:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:46:04 +08:00 |
|
weicw-dshsa
|
6831ba6fca
|
✅ 补个人认证「提交→审核→生效」闭环测试
司机认证最关键的一条业务闭环,单看提交侧或审核侧都验证不到:
提交之后司机侧查到的必须仍是旧生效值(否则等于提交即自动认证),
审核通过之后才变成新值(否则认证永远不生效)。
用内存 Map 承接记录读写,让提交服务与审核服务真实共享同一份状态,
避免两边各自 mock 成「恰好一致」——那种测试对集成缺陷是盲的。
5 个用例覆盖:
- 提交后仍是旧值、审核通过后才变新值;
- 驳回后生效值不变(已认证司机不会被驳回下线);
- 从未认证时审核前查不到生效值;
- 连续提交两次只剩一条待审,且被作废的那条不能再被审核;
- 已通过的记录不可再审(防止把生效快照改坏)。
全模块 63 个测试通过:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:36:41 +08:00 |
|
weicw-dshsa
|
2edbacf838
|
✨ 小程序证件 OCR 薄适配层,含类型映射
小程序按既有契约 POST {imageUrl, type, side?} 到 /driver/auth/ocr,
后端转调已有的百度 OCR 能力(recognizeUrl)。
映射的必要性在于两侧命名对「行驶证」和「驾驶证」的用词正好相反:
驾驶证 —— 小程序传 driver,后端 OCR 用 driving_license
行驶证 —— 小程序传 vehicle,后端 OCR 用 vehicle_license
把入参直接透传下游,调用方不会收到任何错误,只会拿到另一种证件的字段,
问题要到人工核对时才发现。故用显式白名单逐一对应。
采用白名单而非「透传 + 兜底为通用识别」,是因为从业资格证没有对应的百度接口:
静默降级成通用文字识别会让前端以为识别成功,实际拿到一堆通用文本。
该类型明确报错并提示手动填写。
正反面:小程序目前不传 side,按 front 处理(正页是常用场景)。
10 个映射用例覆盖五个小程序类型、后端既有命名、大小写/分隔符变体、
未知类型、空值、不支持类型与 side 归一化。
全模块 58 个测试通过:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:31:42 +08:00 |
|
weicw-dshsa
|
5747975152
|
✨ 管理端认证审核接口,并弃用旧车辆审核入口
审核作用于**一条认证记录**,不是车辆行或司机行。两条路径的差别是本次改造的关键:
- 通过:记录转已通过 + 写审核人/审核时间,并把该对象的生效快照指针**切换**到它
(切换而非追加,故一个对象至多一条生效快照);
- 驳回:记录转已驳回并留原因,生效快照指针**一个字都不动**。
因此「已认证的车被驳回」不会掉出可用池,只是这次提交没被采纳。
已终态(已通过/已驳回/已作废)的记录拒绝再次审核。理由:若允许,
把指针指着的那条「已通过」改成「已驳回」,生效值就凭空消失了。
审核详情同时返回待审快照与当前生效值两侧,供审核页做字段级 diff
(这正是「看出司机改了什么」的实质要求),并附 certified 标记,
便于前端提示「本次驳回不影响该对象继续使用」。
旧接口 POST /transport-vehicle/audit-certification 置为 @Deprecated 并加告警日志:
它作用于车辆行且原地覆盖,在生效值/待审值分离后语义已不成立。
保留而不删除是为了避免小程序历史版本或其它系统 404;它只写展示缓存,
不再影响可用性判定(可用性只看 effective_cert_record_id)。
修复过程中被测试抓到两个真实问题:
1. 通过路径原先对同一行连发两次 UPDATE(先切指针、再刷展示缓存),
合并为一次,消除了「后写覆盖先写」的可能;
2. 新增 EffectivePointerUpdateSqlTest,直接断言 MyBatis-Plus 生成的 UPDATE SET 片段:
指针列必须被「值非 null 才写」的 if 包住——驳回传 null 因此物理上碰不到它。
把「靠约定」变成「靠语句」,若有人给该字段加 ALWAYS 策略,测试立刻变红。
13 个审核用例 + 全模块 48 个测试通过:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:26:20 +08:00 |
|
weicw-dshsa
|
63762b9dd0
|
✨ 司机端认证提交与记录查询接口(车辆/个人)
实现司机在小程序上提交认证、查看生效值与认证记录的能力。
提交链路的结构性保证(这是本次改造的核心约束「提交审核不影响车辆正常使用」):
- 提交只产生一条新的待审记录,**绝不修改 effective_cert_record_id**,
因此已认证的车重新提交后依然可用;待审内容在审核通过前只存在于记录表里;
- 同对象至多一条待审:落新记录前先把前一条待审转已作废,
且只动作待审——已通过/已驳回是终态,尤其不碰生效快照指着的那条;
- 新记录状态显式设为待审:框架会把空 status 静默填成 1,而 1 在本表是「已通过」,
不显式设置会让一次提交凭空变成审核通过;
- 新记录清空驳回原因,避免出现「审核中」却带着上一次驳回文案。
越权校验:司机只能提交/查看本人及本人绑定车辆的认证(复用 VehiclePlateSupport),
不可查看者返回业务异常而不是空列表——空列表会被误读成「没有记录」。
新增:
- DriverAuthController(/driver/auth/**,兼容 /blade-transport/driver/auth/**)
- IDriverCertService / DriverCertServiceImpl
- PersonCertSubmitRequest / VehicleCertSubmitRequest
- TransportCertRecordVO.statusText(状态文案统一在 Wrapper 翻译,四端一套说法)
- IDriverAppService.currentDriverEntity()(认证写入需要司机实体)
17 个用例覆盖票 02 的三条验收点与不变式(含「重新提交后生效值仍是旧的」)。
全模块 33 个测试通过:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:17:06 +08:00 |
|
weicw-dshsa
|
e891100a23
|
✨ 认证记录状态机规则集中为 CertificationRuleSupport 并补测试
把「什么状态能做什么」从持久化里拎出来单独验证。这几条规则是认证改造的地基,
改坏的后果都是线上事故,故先锁住行为再写业务:
- prepareForSubmit:新提交一律待审,并清空驳回原因(否则会继承上一次的驳回文案,
出现「审核中」却带着驳回原因的自相矛盾状态);
- supersedeIfPending:再次提交只作废前一条待审,已通过/已驳回/已作废都是终态不碰
——尤其是不得改动已通过的记录,它可能正被生效快照指针指着;
- isCertified:唯一判据是生效快照指针,故「曾通过即永久保持」;
- isAuditable:只有待审可审,终态再审会把生效快照改坏;
- statusText:四种状态各一个中文说法,与列注释、前端文案保持一致。
TransportCertRecord.REJECT_REASON_MAX_LENGTH 与 reject_reason 列宽(200)对齐。
9 个用例覆盖上述规则与边界(null、未知状态、超长原因)。
可离线执行:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:10:34 +08:00 |
|
weicw-dshsa
|
8a2d83b3cd
|
♻️ 车牌软关联逻辑抽为 VehiclePlateSupport 并补测试
司机 driving_vehicle 与车辆表 plate_no 之间没有外键,只有文本关联,且库中存在
间隔符/横线/大小写差异,一个字段还可能并列多辆车。认证提交的越权校验
(只能操作本人绑定车辆)依赖这套匹配:漏判会让司机改不了自己的车,
误判会让司机改到别人的车,故先把规则集中并锁住行为。
- 新增 VehiclePlateSupport:splitPlates / normalize / isBound;
isBound 对空值一律判不绑定(不能因为双方都为空就放行,那等于越权)。
- DriverAppServiceImpl 原先自己实现了一份 splitPlates/normalizePlate,
改为委托同一个实现,避免两处口径分叉。
- 6 个用例覆盖分隔符变体、去重、规范化、脏数据与绑定判定。
可离线执行:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:08:47 +08:00 |
|
weicw-dshsa
|
9f3914463e
|
✅ blade-transport 引入 JUnit 5 测试基础设施
本模块此前没有可自动执行的测试:无 src/test 目录、pom 无测试依赖、
父 pom 无 surefire、全仓 0 个 @Test(仅有的 6 个 *Test.java 实际是 main() 手跑脚本)。
而认证改造的提交/审核链路带状态机语义(同对象至多一条待审、提交不得修改生效快照等),
必须用可重复执行的单元测试守住,否则只能靠人肉 review。
- blade-transport/pom.xml 增加 spring-boot-starter-test(test scope,版本由 blade-bom 管理);
- 加一个基础设施自检用例,确认 JUnit 5 在本模块能被发现并执行。
可离线执行:mvn -o test -pl blade-service/blade-transport -am
|
2026-10-02 16:03:50 +08:00 |
|
weicw-dshsa
|
36d23cb426
|
📝 认证记录相关文件去掉外部文档引用,注释改为自解释
前一次提交的注释大量引用本仓库之外、同事看不到的东西:
docs/adr/0001、docs/adr/0002、GLOSSARY.md、.scratch/ 下的 spec 与 ticket 编号、
以及 ADR-0002 / D10 / 不变式 I1~I3 这类代号。这些都在另一个 git 仓库
(weicw/tms-erp)里,单独 checkout 本仓库的人根本找不到,只会困惑。
改为把结论直接写在注释里:
- 生效快照指针为何「一经设置永不回退」(原来只写「见 ADR-0002」);
- 为何 redis 状态字段不能用 0 兜底;
- 为何生成列要排除 is_deleted、为何 tenant_id 必须 NOT NULL;
- changeStatus 为何被禁用(原来写「破坏 I3 的留痕」)。
顺带对齐本仓库的既有排版习惯:
- 横幅破折号由 77 个改为 30 个(与 blade_annual_inspection_record.sql 等一致);
- 去掉 SQL 注释里的 markdown 粗体(本目录无其他脚本这么写)。
仅注释变更:SQL 的 diff 已逐行核对,非注释行改动为 0,DDL 一字未动;
迁移重跑仍 exit 0,audit-migrations 通过,mvn clean compile 成功。
|
2026-10-02 15:47:39 +08:00 |
|
weicw-dshsa
|
87f4c3a830
|
✨ 新增认证记录不可变快照数据模型与迁移脚本
按 ADR-0001/0002 建立司机与车辆共用的认证记录表 blade_transport_cert_record
(biz_type 区分、载荷存 JSON、检索字段提升为真实列),并为司机/车辆表补生效快照指针。
不变式结构性保证:
- I1 同对象至多一条待审:生成列 pending_biz_id + 唯一索引,数据库层拦截;
生成列同时排除 is_deleted,避免逻辑删除的待审记录永久占用唯一键。
- I3 快照不可变:snapshot_json 等 5 列标 FieldStrategy.NEVER,永不进入 UPDATE。
- status 承载审核状态(0审核中/1已通过/2已驳回/3已作废),新增时必须显式设置:
框架 resolveEntity 会把 null 的 status 静默填成 1(即「已通过」),
且继承来的 changeStatus 可直接写 status,两条通道均已封堵。
其他:司机表补身份证有效期三列并订正排序规则为 utf8mb4_general_ci
(原先为 0900_ai_ci,与车辆表 JOIN 已发生过 Illegal mix of collations);
生效快照指针加 @JsonProperty(READ_ONLY),防止以 /submit 请求体自行「认证」自己;
同步基线建表脚本与认证审核菜单/权限码。本票不含任何业务接口。
|
2026-10-02 14:40:56 +08:00 |
|
b2894lxlx
|
087f361a8b
|
✨ 组织类型改业务字典,并完善金蝶部门代码与MK再提交
组织类型翻译切换为 organization_type;新增金蝶部门代码字段;流程已结束或已通过时强制新建MK表单实例。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-29 03:15:26 +08:00 |
|
b2894lxlx
|
32cff235e0
|
🐛 修复审核通过后再提交未新建MK流程实例的问题
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-29 02:44:48 +08:00 |
|
b2894lxlx
|
fc1e9a95de
|
mk调试
|
2026-09-29 02:23:59 +08:00 |
|
b2894lxlx
|
beb0850644
|
✨ 解耦MK表单实例与业务ID,公开页按formInstanceId反查业务数据
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-29 02:23:19 +08:00 |
|
b2894lxlx
|
247440288b
|
🐛 修复运单计划-1占位,并对账导入失败明细按原表全量列导出
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-27 23:16:17 +08:00 |
|
b2894lxlx
|
eef9d7ad9d
|
✨ 完善运单导入配载标识、导入状态与凭证文件夹统计
记录配载标识号并在同批次多运单时生成配载单,导入状态与运单状态对齐,凭证按车牌文件夹统计并支持压缩包重传匹配。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-27 22:54:30 +08:00 |
|
b2894lxlx
|
f3a614ffe9
|
Merge branch 'master' of https://git.websoft.top/b2894lxlx/tms-erp-api
|
2026-09-27 21:27:14 +08:00 |
|
b2894lxlx
|
1185c9792c
|
🐛 修复车务非必填-1占位落库,并完善总单分段调度与运单导入
上一段运单完成到达后才可调度且不超过到达量;车务多模块保存/列表/导出将-1归一为null;同步网关大报文限制与导入校验能力。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-27 21:26:54 +08:00 |
|
b2894lxlx
|
0c52797c31
|
Merge remote-tracking branch 'websoft/fix/issue-2-10-import-batch-rollback'
|
2026-09-27 13:03:12 +08:00 |
|
weicw-dshsa
|
c86c5ff968
|
🔀 合并 fix/issue-2-10-import-batch-rollback
导入链路整批修复(issues #2-#10 + 违章/记录类/台账类/运输对账等):
- 所有批量导入改为「任一行失败即整批回滚」
- 失败明细返回原表全量并逐列标红(不再只给错误行)
- 读取阶段异常兜底,不再暴露 HTTP 500 技术文案
- 空表提示改为「未读取到数据行,请确认表头下方已填写内容」
- 修复常用货物型号必填漏校验、运输对账动态费用列静默转 0、
失败明细行号错位等
详见该分支 16 个提交。
|
2026-09-26 15:30:04 +08:00 |
|
weicw-dshsa
|
4ccc28e991
|
🐛 修复三处导入校验缺陷与失败明细行号错位
对全仓 31 处导入入口做了一次「导入校验 vs 新增/编辑校验」的一致性审计,
以下三处是审计中严重度最高的、且业务口径无歧义的缺陷:
1) 常用货物:型号必填只在新增/编辑生效,导入侧被放过
submit(CommonCargo) 走 validate(cargo, requireModel=true),而导入调
submit(cargo, false) —— 页面上存不进去的数据(型号为空)却能通过导入写进库。
CommonCargoExcel 的「型号」列也没标 *,用户按模板填根本不知道它必填。
改法:去掉 requireModel 开关(该参数已成死参数,留着只会让两边再次分叉),
导入与新增/编辑共用同一个 validate;模板表头改 *型号。
2) 运输对账:动态费用列填非数字被静默改成 0.00
VehicleReconciliationFeeReader / CargoReconciliationFeeReader 的 decimal()
在 catch (NumberFormatException) 后直接返回 ZERO —— 「一百元」变成 0.00 存进
feeItemsJson,参与结算金额与自动匹配,属静默写脏数据。
改法:抛新增的 ImportCellReadException(blade-common),带上行号/列名/原值,
由既有读取阶段兜底导出原表全量明细。
3) 运输计划:文案「数量/里程必须为正数」与规则不符
条件只判 < 0(0 是合法值),新增/编辑侧的文案是「不能小于0」。统一为「不能小于0」。
顺带修掉一个更基础的错位:
ImportFailureExcelUtil.readFailureRowIndex 直接拿
ExcelDataConvertException.getRowIndex() 当失败明细的行下标,但前者是
【绝对 0 基行号】(表头=0),后者只对数据行计数。错误恰好落在最后一行时
靠 clamping 侥幸正确,落在首行就会把原因标到下一行 —— 实测首行出错时原因
被标到了第 3 行(应为第 2 行)。现统一换算(-1)并把两类异常收口到
readFailureExcelRowIndex。
验证:
- mvn clean install -DskipTests 通过。
- 全套 16 个导入回归脚本 469 项断言全绿,其中:
- 运单核对新增用例 04(首行读取失败须标在第 2 行)、05(动态费用列填中文
必须报错),运单核对套件 28 项全绿;
- 常用货物新增用例 08(型号为空须整批回滚),套件 29 项全绿;
- 读取失败套件 4 项全绿,证明行号修正未破坏既有断言。
|
2026-09-26 12:45:17 +08:00 |
|
weicw-dshsa
|
9bf7fdfe99
|
⚡ 空表导入提示改为「未读取到数据行,请确认表头下方已填写内容」
原提示「导入数据不能为空」在用户上传「只有表头、未填数据行」的文件时容易误解
(文件并非空文件,只是没填内容)。全项目 27 个模块、30 处统一替换该文案。
验证:mvn install -DskipTests 通过;空表上传返回新文案;
13 个导入回归脚本 383 项断言全绿。
|
2026-09-26 11:50:57 +08:00 |
|
weicw-dshsa
|
2d185f0de6
|
🐛 导入单元格无法转换时改为返回可读失败明细,兜底 26 个导入接口
问题(用户实测反馈):金额列填「一万二千元」这类无法转换的内容时,EasyExcel 在
ExcelUtil.read(...) 阶段就抛 ExcelDataConvertException,根本走不到行级校验;
控制器只 catch ImportFailureException,异常穿透到全局处理器,用户看到的是
HTTP 500 + 「Convert data ... to class java.math.BigDecimal error」这类技术文案,
且完全拿不到失败明细。
影响面:全部 27 个导入接口(读阶段的 ExcelUtil.read 都在 try 内、只 catch
ImportFailureException),非维修记录独有。
改动:
- ImportFailureExcelUtil 新增三个能力(blade-common,公共层一次覆盖各模块):
1) describeReadFailure:把 ExcelDataConvertException 转成
「第N行「列名」格式不正确:<原值>」,列名按列序号反查 @ExcelProperty;
2) readRawRows:以原始文本(Map)方式重读文件,不做类型转换因此绝不抛异常,
用于拿回「原表全部行」;
3) exportReadFailure:一站式兜底(重读 → 导出全量行 + 仅出错行带原因 +
按列标红),返回 false 表示非转换类异常,调用方继续抛出保持原行为。
- 26 个导入控制器在现有 catch 之后追加 catch(Exception) 兜底,未改动原有
行级校验与整批回滚逻辑。
验证:
- 实测维修记录/费用项/币种汇率三个模块:HTTP 500→200,返回全量明细,
文案如「第5行「费用」格式不正确:一万二千元」(行号经单行/四行文件双向校准)。
- 既有回归无退化:记录类 80/80、台账 32/32、港口码头 45/45、货物类型 24/24、
费用项 20/20、币种汇率 25/25。
- 新增 scripts/api-read-failure-test.sh(4 断言)固化该场景。
|
2026-09-26 11:37:07 +08:00 |
|
weicw-dshsa
|
be02a5e942
|
🐛 修复运输对账导入无回滚与运输计划明细不全(批次4)
本批是 16 个模块收尾,两个特殊模块:
1) 运输对账(importVehicles / importCargoes)——最严重的一例:
既无 setRollbackOnly 也不抛异常。方法开头 resetExternal(id) 清空既有外部账单,
循环末尾无条件插入本批正确行,然后 return failures。一旦有失败行:
旧外部账单已被清空、新账单只插入了部分——数据损坏且不可恢复。
改法:任一行失败时抛 ImportFailureException(rows),让 resetExternal 清空
与本批插入在同一事务内一起回滚。另把失败原因写回原行(异常携带原表 rows,
只写 Vehicle/CargoReconciliationFailureExcel 副本会丢原因)。
控制器改 catch 异常导出原表全量(用 export 保留按列标红)。
2) 运输计划(importTransportPlan)——失败明细只返回 errorMap.values()(仅错误行)。
改为抛 ImportFailureException(data) 携带原表全量;控制器 catch 后导出。
验证:mvn install -DskipTests 通过;全项目 12 个导入回归脚本 379 项断言全绿。
全项目已无 TransactionAspectSupport...setRollbackOnly() 调用残留。
注:运输对账需先建对账草稿(多张前置表)才能端到端回归,
本批仅完成代码改造与编译验证,回归用例待补(见后续说明)。
|
2026-09-26 11:10:37 +08:00 |
|
weicw-dshsa
|
962bcc7879
|
🐛 台账/主数据类导入(4 个模块)失败明细返回全量原表
覆盖:设备台账 / 保养计划(blade-transport)、铁路站点 / 机场主数据(blade-system)。
问题同前两批:收集 errorList 后 setRollbackOnly 并 return,控制器据此导出
「仅错误行」——失败明细不是原表全量,用户无法对照原表修正。
改动(同构):
- 服务层:抛 ImportFailureException(data),明细携带原表全部行;
回滚由异常触发(替代 setRollbackOnly)。注意 blade-system 的两个模块
用的是静态导入 TransactionAspectSupport,模式略有差异。
- 控制器:catch 异常取 getFailureList() 导出(用 export 保留按列标红),
补上传文件校验。
验证:mvn install -DskipTests 通过;台账回归 32/32,记录类回归 80/80。
|
2026-09-26 10:58:15 +08:00 |
|
weicw-dshsa
|
96010cf542
|
🐛 记录类导入(第二批 4 个模块)失败明细返回全量原表
覆盖:里程记录 / 油电记录 / 其他费用记录 / 运输变更记录。
问题同第一批:收集 errorList 后 setRollbackOnly 并 return,控制器据此导出
「仅错误行」——失败明细不是原表全量,用户无法对照原表修正。
改动(同构):
- 服务层:抛 ImportFailureException(data),明细携带原表全部行;
回滚由异常触发(替代 setRollbackOnly)。
- 控制器:catch 异常取 getFailureList() 导出(用 export 保留按列标红),
补上传文件校验。
验证:mvn install -DskipTests 通过;记录类回归 80/80
(10 模块 × 2 用例,含第一批 6 个模块的回归确认)。
|
2026-09-26 10:52:02 +08:00 |
|
weicw-dshsa
|
f24209fa8e
|
🐛 记录类导入(6 个模块)失败明细返回全量原表
问题:这 6 个模块(事故记录/年检记录/ETC记录/保险记录/维修记录/换胎记录)
在收集 errorList 后 setRollbackOnly 并 return,控制器据此导出「仅错误行」——
失败明细不是原表全量,用户看不到哪几行是对的、不便对照原表修正。
改动(6 个模块同构处理):
- 服务层:改为抛 ImportFailureException(data),明细携带**原表全部行**;
整批回滚由异常触发(替代 setRollbackOnly,语义不变)。
- 控制器:catch 异常后取 getFailureList() 导出,并补上传文件校验。
用 export(...) 保留按列标红(exportFailureReasonOnly 会丢失该能力)。
验证:mvn install -DskipTests 通过;记录类回归 48/48
(scripts/api-records-import-test.sh,6 模块 × 2 用例)。
标志性断言:3 行正确 + 第 4 行错 → 明细返回全部 4 行、仅第 4 行带原因、
库中零写入;全部正确 → 正常入库。
|
2026-09-26 10:46:39 +08:00 |
|
weicw-dshsa
|
81824b5062
|
🐛 违章记录导入失败明细返回全量原表
问题:importViolationRecord 收集 errorList 后 setRollbackOnly 并 return,
控制器据此导出「仅错误行」——失败明细不是原表全量,用户看不到哪几行是对的、
也不便对照原表修正(用户反馈:违章记录没有返回全量表格)。
改动:
- 服务层:改为抛 ImportFailureException(data),明细携带**原表全部行**;
整批回滚由异常触发(替代 setRollbackOnly,语义不变)。
- 控制器:catch 异常后取 getFailureList() 导出,并补上传文件校验。
用 export(...) 而非 exportFailureReasonOnly(...):后者会把 markErrorColumns
置 false、只标红失败原因列,导致「按列标红」能力丢失(实测 5 个既有用例
标红列变空);export(...) 同时保留全量行与按列标红。
验证:mvn install -DskipTests 通过;违章记录回归 40/40
(既有 9 用例,标红列断言全绿:03 类型 / 04 事项 / 05 事项 / 06 类型 / 08 车牌号/船号);
用户实际上传文件实测:失败明细返回全量 5 行、仅错误行带原因、标红[车船类型,状态]、
库中零写入。其余 8 个导入模块回归 187 项全绿,无连带影响。
|
2026-09-26 10:30:39 +08:00 |
|
b2894lxlx
|
e09380e902
|
🐛 MK流程结束直接同步客商审核通过并完善异常日志
流程状态30时不再校验复评,避免卡在审核中;同步失败补充明确错误日志。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-25 17:45:14 +08:00 |
|
b2894lxlx
|
feb0ce8ee2
|
mk调试
|
2026-09-25 15:21:22 +08:00 |
|
weicw-dshsa
|
f2b6ca2834
|
🐛 行政区划导入补事务实现整批回滚,缓存清理移到成功路径
问题:RegionServiceImpl.importRegion 是唯一没有 @Transactional 的导入板块,
循环内吞掉异常后正常 return,已落库行直接提交。且 cacheChanged 标志导致
「先清缓存、再提交事务」:循环内已有行 submit 成功 → 后面行失败 → 方法
正常返回 → 惰性树缓存被清/重算成失败批次的中间状态,库回滚后缓存脏掉。
改动:
- 服务层:补 @Transactional(rollbackFor = Exception.class);错误收集完后
抛 ImportFailureException(org.springblade.common.excel)触发整批回滚。
- 缓存语义重构:submit 拆出 clearCache 参数(公开 submit(region) 行为不变,
手工新增仍即时清缓存);导入路径传 false 不逐行清缓存,
统一移到方法末尾成功路径上——异常抛出后不可达,失败批次缓存保持原状。
- 控制器:改 catch 异常导出(exportFailureReasonOnly)+ 补上传文件校验。
验证:mvn install -DskipTests 通过;行政区划回归 25/25
(tests/fixtures/region/),Redis 断言覆盖:成功路径缓存被清、
失败路径缓存键原样保留、基线行不被失败批次污染。
|
2026-09-25 08:20:06 +08:00 |
|
weicw-dshsa
|
c5e5433dc8
|
🐛 修复运单管理导入未整批回滚
问题:WaybillServiceImpl.importWaybill 虽有 @Transactional,但循环内吞掉
异常后正常 return,Spring 照常提交——失败行之前的正确行已落库。
本票回滚边界比其它板块宽:submit 虽只写运单主表,但 carrierJson 构造、
多个宽容日期解析都在循环内,失败原因需覆盖;关联表写入在 import-batch
链路,不在本票范围。
改动:
- 服务层:错误收集完后抛 ImportFailureException(org.springblade.common.excel)
触发整批回滚。catch 里原本就把原因写在原行对象上,无需额外处理。
- 控制器:改 catch 异常导出(exportFailureReasonOnly)+ 补上传文件校验。
验证:mvn install -DskipTests 通过;运单管理回归 19/19
(tests/fixtures/waybill-import/manage-excel/,含 02_一行错误多行正确_应零写入)。
既有 import-batch 链路的 *.json 与 excel/ 用例未改动。
|
2026-09-25 08:20:06 +08:00 |
|
weicw-dshsa
|
a8808942a3
|
🐛 修复评分量化表导入未整批回滚(按评定表分组粒度)
问题:CreditScoreQuantificationServiceImpl.importCreditScoreQuantification
循环单位是「评定表名称」分组(每分组一次 submit,写主表+项目+选项等多表),
异常被 catch 吞掉后正常 return,Spring 照常提交——失败分组之前的
正确分组已经落库。
改动:
- 服务层:任一分组失败、errorList 非空时抛 ImportFailureException
(org.springblade.common.excel)触发整批回滚,失败分组之前已 submit
的正确分组一起退掉。失败明细粒度保留既有行为:覆盖失败分组的全部行。
「评定表名称」为空的行不走 submit、保持早退标错路径。
- 控制器:改 catch 异常导出(exportFailureReasonOnly)+ 补上传文件校验。
验证:mvn install -DskipTests 通过;评分量化表回归 15/15
(tests/fixtures/credit-score/,含 02_后组失败前组正确_应零写入)。
另确认既有语义:与库中重名的分组走覆盖式更新(saveOrUpdate),非错误。
|
2026-09-25 08:20:06 +08:00 |
|
weicw-dshsa
|
82d823e68d
|
🐛 修复费用项导入未整批回滚,失败原因写回原行
问题:FeeItemServiceImpl.importFeeItem 虽有 @Transactional,但循环内吞掉
异常后正常 return,Spring 照常提交——失败行之前的正确行已落库。
改动:
- 服务层:错误收集完后抛 ImportFailureException(org.springblade.common.excel)
触发整批回滚;同时把失败原因写回原行对象 excel.setErrorMessage——
抛出异常时携带的是原表 data,导出工具从行对象的 errorMessage 字段取原因,
只写在 FeeItemImportFailureExcel 副本上会导致失败明细原因为空。
- 控制器:改 catch 异常导出(exportFailureReasonOnly)+ 补上传文件校验,
对齐 PortTerminalController 写法。
验证:mvn install -DskipTests 通过;费用项回归 20/20
(tests/fixtures/fee-item/,含 02_一行错误多行正确_应零写入)。
|
2026-09-25 08:20:06 +08:00 |
|