Appearance
20:横切能力:租户、数据权限、远程服务、流程和数据库总表
1. 什么是横切能力
它们不属于“客户”或“合同”单一领域,却会影响所有业务:
text
租户隔离
当前用户和部门
数据权限
操作日志
附件服务
BPM 流程
远程成员、字典、企业工商信息在正式项目中,业务代码能否正确运行,经常取决于这些上下文是否存在。
2. 租户和用户上下文
常见调用:
java
TenantContextHolder.getTenantId();
UserContext.getOpenId();
saasDeptMemberServer.getPrimaryDeptId(openId);用途:
text
tenantId 数据属于哪个租户
openId 当前操作用户
creatorDepartmentId 创建人主部门
followerOpenId 产品跟进负责人
followerDepartmentId 产品跟进部门定时任务或异步线程必须显式设置并最终清理租户上下文。PublicPoolJob 的多租户循环就是示例。
3. 数据权限
客户分页和项目评估/合同分页会调用数据权限组件:
text
DataPermissionServer.applyDataPermissionFilter(resourceCode, QueryModel, userFields, departmentFields)它不是在 Java List 过滤数据,而是向 QueryModel 注入条件,让 Provider/Mapper 的 SQL 在数据库层过滤。
典型效果:
text
普通商务:只能看自己跟进的客户产品
部门负责人:可以看部门成员数据
管理员:可以看全部租户数据因此排查“接口有数据但某用户看不到”时,先检查数据权限、租户、逻辑删除,而不是立刻怀疑 SQL。
4. 远程服务组件
当前 Service 中会通过 component 或 Feign Client 调用:
| 外部能力 | 典型用途 |
|---|---|
| 认证/成员中心 | 用户姓名、部门、领导关系 |
| 租户中心 | 当前租户组织与成员归属 |
| 流程中心 | 发起审批、接收状态回调 |
| 系统/字典中心 | 字典标签、配置 |
| 集成中心 | 企业工商信息查询 |
| 文件/OSS 中心 | 上传文件预览与存储 |
写代码时必须处理远程返回为空、调用失败和重复调用。不能默认“远程服务永远成功”。
5. BPM 流程的统一模式
项目评估、合同、试单的流程形态相同:
text
业务主表保存
-> flowStatus = PENDING
-> SaasBpmModelServer.startFlow(form)
-> 回写 flowId、IN_PROGRESS、flowStartTime
-> 流程回调 updateFlowProgress(flowId, status)
-> 更新业务状态并应用领域联动规则需要注意:外部 BPM 与本地数据库不是一个普通本地事务。若本地保存成功但发起流程失败,要有明确的异常与补偿策略;若流程回调重复到达,更新逻辑要幂等。
6. 数据库总表清单
以下表来自 pginit.sql,按领域分组,覆盖当前仓库的核心业务数据:
text
客户主资料
customer_info
customer_region
customer_business_info
customer_contact
customer_invoice
customer_bank_info
customer_industry
customer_info_change_log
品牌、产品与基础资料
customer_brand
customer_brand_relation
brand_owner_relation
product
industry_dict
region
position
position_change_log
settlement_rule
产品跟进与公海
customer_product_follow
customer_product_follow_contact
customer_product_follow_contact_relation
customer_follow_record
customer_follow_record_comment
customer_follow_deadline_log
customer_product_claim_log
customer_transfer_log
customer_product_public_pool
customer_protect_configuration
customer_protect_special_config
项目评估
project_evaluation
project_evaluation_insurance_instruction
project_evaluation_risk_content
project_evaluation_service_requirement
project_evaluation_settlement_rule
project_evaluation_personnel_type
project_evaluation_advance_payment_purpose
合同
contract
contract_contact
contract_customer_relation
contract_settlement_rule
contract_advance_payment_purpose
contract_business_model
contract_number_sequence
试单与通用能力
trial_order_application
trial_order_product
sys_file
import_record表之间的外键关系很多是在业务代码中按 id 维护,未必全由数据库物理外键强制。因此 Service 的存在非常关键:它负责校验关联记录是否存在,并保证主子表事务一致。
7. 进入正式环境前必须问团队的问题
text
1. 当前环境使用 MySQL 还是 PostgreSQL?对应 DDL 版本是哪份?
2. 数据库迁移脚本如何执行、谁负责审核?
3. Maven 私服和配置中心如何认证?
4. 本地/测试环境如何设置 tenantId 与当前用户?
5. 哪些 resourceCode 对应哪些数据权限?
6. BPM 回调由哪个服务转发,如何测试?
7. 附件上传、预览和 OSS 的测试地址是什么?
8. 公海定时任务在测试环境是否允许手工触发?这些问题没有答案时,不要为了“让项目跑起来”而伪造正式租户和权限逻辑。
8. Demo 到正式项目的映射
| Demo 做法 | 正式 CRM 对应 |
|---|---|
X-Demo-User-Id 请求头 | UserContext.getOpenId() |
单数据库 tenant_id 字段 | TenantContextHolder + 租户插件 |
| 简单 ownerId 过滤 | DataPermissionServer 注入 QueryModel |
内部 /approve 接口 | SaasBpmModelServer + 流程回调 |
demo_file 表 | sys_file + OSS/文件服务 |
| 本地定时器 | PublicPoolJob + 分布式锁 + 多租户循环 |
9. 最终学习检查
当你能把下面的问题答清楚,就已经具备阅读和实现当前 CRM 小需求的能力:
- 一个客户为什么不能在有产品跟进时删除?
- 为什么产品跟进到期不一定会进入公海?
- 为什么项目评估/试单/合同都要先校验产品跟进状态?
- 为什么合同审批通过会更新客户成交日期?
- 为什么列表查询先分页主表再批量查关联表?
- 为什么 Form、DTO、Entity、VO 要拆开?
- 为什么定时任务必须清理租户上下文?
- 为什么流程回调和远程调用要考虑幂等和失败补偿?
