成员列表接口变更 — 前端对接说明

变更日期 2026-05-14 · 后端已就绪 · 前端需要配合调整渲染逻辑

一句话总结
产品 / 项目成员列表接口 现在按" 一人一行 "返回,同一用户的多个角色合并到主角色行,其他角色名通过新增的 additionalRoleNames: string[] 字段返回。前端只需要把这个数组拼到角色名旁边显示即可。
目录
  1. 背景:为什么改
  2. 受影响的接口(仅 2 个)
  3. 改动前后对比
  4. 新增字段 additionalRoleNames
  5. 前端渲染建议
  6. 边界场景
  7. 不受影响的接口
  8. 前端落地 checklist

1. 背景:为什么改

多角色改造后, 产品/项目的"创建者"角色会自动落一条 rdms_user_object_role 。当 创建者本人就是负责人 时,同一用户在同一对象内会出现两条 ACTIVE 记录:

如果后端原样返回,前端列表会出现"同一个人重复两行"。讨论后约定:后端在 列表接口 层做合并展示, 不影响 底层数据(每个角色行仍独立存在,便于后续可能的"按角色单独操作")。

业务边界(重要)
当前业务上 只有 creator + manager 这一种组合 会让同人多角色出现。其他角色(产品专员、开发等)仍是一人一角色。所以前端可以放心按"主角色 + 附加角色名" 的方式渲染,附加列表通常是 0 或 1 个元素。

2. 受影响的接口(仅 2 个)

GET /admin-api/project/product/{productId}/members

产品团队成员列表

GET /admin-api/project/project/{projectId}/members

项目团队成员列表

响应结构两个接口对称,本文档下文统一用 RespVO 表示,字段路径一致。

3. 改动前后对比

假设:产品 P 里 用户A 是产品经理(同时创建了该产品,所以也有 product_creator 角色), 用户B 是产品专员, 用户C 是已退场的历史成员。

改之前(数据原样返回,4 行)

[
  { id: "100", userId: "A", roleCode: "product_manager",
    roleName: "产品经理", status: 0, ... },
  { id: "101", userId: "A", roleCode: "product_creator",
    roleName: "产品创建者", status: 0, ... },
  { id: "102", userId: "B", roleCode: "product_specialist",
    roleName: "产品专员", status: 0, ... },
  { id: "103", userId: "C", roleCode: "product_specialist",
    roleName: "产品专员", status: 1, leftTime: "..." }
]

用户 A 出现 2 次 — 前端要么重复显示,要么自己去重。

改之后(合并后,3 行)

[
  { id: "100", userId: "A", roleCode: "product_manager",
    roleName: "产品经理",
    additionalRoleNames: ["产品创建者"],  // ← 新增字段
    status: 0, ... },
  { id: "102", userId: "B", roleCode: "product_specialist",
    roleName: "产品专员",
    additionalRoleNames: [],
    status: 0, ... },
  { id: "103", userId: "C", roleCode: "product_specialist",
    roleName: "产品专员",
    additionalRoleNames: [],
    status: 1, leftTime: "..." }
]

用户 A 1 行 — 主行 manager,creator 名字进 additionalRoleNames

合并规则

规则 说明
仅合并 ACTIVE 行 status = 0 的多角色行才聚合; status = 1 的历史 INACTIVE 行 保持每条独立成行 (历史角色留痕,便于审计)
主角色选择 同人多角色时, product_manager / project_manager 角色行优先做主;不在则按 roleId 升序选第一条(理论不该走到这条兜底)
主行字段 id / roleId / roleCode / roleName / joinedTime / leftTime / remark 等都按主角色行的值返回
非主角色名顺序 additionalRoleNames 按角色中文名字典序升序,前端可以直接顺序渲染

4. 新增字段 additionalRoleNames

字段名 类型 状态 说明
additionalRoleNames string[] 本次新增 非主角色的中文名列表,多角色场景使用; 单角色时为空数组 [] ,前端可以放心 length 判空

所有 原有字段保持不变iduserIduserNicknameroleIdroleNameroleCodemanagerFlagstatusjoinedTimeleftTimeremark ),前端原有代码不会因为字段消失而报错。

5. 前端渲染建议

5.1 最简单的展示方式 — 拼成一个字符串

const displayRoleName = additionalRoleNames?.length
  ? `${roleName} + ${additionalRoleNames.join(', ')}`
  : roleName;
//
// 例:roleName="产品经理", additionalRoleNames=["产品创建者"]
// → "产品经理 + 产品创建者"
//
// 例:roleName="产品专员", additionalRoleNames=[]
// → "产品专员"

5.2 更友好的展示 — 主角色 + 浅色 chip 标签

<span class="role-main">{{ roleName }}</span>
<span v-for="extra in additionalRoleNames" :key="extra" class="role-tag">
  {{ extra }}
</span>
//
// CSS:role-tag 设计成浅色 background + 小圆角,跟主角色拉开视觉层级

5.3 表格单元格示意

| 用户       | 角色                       | 状态  |
|-----------|---------------------------|------|
| 灿能管理   | 产品经理 [产品创建者]       | 有效 |
| 洪圣文     | 产品专员                   | 有效 |
| 李凡       | 游客                       | 历史 |

6. 边界场景

场景 预期返回 前端展示
用户 A 仅是产品经理(不是创建者) 1 行, additionalRoleNames=[] "产品经理"
用户 A 同时是产品经理 + 创建者 1 行(主行 manager), additionalRoleNames=["产品创建者"] "产品经理 + 产品创建者"
用户 A 仅是创建者(罕见,理论上创建后立即把 manager 转给别人才会出现) 1 行,主行就是 creator, additionalRoleNames=[] "产品创建者"
用户 A 是产品专员(单角色非 manager) 1 行, additionalRoleNames=[] "产品专员"
用户 A 退场(status=1 INACTIVE 历史行) 每条 INACTIVE 行独立 1 行, additionalRoleNames=[] "产品专员(已退场)"等灰显
用户 A 同时有 历史失效 的角色行 + 当前生效 的角色行 ACTIVE 行合并 1 行 + 每条 INACTIVE 行各占 1 行;同用户在列表里会出现多次(不同 status) 分别用"有效 / 历史"区分;不要再二次合并

7. 不受影响的接口

以下接口 不变 ,前端原有调用方式保持:

接口 说明
POST /admin-api/project/product/{productId}/members
POST /admin-api/project/project/{projectId}/members
新增成员 — 仍按"一个角色一条记录"操作,传 userId + roleId
PUT /admin-api/project/.../members/{memberId} 更新成员 — 按 memberId (即 rdms_user_object_role.id )定位具体角色行操作
PUT /admin-api/project/.../members/{memberId}/inactive 失效成员 — 同上,按 memberId 操作
GET /admin-api/project/product/{productId}/context
GET /admin-api/project/project/{projectId}/context
对象上下文 — currentRole.additionalRoleNames 字段早已存在, 不在本次变更范围
特别提醒 — 编辑 / 失效操作
当用户 A 同时是 manager + creator(合并展示成 1 行)时,前端如果允许"编辑这一行的角色"或"踢出",传的 memberId 主角色行的 id (也就是 manager 那条)。 不会影响 同人的 creator 角色行 — creator 角色仍保留。

这是符合设计的行为:creator 是"留痕"角色,原则上不应该被踢出。如果业务上要踢出整个人,需要前端先调一次 list 接口拿到该用户的所有 active 角色行(注意 — 合并后的 list 里看不到 creator 那条的 id ),后续 是否要单独提供"列出某 user 所有角色行"接口 取决于业务实际诉求,目前没有这个接口。

8. 前端落地 checklist


变更影响最小范围说明

本次后端改动仅在 ProductMemberServiceImpl.getProductMemberListProjectMemberServiceImpl.getProjectMemberList 两个方法中实现, 不修改底层数据rdms_user_object_role 仍按一行一角色存储)。即使前端暂时不读 additionalRoleNames 字段,也只是看不到"+ 产品创建者"字样,不会出现数据错误或重复行问题。