8.7 KiB
heads-up(小程序分享): onShareAppMessage / onShareTimeline 需链式继承 adminId
类型: heads-up + 前端待办 (后端无需改动) 关联 PR/Issue: 后端 PR #2977 (Closes #2976) 配套 日期: 2026-05-24 影响范围: 小程序内原生分享渠道(分享给朋友 / 分享到朋友圈)的客人下单定制师归属 接收方: mmg (前端 hl-mini 仓库) 前端: 需要改造 (本仓后端已就绪,等前端补齐链路)
🎯 背景
wx 实测发现:admin (id=1001 SUPER_ADMIN, 非定制师) 在管理端「分享到小程序」生成的 URL Scheme,客人扫码下单后订单的「定制师」被错绑成 admin 自己,不是中间转发的真定制师 wx。
根因: URL Scheme 的 t 参数在微信 wxa/generatescheme 时加密锁死,中间转发不能改写,query 里的 adminId= 永远是"生成人"。
后端已修 (PR #2977 已合 dev,测试服真测拒绝路径通过):
POST /admin/wxapp/share/url-link和/url-scheme加 guard- 仅
status=ACTIVE+roleKeys 含 CUSTOMIZER+wechatUserid 非空的 admin 可生成 - 错误码
200312 SHARE_REQUIRES_BOUND_CUSTOMIZER, 文案: "仅持定制师角色且已绑定企业微信的成员可生成分享链接"
但这只覆盖了链路 A (admin 端管理后台「分享 URL Scheme」按钮)。链路 B / C 还没覆盖,需要 mmg 前端配合。
📊 三个分享渠道现状
| # | 渠道 | 入口 | 当前前端实现 | 客人下单归属 |
|---|---|---|---|---|
| A | admin/定制师 在管理端点「分享 URL Scheme」 | hl-ui MiniappShareModal.vue | URL query 含 adminId=<生成人id>, 小程序 onLoad 接到存 storage |
✅ PR #2977 后只有定制师能生成, customizer 正确 |
| B | 定制师在小程序产品详情页点右上角「分享给朋友」 | onShareAppMessage() |
只传 shareOpenId, 没传 adminId |
❌ 客人下单 customizerId 空 → 兜底随机 |
| C | 定制师在小程序产品详情页点「分享到朋友圈」 | onShareTimeline() |
只传 shareOpenId, 没传 adminId |
❌ 同上 |
🔬 代码定位
仓库 mmg/hl-mini main 分支, 文件 packages/product/core/detail/detail.js:
L191-199 onLoad (接收链路 A 的 adminId, 这部分已经对)
onLoad(options) {
const productId = options.id;
this._shareOpenId = options.shareOpenId || '';
this._adminId = options.adminId || '';
// 链路 A 的 adminId 已写入 storage 桥接订单详情页
if (this._adminId) {
storage.setShareCustomizerId(this._adminId);
}
...
}
L1464-1472 onShareAppMessage (BUG: 没拼 adminId)
onShareAppMessage() {
const { product, productId, shareCardImage, heroBgImage } = this.data;
const openId = storage.get('openId') || '';
return {
title: product.title || '呼籁旅行 ...',
path: `/packages/product/core/detail/detail?id=${productId}&shareOpenId=${openId}`, // ← 缺 adminId
imageUrl: shareCardImage || heroBgImage || ''
};
}
L1474-1482 onShareTimeline (BUG: 同样没拼)
onShareTimeline() {
...
return {
title: ...,
query: `id=${productId}&shareOpenId=${openId}`, // ← 缺 adminId
imageUrl: ...
};
}
🛠️ 前端推荐改造 (链式继承 adminId)
最简方案 — 不需要调任何后端接口, 沿用 storage 桥接的 adminId:
onShareAppMessage() {
const { product, productId, shareCardImage, heroBgImage } = this.data;
const openId = storage.get('openId') || '';
// 链式继承: 当前页面是从分享链接进入的话, _adminId 不为空; 否则从 storage 兜底
const adminId = this._adminId || storage.get('shareCustomizerId') || '';
// path 拼上 adminId, 这样客人扫码进店 onLoad 时 options.adminId 仍是原始定制师
const adminPart = adminId ? `&adminId=${adminId}` : '';
return {
title: product.title || '呼籁旅行 ...',
path: `/packages/product/core/detail/detail?id=${productId}&shareOpenId=${openId}${adminPart}`,
imageUrl: shareCardImage || heroBgImage || ''
};
}
onShareTimeline() {
const { product, productId, shareCardImage, heroBgImage } = this.data;
const openId = storage.get('openId') || '';
const adminId = this._adminId || storage.get('shareCustomizerId') || '';
const adminPart = adminId ? `&adminId=${adminId}` : '';
return {
title: product.title || '呼籁旅行 ...',
query: `id=${productId}&shareOpenId=${openId}${adminPart}`,
imageUrl: shareCardImage || heroBgImage || ''
};
}
链式继承的业务效果
- wx (定制师) 通过 admin 端生成首个链接
?adminId=wx_id发给客人 A - A 进店 onLoad →
_adminId = wx_id→ 存 storage - A 在小程序里再点「分享给朋友」给客人 B → onShareAppMessage 沿用 wx_id → path 含
adminId=wx_id - B 扫码进店下单 → customizerId=wx_id → customizer 归 wx ✅
- 第 N 次转发都归原始定制师, 营销链路追溯完整
🔧 升级方案 (可选, 若定制师自己直接打开小程序产品页要分享)
如果定制师 wx 没有经过 admin 端链接, 自己直接进入小程序 想要分享产品 → _adminId 为空, storage 也无 → 链式继承拿不到 adminId → 客人下单兜底随机。
解决方法: 页面 onLoad 调后端 GET /mp/customizer/me (PR #2913 已上线):
onLoad(options) {
this._shareOpenId = options.shareOpenId || '';
this._adminId = options.adminId || '';
if (this._adminId) {
storage.setShareCustomizerId(this._adminId);
}
// 升级: 如果当前用户是定制师本人, 把自己 adminId 也写入 storage 用于分享
if (!this._adminId) {
customizerApi.me().then(res => {
if (res?.data?.isCustomizer && res?.data?.adminId) {
this._adminId = res.data.adminId;
storage.setShareCustomizerId(this._adminId);
}
});
}
...
}
/mp/customizer/me 响应结构:
{
"code": 200,
"data": {
"isCustomizer": true,
"adminId": "1760000000000099",
"customizerName": "王骁",
"avatar": "..."
}
}
📋 改造范围 (其他可分享页面)
类似的分享 hook 在以下页面也需要同步加 adminId (基于 grep 结果):
- ✅
packages/product/core/detail/detail.js(重点, 上述代码) packages/product/mengma/detail/detail.js(小蒙马产品详情)packages/scenic/detail/detail.js(景区详情)packages/hotel/detail/detail.js(酒店详情)packages/restaurant/detail/detail.js(餐厅详情)packages/guide/detail/detail.js(向导详情)packages/user/travel-guide/travel-guide.js(旅行攻略)packages/product/compare/compare.js(对比页)pages/index/index.js(首页)pages/trip/trip.js(行程页)
建议至少先改 4 个核心入口: 核心产品详情 / 小蒙马产品详情 / 主题(产品线)详情 / 首页。其他后续补。
✅ 验证方式 (前端改完后)
- wx (定制师) 在管理端「分享 URL Scheme」生成 → 给客人 A
- A 进店 → 在小程序内再点产品页右上角「分享给朋友」 → 给客人 B
- B 进店 onLoad →
console.log(this._adminId)应等于 wx 的 admin_id - B 下单 → 后端订单的
customizer_id字段应等于 wx 的 admin_id - 朋友圈分享: A 点「分享到朋友圈」, 别人通过朋友圈进店, 同样验证
_adminId链式继承
🔗 后端接口清单 (mmg 不需要改, 已就绪)
| 接口 | 路径 | 用途 |
|---|---|---|
| 获取当前定制师身份 | GET /mp/customizer/me |
当前 C 端用户是定制师? 返 adminId |
| 客人下单 | POST /mp/order/create |
接受 customizerId (字符串透传雪花 ID) + sharerOpenid (溯源用) |
| Admin 生成 URL Link / Scheme | POST /admin/wxapp/share/url-link /url-scheme |
PR #2977 后限定持 CUSTOMIZER + 绑企微的 admin |
customizerId 在订单创建时由后端 CustomizerValidator.resolve() 校验:
status = ACTIVEroleKeys含CUSTOMIZER- 通过 → 订单 customizer = 该 adminId
- 不通过 → 订单 customizer 走
assignRandomCustomizer兜底随机
📅 时间线建议
| # | 任务 | 负责人 | 状态 |
|---|---|---|---|
| 1 | 后端 admin 端 guard PR #2977 | wx | ✅ 已合 dev / 测试服真测拒绝路径通过 |
| 2 | 前端 onShareAppMessage / Timeline 链式继承 adminId (核心产品页) | mmg | ⏳ |
| 3 | 前端其他分享入口同步改 (景区/酒店/...等) | mmg | ⏳ 可分多 PR |
| 4 | 前端调 /mp/customizer/me 兜底定制师本人首次进入 (可选升级) |
mmg | ⏳ 可选 |
| 5 | 后端 PR #2977 合 main → 上正式 | wx | ⏳ |
联系人
后端: wx (呼籁旅行) 前端: mmg
如有疑问看后端 PR wx/HL#2977 + 工单 wx/HL#2976