Files
apes-Authon-history/docs/代码深度解析与分析过程.md
T
figmar 9cbb1f7695 init: apes-Authon-history — 剥离历史与源码分析归档
- docs/: 源码分析报告、代码深度解析、项目说明书、独立服务化方案
- backend/: 账权模块提取说明(提取原则、文件清单、耦合度验证)
- frontend/: 前端剥离方案、剥离历史与说明、框架修改详情
2026-08-09 13:04:04 +08:00

671 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码深度解析与分析过程
> 本文档记录了对账权平台(原掌渠 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