userId
列表通过
watcherUserIds
字段一并提交到现有的
create-with-team
接口即可。后端自动给这些用户授予
产品/项目关心人
角色,让他们后续能在列表 / 详情看到这个对象(菜单按 watcher 角色权限渲染)。
"关心人"(watcher)是 对产品 / 项目本身长期感兴趣,但不参与交付 的人。典型场景:
| 身份 | 角色 code | 能看什么 | 能做什么 |
|---|---|---|---|
| 产品经理 / 项目负责人 |
product_manager
/
project_manager
|
对象内全部 tab | 所有操作(分配任务、改预算、删对象等) |
| 团队成员(开发 / 测试 / 协办等) | 各业务角色 | 对象内执行细节(任务、代码、文档等) | 跟自己角色 / 任务挂钩的写操作 |
| 关心人 watcher |
product_watcher
/
project_watcher
|
对象主要 tab(概览 / 进度 / 需求) | 只读 — 不参与交付 |
visibility_config
(粒度=方向 or 全局,由系统管理员配)
关心人记录最终落到
rdms_user_object_role
表(与 member 同表,只是
role_id
指向 watcher 角色)。系统按这个角色让用户进入"数据权限通道 1(自己参与)",跟普通成员路径一致 —— 区别只在
菜单 / 按钮粒度
由 watcher 角色绑定的权限决定。
建议 UI:第 2 步页面里在"团队成员"列表下方加一个独立区块" 关心人(选填) ",含一个用户多选控件。
┌─────────────────────────────────────────────────────────┐
│ 第 2 步:维护初始团队 │
├─────────────────────────────────────────────────────────┤
│ │
│ 团队成员 * │
│ ┌────────────────────────────────────────────────────┐ │
│ │ [+] 添加成员 │ │
│ │ · 张三 产品经理 操作 ▾ │ │
│ │ · 李四 开发 操作 ▾ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 关心人(选填) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ [多选用户] 王五 ╳ 赵六 ╳ │ │
│ └────────────────────────────────────────────────────┘ │
│ 提示:关心人将获得"产品/项目关心人"角色,能在列表和概览看到此对象, │
│ 但不开放代码 / 任务 / 文档等执行细节。可与团队成员重叠。 │
│ │
│ [取消] [上一步] [完成] │
└─────────────────────────────────────────────────────────┘
点击"完成"时,按 §4 / §5 的 ReqVO 结构组装:
product
/
project
块 = 第 1 步表单
members
块 = 第 2 步团队成员列表
watcherUserIds
= 第 2 步关心人多选控件的 userId 数组
(可空 / 可省略字段)
| 方法 | 路径 | 用途 | 本次是否新接口 |
|---|---|---|---|
| POST | /project/product/create-with-team |
创建产品(含初始团队 + 关心人) |
已有接口;本次只是 ReqVO 多了
watcherUserIds
字段
|
| POST | /project/project/create-with-team |
创建项目(含初始团队 + 关心人) |
已有接口;本次只是 ReqVO 多了
watcherUserIds
字段
|
| GET |
/system/user/list-all-simple
(别名 /system/user/simple-list
)
|
获取启用用户精简列表(前端选关心人下拉用) | 已有接口,无需任何改动 |
事务性原子接口:产品基础资料 + 初始团队 + 关心人在同一事务内完成,任一步失败整体回滚。
@PreAuthorize("@ss.hasPermission('project:product:create')")
— 创建产品权限码
ProductCreateWithTeamReqVO
)
{
"product": {
"code": "CNPD2026001",
"directionCode": "system",
"name": "RDMS 产品平台",
"managerUserId": 1024,
"description": "面向研发管理的一体化产品"
},
"members": [
{ "userId": 1024, "roleId": 3100000002001, "remark": "产品经理本人" },
{ "userId": 1025, "roleId": 3100000002002, "remark": "开发" }
],
"watcherUserIds": [2001, 2002]
}
| 路径 | 类型 | 必填 | 说明 |
|---|---|---|---|
product |
object | 必填 | 产品基础资料,结构见下 |
product.code |
string ≤ 64 | 选填 | 产品编码;为空时由后端按规则生成 |
product.directionCode |
string ≤ 32 | 必填 |
产品方向字典值(字典类型
object_direction
)
|
product.name |
string ≤ 128 | 必填 | 产品名称 |
product.managerUserId |
Long | 必填 |
产品经理 user_id;必须在
members
列表里有对应的产品经理角色记录
|
product.description |
string | 选填 | 产品描述 |
members |
array<object> | 必填 |
初始团队,
必须包含 userId == product.managerUserId 且 roleId 是
product_manager
的记录
;后端不会自动追加经理
|
members[].userId |
Long | 必填 | 成员 user_id |
members[].roleId |
Long | 必填 |
对象域角色 ID(
scope_type='object'
,
object_type='product'
);前端用 role code 反查得到(如
product_manager
)
|
members[].remark |
string ≤ 500 | 选填 | 成员备注 |
watcherUserIds
|
array<Long> | 选填 | 关心人 user_id 数组 。允许为空 / 省略字段 / 与 members 重叠;后端按 (user, object, role) 三元组写入,重复跳过 / INACTIVE 复活;后端会自动去 null 去重 |
{
"code": 0,
"data": 30001, // 新创建的 productId
"msg": ""
}
| 错误码 / 信息 | 触发条件 |
|---|---|
产品基础资料不能为空 |
product
字段缺失
|
初始团队成员不能为空 |
members
空数组或缺失
|
产品经理不能为空 |
product.managerUserId
缺失
|
产品方向不能为空 |
product.directionCode
缺失
|
PRODUCT_MEMBER_ALREADY_EXISTS |
members
里同 user 同 role 重复(multi-role 唯一索引拦截)
|
PRODUCT_INTERNAL_ROLE_NOT_CONFIGURED |
后端启动校验通过但运行时找不到
product_watcher
角色(理论上不会发生 — 启动校验会拦)
|
事务性原子接口:项目基础资料 + 初始团队 + 关心人在同一事务内完成,任一步失败整体回滚。
@PreAuthorize("@ss.hasPermission('project:project:create')")
— 创建项目权限码
ProjectCreateWithTeamReqVO
)
{
"project": {
"projectCode": "CNPJ2026001",
"projectName": "客户交付项目 A",
"projectType": "delivery",
"directionCode": "system",
"productId": 30001,
"managerUserId": 1024,
"plannedStartDate": "2026-06-01",
"plannedEndDate": "2026-09-30",
"projectDesc": "为客户 XX 定制交付"
},
"members": [
{ "userId": 1024, "roleId": 3100000003001, "remark": "项目负责人本人" },
{ "userId": 1030, "roleId": 3100000003002, "remark": "前端开发" }
],
"watcherUserIds": [2001, 2002]
}
| 路径 | 类型 | 必填 | 说明 |
|---|---|---|---|
project |
object | 必填 |
项目基础资料;新建场景不传
actualStartDate
/
actualEndDate
(实际日期由执行阶段才有值)
|
project.projectCode |
string ≤ 64 | 选填 | 项目编码;为空时由后端按规则生成 |
project.projectName |
string ≤ 200 | 必填 | 项目名称 |
project.projectType |
string ≤ 32 | 必填 | 项目类型字典值 |
project.directionCode |
string | 条件必填 | 项目方向; 未选产品时必填;选了产品则后端按产品方向覆盖 ,前端可不传 |
project.productId |
Long | 选填 | 所属产品 ID;不选表示无产品归属 |
project.managerUserId |
Long | 必填 |
项目负责人 user_id;同产品规则,必须在
members
里出现且对应负责人角色
|
project.plannedStartDate
/
plannedEndDate
|
date
YYYY-MM-DD
|
选填 | 计划日期 |
project.projectDesc |
string ≤ 4000 | 选填 | 项目说明 |
members |
array<object> | 必填 |
结构同
ProjectMemberSaveReqVO
,与产品对称。必须包含 userId == project.managerUserId 且 roleId 为
project_manager
的记录
|
members[].userId |
Long | 必填 | 成员 user_id |
members[].roleId |
Long | 必填 |
对象域角色 ID(
scope_type='object'
,
object_type='project'
)
|
members[].remark |
string ≤ 500 | 选填 | 成员备注 |
watcherUserIds
|
array<Long> | 选填 | 关心人 user_id 数组 。规则与产品端一致:允许空 / 省略 / 与 members 重叠;后端去 null 去重,按三元组幂等写入 |
{
"code": 0,
"data": 40001, // 新创建的 projectId
"msg": ""
}
| 错误码 / 信息 | 触发条件 |
|---|---|
项目基础资料不能为空 |
project
字段缺失
|
项目名称不能为空 |
project.projectName
缺失
|
项目负责人不能为空 |
project.managerUserId
缺失
|
初始团队成员不能为空 |
members
空
|
PROJECT_MEMBER_ALREADY_EXISTS |
members
里同 user 同 role 重复
|
PROJECT_INTERNAL_ROLE_NOT_CONFIGURED |
后端运行时找不到
project_watcher
角色(理论上不会发生)
|
获取所有
已启用
用户的精简信息,用于前端下拉 / 多选控件。也可访问别名
/system/user/simple-list
。
登录态即可(无显式权限码限制)。
无参数。
List<UserSimpleRespVO>
)
{
"code": 0,
"data": [
{ "id": 1024, "nickname": "张三" },
{ "id": 1025, "nickname": "李四" },
{ "id": 2001, "nickname": "王五" }
],
"msg": ""
}
GET /system/user/page
走分页查询;或者拉一次全量后在前端做 search filter。
可以。多角色合法,会落两条
rdms_user_object_role
记录(一条 member 角色 + 一条 watcher 角色),数据权限
取并集
。这种情况通常不会出现 —— 已经在团队里的人不需要再设为 watcher —— 但允许重叠不报错,前端不需要做"过滤已在
members 里的 user"的校验,让后端兜底。
当前
create-with-team 是唯一的批量入口
。创建后维护 watcher 的接口暂未单独提供 —— 如果业务后续需要"在产品 /
项目编辑页面再加关心人",需要再拉一条接口(沿用现有的
add member
/
remove member
模式即可,roleId 传 watcher 角色 ID)。
取决于业务侧给
product_watcher
/
project_watcher
角色在权限管理界面绑了哪些菜单(对象域菜单
system_menu.scope_type='object'
)。建议粒度:
product_manager
/
project_manager
— 负责人
product_creator
/
project_creator
— 创建者(非负责人)
product_watcher
/
project_watcher
—
关心人
implicit_observer_product
/
implicit_observer_project
— 隐式只读 observer(不会出现在 members 列表里,后端运行时兜底返回)
完全等价于传空数组。
watcherUserIds
是可选字段 —— 不写 / 写 null / 写
[]
都不会落任何 watcher 记录,接口正常完成。
当前没有上限校验。从产品语义看,关心人本来就是"少量长期看的人",前端可以加个软性提示(如超过 20 人时提示考虑用方向级 visibility_config 配置),但不阻塞提交。
看不到。工时是敏感数据,按对象内身份控制:协办人只看自己填报的工时;任务/项目负责人可看团队成员工时。 watcher 既不是协办人也不是负责人,工时列表对其不可见 。
| 维度 | 关心人 watcher | 用户可见性配置 visibility_config |
|---|---|---|
| 粒度 | 单个产品 / 项目 | 整个方向 or 全局(all / directions) |
| 谁来加 | 产品 / 项目创建人在新建表单里加 | 系统管理员在用户管理 / 可见性配置页加 |
| 典型场景 | 外部顾问盯几个产品 / 长期评审人 | 跨方向工程师看一片方向的所有产品 / 技术支持组协调全部 |
| 底层存储 |
rdms_user_object_role
(一条对象成员记录)
|
system_user_visibility_config
(一条用户级配置)
|
GET /system/user/list-all-simple
watcherUserIds: number[]
,可空 / 可省略