多园区改造测试全景:从登录到业务隔离的 7 大模块验证清单

多园区改造真正难的不是写代码,而是怎么证明它没漏。登录态、菜单、角色、同步链路、业务数据、回归兼容——任何一个模块漏测,都可能在生产里埋雷。

本文把改造测试要覆盖的 7 大模块、3 个优先级、5 条上线前最后检查完整拆开。无论你是第一次做多园区改造,还是已经在做第 N 次,这套清单都能直接复用。

🎧 文章导读

🎵 背景音乐

多园区改造 7 大测试模块一览

图1:从登录到业务隔离的 7 大测试模块全景

一、目标与边界

1.1 本轮测试要确认的三件事

多园区改造后,所有跨园区逻辑都要”按当前园区生效”,但不能把 A 园区的数据、角色、设备下发到 B 园区。本轮重点确认:

  1. 登录用户能看到、能切换的园区范围正确。
  2. 切换园区后,角色、菜单、人员、业务列表都按当前 park_id 生效。
  3. 钉钉同步、sys_user_parkpass_user、设备下发状态没有因为多园区改造产生脏数据或跨园区下发。

1.2 不测什么

明确不测的边界,让团队对齐预期:

不测项 原因
全量性能压测 本轮只做关键列表分页和批量同步的抽样观察
历史业务字段全覆盖 只看多园区相关字段是否影响原有主流程
自动化测试框架新建 本轮以接口 + SQL + 前端手工验证为主

没有”测全”的项目,只有”够用 + 关键路径有保障”的项目。多园区改造的测试,第一优先级永远是越权和跨园区下发

二、测试前准备:环境、账号、SQL 自检

2.1 环境和基础数据

启动 service-gatewayadmin-servicethrough-service,按需再起 property-servicesecurity-serviceenv-service。dev 环境走网关请求时带 devDebug=1

1
http://127.0.0.1:<gateway-port>/gateway/admin/xxx?devDebug=1

准备两个有效园区 + 一个租户:

标识 含义 要求
PARK_A 主测园区 A 有用户、角色、设备/业务数据
PARK_B 对照园区 B 有不同用户、角色、设备/业务数据
TENANT_1 当前租户 两个园区同租户,方便排除租户变量

准备测试账号矩阵:

用户 数据要求 用途
super_admin 可看全部或管理多个园区 验证管理端全量能力
admin_a 只属于 PARK_A 验证单园区隔离
admin_b 只属于 PARK_B 验证单园区隔离
admin_ab 同时属于 PARK_A、PARK_B 验证园区切换
normal_a PARK_A 普通用户 验证无管理权限场景
ding_user 钉钉同步用户 验证 sys_user_parkpass_user

准备角色矩阵:

角色 数据要求 用途
role_a sys_role.park_id = PARK_A A 园区角色
role_b sys_role.park_id = PARK_B B 园区角色
role_manage_ab 所属园区为 PARK_A,管理园区包含 PARK_B 验证角色管理园区
role_global_or_empty 如项目存在空园区/通用角色 验证兼容历史数据

2.2 测试前的 SQL 自检清单

测试前先跑这 4 条 SQL,预期前 3 条返回 0 行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-- 1. 用户所属园区关系不能重复
SELECT user_id, park_id, COUNT(*) cnt
FROM sys_user_park
WHERE delete_flag = '0'
GROUP BY user_id, park_id
HAVING cnt > 1;

-- 2. 用户角色同一园区内不能重复
SELECT user_id, role_id, park_id, COUNT(*) cnt
FROM sys_user_role
WHERE delete_flag = '0'
GROUP BY user_id, role_id, park_id
HAVING cnt > 1;

-- 3. 用户角色指向的园区应存在
SELECT ur.user_id, ur.role_id, ur.park_id
FROM sys_user_role ur
LEFT JOIN sys_park p ON p.park_id = ur.park_id AND p.delete_flag = '0'
WHERE ur.delete_flag = '0'
AND ur.park_id IS NOT NULL
AND ur.park_id <> ''
AND p.park_id IS NULL;

-- 4. pass_user 的园区不能为空(除非明确是历史占位数据)
SELECT user_id, user_name, phone, park_id, delete_flag, dist_sync_status
FROM pass_user
WHERE delete_flag = '0'
AND (park_id IS NULL OR park_id = '');

第 4 条如有历史数据,要先标记出来——后续测试不要混入这些账号,否则测试结果会被脏数据污染。

三、7 大测试模块拆解

A. 登录与当前园区上下文

目的:登录态下的园区上下文必须可信,切换园区不能错位。

用例 操作 预期
A1 单园区用户登录 admin_a 登录并查 /user/info 当前园区为 PARK_A;园区列表只包含 PARK_A;不出现 PARK_B 的角色、园区名、业务数据
A2 多园区用户登录 admin_ab 登录 园区列表包含 PARK_A、PARK_B;默认园区有明确规则,不能随机跳;当前园区字段、角色列表、菜单列表三者一致
A3 切换园区 admin_ab 切到 PARK_B,再切回 PARK_A 切到 PARK_B 后只返回 PARK_B 下生效的角色和数据;切回 PARK_A 后恢复 PARK_A 的角色和数据;刷新页面后当前园区不丢失
A4 非授权园区切换 admin_a 调用切换接口传 PARK_B;用不存在/已删除的 park_id 再调一次 接口失败;当前园区不变;不新增 sys_user_rolesys_user_park、缓存上下文等脏数据

A4 是越权拦截的关键用例。如果非授权 parkId 没被拦下,会被切园区到其他园区——这是上线后最容易出安全事件的入口。

B. 用户所属园区

用例 关键校验
B1 用户列表展示所属园区 userParkIdsuserParkName 与权限范围一致;多园区用户的园区名称和 ID 顺序一致;PARK_B-only 用户不会因为无关角色混进 PARK_A 结果
B2 编辑用户所属园区 normal_a 增加 PARK_B 后 sys_user_park 出现两条有效关系;移除 PARK_B 后软删;重复保存不会产生重复有效行
B3 删除或停用用户 停用用户不能登录;相关有效关系不再让该用户进入园区;不误删其他用户的 sys_user_parksys_user_role

C. 角色管理园区

用例 关键校验
C1 创建/编辑角色所属园区 sys_role.park_id = PARK_A;PARK_B 普通管理员不可见或不可操作
C2 配置角色管理园区 sys_role_park 有唯一有效的 (role_id, PARK_B);重复保存不产生重复行;删除后接口不再返回 PARK_B
C3 角色列表字段 所属园区和管理园区不混淆;搜索和分页不会破坏园区过滤;软删园区或软删角色不出现在有效结果中

D. 用户角色分配

这是最容易出问题的模块。多园区用户的角色关系要按 (user_id, role_id, park_id) 区分,否则切园区时菜单会乱套。

用例 关键校验
D1 单园区分配角色 只新增/更新 (normal_a, role_a, PARK_A);PARK_B 没有被写入同角色关系
D2 同一用户多园区分配同一角色 sys_user_roleuser_id + role_id + park_id 区分;切 PARK_A 只加载 PARK_A 的角色,切 PARK_B 只加载 PARK_B 的角色
D3 清空某园区角色 admin_ab 调用角色保存接口,parkId = PARK_A,角色列表为空——只清空 PARK_A 的角色,PARK_B 不受影响
D4 非授权园区角色分配 接口拒绝;不写入 sys_user_role;错误提示可定位到无权限或园区不合法

D3 是验证”清空不影响其他园区”的关键。如果代码里写错 DELETE WHERE user_id = ?(不带 park_id),整个用户的所有园区角色都会被清空,这是上线后常见的回归 bug。

E. 园区管理员列表

用例 关键校验
E1 查询某园区管理员 PARK_A 只返回 sys_user_role.park_id = PARK_A 的有效用户角色;已删除用户、已删除角色、孤立 user_id 不返回;排序按绑定时间倒序
E2 手机号/用户名展示 手机号如有加密,接口返回前端期望的明文或脱敏格式;角色名、园区名和 ID 对得上

F. 钉钉同步到用户园区

钉钉同步是”数据进入系统的入口”,错了之后下游所有模块都会脏。

用例 关键校验
F1 有部门用户同步 sys_user_park.park_id = PARK_Asys_user_park.org_id 存公司级组织 ID;重复同步不产生重复有效关系
F2 多部门/跨园区用户同步 用户能得到 PARK_A、PARK_B 两个有效园区关系;登录后可切换两个园区;每个园区下角色仍按 sys_user_role.park_id 决定
F3 无法识别园区的钉钉用户 不写错误的默认 park_id;不生成空 park_idsys_user_park;日志能说明跳过原因

G. sys_user 到 pass_user 同步

这是设备下发的源头。如果 pass_user.park_id 错了,门禁/车行下发就会拿错人员。

用例 关键校验
G1 新用户同步到 pass_user 新增 pass_user.park_id = PARK_Adelete_flag = '0';需要下发设备时 dist_sync_status = 'PENDING'
G2 已存在 pass_user 更新 只更新发生变化的字段;园区变更能同步到 pass_user.park_id;需要重新下发时才标记 PENDING
G3 多园区用户 pass_user 策略 一人一条时必须有明确主园区;一人多园区时必须每条能区分园区;不允许同一设备下发拿错园区人员
G4 软删用户同步 pass_user 按项目约定软删或停用;已软删记录不被下发任务重新捞起;findByDistSyncStatus('PENDING') 不返回 delete_flag != '0' 的人员

G3 在产品未定义一人多园区下发策略时,要标为待确认,不要强行验收。强行写死策略反而会埋雷。

H. 业务数据园区隔离

每个已改造业务模块至少抽 2 个列表、1 个新增、1 个详情、1 个编辑。优先测经常被首页或大屏使用的接口。

业务数据园区隔离的多服务矩阵

图2:admin/through/property/security/env 各业务的园区隔离验证矩阵

用例 关键校验
H1 列表隔离 PARK_A 只返回 A 数据;PARK_B 只返回 B 数据;不传 park_id 时以后端当前园区为准,不让前端伪造越权
H2 新增数据自动带当前园区 新增记录的 park_id 等于当前园区;前端传其他 park_id 时,后端按权限规则覆盖或拒绝,不能越权写入
H3 详情/编辑/删除越权 在 PARK_A 下拿 PARK_B 数据 ID 调详情 → 不可见或返回无权限;编辑、删除失败;数据库无变化
H4 DEFAULT_PARK_ID 回归 重点检查 env、security、through 里历史写死 DEFAULT_PARK_ID 的入口;走新增或同步入口,查数据库实际 park_id;已改造入口不再写死默认园区

I. 前端页面联调

前端是用户接触的第一线,错位显示(菜单列表与数据不一致)会直接被业务方发现。

用例 关键校验
I1 顶部园区切换器 切换器显示当前园区;列表自动刷新或提示刷新;刷新后当前园区符合产品约定
I2 用户/角色页面 字段展示完整,不错位;下拉园区来源符合接口返回;保存后页面立即反映最新关系
I3 无权限提示 前端不展示无权限按钮或页面;后端仍然拒绝越权请求;提示不暴露敏感数据

J. 回归测试

用例 关键校验
J1 原单园区流程 操作路径和改造前一致;不因为多园区字段必填导致老流程失败
J2 历史空 park_id 数据 产品明确允许的通用数据仍可用;不允许的空园区数据不应出现在普通园区视图;需要清洗的数据单独列清单
J3 分页和搜索 count 和列表使用同一园区过滤条件;搜索不会绕过园区过滤;排序稳定

四、测试优先级

优先级 模块 失败影响
第一优先级 A、D、G、H3 越权或下发错误(绝对不能漏
第二优先级 B、C、E、F 管理端使用和同步准确性
第三优先级 I、J 联调体验和历史兼容

如果时间只够半天,最低限度要跑:

  1. admin_a 不能切 PARK_B。
  2. admin_ab 切 PARK_A/PARK_B 后菜单和业务列表变化正确。
  3. 给同一用户分别保存 PARK_A、PARK_B 角色,互不覆盖。
  4. 清空 PARK_A 角色不影响 PARK_B。
  5. 钉钉同步用户能写入 sys_user_park
  6. pass_user 没有空 park_id,新增或变更用户的下发状态符合预期。
  7. PARK_A 下不能详情、编辑、删除 PARK_B 数据。

五、上线前最后检查 SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 1. 重复用户园区关系
SELECT user_id, park_id, COUNT(*) cnt
FROM sys_user_park
WHERE delete_flag = '0'
GROUP BY user_id, park_id
HAVING cnt > 1;

-- 2. 重复用户角色关系
SELECT user_id, role_id, park_id, COUNT(*) cnt
FROM sys_user_role
WHERE delete_flag = '0'
GROUP BY user_id, role_id, park_id
HAVING cnt > 1;

-- 3. 角色管理园区重复
SELECT role_id, park_id, COUNT(*) cnt
FROM sys_role_park
WHERE delete_flag = '0'
GROUP BY role_id, park_id
HAVING cnt > 1;

-- 4. 有效 pass_user 空园区
SELECT user_id, user_name, phone, park_id, dist_sync_status
FROM pass_user
WHERE delete_flag = '0'
AND (park_id IS NULL OR park_id = '');

-- 5. 可能被园区切换拦住的历史用户角色
SELECT ur.user_id, ur.role_id, ur.park_id
FROM sys_user_role ur
WHERE ur.delete_flag = '0'
AND ur.park_id IS NOT NULL
AND ur.park_id <> ''
AND NOT EXISTS (SELECT 1 FROM sys_role r WHERE r.role_id = ur.role_id AND r.park_id = ur.park_id AND r.delete_flag = '0')
AND NOT EXISTS (SELECT 1 FROM sys_role_park rp WHERE rp.role_id = ur.role_id AND rp.park_id = ur.park_id AND rp.delete_flag = '0')
AND NOT EXISTS (SELECT 1 FROM sys_user_park up WHERE up.user_id = ur.user_id AND up.park_id = ur.park_id AND up.delete_flag = '0');

预期:上线前 1、2、3、4 返回 0 行;第 5 条如果有结果,要么补 sys_role_park,要么补 sys_user_park,要么确认这些历史角色关系应清理。

六、经验总结

6.1 三个最容易漏的测试场景

  1. D3 清空某园区角色:很多团队会测”加”,但不测”清空”。”清空”代码路径通常用 DELETE WHERE user_id = ?,忘记带 park_id 就会清掉所有园区。
  2. G3 多园区用户 pass_user 策略:一人一条 vs 一人多园区,下发策略完全不同,但产品常常没有明确定义。
  3. H4 DEFAULT_PARK_ID 回归:历史代码里写死的 DEFAULT_PARK_ID 是改造最大的遗留风险。

6.2 编写测试用例的三个原则

  1. 操作步骤最小化:每条用例只验证一个意图,混入多个验证点会让失败定位变难。
  2. 预期可观测:预期要写数据库字段、接口字段、缓存键值,不能写”系统正常运行”。
  3. 失败用例优先:先写”非授权切换”这种应当失败的用例,再写正常通过的用例——前者一旦通过就证明拦截有效。

6.3 测试记录模板

每轮测试记录建议结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## 测试记录 - YYYY-MM-DD

### 环境
- 分支:
- admin-service commit:
- through-service commit:
- gateway commit:
- 数据库:
- 测试账号:

### 执行结果
| 用例 | 结果 | 备注 |
| --- | --- | --- |
| A1 单园区用户登录 | 未测/通过/失败 | |
| A2 多园区用户登录 | 未测/通过/失败 | |
| A3 切换园区 | 未测/通过/失败 | |
| ... | | |

### 问题清单
| 编号 | 问题 | 复现步骤 | 影响 | 处理人 | 状态 |
| --- | --- | --- | --- | --- | --- |

### 数据清理
- 新增测试用户:
- 新增测试角色:
- 新增测试园区关系:
- 是否已清理:

七、结语

多园区改造不是”加几个字段”那么简单——它是把整个系统的数据访问维度从”全局”切成”按当前园区”。**测试要验证的不是”能不能跑”,而是”会不会跑串”**。本文的 7 大模块清单 + 3 个优先级 + 上线前 5 条 SQL,能覆盖 90% 的多园区改造回归场景。下次接到多园区改造需求,直接把这份清单贴到测试计划里就行。

多园区改造测试的最高原则:A 园区发生的事,绝不能在 B 园区看到。