# correction(小程序分享): 撤回链式继承 adminId 建议 — 后端 sharerOpenid 反查链已就绪

> **类型**: 更正前一个 heads-up
> **日期**: 2026-05-24
> **更正对象**: `24_headsup_mini_share_chain_adminid.md` (同日发的)
> **接收方**: mmg (前端 hl-mini)

---

## 🙏 致歉 + 更正

前一份 changelog 让 mmg 在所有分享页面"链式继承 adminId"的建议是**多余的**。wx 提醒我后端反查链早已就绪,我重新精查 hl-mini 仓库代码 + 比对 OrderCreateService 后确认: **产品详情页(`product/core` + `product/mengma`)的分享路径目前已经能正确归属定制师**,前端无需任何"链式继承"改造。

下面整理实际现状。

---

## ✅ 实际工作机制 (已闭环部分)

### 后端反查链 (PR #2913 已合 main / 已上正式)

`OrderCreateService.resolveSharedCustomizerId(ctx)` 顺序:

1. **`ctx.customizerId` 显式传值** (URL `?adminId=xxx` / customizerId) → 直接采用 (老 URL Scheme 链路兼容)
2. **`customizerId` 空 + `sharerOpenid` 非空** → 调用 `userFeignClient.getCustomizerByOpenid(openid)`
   - 在 user-service 内部一次性聚合反查 (openid → user.phone → admin_user.mobile → adminId)
   - phone 不通过 Feign URL 暴露 (二轮 CR 加固)
3. 任何一步失败 / 命中的 admin 不是活跃 CUSTOMIZER → 兜底 `assignRandomCustomizer`

### 前端 product/core + mengma 详情页 (已生效)

```js
onShareAppMessage() {
  const openId = storage.get('openId') || '';
  return {
    path: `/packages/product/core/detail/detail?id=${productId}&shareOpenId=${openId}`,
    ...
  };
}
```

`shareOpenId` 是分享人的小程序 openid (从 `storage.get('openId')` 取当前登录用户的)。

### 完整链路 (链路 B 实际工作)

```
wx 在产品详情页点「分享给朋友」
        ↓
onShareAppMessage path 含 shareOpenId = wx 的 openid
        ↓
客人扫码进店 onLoad → _shareOpenId = wx 的 openid
        ↓
客人下单 confirm.js → orderData.sharerOpenid = wx 的 openid
        ↓
后端 MpOrderSaveReqVO 接 sharerOpenid → ctx.setSharerOpenid()
        ↓
OrderCreateService.resolveSharedCustomizerId 走反查链
        ↓
userFeignClient.getCustomizerByOpenid(wx_openid) → wx 的 adminId
        ↓
customizerValidator.resolve(wx_adminId) 校验 CUSTOMIZER 角色通过
        ↓
order.customizerId = wx_adminId ✅ 归原始定制师
```

**产品详情页(core + mengma)的分享已经完全正确**, 无需链式继承 adminId, 无需调 `/mp/customizer/me`。

---

## ⚠️ 真正缺的部分: 其他可分享页未透传 shareOpenId

grep 现状: **15 个可分享页面没传 shareOpenId**, 客人从这些页面扫码进店分享, sharerOpenid 字段后端拿不到, 后端反查链无法识别分享人, 客人下单走 `assignRandomCustomizer` 兜底随机。

### 没传 shareOpenId 的页面清单 (按业务影响排序)

| 优先级 | 页面 | 文件 | 现状 path |
|---|---|---|---|
| **P0** | 首页 | `pages/index/index.js` L122-129 | `/pages/index/index` |
| **P1** | 酒店详情 | `packages/hotel/detail/detail.js` L131-138 | `?id=${id}` |
| **P1** | 景区详情 | `packages/scenic/detail/detail.js` L189-197 | `?id=${id}` |
| **P1** | 餐厅详情 | `packages/restaurant/detail/detail.js` L65-72 | `?id=${id}` |
| **P1** | 活动详情 | `packages/activity/detail/detail.js` L86-93 | `?id=${id}` |
| **P2** | 定制师详情 | `packages/custom/detail/detail.js` L183-190 | `?id=${designerId}` |
| **P2** | 攻略详情 | `packages/guide/detail/detail.js` L70-77 | `?id=${id}` |
| **P3** | 酒店/景区/餐厅/活动**列表页** | 各 `list.js` | 各种 list path |
| **P3** | 定制入口 | `packages/custom/custom/custom.js` L119-125 | `/custom/custom` |
| **P3** | 旅行指南 | `packages/user/travel-guide/travel-guide.js` L9-14 | `/travel-guide` |
| **P3** | 定制案例 | `packages/product/custom-all/list/list.js` L25-29 | `/custom-all/list` |
| **P3** | 评价列表 | `packages/review/list/list.js` L155+ | `?targetId=...&targetType=...` |

### 推荐改法 (所有页面统一模式)

```diff
onShareAppMessage() {
  const { ... } = this.data;
+ const openId = storage.get('openId') || '';
+ const shareSuffix = openId ? `&shareOpenId=${openId}` : '';
  return {
    title: ...,
-   path: `/packages/hotel/detail/detail?id=${this.data.id}`,
+   path: `/packages/hotel/detail/detail?id=${this.data.id}${shareSuffix}`,
    imageUrl: ...
  };
}
```

> ⚠️ 注意首页 `pages/index/index` 这种没有现成 query 的, `shareSuffix` 要改成 `?shareOpenId=${openId}` (注意 `?` 前缀)。

### 还需注意 onShareTimeline (朋友圈分享)

product/core L1474-1482 + product/mengma 同样有 `query: \`id=${productId}&shareOpenId=${openId}\``。其他详情页如果有 onShareTimeline 也要同步补 shareOpenId。grep 看 onShareTimeline 比 onShareAppMessage 实现少, 建议加一份扫描。

---

## 🧹 可选清理 (confirm.js)

下单代码 `packages/order/confirm/confirm.js` L837-838:
```js
if (this._adminId) {
  orderData.adminId = this._adminId;  // ← 后端 MpOrderSaveReqVO 不接, Jackson @JsonIgnoreProperties(ignoreUnknown=true) 直接吞掉
}
```

`adminId` 字段后端不接收, 是死代码 (无害但冗余)。 `_adminId` 已经走 L176-179 `_customizerId` 兜底链, 不需要单独透传。**可以删掉**, 也可以留着 (后端不会因此报错)。

---

## 📊 三个分享渠道最新现状

| # | 渠道 | 当前归属正确? | 还要做什么 |
|---|---|---|---|
| **A** | admin 端 URL Scheme | ✅ PR #2977 后限定持 CUSTOMIZER + 绑企微的 admin 可生成 | — |
| **B** | 产品详情页(core + mengma) `onShareAppMessage` | ✅ shareOpenId 已透传, 后端反查链生效 | — |
| **B'** | 产品详情页(core + mengma) `onShareTimeline` (朋友圈) | ✅ 同上 | — |
| **B''** | **其他详情/列表页 onShareAppMessage** | ❌ shareOpenId 没透传, 客人下单兜底随机 | **补 shareOpenId 透传** (上方 15 个文件) |

---

## ✅ 验证方式 (前端改完后)

1. wx (定制师) 在小程序内任一详情页点「分享给朋友」给客人
2. 客人扫码进店, 浏览到产品页下单
3. 后端订单 `customizer_id` 字段应该等于 wx 的 admin_id (不是兜底随机)
4. 后端日志会有: `订单按 sharerOpenid 反查定制师成功: openid=xxx, adminId=wx_id`

---

## 📅 时间线

| # | 任务 | 负责人 | 状态 |
|---|---|---|---|
| 1 | 后端 PR #2977 admin guard | wx | ✅ 合 dev / 测试服真测拒绝路径通过 |
| 2 | ~~前端 onShareAppMessage 链式继承 adminId~~ | ~~mmg~~ | ❌ **撤回, 不需要做** |
| 3 | 前端其他可分享页补 shareOpenId 透传 (15 个文件 / 按优先级分多 PR) | mmg | ⏳ 待 mmg 排期 |
| 4 | 可选: confirm.js L837-838 删 orderData.adminId 死字段 | mmg | ⏳ 可选 |
| 5 | 后端 PR #2977 合 main → 上正式 | wx | ⏳ |

---

## 联系人

后端: wx (呼籁旅行)
前端: mmg

如有疑问看后端 PR https://git.1814.love:8443/wx/HL/pulls/2977 + 工单 https://git.1814.love:8443/wx/HL/issues/2976
