# 代码深度解析与分析过程 > 本文档记录了对账权平台(原掌渠 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 + 切数据源) → LoginAspect(apes-commons 中,@Login AOP 填充 AuthContext) → Controller 方法执行 ``` #### 3.1.1 AccessTokenFilter(开放平台认证) ```java // 文件:cn.apes.cloud.filter.AccessTokenFilter // 职责:为外部系统提供免登录的 API 访问 // 1. 从 Header 提取 Token(X-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 userBucket = redissonClient.getBucket(redisToken); userBucket.set(userObj, 1, TimeUnit.DAYS); RBucket 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 LoginAspect(apes-commons 内部,不可见源码) 根据 pom.xml 中的 `apes-commons:0.0.7-SNAPSHOT` 依赖,推断其行为: - 拦截标注 `@Login` 的 Controller 类/方法 - 从 `request.getHeader("token")` 获取 Token - 从 Redis 读取 `RBucket(token)` 和 `RBucket(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. 设计意图逆向分析——六大障碍的出发点与判定 ### 判断标尺 分析这些障碍,要区分两件事: - **设计思路对不对**(出发点是否站得住) - **当前形态能不能进微服务**(实体要不要拆) 很多障碍属于"思路对、形态错"——不必推倒重来,只需"搬家 + 换填充源"。 ### 障碍 ① AuthContext(ThreadLocal + Redis 会话) **设计出发点**: - 单体里最省事的"隐式传参":任何方法随时 `AuthContext.getLoginInfo().getCustomer().getId()` 拿当前租户,不用在每层方法签名里塞 `customerId` - Redis 做后端存储(key=token → user JSON,key=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-sdk,Maven 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