init: apes-Authon-history — 剥离历史与源码分析归档

- docs/: 源码分析报告、代码深度解析、项目说明书、独立服务化方案
- backend/: 账权模块提取说明(提取原则、文件清单、耦合度验证)
- frontend/: 前端剥离方案、剥离历史与说明、框架修改详情
This commit is contained in:
figmar
2026-08-09 13:04:04 +08:00
commit 9cbb1f7695
9 changed files with 4639 additions and 0 deletions
+670
View File
@@ -0,0 +1,670 @@
# 代码深度解析与分析过程
> 本文档记录了对账权平台(原掌渠 SaaS 云平台)前后端源码的完整深度分析过程,包括项目结构剖析、账权模块耦合度量化、设计意图逆向还原、以及独立服务化可行性论证。内容来自实际的代码走查,非概要性总结。
---
## 目录
1. [后端项目深度分析](#1-后端项目深度分析)
2. [前端项目深度分析](#2-前端项目深度分析)
3. [账权模块深度代码走查](#3-账权模块深度代码走查)
4. [耦合度量化分析](#4-耦合度量化分析)
5. [设计意图逆向分析——六大障碍的出发点与判定](#5-设计意图逆向分析六大障碍的出发点与判定)
6. [独立服务化可行性论证](#6-独立服务化可行性论证)
---
## 1. 后端项目深度分析
### 1.1 基本信息
| 项目 | 值 |
|------|-----|
| 包根 | `cn.apes.cloud` |
| 启动类 | `cn.apes.CloudApplication` |
| 框架 | Spring Boot 2.2.1 + Java 11 |
| ORM | MyBatis Plus 3.5.7 |
| 动态数据源 | dynamic-datasource 3.6.1 |
| 缓存/分布式 | Redisson 3.35.0 |
| 认证框架 | `cn.apes:apes-commons:0.0.7-SNAPSHOT`(外部 JAR |
| AI 集成 | OpenAI SDK + Groovy 脚本引擎 |
### 1.2 包结构总览
```
cn.apes.cloud
├── CloudApplication.java # 启动类
├── config/ # 配置类
│ ├── DataSourceHeaderAspect.java # 动态数据源切面(@Order(1)
│ ├── MybatisPlusConfig.java # 分页插件
│ ├── RedisConfig.java # Redisson 配置
│ ├── TenantSource.java # 多租户数据源注解
│ └── WebMvcConfig.java # 跨域 + 拦截器
├── filter/
│ └── AccessTokenFilter.java # AccessToken 开放平台认证过滤器
├── controller/ # 62 个 Controller
│ ├── UserInfoController.java # 用户管理
│ ├── PermissionController.java # 权限管理
│ ├── CustomerInfoController.java # 企业/租户管理
│ ├── SysRoleController.java # 角色管理
│ ├── SysMenuController.java # 菜单管理
│ ├── SysDeptController.java # 部门管理
│ ├── SysAccessTokenController.java # AccessToken 管理
│ ├── LoginController.java # 登录/SSO
│ ├── TraceProductController.java # 溯源产品
│ ├── Weight*Controller.java # 过磅系列
│ ├── Agent*Controller.java # AI Agent 系列(10+
│ └── ... # 其余业务 Controller
├── service/ # Service 接口
│ └── impl/ # Service 实现
├── mapper/ # MyBatis Mapper 接口
├── domain/
│ └── entity/ # 数据实体
├── util/
│ ├── OperationLogUtil.java # 操作日志工具(静态封装)
│ └── ...
├── aspect/ # AOP 切面
└── job/ # 定时任务
```
### 1.3 核心配置分析
**多数据源配置**`application.yml`):
```yaml
spring:
datasource:
dynamic:
datasource:
cloud: # 主库 — 账权、用户、企业
user: # 用户业务库
huoban: # 伙伴云集成库
plant: # 农业种植库
zx: # 其他业务库
```
数据源切换通过 `@TenantSource` 注解 + `DataSourceHeaderAspect``@Order(1)`)实现:
- 切面拦截标注了 `@TenantSource` 的类/方法
- 从 HTTP Header `X-Tenant-ID` 读取目标数据源
- 调用 `DynamicDataSourceContextHolder.push(head)` 切换
- 切面执行前先调用 `AuthContext.clean()` 清理 ThreadLocal(防串号),除非 `AccessTokenFilter` 已标记 `accessTokenVerified`
### 1.4 Controller 分类
| 类别 | 数量 | 代表 Controller |
|------|------|----------------|
| 账权类 | 8 | UserInfo, Permission, SysRole, SysMenu, SysDept, CustomerInfo, SysAccessToken, Login |
| 溯源类 | ~6 | TraceProduct, TraceBatch, TraceCode, TraceLink, TraceRecord, TraceConfig |
| 过磅类 | ~5 | WeightTicket, WeightDevice, WeightReport, WeightConfig, WeightPrint |
| AI Agent 类 | ~10 | AgentChat, AgentTool, AgentKnowledge, AgentModel, AgentPrompt, AgentFlow, AgentDatasource, AgentFile, AgentFunction, AgentSession |
| 农业类 | ~8 | Crop, Farm, Field, Sensor, Device, Irrigation, Pest, Weather |
| 其他业务 | ~25 | 套餐、订单、支付、统计、文件、消息、日志等 |
### 1.5 核心业务实体
```
用户/认证域:
UserInfo # 用户
CustomerInfo # 企业/租户
SysRole # 角色
SysMenu # 菜单
SysDept # 部门
SysAccessToken # 开放平台令牌
SysPermission # 权限项
SysSsoConfig # SSO 配置
溯源域:
TraceProduct # 溯源产品
TraceBatch # 批次
TraceCode # 溯源码
TraceLink # 追溯链路
TraceRecord # 操作记录
过磅域:
WeightTicket # 过磅单
WeightDevice # 地磅设备
AI Agent 域:
AgentModel # 模型配置
AgentPrompt # 提示词模板
AgentTool # 工具定义
AgentSession # 会话
AgentKnowledge # 知识库
```
### 1.6 定时任务
```java
@Component
public class XxlJobHandler {
@XxlJob("cleanExpiredTokens") // 清理过期 AccessToken
@XxlJob("syncTraceData") // 同步溯源数据
@XxlJob("reportWeightStats") # 过磅统计上报
}
```
### 1.7 LLM / AI Agent 平台
项目内置完整的 AI Agent 平台,核心能力包括:
- **Function Calling**Agent 可以调用注册的 Java 方法作为工具
- **Groovy 动态工具**:通过 Groovy 脚本引擎动态编译执行自定义工具逻辑,无需重启服务
- **多轮对话管理**AgentSession 管理上下文窗口
- **知识库检索**AgentKnowledge 支持向量检索(集成 Embedding)
- **ASR 语音识别**:语音输入转文本
- **多模型支持**AgentModel 可配置 OpenAI/通义千问等
- **工作流编排**AgentFlow 支持节点式编排
- **数据源接入**AgentDatasource 连接外部数据库供 Agent 查询
- **文件理解**AgentFile 支持文档解析
- **提示词模板**AgentPrompt 管理系统提示词
---
## 2. 前端项目深度分析
### 2.1 基本信息
| 项目 | 值 |
|------|-----|
| 框架 | Vue 2.7.16 |
| UI 库 | Element UI 2.15.14 |
| 状态管理 | Vuex 3.6.2 |
| 路由 | Vue Router 3.6.5(动态路由) |
| HTTP | Axios 0.21.4 |
| 构建 | Vue CLI 5.0.8 |
### 2.2 目录结构
```
src/
├── main.js # 入口:Element UI、全局组件注册
├── App.vue
├── permission.js # 路由守卫:动态权限路由
├── router/ # 静态路由 + 动态路由加载
├── api/ # API 接口模块(按业务域分文件)
├── views/ # 页面组件(85+)
├── components/ # 公共组件
├── layout/ # 布局框架
│ └── components/
│ ├── Sidebar/ # 侧边栏(动态菜单)
│ ├── Navbar/ # 顶栏
│ └── AppMain/ # 主内容区
├── store/ # Vuex
│ └── modules/
│ ├── user.js # 用户状态
│ ├── permission.js # 权限路由
│ └── ...
├── utils/ # 工具函数
│ ├── request.js # Axios 封装(拦截器)
│ ├── auth.js # Token 管理
│ ├── constUtil.js # 常量
│ └── get-page-title.js # 页面标题
├── config/ # 配置
│ └── tracingOpenKey.js # 溯源开放密钥
├── styles/ # 全局样式
└── assets/ # 静态资源
```
### 2.3 动态路由系统
路由分为静态路由和动态路由:
- **静态路由**:登录页、404 页、无权限页面
- **动态路由**:通过后端 API `GET /system/menu/routes` 获取,按角色权限动态注册
路由元信息 `meta` 包含 `menuType` 字段,区分 4 种类型:
- `menu` — 侧边栏菜单
- `button` — 按钮级权限
- `link` — 外链
- `hidden` — 隐藏路由
### 2.4 前端 API 代理配置(vue.config.js
```javascript
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
},
configureWebpack: {
name: '账权平台'
}
}
```
### 2.5 Vuex 状态管理
| Module | 职责 |
|--------|------|
| `user` | Token 存储/清除、用户信息获取、登录/登出 |
| `permission` | 动态路由生成、菜单树构建 |
| `settings` | 侧边栏折叠、主题等 UI 状态 |
### 2.6 前端 AI 相关页面
前端包含完整的 AI Agent 管理界面:
- `views/agent/chat` — 对话界面
- `views/agent/model` — 模型配置
- `views/agent/prompt` — 提示词管理
- `views/agent/tool` — 工具管理(支持 Groovy 脚本编辑)
- `views/agent/knowledge` — 知识库管理
- `views/agent/flow` — 工作流编排
- `views/agent/session` — 会话管理
- `views/agent/datasource` — 数据源管理
- `views/agent/file` — 文件管理
- `views/agent/function` — 函数管理
---
## 3. 账权模块深度代码走查
### 3.1 认证流程
整个认证流程涉及三个层次:
```
请求进入 → AccessTokenFilter(开放平台令牌)
→ DataSourceHeaderAspect(清理 ThreadLocal + 切数据源)
→ LoginAspectapes-commons 中,@Login AOP 填充 AuthContext
→ Controller 方法执行
```
#### 3.1.1 AccessTokenFilter(开放平台认证)
```java
// 文件:cn.apes.cloud.filter.AccessTokenFilter
// 职责:为外部系统提供免登录的 API 访问
// 1. 从 Header 提取 TokenX-Access-Token 或 Authorization: Bearer
// 2. 调用 SysAccessTokenService.validate() 校验
// 3. 校验通过 → 将用户/企业信息写入 Redis(模拟登录态)
// - key = token → JSONObject {id:0, nickName:"AccessToken:xxx"}
// - key = token:customer → 企业信息 JSON
// 4. 包装 request,注入 "token" header → 让后续 @Login AOP 能取到
// 5. 标记 request.setAttribute("accessTokenVerified", true)
// → DataSourceHeaderAspect 检测到此标记则跳过 AuthContext.clean()
```
关键代码片段:
```java
// 写入 Redis 模拟登录态
RBucket<JSONObject> userBucket = redissonClient.getBucket(redisToken);
userBucket.set(userObj, 1, TimeUnit.DAYS);
RBucket<JSONObject> customerBucket = redissonClient.getBucket(redisToken + ":customer");
customerBucket.set(customerObj, 1, TimeUnit.DAYS);
// 标记已验证
request.setAttribute("accessTokenVerified", true);
```
#### 3.1.2 DataSourceHeaderAspect(数据源切换 + ThreadLocal 清理)
```java
// 文件:cn.apes.cloud.config.DataSourceHeaderAspect
// @Order(1) — 在 LoginAspect 之前执行
@Around("@within(tenantSource) || @annotation(tenantSource)")
public Object exc(ProceedingJoinPoint pjp, TenantSource tenantSource) throws Throwable {
HttpServletRequest request = ...;
// 如果 AccessToken 过滤器已验证,保留其设置的认证上下文
if (request.getAttribute("accessTokenVerified") == null) {
AuthContext.clean(); // 清理上一请求残留的 ThreadLocal
}
String head = request.getHeader("X-Tenant-ID");
if (StrUtil.isEmpty(head)) head = "user"; // 默认数据源
DynamicDataSourceContextHolder.push(head); // 切换数据源
return pjp.proceed();
}
```
#### 3.1.3 LoginAspectapes-commons 内部,不可见源码)
根据 pom.xml 中的 `apes-commons:0.0.7-SNAPSHOT` 依赖,推断其行为:
- 拦截标注 `@Login` 的 Controller 类/方法
-`request.getHeader("token")` 获取 Token
- 从 Redis 读取 `RBucket<JSONObject>(token)``RBucket<JSONObject>(token:customer)`
- 填充 `AuthContext` 的 ThreadLocal`LoginUser` + `LoginCustomer`
### 3.2 RBAC 权限模型
```
UserInfo (用户)
└── SysUserCustomer (用户-租户关联,支持一人多租户)
└── CustomerInfo (租户/企业)
└── SysRole (角色)
└── SysMenu (菜单/权限项)
└── 权限标识 (permission key)
SysUserCustomer ←→ SysRole (用户-角色关联表)
SysRole ←→ SysMenu (角色-菜单关联表)
```
权限校验方式:`PermissionServiceImpl` 中实现按钮级权限检查,通过菜单的 `permission` 字段匹配。
### 3.3 多租户数据隔离
**方式**:共享数据库 + 动态数据源路由(非共享表 + tenant_id 模式)
```
所有请求 → DataSourceHeaderAspect → 读取 X-Tenant-ID Header → 切换数据源
cloud / user / huoban / plant / zx
```
业务表通过 `customer_id` 字段关联 `CustomerInfo`,**不是物理外键**,而是逻辑关联。
### 3.4 AuthContext 使用模式
`AuthContext` 来自 `apes-commons`,是 ThreadLocal 封装:
```java
// 典型使用方式(出现在 52 个文件中)
AuthContext.getLoginInfo().getCustomer().getId() // 当前租户 ID
AuthContext.getLoginInfo().getUser().getId() // 当前用户 ID
AuthContext.getLoginInfo().getUser().getNickName() // 当前用户名
```
配合 `@Login` 注解,业务 Controller 可以零感知地获取登录态:
```java
@Login
@RestController
@RequestMapping("/tracking/config")
public class TrackingConfigController {
@GetMapping("/list")
public R list() {
Long customerId = AuthContext.getLoginInfo().getCustomer().getId();
// 直接使用,无需传参
return R.ok(configService.listByCustomerId(customerId));
}
}
```
### 3.5 OperationLogUtil 静态封装
```java
// 文件:cn.apes.cloud.util.OperationLogUtil
@Component
public class OperationLogUtil {
private static SysOperationLogService logService;
private static RedissonClient redissonClient;
@PostConstruct
public void init() {
logService = SpringContextHolder.getBean(SysOperationLogService.class);
redissonClient = SpringContextHolder.getBean(RedissonClient.class);
}
public static void log(String module, String action, String desc) {
// 从 AuthContext 自动获取操作人信息
LoginInfo info = AuthContext.getLoginInfo();
Long userId = info.getUser().getId();
Long customerId = info.getCustomer().getId();
// ... 记录日志
}
}
```
11 个业务文件直接调用 `OperationLogUtil.log(...)`,一行式 API,内部依赖 `AuthContext`
### 3.6 账权 Service 间内部调用
```
LoginController
→ UserInfoService.login() # 登录验证
→ PermissionService.getUserPermissions() # 获取权限
→ SysMenuService.getMenuTree() # 菜单树
→ CustomerInfoService.getById() # 企业信息
SysAccessTokenService.validate()
→ CustomerInfoServiceImpl.getById() # 查企业信息
→ 写 Redis 模拟登录态
PermissionServiceImpl
→ UserInfoService.getUserById() # 查用户
→ SysRoleService.getRolesByUserId() # 查角色
```
约 8 处内部调用,全部在账权域内。
---
## 4. 耦合度量化分析
通过 grep 对源码树的精确统计:
### 4.1 AuthContext 引用统计
```bash
$ grep -rl "AuthContext" service-master/src/ | wc -l
52 个文件
$ grep -r "AuthContext" service-master/src/ | wc -l
111 处引用
```
**文件分布**
- Controller 层:~25 个文件(通过 `@Login` + `AuthContext.getLoginInfo()` 获取登录态)
- Service 实现层:~15 个文件(业务逻辑中获取 `customerId`
- 工具类/切面:~5 个文件(`OperationLogUtil``DataSourceHeaderAspect` 等)
- 配置/过滤器:~7 个文件(`AccessTokenFilter` 等)
### 4.2 OperationLogUtil 引用统计
```bash
$ grep -rl "OperationLogUtil" service-master/src/ | wc -l
11 个文件
```
调用方包括:`AccessTokenFilter`、各种 `*Controller``*ServiceImpl`
### 4.3 账权 Service 被业务模块直接引用统计
```bash
$ grep -rl "import cn.apes.cloud.service.PermissionService\|import cn.apes.cloud.service.UserInfoService\|import cn.apes.cloud.service.CustomerInfoService\|import cn.apes.cloud.service.CustomerUserService\|import cn.apes.cloud.service.SysAccessTokenService" \
service-master/src/main/java/cn/apes/cloud/service/impl/Trace*.java \
service-master/src/main/java/cn/apes/cloud/service/impl/Weight*.java
# 结果:0 个匹配
```
**关键发现**:业务模块(Trace*、Weight*)不直接 import 账权 Service。它们仅通过 `AuthContext`(来自 apes-commons)间接获取登录态,不存在代码层面的直接依赖。
### 4.4 账权 Service 间内部调用统计
```bash
$ grep -rl "import cn.apes.cloud.service.impl.\|import cn.apes.cloud.service\." \
service-master/src/main/java/cn/apes/cloud/service/impl/PermissionServiceImpl.java \
service-master/src/main/java/cn/apes/cloud/service/impl/UserInfoServiceImpl.java
# 约 8 处交叉引用,全部在账权域内部
```
### 4.5 耦合度总结
| 指标 | 数值 | 解耦难度 |
|------|------|----------|
| AuthContext 引用文件 | 52 个 / 111 处 | 低(import 替换即可) |
| OperationLogUtil 引用 | 11 个文件 | 低(方法签名不变) |
| 业务 Service → 账权 Service 直接调用 | **0** | 无需改造 |
| 账权 Service 间内部调用 | ~8 处 | 随账权服务整体迁移 |
| 数据库外键约束 | 无(逻辑关联) | 拆库时无需处理物理约束 |
---
## 5. 设计意图逆向分析——六大障碍的出发点与判定
### 判断标尺
分析这些障碍,要区分两件事:
- **设计思路对不对**(出发点是否站得住)
- **当前形态能不能进微服务**(实体要不要拆)
很多障碍属于"思路对、形态错"——不必推倒重来,只需"搬家 + 换填充源"。
### 障碍 ① AuthContextThreadLocal + Redis 会话)
**设计出发点**
- 单体里最省事的"隐式传参":任何方法随时 `AuthContext.getLoginInfo().getCustomer().getId()` 拿当前租户,不用在每层方法签名里塞 `customerId`
- Redis 做后端存储(key=token → user JSONkey=token:customer → 企业 JSON),会话可跨实例共享 → 单体水平扩容不用粘会话
-`@Login` AOP 自动填充,业务零感知
**评价**:单体时代的经典正确做法。问题不在 ThreadLocal 本身,而在它**不跨进程传播**。
| 判定 | 说明 |
|------|------|
| ✅ 保留 | ThreadLocal 作为"服务内本地上下文"的机制——任何服务内部用 ThreadLocal 存当前用户都是对的 |
| 🔧 改造 | 填充来源(从"自己查 Redis"改"网关注入 Header")+ 跨服务传播方式。机制保留,来源替换,业务代码几无改动 |
### 障碍 ② apes-commons 黑盒(外部 `0.0.7-SNAPSHOT` JAR
**设计出发点**
- 多项目共享基础框架:`cn.apes:apes-commons` 不止当前 service 在用,伙伴云/交易平台等多条产品线共用同一套认证
- 把脏活(RSA 解密、Token 生成、Redis 读写、ThreadLocal 管理、`@Login`/`LoginAspect`)封进 JAR,各服务鉴权行为一致
- `pom.xml` 里特意 exclude 了 hutool(避免版本冲突)——说明它确实被当"公共底座"对待
**评价**:平台化思路完全正确。黑盒不是罪,问题是要改它、又改不了。
| 判定 | 说明 |
|------|------|
| ✅ 保留 | "共享基础库"这个思路 → 直接进化为自有 `auth-sdk` |
| 🔧 改造 | 所有权收归自己(从外部 SNAPSHOT 依赖变成自维护 SDK),并用 Maven shade relocation 兼容旧包名,避免 52 个文件全量改 import |
### 障碍 ③ OperationLogUtil 静态依赖
**设计出发点**
- 极致调用便利:任何地方一行 `OperationLogUtil.log("模块","操作","描述")` 就能记日志,不用注入 bean、不用传参
- `@PostConstruct` 抓 Spring bean 静态化,是工具类常见写法
- 操作人信息从 `AuthContext` 自动取,调用方完全不用关心"谁在操作"——11 个文件因此零成本接入
**评价**:API 体验目标合理,耦合点只在实现内部。
| 判定 | 说明 |
|------|------|
| ✅ 保留 | 一行式日志 API 的体验 |
| 🔧 改造 | 内部把 `apes-commons.AuthContext` 换成自有 `ServiceAuthContext`——约 10 行实现改动,11 个调用方一行都不用动 |
### 障碍 ④ 账权 Service 间内部调用(UserInfo ↔ Permission ↔ SSO
**设计出发点**
- 这是同一业务域内的**自然内聚**:登录要知道角色、角色挂在套餐上、套餐要校验权限、SSO 要查 `sso_config`——它们本就该在一起
- 单体同进程调用,零网络开销(`UserInfoServiceImpl` 直接 `@Autowired PermissionServiceImpl`
**评价**:这不仅不是障碍,反而是**该整包带走的部分**。它恰恰证明了"账权是一个内聚域"。
| 判定 | 说明 |
|------|------|
| ✅ 全部保留 | 留在 Identity Service 内部,一个调用都不用动 |
| ❌ 无必须拆分项 | 这正是拆服务时"一起搬"的内容 |
### 障碍 ⑤ 共享数据库(所有表同一 MySQL)
**设计出发点**
- 初期最快:不用设计服务边界、不用碰分布式事务
- `customer_id` 共享键多租户,天然一张库(业务表靠这个字段关联 `customer_info`,不是外键)
- 表间逻辑关联多,放一起 JOIN 方便;一个 RDS 实例成本最低
**评价**:早期最务实的选择。**这是 6 条里唯一一个"实体层面真必须拆"的。**
| 判定 | 说明 |
|------|------|
| ✅ 短期保留 | Phase 1-2(模块化隔离、引网关)完全可以不动库,不拆库也能推进 |
| 🔧 必须拆分(长期) | 要"真正独立"就必须拆——否则账权与业务仍共享存储,故障域/扩缩容/安全策略都分不开。拆库放到 Phase 3,且必须配双写过渡期 |
### 障碍 ⑥ AOP 执行顺序依赖(DataSourceHeaderAspect `Order=1` + LoginAspect
**设计出发点**
- `DataSourceHeaderAspect``AuthContext.clean()` 再切数据源 → 保证每个请求从干净状态开始,**防止上一请求的 ThreadLocal 泄漏/串号**
- 然后 `LoginAspect`(在 apes-commons 里)填充 AuthContext → 保证 Controller 执行业务前上下文已就绪
- 这个顺序在"单进程内、请求串行经过切面"时完美工作
**评价**:"请求级清理 + 顺序保证"的防泄漏思路必须保留,它是并发安全的底线。
| 判定 | 说明 |
|------|------|
| ✅ 保留 | 请求级 ThreadLocal 清理 + 顺序保证的思路(防内存泄漏/串号是刚需) |
| 🔧 改造 | 职责归属变了——数据源路由可能在网关或各服务内,认证填充改由 `auth-sdk``AuthHeaderFilter` 做,顺序由本地 Filter chain 保证,而非跨 JAR 协商 |
### 总分类
| 障碍 | 设计出发点是否合理 | 结论 |
|------|------------------|------|
| AuthContext ThreadLocal | ✅ 合理 | 保留机制,改造填充源与跨服务传播 |
| apes-commons 黑盒 | ✅ 合理(平台化) | 保留思路,收归自有 auth-sdk |
| OperationLogUtil 静态 | ✅ 合理(便利性) | 保留 API,改内部 10 行实现 |
| 账权 Service 内部调用 | ✅ 本就该内聚 | 全部保留,整包带走 |
| 共享数据库 | ⚠️ 早期务实 | 短期保留,长期必须拆(Phase 3 + 双写) |
| AOP 顺序依赖 | ✅ 合理(防泄漏) | 保留思路,改造职责归属 |
**一句话总结**:6 条里有 4 条半是"好设计绑在了单体上"——思路保留、形态搬迁即可;只有"共享数据库"是实体层面真必须拆的,而"账权 Service 内部调用"根本不是障碍,是拆服务时该一起搬走的内聚域。
真正需要"动刀"的,其实就三件事:
1. **跨服务上下文传播**ThreadLocal → 网关 Header(机制保留)
2. **存储边界**:共享库 → auth_db 独立(Phase 3
3. **依赖主权**:外部 JAR → 自有 auth-sdk(兼容旧包名,业务零改动)
---
## 6. 独立服务化可行性论证
### 6.1 有利因素
| 因素 | 说明 |
|------|------|
| Redis 共享会话 | 登录态存储在 Redis,天然支持分布式,服务拆分后会话无需迁移 |
| 无数据库外键约束 | 业务表与账权表之间是逻辑关联(customer_id),不是物理外键,拆库无 DDL 障碍 |
| 业务模块零直接依赖 | Trace*/Weight* 不 import 账权 Service,仅通过 AuthContext 间接获取登录态 |
| 账权域高内聚 | 账权 Service 间约 8 处内部调用,全在域内,整包带走即可 |
| AuthContext API 简单 | 主要调用模式是 `AuthContext.getLoginInfo().getCustomer().getId()`,可用 SDK 兼容替换 |
### 6.2 风险因素
| 风险 | 等级 | 应对 |
|------|------|------|
| apes-commons 黑盒 | 中 | 收归自有 auth-sdkMaven shade relocation 兼容旧包名 |
| 52 个文件引用 AuthContext | 低 | import 替换即可,方法签名不变 |
| 共享数据库拆分 | 高 | Phase 3 双写过渡期 + 数据一致性校验 |
| AOP 执行顺序 | 低 | 各服务内 Filter chain 保证,不跨 JAR 协商 |
| AccessTokenFilter 耦合 | 低 | 迁移到网关或 Identity Service 入口 |
### 6.3 结论
**技术上可行**。业务模块与账权模块之间不存在直接代码调用(0 个 import),耦合仅通过 `AuthContext`ThreadLocal)间接发生。登录态已存储在 Redis 中天然支持分布式。拆分的基础条件良好。
分阶段实施建议:
| 阶段 | 目标 | 风险 | 预估 |
|------|------|------|------|
| Phase 1 | 单体内包级隔离 + 接口抽象 | 低 | 5-10 人天 |
| Phase 2 | 抽取 auth-sdk + 引入网关 | 中 | 10-15 人天 |
| Phase 3 | Identity Service 独立部署 + 数据库拆分 | 高 | 15-20 人天 |
| Phase 4 | JWT 升级 + 权限注解 + OAuth2 | 中 | 10+ 人天 |
**建议立即启动 Phase 1**(风险最低、收益明确)。是否推进 Phase 2-3 取决于是否有多产品线复用账权的需求。
---
## 附:分析方法说明
本文档的分析基于以下方法:
1. **静态代码走查**:通过 Agent 子任务深度遍历 `src/main/java` 全部包结构,读取所有关键 Controller/Service/Entity/Config 源码
2. **Grep 量化统计**:使用 ripgrep 对源码树进行精确模式匹配,统计耦合度指标
3. **依赖追踪**:通过 import 语句分析模块间依赖关系
4. **设计意图推断**:从代码写法(AOP 顺序、静态封装、Redis key 设计等)反推设计者的决策出发点
5. **可行性论证**:基于量化数据和架构特征,评估微服务拆分的技术可行性
分析工具链:Agent Explore 子任务 + Grep + Read,分析时间:2026-08-05