Files
tjt_czjs_backend/doc/后端开发文档.md
2026-01-19 18:54:03 +08:00

867 lines
22 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.

# Lyzsys 后端开发文档
## 目录
- [项目概述](#项目概述)
- [技术栈](#技术栈)
- [项目架构](#项目架构)
- [模块说明](#模块说明)
- [开发指南](#开发指南)
- [核心功能](#核心功能)
- [部署指南](#部署指南)
---
## 项目概述
Lyzsys 是一个基于 Spring Boot 的企业级后台管理系统采用模块化单体架构支持多租户、工作流、权限管理、AI 集成等企业级功能。
### 核心特性
- **模块化架构**: 基于 Maven 多模块的清晰分层设计
- **多租户支持**: 原生支持 SaaS 多租户场景
- **权限管理**: 基于 RBAC 的动态权限和数据权限
- **工作流引擎**: 集成 Flowable 实现业务流程管理
- **AI 集成**: 支持多个 AI 服务提供商集成
- **微服务就绪**: 模块化设计便于未来微服务化改造
---
## 技术栈
### 核心框架
| 技术 | 版本 | 说明 |
|------|------|------|
| Spring Boot | 2.7.18 | 应用开发框架 |
| Spring Security | 5.8.16 | 安全框架 |
| MyBatis Plus | 3.5.15 | ORM 增强框架 |
| Flowable | 6.8.0 | 工作流引擎 |
| Redisson | 3.52.0 | 分布式锁客户端 |
### 数据存储
| 技术 | 版本 | 说明 |
|------|------|------|
| MySQL | 5.7+ | 主数据库 |
| Redis | 5.0+ | 缓存数据库 |
| 支持数据库 | - | MySQL、PostgreSQL、Oracle、SQL Server、H2 等 |
### 工具库
| 技术 | 版本 | 说明 |
|------|------|------|
| Lombok | 1.18.34 | 简化 Java 代码 |
| MapStruct | 1.6.3 | 对象映射 |
| Jackson | 2.13.5 | JSON 处理 |
| Hibernate Validator | 6.2.5 | 参数校验 |
| Knife4j | 3.0.3 | API 文档 |
---
## 项目架构
### 整体架构
Lyzsys 后端采用**模块化单体架构**,基于 **Controller-Service-DAL** 三层架构模式:
```
┌─────────────────────────────────────────────────────────────┐
│ 前端应用 │
│ (lyzsys-ui-admin / lyzsys-ui-admin-uniapp) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ lyzsys-server │
│ (应用启动入口) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ lyzsys-framework│ │ lyzsys-module-xxx│ │lyzsys-dependencies│
│ (框架层) │ │ (业务模块) │ │ (依赖管理) │
└──────────────┘ └──────────────────┘ └──────────────┘
```
### 分层架构
每个业务模块都遵循标准的三层架构:
```
controller/ # 控制层 - 接收 HTTP 请求
├── admin/ # 管理后台 API
└── app/ # 用户 App API
service/ # 服务层 - 业务逻辑处理
├── IxxxService.java # 服务接口
└── xxxServiceImpl.java # 服务实现
dal/ # 数据访问层
├── dataobject/ # 数据对象 (DO)
├── mysql/ # MyBatis Mapper
└── redis/ # Redis 操作
```
### 架构设计原则
1. **依赖倒置**: Controller 依赖 Service 接口,而非实现
2. **单一职责**: 每层只负责自己的职责
3. **开闭原则**: 通过接口扩展,而非修改
4. **非严格分层**: 允许跨层访问(如 Service 直接调用多个 Mapper
---
## 模块说明
### Maven 模块分类
项目共有四类 Maven Module
#### 1. lyzsys-dependencies
**作用**: Maven 依赖版本管理 (BOM)
**说明**: 定义项目中所有 Maven 依赖的版本号,解决依赖冲突问题。
**关键文件**:
- `pom.xml`: 依赖版本定义
#### 2. lyzsys-framework
**作用**: Java 框架拓展
**说明**: 提供项目的基础框架能力,分为技术组件和业务组件。
**技术组件**:
| 模块 | 作用 |
|------|------|
| lyzsys-common | 基础 pojo 类、枚举、工具类 |
| lyzsys-spring-boot-starter-web | Web 封装,全局异常、访问日志 |
| lyzsys-spring-boot-starter-security | 认证授权,基于 Spring Security |
| lyzsys-spring-boot-starter-mybatis | 数据库操作,基于 MyBatis Plus |
| lyzsys-spring-boot-starter-redis | 缓存操作,基于 Spring Data Redis + Redisson |
| lyzsys-spring-boot-starter-mq | 消息队列,基于 Redis 实现 |
| lyzsys-spring-boot-starter-job | 定时任务,基于 Quartz 实现 |
| lyzsys-spring-boot-starter-protection | 服务保障,幂等、分布式锁、限流、熔断 |
| lyzsys-spring-boot-starter-excel | Excel 导入导出,基于 EasyExcel |
| lyzsys-spring-boot-starter-monitor | 服务监控,链路追踪、日志服务 |
| lyzsys-spring-boot-starter-websocket | WebSocket 封装,支持 Token 认证 |
**业务组件**:
| 模块 | 作用 |
|------|------|
| lyzsys-spring-boot-starter-biz-tenant | SaaS 多租户 |
| lyzsys-spring-boot-starter-biz-data-permission | 数据权限 |
| lyzsys-spring-boot-starter-biz-ip | 地区 & IP 库 |
#### 3. lyzsys-module-xxx
**作用**: 业务功能模块
**核心模块**:
| 模块 | 说明 | 是否必须 |
|------|------|----------|
| lyzsys-module-system | 系统功能模块 | √ |
| lyzsys-module-infra | 基础设施模块 | √ |
| lyzsys-module-ai | AI 大模型集成 | × |
| lyzsys-module-bpm | 业务流程管理 | × |
| lyzsys-module-crm | 客户关系管理 | × |
| lyzsys-module-erp | 企业资源规划 | × |
| lyzsys-module-iot | 物联网平台 | × |
| lyzsys-module-mall | 商城系统 | × |
| lyzsys-module-member | 会员中心 | × |
| lyzsys-module-mp | 微信公众号 | × |
| lyzsys-module-pay | 支付系统 | × |
| lyzsys-module-report | 报表系统 | × |
#### 4. lyzsys-server
**作用**: 管理后台 + 用户 App 的服务端
**说明**: 后端 Server 的主项目,通过引入需要的 lyzsys-module-xxx 业务模块,提供 RESTful API。
### 模块标准结构
每个业务模块采用统一的目录结构:
```
lyzsys-module-xxx/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── cn/iocoder/lyzsys/module/xxx/
│ │ │ ├── api/ # 对外 API 接口
│ │ │ │ ├── xxxApi.java
│ │ │ │ └── xxxApiImpl.java
│ │ │ ├── controller/ # 控制层
│ │ │ │ ├── admin/ # 管理后台 Controller
│ │ │ │ │ ├── xxxController.java
│ │ │ │ │ └── vo/ # 视图对象
│ │ │ │ │ ├── xxxReqVO.java
│ │ │ │ │ └── xxxRespVO.java
│ │ │ │ └── app/ # 用户 App Controller
│ │ │ ├── service/ # 服务层
│ │ │ │ ├── xxxService.java
│ │ │ │ └── xxxServiceImpl.java
│ │ │ ├── dal/ # 数据访问层
│ │ │ │ ├── dataobject/ # 数据对象
│ │ │ │ │ └── xxxDO.java
│ │ │ │ ├── mysql/ # MyBatis Mapper
│ │ │ │ │ └── xxxMapper.java
│ │ │ │ └── redis/ # Redis 操作
│ │ │ │ └── xxxRedisDAO.java
│ │ │ ├── convert/ # 对象转换器
│ │ │ │ └── xxxConvert.java
│ │ │ ├── enums/ # 枚举类
│ │ │ │ └── xxxEnum.java
│ │ │ └── framework/ # 模块框架配置
│ │ └── resources/
│ │ └── mapper/ # MyBatis XML
│ └── test/
│ └── java/
└── pom.xml
```
---
## 开发指南
### 环境准备
#### 必需软件
| 软件 | 版本要求 | 下载地址 |
|------|----------|----------|
| JDK | 8+ | https://www.oracle.com/java/technologies/downloads/ |
| MySQL | 5.7+ | https://dev.mysql.com/downloads/mysql/ |
| Redis | 5.0+ | https://redis.io/download |
| Maven | 3.6+ | https://maven.apache.org/download.cgi |
| IDE | IntelliJ IDEA 推荐 | https://www.jetbrains.com/idea/ |
#### 配置数据库
1. 创建数据库:
```sql
CREATE DATABASE lyzsys CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
2. 导入 SQL 脚本(位置:`sql/` 目录)
3. 修改配置文件 `lyzsys-server/src/main/resources/application.yaml`
### 快速开始
#### 1. 克隆项目
```bash
git clone https://github.com/your-org/lyzsys.git
cd lyzsys/lyzsys_backend
```
#### 2. 导入 IDE
将项目导入 IntelliJ IDEA
```
File → Open → 选择 lyzsys_backend 目录
```
等待 Maven 依赖下载完成。
#### 3. 启动 Redis
```bash
redis-server
```
#### 4. 运行主类
找到 `lyzsys-server/src/main/java/cn/iocoder/lyzsys/server/LyzsysServerApplication.java`
右键 → Run 'LyzsysServerApplication'
#### 5. 访问应用
- 应用地址: http://localhost:48080
- API 文档: http://localhost:48080/doc.html
- 用户名: admin
- 密码: admin123
### 开发规范
#### 命名规范
**包命名**: 全小写,使用点分隔
```
cn.iocoder.lyzsys.module.system.controller.admin
```
**类命名**: 大驼峰
```
UserController, UserService, UserDO
```
**方法命名**: 小驼峰
```
getUserById(), createUser(), deleteUser()
```
**变量命名**: 小驼峰
```
userName, userAge, isAdmin
```
**常量命名**: 全大写,下划线分隔
```
MAX_SIZE, DEFAULT_PAGE_SIZE, ERROR_CODE
```
#### 注释规范
**类注释**:
```java
/**
* 用户 Controller
*
* @author 芋道源码
*/
@RestController
@RequestMapping("/system/user")
public class UserController {
}
```
**方法注释**:
```java
/**
* 创建用户
*
* @param createReqVO 创建信息
* @return 用户 ID
*/
@PostMapping("/create")
public CommonResult<Long> createUser(@Valid @RequestBody UserCreateReqVO createReqVO) {
return success(userService.createUser(createReqVO));
}
```
#### 代码规范
1. **使用 Lombok 简化代码**:
```java
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserDO extends TenantBaseDO {
private Long id;
private String username;
}
```
2. **使用 MapStruct 进行对象转换**:
```java
@Mapper
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
UserDO convert(UserCreateReqVO bean);
UserRespVO convert(UserDO bean);
}
```
3. **异常处理**:
```java
// 使用全局异常处理器
throw new ServiceException(USER_USERNAME_EXISTS);
// 或使用工具方法
throw exception(USER_USERNAME_EXISTS);
```
### 接口开发
#### Controller 层
```java
@Tag(name = "管理后台 - 用户")
@RestController
@RequestMapping("/system/user")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/create")
@Operation(summary = "创建用户")
@PreAuthorize("@ss.hasPermission('system:user:create')")
public CommonResult<Long> createUser(@Valid @RequestBody UserCreateReqVO createReqVO) {
return success(userService.createUser(createReqVO));
}
@GetMapping("/get")
@Operation(summary = "获取用户详情")
@Parameter(name = "id", description = "用户 ID", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:user:query')")
public CommonResult<UserRespVO> getUser(@RequestParam("id") Long id) {
return success(userService.getUser(id));
}
}
```
#### Service 层
```java
public interface UserService {
Long createUser(UserCreateReqVO createReqVO);
UserRespVO getUser(Long id);
void deleteUser(Long id);
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createUser(UserCreateReqVO createReqVO) {
// 1. 参数校验
validateUserExists(null, createReqVO.getUsername());
// 2. 插入用户
UserDO user = UserConvert.INSTANCE.convert(createReqVO);
user.setPassword(encodePassword(createReqVO.getPassword()));
userMapper.insert(user);
// 3. 返回 ID
return user.getId();
}
@Override
public UserRespVO getUser(Long id) {
UserDO user = userMapper.selectById(id);
return UserConvert.INSTANCE.convert(user);
}
}
```
#### Mapper 层
```java
@Mapper
public interface UserMapper extends BaseMapperX<UserDO> {
default UserDO selectByUsername(String username) {
return selectOne("username", username);
}
default List<UserDO> selectListByDeptIds(Collection<Long> deptIds) {
return selectList(new LambdaQueryWrapperX<UserDO>()
.in(UserDO::getDeptId, deptIds));
}
default PageResult<UserDO> selectPage(UserPageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<UserDO>()
.likeIfPresent(UserDO::getUsername, pageReqVO.getUsername())
.likeIfPresent(UserDO::getMobile, pageReqVO.getMobile())
.betweenIfPresent(UserDO::getCreateTime, pageReqVO.getCreateTime())
.orderByDesc(UserDO::getId));
}
}
```
---
## 核心功能
### 认证授权
#### 认证流程
1. **登录认证**:
```
用户输入用户名密码 → 后端验证 → 生成 JWT Token → 返回 Token
```
2. **Token 验证**:
```
请求携带 Token → 后端解析 Token → 验证有效性 → 允许访问
```
3. **Token 刷新**:
```
Token 即将过期 → 使用刷新令牌 → 获取新 Token
```
#### 权限模型
**RBAC (基于角色的访问控制)**:
```
用户 (User)
角色 (Role)
权限 (Permission)
资源 (Resource)
```
**数据权限**:
- 全部数据权限
- 本部门数据权限
- 本部门及以下数据权限
- 仅本人数据权限
- 自定义数据权限
### 多租户
#### 租户隔离
1. **数据库隔离**: 通过 `tenant_id` 字段隔离
2. **自动过滤**: MyBatis Plus 拦截器自动添加租户条件
3. **租户上下文**: `TenantContextHolder` 管理当前租户
#### 使用示例
```java
// 继承租户基类
@TableName("system_user")
public class UserDO extends TenantBaseDO {
private Long id;
private String username;
// tenant_id 由 TenantBaseDO 提供
}
// 查询自动过滤租户
List<UserDO> users = userMapper.selectList();
// SQL: SELECT * FROM system_user WHERE tenant_id = ?
```
### 工作流 (Flowable)
#### 流程定义
```java
@Service
public class ProcessDefinitionService {
public String createProcessDefinition(ProcessDefinitionCreateReqVO createReqVO) {
// 1. 校验流程模型
validateProcessModel(createReqVO.getModelId());
// 2. 部署流程定义
Deployment deployment = repositoryService.createDeployment()
.name(createReqVO.getName())
.addBytes(createReqVO.getBpmnBytes())
.deploy();
// 3. 返回流程定义 ID
return deployment.getId();
}
}
```
#### 流程实例
```java
@Service
public class ProcessInstanceService {
public String startProcessInstance(ProcessInstanceStartReqVO startReqVO) {
// 1. 启动流程实例
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
startReqVO.getProcessDefinitionKey(),
startReqVO.getBusinessKey()
);
// 2. 完成第一个任务
taskService.complete(instance.getId());
// 3. 返回实例 ID
return instance.getId();
}
}
```
### 缓存管理
#### Redis 缓存
```java
// 使用 RedisTemplate
@Service
@RequiredArgsConstructor
public class UserServiceImpl {
private final RedisTemplate<String, UserDO> redisTemplate;
public UserDO getUser(Long id) {
String key = "user:" + id;
UserDO user = redisTemplate.opsForValue().get(key);
if (user == null) {
user = userMapper.selectById(id);
redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);
}
return user;
}
}
```
#### 缓存注解
```java
@Cacheable(value = "user", key = "#id")
public UserDO getUser(Long id) {
return userMapper.selectById(id);
}
@CachePut(value = "user", key = "#user.id")
public UserDO updateUser(UserDO user) {
userMapper.updateById(user);
return user;
}
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
```
### 消息队列
#### Redis 消息队列
```java
@Component
@RequiredArgsConstructor
public class SmsProducer {
private final RedisTemplate<String, Object> redisTemplate;
public void sendSmsSendMessage(Long userId, String mobile, String content) {
SmsSendMessage message = new SmsSendMessage()
.setUserId(userId)
.setMobile(mobile)
.setContent(content);
redisTemplate.convertAndSend("sms_send", message);
}
}
@Component
@RequiredArgsConstructor
public class SmsSendConsumer {
private final SmsService smsService;
@RedisStreamListener(stream = "sms_send", consumerGroup = "sms_send_group")
public void consumeSmsSendMessage(SmsSendMessage message) {
smsService.sendSms(message.getMobile(), message.getContent());
}
}
```
### 定时任务
#### Quartz 定时任务
```java
@Component
public class DemoJob extends QuartzJobBean {
@Autowired
private UserService userService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// 定时清理过期用户
userService.cleanExpiredUsers();
}
}
```
### 文件管理
#### 文件上传
```java
@RestController
@RequestMapping("/infra/file")
public class FileController {
@PostMapping("/upload")
public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file) {
// 1. 上传文件
String url = fileService.uploadFile(file);
// 2. 返回 URL
return success(url);
}
}
```
#### 文件存储
支持多种存储方式:
- 本地存储
- 阿里云 OSS
- 腾讯云 COS
- MinIO
- FTP / SFTP
### Excel 操作
#### 导入
```java
@PostMapping("/import")
public CommonResult<Integer> importUsers(@RequestParam("file") MultipartFile file) {
List<UserImportExcelVO> list = ExcelUtils.read(file, UserImportExcelVO.class);
return success(userService.importUsers(list));
}
```
#### 导出
```java
@GetMapping("/export")
public void exportUsers(HttpServletResponse response, UserPageReqVO pageReqVO) {
List<UserDO> list = userService.getUserList(pageReqVO);
ExcelUtils.write(response, "用户数据.xls", "用户列表", UserRespVO.class,
BeanConvertUtils.convertList(list, UserRespVO.class));
}
```
---
## 部署指南
### 本地部署
#### 1. 打包项目
```bash
cd lyzsys_backend
mvn clean package -DskipTests
```
#### 2. 上传文件
`lyzsys-server/target/lyzsys-server.jar` 上传到服务器
#### 3. 启动应用
```bash
java -jar lyzsys-server.jar --spring.profiles.active=prod
```
### Docker 部署
#### 1. 构建镜像
```bash
docker build -t lyzsys/lyzsys-server:latest .
```
#### 2. 运行容器
```bash
docker run -d \
--name lyzsys-server \
-p 48080:48080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/lyzsys \
-e SPRING_DATASOURCE_USERNAME=root \
-e SPRING_DATASOURCE_PASSWORD=password \
-e SPRING_DATA_REDIS_HOST=redis \
lyzsys/lyzsys-server:latest
```
### 配置说明
#### application.yaml
```yaml
spring:
# 数据源配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/lyzsys?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
# Redis 配置
redis:
host: 127.0.0.1
port: 6379
database: 1
password:
timeout: 0
# 项目配置
lyzsys:
info:
base-package: cn.iocoder.lyzsys
web:
api-prefix: /admin-api
security:
mock-enable: true
upload:
base-url: http://127.0.0.1:48080
```
---
## 附录
### 常见问题
#### Q1: 如何添加新的业务模块?
1.`lyzsys-module-xxx` 目录下创建新模块
2. 按照标准模块结构创建包和类
3.`lyzsys-server/pom.xml` 中添加模块依赖
4.`application.yaml` 中配置模块扫描路径
#### Q2: 如何自定义异常?
1.`module-system/enums/ErrorCodeConstants.java` 中添加错误码
2. 使用 `throw exception(ERROR_CODE)` 抛出异常
3. 全局异常处理器会自动处理并返回统一格式
#### Q3: 如何实现数据权限?
1.`@Table` 注解中配置 `dataPermission` 属性
2. 使用 `@DataPermission` 注解标记需要数据权限的方法
3. 框架会自动添加数据权限过滤条件
### 参考资源
- [Spring Boot 官方文档](https://spring.io/projects/spring-boot)
- [MyBatis Plus 官方文档](https://baomidou.com/)
- [Flowable 官方文档](https://www.flowable.com/open-source/docs)
- [Redis 官方文档](https://redis.io/documentation)
- [芋道源码文档](https://doc.iocoder.cn/)
---
**文档版本**: v1.0.0
**最后更新**: 2025-01-19
**维护团队**: Lyzsys 开发团队