hl-api-changelog/changelogs/2026-05/09_feat_admin_product-share-copy-to-designer.md
API Changelog Bot b56239d609 feat(admin): 产品分享=复制产品给定制师后端实现 (PR #1919, Issue #1918)
GET /admin/user/customizers - 定制师下拉(无 status / 企微过滤)
POST /admin/product/item/{id}/share body {targetAdminId} - 复制产品+产品线给目标定制师
权限点 PRODUCT_SHARE 默认绑 SUPER_ADMIN / ADMIN

明确澄清: 与 09_frontend_notice_admin_product-share-remove-target-user-step.md
(已撤回) 不是同一个事情, 那个通知针对的是 url-scheme 老接口, 本通知是真正的
"分享产品" 新功能 (复制产品到目标定制师名下), mmg 不要混淆。
2026-05-09 18:06:09 +08:00

10 KiB

admin 产品分享 = 复制产品给定制师 (后端实现)

服务: hl-user-service (8081) + hl-product-service-v2 (8083) PR: #1919 (合并到 dev a18329b7) Issue: #1918 日期: 2026-05-09 影响范围: admin 产品列表「分享」按钮 → 「分享产品」弹窗 → 选目标定制师 → 复制产品给该定制师


⚠️ 关键澄清(请 mmg 与上一条撤回通知一起看)

本日早些时候有一条已撤回的 changelog 09_frontend_notice_admin_product-share-remove-target-user-step.md,误以为「分享产品弹窗的目标用户选择步骤」是 url-scheme 接口的多余步骤,要前端删掉。那个判断针对的是 POST /admin/wxapp/share/url-scheme 这个老接口(生成微信短链),与本通知没有关系

真相是: 你看到的「分享产品 — 选目标用户」弹窗对应的是另一个新功能 — admin 把自己负责的产品复制一份给指定的定制师,让定制师在自己工作台里看到这个产品(状态草稿,需重新编辑)。这个后端原本不存在,本 PR #1919 才补齐。

也就是说:

  • 你的弹窗 UI 在 master 上确实有(我们之前调研错了),不要删
  • 它需要调用的是新增POST /admin/product/item/{id}/share 接口,而不是老的 url-scheme
  • 目标用户下拉改调 GET /admin/user/customizers(本 PR 新增)

一、背景

业务场景: admin (通常是超管/客服)在产品列表上看到一个产品(可能是从供应商那里整理好的、或自己之前建的),想转给某个定制师让 ta 接手维护/二次编辑。复制后:

  • 定制师在自己的产品列表里看到这个产品(createdBy = 该定制师)
  • 状态自动是 DRAFT(草稿),让定制师确认/调整后再发布
  • 标题加(来自 {操作人 username} 的分享)后缀,让定制师知道来源
  • 价格日历不复制(让定制师按自己客户/批次重新设置)
  • 关联的产品线也复制一份独立副本(给定制师独立空间,改产品线不影响别人)

权限走标准权限点 PRODUCT_SHARE(放在 admin_permission 表),默认绑定 SUPER_ADMIN + ADMIN 两个角色。不写死角色 — 后续业务侧可在权限管理页面给任意角色绑该权限点。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 目标定制师下拉(分享专用) GET /admin/user/customizers 新增 返「角色含 CUSTOMIZER」的所有 admin,无 status / 企微绑定过滤
2 分享产品给定制师 POST /admin/product/item/{id}/share 新增 复制产品+产品线到目标定制师名下

三、接口详情

1. 目标定制师下拉 GET /admin/user/customizers

弹窗里「目标用户」下拉应改调本接口(替换之前误调的 /admin/user?page=1&pageSize=100)。

请求: 无参 (Header 带 admin token)

响应 Result<List<CustomizerSimpleVO>>:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": [
    { "adminId": 1002, "username": "alice", "avatar": "https://..." },
    { "adminId": 1015, "username": "bob",   "avatar": "" }
  ]
}
字段 类型 说明
data[].adminId Long 定制师 admin_user.admin_id,作为分享接口的 targetAdminId
data[].username String 定制师用户名(下拉显示用)
data[].avatar String 头像 URL,取自 admin_user.avatar 字段(不查企微 wechat_user 也不查 designer_profile),没有时返空字符串 ""

与已有 /admin/user/designers 的区别(选用本接口的原因):

维度 /admin/user/customizers(本接口) /admin/user/designers(已有接口)
过滤 status 不过滤 (LOCKED / DISABLED 也返回) status = ACTIVE
过滤企微绑定 不过滤 (未绑定企微也返回) 必须 enterprise_wechat_id 非空
返回 VO 简单 3 字段 重 VO(认证等级/服务领域/手机/简介等)
用途 分享产品下拉,只要是定制师就能选 小程序 C 端定制师列表展示

→ 本接口刻意放宽过滤,因为分享产品场景下「即使临时锁定/没绑企微的定制师也允许分享给他」(分享是给账号,定制师后续登录处理)。

鉴权: 标准 admin token(网关已校验),接口本身不再做角色硬编码检查 — 任何登录 admin 都能调用。


2. 分享产品给定制师 POST /admin/product/item/{id}/share

Headers:

  • Content-Type: application/json
  • Authorization: Bearer <admin token>

Path 参数:

  • id Long 必填 — 源产品 productId

Request body ProductShareReqVO:

{ "targetAdminId": 1002 }
字段 类型 必填 说明
targetAdminId Long 目标定制师 adminId(取自上方 /admin/user/customizers 列表的 data[].adminId)

Response Result<ProductShareRespVO>:

{
  "code": 200,
  "message": "成功",
  "success": true,
  "data": {
    "newProductId": 1818070000000000123,
    "newProductNo": "C260509047",
    "newLineId": 1818070000000000456,
    "newProductName": "云南 8 日游(来自 wx 的分享)"
  }
}
字段 类型 说明
newProductId Long 新复制出来的产品 ID
newProductNo String 新生成的产品编号(走 ProductNoGenerator,如 C260509047)
newLineId Long 新复制的产品线 ID(独立副本)
newProductName String 新产品名称(原名 + (来自 {操作人 username} 的分享) 中文括号)

业务行为(逐项):

  1. 校验当前 admin 持 PRODUCT_SHARE 权限点 → 否返 403
  2. 校验 targetAdminId 对应 admin 的 roleKeysCUSTOMIZER → 否返 400 "目标用户不是定制师"
  3. 校验源产品存在 → 否返 404
  4. 复制产品线: 新生成 lineId,其余字段照抄源产品线
  5. 复制产品(基于已有 ProductCopyService):
    • lineId 指向新产品线
    • name = 源 name + (来自 {操作人 username} 的分享)
    • createdBy = targetAdminId(覆盖默认的当前操作人)
    • status = DRAFT
    • productNo = 新生成
    • 不复制 product_price_calendar 表(价格日历由定制师重新配置)
    • 复制范围: 主表 + 4 张 1:1 子表 + 备品/费用 1:N 子表 + 行程相关 4 表(同 copyProduct)

错误码:

HTTP 业务消息 触发条件
403 (走全局异常) 当前 admin 无 PRODUCT_SHARE 权限点
400 目标用户不是定制师 targetAdminId 对应 admin 不持 CUSTOMIZER 角色
404 (走全局异常) 源产品不存在或已删除

四、前端改造点(mmg 关注)

文件: hl-ui/src/views/product/list/components/...(分享弹窗相关)

  1. 下拉接口换地址:

    • 改前: GET /admin/user?page=1&pageSize=100 (老接口被硬编码角色挡,显示"没有权限")
    • 改后: GET /admin/user/customizers (本 PR 新增,无过滤)
    • 字段映射: data[].adminId → 下拉 value, data[].username → 下拉 label, data[].avatar → 下拉 icon(可选)
  2. 「确定」按钮接口:

    • 改前: 不存在(后端没接口,所以点"确定"实际什么都没发生 / 报 404)
    • 改后: POST /admin/product/item/{id}/share body {targetAdminId}
    • 成功后展示成功 toast,可加跳转「定制师视角查看新产品」入口(用 data.newProductId)
  3. 目标用户下拉为空时的兜底:

    • 如果系统里一个定制师都没有,接口返 data: [],前端显示「请先创建定制师账号」之类的空态
    • 不要再用上一版逻辑里"没权限"的 toast(本接口不会再返 403)
  4. "分享后规则"提示文案(已在弹窗右侧展示,与后端行为已对齐):

    • 状态为草稿,需重新编辑发布 后端确实置 DRAFT
    • 标题会添加"(来自 xxx 的分享)"后缀 后端确实加,xxx = 当前 admin.username
    • 生成新的产品编号 后端走 ProductNoGenerator
    • 不包含价格日历(需重新设置) 后端确实不复制 product_price_calendar

五、权限点 PRODUCT_SHARE

新加权限点(admin_permission 表):

字段
permission_code PRODUCT_SHARE
permission_name 分享产品
resource_type PRODUCT
action SHARE
description 允许将自己的产品复制一份分享给指定的定制师
status ACTIVE

默认绑定角色(admin_role_permission 表):

  • SUPER_ADMIN
  • ADMIN

CUSTOMIZER 不绑 — 业务侧默认由 SUPER_ADMIN/ADMIN 负责跨定制师分享,普通 CUSTOMIZER 只是接收方。如业务后续要让定制师之间互相分享,在权限管理页面给 CUSTOMIZER 角色加这个权限点即可,无需改后端代码


六、不在本 PR 范围

  • AdminUserController.listAdmins() 第 162 行硬编码 if (!"SUPER_ADMIN".equals(role) && !"ADMIN".equals(role)) ... 不动 — 留后续单独治理工单
  • 已分享产品的二次分享 / 取消分享 / 审计追踪 / 通知目标定制师等扩展功能
  • 前端 hl-ui 的弹窗 UI 调整(由 mmg 完成)

七、测试与验证

  • 全 1160(product) + 2341(user) 单元测试均无回归
  • 新增 19 个单测覆盖: DesignerServiceTest +5 / InternalUserControllerTest +3 / ProductShareServiceTest 11/11
  • 本地启动全链路 API 测试未做(权衡时间) — 合 dev 后由后端在测试服 1.182.108.58 用真 admin token 通过网关 9443 立即验证,如发现问题立刻提下一个 PR

八、相关 PR / 工单

PR / Issue 说明 是否相关
#1919 本 PR 主体
#1918 工单 已 closed
已撤回的 09_frontend_notice_admin_product-share-remove-target-user-step.md 误将本功能识别为 url-scheme 多余步骤 ⚠️ 已撤回,本通知是真正的解决方案
#1488 老的 url-scheme 接口(生成微信短链) 不相关,是另一个功能

联系人: wx