|
|
|
|
# 架构设计
|
|
|
|
|
|
|
|
|
|
<cite>
|
|
|
|
|
**本文引用的文件**
|
|
|
|
|
- [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java)
|
|
|
|
|
- [application.yml](file://crm-app/src/main/resources/application.yml)
|
|
|
|
|
- [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java)
|
|
|
|
|
- [application.yml](file://crm-auth/src/main/resources/application.yml)
|
|
|
|
|
- [pom.xml](file://pom.xml)
|
|
|
|
|
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
|
|
|
|
|
- [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java)
|
|
|
|
|
- [CorsConfig.java](file://crm-base/src/main/java/com/crm/base/config/CorsConfig.java)
|
|
|
|
|
- [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
- [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java)
|
|
|
|
|
- [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java)
|
|
|
|
|
- [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java)
|
|
|
|
|
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
|
|
|
|
|
- [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java)
|
|
|
|
|
- [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java)
|
|
|
|
|
- [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java)
|
|
|
|
|
- [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java)
|
|
|
|
|
- [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java)
|
|
|
|
|
- [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
- [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java)
|
|
|
|
|
- [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java)
|
|
|
|
|
- [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java)
|
|
|
|
|
- [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java)
|
|
|
|
|
- [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java)
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java)
|
|
|
|
|
- [ThirdPartyUserInfo.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyUserInfo.java)
|
|
|
|
|
- [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java)
|
|
|
|
|
- [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java)
|
|
|
|
|
- [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java)
|
|
|
|
|
- [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java)
|
|
|
|
|
- [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java)
|
|
|
|
|
- [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java)
|
|
|
|
|
- [0009-sso-reuses-scan-login-endpoint.md](file://docs/adr/0009-sso-reuses-scan-login-endpoint.md)
|
|
|
|
|
</cite>
|
|
|
|
|
|
|
|
|
|
## 更新摘要
|
|
|
|
|
**所做更改**
|
|
|
|
|
- 更新了第三方集成模式章节,详细说明单点登录端点复用决策
|
|
|
|
|
- 新增了SSO架构设计说明,解释钉钉免登与扫码登录的统一处理机制
|
|
|
|
|
- 更新了认证流程图,展示两种登录方式的统一处理路径
|
|
|
|
|
- 增强了扩展性设计说明,包含未来JS-SDK支持的规划
|
|
|
|
|
|
|
|
|
|
## 目录
|
|
|
|
|
1. [引言](#引言)
|
|
|
|
|
2. [项目结构](#项目结构)
|
|
|
|
|
3. [核心组件](#核心组件)
|
|
|
|
|
4. [架构总览](#架构总览)
|
|
|
|
|
5. [详细组件分析](#详细组件分析)
|
|
|
|
|
6. [依赖关系分析](#依赖关系分析)
|
|
|
|
|
7. [性能考量](#性能考量)
|
|
|
|
|
8. [故障排查指南](#故障排查指南)
|
|
|
|
|
9. [结论](#结论)
|
|
|
|
|
10. [附录](#附录)
|
|
|
|
|
|
|
|
|
|
## 引言
|
|
|
|
|
本架构设计文档面向架构师与高级开发者,围绕CRM系统的微服务划分、分层设计原则、组件交互关系与数据流向展开。重点阐述安全架构(认证授权、数据权限)、数据访问层(MyBatis-Plus、分布式ID)、缓存策略(Redis、部门树缓存)、文件存储方案(分片上传、对象存储、在线预览)等关键技术决策。同时给出系统边界定义、第三方集成模式与扩展点设计,并提供架构图表以说明模块间的依赖关系与通信机制。
|
|
|
|
|
|
|
|
|
|
**更新** 本次更新重点阐述了单点登录(SSO)的架构决策,特别是钉钉免登录场景下复用现有扫码登录端点的设计模式,消除了冗余代码的同时支持多种登录方式。
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
本项目采用多模块Maven工程,按领域与服务进行拆分:
|
|
|
|
|
- crm-app:应用启动入口与全局配置聚合
|
|
|
|
|
- crm-base:公共基础能力(统一异常、通用实体、安全工具、MyBatis-Plus与Redis配置、分布式ID生成等)
|
|
|
|
|
- crm-auth:认证授权与系统管理域(用户、角色、菜单、部门、数据权限、第三方登录)
|
|
|
|
|
- crm-file:文件服务(分片上传、断点续传、对象存储、在线预览、清理任务)
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
graph TB
|
|
|
|
|
subgraph "应用层"
|
|
|
|
|
APP["crm-app<br/>应用启动"]
|
|
|
|
|
end
|
|
|
|
|
subgraph "基础能力"
|
|
|
|
|
BASE["crm-base<br/>通用配置/安全/工具"]
|
|
|
|
|
end
|
|
|
|
|
subgraph "业务服务"
|
|
|
|
|
AUTH["crm-auth<br/>认证与系统管理"]
|
|
|
|
|
FILE["crm-file<br/>文件服务"]
|
|
|
|
|
end
|
|
|
|
|
APP --> AUTH
|
|
|
|
|
APP --> FILE
|
|
|
|
|
AUTH --> BASE
|
|
|
|
|
FILE --> BASE
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java)
|
|
|
|
|
- [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java)
|
|
|
|
|
- [pom.xml](file://pom.xml)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java)
|
|
|
|
|
- [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java)
|
|
|
|
|
- [pom.xml](file://pom.xml)
|
|
|
|
|
|
|
|
|
|
## 核心组件
|
|
|
|
|
- 统一响应与分页:Result、PageResult
|
|
|
|
|
- 通用实体:BaseEntity、OwnedEntity(支持审计字段与数据归属)
|
|
|
|
|
- 安全上下文:LoginUser、SecurityUtils、DataVisibilityContext
|
|
|
|
|
- 数据权限:@DataScope注解、DataScopeHelper、拦截器与SQL注入器
|
|
|
|
|
- MyBatis-Plus:CRUD增强、自动填充、分页插件
|
|
|
|
|
- Redis:缓存与分布式会话支撑
|
|
|
|
|
- 分布式ID:雪花算法实现
|
|
|
|
|
- 文件服务:分片上传、断点续传、对象存储、在线预览、定时清理
|
|
|
|
|
- 第三方认证:工厂模式抽象、统一用户信息模型、组织准入校验
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java)
|
|
|
|
|
- [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java)
|
|
|
|
|
- [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java)
|
|
|
|
|
- [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java)
|
|
|
|
|
- [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java)
|
|
|
|
|
- [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java)
|
|
|
|
|
- [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java)
|
|
|
|
|
- [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java)
|
|
|
|
|
- [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java)
|
|
|
|
|
- [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
- [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java)
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [ThirdPartyUserInfo.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyUserInfo.java)
|
|
|
|
|
|
|
|
|
|
## 架构总览
|
|
|
|
|
系统采用"网关/应用聚合 + 领域微服务"的分层与解耦模式:
|
|
|
|
|
- 应用层:统一入口与跨服务编排
|
|
|
|
|
- 领域服务:认证授权、文件服务等独立部署
|
|
|
|
|
- 基础能力:被各服务复用,保证一致性与可维护性
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
graph TB
|
|
|
|
|
Client["客户端"] --> API["API层<br/>控制器"]
|
|
|
|
|
API --> SVC["服务层<br/>业务编排"]
|
|
|
|
|
SVC --> SEC["安全层<br/>鉴权/数据权限"]
|
|
|
|
|
SVC --> DB["数据访问层<br/>MyBatis-Plus"]
|
|
|
|
|
SVC --> CACHE["缓存层<br/>Redis"]
|
|
|
|
|
SVC --> EXT["外部集成<br/>第三方登录/文件预览"]
|
|
|
|
|
DB --> DS["数据库"]
|
|
|
|
|
CACHE --> RC["Redis集群"]
|
|
|
|
|
EXT --> OSS["对象存储"]
|
|
|
|
|
EXT --> VIEW["在线预览服务"]
|
|
|
|
|
EXT --> THIRD["第三方平台<br/>钉钉OAuth"]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java)
|
|
|
|
|
- [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
- [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java)
|
|
|
|
|
- [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java)
|
|
|
|
|
- [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java)
|
|
|
|
|
- [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
- [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java)
|
|
|
|
|
|
|
|
|
|
## 详细组件分析
|
|
|
|
|
|
|
|
|
|
### 认证与安全架构
|
|
|
|
|
- 认证流程:客户端提交凭证 → 过滤器校验JWT → 构建登录用户上下文 → 控制器调用服务层完成登录逻辑 → 返回令牌与用户信息
|
|
|
|
|
- 授权与数据权限:基于角色的菜单权限与基于注解的数据范围控制,结合SQL注入器动态拼接数据可见性条件
|
|
|
|
|
- 第三方登录:通过工厂模式扩展不同平台(如钉钉),统一抽象为第三方认证客户端
|
|
|
|
|
|
|
|
|
|
**更新** 单点登录(SSO)采用端点复用策略,钉钉免登录与扫码登录共用同一接口,简化了架构设计。
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
participant C as "客户端"
|
|
|
|
|
participant F as "JwtAuthenticationFilter"
|
|
|
|
|
participant A as "AuthController"
|
|
|
|
|
participant S as "AuthServiceImpl"
|
|
|
|
|
participant T as "TokenService"
|
|
|
|
|
participant R as "Redis"
|
|
|
|
|
C->>F : "携带JWT请求"
|
|
|
|
|
F->>F : "解析并验证Token"
|
|
|
|
|
F-->>A : "放行并注入Security上下文"
|
|
|
|
|
A->>S : "登录/刷新Token"
|
|
|
|
|
S->>T : "生成/更新Token"
|
|
|
|
|
T->>R : "写入会话/黑名单"
|
|
|
|
|
T-->>S : "返回Token"
|
|
|
|
|
S-->>A : "返回结果"
|
|
|
|
|
A-->>C : "统一响应Result"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java)
|
|
|
|
|
- [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java)
|
|
|
|
|
- [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
- [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java)
|
|
|
|
|
- [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java)
|
|
|
|
|
- [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java)
|
|
|
|
|
- [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
- [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java)
|
|
|
|
|
|
|
|
|
|
#### 数据权限模型
|
|
|
|
|
- 注解驱动:@DataScope标注方法或类,声明数据范围级别
|
|
|
|
|
- 执行期处理:DataScopeHelper解析当前用户与范围,动态注入SQL过滤条件
|
|
|
|
|
- 上下文隔离:DataVisibilityContext在请求链路中传递可见性参数
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
classDiagram
|
|
|
|
|
class DataScope {
|
|
|
|
|
+注解 : 声明数据范围
|
|
|
|
|
}
|
|
|
|
|
class DataScopeHelper {
|
|
|
|
|
+解析范围()
|
|
|
|
|
+注入SQL条件()
|
|
|
|
|
}
|
|
|
|
|
class DataVisibilityContext {
|
|
|
|
|
+设置可见性()
|
|
|
|
|
+获取可见性()
|
|
|
|
|
}
|
|
|
|
|
DataScopeHelper --> DataVisibilityContext : "读写上下文"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java)
|
|
|
|
|
- [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java)
|
|
|
|
|
- [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java)
|
|
|
|
|
- [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java)
|
|
|
|
|
- [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java)
|
|
|
|
|
|
|
|
|
|
### 数据访问层设计
|
|
|
|
|
- ORM框架:MyBatis-Plus提供CRUD增强、分页、自动填充、条件构造器
|
|
|
|
|
- 分布式ID:SnowflakeIdWorker生成唯一主键,避免冲突
|
|
|
|
|
- 通用基类:BaseEntity统一审计字段,OwnedEntity扩展数据归属字段
|
|
|
|
|
- 配置:注册MetaObjectFillHandler、分页插件、自定义SQL注入器
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
flowchart TD
|
|
|
|
|
Start(["进入查询"]) --> BuildQuery["构建查询条件"]
|
|
|
|
|
BuildQuery --> ApplyScope["应用数据权限条件"]
|
|
|
|
|
ApplyScope --> Execute["执行SQL(分页/排序)"]
|
|
|
|
|
Execute --> MapEntity["映射到实体(BaseEntity/OwenedEntity)"]
|
|
|
|
|
MapEntity --> Return["返回结果集"]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java)
|
|
|
|
|
- [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java)
|
|
|
|
|
- [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java)
|
|
|
|
|
- [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java)
|
|
|
|
|
- [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java)
|
|
|
|
|
- [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java)
|
|
|
|
|
- [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java)
|
|
|
|
|
|
|
|
|
|
### 缓存策略
|
|
|
|
|
- 部门树缓存:将部门层级结构缓存至Redis,降低频繁查询压力
|
|
|
|
|
- 会话与令牌:TokenService使用Redis维护会话状态与黑名单
|
|
|
|
|
- 通用缓存:RedisConfig提供连接池、序列化等基础能力
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
participant S as "服务层"
|
|
|
|
|
participant D as "DeptTreeCache"
|
|
|
|
|
participant R as "Redis"
|
|
|
|
|
S->>D : "获取部门树"
|
|
|
|
|
D->>R : "读取缓存"
|
|
|
|
|
alt "命中"
|
|
|
|
|
R-->>D : "返回缓存数据"
|
|
|
|
|
D-->>S : "直接返回"
|
|
|
|
|
else "未命中"
|
|
|
|
|
D->>D : "查询数据库构建树"
|
|
|
|
|
D->>R : "写入缓存"
|
|
|
|
|
D-->>S : "返回数据"
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java)
|
|
|
|
|
- [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java)
|
|
|
|
|
|
|
|
|
|
### 文件存储方案
|
|
|
|
|
- 分片上传:前端分片、服务端合并、断点续传与会话管理
|
|
|
|
|
- 对象存储:MinIO作为底层存储,统一文件元数据与下载接口
|
|
|
|
|
- 在线预览:KkFileViewClient对接预览服务,返回预览链接
|
|
|
|
|
- 清理任务:定时扫描孤儿分片,释放存储空间
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
participant FE as "前端"
|
|
|
|
|
participant FC as "FileController"
|
|
|
|
|
participant FA as "FileApiImpl"
|
|
|
|
|
participant FS as "FileInfoServiceImpl"
|
|
|
|
|
participant OSS as "对象存储"
|
|
|
|
|
participant PV as "预览服务"
|
|
|
|
|
FE->>FC : "发起分片上传"
|
|
|
|
|
FC->>FA : "初始化上传会话"
|
|
|
|
|
FA->>FS : "创建/更新分片记录"
|
|
|
|
|
loop "分片传输"
|
|
|
|
|
FE->>FC : "上传分片"
|
|
|
|
|
FC->>FA : "保存分片到OSS"
|
|
|
|
|
FA->>FS : "更新分片状态"
|
|
|
|
|
end
|
|
|
|
|
FE->>FC : "触发合并"
|
|
|
|
|
FC->>FA : "合并分片并生成文件信息"
|
|
|
|
|
FA->>PV : "生成预览链接"
|
|
|
|
|
FA-->>FE : "返回文件信息与预览地址"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java)
|
|
|
|
|
- [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java)
|
|
|
|
|
- [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java)
|
|
|
|
|
- [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java)
|
|
|
|
|
- [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java)
|
|
|
|
|
- [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java)
|
|
|
|
|
- [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java)
|
|
|
|
|
- [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java)
|
|
|
|
|
- [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java)
|
|
|
|
|
- [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java)
|
|
|
|
|
- [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java)
|
|
|
|
|
|
|
|
|
|
### 第三方集成模式
|
|
|
|
|
- 工厂模式:ThirdPartyAuthClientFactory根据类型选择具体客户端
|
|
|
|
|
- 统一抽象:ThirdPartyUserInfo标准化第三方用户信息
|
|
|
|
|
- 可扩展:新增平台只需实现客户端接口并注册到工厂
|
|
|
|
|
- **单点登录优化**:钉钉免登录复用现有扫码登录端点,消除冗余代码
|
|
|
|
|
|
|
|
|
|
**更新** 单点登录(SSO)架构采用了端点复用策略,钉钉免登录与扫码登录共用 `POST /api/auth/login/dingtalk` 接口。这种设计避免了为免登录场景创建独立的SSO端点,因为两种场景的authCode都通过钉钉 `oauth2/userAccessToken` API换取用户级accessToken,后端处理逻辑完全一致。
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
participant Browser as "浏览器"
|
|
|
|
|
participant DingTalk as "钉钉平台"
|
|
|
|
|
participant CRM as "CRM后端"
|
|
|
|
|
participant Auth as "认证服务"
|
|
|
|
|
participant Client as "钉钉客户端"
|
|
|
|
|
Note over Browser,DingTalk : 扫码登录流程
|
|
|
|
|
Browser->>DingTalk : "打开扫码页面"
|
|
|
|
|
DingTalk-->>Browser : "返回二维码"
|
|
|
|
|
Browser->>DingTalk : "用户扫码确认"
|
|
|
|
|
DingTalk-->>Browser : "返回authCode"
|
|
|
|
|
Browser->>CRM : "POST /api/auth/login/dingtalk?authCode=xxx"
|
|
|
|
|
CRM->>Auth : "调用登录服务"
|
|
|
|
|
Auth->>Client : "getUserInfo(authCode)"
|
|
|
|
|
Client->>DingTalk : "换取accessToken"
|
|
|
|
|
DingTalk-->>Client : "返回accessToken"
|
|
|
|
|
Client->>DingTalk : "查询用户信息"
|
|
|
|
|
DingTalk-->>Client : "返回用户信息"
|
|
|
|
|
Client-->>Auth : "返回ThirdPartyUserInfo"
|
|
|
|
|
Auth->>Auth : "用户匹配/注册/绑定身份"
|
|
|
|
|
Auth-->>CRM : "返回登录结果"
|
|
|
|
|
CRM-->>Browser : "返回JWT Token"
|
|
|
|
|
Note over Browser,DingTalk : 免登录流程复用同一端点
|
|
|
|
|
Browser->>DingTalk : "已登录态访问CRM"
|
|
|
|
|
DingTalk-->>Browser : "自动重定向带authCode"
|
|
|
|
|
Browser->>CRM : "POST /api/auth/login/dingtalk?authCode=xxx"
|
|
|
|
|
Note over CRM : "处理逻辑与扫码登录完全相同"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java)
|
|
|
|
|
- [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java)
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [0009-sso-reuses-scan-login-endpoint.md](file://docs/adr/0009-sso-reuses-scan-login-endpoint.md)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java)
|
|
|
|
|
- [ThirdPartyUserInfo.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyUserInfo.java)
|
|
|
|
|
- [0009-sso-reuses-scan-login-endpoint.md](file://docs/adr/0009-sso-reuses-scan-login-endpoint.md)
|
|
|
|
|
|
|
|
|
|
### 单点登录架构设计
|
|
|
|
|
**新增** 基于ADR-0009的决策,系统采用了统一的第三方认证架构:
|
|
|
|
|
|
|
|
|
|
- **端点复用策略**:钉钉免登录与扫码登录共用 `POST /api/auth/login/dingtalk` 接口
|
|
|
|
|
- **统一处理逻辑**:两种场景的authCode都通过相同的钉钉API换取用户级accessToken
|
|
|
|
|
- **零后端改动**:前端自行管理OAuth重定向URL和clientId,后端无需区分登录来源
|
|
|
|
|
- **扩展性考虑**:未来如需支持钉钉桌面客户端内H5免登(JS-SDK),需新增签名端点
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
classDiagram
|
|
|
|
|
class ThirdPartyAuthClient {
|
|
|
|
|
<<interface>>
|
|
|
|
|
+getType() IdentityTypeEnum
|
|
|
|
|
+getUserInfo(authCode) ThirdPartyUserInfo
|
|
|
|
|
+isOrgMember(user) boolean
|
|
|
|
|
}
|
|
|
|
|
class DingTalkAuthClient {
|
|
|
|
|
+fetchUserAccessToken(code) String
|
|
|
|
|
+fetchUserInfo(token) ThirdPartyUserInfo
|
|
|
|
|
+getCorpAccessToken() String
|
|
|
|
|
+requestTokenApi(code) String
|
|
|
|
|
+requestUserInfoApi(token) String
|
|
|
|
|
+requestCorpTokenApi() String
|
|
|
|
|
+requestGetByUnionIdApi(token, unionId) String
|
|
|
|
|
}
|
|
|
|
|
class ThirdPartyAuthClientFactory {
|
|
|
|
|
+getClient(type) ThirdPartyAuthClient
|
|
|
|
|
}
|
|
|
|
|
class AuthServiceImpl {
|
|
|
|
|
+login(type, authCode) LoginResultDTO
|
|
|
|
|
+doLoginInTx(type, thirdUser) AuthUser
|
|
|
|
|
+preCheckOrgMembership(type, user, client) void
|
|
|
|
|
}
|
|
|
|
|
ThirdPartyAuthClient <|.. DingTalkAuthClient
|
|
|
|
|
ThirdPartyAuthClientFactory --> ThirdPartyAuthClient
|
|
|
|
|
AuthServiceImpl --> ThirdPartyAuthClientFactory
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java)
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java)
|
|
|
|
|
- [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java)
|
|
|
|
|
- [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java)
|
|
|
|
|
- [0009-sso-reuses-scan-login-endpoint.md](file://docs/adr/0009-sso-reuses-scan-login-endpoint.md)
|
|
|
|
|
|
|
|
|
|
## 依赖关系分析
|
|
|
|
|
- 模块耦合:crm-app仅负责启动与聚合;crm-auth与crm-file均依赖crm-base
|
|
|
|
|
- 内部依赖:auth服务内service→mapper→entity的单向依赖;file服务内controller→api→service→mapper
|
|
|
|
|
- 外部依赖:Redis、对象存储、第三方登录平台
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
graph LR
|
|
|
|
|
APP["crm-app"] --> AUTH["crm-auth"]
|
|
|
|
|
APP --> FILE["crm-file"]
|
|
|
|
|
AUTH --> BASE["crm-base"]
|
|
|
|
|
FILE --> BASE
|
|
|
|
|
AUTH --> REDIS["Redis"]
|
|
|
|
|
FILE --> OSS["对象存储"]
|
|
|
|
|
AUTH --> THIRD["第三方登录平台"]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
图表来源
|
|
|
|
|
- [pom.xml](file://pom.xml)
|
|
|
|
|
- [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java)
|
|
|
|
|
- [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java)
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [pom.xml](file://pom.xml)
|
|
|
|
|
- [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java)
|
|
|
|
|
- [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java)
|
|
|
|
|
|
|
|
|
|
## 性能考量
|
|
|
|
|
- 数据访问优化:合理使用分页、索引与条件构造器,避免全表扫描;利用MyBatis-Plus批量操作减少往返
|
|
|
|
|
- 缓存策略:热点数据(如部门树、字典)优先缓存;注意缓存穿透、雪崩与一致性策略
|
|
|
|
|
- 分布式ID:雪花算法避免中心节点瓶颈,提升写入吞吐
|
|
|
|
|
- 文件上传:分片并发上传与断点续传提升大文件体验;合并后及时清理临时分片
|
|
|
|
|
- 安全开销:JWT无状态减少会话存储压力;敏感操作加限流与幂等保护
|
|
|
|
|
- **第三方调用优化**:钉钉企业级token缓存、组织准入校验事务外执行,避免长时间占用数据库连接
|
|
|
|
|
|
|
|
|
|
[本节为通用指导,不直接分析具体文件]
|
|
|
|
|
|
|
|
|
|
## 故障排查指南
|
|
|
|
|
- 统一异常处理:GlobalExceptionHandlerAdvice集中捕获业务与系统异常,返回标准错误码与消息
|
|
|
|
|
- 常见错误定位:检查日志中的TraceId;核对Redis连接与对象存储配置;确认JWT签名与过期时间
|
|
|
|
|
- 数据权限问题:核查@DataScope注解与上下文是否正确设置;检查SQL注入器是否生效
|
|
|
|
|
- 文件上传失败:查看分片完整性与合并任务;确认对象存储桶权限与网络连通性
|
|
|
|
|
- **第三方登录问题**:检查钉钉配置(client-id/secret)、网络连通性、token缓存状态、组织准入校验结果
|
|
|
|
|
|
|
|
|
|
章节来源
|
|
|
|
|
- [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java)
|
|
|
|
|
- [application.yml](file://crm-app/src/main/resources/application.yml)
|
|
|
|
|
- [application.yml](file://crm-auth/src/main/resources/application.yml)
|
|
|
|
|
|
|
|
|
|
## 结论
|
|
|
|
|
本架构以清晰的模块边界与分层设计为基础,通过安全、数据访问、缓存与文件服务的专项设计,满足CRM系统在认证授权、数据权限、高可用与可扩展方面的需求。**特别地,单点登录架构通过端点复用策略实现了简洁高效的第三方认证处理**。未来可在网关层引入更细粒度的路由与限流,在服务间引入事件总线以提升异步处理能力,并持续完善监控与可观测性体系。
|
|
|
|
|
|
|
|
|
|
[本节为总结性内容,不直接分析具体文件]
|
|
|
|
|
|
|
|
|
|
## 附录
|
|
|
|
|
- 系统边界定义:认证与系统管理(crm-auth)、文件服务(crm-file)、基础能力(crm-base)、应用聚合(crm-app)
|
|
|
|
|
- 扩展点设计:第三方登录客户端工厂、数据权限注解、文件上传处理器、缓存项命名规范
|
|
|
|
|
- 配置要点:跨域、Jackson序列化、Knife4j文档、定时任务属性、雪花ID参数
|
|
|
|
|
- **SSO设计决策**:钉钉免登录复用扫码登录端点,消除冗余代码,支持未来JS-SDK扩展
|
|
|
|
|
|
|
|
|
|
[本节为补充信息,不直接分析具体文件]
|