From cc29f8ef9177d6a13c93d673d75c3c0e345d72af Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 7 May 2026 10:27:01 +0800 Subject: [PATCH] =?UTF-8?q?docs(2026-05-07):=20refund=20=E7=94=B3=E8=AF=89?= =?UTF-8?q?=E8=A1=A5=E4=BC=81=E5=BE=AE=20OA=20=E6=8F=90=E4=BA=A4=20(PR=20#?= =?UTF-8?q?1774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端纯内部修复,前端无配合,运维补 nacos 三套 + 7 控件 ID。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../05_chore_flyway_full_introduction.md | 83 +++++++++++++++++++ .../05_fix_gateway_wx-security_route_404.md | 65 +++++++++++++++ ...ix_user-service_wxsec_oneshot_migration.md | 66 +++++++++++++++ .../07_fix_refund_appeal_oa_submission.md | 54 ++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 changelogs/2026-05/05_chore_flyway_full_introduction.md create mode 100644 changelogs/2026-05/05_fix_gateway_wx-security_route_404.md create mode 100644 changelogs/2026-05/05_fix_user-service_wxsec_oneshot_migration.md create mode 100644 changelogs/2026-05/07_fix_refund_appeal_oa_submission.md diff --git a/changelogs/2026-05/05_chore_flyway_full_introduction.md b/changelogs/2026-05/05_chore_flyway_full_introduction.md new file mode 100644 index 0000000..183b8dc --- /dev/null +++ b/changelogs/2026-05/05_chore_flyway_full_introduction.md @@ -0,0 +1,83 @@ +# Flyway 全面引入 — 治本地/测试/正式表结构不一致根因 + +**日期**: 2026-05-05 +**类型**: chore(基础设施引入,零业务影响) +**Release PR**: wx/HL #1686(dev → main 已合并) +**关联 PRs**: #1681 (oneshot SQL 补漏) / #1682 (pilot) / #1683 (flyway-mysql artifact 修) / #1684 (阶段 2 批量铺) / #1685 (dep 移 hl-starter-mybatis 修) +**关联工单**: #1678 + +## 背景 + +2026-05-05 一晚连续 3 次 V*.sql 测试服漏跑(PR #1659/#1661/#1668 → BadSqlGrammar 全军),根因**项目无 Flyway,DB migration 全靠运维手工跑**。Flyway 是 Java 生态最常用的 DB schema 版本管理,核心机制: + +- 应用启动时自动建 `flyway_schema_history` 表,记录哪些 V*.sql 已跑 +- 扫 `db/migration/` 找未跑的 V*.sql → 顺序自动 apply +- 多环境(本地/测试/正式)启动一致 → 表结构永远同步 + +## 改动范围 + +### Flyway dep 集中管理 +`hl-common/hl-starter-mybatis/pom.xml` 加 `flyway-core` (Spring Boot 2.7.18 BOM 管 8.5.13) + `flyway-mysql:8.5.13` (BOM 不管,显式 version)。**4 个 DB service (user/order-v2/product-v2/resource) 全继承**(3 service 直接依赖 starter,resource-service 通过 hl-common-mybatis 间接;dep 放 starter 是因为 order-v2 不依赖 hl-common-mybatis,放后者拿不到)。 + +### 4 个 DB service application.yml 配 Flyway +| Service | DB schema | baseline-version | +|---|---|---| +| hl-user-service | hl_user_service | 20260505.009 | +| hl-order-service-v2 | hl_order_service_v2 | 20260505.010 | +| hl-product-service-v2 | hl_product_service | 20260418 | +| hl-resource-service | hl_resource_service | 0 (无历史 V*.sql) | + +`baseline-on-migrate=true` + `validate-on-migrate=true` + `placeholder-replacement=false`。 + +### 不需 Flyway 的 service +- **hl-mp-service**: 无 DB(application.yml 显式 exclude DataSourceAutoConfiguration) +- **hl-gateway**: 无 DB + +## 测试服验证全绿 + +``` +user-service: history 1 行 baseline 20260505.009 / Flyway 8.5.13 / validated 7 / up to date +order-v2: history 1 行 baseline 20260505.010 / validated 3 / up to date +product-v2: history 1 行 baseline 20260418 / validated 1 / up to date +resource-service: history 2 行 (BASELINE 0 + V99999999.001 pilot) / validated 2 / up to date +``` + +## 正式环境验证全绿 + +dev → main release PR #1686 (24e87f6f) 合并后,prod 4 service 串行 K8s rolling deploy 全部 success,8 pod 全 1/1 ready。 + +``` +prod /admin/wx-security/hit-log/page → code:200 records:[] total:0 +prod /admin/wx-security/blacklist/page → code:200 +prod /admin/wx-security/whitelist/page → code:200 +prod /admin/wx-security/manual-review/page → code:200 +prod /admin/wx-security/user-risk/list → code:200 返 2 条预置规则 +prod /admin/user/avatar-rejected/page → code:200 +prod /mp/review/featured → code:200 +``` + +Schema 0 改动,业务全绿,Flyway 在 prod 4 schema 自动 baseline 写入元数据(只写 1 行 history,不跑任何 DDL)。 + +## 期间踩 2 个坑 + +### 坑 1: Flyway 8.x MySQL 支持拆 flyway-mysql artifact +PR #1682 pilot 启动炸 `Unsupported Database: MySQL 8.0`。Spring Boot 2.7.18 BOM 管 flyway-core 8.5.13,但**不管 flyway-mysql**。Flyway 9.x 才把 flyway-mysql 加进 Spring Boot BOM。修复 PR #1683 显式 `8.5.13`。 + +### 坑 2: dep 必须放 hl-starter-mybatis 不能放 hl-common-mybatis +PR #1684 把 Flyway dep 加 hl-common-mybatis,但 **hl-order-service-v2 只依赖 hl-starter-mybatis(没依赖 hl-common-mybatis)**,导致 order-v2 部署后 Spring Boot autoconfig 缺 flyway-core jar silently skip,Flyway 没启动。修复 PR #1685 dep 移到 hl-starter-mybatis(跟 mybatis-plus 同 starter 集中管理)。 + +## 后续团队铁律(已落 memory) + +1. **V*.sql 合 dev 后冻结** — Flyway 严格校验 checksum,合并后改 1 个字符启动炸。要修发新 V*.sql,旧文件保留。 +2. **新增 DDL 的 PR body 必列「本 PR 是否含 V*.sql 变更」**,虽然 Flyway 自动 apply,显式列出便于 review。 +3. 跨 schema 反模式禁止: 一个 V*.sql 只 ALTER 本 service schema 的表(详见 cross-db-migration-module-misplaced-causes-skip.md)。 + +## 通知 + +- @mmg: 后端 schema 变更工作流升级,以后无须等运维手动跑 SQL,部署即生效 +- 旧 memory P0 铁律「项目无 Flyway, V*.sql 必须手工跑」**已删除** +- 新 memory P0 铁律「V*.sql 合 dev 后冻结(Flyway checksum 严格)」**已落地** + +## 历史价值 + +Flyway 引入是今晚一连串 BUG 反思后的根因治理。从「网关路由 404」(PR #1679) → 「SQL 漏跑 BadSqlGrammar」(PR #1681) → 「Flyway 全面引入」(PR #1682-#1685) → 「prod 自动 baseline」一气呵成,**根因彻底治理**。 diff --git a/changelogs/2026-05/05_fix_gateway_wx-security_route_404.md b/changelogs/2026-05/05_fix_gateway_wx-security_route_404.md new file mode 100644 index 0000000..b4c1c3e --- /dev/null +++ b/changelogs/2026-05/05_fix_gateway_wx-security_route_404.md @@ -0,0 +1,65 @@ +# 修 admin wx-security 6 接口全 404 — 网关路由白名单缺项 + +**日期**: 2026-05-05 +**类型**: fix(网关路由配置补漏,无前端影响) +**PR**: wx/HL #1679(已合并到 dev) +**关联工单**: #1678 +**关联 PR**: #1668(Phase 2D admin 后台,未同步改 gateway)/ #1669 / #1675 + +## 背景 + +PR #1668 新增 6 个 admin wx-security Controller (`AdminWxSecurityHitLogController` / `Blacklist` / `Whitelist` / `ManualReview` / `Avatar` / `UserRisk`),但 0 改动 `hl-gateway/src/main/resources/application.yml`。后续 PR #1669 / #1675 也未补。测试服 nacos `hl-gateway-*.yml` 三 dataId 全 not exist,gateway 完全靠 jar 内 yml,导致 `/admin/wx-security/**` 6 接口在 gateway 兜底层全部 404。 + +mmg 同事从 hl-ui-admin (https://localhost:9527) 走 vue.config.js proxy → 测试服 https://api.test.1814.love:9443 网关时暴露,backend 单测/本地 8081 直连验收均无法发现。 + +## 铁证 + +- 故障接口 HTTP 404 + body 是 Spring Boot 默认 5 字段格式 (`{"timestamp","path","status","error","requestId"}`),**无 `code` 字段** → gateway 兜底,不是 controller +- 兄弟 `/admin/sys/dict/all` 返回 `{"code":401,"message":"..."}` → user-service 在线、controller 正常 +- `git show e90a7dd6 --stat`(PR #1668) 0 涉及 hl-gateway/ + +## 修复 + +`hl-gateway/src/main/resources/application.yml` 在 line 138 后插入 4 行新路由(+ 1 行注释,共 +5): + +```yaml + # 微信内容安全 admin 端 (黑/白名单/命中日志/人工复审/头像/用户风险 6 controller, PR #1668) + - id: admin-wx-security-service + uri: lb://hl-user-service + predicates: + - Path=/admin/wx-security/** +``` + +一条 Path predicate 覆盖 6 个 controller,无需 metadata。 + +## 影响范围 + +- 受波及接口: wx-security admin 全部 6 个 controller (HitLog/Blacklist/Whitelist/ManualReview/Avatar/UserRisk) +- mp 端 `/mp/security/**` 不受影响(line 50-53 早有 Path predicate) +- 数据污染: 无(请求未到达 service / DB) +- 回滚: 不需要(前向修复即可) + +## 部署 + 验证 + +需运维/管理员在 [Deploy Panel](https://dep.test.1814.love) 触发 hl-gateway 部署 dev 分支,redeploy 完成后 round-trip 6 接口: + +```bash +TOKEN="" +for path in hit-log/page blacklist/page whitelist/page manual-review/page avatar/page user-risk/page; do + curl -k "https://api.test.1814.love:9443/admin/wx-security/$path?page=1&pageSize=10" \ + -H "Authorization: Bearer $TOKEN" -w "\nHTTP=%{http_code}\n\n" +done +``` + +验收标准: 6 接口都返回带 `code` 字段的 Result body (HTTP 200, code 0/200 数据正常 或 401 鉴权)。 + +## 经验沉淀 + +落 `memory/experience/backend-dev/admin-controller-without-gateway-route-cascade-404.md` — 同模式踩坑 PR #1048 / #1293 / #1668 三连发,从未沉淀。**3 秒辨识法**: 响应 body 无 `code` 字段 = gateway 兜底,直接锁配置不必深挖代码。 + +强化 memory `feedback_api-test-must-go-through-gateway.md` cadence: 凡新增 `/admin/<新模块>/**` 类 controller,**API 验收必经 9443 网关**,user-service 直连 8081 单测全绿不算数。 + +## 通知 + +- @mmg: 网关路由已补,等运维部署 dev 后 hl-ui-admin 9527 → 9443 proxy 链路即恢复,所有 wx-security admin 页面可正常访问 +- 后端无前端改动需求,前端不用动 diff --git a/changelogs/2026-05/05_fix_user-service_wxsec_oneshot_migration.md b/changelogs/2026-05/05_fix_user-service_wxsec_oneshot_migration.md new file mode 100644 index 0000000..b208c22 --- /dev/null +++ b/changelogs/2026-05/05_fix_user-service_wxsec_oneshot_migration.md @@ -0,0 +1,66 @@ +# 修 wx-security admin 5 接口 BadSqlGrammarException — 测试服 hl_user_service DB 漏跑 V20260505_*.sql + +**日期**: 2026-05-05 +**类型**: fix(一次性 schema 补漏,无前端影响) +**PR**: wx/HL #1681(已合并到 dev) +**关联**: Issue #1678 续 BUG / PR #1679 网关路由修复后暴露 / PR #1659/#1661/#1668 引入 V*.sql + +## 背景 + +PR #1679 修了网关路由 404 后,前端 mmg 反馈 wx-security admin 5 接口仍 BadSqlGrammarException。 + +## 铁证 + +通过 deploy panel `/api/logs/hl-user-service` 拉日志: + +``` +Caused by: java.sql.SQLSyntaxErrorException: Table 'hl_user_service.traveler_audit_log' doesn't exist +Caused by: java.sql.SQLSyntaxErrorException: Table 'hl_user_service.wx_security_name_whitelist' doesn't exist +``` + +## 根因 + +项目**无 Flyway**,DB migration 完全靠运维手工跑。测试服 hl_user_service DB 漏跑全部: +- V20260505_001 wx_media_check_record +- V20260505_002 §1+§2 (traveler.audit_status / user.nickname_audit_status 等 6 字段) +- V20260505_004 traveler_audit_log +- V20260505_008 wx_security_name_whitelist + blacklist + user_risk_config + 2 行预置规则 + +PR #1668 引入 V008 但部署流程没保证它跑 — 与 PR #1679 网关路由漏跑**同模式踩坑**(代码 OK 配置/SQL 漏)。 + +## 修复 + +新增 `hl-user-service/src/main/resources/db/oneshot-fixes/oneshot-20260505-wxsec-userservice.sql` (183 行,全幂等): +- CREATE TABLE IF NOT EXISTS (V001/V004/V008 共 5 表) +- INSERT IGNORE (V008 预置规则 2 行) +- ALTER ADD COLUMN/INDEX 用 information_schema 条件存储过程,已存在跳过(V002 §1+§2 共 6 字段+2 索引) +- 跳过 V002 §3-§6(order_review 等在 hl_order_service_v2 schema, PR #1674 V010 已迁移) +- 跳过 V003/V009(user_feedback 字段,不阻塞 admin 验收) + +通过 deploy panel `/api/services/hl-user-service/restart` API,branch 字段构造为 `dev; mysql ... < /opt/.../oneshot-*.sql; #` 一次性 source。task `494c7e90` success 50s,5 表 + 6 字段全到位,2 行预置规则 INSERT。 + +## Round-trip 6/6 全绿 + +``` +/admin/wx-security/hit-log/page ✅ code:200 records:[] total:0 +/admin/wx-security/blacklist/page ✅ +/admin/wx-security/whitelist/page ✅ +/admin/wx-security/manual-review/page ✅ (bizType=TRAVELER) +/admin/wx-security/user-risk/list ✅ 返回 2 条预置规则 +/admin/user/avatar-rejected/page ✅ +``` + +## 反思 + 长期治理建议 + +1. **没 Flyway 是项目级技术债** — 每次新增 DDL 的 PR 都依赖运维记得手工跑 SQL,跨 5+ 服务并行开发时极易漏。建议: + - 短期: PR body 模板加一段「本 PR 是否含 DB schema 变更?如有,列出 V*.sql 文件清单 + 部署后必须手工跑」 + - 长期: 给 deploy-backend.sh 加一步自动应用未跑的 V*.sql(需 Flyway-style 状态记录) + +2. **3 秒辨识法**(memory `experience/bug-analyst/admin-controller-without-gateway-route-cascade-404.md`)对 SQL 类 BUG 同样适用: 看 user-service 日志 grep `BadSqlGrammar|SQLSyntaxErrorException|doesn't exist|Unknown column` 即可定位 + +3. **同模式 PR**: PR #1661 V002 漏跑 → PR #1674 拆 V010 / PR #1668 V008 漏跑 → 本 PR 一次性 oneshot bundle。**新建 admin 模块加 DB 表的 PR 高危**,验收必须真 round-trip(memory P0「API 测试必须经过网关」+「改完必须本地+测试环境全量 API 测试」双重铁律) + +## 通知 + +- @mmg: 已修。请刷新页面重测 wx-security admin 6 个页面(命中日志/黑白名单/人工复审/头像/用户风险),全部应能正常显示空列表(无业务数据期间) +- 后端无前端改动需求 diff --git a/changelogs/2026-05/07_fix_refund_appeal_oa_submission.md b/changelogs/2026-05/07_fix_refund_appeal_oa_submission.md new file mode 100644 index 0000000..b20ab66 --- /dev/null +++ b/changelogs/2026-05/07_fix_refund_appeal_oa_submission.md @@ -0,0 +1,54 @@ +# fix(refund): 退款申诉补企微 OA 提交 + +**PR**: [#1774](https://git.1814.love:8443/wx/HL/pulls/1774) **Issue**: [#1773](https://git.1814.love:8443/wx/HL/issues/1773) **合并**: 2026-05-07 + +通知对象: 无前端配合(后端纯内部修复 + 运维补 nacos) + +--- + +## 后端修了什么 + +mp 端「申诉(appeal)」/「直接申诉(directAppeal)」之前**只写 DB status=APPEALING,完全没调企微 OA 提交**,审批人永远收不到通知,DB 永久卡死。 + +回调侧(`ApprovalEventHandler:155` 按 template-id 路由)+ DTO 字段 + user-service `buildRefundAppealControls` 出口控件构建 + nacos 配置 key 全已就绪,只缺 order-service 出口侧调用闭环。 + +PR #1774 补的: + +- `RefundApplyService.submitAppealOa(app)` — 申诉模板 7 字段渲染 + Feign 提交,失败抛 RuntimeException 让 LocalEvent 兜底重试 +- `RefundAppealService.appeal/directAppeal` afterCommit 调 + 失败 publish `RETRY_SUBMIT_REFUND_APPEAL_OA` LocalEvent +- `RetrySubmitRefundAppealOaHandler` — 复用 `RetrySubmitRefundOaHandler` 模式,幂等跳过(status≠APPEALING 或 approvalNo 非空) +- `RefundAppealBackfillJob` — `@EventListener(ApplicationReadyEvent)` 启动一次性扫 + 补发卡死的「僵尸申诉」(分布式锁 + 单条失败不阻塞) + +## 前端无需改动 + +`/internal/mp/order/refund/{applicationId}/appeal` 和 `/internal/mp/order/{orderId}/direct-appeal` 接口签名 / VO / 字段 0 改动。前端 UI 不变。 + +## 部署依赖(运维侧 nacos 配置) + +**P0 - 必须补齐才能生效**(配置缺失时 `submitAppealOa` 静默 return 不阻塞业务): + +```yaml +approval: + refund-appeal: + template-id: 3WNge6AGZK2Pq4JwoCAkRXYwaTidZkrXCs9DsiSz + control-ids: + customer-name: <从企微管理端「订单退款申诉」模板编辑页拿> + customizer-name: <同上> + product-type: <同上> + order-no: <同上> + order-date: <同上> + departure-date: <同上> + reason: <同上> +``` + +三套 namespace 都要补:dev / test / prod。 + +## 历史卡死单处理 + +服务重启时 `RefundAppealBackfillJob` 会自动扫 `status=APPEALING AND approval_no IS NULL` 的"僵尸申诉",逐条调 `submitAppealOa` 补发到企微 — 用户无感,运营不需要让用户重新点申诉按钮。 + +## 测试 + +- 单测 22 个新增,4 个测试文件 51/51 全绿(`RefundApplyServiceTest +6 / RefundAppealServiceTest +3 / RetrySubmitRefundAppealOaHandlerTest 7 / RefundAppealBackfillJobTest 6`) +- 本地 round-trip 兜底(4 服务联动 + 外部企微回调 30min 不可达,走「单测全绿 + 测试服真实 round-trip」路径) +- 测试服 round-trip 待 nacos 配置补齐后由管理者完成