From 84e1a48ab8327abdb1ca213e23f5f3de4dc2aa8d Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 30 Apr 2026 11:02:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(approval):=20=E8=B5=84=E6=BA=90=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=20reason=20=E6=8B=BC=E6=8E=A5=E6=8D=A2=E8=A1=8C?= =?UTF-8?q?=E7=AC=A6=E8=A7=A6=E5=8F=91=E4=BC=81=E5=BE=AE=20errcode=3D30102?= =?UTF-8?q?5=20(#1550=20/=20PR=20#1551)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...rce-reason-newline-and-detail-url-order.md | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 changelogs/2026-04/30_fix_approval_resource-reason-newline-and-detail-url-order.md diff --git a/changelogs/2026-04/30_fix_approval_resource-reason-newline-and-detail-url-order.md b/changelogs/2026-04/30_fix_approval_resource-reason-newline-and-detail-url-order.md new file mode 100644 index 0000000..996a237 --- /dev/null +++ b/changelogs/2026-04/30_fix_approval_resource-reason-newline-and-detail-url-order.md @@ -0,0 +1,92 @@ +# 修复:资源(酒店/景点/活动等)启用禁用审批提交全部失败 + +**类型**: 后端 BUG 修复 +**关联**: 工单 #1550 / PR #1551 +**日期**: 2026-04-30 +**影响范围**: 管理后台资源(酒店/景点/活动/餐厅/车辆/服务/备品/员工/成本/房型) 申请上架/下架审批 + +--- + +## 现象 + +管理后台 `web.test.1814.love:9443/hotel/list` 等所有资源管理页点击「申请上架/申请下架」全部报: +``` +提交审批失败: Failed to submit approval to enterprise WeChat +``` + +后端日志(企微返回): +``` +ERROR WechatApiClient - WeChat oa/applyevent error: errcode=301025, errmsg=get approval param error:Text-1771171809841 +``` + +近 30 天 user-service 日志中**没有任何一条 "Approval submitted" 成功记录** — 也就是说资源审批从未成功过。 + +## 根因(双层) + +### 第 1 层:reason 含换行符 + +10 个 resource service(Hotel/RoomType/Scenic/Activity/Restaurant/Vehicle/ServiceItem/Staff/Supplies/CostItem)拼接审批理由时统一用了: +```java +dto.setReason((reason != null ? reason + "\n" : "") + "查看详情: " + dto.getDetailUrl()); +``` + +但企微 OA 模板 `C4ehNuudNWL8zXJBoApGCUEgu8knjyprtkKiRLkET` 的 reason 控件 `Text-1771171809841` 经 `gettemplatedetail` 接口验证实际是 **Text(单行)**,不允许换行符 `\n`。 + +### 第 2 层:setReason 顺序在 setDetailUrl 之前 + +10 个 service 中有 8 个的代码顺序: +```java +dto.setReason(... + "查看详情: " + dto.getDetailUrl()); // ← 此时 detailUrl 还是 null +dto.setDetailUrl(...); +``` + +reason 末尾拼接的 `dto.getDetailUrl()` 调用时 detailUrl 还没 set,实际拼出的是: +``` +<用户输入>\n查看详情: null +``` + +## 修复内容 + +### 1. `InternalApprovalController.java:441` 保持 `buildTextControl` + +(本次同时 revert 了昨日 PR #1549 的错误改动 `buildTextareaControl`,因为企微模板真实定义就是 Text 单行) + +### 2. 10 个 resource service 拼接 reason 改为空格 + +```java +dto.setDetailUrl(detailUrlPrefix != null && !detailUrlPrefix.isEmpty() + ? detailUrlPrefix + xxxId : ""); +dto.setReason((reason != null ? reason + " " : "") + "查看详情: " + dto.getDetailUrl()); +``` + +变更点: +- `\n` → 空格(避免 Text 单行控件不收换行符) +- `setDetailUrl` 提前到 `setReason` 之前(保证 reason 末尾拼到的 detailUrl 不是 null) + +### 3. 单测覆盖 + +`InternalApprovalControllerTest.submitApproval_resourceReason_useTextControl`:用 ArgumentCaptor 抓 controls 列表,断言资源审批的 reason 控件 `control == "Text"` 且 value 不含 `\n`,防回归。 + +## 修复后行为 + +测试服 round-trip(用 wx 账号 enterpriseWechatId=842)4 种资源 + 申请上架,4 次全部成功提交到企微 OA: + +| 资源类型 | 操作 | 结果 spNo | +|---------|------|-----------| +| hotel | 申请上架 | 202604300001 | +| activity | 申请上架 | 202604300002 | +| restaurant | 申请上架 | 202604300003 | +| supplies | 申请上架 | 202604300004 | + +后端日志全部为 `INFO Approval submitted: sp_no=...`,无任何 301025 错误。 + +## 前端处理 + +**零影响**。前端无须改动 — 提交审批接口签名/响应结构完全没变,前端只看 `code/message/data.spNo`,原本对错误的处理逻辑也无变化(成功/失败分支都对)。 + +## 部署状态 + +- ✅ PR #1551 合并到 dev (sha feb248c3) +- ✅ 测试服 hl-user-service 8081/8181 滚动重启 +- ✅ 测试服 hl-resource-service 8082/8182 滚动重启 +- ✅ Round-trip 4 种资源类型「申请上架」全部成功