# 架构总览（abacus 动静分离微服务）

> 权威来源：`docs/architecture.md` · 编码约束见 `docs/coding-standards.md` · AI 工作流见 `docs/agent-guide.md`

## 1. 总体形态：前后端分离 + 动静同包发布

两个仓库协作，**共享一份 docs/ 文档中心**（本目录）：

```
D:\workBuddySpace\member\
├── docs/                       ← 团队文档单一权威（本目录）
├── abacus-static-framework/    ← Vue 3 前端底座（src/framework 只读 / src/subsystem 业务可写）
└── abacus.springboot.example/  ← Spring Boot Maven 微服务模板（业务写在 src/main/java 与 resources/sql）
```

交付链路（动静分离）：

```
前端源码 ──npm run build:prod──► 产物输出到后端 resources/html/vue/<应用名>/
                                     │
后端 mvn package（assembly.xml）     │
                                     ▼
              *-bin.zip = jar + doc + sql + html  ← 一个发布单元，一起发版
```

- 前端产物**不打进 jar**，随 zip 的 `html/` 目录部署（Nginx/容器托管静态资源，Java 进程提供 API）。
- 开发期前端 `vite.config.ts server.proxy` 反代后端，替代 Nginx 联调。

## 2. 前端底座（abacus-static-framework）

| 项 | 选型 |
|---|---|
| 框架 | Vue 3.5 + TypeScript 5 + Vite 6 |
| UI | Element Plus（通用）+ vxe-table（仅表格） |
| 状态 | Pinia |
| 代码生成 | unplugin-auto-import / unplugin-vue-components（自动引入，无需手写常见 import） |

关键机制：

- **只读/可写分区**：`src/framework/**` 只读共享；业务只写 `src/subsystem/**`（api / router / views / extend 及自建 components / hooks / store / styles / types / utils）。
- **请求管线**（`framework/utils/request.ts`）：统一响应体解包、服务别名映射、token 自动刷新、commonParam 审计头——业务无感，详见 `coding-standards.md §6`。
- **动态菜单权限**：`permissionStore.filterAsyncRoutes` 按 `url === path` 匹配后端菜单与 `subsystem/router/dynamicRouter.ts`，过滤后 `addRoute`。
- **运行时配置**：`window.frameBaseConfig`（含 `backendServices` 服务别名映射）由宿主注入，`public/data/data.js` 声明当前子系统（`currentSubsystem` / `currentSubsystemName`）。

## 3. 后端模板（abacus.springboot.example）

| 项 | 选型 |
|---|---|
| 构建 | Maven，父工程 `abacus.framework:abacus.springcloud.pom:1.5.0`，artifactId `abacus.springboot.<应用名>` |
| 框架 | Spring Boot + abacus 系列（config 2.1.0 / commons 1.6.5 / fundation 3.4.4 / business.service / asyncTool 1.0.0） |
| 注册/配置中心 | Nacos（bootstrap.yml，extension-configs 引入 abacus-database / discovery / acas / redis / actuator / xxljob 公共配置） |
| 数据库 | MySQL / SQL Server(jtds+mssql) / Oracle / 达梦，Druid + JPA；多数据源由 Nacos `abacus-database.yml` 提供 |
| 定时任务 | XXL-JOB 2.3.0（application-xxljob.yml） |
| 消息队列 | Kafka（配置经 Nacos 公共配置） |
| 接口文档 | springdoc（`springdoc.packages-to-scan`） |
| 监控 | Actuator + Prometheus（base-path `/services/actuator`） |

分层（包前缀 `abacus.springboot.<应用名>.`）：`api(+api/view) / controller / dao / esb / pi / impl / repository / wsi / config / util / vo`，职责与扫描配置见 `coding-standards.md §5.1`。

统一响应体 `{status, message, data, errorCode}` 由 `abacus.commons` 的 `ResultResponseBodyWrapper` 包装（`Application.java` 注册 `@Bean`，`abacus.responseExcludePath` 排除白名单 URI）。

## 4. 关键一致性：APP_NAME 四处

应用名（APP_NAME）必须同时一致于：① `bootstrap.yml spring.application.name`（Nacos 注册）② `application.yml` 扫描包前缀（`abacus.controllerPackages` / `abacus.entitypackages` / `springdoc.packages-to-scan`）③ 前端 `api/<别名>/*.ts` 的 URL 首段 ④ `vite.config.ts build.outDir` 目录名。漏配任一处将**静默失败**，排查见 `coding-standards.md §3`。

## 5. 接口契约速览

| 契约项 | 约定 |
|---|---|
| 响应体 | `{status, message, data, errorCode}`；`status===200` 前端自动解包 data |
| 异常码 | 业务首位 `9`、系统首位 `5` |
| URL | 首段=服务别名 → `backendServices` 映射真实服务名 |
| 分页 | 响应 `{data, total}`（vxe-grid 的 result/total） |
| Excel | ArrayBuffer 响应不解包 |
| 鉴权 | Bearer token；<10min 自动刷新 + 并发队列 |

## 6. 治理体系

- 前端仓库：`.project.agents/`（CLAUDE.md / AGENTS.md / SELF_CONSTRAINTS.md / VIBECODING_GUIDE.md / docs/context/{PRD,ARCHITECTURE,ROADMAP,CONVENTIONS,UIUX}.md）
- 后端仓库：`.project.agents/`（同构，无 UIUX.md）
- 单源化：共享约束只存在于 `docs/`，仓库内 CONVENTIONS 用指针引用，禁止复制正文。

## 7. SaaS 多租户架构（新增 · 默认形态）

> 所有基于本底座的业务系统**默认按多租户 SaaS 形态交付**。本节定义租户隔离策略、前后端协作方式与扩展能力；编码强制约束见 `coding-standards.md §9`。

### 7.1 多租户隔离策略与选择理由

**结论：行级隔离（`tenant_id` 列）为主，Schema/库级隔离为大型/合规租户的升级路径，统称"混合分层隔离"。**

| 隔离级别 | 实现方式 | 适用对象 | 成本 / 风险 |
|---|---|---|---|
| **行级（默认）** | 每张业务表含 `tenant_id` 列；查询统一附加 `tenant_id = :tenantId` | 绝大多数租户（试用 / 标准版） | 成本低、弹性好，单实例支撑数千租户；**需严防漏写过滤**（§9 强制） |
| Schema 级 | 同一 DB 实例下每租户独立 schema；经动态数据源路由 | 数据敏感 / 合规客户 | 隔离强、成本中；DDL 维护量上升 |
| 库级 | 每租户独立数据库实例 + 独立连接池 | 大型企业 / 集团、数据驻留要求 | 隔离最强、成本最高；运维复杂 |

**选择理由（为什么行级为主）：**
- 底座已具备 **Druid + JPA + 多数据源**（Nacos `abacus-database.yml` 的 `enableMultiSource` / `multidb0~9` / `defaultDB` / `readDB` / `dynamicRelation`）——该能力可直接复用作"Schema / 库级租户的路由底座"，无需另起炉灶。
- 行级隔离与现有 `JpaRepository` / `JpaSpecificationExecutor`、原生 SQL（`pi/impl`）天然契合，可**不动表结构范式**落地；AI 生成代码能自动带上租户过滤（见 §9）。
- 租户扩展时，通过 `tenant` 表的 `isolation_type` + `datasource_key` 字段，将"标准租户"逐步**升级**为 Schema / 库级，业务代码无需重构（查询仍走同一 `TenantContext`，仅数据源路由不同）。

**核心主数据表（新增，属平台租户，可豁免行级过滤但须标注 `@PlatformTenantOnly`）：**
- `tenant`（租户主数据）：`id`(业务主键 / DaoIdGenerator) / `tenant_code`(唯一，如 `acme`) / `name` / `tier`(trial / standard / enterprise) / `isolation_type`(row / schema / db) / `datasource_key`(指向 Nacos 数据源键) / `status`。
- `tenant_config`（品牌 / 主题 / 模块）：`tenant_id` / `logo_url` / `primary_color` / `product_name` / `theme` / `subscribed_modules`(逗号分隔功能码)。
- `tenant_quota`（配额）：`tenant_id` / `max_users` / `storage_bytes` / `api_qps` / `feature_flags`。

> 建表须遵循 §5.8：`sql/table/{mysql,sqlserver}.sql` 双份同步 + `upgrade.xml` 追加版本段。

### 7.2 前端设计（按登录租户动态加载）

- **租户上下文来源**：登录成功后，认证返回 JWT（claims 含 `tenantId` / `tenantCode` / `roles`）与 `tenantConfig`。前端建立 `subsystem/store/tenant.ts`（`useTenantStore` Pinia），保存 `tenantId` + `tenantConfig`，与 token 一并持久化到 localStorage。
- **品牌与主题动态化**：`framework/utils/theme.ts` 提供 `applyTenantTheme(config)`——将 `primary_color` 写入 Element Plus 运行时主题（`--el-color-primary` 及派生 6 档明度），替换 logo（`tenantConfig.logo_url`）、产品名（`product_name` 注入标题 / 页脚）。登录后调用一次，租户切换时重调。
- **功能模块与菜单**：`permissionStore.filterAsyncRoutes` 在原有"权限过滤"基础上叠加**租户模块门禁**——仅当菜单所属模块的 feature code ∈ `tenantConfig.subscribed_modules` 时才保留（即"有权限但租户未订阅 → 不可见"）。
- **租户切换（运营 / 平台管理员）**：`useTenantStore.switchTenant(code)` → 重新拉取 `tenantConfig` → `applyTenantTheme` → 重新 `filterAsyncRoutes` → 刷新菜单。
- **请求附带租户标识**：扩展 `framework/utils/request.ts`，在拦截器中自动附加 `X-Tenant-Id` 头（取自 `useTenantStore.tenantId`），与 token 一并发送；**业务代码不得手写租户头**。

### 7.3 后端设计（识别、隔离、缓存、异步全链路）

- **租户识别（token 为准）**：以 JWT 中的 `tenantId` 为数据隔离的**唯一真相源**；`X-Tenant-Id` 头仅作下游服务透传 / 审计用，不单独作为隔离依据（防越权）。跨租户运营操作须走独立高权限接口（如 `@CrossTenant` 标注 + 二次鉴权）。
- **TenantContextHolder**：`ThreadLocal<TenantContext>`（`tenantId` / `tenantCode` / `tier` / `isolationType` / `dataSourceKey`）。由 `TenantInterceptor`（`HandlerInterceptor.preHandle`）在请求入口从 `Authentication` 取出并写入，`afterCompletion` 中 `remove()` 防泄漏。
- **查询天然带租户过滤（核心）**：
  - JPA：在 `dao` 实体启用 Hibernate `@Filter(name="tenantFilter", condition="tenant_id = :tenantId")`，由 `TenantEntityManager` / `OpenSessionInView` 拦截器统一激活并填入 `:tenantId`；或提供基类 `TenantRepository<T> extends JpaRepository<T,String>, JpaSpecificationExecutor<T>`，所有 `findByXxx` 自动叠加 `equal("tenantId", currentTenant())`。
  - 原生 SQL（`pi/impl`）：**每条查询必须** `... AND t.tenant_id = ?`，参数取自 `TenantContextHolder.getTenantId()`，沿用 §5.4 的 `?` 参数化（I2）。
  - 硬规则：数据访问方法**禁止省略租户条件**；跨租户场景必须显式 `@CrossTenant` 且经审批。
- **缓存隔离（Redis）**：全部缓存 key 加前缀 `tenant:{tenantId}:<原key>`（`TenantCacheKeyGenerator`）。漏加前缀会跨租户串数据，属高危（P0）。
- **异步上下文传播**：
  - `@Async`：`TenantTaskDecorator`（实现 `TaskDecorator`）将父线程 `TenantContext` 传给子线程。
  - Kafka：Producer 打 `tenantId` 消息头；Consumer 在处理器开头据消息头重建 `TenantContext`。
  - XXL-JOB：分片 / 参数携带 `tenantId`；Job Handler 处理每个租户数据前先 `setTenantContext`，且任务本身需"多租户感知"（按租户遍历或参数指定）。
- **ID 与数据源**：`DaoIdGenerator` 全局唯一（prefix + 日期 + 流水），跨租户不冲突；`tenant_id` 作为行属性而非主键一部分。Schema / 库级租户经 `dynamicRelation` 路由到其 `datasource_key` 对应数据源。

### 7.4 前后端协作方式（端到端）

```mermaid
sequenceDiagram
    participant U as 用户
    participant FE as 前端(Vue)
    participant Auth as 认证/登录
    participant BE as 后端(微服务)
    participant DB as 数据库/缓存
    U->>FE: 登录(账号密码)
    FE->>Auth: POST /login
    Auth-->>FE: JWT(tenantId,tenantCode,roles) + tenantConfig
    FE->>FE: useTenantStore 保存 + applyTenantTheme + filterAsyncRoutes(权限 ∩ 模块)
    U->>FE: 业务操作
    FE->>BE: 请求 + Authorization:Bearer + X-Tenant-Id
    BE->>BE: TenantInterceptor → TenantContextHolder
    BE->>DB: 查询自动带 tenant_id=? / Redis key=tenant:{id}:...
    DB-->>BE: 仅本租户数据
    BE-->>FE: {status:200, data: 本租户数据}
```

- **契约要点**：前端无需、也不应感知其他租户；所有隔离在服务端完成。URL / 业务参数中**不携带** `tenantId`（tenant 来自 token / 头）。接口契约补充见 §6。
- **故障隔离**：任一层漏写租户条件由 §9 强制检查 + 评审拦截；缓存 / 异步串租户属 P0 事故。

### 7.5 扩展性（性能 / 计费 / 配额 / 自动化开通）

- **性能**：每张租户表建**复合索引 `(tenant_id, ...)`**；按 `tier` 配置连接池（企业版走独立 `datasource_key` 专属池）；大表按 `tenant_id` 分区；读副本按租户流量分配。
- **计量计费**：新增 `billing` 能力（超出示例模板范围，给出设计钩子）——业务流通过 Kafka 异步发送 `tenant.usage` 事件（API 调用数 / 存储字节 / 活跃用户 / 功能用量），`usage-aggregation` 任务按月汇总生成账单。计量必须**异步、非阻塞**，不侵入主流程。
- **配额管理**：`tenant_quota` 表 + `QuotaService` / `QuotaInterceptor`，在"影响配额的操作"（建用户、上传、大批量导出）前校验；超出返回 `9xxx` 业务异常（§6.2）。
- **自动化开通（Provisioning）**：`esb/TenantProvisioningService` 由运营接口 / `tenant.signup` Kafka 事件触发，**幂等 + 可重试（XXL-JOB 兜底）**：
  - 行级：插 `tenant` + 种子默认管理员 + 赋默认模块订阅 + 注册菜单 / 模块数据（沿用 `sql/data/` 模式）+ 默认配置。
  - Schema / 库级：经专属数据源执行建 schema / DB + 基线 DDL（mysql / sqlserver 双份）+ 种子数据；在 Nacos 注册该租户的数据源键。
  - 完成后发 `tenant.provisioned` 事件。

### 7.6 落地实现建议（映射到 abacus 分层）

**后端新增（包前缀 `abacus.springboot.<应用名>.`）：**
- `config/TenantConfig.java`：注册 `TenantInterceptor`、`TenantContextHolder`、`TenantTaskDecorator`、`TenantCacheKeyGenerator`、Hibernate tenant filter。
- `dao/`：`Tenant`、`TenantConfig`、`TenantQuota`（均 `@Entity`；主数据表本身由平台租户管理，可豁免过滤但需标注 `@PlatformTenantOnly`）。
- `api/TenantController.java`（`/tenant/config` 返回品牌 / 主题 / 模块）、`controller/` 下运营开通接口。
- `esb/TenantBizService.java`、`esb/TenantProvisioningService.java`。
- `pi/TenantAccess.java`（原生 SQL 含 `tenant_id = ?`）。

**前端新增：**
- `subsystem/store/tenant.ts`（`useTenantStore`）。
- `framework/utils/theme.ts`（`applyTenantTheme`）。
- 扩展 `framework/utils/request.ts`：自动附加 `X-Tenant-Id`，登录后拉取并缓存 `/tenant/config`。
- 扩展 `permissionStore.filterAsyncRoutes`：叠加模块门禁。

**AI 生成代码时**：涉及任何数据访问，`coding-standards.md §9` 强制要求带上租户过滤；新业务表 DDL 必须含 `tenant_id` 列（行级默认）。
