一篇偏架构视角的对照分析:同一套组织语义,如何通过运行模式配置在三种隔离策略间切换,各自适用的场景、付出的代价,以及从一种形态演进到另一种形态时要做哪些准备。
在企业数字化里,组织结构从来不是一张通讯录——它是流程找人、权限隔离、低代码应用分域的共同地基。人员、部门、角色,以及它们之间的对应关系,决定了系统「谁能看见什么、谁能审批什么」。
该 BPM 平台将这套语义持久化在 Port_* 系列数据表中,实体类统一归属 BP.Port 命名空间。五张核心表(Port_Dept、Port_Emp、Port_Station、Port_DeptEmp、Port_DeptEmpStation)撑起全部组织语义;在此基础上,该平台用一个配置项切换三种截然不同的运行世界:
配置值 | 枚举 | 俗称 | 一句话比喻 |
|---|---|---|---|
0 | RunModel.Single | 单组织版 | 一个家,一位家长 |
1 | RunModel.GroupInc | 集团版 | 一个生产队,队长管多户人家 |
2 | RunModel.SAAS | 多租户版 | 联合国开大会,各国互不隶属 |
这不是三套产品,而是同一套引擎、同一套表结构、三种隔离策略——从单机部署到集团管控,再到 SaaS 商业化,改配置即可演进,不必推倒重来。
RunModel运行模式在源码中定义为枚举 RunModel(BP.Sys.EnumLab):
public enum RunModel
{
Single, // 单机版 / 单组织
GroupInc, // 集团模式
SAAS, // 多租户模式
PASS // 平台应用模式(扩展)
}系统通过 appsettings.json / app.config 中的 RunModel 键读取,逻辑集中在 SystemConfig.RunModel:
配置值 | 生效模式 |
|---|---|
0 或 3 | 单组织(Single) |
1 | 集团(GroupInc) |
2 | 多租户(SAAS) |
设计要点:全站数百处业务分支——人员编号规则、管理员判定、数据查询过滤、枚举共享——均读取这一开关。组织隔离不是某个模块的「补丁」,而是贯穿 BP.En30 引擎层的横切能力。
Port_* 与 BP.Port无论哪种运行模式,组织结构的核心语义不变:
主体 | 关系 | 客体 | 连接键 |
|---|---|---|---|
Port_Org | 一对多 | Port_Dept | OrgNo |
Port_Dept | 一对多 | Port_Emp | FK_Dept |
Port_Dept | 一对多 | Port_DeptEmp | FK_Dept |
Port_Emp | 一对多 | Port_DeptEmp | FK_Emp |
Port_Station | 一对多 | Port_DeptEmpStation | FK_Station |
Port_Dept | 一对多 | Port_DeptEmpStation | FK_Dept |
Port_Emp | 一对多 | Port_DeptEmpStation | FK_Emp |
Port_Org | 一对多 | Port_OrgAdminer | OrgNo |
表名 | 实体类 | 职责 |
|---|---|---|
Port_Dept | Dept | 部门树,ParentNo + OrgNo |
Port_Emp | Emp | 人员主档,含主部门 FK_Dept |
Port_Station | Station | 角色(岗位) |
Port_DeptEmp | DeptEmp | 部门—人员(支持兼职) |
Port_DeptEmpStation | DeptEmpStation | 部门—角色—人员(流程选人核心) |
Port_Org | Org | 组织/租户单元 |
Port_OrgAdminer | — | 主管理员与二级管理员 |
流程引擎「按部门 + 角色找人」的 SQL 直指五表核心:
SELECT FK_Emp FROM Port_DeptEmpStation
WHERE FK_Station = '角色编号' AND FK_Dept = '部门编号'三种模式共享这套表,差异体现在 OrgNo 怎么用、账号是否全局唯一、数据能否跨组织共享。
adminadmin 统一管理组织、数据源与流程。OrgNo 隔离——WebUser.OrgNo 在单组织模式下直接返回空字符串。组织编号为空(WebUser.cs):
if (BP.Difference.SystemConfig.RunModel == RunModel.Single)
return ""; // OrgNo 属性管理员判定:admin / system 账号,或 Port_OrgAdminer 中的二级管理员,或历史 GloVar 标记均可获得管理权限。
人员编号:Emp.UserID 与 Emp.No 等价,无租户前缀:
// SAAS 模式下 UserID 可重复;单组织下 UserID 即 No
if (BP.Difference.SystemConfig.RunModel == RunModel.SAAS)
return this.GetValStringByKey(EmpAttr.UserID);
return this.GetValStringByKey(EmpAttr.No);数据查询:枚举、表单、角色等实体不附加 OrgNo 过滤条件,模型最简、集成成本最低。
单组织版 = 零隔离开销的完整 BPM。适合「我就要一套流程平台」的客户,部署快、学习曲线平。
该平台用「生产队」比喻集团版,形象且准确:
admin:相当于生产队长,可创建多个下属组织。Port_Org.Adminer 登记;可配置多位二级管理员(Port_OrgAdminer)。OrgNo 隔离,A 组织设计的流程,B 组织默认看不见。IsShare 标记,可将本组织资产共享给其他组织使用。Port_Emp.No 唯一),人员可在组织间调配,符合大型国企、控股集团的 HR 实践。组织实体(BP.WF.Port.Admin2Group.Org → 表 Port_Org):
Map map = new Map("Port_Org", "独立组织");
map.AddTBString(OrgAttr.Adminer, null, "创始人", ...); // 主管理员管理员层级(WebUser.IsAdmin / WebUser.IsAdmin2):
IsAdmin:admin 系统账号,或 Port_OrgAdminer 中当前组织的管理员,或 Port_Org.Adminer 主管理员。IsAdmin2:在 Port_OrgAdminer 中但不是 Port_Org.Adminer 主管理员 → 二级协助管理员。组织上下文注入:集团模式下登录后,WebUser.OrgNo 从 Port_Emp 按人员 No 解析:
string no = DBAccess.RunSQLReturnString(
"SELECT OrgNo FROM Port_Emp WHERE No='" + WebUser.No + "'");数据隔离 + 跨组织共享(以枚举为例,MapData.cs):
// 本组织枚举
sqlWhere = " EnumKey IN (...) AND OrgNo='" + orgNo + "'";
// 集团共享:其他组织 IsShare=1 的枚举 UNION 进来
if (RunModel == RunModel.GroupInc) {
sqlEnum += " UNION SELECT A.* FROM ... AND B.IsShare=1 AND B.OrgNo!='" + WebUser.OrgNo + "'";
}Sys_EnumMain 在集团模式下显式提供共享开关:
if (SystemConfig.RunModel == RunModel.GroupInc)
map.AddBoolean("IsShare", true, "是否共享给其他组织?", true, true);角色体系可按组织定制(Station.cs):GroupStationModel 支持「每组织独立岗责」或「每部门独立岗责」,适应不同集团管控粒度。
集团版 = 统一身份 + 分权治理 + 可选共享。一个账号走天下,各子公司流程自治,集团资产可下沉复用。
admin:平台运营方,负责开通租户(组织)。001 与租户 B 的 001 是两个人。OrgNo + 编号前缀实现硬隔离。账号双字段设计(Emp.cs):
字段 | 含义 |
|---|---|
UserID | 租户内登录名,可重复 |
No | 全局主键,格式 OrgNo_UserID |
/// 用户ID: SAAS模式下 UserID 是可以重复的.
if (RunModel == RunModel.SAAS) {
if (!value.StartsWith(WebUser.OrgNo + "_"))
this.SetValByKey(EmpAttr.No, WebUser.OrgNo + "_" + value);
}对外暴露的统一用户标识(WebUser.UserID):
if (SystemConfig.RunModel == RunModel.SAAS)
return WebUser.OrgNo + "_" + WebUser.No; // 全局唯一
return WebUser.No;组织解析按 UserID(与集团版按 No 不同):
// SAAS:按 UserID 查 OrgNo
"SELECT OrgNo FROM Port_Emp WHERE UserID='" + WebUser.No + "'"部门人员关系带 OrgNo(DeptEmp.cs):SAAS 模式下 Port_DeptEmp 查询附加 OrgNo 条件,防止跨租户兼职数据串读。
SQL 构建层适配(SqlBuilder.cs):关联 Port_Emp 时,SAAS 模式自动将外键字段映射为 UserID,保证报表与实体查询在租户语义下正确。
枚举主键租户化(SysEnumMain 设计注释):
// 如果是 SAAS 模式, No = OrgNo + "_" + EnumKeySAAS 版 = 强隔离的小国家模型。平台方一套部署服务千企,租户账号可「重名」,引擎用
OrgNo_前缀保证全局不碰撞。
维度 | 单组织 Single | 集团 GroupInc | SAAS 多租户 |
|---|---|---|---|
配置值 | 0 | 1 | 2 |
顶层管理员 | admin | 集团 admin | 平台 admin |
组织单元 | 无(逻辑单组织) | Port_Org,可多级 | Port_Org 租户 |
OrgNo 作用 | 不使用(空) | 核心隔离键 | 核心隔离键 |
人员 No 唯一性 | 全局唯一 | 全局唯一 | OrgNo_UserID 全局唯一 |
UserID 可重复 | 否(等同 No) | 否 | 是(租户内唯一即可) |
跨组织数据共享 | 不适用 | 支持(如 IsShare) | 不支持 |
组织间关系 | — | 可有层级/隶属 | 平级、互不往来 |
典型客户 | 中小企业、内网 | 国企集团、控股公司 | ISV、云 BPM 平台 |
比喻 | 一家一户 | 生产队 + 多户人家 | 联合国 + 各国 |
运行模式 | 顶层管理员 | 组织单元 | 组织间关系 |
|---|---|---|---|
单组织 Single | admin | 无(逻辑单组织) | 不适用 |
集团 GroupInc | 集团 admin | Port_Org(可多级) | 可有层级隶属,IsShare 可选共享 |
多租户 SAAS | 平台 admin | Port_Org 租户 | 平级,互不隶属、互不往来 |
方案 | 代表思路 | 典型实现 |
|---|---|---|
A. 独立库/独立 Schema | 每租户一个数据库 | 阿里云 RDS 多库、部分 OA |
B. 共享库 + 行级 tenant_id | 所有表加租户列 | Salesforce、多数 SaaS ORM |
C. 共享库 + 逻辑组织树 | 一棵大树,节点即组织 | 用友 NC、部分 ERP |
该平台走的是 「B + C 混合」 路线:
OrgNo),部署与运维成本低于独立库方案;OrgNo_ 编号前缀,解决「租户内账号可重名」的商用刚需。评价维度 | 独立库/Schema | 纯 tenant_id 行隔离 | 该平台三态方案 |
|---|---|---|---|
租户强隔离 | ★★★★★ | ★★★ | ★★★★(SAAS 前缀 + OrgNo 双保险) |
部署运维成本 | ★★ | ★★★★★ | ★★★★★(单库单实例) |
集团内资源共享 | ★★ | ★★★ | ★★★★(IsShare 等显式机制) |
从单组织平滑升级 | ★ | ★★★ | ★★★★★(改配置 + 补 OrgNo) |
集成 HR 主数据 | ★★★ | ★★★★ | ★★★★(五表与编制语义对齐) |
防串租户工程纪律 | 物理隔离 | 依赖每 SQL 带条件 | 引擎层 WebUser.OrgNo + 实体 Hidden 条件 |
跨租户账号重名 | 天然支持 | 需额外设计 | 原生支持(SAAS UserID) |
从私有化单机到集团管控再到云端 SaaS,无需 fork 代码分支。RunModel 一处配置,人员实体、管理员逻辑、枚举查询、SQL 构建器同步切换——显著降低产品线的研发与测试矩阵。
五表核心模型在三种模式下保持一致,流程选人规则、部门树、兼职与角色绑定不因升级模式而失效。实施顾问学会一套组织建模方法,即可服务不同规模客户。
001、admin 无需加随机后缀)。集团/SAAS 模式下,Emp、Station、DeptEmp 等实体的 EnMap 自动注入:
if (RunModel != RunModel.Single)
map.AddHidden("OrgNo", "=", "@WebUser.OrgNo");列表查询、表单维护默认带组织过滤,减少「忘写 WHERE OrgNo」导致的数据穿透。
Port_Org.Adminer(主管理员)+ Port_OrgAdminer(含二级)+ 系统 admin,三级权限边界在 WebUser.IsAdmin / IsAdmin2 中集中实现,便于审计与权责划分。
局限 | 说明 | 应对建议 |
|---|---|---|
共享库架构的上限 | 超大规模租户(万级以上)单库压力需分库分表 | 按 OrgNo 水平拆分;只读副本;冷热数据归档 |
引擎分支散布 | RunModel 判断分布在多文件 | 新功能优先走 WebUser.OrgNo 与实体 Hidden 约定;避免重复造隔离逻辑 |
集团 vs SAAS 概念相近 | 实施顾问可能混淆 | 牢记:集团 = 账号全局唯一 + 可共享;SAAS = 账号可重名 + 绝不共享 |
SAAS 编号前缀 | 外部系统集成时需传递完整 No | 使用 WebUser.UserID(已含 OrgNo_)作为对外主键;文档明确映射规则 |
单组织迁移 | 升级到集团需补录 OrgNo、创建 Port_Org | 提供 OrganizationAPI 同步接口与数据初始化工具 |
纯行隔离非物理隔离 | 金融等强合规场景可能要求独立库 | 支持按租户导出/独立部署实例;敏感客户可采用「一租户一实例」托管 |
┌─────────────────────────────────────┐
│ 需要 BPM 平台? │
└─────────────────┬───────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
仅一个法人实体 多法人/多子公司 对外售卖云服务
无组织隔离诉求 同一套 HR 主数据 租户互不感知
│ │ │
▼ ▼ ▼
RunModel=0 RunModel=1 RunModel=2
单组织版 集团版 SAAS 版如果你…… | 推荐模式 |
|---|---|
500 人以下企业,一套流程走天下 | 单组织 |
央企/集团,各子公司自建流程但账号统一 | 集团 |
软件公司做 BPM 云平台,客户自助开通 | SAAS |
先从单组织起步,未来可能集团化 | 单组织 → 集团(补 OrgNo,改配置) |
已集团化,要对外 SaaS 化 | 评估账号模型差异,规划 UserID 与编号迁移 |
该 BPM 平台的组织类型划分,体现的不仅是技术选型,更是一种务实的企业软件哲学:
不为一棵树砍整片森林——五表撑起全部组织语义; 不为一种客户造一个产品——一个
RunModel切换三种商业世界; 不让流程引擎绕过组织——OrgNo从登录态渗透到查询、枚举、角色与管理员判定。
从单组织的「一位 admin」,到集团版的「队长与家长」,再到 SAAS 的「联合国与各小国」——该平台用同一套 Port_* 底座,把组织结构做成了可生长、可商业化、可集成的平台能力。
这正是该平台 BP 架构在 4.3 节所要传达的核心:组织类型不是表多表少的问题,而是隔离边界与商业形态是否匹配的问题。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。