|
|
@@ -0,0 +1,75 @@
|
|
|
+## 架构要点
|
|
|
+
|
|
|
+### 租户识别
|
|
|
+
|
|
|
+```
|
|
|
+Request → TenantInterceptor (读 X-Tenant-Id) → TenantContext.set(tenantId) → Controller → Service → Client
|
|
|
+ ↓
|
|
|
+ Client 从 TenantContext 拿 appKey/appSecret
|
|
|
+```
|
|
|
+
|
|
|
+`TenantContext` 用 `ThreadLocal<TenantProfile>`;`TenantProfile` 包含:
|
|
|
+- `tenantId`(宜搭应用表主键)
|
|
|
+- `vendorCredentials: Map<String, VendorCredential>`(钉钉/aliwork 等)
|
|
|
+- `extraJson`(tenant 级自定义配置)
|
|
|
+
|
|
|
+### 租户配置加载
|
|
|
+
|
|
|
+**数据源**:宜搭"应用表"(formUuid 配置)。字段约定:
|
|
|
+
|
|
|
+| 宜搭字段 | 含义 |
|
|
|
+|---------|------|
|
|
|
+| `textField_tenantId` | 租户唯一标识(建议用客户代号如 `guangming`) |
|
|
|
+| `textField_vendor` | 第三方平台 key(`dingtalk` / `aliwork` / ...) |
|
|
|
+| `textField_appKey` | 该 vendor 的 appKey |
|
|
|
+| `textField_appSecret` | 该 vendor 的 appSecret(写入时走宜搭加密字段) |
|
|
|
+| `textField_corpId` | 可选(钉钉需要) |
|
|
|
+| `textareaField_extraJson` | JSON 字符串,扩展参数 |
|
|
|
+| `radioField_enabled` | `on` / `off` |
|
|
|
+
|
|
|
+**加载策略**:
|
|
|
+- 启动时全量拉取 → 内存 `Map<tenantId, TenantProfile>`
|
|
|
+- TTL 10 分钟(可配 `tenant.registry.ttlSeconds`)过期后按需刷新
|
|
|
+- 手动 `/pro/_admin/reloadTenant?tenantId=xxx`(仅 dev profile 开放)做热刷
|
|
|
+
|
|
|
+### Client 透传机制
|
|
|
+
|
|
|
+基座 `DDClient` 签名不变(仍接受 appKey/appSecret 参数),`mjava-pro` 提供一层 `DynamicDDService` 组合:
|
|
|
+
|
|
|
+```java
|
|
|
+// Service 层内部
|
|
|
+DDConf conf = TenantContext.current().credential("dingtalk").toDDConf();
|
|
|
+ddClient.listDepartmentUserDetail(conf, deptId, cursor, size);
|
|
|
+```
|
|
|
+
|
|
|
+**不做** AOP 反射修改签名 — 保持显式、可读、可调试。
|
|
|
+
|
|
|
+### Token 缓存隔离
|
|
|
+
|
|
|
+`UtilToken` 当前 key 是裸 `{vendor}:{appKey}`。多租户环境下两个客户可能共用同一 appKey(极少,但要防撞)。建议基座支持 namespace:
|
|
|
+
|
|
|
+```java
|
|
|
+UtilToken.put(tenantId, "dingtalk:" + appKey, token, expireSec);
|
|
|
+UtilToken.get(tenantId, "dingtalk:" + appKey);
|
|
|
+```
|
|
|
+
|
|
|
+若基座改动太大,**回退方案**:`mjava-pro` 约束 key 为 `{tenantId}:{vendor}:{appKey}`,不改基座。
|
|
|
+
|
|
|
+### 错误处理
|
|
|
+
|
|
|
+- tenantId 缺失 → `401 TENANT_REQUIRED`
|
|
|
+- tenantId 不存在或 disabled → `403 TENANT_NOT_FOUND`
|
|
|
+- vendor 配置缺失 → `500 TENANT_VENDOR_MISCONFIGURED` + 审计日志
|
|
|
+
|
|
|
+## 技术约束
|
|
|
+
|
|
|
+- 沿用 `mjava-baseline.md` 全部规范
|
|
|
+- `application-prod.yml` 只配宜搭应用表 formUuid / systemToken(访问宜搭自身的凭据仍是静态),不配任何客户凭据
|
|
|
+- 请求监听日志必须含 `tenantId` 字段(在 §3.5 审计字段基础上扩展)
|
|
|
+
|
|
|
+## Non-Goals(设计边界)
|
|
|
+
|
|
|
+- 不考虑租户级数据库隔离(所有租户共享同一 MySQL)
|
|
|
+- 不考虑租户级流量限额(QPS 限流等;有需求再单独提案)
|
|
|
+- 不考虑跨租户工作流(暂时假设每次请求单租户上下文)
|
|
|
+- 不考虑 Redis 分布式缓存(单实例 TimedCache 足够 Phase 1)
|