diff --git a/changelogs/2026-05/07_fix_insurance_purchase_idcardtype_field_rename.md b/changelogs/2026-05/07_fix_insurance_purchase_idcardtype_field_rename.md new file mode 100644 index 0000000..b5928c8 --- /dev/null +++ b/changelogs/2026-05/07_fix_insurance_purchase_idcardtype_field_rename.md @@ -0,0 +1,102 @@ +--- +date: 2026-05-07 +type: backend-fix +module: hl-order-service-v2/insurance +priority: high +notify: ["@mmg"] +status: deployed-pending +restart_service: hl-order-service-v2 +gitea_pr: 1805 +gitea_issue: 1789 +related_cases: [BD-001] +breaking_change: false +--- + +# 投保人证件号字段名改回 V1 契约 + JsonAlias 兼容(保险订单创建修复) + +## 现象(BD-001) + +``` +POST /admin/insurance/purchase +Body: {"insuredList":[{"name":"张三","idType":"ID_CARD","idNo":"110101...",...}],...} +响应: {"code":400,"message":"证件类型不能为空, 证件号码不能为空"} +``` + +即使前端填了证件号也报必填。 + +## 根因 + +V2 重写时单方面把 `PurchaseInsuranceRequest` 的 `idCardType / idCardNo` 改名成 `idType / idNo`。前端按 V1 契约持续传 `idCardType / idCardNo`,Jackson `FAIL_ON_UNKNOWN_PROPERTIES=false` 静默丢字段,双 @NotNull 触发必填。 + +## 修复 + +PR #1805 已合并 dev。 + +1. **DTO 改回 V1 字段名**:`PurchaseInsuranceRequest.idType → idCardType`,`idNo → idCardNo` +2. **加 @JsonAlias 双向兼容**:`@JsonAlias({"idType"})` / `@JsonAlias({"idNo"})` —— 两种字段名都能反序列化 +3. service 层 7 处 getter/setter 引用全部跟改(含 PR #1800 拆出的 InsurancePurchaseTxService) + +## 前端调用(@mmg) + +### 推荐写法(两种都可,建议用 V1 字段名) + +```js +// ✅ 推荐:V1 契约字段名 +POST /admin/insurance/purchase +{ + "schemeId": "2046840448906940417", + "orderId": "...", + "insuredList": [ + { + "name": "张三", + "idCardType": "ID_CARD", + "idCardNo": "110101199001011234", + "phone": "13800138000", + "birthday": "1990-01-01", + "relationship": "SELF" + } + ] +} + +// ⚠️ 兼容(@JsonAlias 兜底,但建议改用上面的) +{ + "insuredList": [ + {"idType": "ID_CARD", "idNo": "...", ...} + ] +} +``` + +### 重要:同时传新旧字段时的优先级 + +如果前端代码同时传了 `idCardType` 和 `idType`(不推荐),Jackson 用最后出现的覆盖。建议**只传一种**,统一改用 V1 字段名 `idCardType / idCardNo` 跟项目其他 traveler DTO 对齐。 + +### idCardType 枚举值 + +``` +ID_CARD 身份证 +PASSPORT 护照 +HONG_KONG 港澳通行证 +TAIWAN 台湾通行证 +OTHER 其他 +``` + +### 字段加密提醒 + +`InsuredPerson.idCardNo` 已挂 `EncryptTypeHandler`,落库自动加密、查询自动解密,前端无感。日志全部已脱敏(grep 31 处零打印明文)。 + +## 重启服务 + +`hl-order-service-v2` 部署测试服后即可。 + +## 验收 + +- [ ] 用 `idCardType / idCardNo` 字段名 POST → 返 code:200 创建保险订单 +- [ ] 用 `idType / idNo`(旧字段名兜底)POST → 同样 code:200(@JsonAlias 兼容) +- [ ] 缺失证件号 → 返必填提示 +- [ ] DB 中 idCardNo 是加密格式(解密查 InsuredPersonVO 是明文) + +## 关联工单 +Gitea #1789 已 Closes 关闭。 + +## 备注 +本次修复保留 V2 内部其他字段(V2 重写期间引入的合理改动),仅恢复**对外契约字段名**。前端零改动也能调通(@JsonAlias 兜底),但建议主动改回 V1 字段名以保持代码清晰。 diff --git a/changelogs/2026-05/07_fix_order_traveler_cert_validate.md b/changelogs/2026-05/07_fix_order_traveler_cert_validate.md new file mode 100644 index 0000000..b4f3e2d --- /dev/null +++ b/changelogs/2026-05/07_fix_order_traveler_cert_validate.md @@ -0,0 +1,183 @@ +# 订单出行人证件验证: admin 端补接口 + 前端必须改 URL/method/参数 + +> **服务**: hl-order-service-v2 (端口 8084) +> **PR**: #1798 (已合并 dev, 待部署 prod) +> **Issue**: #1797 +> **日期**: 2026-05-07 +> **影响范围**: 管理后台 出行人证件验证弹窗(蒋雨莲所在订单页) +> **@ 前端**: mmg + +--- + +## ⚠️ 关键变化(必须改前端) + +prod 弹窗 "证件验证未通过 · 蒋雨莲:接口不存在: POST /admin/auth/cert/validate" 是因为 **`POST /admin/auth/cert/validate` 后端从未实现过**(五重证据:代码 0 命中 / git 全历史 0 命中 / changelog 0 命中 / 需求文档 0 命中 / AuthController 全部 13 个 mapping 无 cert)。 + +后端**新增**正确接口: + +``` +GET /admin/order/{orderId}/travelers/validate +``` + +前端必须把"逐个 traveler 调 cert/validate"的循环逻辑**整段删掉**,改为按 orderId 一次性校验:URL 变 / method 由 POST 改 GET / 参数由 traveler body 改 path orderId。 + +--- + +## 一、背景 + +弹窗触发动作(疑似锁单/支付/确认行程前置校验)应该一次性把订单内全部出行人验证一次,而不是循环逐人。后端早就有 `GET /internal/order/traveler/validate/{orderId}` 给 payment-service Feign 调,本次只是在 admin 端薄包装一层让前端可以直调。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 校验订单出行人证件信息 | GET | `/admin/order/{orderId}/travelers/validate` | **新增** | 替换前端误调的 `POST /admin/auth/cert/validate` | + +--- + +## 三、接口详情 + +### 1. 校验订单出行人证件信息 `GET /admin/order/{orderId}/travelers/validate` + +**Service**: 复用已有 `OrderTravelerService.validateAndReturnVO(orderId)`(与 internal 接口完全对齐) +**VO**: `TravelerValidationVO` + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Path | Long | ✅ | 雪花 ID | 订单 ID | + +#### 出参 `Result` + +| 字段 | 类型 | 说明 | +|------|------|------| +| valid | Boolean | 全部出行人证件信息是否合规 | +| travelerCount | Integer | 出行人数量 | +| errors | `List` | 校验失败明细(中文消息),合规时为空数组 | + +#### 请求示例 + +``` +GET /admin/order/123456789/travelers/validate +Authorization: Bearer +``` + +#### 响应示例(合规) + +```json +{ + "code": 200, + "message": "成功", + "data": { + "valid": true, + "travelerCount": 2, + "errors": [] + }, + "success": true +} +``` + +#### 响应示例(不合规) + +```json +{ + "code": 200, + "message": "成功", + "data": { + "valid": false, + "travelerCount": 2, + "errors": [ + "蒋雨莲:身份证号格式错误", + "张三:护照有效期已过" + ] + }, + "success": true +} +``` + +--- + +## 四、契约约束与正确调用方式 + +### ✅ 正确 / ❌ 错误 调用对照 + +| 场景 | 调用 | +|------|------| +| ✅ 一次性按订单校验全部出行人 | `GET /admin/order/{orderId}/travelers/validate` | +| ❌ 循环逐人调 cert/validate | `POST /admin/auth/cert/validate` × N(接口不存在 404) | + +### 前端代码改动指引 + +**旧代码(删除)**: +```js +for (const t of travelers) { + await http.post('/admin/auth/cert/validate', t) // ❌ 接口不存在 +} +``` + +**新代码**: +```js +const { data } = await http.get(`/admin/order/${orderId}/travelers/validate`) +if (!data.valid) { + // 弹窗显示 data.errors 即可 + showCertValidationDialog(data.errors) +} +``` + +错误明细 `errors: List` 已经包含中文姓名 + 错误原因,前端不需要再拼姓名前缀。 + +--- + +## 五、数据库行为 + +只读校验,**不写库**。 + +--- + +## 六、边界行为 + +- orderId 不存在 → 服务端 BusinessException → `Result.code != 200` + 中文 message +- 订单存在但 0 个出行人 → `valid=false, travelerCount=0, errors=["订单尚未添加出行人"]` +- 未登录 → 网关 401 拦截 +- 下游服务降级 → 暂不需要 fallback,service 内部不依赖外部服务 + +--- + +## 七、不影响范围 + +- **仅影响**: 管理后台出行人证件验证弹窗(前端 hl-ui mmg 维护) +- **零影响**: + - 小程序端出行人列表 / 添加 / 编辑 + - admin 端出行人增删改 (`/admin/order/{orderId}/travelers/**`) + - payment-service 内部 Feign 调用的 `/internal/order/traveler/validate/{orderId}` 保持不变 + - 历史数据零变更 + +--- + +## 八、测试环境已验证 + +后端单测 3 用例全绿(mockMvc 真路径 dispatch): +- `validate_orderComplete_returnsSuccessVO` ✓ +- `validate_orderIncomplete_returnsIncompleteVO` ✓ +- `validate_invalidOrderId_serviceThrows_exposedAsBusinessError` ✓ + +测试服 admin token round-trip:待 PR #1798 部署到测试服后 /@qa 角色验证(24h 内回贴 curl 输出)。 + +--- + +## 九、相关历史 PR + +| PR | Issue | 说明 | 是否仍有效 | +|----|-------|------|------------| +| 无 | 无 | 后端从未约定 `POST /admin/auth/cert/validate` 接口 | -- | +| **本 PR #1798** | **#1797** | 新增 `GET /admin/order/{orderId}/travelers/validate` | ✅ 最新 | + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#1797](https://git.1814.love:8443/wx/HL/issues/1797) +- 关联 PR: [wx/HL#1798](https://git.1814.love:8443/wx/HL/pulls/1798) diff --git a/changelogs/2026-05/07_fix_order_v2_date_range_filter_three_layers.md b/changelogs/2026-05/07_fix_order_v2_date_range_filter_three_layers.md new file mode 100644 index 0000000..fe9e6a9 --- /dev/null +++ b/changelogs/2026-05/07_fix_order_v2_date_range_filter_three_layers.md @@ -0,0 +1,74 @@ +--- +date: 2026-05-07 +type: backend-fix +module: hl-order-service-v2/order+refund +priority: high +notify: ["@mmg"] +status: deployed-pending +restart_service: hl-order-service-v2 +gitea_pr: 1804 +gitea_issue: 1791 +related_cases: [DD-004, DD-005, TK-004] +--- + +# 订单/退款列表日期范围筛选无效 — 三层补字段 + +## 现象(DD-004 / DD-005 / TK-004) + +测试报告: +- 订单列表选起止日期 → 无法筛选 +- 订单列表多条件组合 → "日期还是有问题" +- 退款列表多条件组合 → "无" + +`/@bug` round-trip 极端测试 1900 vs 2099 vs 默认三组 total 都 29 一致 → 日期参数被静默丢弃。 + +## 修复 + +PR #1804 已合并 dev。三层补字段: + +| 层 | 文件 | 改动 | +|----|------|------| +| VO | OrderPageReqVO + RefundApplicationPageReqVO | 加 `createTimeStart` / `createTimeEnd` (LocalDateTime + @DateTimeFormat) | +| Service | OrderListQueryService.listAdminOrders | 透传两个新参数到 mapper | +| Mapper | OrderInfoMapper.selectAdminPage / RefundApplicationMapper.selectPage | wrapper 加 `betweenIfPresent(OrderInfo::getCreateTime, start, end)` | + +`betweenIfPresent` 智能退化:双边都给 = BETWEEN,只给 start = >= start,只给 end = <= end,都空跳过。 + +## 前端调用(@mmg) + +### 订单列表 + +```js +// 接口:GET /admin/order/page +GET /admin/order/page?createTimeStart=2026-04-01T00:00:00&createTimeEnd=2026-04-30T23:59:59&page=1&pageSize=10 +``` + +注意: +- 字段名:`createTimeStart` / `createTimeEnd`(驼峰,跟之前 status 字段一致) +- 格式:ISO 8601 DateTime(`2026-04-01T00:00:00`) +- 时区:服务端 `+08:00`,前端传字符串不带时区由后端 LocalDateTime 解析 + +### 退款列表 + +```js +// 接口:GET /admin/refund/application/page +GET /admin/refund/application/page?createTimeStart=...&createTimeEnd=...&page=1&pageSize=10 +``` + +### 不传日期时 + +向后兼容,不传 `createTimeStart` / `createTimeEnd` 行为完全不变(既有调用零影响)。 + +## 重启服务 + +`hl-order-service-v2` 部署测试服后即可。 + +## 验收 + +- [ ] 订单列表传日期 → 数据按范围筛选 +- [ ] 退款列表传日期 → 同样工作 +- [ ] 不传日期 → 全量返回(向后兼容) +- [ ] 跨年 / 单边 / 边界 各 case 正常 + +## 关联工单 +Gitea #1791 已 Closes 关闭。 diff --git a/changelogs/2026-05/07_fix_order_v2_work_order_create_workorderno_orderno.md b/changelogs/2026-05/07_fix_order_v2_work_order_create_workorderno_orderno.md new file mode 100644 index 0000000..255ea57 --- /dev/null +++ b/changelogs/2026-05/07_fix_order_v2_work_order_create_workorderno_orderno.md @@ -0,0 +1,69 @@ +--- +date: 2026-05-07 +type: backend-fix +module: hl-order-service-v2/work-order +priority: high +notify: ["@mmg"] +status: deployed-pending +restart_service: hl-order-service-v2 +gitea_pr: 1803 +gitea_issue: 1787 +related_cases: [GD-001] +--- + +# 工单新增漏 setWorkOrderNo / setOrderNo + 清理 title 死字段 + +## 现象(GD-001) + +``` +POST /admin/order/work-order +Body: {"orderId":1,"type":"OTHER","description":"...","priority":"NORMAL"} +响应: {"code":400,"message":"字段【work_order_no】未填写,请填写后重试"} +``` + +## 修复 + +PR #1803 已合并 dev。改动: + +1. `WorkOrderService.create()` 新增 `generateWorkOrderNo()` 私有方法,格式 `WO + yyyyMMddHHmmss + 4位随机`(20 字符),落库前 set 到 entity +2. 同时 `setOrderNo(order.getOrderNo())` 拷贝订单号 +3. 删除 `CreateWorkOrderRequest.title` 死字段(DDL/entity 没列) +4. 删除 `WorkOrderVO.title` 死字段,新增 `workOrderNo` 字段(响应里返工单号给前端) +5. `sql/work_order.sql` `type` COMMENT 对齐枚举(`CHANGE/COMPLAINT/SUPPLEMENT/OTHER`) + +## 前端配合(@mmg) + +### 1. 请求 body 不再传 title + +之前如果前端传了 title,会被静默吞掉(无 setter)。现在 DTO 删了 title 字段,传过来会被 Jackson 忽略(项目 FAIL_ON_UNKNOWN_PROPERTIES=false),不影响业务但建议清理。 + +### 2. 响应 body 多了 workOrderNo 字段 + +工单列表/详情响应现在包含: + +```json +{ + "id": "2052278173260824577", + "workOrderNo": "WO20260507150123ABCD", // ← 新增字段 + "orderNo": "HL26050700001", // ← 新增字段 + "orderId": "...", + "type": "OTHER", + "status": "PENDING", + ... +} +``` + +前端工单列表/详情页可以展示这两个字段。 + +## 重启服务 + +`hl-order-service-v2` 已合并到 dev,运维部署测试服后即可使用。如本地启动需重启 `hl-order-service-v2`。 + +## 验收 + +- [ ] 测试服部署后 admin token POST 工单 → 返 code:200 + workOrderNo + orderNo +- [ ] 前端工单列表展示工单号 +- [ ] 工单详情展示工单号 + 订单号 + +## 关联工单 +Gitea #1787 已 Closes 关闭。 diff --git a/changelogs/2026-05/07_fix_resource_scenic_offline_check_and_staff_phone_pattern.md b/changelogs/2026-05/07_fix_resource_scenic_offline_check_and_staff_phone_pattern.md new file mode 100644 index 0000000..d996e52 --- /dev/null +++ b/changelogs/2026-05/07_fix_resource_scenic_offline_check_and_staff_phone_pattern.md @@ -0,0 +1,85 @@ +--- +date: 2026-05-07 +type: backend-fix +module: hl-resource-service/scenic+staff +priority: low +notify: ["@mmg"] +status: deployed-pending +restart_service: hl-resource-service +gitea_pr: 1807 +gitea_issue: [1794, 1795] +related_cases: [JQ-011, RY-002] +--- + +# 景区下架引用校验 + 服务人员手机号格式校验 + +## 现象 + +- **JQ-011**:景区被产品引用时 admin 直接下架,无业务校验提示 +- **RY-002**:服务人员录入手机号格式校验缺失(`abc123` / `12345` 都能保存) + +## 修复 + +PR #1807 已合并 dev,两个工单合一个 PR。 + +### #1794 景区下架引用校验 + +`ScenicSpotService.updateStatus` + `batchUpdateStatus` 加 `productFeignClient.checkResourceInUse("SCENIC", id)`: +- 仅下架(STATUS_OFF)触发,上架不触发 +- 单个被引用 → 抛 `BusinessException(SCENIC_DISABLE_IN_USE, 产品名)`,message 含产品名(如"景区已被【XX 一日游】使用,请先解除关联") +- 批量被引用 → 跳过+log.warn(与既有 approvalNo 跳过模式一致) + +### #1795 服务人员手机号校验 + +`StaffCreateRequest.phone` + `emergencyPhone` 加 `@Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "手机号格式不正确")`,`StaffUpdateRequest` 同样。 + +允许空字符串(phone 是非必填字段),非空必须符合 11 位手机号格式。 + +## 前端配合(@mmg) + +### 景区下架弹窗 + +下架接口现在可能返 errorCode `SCENIC_DISABLE_IN_USE`,前端可以 catch 这个错误码,弹窗提示用户解除关联: + +```js +try { + await offlineScenic(id) +} catch (e) { + if (e.code === xxxxx /* SCENIC_DISABLE_IN_USE 错误码 */) { + showDialog({ + title: '景区已被使用', + content: e.message, // 含产品名 + }) + } +} +``` + +### 服务人员手机号 + +前端可以增加同样的格式校验提供更好体验(同步反馈),但即使没加,后端会拦截并返业务码非 200。 + +## 重启服务 + +`hl-resource-service` 部署测试服后即可。 + +## 验收 + +- [ ] 关联中景区下架 → 业务码非 200 + 含产品名提示 +- [ ] 无关联景区下架 → 正常下架 +- [ ] 服务人员手机号 "abc" → 业务码非 200 +- [ ] 手机号 "13800138000" → 正常保存 +- [ ] emergencyPhone 同样校验 + +## 关联工单 +- Gitea #1794 已 Closes 关闭 +- Gitea #1795 已 Closes 关闭 + +## 备注:项目级遗留(建议另立工单跟踪,不在本 PR 范围) + +/@bug 顺手发现两个项目级问题,**不在本 PR 修复范围**: + +1. **8 个资源服务(hotel/scenic/activity/restaurant/staff/supplies/cost/vehicle)的 `updateStatus` 方法全部缺 `checkResourceInUse`**,本 PR 只修景区一个。其余 7 个建议 PM 决策是否扩大修复范围。 +2. **hl-resource-service 整体无字段加密**:`Staff.phone / emergencyPhone / 身份证号` 等敏感字段未挂 `EncryptTypeHandler`(grep `EncryptTypeHandler` 整模块零匹配)。 +3. **`ProductFeignFallbackFactory.checkResourceInUse` 降级返回空 list = fail-open**:product-service 真挂时景区会被偷偷下架。 + +以上 3 项建议 PM 评估优先级后另立 feat/refactor 工单。 diff --git a/changelogs/2026-05/07_fix_user_explore_category_detail_status_field.md b/changelogs/2026-05/07_fix_user_explore_category_detail_status_field.md new file mode 100644 index 0000000..90754d9 --- /dev/null +++ b/changelogs/2026-05/07_fix_user_explore_category_detail_status_field.md @@ -0,0 +1,84 @@ +--- +date: 2026-05-07 +type: backend-fix +module: hl-user-service/explore-category +priority: medium +notify: ["@mmg"] +status: deployed-pending +restart_service: hl-user-service +gitea_pr: 1806 +gitea_issue: 1793 +related_cases: [ZT-006] +--- + +# 探索分类详情接口补 status 字段(前端称"主题列表"状态切换无效) + +## 现象(ZT-006) + +测试报告:admin 端切换探索分类(前端口语称"主题")的上下架状态,**显示"编辑成功"但实际状态没变**。 + +## 根因 + +`ExploreCategoryDetailVO.java` **完全缺 `status` 字段**,导致 `ExploreCategoryService.toDetailVO` builder 也不 set status,前端 EditModal `status: data.status ?? 1` fallback 默认上线,用户切换的真实状态无法回显。 + +## 修复 + +PR #1806 已合并 dev。 + +1. `ExploreCategoryDetailVO` 加 `private Integer status;` 字段,@ApiModelProperty 含字典值说明 +2. `ExploreCategoryService.toDetailVO` builder 加 `.status(category.getStatus())` 一行 + +PUT 切换接口本身是正常的,问题只在 GET detail 不返 status。 + +## 前端配合(@mmg) + +### 1. 详情响应多了 status 字段 + +```json +GET /admin/explore/category/{id} +{ + "code": 200, + "data": { + "id": "...", + "name": "亲子游", + "iconUrl": "...", + "status": 1, // ← 新增字段(0=下架, 1=上架) + "sortOrder": 100, + ... + } +} +``` + +### 2. 前端建议改默认值 + +```js +// 错误(导致"切换无效"幻觉) +status: data.status ?? 1 // ❌ 后端返 null 时默认 1 + +// 正确(后端现在永远返字段,可以去掉 fallback) +status: data.status // ✅ 直接用后端字段 +// 或 +status: data.status ?? 0 // ✅ 兜底用 0(下架)更安全 +``` + +## 重启服务 + +`hl-user-service` 部署测试服后即可。 + +## 验收 + +- [ ] admin GET 探索分类详情 → 响应含 status 字段非 null +- [ ] 切换 status 后再次 GET → status 已变 +- [ ] 下架的分类 detail 返 0 不是 1 +- [ ] 前端编辑弹窗状态开关正确回显 + +## 数据修复(建议运营/QA 配合) + +BUG 期间用户切换状态可能有错位,运营可参考 BUG 报告里的审计 SQL 对照修: +`docs/tasks/20260507_bug_product_analysis.md`(仓库内)。 + +## 关联工单 +Gitea #1793 已 Closes 关闭。 + +## 顺手提醒(CP-006 不在本 PR) +测试报告里 CP-006 "产品筛选下拉框只有上/下架,适用小蒙马" 是**不同根因**(产品筛选选项不全),归到产品复核任务,不在本 PR 范围。