Showing
167 changed files
with
4857 additions
and
0 deletions
backend/README.md
0 → 100644
| 1 | +# Apple经销商ERP系统 - 后端项目 | ||
| 2 | + | ||
| 3 | +## 项目简介 | ||
| 4 | + | ||
| 5 | +这是Apple经销商ERP系统的后端服务,基于Spring Boot框架开发,提供RESTful API接口。 | ||
| 6 | + | ||
| 7 | +## 技术栈 | ||
| 8 | + | ||
| 9 | +- **框架**: Spring Boot 2.7.x | ||
| 10 | +- **数据库**: MySQL 8.0 | ||
| 11 | +- **缓存**: Redis 6.0 | ||
| 12 | +- **ORM**: MyBatis Plus 3.5.x | ||
| 13 | +- **安全**: Spring Security + JWT | ||
| 14 | +- **构建工具**: Maven 3.8.x | ||
| 15 | +- **JDK版本**: OpenJDK 8 | ||
| 16 | + | ||
| 17 | +## 项目结构 | ||
| 18 | + | ||
| 19 | +``` | ||
| 20 | +backend/ | ||
| 21 | +├── src/ | ||
| 22 | +│ ├── main/ | ||
| 23 | +│ │ ├── java/ | ||
| 24 | +│ │ │ └── com/apple/erp/ | ||
| 25 | +│ │ │ ├── AppleErpApplication.java | ||
| 26 | +│ │ │ ├── config/ # 配置类 | ||
| 27 | +│ │ │ ├── controller/ # 控制器层 | ||
| 28 | +│ │ │ ├── service/ # 服务层 | ||
| 29 | +│ │ │ ├── mapper/ # 数据访问层 | ||
| 30 | +│ │ │ ├── entity/ # 实体类 | ||
| 31 | +│ │ │ ├── dto/ # 数据传输对象 | ||
| 32 | +│ │ │ ├── vo/ # 视图对象 | ||
| 33 | +│ │ │ ├── utils/ # 工具类 | ||
| 34 | +│ │ │ ├── exception/ # 异常处理 | ||
| 35 | +│ │ │ ├── aspect/ # 切面 | ||
| 36 | +│ │ │ └── enums/ # 枚举类 | ||
| 37 | +│ │ └── resources/ | ||
| 38 | +│ │ ├── application.yml # 主配置文件 | ||
| 39 | +│ │ ├── mapper/ # MyBatis映射文件 | ||
| 40 | +│ │ ├── static/ # 静态资源 | ||
| 41 | +│ │ └── templates/ # 模板文件 | ||
| 42 | +│ └── test/ # 测试代码 | ||
| 43 | +├── target/ # 编译输出目录 | ||
| 44 | +├── pom.xml # Maven配置文件 | ||
| 45 | +└── README.md # 项目说明 | ||
| 46 | +``` | ||
| 47 | + | ||
| 48 | +## 核心功能模块 | ||
| 49 | + | ||
| 50 | +### 系统管理模块 | ||
| 51 | +- 用户管理 (SysUser) | ||
| 52 | +- 角色管理 (SysRole) | ||
| 53 | +- 菜单管理 (SysMenu) | ||
| 54 | +- 字典管理 (SysDict) | ||
| 55 | +- 日志管理 (SysLog) | ||
| 56 | + | ||
| 57 | +### 业务功能模块 | ||
| 58 | +- 订单管理 (Order) | ||
| 59 | +- 出库管理 (Delivery) | ||
| 60 | +- 发票管理 (Invoice) | ||
| 61 | +- 返利管理 (Rebate) | ||
| 62 | +- 异常处理 (Exception) | ||
| 63 | +- 产品管理 (Product) | ||
| 64 | +- 经销商管理 (Dealer) | ||
| 65 | + | ||
| 66 | +## 开发规范 | ||
| 67 | + | ||
| 68 | +### 代码规范 | ||
| 69 | +- 遵循阿里巴巴Java开发手册 | ||
| 70 | +- 使用统一的代码格式化配置 | ||
| 71 | +- 类名使用大驼峰命名 | ||
| 72 | +- 方法名和变量名使用小驼峰命名 | ||
| 73 | +- 常量使用全大写下划线分隔 | ||
| 74 | + | ||
| 75 | +### 接口规范 | ||
| 76 | +- RESTful API设计 | ||
| 77 | +- 统一返回格式 | ||
| 78 | +- 统一异常处理 | ||
| 79 | +- 接口文档使用Swagger | ||
| 80 | + | ||
| 81 | +### 数据库规范 | ||
| 82 | +- 表名使用下划线分隔 | ||
| 83 | +- 字段名使用下划线分隔 | ||
| 84 | +- 主键统一使用id | ||
| 85 | +- 创建时间字段:create_time | ||
| 86 | +- 更新时间字段:update_time | ||
| 87 | +- 逻辑删除字段:deleted | ||
| 88 | + | ||
| 89 | +## 环境配置 | ||
| 90 | + | ||
| 91 | +### 开发环境 | ||
| 92 | +```yaml | ||
| 93 | +spring: | ||
| 94 | + datasource: | ||
| 95 | + url: jdbc:mysql://localhost:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai | ||
| 96 | + username: root | ||
| 97 | + password: MySQL@123 | ||
| 98 | + driver-class-name: com.mysql.cj.jdbc.Driver | ||
| 99 | +``` | ||
| 100 | + | ||
| 101 | +### 测试环境 | ||
| 102 | +```yaml | ||
| 103 | +spring: | ||
| 104 | + datasource: | ||
| 105 | + url: jdbc:mysql://test-server:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai | ||
| 106 | + username: test_user | ||
| 107 | + password: test_password | ||
| 108 | + driver-class-name: com.mysql.cj.jdbc.Driver | ||
| 109 | +``` | ||
| 110 | + | ||
| 111 | +### 生产环境 | ||
| 112 | +```yaml | ||
| 113 | +spring: | ||
| 114 | + datasource: | ||
| 115 | + url: jdbc:mysql://prod-server:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai | ||
| 116 | + username: prod_user | ||
| 117 | + password: ${DB_PASSWORD} | ||
| 118 | + driver-class-name: com.mysql.cj.jdbc.Driver | ||
| 119 | +``` | ||
| 120 | + | ||
| 121 | +## 快速开始 | ||
| 122 | + | ||
| 123 | +### 1. 环境准备 | ||
| 124 | +- 安装JDK 8+ | ||
| 125 | +- 安装Maven 3.8+ | ||
| 126 | +- 安装MySQL 8.0+ | ||
| 127 | +- 安装Redis 6.0+ | ||
| 128 | + | ||
| 129 | +### 2. 数据库初始化 | ||
| 130 | +```bash | ||
| 131 | +# 创建数据库 | ||
| 132 | +mysql -u root -p < ../database/scripts/init/01_create_database.sql | ||
| 133 | + | ||
| 134 | +# 创建表结构 | ||
| 135 | +mysql -u root -p apple_erp < ../database/scripts/init/02_create_tables.sql | ||
| 136 | + | ||
| 137 | +# 初始化数据 | ||
| 138 | +mysql -u root -p apple_erp < ../database/scripts/data/01_init_system_data.sql | ||
| 139 | +``` | ||
| 140 | + | ||
| 141 | +### 3. 启动服务 | ||
| 142 | +```bash | ||
| 143 | +# 编译项目 | ||
| 144 | +mvn clean compile | ||
| 145 | + | ||
| 146 | +# 运行测试 | ||
| 147 | +mvn test | ||
| 148 | + | ||
| 149 | +# 启动服务 | ||
| 150 | +mvn spring-boot:run | ||
| 151 | +``` | ||
| 152 | + | ||
| 153 | +### 4. 访问接口 | ||
| 154 | +- 服务地址: http://localhost:8083 | ||
| 155 | +- API文档: http://localhost:8083/swagger-ui.html | ||
| 156 | +- 健康检查: http://localhost:8083/actuator/health | ||
| 157 | + | ||
| 158 | +## 开发指南 | ||
| 159 | + | ||
| 160 | +### 1. 新增业务模块 | ||
| 161 | +1. 创建Entity实体类 | ||
| 162 | +2. 创建Mapper接口和XML文件 | ||
| 163 | +3. 创建Service接口和实现类 | ||
| 164 | +4. 创建Controller控制器 | ||
| 165 | +5. 创建DTO和VO类 | ||
| 166 | +6. 编写单元测试 | ||
| 167 | + | ||
| 168 | +### 2. 数据库操作 | ||
| 169 | +- 使用MyBatis Plus进行数据库操作 | ||
| 170 | +- 复杂查询使用XML映射文件 | ||
| 171 | +- 简单CRUD使用注解方式 | ||
| 172 | + | ||
| 173 | +### 3. 异常处理 | ||
| 174 | +- 使用全局异常处理器 | ||
| 175 | +- 自定义业务异常类 | ||
| 176 | +- 统一错误码定义 | ||
| 177 | + | ||
| 178 | +### 4. 日志记录 | ||
| 179 | +- 使用SLF4J + Logback | ||
| 180 | +- 操作日志使用AOP记录 | ||
| 181 | +- 错误日志记录详细堆栈信息 | ||
| 182 | + | ||
| 183 | +## 测试 | ||
| 184 | + | ||
| 185 | +### 单元测试 | ||
| 186 | +```bash | ||
| 187 | +mvn test | ||
| 188 | +``` | ||
| 189 | + | ||
| 190 | +### 集成测试 | ||
| 191 | +```bash | ||
| 192 | +mvn verify | ||
| 193 | +``` | ||
| 194 | + | ||
| 195 | +### 性能测试 | ||
| 196 | +```bash | ||
| 197 | +mvn test -Dtest=PerformanceTest | ||
| 198 | +``` | ||
| 199 | + | ||
| 200 | +## 部署 | ||
| 201 | + | ||
| 202 | +### Docker部署 | ||
| 203 | +```bash | ||
| 204 | +# 构建镜像 | ||
| 205 | +docker build -t apple-erp-backend . | ||
| 206 | + | ||
| 207 | +# 运行容器 | ||
| 208 | +docker run -d -p 8083:8083 apple-erp-backend | ||
| 209 | +``` | ||
| 210 | + | ||
| 211 | +### 传统部署 | ||
| 212 | +```bash | ||
| 213 | +# 打包 | ||
| 214 | +mvn clean package | ||
| 215 | + | ||
| 216 | +# 运行 | ||
| 217 | +java -jar target/apple-erp-backend.jar | ||
| 218 | +``` | ||
| 219 | + | ||
| 220 | +## 监控 | ||
| 221 | + | ||
| 222 | +### 健康检查 | ||
| 223 | +- Spring Boot Actuator | ||
| 224 | +- 自定义健康检查指标 | ||
| 225 | + | ||
| 226 | +### 日志监控 | ||
| 227 | +- 应用日志 | ||
| 228 | +- 访问日志 | ||
| 229 | +- 错误日志 | ||
| 230 | + | ||
| 231 | +### 性能监控 | ||
| 232 | +- JVM监控 | ||
| 233 | +- 数据库监控 | ||
| 234 | +- 接口性能监控 | ||
| 235 | + | ||
| 236 | +## 常见问题 | ||
| 237 | + | ||
| 238 | +### 1. 数据库连接问题 | ||
| 239 | +- 检查数据库服务是否启动 | ||
| 240 | +- 检查连接配置是否正确 | ||
| 241 | +- 检查网络连接是否正常 | ||
| 242 | + | ||
| 243 | +### 2. Redis连接问题 | ||
| 244 | +- 检查Redis服务是否启动 | ||
| 245 | +- 检查连接配置是否正确 | ||
| 246 | +- 检查防火墙设置 | ||
| 247 | + | ||
| 248 | +### 3. 端口占用问题 | ||
| 249 | +- 检查端口是否被占用 | ||
| 250 | +- 修改配置文件中的端口号 | ||
| 251 | +- 停止占用端口的进程 | ||
| 252 | + | ||
| 253 | +## 更新日志 | ||
| 254 | + | ||
| 255 | +### v1.0.0 (2024-01-01) | ||
| 256 | +- 初始版本发布 | ||
| 257 | +- 基础功能模块完成 | ||
| 258 | +- 系统管理模块完成 | ||
| 259 | + | ||
| 260 | +## 贡献指南 | ||
| 261 | + | ||
| 262 | +1. Fork项目 | ||
| 263 | +2. 创建功能分支 | ||
| 264 | +3. 提交代码 | ||
| 265 | +4. 创建Pull Request | ||
| 266 | + | ||
| 267 | +## 许可证 | ||
| 268 | + | ||
| 269 | +MIT License |
backend/pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| 3 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 4 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 | ||
| 5 | + http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| 6 | + <modelVersion>4.0.0</modelVersion> | ||
| 7 | + | ||
| 8 | + <parent> | ||
| 9 | + <groupId>org.springframework.boot</groupId> | ||
| 10 | + <artifactId>spring-boot-starter-parent</artifactId> | ||
| 11 | + <version>2.7.18</version> | ||
| 12 | + <relativePath/> | ||
| 13 | + </parent> | ||
| 14 | + | ||
| 15 | + <groupId>com.apple</groupId> | ||
| 16 | + <artifactId>apple-erp-backend</artifactId> | ||
| 17 | + <version>1.0.0</version> | ||
| 18 | + <name>Apple经销商ERP系统后端</name> | ||
| 19 | + <description>Apple经销商ERP系统后端服务</description> | ||
| 20 | + <packaging>jar</packaging> | ||
| 21 | + | ||
| 22 | + <properties> | ||
| 23 | + <java.version>8</java.version> | ||
| 24 | + <maven.compiler.source>8</maven.compiler.source> | ||
| 25 | + <maven.compiler.target>8</maven.compiler.target> | ||
| 26 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| 27 | + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||
| 28 | + | ||
| 29 | + <!-- 依赖版本管理 --> | ||
| 30 | + <mybatis-plus.version>3.5.3.1</mybatis-plus.version> | ||
| 31 | + <druid.version>1.2.18</druid.version> | ||
| 32 | + <jwt.version>0.11.5</jwt.version> | ||
| 33 | + <hutool.version>5.8.20</hutool.version> | ||
| 34 | + <fastjson.version>2.0.32</fastjson.version> | ||
| 35 | + <poi.version>5.2.3</poi.version> | ||
| 36 | + <knife4j.version>3.0.3</knife4j.version> | ||
| 37 | + <springdoc.version>1.7.0</springdoc.version> | ||
| 38 | + </properties> | ||
| 39 | + | ||
| 40 | + <dependencies> | ||
| 41 | + <!-- Spring Boot Web Starter --> | ||
| 42 | + <dependency> | ||
| 43 | + <groupId>org.springframework.boot</groupId> | ||
| 44 | + <artifactId>spring-boot-starter-web</artifactId> | ||
| 45 | + </dependency> | ||
| 46 | + | ||
| 47 | + <!-- Spring Boot Security Starter --> | ||
| 48 | + <dependency> | ||
| 49 | + <groupId>org.springframework.boot</groupId> | ||
| 50 | + <artifactId>spring-boot-starter-security</artifactId> | ||
| 51 | + </dependency> | ||
| 52 | + | ||
| 53 | + <!-- Spring Boot Validation Starter --> | ||
| 54 | + <dependency> | ||
| 55 | + <groupId>org.springframework.boot</groupId> | ||
| 56 | + <artifactId>spring-boot-starter-validation</artifactId> | ||
| 57 | + </dependency> | ||
| 58 | + | ||
| 59 | + <!-- Spring Boot Data Redis Starter --> | ||
| 60 | + <dependency> | ||
| 61 | + <groupId>org.springframework.boot</groupId> | ||
| 62 | + <artifactId>spring-boot-starter-data-redis</artifactId> | ||
| 63 | + </dependency> | ||
| 64 | + | ||
| 65 | + <!-- Spring Boot AOP Starter --> | ||
| 66 | + <dependency> | ||
| 67 | + <groupId>org.springframework.boot</groupId> | ||
| 68 | + <artifactId>spring-boot-starter-aop</artifactId> | ||
| 69 | + </dependency> | ||
| 70 | + | ||
| 71 | + <!-- Spring Boot Mail Starter --> | ||
| 72 | + <dependency> | ||
| 73 | + <groupId>org.springframework.boot</groupId> | ||
| 74 | + <artifactId>spring-boot-starter-mail</artifactId> | ||
| 75 | + </dependency> | ||
| 76 | + | ||
| 77 | + <!-- Spring Boot Quartz Starter --> | ||
| 78 | + <dependency> | ||
| 79 | + <groupId>org.springframework.boot</groupId> | ||
| 80 | + <artifactId>spring-boot-starter-quartz</artifactId> | ||
| 81 | + </dependency> | ||
| 82 | + | ||
| 83 | + <!-- MySQL Driver --> | ||
| 84 | + <dependency> | ||
| 85 | + <groupId>com.mysql</groupId> | ||
| 86 | + <artifactId>mysql-connector-j</artifactId> | ||
| 87 | + <scope>runtime</scope> | ||
| 88 | + </dependency> | ||
| 89 | + | ||
| 90 | + <!-- H2 Database for testing --> | ||
| 91 | + <dependency> | ||
| 92 | + <groupId>com.h2database</groupId> | ||
| 93 | + <artifactId>h2</artifactId> | ||
| 94 | + <scope>runtime</scope> | ||
| 95 | + </dependency> | ||
| 96 | + | ||
| 97 | + <!-- Druid 数据库连接池 --> | ||
| 98 | + <dependency> | ||
| 99 | + <groupId>com.alibaba</groupId> | ||
| 100 | + <artifactId>druid-spring-boot-starter</artifactId> | ||
| 101 | + <version>${druid.version}</version> | ||
| 102 | + </dependency> | ||
| 103 | + | ||
| 104 | + <!-- MyBatis Plus --> | ||
| 105 | + <dependency> | ||
| 106 | + <groupId>com.baomidou</groupId> | ||
| 107 | + <artifactId>mybatis-plus-boot-starter</artifactId> | ||
| 108 | + <version>${mybatis-plus.version}</version> | ||
| 109 | + </dependency> | ||
| 110 | + | ||
| 111 | + <!-- MyBatis Plus 代码生成器 --> | ||
| 112 | + <dependency> | ||
| 113 | + <groupId>com.baomidou</groupId> | ||
| 114 | + <artifactId>mybatis-plus-generator</artifactId> | ||
| 115 | + <version>${mybatis-plus.version}</version> | ||
| 116 | + </dependency> | ||
| 117 | + | ||
| 118 | + <!-- JWT --> | ||
| 119 | + <dependency> | ||
| 120 | + <groupId>io.jsonwebtoken</groupId> | ||
| 121 | + <artifactId>jjwt-api</artifactId> | ||
| 122 | + <version>${jwt.version}</version> | ||
| 123 | + </dependency> | ||
| 124 | + <dependency> | ||
| 125 | + <groupId>io.jsonwebtoken</groupId> | ||
| 126 | + <artifactId>jjwt-impl</artifactId> | ||
| 127 | + <version>${jwt.version}</version> | ||
| 128 | + <scope>runtime</scope> | ||
| 129 | + </dependency> | ||
| 130 | + <dependency> | ||
| 131 | + <groupId>io.jsonwebtoken</groupId> | ||
| 132 | + <artifactId>jjwt-jackson</artifactId> | ||
| 133 | + <version>${jwt.version}</version> | ||
| 134 | + <scope>runtime</scope> | ||
| 135 | + </dependency> | ||
| 136 | + | ||
| 137 | + <!-- Hutool 工具类 --> | ||
| 138 | + <dependency> | ||
| 139 | + <groupId>cn.hutool</groupId> | ||
| 140 | + <artifactId>hutool-all</artifactId> | ||
| 141 | + <version>${hutool.version}</version> | ||
| 142 | + </dependency> | ||
| 143 | + | ||
| 144 | + <!-- FastJSON --> | ||
| 145 | + <dependency> | ||
| 146 | + <groupId>com.alibaba</groupId> | ||
| 147 | + <artifactId>fastjson</artifactId> | ||
| 148 | + <version>${fastjson.version}</version> | ||
| 149 | + </dependency> | ||
| 150 | + | ||
| 151 | + <!-- Apache POI Excel处理 --> | ||
| 152 | + <dependency> | ||
| 153 | + <groupId>org.apache.poi</groupId> | ||
| 154 | + <artifactId>poi</artifactId> | ||
| 155 | + <version>${poi.version}</version> | ||
| 156 | + </dependency> | ||
| 157 | + <dependency> | ||
| 158 | + <groupId>org.apache.poi</groupId> | ||
| 159 | + <artifactId>poi-ooxml</artifactId> | ||
| 160 | + <version>${poi.version}</version> | ||
| 161 | + </dependency> | ||
| 162 | + | ||
| 163 | + <!-- Knife4j API文档 - 暂时移除 --> | ||
| 164 | + <!-- <dependency> | ||
| 165 | + <groupId>com.github.xiaoymin</groupId> | ||
| 166 | + <artifactId>knife4j-spring-boot-starter</artifactId> | ||
| 167 | + <version>${knife4j.version}</version> | ||
| 168 | + </dependency> --> | ||
| 169 | + | ||
| 170 | + <!-- SpringDoc OpenAPI --> | ||
| 171 | + <dependency> | ||
| 172 | + <groupId>org.springdoc</groupId> | ||
| 173 | + <artifactId>springdoc-openapi-ui</artifactId> | ||
| 174 | + <version>${springdoc.version}</version> | ||
| 175 | + </dependency> | ||
| 176 | + | ||
| 177 | + <!-- SpringDoc OpenAPI Security --> | ||
| 178 | + <dependency> | ||
| 179 | + <groupId>org.springdoc</groupId> | ||
| 180 | + <artifactId>springdoc-openapi-security</artifactId> | ||
| 181 | + <version>${springdoc.version}</version> | ||
| 182 | + </dependency> | ||
| 183 | + | ||
| 184 | + <!-- Spring Boot Actuator 监控 --> | ||
| 185 | + <dependency> | ||
| 186 | + <groupId>org.springframework.boot</groupId> | ||
| 187 | + <artifactId>spring-boot-starter-actuator</artifactId> | ||
| 188 | + </dependency> | ||
| 189 | + | ||
| 190 | + <!-- Lombok --> | ||
| 191 | + <dependency> | ||
| 192 | + <groupId>org.projectlombok</groupId> | ||
| 193 | + <artifactId>lombok</artifactId> | ||
| 194 | + <optional>true</optional> | ||
| 195 | + </dependency> | ||
| 196 | + | ||
| 197 | + <!-- Spring Boot Test Starter --> | ||
| 198 | + <dependency> | ||
| 199 | + <groupId>org.springframework.boot</groupId> | ||
| 200 | + <artifactId>spring-boot-starter-test</artifactId> | ||
| 201 | + <scope>test</scope> | ||
| 202 | + </dependency> | ||
| 203 | + | ||
| 204 | + <!-- Spring Security Test --> | ||
| 205 | + <dependency> | ||
| 206 | + <groupId>org.springframework.security</groupId> | ||
| 207 | + <artifactId>spring-security-test</artifactId> | ||
| 208 | + <scope>test</scope> | ||
| 209 | + </dependency> | ||
| 210 | + </dependencies> | ||
| 211 | + | ||
| 212 | + <build> | ||
| 213 | + <plugins> | ||
| 214 | + <!-- Spring Boot Maven Plugin --> | ||
| 215 | + <plugin> | ||
| 216 | + <groupId>org.springframework.boot</groupId> | ||
| 217 | + <artifactId>spring-boot-maven-plugin</artifactId> | ||
| 218 | + <version>2.7.18</version> | ||
| 219 | + <configuration> | ||
| 220 | + <excludes> | ||
| 221 | + <exclude> | ||
| 222 | + <groupId>org.projectlombok</groupId> | ||
| 223 | + <artifactId>lombok</artifactId> | ||
| 224 | + </exclude> | ||
| 225 | + </excludes> | ||
| 226 | + </configuration> | ||
| 227 | + </plugin> | ||
| 228 | + | ||
| 229 | + <!-- Maven Compiler Plugin --> | ||
| 230 | + <plugin> | ||
| 231 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 232 | + <artifactId>maven-compiler-plugin</artifactId> | ||
| 233 | + <version>3.8.1</version> | ||
| 234 | + <configuration> | ||
| 235 | + <source>8</source> | ||
| 236 | + <target>8</target> | ||
| 237 | + <encoding>UTF-8</encoding> | ||
| 238 | + </configuration> | ||
| 239 | + </plugin> | ||
| 240 | + | ||
| 241 | + <!-- Maven Surefire Plugin --> | ||
| 242 | + <plugin> | ||
| 243 | + <groupId>org.apache.maven.plugins</groupId> | ||
| 244 | + <artifactId>maven-surefire-plugin</artifactId> | ||
| 245 | + <version>2.22.2</version> | ||
| 246 | + <configuration> | ||
| 247 | + <skipTests>false</skipTests> | ||
| 248 | + </configuration> | ||
| 249 | + </plugin> | ||
| 250 | + </plugins> | ||
| 251 | + </build> | ||
| 252 | + | ||
| 253 | + <!-- 仓库配置 --> | ||
| 254 | + <repositories> | ||
| 255 | + <repository> | ||
| 256 | + <id>central</id> | ||
| 257 | + <name>Central Repository</name> | ||
| 258 | + <url>https://repo1.maven.org/maven2</url> | ||
| 259 | + </repository> | ||
| 260 | + <repository> | ||
| 261 | + <id>aliyun</id> | ||
| 262 | + <name>Aliyun Repository</name> | ||
| 263 | + <url>https://maven.aliyun.com/repository/public</url> | ||
| 264 | + </repository> | ||
| 265 | + </repositories> | ||
| 266 | + | ||
| 267 | + <pluginRepositories> | ||
| 268 | + <pluginRepository> | ||
| 269 | + <id>central</id> | ||
| 270 | + <name>Central Repository</name> | ||
| 271 | + <url>https://repo1.maven.org/maven2</url> | ||
| 272 | + </pluginRepository> | ||
| 273 | + <pluginRepository> | ||
| 274 | + <id>aliyun</id> | ||
| 275 | + <name>Aliyun Repository</name> | ||
| 276 | + <url>https://maven.aliyun.com/repository/public</url> | ||
| 277 | + </pluginRepository> | ||
| 278 | + </pluginRepositories> | ||
| 279 | +</project> |
| 1 | +package com.apple.erp; | ||
| 2 | + | ||
| 3 | +import org.mybatis.spring.annotation.MapperScan; | ||
| 4 | +import org.springframework.boot.SpringApplication; | ||
| 5 | +import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
| 6 | +import org.springframework.cache.annotation.EnableCaching; | ||
| 7 | +import org.springframework.scheduling.annotation.EnableAsync; | ||
| 8 | +import org.springframework.scheduling.annotation.EnableScheduling; | ||
| 9 | +import org.springframework.transaction.annotation.EnableTransactionManagement; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * Apple经销商ERP系统启动类 | ||
| 13 | + * | ||
| 14 | + * @author Apple ERP Team | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @since 2024-01-01 | ||
| 17 | + */ | ||
| 18 | +@SpringBootApplication | ||
| 19 | +@EnableTransactionManagement | ||
| 20 | +@EnableCaching | ||
| 21 | +@EnableAsync | ||
| 22 | +@EnableScheduling | ||
| 23 | +public class AppleErpApplication { | ||
| 24 | + | ||
| 25 | + public static void main(String[] args) { | ||
| 26 | + // 设置系统属性 | ||
| 27 | + System.setProperty("spring.devtools.restart.enabled", "false"); | ||
| 28 | + System.setProperty("spring.devtools.livereload.enabled", "false"); | ||
| 29 | + | ||
| 30 | + // 启动Spring Boot应用 | ||
| 31 | + SpringApplication.run(AppleErpApplication.class, args); | ||
| 32 | + | ||
| 33 | + System.out.println("================================="); | ||
| 34 | + System.out.println("Apple经销商ERP系统启动成功!"); | ||
| 35 | + System.out.println("访问地址: http://localhost:8083"); | ||
| 36 | + System.out.println("API文档: http://localhost:8083/doc.html"); | ||
| 37 | + System.out.println("数据库监控: http://localhost:8083/druid"); | ||
| 38 | + System.out.println("================================="); | ||
| 39 | + } | ||
| 40 | +} |
| 1 | +# Apple经销商ERP系统 - 数据库认证功能说明 | ||
| 2 | + | ||
| 3 | +## 概述 | ||
| 4 | + | ||
| 5 | +系统已成功从硬编码认证改为数据库认证,现在支持从数据库读取用户信息、角色和权限进行JWT认证。 | ||
| 6 | + | ||
| 7 | +## 主要变更 | ||
| 8 | + | ||
| 9 | +### 1. 新增文件 | ||
| 10 | + | ||
| 11 | +- `entity/SysUser.java` - 系统用户实体类 | ||
| 12 | +- `entity/SysRole.java` - 系统角色实体类 | ||
| 13 | +- `mapper/SysUserMapper.java` - 用户数据访问接口 | ||
| 14 | +- `service/SysUserService.java` - 用户服务接口 | ||
| 15 | +- `service/impl/SysUserServiceImpl.java` - 用户服务实现 | ||
| 16 | +- `resources/mapper/SysUserMapper.xml` - MyBatis映射文件 | ||
| 17 | + | ||
| 18 | +### 2. 修改文件 | ||
| 19 | + | ||
| 20 | +- `config/UserDetailsServiceImpl.java` - 改为从数据库读取用户信息 | ||
| 21 | +- `controller/AuthController.java` - 添加登录信息记录功能 | ||
| 22 | + | ||
| 23 | +## 功能特性 | ||
| 24 | + | ||
| 25 | +### 1. 数据库认证 | ||
| 26 | +- ✅ 从`t_sys_user`表读取用户信息 | ||
| 27 | +- ✅ 支持BCrypt密码加密验证 | ||
| 28 | +- ✅ 动态角色和权限管理 | ||
| 29 | +- ✅ 用户状态检查(启用/停用) | ||
| 30 | + | ||
| 31 | +### 2. 权限管理 | ||
| 32 | +- ✅ 从`t_sys_role`表获取用户角色 | ||
| 33 | +- ✅ 从`t_sys_menu`表获取菜单权限 | ||
| 34 | +- ✅ 支持角色-菜单关联权限 | ||
| 35 | +- ✅ 动态权限加载 | ||
| 36 | + | ||
| 37 | +### 3. 登录记录 | ||
| 38 | +- ✅ 记录用户最后登录时间 | ||
| 39 | +- ✅ 记录用户最后登录IP | ||
| 40 | +- ✅ 支持获取客户端真实IP | ||
| 41 | + | ||
| 42 | +## 默认账号 | ||
| 43 | + | ||
| 44 | +| 用户名 | 密码 | 角色 | 权限说明 | | ||
| 45 | +|--------|------|------|----------| | ||
| 46 | +| admin | admin123 | 系统管理员 | 拥有所有权限 | | ||
| 47 | +| operator | operator123 | 运营人员 | 拥有业务操作权限 | | ||
| 48 | +| auditor | auditor123 | 审核人员 | 拥有审核权限 | | ||
| 49 | + | ||
| 50 | +## 数据库表结构 | ||
| 51 | + | ||
| 52 | +### 核心表 | ||
| 53 | +- `t_sys_user` - 系统用户表 | ||
| 54 | +- `t_sys_role` - 系统角色表 | ||
| 55 | +- `t_sys_user_role` - 用户角色关联表 | ||
| 56 | +- `t_sys_menu` - 系统菜单表 | ||
| 57 | +- `t_sys_role_menu` - 角色菜单关联表 | ||
| 58 | + | ||
| 59 | +### 认证流程 | ||
| 60 | +1. 用户登录时,系统从`t_sys_user`表查询用户信息 | ||
| 61 | +2. 验证BCrypt加密的密码 | ||
| 62 | +3. 查询用户关联的角色信息 | ||
| 63 | +4. 查询角色关联的菜单权限 | ||
| 64 | +5. 构建Spring Security的UserDetails对象 | ||
| 65 | +6. 生成JWT令牌 | ||
| 66 | + | ||
| 67 | +## API接口 | ||
| 68 | + | ||
| 69 | +### 登录接口 | ||
| 70 | +```http | ||
| 71 | +POST /api/auth/login | ||
| 72 | +Content-Type: application/json | ||
| 73 | + | ||
| 74 | +{ | ||
| 75 | + "username": "admin", | ||
| 76 | + "password": "admin123" | ||
| 77 | +} | ||
| 78 | +``` | ||
| 79 | + | ||
| 80 | +### 响应示例 | ||
| 81 | +```json | ||
| 82 | +{ | ||
| 83 | + "code": 200, | ||
| 84 | + "message": "登录成功", | ||
| 85 | + "data": { | ||
| 86 | + "token": "eyJhbGciOiJIUzUxMiJ9...", | ||
| 87 | + "refreshToken": "eyJhbGciOiJIUzUxMiJ9...", | ||
| 88 | + "username": "admin" | ||
| 89 | + } | ||
| 90 | +} | ||
| 91 | +``` | ||
| 92 | + | ||
| 93 | +## 测试方法 | ||
| 94 | + | ||
| 95 | +### 1. 数据库测试 | ||
| 96 | +执行测试脚本验证数据库结构: | ||
| 97 | +```sql | ||
| 98 | +-- 执行测试脚本 | ||
| 99 | +source database/scripts/05_test_database_auth.sql | ||
| 100 | +``` | ||
| 101 | + | ||
| 102 | +### 2. API测试 | ||
| 103 | +使用Postman或curl测试登录接口: | ||
| 104 | +```bash | ||
| 105 | +curl -X POST http://localhost:8080/api/auth/login \ | ||
| 106 | + -H "Content-Type: application/json" \ | ||
| 107 | + -d '{"username":"admin","password":"admin123"}' | ||
| 108 | +``` | ||
| 109 | + | ||
| 110 | +### 3. 权限测试 | ||
| 111 | +登录后使用JWT令牌访问受保护的接口: | ||
| 112 | +```bash | ||
| 113 | +curl -X GET http://localhost:8080/api/auth/userinfo \ | ||
| 114 | + -H "Authorization: Bearer YOUR_JWT_TOKEN" | ||
| 115 | +``` | ||
| 116 | + | ||
| 117 | +## 配置说明 | ||
| 118 | + | ||
| 119 | +### 1. 数据库配置 | ||
| 120 | +确保`application.yml`中配置了正确的数据库连接: | ||
| 121 | +```yaml | ||
| 122 | +spring: | ||
| 123 | + datasource: | ||
| 124 | + url: jdbc:mysql://localhost:3306/apple_erp | ||
| 125 | + username: apple_erp | ||
| 126 | + password: apple_erp123 | ||
| 127 | +``` | ||
| 128 | + | ||
| 129 | +### 2. MyBatis配置 | ||
| 130 | +确保`application.yml`中配置了MyBatis映射文件路径: | ||
| 131 | +```yaml | ||
| 132 | +mybatis-plus: | ||
| 133 | + mapper-locations: classpath:mapper/*.xml | ||
| 134 | +``` | ||
| 135 | + | ||
| 136 | +## 安全特性 | ||
| 137 | + | ||
| 138 | +### 1. 密码安全 | ||
| 139 | +- 使用BCrypt加密存储密码 | ||
| 140 | +- 支持密码强度验证 | ||
| 141 | +- 防止密码明文存储 | ||
| 142 | + | ||
| 143 | +### 2. 权限控制 | ||
| 144 | +- 基于角色的访问控制(RBAC) | ||
| 145 | +- 细粒度权限管理 | ||
| 146 | +- 动态权限加载 | ||
| 147 | + | ||
| 148 | +### 3. 会话管理 | ||
| 149 | +- JWT令牌认证 | ||
| 150 | +- 令牌过期机制 | ||
| 151 | +- 刷新令牌支持 | ||
| 152 | + | ||
| 153 | +## 故障排除 | ||
| 154 | + | ||
| 155 | +### 1. 认证失败 | ||
| 156 | +- 检查用户名和密码是否正确 | ||
| 157 | +- 确认用户状态为启用(status=1) | ||
| 158 | +- 验证数据库连接是否正常 | ||
| 159 | + | ||
| 160 | +### 2. 权限不足 | ||
| 161 | +- 检查用户是否分配了角色 | ||
| 162 | +- 确认角色是否关联了菜单权限 | ||
| 163 | +- 验证角色和菜单状态是否启用 | ||
| 164 | + | ||
| 165 | +### 3. 数据库连接问题 | ||
| 166 | +- 检查数据库服务是否启动 | ||
| 167 | +- 验证连接配置是否正确 | ||
| 168 | +- 确认数据库表是否创建完成 | ||
| 169 | + | ||
| 170 | +## 扩展功能 | ||
| 171 | + | ||
| 172 | +### 1. 添加新用户 | ||
| 173 | +```sql | ||
| 174 | +INSERT INTO t_sys_user (username, real_name, password, phone, email, status, remark, create_by) | ||
| 175 | +VALUES ('newuser', '新用户', '$2a$10$...', '13800138000', 'newuser@example.com', 1, '新用户', 'admin'); | ||
| 176 | +``` | ||
| 177 | + | ||
| 178 | +### 2. 分配角色 | ||
| 179 | +```sql | ||
| 180 | +INSERT INTO t_sys_user_role (user_id, role_id, create_by) | ||
| 181 | +VALUES (用户ID, 角色ID, 'admin'); | ||
| 182 | +``` | ||
| 183 | + | ||
| 184 | +### 3. 修改密码 | ||
| 185 | +```sql | ||
| 186 | +UPDATE t_sys_user | ||
| 187 | +SET password = '$2a$10$新的BCrypt哈希' | ||
| 188 | +WHERE username = '用户名'; | ||
| 189 | +``` | ||
| 190 | + | ||
| 191 | +## 注意事项 | ||
| 192 | + | ||
| 193 | +1. **密码加密**: 所有密码必须使用BCrypt加密存储 | ||
| 194 | +2. **软删除**: 使用`del_flag`字段进行软删除,不要物理删除数据 | ||
| 195 | +3. **状态管理**: 用户和角色都有状态字段,停用后无法登录 | ||
| 196 | +4. **权限缓存**: 权限信息在用户登录时加载,修改权限后需要重新登录生效 | ||
| 197 | +5. **日志记录**: 系统会记录用户登录时间和IP,便于安全审计 | ||
| 198 | + | ||
| 199 | +--- | ||
| 200 | + | ||
| 201 | +**文档版本**: v1.0 | ||
| 202 | +**创建时间**: 2025-01-27 | ||
| 203 | +**维护人员**: 开发团队 |
| 1 | +package com.apple.erp.aspect; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.service.SysLogService; | ||
| 4 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
| 5 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.aspectj.lang.JoinPoint; | ||
| 8 | +import org.aspectj.lang.ProceedingJoinPoint; | ||
| 9 | +import org.aspectj.lang.annotation.*; | ||
| 10 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | +import org.springframework.security.core.Authentication; | ||
| 12 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
| 13 | +import org.springframework.stereotype.Component; | ||
| 14 | +import org.springframework.web.context.request.RequestContextHolder; | ||
| 15 | +import org.springframework.web.context.request.ServletRequestAttributes; | ||
| 16 | + | ||
| 17 | +import javax.servlet.http.HttpServletRequest; | ||
| 18 | +import java.time.LocalDateTime; | ||
| 19 | +import java.util.Arrays; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 操作日志切面 | ||
| 23 | + * | ||
| 24 | + * @author Apple ERP Team | ||
| 25 | + * @version 1.0.0 | ||
| 26 | + * @since 2024-01-01 | ||
| 27 | + */ | ||
| 28 | +@Aspect | ||
| 29 | +@Component | ||
| 30 | +@Slf4j | ||
| 31 | +public class LogAspect { | ||
| 32 | + | ||
| 33 | + @Autowired | ||
| 34 | + private SysLogService sysLogService; | ||
| 35 | + | ||
| 36 | + @Autowired | ||
| 37 | + private ObjectMapper objectMapper; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 定义切点:拦截所有Controller层的public方法 | ||
| 41 | + */ | ||
| 42 | + @Pointcut("execution(public * com.apple.erp.controller..*(..))") | ||
| 43 | + public void logPointcut() {} | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 环绕通知:记录操作日志 | ||
| 47 | + */ | ||
| 48 | + @Around("logPointcut()") | ||
| 49 | + public Object around(ProceedingJoinPoint point) throws Throwable { | ||
| 50 | + long startTime = System.currentTimeMillis(); | ||
| 51 | + Object result = null; | ||
| 52 | + Exception exception = null; | ||
| 53 | + | ||
| 54 | + try { | ||
| 55 | + result = point.proceed(); | ||
| 56 | + } catch (Exception e) { | ||
| 57 | + exception = e; | ||
| 58 | + throw e; | ||
| 59 | + } finally { | ||
| 60 | + long endTime = System.currentTimeMillis(); | ||
| 61 | + long executeTime = endTime - startTime; | ||
| 62 | + | ||
| 63 | + // 记录操作日志 | ||
| 64 | + recordOperationLog(point, result, exception, executeTime); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + return result; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * 记录操作日志 | ||
| 72 | + */ | ||
| 73 | + private void recordOperationLog(ProceedingJoinPoint point, Object result, Exception exception, long executeTime) { | ||
| 74 | + try { | ||
| 75 | + // 获取请求信息 | ||
| 76 | + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | ||
| 77 | + if (attributes == null) { | ||
| 78 | + return; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + HttpServletRequest request = attributes.getRequest(); | ||
| 82 | + | ||
| 83 | + // 获取用户信息 | ||
| 84 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
| 85 | + String username = "anonymous"; | ||
| 86 | + Long userId = null; | ||
| 87 | + | ||
| 88 | + if (authentication != null && authentication.isAuthenticated() && | ||
| 89 | + !"anonymousUser".equals(authentication.getName())) { | ||
| 90 | + username = authentication.getName(); | ||
| 91 | + // 这里可以根据需要获取用户ID,暂时设为null | ||
| 92 | + userId = null; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + // 获取方法信息 | ||
| 96 | + String className = point.getTarget().getClass().getSimpleName(); | ||
| 97 | + String methodName = point.getSignature().getName(); | ||
| 98 | + String operation = className + "." + methodName; | ||
| 99 | + | ||
| 100 | + // 获取模块信息(从类名推断) | ||
| 101 | + String module = getModuleFromClassName(className); | ||
| 102 | + | ||
| 103 | + // 获取请求参数 | ||
| 104 | + String requestParam = getRequestParam(point); | ||
| 105 | + | ||
| 106 | + // 获取IP地址 | ||
| 107 | + String ip = getClientIpAddress(request); | ||
| 108 | + | ||
| 109 | + // 判断操作结果 | ||
| 110 | + Integer status = exception == null ? 1 : 0; | ||
| 111 | + String errorMsg = exception != null ? exception.getMessage() : null; | ||
| 112 | + | ||
| 113 | + // 记录日志 | ||
| 114 | + sysLogService.recordLog( | ||
| 115 | + "0", // 操作日志 | ||
| 116 | + userId, | ||
| 117 | + username, | ||
| 118 | + module, | ||
| 119 | + operation, | ||
| 120 | + ip, | ||
| 121 | + status, | ||
| 122 | + errorMsg, | ||
| 123 | + requestParam | ||
| 124 | + ); | ||
| 125 | + | ||
| 126 | + } catch (Exception e) { | ||
| 127 | + log.error("记录操作日志失败", e); | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + /** | ||
| 132 | + * 从类名获取模块名 | ||
| 133 | + */ | ||
| 134 | + private String getModuleFromClassName(String className) { | ||
| 135 | + if (className.contains("User")) { | ||
| 136 | + return "用户管理"; | ||
| 137 | + } else if (className.contains("Role")) { | ||
| 138 | + return "角色管理"; | ||
| 139 | + } else if (className.contains("Menu")) { | ||
| 140 | + return "菜单管理"; | ||
| 141 | + } else if (className.contains("Dict")) { | ||
| 142 | + return "字典管理"; | ||
| 143 | + } else if (className.contains("Log")) { | ||
| 144 | + return "日志管理"; | ||
| 145 | + } else if (className.contains("Auth")) { | ||
| 146 | + return "认证管理"; | ||
| 147 | + } else { | ||
| 148 | + return "系统管理"; | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + /** | ||
| 153 | + * 获取请求参数 | ||
| 154 | + */ | ||
| 155 | + private String getRequestParam(ProceedingJoinPoint point) { | ||
| 156 | + try { | ||
| 157 | + Object[] args = point.getArgs(); | ||
| 158 | + if (args == null || args.length == 0) { | ||
| 159 | + return null; | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + // 过滤掉HttpServletRequest、HttpServletResponse等参数 | ||
| 163 | + Object[] filteredArgs = Arrays.stream(args) | ||
| 164 | + .filter(arg -> !isExcludedParam(arg)) | ||
| 165 | + .toArray(); | ||
| 166 | + | ||
| 167 | + if (filteredArgs.length == 0) { | ||
| 168 | + return null; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + // 如果只有一个参数,直接序列化 | ||
| 172 | + if (filteredArgs.length == 1) { | ||
| 173 | + return objectMapper.writeValueAsString(filteredArgs[0]); | ||
| 174 | + } else { | ||
| 175 | + // 多个参数,序列化为数组 | ||
| 176 | + return objectMapper.writeValueAsString(filteredArgs); | ||
| 177 | + } | ||
| 178 | + } catch (JsonProcessingException e) { | ||
| 179 | + log.warn("序列化请求参数失败", e); | ||
| 180 | + return "参数序列化失败"; | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + /** | ||
| 185 | + * 判断是否为需要排除的参数 | ||
| 186 | + */ | ||
| 187 | + private boolean isExcludedParam(Object arg) { | ||
| 188 | + if (arg == null) { | ||
| 189 | + return true; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + String className = arg.getClass().getName(); | ||
| 193 | + return className.startsWith("javax.servlet.") || | ||
| 194 | + className.startsWith("org.springframework.web.") || | ||
| 195 | + className.startsWith("org.springframework.security.") || | ||
| 196 | + className.contains("MultipartFile") || | ||
| 197 | + className.contains("Part"); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + /** | ||
| 201 | + * 获取客户端IP地址 | ||
| 202 | + */ | ||
| 203 | + private String getClientIpAddress(HttpServletRequest request) { | ||
| 204 | + String ip = request.getHeader("X-Forwarded-For"); | ||
| 205 | + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { | ||
| 206 | + ip = request.getHeader("Proxy-Client-IP"); | ||
| 207 | + } | ||
| 208 | + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { | ||
| 209 | + ip = request.getHeader("WL-Proxy-Client-IP"); | ||
| 210 | + } | ||
| 211 | + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { | ||
| 212 | + ip = request.getHeader("HTTP_CLIENT_IP"); | ||
| 213 | + } | ||
| 214 | + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { | ||
| 215 | + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); | ||
| 216 | + } | ||
| 217 | + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { | ||
| 218 | + ip = request.getRemoteAddr(); | ||
| 219 | + } | ||
| 220 | + return ip; | ||
| 221 | + } | ||
| 222 | +} |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.utils.JwtUtils; | ||
| 4 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 5 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| 6 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
| 7 | +import org.springframework.security.core.userdetails.UserDetails; | ||
| 8 | +import org.springframework.security.core.userdetails.UserDetailsService; | ||
| 9 | +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | ||
| 10 | +import org.springframework.stereotype.Component; | ||
| 11 | +import org.springframework.web.filter.OncePerRequestFilter; | ||
| 12 | + | ||
| 13 | +import javax.servlet.FilterChain; | ||
| 14 | +import javax.servlet.ServletException; | ||
| 15 | +import javax.servlet.http.HttpServletRequest; | ||
| 16 | +import javax.servlet.http.HttpServletResponse; | ||
| 17 | +import java.io.IOException; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * JWT认证过滤器 | ||
| 21 | + * | ||
| 22 | + * @author Apple ERP Team | ||
| 23 | + * @version 1.0.0 | ||
| 24 | + * @since 2024-01-01 | ||
| 25 | + */ | ||
| 26 | +@Component | ||
| 27 | +public class JwtAuthenticationFilter extends OncePerRequestFilter { | ||
| 28 | + | ||
| 29 | + @Autowired | ||
| 30 | + private JwtUtils jwtUtils; | ||
| 31 | + | ||
| 32 | + @Autowired | ||
| 33 | + private UserDetailsService userDetailsService; | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, | ||
| 37 | + FilterChain filterChain) throws ServletException, IOException { | ||
| 38 | + | ||
| 39 | + String authHeader = request.getHeader(jwtUtils.getHeader()); | ||
| 40 | + String token = jwtUtils.getTokenFromHeader(authHeader); | ||
| 41 | + | ||
| 42 | + if (token != null && SecurityContextHolder.getContext().getAuthentication() == null) { | ||
| 43 | + try { | ||
| 44 | + String username = jwtUtils.getUsernameFromToken(token); | ||
| 45 | + | ||
| 46 | + if (username != null && jwtUtils.validateToken(token, username)) { | ||
| 47 | + UserDetails userDetails = userDetailsService.loadUserByUsername(username); | ||
| 48 | + | ||
| 49 | + UsernamePasswordAuthenticationToken authentication = | ||
| 50 | + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); | ||
| 51 | + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | ||
| 52 | + | ||
| 53 | + SecurityContextHolder.getContext().setAuthentication(authentication); | ||
| 54 | + } | ||
| 55 | + } catch (Exception e) { | ||
| 56 | + logger.error("JWT认证失败: " + e.getMessage()); | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + filterChain.doFilter(request, response); | ||
| 61 | + } | ||
| 62 | +} |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.DbType; | ||
| 4 | +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; | ||
| 5 | +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; | ||
| 6 | +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; | ||
| 7 | +import org.mybatis.spring.annotation.MapperScan; | ||
| 8 | +import org.springframework.context.annotation.Bean; | ||
| 9 | +import org.springframework.context.annotation.Configuration; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * MyBatis Plus配置类 | ||
| 13 | + * | ||
| 14 | + * @author Apple ERP Team | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @since 2024-01-01 | ||
| 17 | + */ | ||
| 18 | +@Configuration | ||
| 19 | +@MapperScan("com.apple.erp.mapper") | ||
| 20 | +public class MybatisPlusConfig { | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * MyBatis Plus拦截器配置 | ||
| 24 | + */ | ||
| 25 | + @Bean | ||
| 26 | + public MybatisPlusInterceptor mybatisPlusInterceptor() { | ||
| 27 | + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); | ||
| 28 | + | ||
| 29 | + // 分页插件 | ||
| 30 | + PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); | ||
| 31 | + // 设置最大单页限制数量,默认 500 条,-1 不受限制 | ||
| 32 | + paginationInterceptor.setMaxLimit(1000L); | ||
| 33 | + // 设置溢出总页数后是否进行处理 | ||
| 34 | + paginationInterceptor.setOverflow(false); | ||
| 35 | + interceptor.addInnerInterceptor(paginationInterceptor); | ||
| 36 | + | ||
| 37 | + // 乐观锁插件 | ||
| 38 | + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); | ||
| 39 | + | ||
| 40 | + return interceptor; | ||
| 41 | + } | ||
| 42 | +} |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonAutoDetect; | ||
| 4 | +import com.fasterxml.jackson.annotation.PropertyAccessor; | ||
| 5 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 6 | +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; | ||
| 7 | +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; | ||
| 8 | +import org.springframework.cache.CacheManager; | ||
| 9 | +import org.springframework.cache.annotation.EnableCaching; | ||
| 10 | +import org.springframework.context.annotation.Bean; | ||
| 11 | +import org.springframework.context.annotation.Configuration; | ||
| 12 | +import org.springframework.data.redis.cache.RedisCacheConfiguration; | ||
| 13 | +import org.springframework.data.redis.cache.RedisCacheManager; | ||
| 14 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| 15 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 16 | +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | ||
| 17 | +import org.springframework.data.redis.serializer.RedisSerializationContext; | ||
| 18 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
| 19 | + | ||
| 20 | +import java.time.Duration; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * Redis配置类 | ||
| 24 | + * | ||
| 25 | + * @author Apple ERP Team | ||
| 26 | + * @version 1.0.0 | ||
| 27 | + * @since 2024-01-01 | ||
| 28 | + */ | ||
| 29 | +@Configuration | ||
| 30 | +@EnableCaching | ||
| 31 | +public class RedisConfig { | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * RedisTemplate配置 | ||
| 35 | + */ | ||
| 36 | + @Bean | ||
| 37 | + public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { | ||
| 38 | + RedisTemplate<String, Object> template = new RedisTemplate<>(); | ||
| 39 | + template.setConnectionFactory(connectionFactory); | ||
| 40 | + | ||
| 41 | + // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 | ||
| 42 | + Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); | ||
| 43 | + | ||
| 44 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 45 | + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | ||
| 46 | + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); | ||
| 47 | + // 注册Java 8时间模块 | ||
| 48 | + mapper.registerModule(new JavaTimeModule()); | ||
| 49 | + // 禁用时间戳序列化 | ||
| 50 | + mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); | ||
| 51 | + serializer.setObjectMapper(mapper); | ||
| 52 | + | ||
| 53 | + // 使用StringRedisSerializer来序列化和反序列化redis的key值 | ||
| 54 | + template.setKeySerializer(new StringRedisSerializer()); | ||
| 55 | + template.setValueSerializer(serializer); | ||
| 56 | + | ||
| 57 | + // Hash的key也采用StringRedisSerializer的序列化方式 | ||
| 58 | + template.setHashKeySerializer(new StringRedisSerializer()); | ||
| 59 | + template.setHashValueSerializer(serializer); | ||
| 60 | + | ||
| 61 | + template.afterPropertiesSet(); | ||
| 62 | + return template; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 缓存管理器配置 | ||
| 67 | + */ | ||
| 68 | + @Bean | ||
| 69 | + public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { | ||
| 70 | + // 配置序列化 | ||
| 71 | + Jackson2JsonRedisSerializer<Object> cacheSerializer = new Jackson2JsonRedisSerializer<>(Object.class); | ||
| 72 | + ObjectMapper cacheMapper = new ObjectMapper(); | ||
| 73 | + cacheMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | ||
| 74 | + cacheMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); | ||
| 75 | + // 注册Java 8时间模块 | ||
| 76 | + cacheMapper.registerModule(new JavaTimeModule()); | ||
| 77 | + // 禁用时间戳序列化 | ||
| 78 | + cacheMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); | ||
| 79 | + cacheSerializer.setObjectMapper(cacheMapper); | ||
| 80 | + | ||
| 81 | + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() | ||
| 82 | + .entryTtl(Duration.ofHours(1)) // 设置缓存有效期1小时 | ||
| 83 | + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) | ||
| 84 | + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(cacheSerializer)) | ||
| 85 | + .disableCachingNullValues(); // 不缓存空值 | ||
| 86 | + | ||
| 87 | + return RedisCacheManager.builder(connectionFactory) | ||
| 88 | + .cacheDefaults(config) | ||
| 89 | + .build(); | ||
| 90 | + } | ||
| 91 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.utils.JwtUtils; | ||
| 4 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 5 | +import org.springframework.context.annotation.Bean; | ||
| 6 | +import org.springframework.context.annotation.Configuration; | ||
| 7 | +import org.springframework.security.authentication.AuthenticationManager; | ||
| 8 | +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; | ||
| 9 | +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | ||
| 10 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| 11 | +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
| 12 | +import org.springframework.security.config.http.SessionCreationPolicy; | ||
| 13 | +import org.springframework.security.core.userdetails.UserDetailsService; | ||
| 14 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
| 15 | +import org.springframework.security.crypto.password.PasswordEncoder; | ||
| 16 | +import org.springframework.security.web.SecurityFilterChain; | ||
| 17 | +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
| 18 | +import org.springframework.web.cors.CorsConfiguration; | ||
| 19 | +import org.springframework.web.cors.CorsConfigurationSource; | ||
| 20 | +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
| 21 | + | ||
| 22 | +import java.util.Arrays; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * Spring Security配置类 | ||
| 26 | + * | ||
| 27 | + * @author Apple ERP Team | ||
| 28 | + * @version 1.0.0 | ||
| 29 | + * @since 2024-01-01 | ||
| 30 | + */ | ||
| 31 | +@Configuration | ||
| 32 | +@EnableWebSecurity | ||
| 33 | +@EnableGlobalMethodSecurity(prePostEnabled = true) | ||
| 34 | +public class SecurityConfig { | ||
| 35 | + | ||
| 36 | + @Autowired | ||
| 37 | + private JwtUtils jwtUtils; | ||
| 38 | + | ||
| 39 | + @Autowired | ||
| 40 | + private JwtAuthenticationFilter jwtAuthenticationFilter; | ||
| 41 | + | ||
| 42 | + @Autowired | ||
| 43 | + private UserDetailsService userDetailsService; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 密码编码器 | ||
| 47 | + */ | ||
| 48 | + @Bean | ||
| 49 | + public PasswordEncoder passwordEncoder() { | ||
| 50 | + return new BCryptPasswordEncoder(); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 认证管理器 | ||
| 55 | + */ | ||
| 56 | + @Bean | ||
| 57 | + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { | ||
| 58 | + return config.getAuthenticationManager(); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 安全过滤器链 | ||
| 63 | + */ | ||
| 64 | + @Bean | ||
| 65 | + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { | ||
| 66 | + http.cors().and().csrf().disable() | ||
| 67 | + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) | ||
| 68 | + .and() | ||
| 69 | + .userDetailsService(userDetailsService) | ||
| 70 | + .authorizeRequests(authz -> authz | ||
| 71 | + // 允许访问的路径 | ||
| 72 | + .antMatchers("/api/auth/**").permitAll() | ||
| 73 | + .antMatchers("/api/public/**").permitAll() | ||
| 74 | + .antMatchers("/api/test/public").permitAll() // 允许测试接口 | ||
| 75 | + .antMatchers("/actuator/**").permitAll() | ||
| 76 | + .antMatchers("/druid/**").permitAll() | ||
| 77 | + .antMatchers("/swagger-ui/**").permitAll() | ||
| 78 | + .antMatchers("/swagger-ui.html").permitAll() | ||
| 79 | + .antMatchers("/v3/api-docs/**").permitAll() | ||
| 80 | + .antMatchers("/api-docs/**").permitAll() | ||
| 81 | + .antMatchers("/doc.html").permitAll() | ||
| 82 | + .antMatchers("/favicon.ico").permitAll() | ||
| 83 | + // 其他请求需要认证 | ||
| 84 | + .anyRequest().authenticated() | ||
| 85 | + ) | ||
| 86 | + .exceptionHandling() | ||
| 87 | + .authenticationEntryPoint((request, response, authException) -> { | ||
| 88 | + response.setContentType("application/json;charset=UTF-8"); | ||
| 89 | + response.setStatus(401); | ||
| 90 | + response.getWriter().write("{\"code\":401,\"message\":\"未授权访问\",\"data\":null}"); | ||
| 91 | + }) | ||
| 92 | + .accessDeniedHandler((request, response, accessDeniedException) -> { | ||
| 93 | + response.setContentType("application/json;charset=UTF-8"); | ||
| 94 | + response.setStatus(403); | ||
| 95 | + response.getWriter().write("{\"code\":403,\"message\":\"权限不足\",\"data\":null}"); | ||
| 96 | + }); | ||
| 97 | + | ||
| 98 | + // 添加JWT过滤器 | ||
| 99 | + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | ||
| 100 | + | ||
| 101 | + return http.build(); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + /** | ||
| 105 | + * CORS配置 | ||
| 106 | + */ | ||
| 107 | + @Bean | ||
| 108 | + public CorsConfigurationSource corsConfigurationSource() { | ||
| 109 | + CorsConfiguration configuration = new CorsConfiguration(); | ||
| 110 | + configuration.setAllowedOriginPatterns(Arrays.asList("*")); | ||
| 111 | + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); | ||
| 112 | + configuration.setAllowedHeaders(Arrays.asList("*")); | ||
| 113 | + configuration.setAllowCredentials(true); | ||
| 114 | + configuration.setMaxAge(3600L); | ||
| 115 | + | ||
| 116 | + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
| 117 | + source.registerCorsConfiguration("/**", configuration); | ||
| 118 | + return source; | ||
| 119 | + } | ||
| 120 | +} |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.models.OpenAPI; | ||
| 4 | +import io.swagger.v3.oas.models.info.Contact; | ||
| 5 | +import io.swagger.v3.oas.models.info.Info; | ||
| 6 | +import io.swagger.v3.oas.models.info.License; | ||
| 7 | +import io.swagger.v3.oas.models.security.SecurityRequirement; | ||
| 8 | +import io.swagger.v3.oas.models.security.SecurityScheme; | ||
| 9 | +import io.swagger.v3.oas.models.Components; | ||
| 10 | +import org.springframework.context.annotation.Bean; | ||
| 11 | +import org.springframework.context.annotation.Configuration; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * Swagger配置类 | ||
| 15 | + * | ||
| 16 | + * @author Apple ERP Team | ||
| 17 | + * @version 1.0.0 | ||
| 18 | + * @since 2024-01-01 | ||
| 19 | + */ | ||
| 20 | +@Configuration | ||
| 21 | +public class SwaggerConfig { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * OpenAPI配置 | ||
| 25 | + */ | ||
| 26 | + @Bean | ||
| 27 | + public OpenAPI customOpenAPI() { | ||
| 28 | + return new OpenAPI() | ||
| 29 | + .info(new Info() | ||
| 30 | + .title("Apple经销商ERP系统API文档") | ||
| 31 | + .description("Apple经销商ERP系统后端API接口文档") | ||
| 32 | + .version("1.0.0") | ||
| 33 | + .contact(new Contact() | ||
| 34 | + .name("Apple ERP Team") | ||
| 35 | + .email("developer@apple-erp.com") | ||
| 36 | + .url("https://www.apple-erp.com")) | ||
| 37 | + .license(new License() | ||
| 38 | + .name("MIT License") | ||
| 39 | + .url("https://opensource.org/licenses/MIT"))) | ||
| 40 | + .addSecurityItem(new SecurityRequirement().addList("Bearer Authentication")) | ||
| 41 | + .components(new Components() | ||
| 42 | + .addSecuritySchemes("Bearer Authentication", | ||
| 43 | + new SecurityScheme() | ||
| 44 | + .type(SecurityScheme.Type.HTTP) | ||
| 45 | + .scheme("bearer") | ||
| 46 | + .bearerFormat("JWT") | ||
| 47 | + .description("请输入JWT令牌,格式:Bearer {token}"))); | ||
| 48 | + } | ||
| 49 | +} |
| 1 | +package com.apple.erp.config; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.entity.SysUser; | ||
| 4 | +import com.apple.erp.service.SysUserService; | ||
| 5 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 6 | +import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
| 7 | +import org.springframework.security.core.userdetails.User; | ||
| 8 | +import org.springframework.security.core.userdetails.UserDetails; | ||
| 9 | +import org.springframework.security.core.userdetails.UserDetailsService; | ||
| 10 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
| 11 | +import org.springframework.stereotype.Service; | ||
| 12 | + | ||
| 13 | +import java.util.ArrayList; | ||
| 14 | +import java.util.List; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * 用户详情服务实现类 | ||
| 18 | + * | ||
| 19 | + * @author Apple ERP Team | ||
| 20 | + * @version 1.0.0 | ||
| 21 | + * @since 2024-01-01 | ||
| 22 | + */ | ||
| 23 | +@Service | ||
| 24 | +public class UserDetailsServiceImpl implements UserDetailsService { | ||
| 25 | + | ||
| 26 | + @Autowired | ||
| 27 | + private SysUserService sysUserService; | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
| 31 | + // 从数据库查询用户信息 | ||
| 32 | + SysUser sysUser = sysUserService.findByUsername(username); | ||
| 33 | + | ||
| 34 | + if (sysUser == null) { | ||
| 35 | + throw new UsernameNotFoundException("用户不存在: " + username); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + // 检查用户状态 | ||
| 39 | + if (sysUser.getStatus() == 0) { | ||
| 40 | + throw new UsernameNotFoundException("用户已被停用: " + username); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // 获取用户权限 | ||
| 44 | + List<String> permissions = sysUserService.getUserPermissions(username); | ||
| 45 | + List<SimpleGrantedAuthority> authorities = new ArrayList<>(); | ||
| 46 | + | ||
| 47 | + // 添加角色权限 | ||
| 48 | + if (sysUser.getRoles() != null) { | ||
| 49 | + sysUser.getRoles().forEach(role -> { | ||
| 50 | + authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode())); | ||
| 51 | + }); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + // 添加功能权限 | ||
| 55 | + if (permissions != null) { | ||
| 56 | + permissions.forEach(permission -> { | ||
| 57 | + if (permission != null && !permission.trim().isEmpty()) { | ||
| 58 | + authorities.add(new SimpleGrantedAuthority(permission)); | ||
| 59 | + } | ||
| 60 | + }); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + // 构建UserDetails对象 | ||
| 64 | + return User.builder() | ||
| 65 | + .username(sysUser.getUsername()) | ||
| 66 | + .password(sysUser.getPassword()) // 密码已经是BCrypt加密的 | ||
| 67 | + .authorities(authorities) | ||
| 68 | + .accountExpired(false) | ||
| 69 | + .accountLocked(false) | ||
| 70 | + .credentialsExpired(false) | ||
| 71 | + .disabled(sysUser.getStatus() == 0) | ||
| 72 | + .build(); | ||
| 73 | + } | ||
| 74 | +} |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.LoginReq; | ||
| 4 | +import com.apple.erp.dto.request.RefreshTokenReq; | ||
| 5 | +import com.apple.erp.dto.response.ApiRes; | ||
| 6 | +import com.apple.erp.dto.response.LoginRes; | ||
| 7 | +import com.apple.erp.dto.response.UserInfoRes; | ||
| 8 | +import com.apple.erp.entity.SysUser; | ||
| 9 | +import com.apple.erp.service.SysUserService; | ||
| 10 | +import com.apple.erp.service.SysLogService; | ||
| 11 | +import com.apple.erp.utils.JwtUtils; | ||
| 12 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import lombok.extern.slf4j.Slf4j; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.http.ResponseEntity; | ||
| 18 | +import org.springframework.security.authentication.AuthenticationManager; | ||
| 19 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| 20 | +import org.springframework.security.core.Authentication; | ||
| 21 | +import org.springframework.security.core.context.SecurityContextHolder; | ||
| 22 | +import org.springframework.web.bind.annotation.*; | ||
| 23 | + | ||
| 24 | +import javax.servlet.http.HttpServletRequest; | ||
| 25 | +import javax.validation.Valid; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 认证控制器 | ||
| 29 | + * | ||
| 30 | + * @author Apple ERP Team | ||
| 31 | + * @version 1.0.0 | ||
| 32 | + * @since 2024-01-01 | ||
| 33 | + */ | ||
| 34 | +@Slf4j | ||
| 35 | +@RestController | ||
| 36 | +@RequestMapping("/api/auth") | ||
| 37 | +@CrossOrigin(origins = "*") | ||
| 38 | +@Tag(name = "认证管理", description = "用户认证相关接口") | ||
| 39 | +public class AuthController { | ||
| 40 | + | ||
| 41 | + @Autowired | ||
| 42 | + private AuthenticationManager authenticationManager; | ||
| 43 | + | ||
| 44 | + @Autowired | ||
| 45 | + private JwtUtils jwtUtils; | ||
| 46 | + | ||
| 47 | + @Autowired | ||
| 48 | + private SysUserService sysUserService; | ||
| 49 | + | ||
| 50 | + @Autowired | ||
| 51 | + private SysLogService sysLogService; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 用户登录 | ||
| 55 | + */ | ||
| 56 | + @Operation(summary = "用户登录", description = "用户登录获取JWT令牌") | ||
| 57 | + @PostMapping("/login") | ||
| 58 | + public ResponseEntity<ApiRes<LoginRes>> login( | ||
| 59 | + @Parameter(description = "登录信息", required = true) | ||
| 60 | + @Valid @RequestBody LoginReq loginRequest, | ||
| 61 | + HttpServletRequest request) { | ||
| 62 | + try { | ||
| 63 | + String username = loginRequest.getUsername(); | ||
| 64 | + String password = loginRequest.getPassword(); | ||
| 65 | + | ||
| 66 | + // 添加调试日志 | ||
| 67 | + System.out.println("尝试登录 - 用户名: " + username + ", 密码: " + password); | ||
| 68 | + | ||
| 69 | + // 认证 | ||
| 70 | + Authentication authentication = authenticationManager.authenticate( | ||
| 71 | + new UsernamePasswordAuthenticationToken(username, password) | ||
| 72 | + ); | ||
| 73 | + | ||
| 74 | + System.out.println("认证成功: " + authentication.getName()); | ||
| 75 | + | ||
| 76 | + SecurityContextHolder.getContext().setAuthentication(authentication); | ||
| 77 | + | ||
| 78 | + // 更新用户最后登录信息并记录登录日志 | ||
| 79 | + try { | ||
| 80 | + SysUser user = sysUserService.findByUsername(username); | ||
| 81 | + if (user != null) { | ||
| 82 | + String clientIp = getClientIp(request); | ||
| 83 | + sysUserService.updateLastLoginInfo(user.getUserId(), clientIp); | ||
| 84 | + | ||
| 85 | + // 记录登录成功日志 | ||
| 86 | + sysLogService.recordLoginLog(user.getUserId(), username, clientIp, 1, null); | ||
| 87 | + } | ||
| 88 | + } catch (Exception e) { | ||
| 89 | + // 记录日志但不影响登录流程 | ||
| 90 | + System.err.println("更新用户登录信息失败: " + e.getMessage()); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + // 生成JWT令牌 | ||
| 94 | + String token = jwtUtils.generateToken(username); | ||
| 95 | + String refreshToken = jwtUtils.generateRefreshToken(username); | ||
| 96 | + | ||
| 97 | + LoginRes loginResponse = new LoginRes(token, refreshToken, username); | ||
| 98 | + ApiRes<LoginRes> response = ApiRes.success("登录成功", loginResponse); | ||
| 99 | + return ResponseEntity.ok(response); | ||
| 100 | + } catch (Exception ex) { | ||
| 101 | + System.err.println(ex.getStackTrace()); | ||
| 102 | + log.error(ex.getMessage()); | ||
| 103 | + | ||
| 104 | + // 记录登录失败日志 | ||
| 105 | + try { | ||
| 106 | + String clientIp = getClientIp(request); | ||
| 107 | + sysLogService.recordLoginLog(null, loginRequest.getUsername(), clientIp, 0, ex.getMessage()); | ||
| 108 | + } catch (Exception e) { | ||
| 109 | + log.error("记录登录失败日志失败", e); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + ApiRes<LoginRes> response = ApiRes.error("用户名或密码错误"); | ||
| 113 | + return ResponseEntity.status(401).body(response); | ||
| 114 | + } | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + /** | ||
| 118 | + * 刷新令牌 | ||
| 119 | + */ | ||
| 120 | + @Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌") | ||
| 121 | + @PostMapping("/refresh") | ||
| 122 | + public ResponseEntity<ApiRes<LoginRes>> refreshToken( | ||
| 123 | + @Parameter(description = "刷新令牌信息", required = true) | ||
| 124 | + @Valid @RequestBody RefreshTokenReq refreshRequest) { | ||
| 125 | + try { | ||
| 126 | + String refreshToken = refreshRequest.getRefreshToken(); | ||
| 127 | + String username = jwtUtils.getUsernameFromToken(refreshToken); | ||
| 128 | + | ||
| 129 | + if (jwtUtils.validateToken(refreshToken, username)) { | ||
| 130 | + String newToken = jwtUtils.generateToken(username); | ||
| 131 | + String newRefreshToken = jwtUtils.generateRefreshToken(username); | ||
| 132 | + | ||
| 133 | + LoginRes loginResponse = new LoginRes(newToken, newRefreshToken, username); | ||
| 134 | + ApiRes<LoginRes> response = ApiRes.success("令牌刷新成功", loginResponse); | ||
| 135 | + return ResponseEntity.ok(response); | ||
| 136 | + } else { | ||
| 137 | + throw new Exception("无效的刷新令牌"); | ||
| 138 | + } | ||
| 139 | + } catch (Exception e) { | ||
| 140 | + ApiRes<LoginRes> response = ApiRes.error("令牌刷新失败"); | ||
| 141 | + return ResponseEntity.status(401).body(response); | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + /** | ||
| 146 | + * 用户登出 | ||
| 147 | + */ | ||
| 148 | + @Operation(summary = "用户登出", description = "用户登出清除认证信息和缓存") | ||
| 149 | + @PostMapping("/logout") | ||
| 150 | + public ResponseEntity<ApiRes<Void>> logout(HttpServletRequest request) { | ||
| 151 | + try { | ||
| 152 | + // 获取当前用户信息 | ||
| 153 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
| 154 | + String username = null; | ||
| 155 | + Long userId = null; | ||
| 156 | + | ||
| 157 | + if (authentication != null && authentication.isAuthenticated() && | ||
| 158 | + !"anonymousUser".equals(authentication.getName())) { | ||
| 159 | + username = authentication.getName(); | ||
| 160 | + | ||
| 161 | + // 获取用户ID | ||
| 162 | + try { | ||
| 163 | + SysUser user = sysUserService.findByUsername(username); | ||
| 164 | + if (user != null) { | ||
| 165 | + userId = user.getUserId(); | ||
| 166 | + } | ||
| 167 | + } catch (Exception e) { | ||
| 168 | + log.warn("获取用户信息失败: " + e.getMessage()); | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + // 清除Redis中的用户权限缓存 | ||
| 173 | + if (username != null) { | ||
| 174 | + try { | ||
| 175 | + sysUserService.clearUserCache(username); | ||
| 176 | + log.info("用户 {} 的权限缓存已清除", username); | ||
| 177 | + } catch (Exception e) { | ||
| 178 | + log.error("清除用户权限缓存失败: " + e.getMessage()); | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + // 记录登出日志 | ||
| 183 | + if (username != null) { | ||
| 184 | + try { | ||
| 185 | + String clientIp = getClientIp(request); | ||
| 186 | + sysLogService.recordLoginLog(userId, username, clientIp, 1, null); | ||
| 187 | + log.info("用户 {} 登出日志已记录", username); | ||
| 188 | + } catch (Exception e) { | ||
| 189 | + log.error("记录登出日志失败: " + e.getMessage()); | ||
| 190 | + } | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + // 清除Spring Security上下文 | ||
| 194 | + SecurityContextHolder.clearContext(); | ||
| 195 | + | ||
| 196 | + ApiRes<Void> response = ApiRes.success("登出成功", null); | ||
| 197 | + return ResponseEntity.ok(response); | ||
| 198 | + | ||
| 199 | + } catch (Exception e) { | ||
| 200 | + log.error("用户登出失败", e); | ||
| 201 | + ApiRes<Void> response = ApiRes.error("登出失败"); | ||
| 202 | + return ResponseEntity.status(500).body(response); | ||
| 203 | + } | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + /** | ||
| 207 | + * 获取当前用户信息 | ||
| 208 | + */ | ||
| 209 | + @Operation(summary = "获取用户信息", description = "获取当前登录用户的详细信息") | ||
| 210 | + @GetMapping("/userinfo") | ||
| 211 | + public ResponseEntity<ApiRes<UserInfoRes>> getUserInfo() { | ||
| 212 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||
| 213 | + | ||
| 214 | + if (authentication != null && authentication.isAuthenticated()) { | ||
| 215 | + String username = authentication.getName(); | ||
| 216 | + | ||
| 217 | + UserInfoRes userInfo = new UserInfoRes(username, authentication.getAuthorities()); | ||
| 218 | + ApiRes<UserInfoRes> response = ApiRes.success("获取用户信息成功", userInfo); | ||
| 219 | + return ResponseEntity.ok(response); | ||
| 220 | + } else { | ||
| 221 | + ApiRes<UserInfoRes> response = ApiRes.error("未认证"); | ||
| 222 | + return ResponseEntity.status(401).body(response); | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + /** | ||
| 227 | + * 获取客户端IP地址 | ||
| 228 | + */ | ||
| 229 | + private String getClientIp(HttpServletRequest request) { | ||
| 230 | + String xForwardedFor = request.getHeader("X-Forwarded-For"); | ||
| 231 | + if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) { | ||
| 232 | + return xForwardedFor.split(",")[0]; | ||
| 233 | + } | ||
| 234 | + String xRealIp = request.getHeader("X-Real-IP"); | ||
| 235 | + if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) { | ||
| 236 | + return xRealIp; | ||
| 237 | + } | ||
| 238 | + return request.getRemoteAddr(); | ||
| 239 | + } | ||
| 240 | +} |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.DictItemAddReq; | ||
| 4 | +import com.apple.erp.dto.request.DictItemQueryReq; | ||
| 5 | +import com.apple.erp.dto.request.DictItemUpdateReq; | ||
| 6 | +import com.apple.erp.dto.response.ApiRes; | ||
| 7 | +import com.apple.erp.dto.response.DictItemRes; | ||
| 8 | +import com.apple.erp.entity.SysDictItem; | ||
| 9 | +import com.apple.erp.service.SysDictItemService; | ||
| 10 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 11 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 12 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import org.springframework.beans.BeanUtils; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 18 | +import org.springframework.validation.annotation.Validated; | ||
| 19 | +import org.springframework.web.bind.annotation.*; | ||
| 20 | + | ||
| 21 | +import javax.validation.Valid; | ||
| 22 | +import java.util.Arrays; | ||
| 23 | +import java.util.List; | ||
| 24 | +import java.util.stream.Collectors; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * 系统字典项管理Controller | ||
| 28 | + * | ||
| 29 | + * @author Apple ERP Team | ||
| 30 | + * @version 1.0.0 | ||
| 31 | + * @since 2024-01-01 | ||
| 32 | + */ | ||
| 33 | +@Tag(name = "系统字典项管理", description = "系统字典项管理相关接口") | ||
| 34 | +@RestController | ||
| 35 | +@RequestMapping("/api/system/dict/item") | ||
| 36 | +@Validated | ||
| 37 | +public class SysDictItemController { | ||
| 38 | + | ||
| 39 | + @Autowired | ||
| 40 | + private SysDictItemService sysDictItemService; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 获取字典项列表 | ||
| 44 | + * 分页查询字典项信息,支持按字典类型ID、字典标签、字典值等条件筛选 | ||
| 45 | + * | ||
| 46 | + * @param queryReq 查询条件 | ||
| 47 | + * @return 字典项分页列表 | ||
| 48 | + */ | ||
| 49 | + @Operation(summary = "获取字典项列表", description = "分页查询字典项信息,支持多条件筛选") | ||
| 50 | + @GetMapping("/list") | ||
| 51 | + @PreAuthorize("hasAuthority('sys:dict:list')") | ||
| 52 | + public ApiRes<Page<DictItemRes>> list(DictItemQueryReq queryReq) { | ||
| 53 | + // 构建查询条件 | ||
| 54 | + SysDictItem dictItem = new SysDictItem(); | ||
| 55 | + BeanUtils.copyProperties(queryReq, dictItem); | ||
| 56 | + LambdaQueryWrapper<SysDictItem> wrapper = sysDictItemService.getQueryWrapper(dictItem); | ||
| 57 | + | ||
| 58 | + // 分页查询 | ||
| 59 | + Page<SysDictItem> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize()); | ||
| 60 | + Page<SysDictItem> dictItemPage = sysDictItemService.page(page, wrapper); | ||
| 61 | + | ||
| 62 | + // 转换为响应DTO | ||
| 63 | + List<DictItemRes> dictItemResList = dictItemPage.getRecords().stream() | ||
| 64 | + .map(this::convertToDictItemRes).collect(Collectors.toList()); | ||
| 65 | + Page<DictItemRes> resultPage = new Page<>(dictItemPage.getCurrent(), dictItemPage.getSize(), dictItemPage.getTotal()); | ||
| 66 | + resultPage.setRecords(dictItemResList); | ||
| 67 | + | ||
| 68 | + return ApiRes.success(resultPage); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 根据字典类型获取字典项列表 | ||
| 73 | + * 获取指定字典类型下的所有字典项 | ||
| 74 | + * | ||
| 75 | + * @param dictType 字典类型编码 | ||
| 76 | + * @return 字典项列表 | ||
| 77 | + */ | ||
| 78 | + @Operation(summary = "根据字典类型获取字典项列表", description = "获取指定字典类型下的所有字典项") | ||
| 79 | + @GetMapping("/type/{dictType}") | ||
| 80 | + @PreAuthorize("hasAuthority('sys:dict:list')") | ||
| 81 | + public ApiRes<List<DictItemRes>> getDictItemsByType(@Parameter(description = "字典类型编码") @PathVariable String dictType) { | ||
| 82 | + List<SysDictItem> dictItems = sysDictItemService.getDictItemsByType(dictType); | ||
| 83 | + List<DictItemRes> dictItemResList = dictItems.stream() | ||
| 84 | + .map(this::convertToDictItemRes).collect(Collectors.toList()); | ||
| 85 | + return ApiRes.success(dictItemResList); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * 获取字典项详情 | ||
| 90 | + * 根据字典项ID获取字典项详细信息 | ||
| 91 | + * | ||
| 92 | + * @param dictItemId 字典项ID | ||
| 93 | + * @return 字典项详情 | ||
| 94 | + */ | ||
| 95 | + @Operation(summary = "获取字典项详情", description = "根据字典项ID获取字典项详细信息") | ||
| 96 | + @GetMapping("/{dictItemId}") | ||
| 97 | + @PreAuthorize("hasAuthority('sys:dict:query')") | ||
| 98 | + public ApiRes<DictItemRes> getInfo(@Parameter(description = "字典项ID") @PathVariable Long dictItemId) { | ||
| 99 | + SysDictItem dictItem = sysDictItemService.getById(dictItemId); | ||
| 100 | + if (dictItem == null) { | ||
| 101 | + return ApiRes.error("字典项不存在"); | ||
| 102 | + } | ||
| 103 | + return ApiRes.success(convertToDictItemRes(dictItem)); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * 新增字典项 | ||
| 108 | + * 创建新的字典项 | ||
| 109 | + * | ||
| 110 | + * @param addReq 字典项新增请求 | ||
| 111 | + * @return 操作结果 | ||
| 112 | + */ | ||
| 113 | + @Operation(summary = "新增字典项", description = "创建新的字典项") | ||
| 114 | + @PostMapping | ||
| 115 | + @PreAuthorize("hasAuthority('sys:dict:add')") | ||
| 116 | + public ApiRes<Void> add(@Valid @RequestBody DictItemAddReq addReq) { | ||
| 117 | + // 检查字典标签是否唯一 | ||
| 118 | + if (!sysDictItemService.checkDictLabelUnique(addReq.getDictLabel(), addReq.getDictTypeId(), null)) { | ||
| 119 | + return ApiRes.error("字典标签已存在"); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + // 检查字典值是否唯一 | ||
| 123 | + if (!sysDictItemService.checkDictValueUnique(addReq.getDictValue(), addReq.getDictTypeId(), null)) { | ||
| 124 | + return ApiRes.error("字典值已存在"); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + // 转换并保存 | ||
| 128 | + SysDictItem dictItem = new SysDictItem(); | ||
| 129 | + BeanUtils.copyProperties(addReq, dictItem); | ||
| 130 | + dictItem.setSort(addReq.getSort() != null ? addReq.getSort() : 0); | ||
| 131 | + | ||
| 132 | + boolean success = sysDictItemService.save(dictItem); | ||
| 133 | + return success ? ApiRes.success(null) : ApiRes.error("新增字典项失败"); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + /** | ||
| 137 | + * 修改字典项 | ||
| 138 | + * 更新字典项信息 | ||
| 139 | + * | ||
| 140 | + * @param updateReq 字典项更新请求 | ||
| 141 | + * @return 操作结果 | ||
| 142 | + */ | ||
| 143 | + @Operation(summary = "修改字典项", description = "更新字典项信息") | ||
| 144 | + @PutMapping | ||
| 145 | + @PreAuthorize("hasAuthority('sys:dict:edit')") | ||
| 146 | + public ApiRes<Void> edit(@Valid @RequestBody DictItemUpdateReq updateReq) { | ||
| 147 | + // 检查字典项是否存在 | ||
| 148 | + SysDictItem existDictItem = sysDictItemService.getById(updateReq.getDictItemId()); | ||
| 149 | + if (existDictItem == null) { | ||
| 150 | + return ApiRes.error("字典项不存在"); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + // 检查字典标签是否唯一 | ||
| 154 | + if (!sysDictItemService.checkDictLabelUnique(updateReq.getDictLabel(), updateReq.getDictTypeId(), updateReq.getDictItemId())) { | ||
| 155 | + return ApiRes.error("字典标签已存在"); | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + // 检查字典值是否唯一 | ||
| 159 | + if (!sysDictItemService.checkDictValueUnique(updateReq.getDictValue(), updateReq.getDictTypeId(), updateReq.getDictItemId())) { | ||
| 160 | + return ApiRes.error("字典值已存在"); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + // 转换并更新 | ||
| 164 | + SysDictItem dictItem = new SysDictItem(); | ||
| 165 | + BeanUtils.copyProperties(updateReq, dictItem); | ||
| 166 | + dictItem.setSort(updateReq.getSort() != null ? updateReq.getSort() : 0); | ||
| 167 | + | ||
| 168 | + boolean success = sysDictItemService.updateById(dictItem); | ||
| 169 | + return success ? ApiRes.success(null) : ApiRes.error("修改字典项失败"); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + /** | ||
| 173 | + * 删除字典项 | ||
| 174 | + * 批量删除字典项 | ||
| 175 | + * | ||
| 176 | + * @param dictItemIds 字典项ID列表(逗号分隔) | ||
| 177 | + * @return 操作结果 | ||
| 178 | + */ | ||
| 179 | + @Operation(summary = "删除字典项", description = "批量删除字典项") | ||
| 180 | + @DeleteMapping("/{dictItemIds}") | ||
| 181 | + @PreAuthorize("hasAuthority('sys:dict:remove')") | ||
| 182 | + public ApiRes<Void> remove(@Parameter(description = "字典项ID列表") @PathVariable String dictItemIds) { | ||
| 183 | + try { | ||
| 184 | + List<Long> ids = Arrays.stream(dictItemIds.split(",")) | ||
| 185 | + .map(Long::valueOf) | ||
| 186 | + .collect(Collectors.toList()); | ||
| 187 | + | ||
| 188 | + boolean success = sysDictItemService.removeByIds(ids); | ||
| 189 | + return success ? ApiRes.success(null) : ApiRes.error("删除字典项失败"); | ||
| 190 | + } catch (NumberFormatException e) { | ||
| 191 | + return ApiRes.error("字典项ID格式错误"); | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + /** | ||
| 196 | + * 转换SysDictItem为DictItemRes | ||
| 197 | + * | ||
| 198 | + * @param dictItem 字典项实体 | ||
| 199 | + * @return 字典项响应DTO | ||
| 200 | + */ | ||
| 201 | + private DictItemRes convertToDictItemRes(SysDictItem dictItem) { | ||
| 202 | + DictItemRes dictItemRes = new DictItemRes(); | ||
| 203 | + BeanUtils.copyProperties(dictItem, dictItemRes); | ||
| 204 | + | ||
| 205 | + // TODO: 设置字典类型名称(需要关联查询) | ||
| 206 | + // dictItemRes.setDictType(dictTypeName); | ||
| 207 | + | ||
| 208 | + return dictItemRes; | ||
| 209 | + } | ||
| 210 | +} |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.DictTypeAddReq; | ||
| 4 | +import com.apple.erp.dto.request.DictTypeQueryReq; | ||
| 5 | +import com.apple.erp.dto.request.DictTypeUpdateReq; | ||
| 6 | +import com.apple.erp.dto.response.ApiRes; | ||
| 7 | +import com.apple.erp.dto.response.DictTypeRes; | ||
| 8 | +import com.apple.erp.entity.SysDictType; | ||
| 9 | +import com.apple.erp.service.SysDictTypeService; | ||
| 10 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 11 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 12 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import org.springframework.beans.BeanUtils; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 18 | +import org.springframework.validation.annotation.Validated; | ||
| 19 | +import org.springframework.web.bind.annotation.*; | ||
| 20 | + | ||
| 21 | +import javax.validation.Valid; | ||
| 22 | +import java.util.Arrays; | ||
| 23 | +import java.util.List; | ||
| 24 | +import java.util.stream.Collectors; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * 系统字典类型管理Controller | ||
| 28 | + * | ||
| 29 | + * @author Apple ERP Team | ||
| 30 | + * @version 1.0.0 | ||
| 31 | + * @since 2024-01-01 | ||
| 32 | + */ | ||
| 33 | +@Tag(name = "系统字典类型管理", description = "系统字典类型管理相关接口") | ||
| 34 | +@RestController | ||
| 35 | +@RequestMapping("/api/system/dict/type") | ||
| 36 | +@Validated | ||
| 37 | +public class SysDictTypeController { | ||
| 38 | + | ||
| 39 | + @Autowired | ||
| 40 | + private SysDictTypeService sysDictTypeService; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 获取字典类型列表 | ||
| 44 | + * 分页查询字典类型信息,支持按字典类型、字典名称、状态等条件筛选 | ||
| 45 | + * | ||
| 46 | + * @param queryReq 查询条件 | ||
| 47 | + * @return 字典类型分页列表 | ||
| 48 | + */ | ||
| 49 | + @Operation(summary = "获取字典类型列表", description = "分页查询字典类型信息,支持多条件筛选") | ||
| 50 | + @GetMapping("/list") | ||
| 51 | + @PreAuthorize("hasAuthority('sys:dict:list')") | ||
| 52 | + public ApiRes<Page<DictTypeRes>> list(DictTypeQueryReq queryReq) { | ||
| 53 | + // 构建查询条件 | ||
| 54 | + SysDictType dictType = new SysDictType(); | ||
| 55 | + BeanUtils.copyProperties(queryReq, dictType); | ||
| 56 | + LambdaQueryWrapper<SysDictType> wrapper = sysDictTypeService.getQueryWrapper(dictType); | ||
| 57 | + | ||
| 58 | + // 分页查询 | ||
| 59 | + Page<SysDictType> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize()); | ||
| 60 | + Page<SysDictType> dictTypePage = sysDictTypeService.page(page, wrapper); | ||
| 61 | + | ||
| 62 | + // 转换为响应DTO | ||
| 63 | + List<DictTypeRes> dictTypeResList = dictTypePage.getRecords().stream() | ||
| 64 | + .map(this::convertToDictTypeRes).collect(Collectors.toList()); | ||
| 65 | + Page<DictTypeRes> resultPage = new Page<>(dictTypePage.getCurrent(), dictTypePage.getSize(), dictTypePage.getTotal()); | ||
| 66 | + resultPage.setRecords(dictTypeResList); | ||
| 67 | + | ||
| 68 | + return ApiRes.success(resultPage); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 获取字典类型详情 | ||
| 73 | + * 根据字典类型ID获取字典类型详细信息 | ||
| 74 | + * | ||
| 75 | + * @param dictTypeId 字典类型ID | ||
| 76 | + * @return 字典类型详情 | ||
| 77 | + */ | ||
| 78 | + @Operation(summary = "获取字典类型详情", description = "根据字典类型ID获取字典类型详细信息") | ||
| 79 | + @GetMapping("/{dictTypeId}") | ||
| 80 | + @PreAuthorize("hasAuthority('sys:dict:query')") | ||
| 81 | + public ApiRes<DictTypeRes> getInfo(@Parameter(description = "字典类型ID") @PathVariable Long dictTypeId) { | ||
| 82 | + SysDictType dictType = sysDictTypeService.getById(dictTypeId); | ||
| 83 | + if (dictType == null) { | ||
| 84 | + return ApiRes.error("字典类型不存在"); | ||
| 85 | + } | ||
| 86 | + return ApiRes.success(convertToDictTypeRes(dictType)); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * 新增字典类型 | ||
| 91 | + * 创建新的字典类型 | ||
| 92 | + * | ||
| 93 | + * @param addReq 字典类型新增请求 | ||
| 94 | + * @return 操作结果 | ||
| 95 | + */ | ||
| 96 | + @Operation(summary = "新增字典类型", description = "创建新的字典类型") | ||
| 97 | + @PostMapping | ||
| 98 | + @PreAuthorize("hasAuthority('sys:dict:add')") | ||
| 99 | + public ApiRes<Void> add(@Valid @RequestBody DictTypeAddReq addReq) { | ||
| 100 | + // 检查字典类型是否唯一 | ||
| 101 | + if (!sysDictTypeService.checkDictTypeUnique(addReq.getDictType(), null)) { | ||
| 102 | + return ApiRes.error("字典类型已存在"); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + // 转换并保存 | ||
| 106 | + SysDictType dictType = new SysDictType(); | ||
| 107 | + BeanUtils.copyProperties(addReq, dictType); | ||
| 108 | + dictType.setStatus(addReq.getStatus() != null ? addReq.getStatus() : 1); | ||
| 109 | + | ||
| 110 | + boolean success = sysDictTypeService.save(dictType); | ||
| 111 | + return success ? ApiRes.success(null) : ApiRes.error("新增字典类型失败"); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + /** | ||
| 115 | + * 修改字典类型 | ||
| 116 | + * 更新字典类型信息 | ||
| 117 | + * | ||
| 118 | + * @param updateReq 字典类型更新请求 | ||
| 119 | + * @return 操作结果 | ||
| 120 | + */ | ||
| 121 | + @Operation(summary = "修改字典类型", description = "更新字典类型信息") | ||
| 122 | + @PutMapping | ||
| 123 | + @PreAuthorize("hasAuthority('sys:dict:edit')") | ||
| 124 | + public ApiRes<Void> edit(@Valid @RequestBody DictTypeUpdateReq updateReq) { | ||
| 125 | + // 检查字典类型是否存在 | ||
| 126 | + SysDictType existDictType = sysDictTypeService.getById(updateReq.getDictTypeId()); | ||
| 127 | + if (existDictType == null) { | ||
| 128 | + return ApiRes.error("字典类型不存在"); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + // 检查字典类型是否唯一 | ||
| 132 | + if (!sysDictTypeService.checkDictTypeUnique(updateReq.getDictType(), updateReq.getDictTypeId())) { | ||
| 133 | + return ApiRes.error("字典类型已存在"); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + // 转换并更新 | ||
| 137 | + SysDictType dictType = new SysDictType(); | ||
| 138 | + BeanUtils.copyProperties(updateReq, dictType); | ||
| 139 | + dictType.setStatus(updateReq.getStatus() != null ? updateReq.getStatus() : 1); | ||
| 140 | + | ||
| 141 | + boolean success = sysDictTypeService.updateById(dictType); | ||
| 142 | + return success ? ApiRes.success(null) : ApiRes.error("修改字典类型失败"); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + /** | ||
| 146 | + * 删除字典类型 | ||
| 147 | + * 批量删除字典类型 | ||
| 148 | + * | ||
| 149 | + * @param dictTypeIds 字典类型ID列表(逗号分隔) | ||
| 150 | + * @return 操作结果 | ||
| 151 | + */ | ||
| 152 | + @Operation(summary = "删除字典类型", description = "批量删除字典类型") | ||
| 153 | + @DeleteMapping("/{dictTypeIds}") | ||
| 154 | + @PreAuthorize("hasAuthority('sys:dict:remove')") | ||
| 155 | + public ApiRes<Void> remove(@Parameter(description = "字典类型ID列表") @PathVariable String dictTypeIds) { | ||
| 156 | + try { | ||
| 157 | + List<Long> ids = Arrays.stream(dictTypeIds.split(",")) | ||
| 158 | + .map(Long::valueOf) | ||
| 159 | + .collect(Collectors.toList()); | ||
| 160 | + | ||
| 161 | + // 检查是否被字典项使用 | ||
| 162 | + for (Long dictTypeId : ids) { | ||
| 163 | + if (sysDictTypeService.checkDictTypeExistItem(dictTypeId)) { | ||
| 164 | + return ApiRes.error("字典类型已分配,不允许删除"); | ||
| 165 | + } | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + boolean success = sysDictTypeService.removeByIds(ids); | ||
| 169 | + return success ? ApiRes.success(null) : ApiRes.error("删除字典类型失败"); | ||
| 170 | + } catch (NumberFormatException e) { | ||
| 171 | + return ApiRes.error("字典类型ID格式错误"); | ||
| 172 | + } | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + /** | ||
| 176 | + * 刷新字典缓存 | ||
| 177 | + * 清除字典相关的缓存数据 | ||
| 178 | + * | ||
| 179 | + * @return 操作结果 | ||
| 180 | + */ | ||
| 181 | + @Operation(summary = "刷新字典缓存", description = "清除字典相关的缓存数据") | ||
| 182 | + @DeleteMapping("/refreshCache") | ||
| 183 | + @PreAuthorize("hasAuthority('sys:dict:remove')") | ||
| 184 | + public ApiRes<Void> refreshCache() { | ||
| 185 | + // TODO: 实现字典缓存刷新逻辑 | ||
| 186 | + return ApiRes.success(null); | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + /** | ||
| 190 | + * 获取字典选择框列表 | ||
| 191 | + * 获取所有可用的字典类型列表 | ||
| 192 | + * | ||
| 193 | + * @return 字典类型列表 | ||
| 194 | + */ | ||
| 195 | + @Operation(summary = "获取字典选择框列表", description = "获取所有可用的字典类型列表") | ||
| 196 | + @GetMapping("/optionselect") | ||
| 197 | + @PreAuthorize("hasAuthority('sys:dict:list')") | ||
| 198 | + public ApiRes<List<DictTypeRes>> optionselect() { | ||
| 199 | + LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>(); | ||
| 200 | + wrapper.eq(SysDictType::getStatus, 1) | ||
| 201 | + .eq(SysDictType::getDelFlag, "0") | ||
| 202 | + .orderByAsc(SysDictType::getDictType); | ||
| 203 | + | ||
| 204 | + List<SysDictType> dictTypes = sysDictTypeService.list(wrapper); | ||
| 205 | + List<DictTypeRes> dictTypeResList = dictTypes.stream() | ||
| 206 | + .map(this::convertToDictTypeRes).collect(Collectors.toList()); | ||
| 207 | + | ||
| 208 | + return ApiRes.success(dictTypeResList); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + /** | ||
| 212 | + * 转换SysDictType为DictTypeRes | ||
| 213 | + * | ||
| 214 | + * @param dictType 字典类型实体 | ||
| 215 | + * @return 字典类型响应DTO | ||
| 216 | + */ | ||
| 217 | + private DictTypeRes convertToDictTypeRes(SysDictType dictType) { | ||
| 218 | + DictTypeRes dictTypeRes = new DictTypeRes(); | ||
| 219 | + BeanUtils.copyProperties(dictType, dictTypeRes); | ||
| 220 | + | ||
| 221 | + // 设置状态描述 | ||
| 222 | + if (dictType.getStatus() != null) { | ||
| 223 | + dictTypeRes.setStatusText(dictType.getStatus() == 1 ? "启用" : "停用"); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + return dictTypeRes; | ||
| 227 | + } | ||
| 228 | +} |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.response.ApiRes; | ||
| 4 | +import com.apple.erp.dto.request.LogQueryReq; | ||
| 5 | +import com.apple.erp.dto.response.LogDetailRes; | ||
| 6 | +import com.apple.erp.dto.response.LogRes; | ||
| 7 | +import com.apple.erp.service.SysLogService; | ||
| 8 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 9 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 12 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 13 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 14 | +import org.springframework.web.bind.annotation.*; | ||
| 15 | + | ||
| 16 | +import java.time.LocalDateTime; | ||
| 17 | +import java.util.List; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * 系统日志Controller | ||
| 21 | + * | ||
| 22 | + * @author Apple ERP Team | ||
| 23 | + * @version 1.0.0 | ||
| 24 | + * @since 2024-01-01 | ||
| 25 | + */ | ||
| 26 | +@Tag(name = "系统日志管理", description = "系统日志相关接口") | ||
| 27 | +@RestController | ||
| 28 | +@RequestMapping("/api/system/log") | ||
| 29 | +public class SysLogController { | ||
| 30 | + | ||
| 31 | + @Autowired | ||
| 32 | + private SysLogService sysLogService; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 分页查询日志列表 | ||
| 36 | + */ | ||
| 37 | + @Operation(summary = "分页查询日志列表", description = "根据条件分页查询系统日志") | ||
| 38 | + @GetMapping("/page") | ||
| 39 | + @PreAuthorize("hasAuthority('system:log:list')") | ||
| 40 | + public ApiRes<Page<LogRes>> getLogPage(LogQueryReq queryReq) { | ||
| 41 | + Page<LogRes> page = sysLogService.getLogPage(queryReq); | ||
| 42 | + return ApiRes.success(page); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 获取日志详情 | ||
| 47 | + */ | ||
| 48 | + @Operation(summary = "获取日志详情", description = "根据日志ID获取日志详细信息") | ||
| 49 | + @GetMapping("/{logId}") | ||
| 50 | + @PreAuthorize("hasAuthority('system:log:query')") | ||
| 51 | + public ApiRes<LogDetailRes> getLogDetail( | ||
| 52 | + @Parameter(description = "日志ID", required = true) | ||
| 53 | + @PathVariable Long logId) { | ||
| 54 | + LogDetailRes logDetail = sysLogService.getLogDetail(logId); | ||
| 55 | + if (logDetail == null) { | ||
| 56 | + return ApiRes.error("日志不存在"); | ||
| 57 | + } | ||
| 58 | + return ApiRes.success(logDetail); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 根据用户ID查询登录日志 | ||
| 63 | + */ | ||
| 64 | + @Operation(summary = "查询用户登录日志", description = "根据用户ID查询登录日志") | ||
| 65 | + @GetMapping("/login/{userId}") | ||
| 66 | + @PreAuthorize("hasAuthority('system:log:list')") | ||
| 67 | + public ApiRes<List<LogRes>> getLoginLogsByUserId( | ||
| 68 | + @Parameter(description = "用户ID", required = true) | ||
| 69 | + @PathVariable Long userId, | ||
| 70 | + @Parameter(description = "开始时间") | ||
| 71 | + @RequestParam(required = false) LocalDateTime startTime, | ||
| 72 | + @Parameter(description = "结束时间") | ||
| 73 | + @RequestParam(required = false) LocalDateTime endTime) { | ||
| 74 | + List<LogRes> loginLogs = sysLogService.getLoginLogsByUserId(userId, startTime, endTime); | ||
| 75 | + return ApiRes.success(loginLogs); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 清理过期日志 | ||
| 80 | + */ | ||
| 81 | + @Operation(summary = "清理过期日志", description = "清理指定天数之前的日志") | ||
| 82 | + @DeleteMapping("/clean") | ||
| 83 | + @PreAuthorize("hasAuthority('system:log:clean')") | ||
| 84 | + public ApiRes<String> cleanExpiredLogs( | ||
| 85 | + @Parameter(description = "过期天数", required = true) | ||
| 86 | + @RequestParam int expireDays) { | ||
| 87 | + if (expireDays <= 0) { | ||
| 88 | + return ApiRes.error("过期天数必须大于0"); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + int cleanedCount = sysLogService.cleanExpiredLogs(expireDays); | ||
| 92 | + return ApiRes.success("成功清理 " + cleanedCount + " 条过期日志"); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + /** | ||
| 96 | + * 统计日志数量 | ||
| 97 | + */ | ||
| 98 | + @Operation(summary = "统计日志数量", description = "根据条件统计日志数量") | ||
| 99 | + @GetMapping("/count") | ||
| 100 | + @PreAuthorize("hasAuthority('system:log:count')") | ||
| 101 | + public ApiRes<Integer> countLogs( | ||
| 102 | + @Parameter(description = "日志类型") | ||
| 103 | + @RequestParam(required = false) String logType, | ||
| 104 | + @Parameter(description = "开始时间") | ||
| 105 | + @RequestParam(required = false) LocalDateTime startTime, | ||
| 106 | + @Parameter(description = "结束时间") | ||
| 107 | + @RequestParam(required = false) LocalDateTime endTime) { | ||
| 108 | + int count = sysLogService.countLogs(logType, startTime, endTime); | ||
| 109 | + return ApiRes.success(count); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + /** | ||
| 113 | + * 删除日志 | ||
| 114 | + */ | ||
| 115 | + @Operation(summary = "删除日志", description = "根据日志ID删除日志") | ||
| 116 | + @DeleteMapping("/{logId}") | ||
| 117 | + @PreAuthorize("hasAuthority('system:log:remove')") | ||
| 118 | + public ApiRes<String> deleteLog( | ||
| 119 | + @Parameter(description = "日志ID", required = true) | ||
| 120 | + @PathVariable Long logId) { | ||
| 121 | + boolean success = sysLogService.removeById(logId); | ||
| 122 | + if (success) { | ||
| 123 | + return ApiRes.success("删除成功"); | ||
| 124 | + } else { | ||
| 125 | + return ApiRes.error("删除失败"); | ||
| 126 | + } | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + /** | ||
| 130 | + * 批量删除日志 | ||
| 131 | + */ | ||
| 132 | + @Operation(summary = "批量删除日志", description = "根据日志ID列表批量删除日志") | ||
| 133 | + @DeleteMapping("/batch") | ||
| 134 | + @PreAuthorize("hasAuthority('system:log:remove')") | ||
| 135 | + public ApiRes<String> deleteLogs( | ||
| 136 | + @Parameter(description = "日志ID列表", required = true) | ||
| 137 | + @RequestBody List<Long> logIds) { | ||
| 138 | + if (logIds == null || logIds.isEmpty()) { | ||
| 139 | + return ApiRes.error("日志ID列表不能为空"); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + boolean success = sysLogService.removeByIds(logIds); | ||
| 143 | + if (success) { | ||
| 144 | + return ApiRes.success("批量删除成功"); | ||
| 145 | + } else { | ||
| 146 | + return ApiRes.error("批量删除失败"); | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | +} |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.vo.ResultVO; | ||
| 4 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 5 | +import io.swagger.v3.oas.annotations.security.SecurityRequirement; | ||
| 6 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 7 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 8 | +import org.springframework.web.bind.annotation.*; | ||
| 9 | + | ||
| 10 | +import java.util.HashMap; | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 测试控制器 | ||
| 15 | + * | ||
| 16 | + * @author Apple ERP Team | ||
| 17 | + * @version 1.0.0 | ||
| 18 | + * @since 2024-01-01 | ||
| 19 | + */ | ||
| 20 | +@RestController | ||
| 21 | +@RequestMapping("/api/test") | ||
| 22 | +@CrossOrigin(origins = "*") | ||
| 23 | +@Tag(name = "测试接口", description = "JWT认证功能测试接口") | ||
| 24 | +public class TestController { | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 公开接口测试 | ||
| 28 | + */ | ||
| 29 | + @Operation(summary = "公开接口测试", description = "无需认证的公开接口") | ||
| 30 | + @GetMapping("/public") | ||
| 31 | + public ResultVO<Map<String, Object>> publicTest() { | ||
| 32 | + Map<String, Object> data = new HashMap<>(); | ||
| 33 | + data.put("message", "这是一个公开接口"); | ||
| 34 | + data.put("timestamp", System.currentTimeMillis()); | ||
| 35 | + return ResultVO.success("公开接口测试成功", data); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 需要认证的接口测试 | ||
| 40 | + */ | ||
| 41 | + @Operation(summary = "认证接口测试", description = "需要JWT令牌认证的接口") | ||
| 42 | + @SecurityRequirement(name = "Bearer Authentication") | ||
| 43 | + @GetMapping("/auth") | ||
| 44 | + public ResultVO<Map<String, Object>> authTest() { | ||
| 45 | + Map<String, Object> data = new HashMap<>(); | ||
| 46 | + data.put("message", "这是一个需要认证的接口"); | ||
| 47 | + data.put("timestamp", System.currentTimeMillis()); | ||
| 48 | + return ResultVO.success("认证接口测试成功", data); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 需要管理员权限的接口测试 | ||
| 53 | + */ | ||
| 54 | + @Operation(summary = "管理员接口测试", description = "需要ADMIN角色权限的接口") | ||
| 55 | + @SecurityRequirement(name = "Bearer Authentication") | ||
| 56 | + @GetMapping("/admin") | ||
| 57 | + @PreAuthorize("hasRole('ADMIN')") | ||
| 58 | + public ResultVO<Map<String, Object>> adminTest() { | ||
| 59 | + Map<String, Object> data = new HashMap<>(); | ||
| 60 | + data.put("message", "这是一个需要管理员权限的接口"); | ||
| 61 | + data.put("timestamp", System.currentTimeMillis()); | ||
| 62 | + return ResultVO.success("管理员接口测试成功", data); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 需要特定权限的接口测试 | ||
| 67 | + */ | ||
| 68 | + @Operation(summary = "权限接口测试", description = "需要特定权限的接口") | ||
| 69 | + @SecurityRequirement(name = "Bearer Authentication") | ||
| 70 | + @GetMapping("/permission") | ||
| 71 | + @PreAuthorize("hasAuthority('sys:user:list')") | ||
| 72 | + public ResultVO<Map<String, Object>> permissionTest() { | ||
| 73 | + Map<String, Object> data = new HashMap<>(); | ||
| 74 | + data.put("message", "这是一个需要特定权限的接口"); | ||
| 75 | + data.put("timestamp", System.currentTimeMillis()); | ||
| 76 | + return ResultVO.success("权限接口测试成功", data); | ||
| 77 | + } | ||
| 78 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 字典项新增请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class DictItemAddReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 字典类型ID | ||
| 21 | + */ | ||
| 22 | + @NotNull(message = "字典类型ID不能为空") | ||
| 23 | + private Long dictTypeId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典标签 | ||
| 27 | + */ | ||
| 28 | + @NotBlank(message = "字典标签不能为空") | ||
| 29 | + @Size(max = 100, message = "字典标签长度不能超过100个字符") | ||
| 30 | + private String dictLabel; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 字典值 | ||
| 34 | + */ | ||
| 35 | + @NotBlank(message = "字典值不能为空") | ||
| 36 | + @Size(max = 50, message = "字典值长度不能超过50个字符") | ||
| 37 | + private String dictValue; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 排序号 | ||
| 41 | + */ | ||
| 42 | + private Integer sort; | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 备注 | ||
| 46 | + */ | ||
| 47 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 48 | + private String remark; | ||
| 49 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 字典项查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class DictItemQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 页码 | ||
| 17 | + */ | ||
| 18 | + private Long pageNum = 1L; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 每页大小 | ||
| 22 | + */ | ||
| 23 | + private Long pageSize = 10L; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典类型ID | ||
| 27 | + */ | ||
| 28 | + private Long dictTypeId; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 字典标签 | ||
| 32 | + */ | ||
| 33 | + private String dictLabel; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 字典值 | ||
| 37 | + */ | ||
| 38 | + private String dictValue; | ||
| 39 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 字典项更新请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class DictItemUpdateReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 字典项ID | ||
| 21 | + */ | ||
| 22 | + @NotNull(message = "字典项ID不能为空") | ||
| 23 | + private Long dictItemId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典类型ID | ||
| 27 | + */ | ||
| 28 | + @NotNull(message = "字典类型ID不能为空") | ||
| 29 | + private Long dictTypeId; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 字典标签 | ||
| 33 | + */ | ||
| 34 | + @NotBlank(message = "字典标签不能为空") | ||
| 35 | + @Size(max = 100, message = "字典标签长度不能超过100个字符") | ||
| 36 | + private String dictLabel; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 字典值 | ||
| 40 | + */ | ||
| 41 | + @NotBlank(message = "字典值不能为空") | ||
| 42 | + @Size(max = 50, message = "字典值长度不能超过50个字符") | ||
| 43 | + private String dictValue; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 排序号 | ||
| 47 | + */ | ||
| 48 | + private Integer sort; | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 备注 | ||
| 52 | + */ | ||
| 53 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 54 | + private String remark; | ||
| 55 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.Pattern; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 字典类型新增请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class DictTypeAddReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 字典类型 | ||
| 21 | + */ | ||
| 22 | + @NotBlank(message = "字典类型不能为空") | ||
| 23 | + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型只能包含小写字母、数字和下划线,且必须以字母开头") | ||
| 24 | + @Size(max = 50, message = "字典类型长度不能超过50个字符") | ||
| 25 | + private String dictType; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 字典名称 | ||
| 29 | + */ | ||
| 30 | + @NotBlank(message = "字典名称不能为空") | ||
| 31 | + @Size(max = 100, message = "字典名称长度不能超过100个字符") | ||
| 32 | + private String dictName; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 状态(0-停用/1-启用) | ||
| 36 | + */ | ||
| 37 | + private Integer status; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 备注 | ||
| 41 | + */ | ||
| 42 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 43 | + private String remark; | ||
| 44 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 字典类型查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class DictTypeQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 页码 | ||
| 17 | + */ | ||
| 18 | + private Long pageNum = 1L; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 每页大小 | ||
| 22 | + */ | ||
| 23 | + private Long pageSize = 10L; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典类型 | ||
| 27 | + */ | ||
| 28 | + private String dictType; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 字典名称 | ||
| 32 | + */ | ||
| 33 | + private String dictName; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 状态(0-停用/1-启用) | ||
| 37 | + */ | ||
| 38 | + private Integer status; | ||
| 39 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Pattern; | ||
| 8 | +import javax.validation.constraints.Size; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 字典类型更新请求DTO | ||
| 12 | + * | ||
| 13 | + * @author Apple ERP Team | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @since 2024-01-01 | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +public class DictTypeUpdateReq { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 字典类型ID | ||
| 22 | + */ | ||
| 23 | + @NotNull(message = "字典类型ID不能为空") | ||
| 24 | + private Long dictTypeId; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 字典类型 | ||
| 28 | + */ | ||
| 29 | + @NotBlank(message = "字典类型不能为空") | ||
| 30 | + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型只能包含小写字母、数字和下划线,且必须以字母开头") | ||
| 31 | + @Size(max = 50, message = "字典类型长度不能超过50个字符") | ||
| 32 | + private String dictType; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 字典名称 | ||
| 36 | + */ | ||
| 37 | + @NotBlank(message = "字典名称不能为空") | ||
| 38 | + @Size(max = 100, message = "字典名称长度不能超过100个字符") | ||
| 39 | + private String dictName; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 状态(0-停用/1-启用) | ||
| 43 | + */ | ||
| 44 | + private Integer status; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 备注 | ||
| 48 | + */ | ||
| 49 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 50 | + private String remark; | ||
| 51 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 日志查询请求DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@Schema(description = "日志查询请求") | ||
| 17 | +public class LogQueryReq { | ||
| 18 | + | ||
| 19 | + @Schema(description = "日志类型(0-操作日志/1-异常日志/2-登录日志)") | ||
| 20 | + private String logType; | ||
| 21 | + | ||
| 22 | + @Schema(description = "用户ID") | ||
| 23 | + private Long userId; | ||
| 24 | + | ||
| 25 | + @Schema(description = "用户名") | ||
| 26 | + private String username; | ||
| 27 | + | ||
| 28 | + @Schema(description = "操作模块") | ||
| 29 | + private String module; | ||
| 30 | + | ||
| 31 | + @Schema(description = "操作内容") | ||
| 32 | + private String operation; | ||
| 33 | + | ||
| 34 | + @Schema(description = "操作IP") | ||
| 35 | + private String ip; | ||
| 36 | + | ||
| 37 | + @Schema(description = "操作结果(0-失败/1-成功)") | ||
| 38 | + private Integer status; | ||
| 39 | + | ||
| 40 | + @Schema(description = "开始时间") | ||
| 41 | + private LocalDateTime startTime; | ||
| 42 | + | ||
| 43 | + @Schema(description = "结束时间") | ||
| 44 | + private LocalDateTime endTime; | ||
| 45 | + | ||
| 46 | + @Schema(description = "页码", example = "1") | ||
| 47 | + private Integer pageNum = 1; | ||
| 48 | + | ||
| 49 | + @Schema(description = "每页大小", example = "10") | ||
| 50 | + private Integer pageSize = 10; | ||
| 51 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotBlank; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 登录请求对象 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@Schema(description = "用户登录请求") | ||
| 18 | +public class LoginReq { | ||
| 19 | + | ||
| 20 | + @NotBlank(message = "用户名不能为空") | ||
| 21 | + @Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间") | ||
| 22 | + @Schema(description = "用户名", example = "admin", required = true) | ||
| 23 | + private String username; | ||
| 24 | + | ||
| 25 | + @NotBlank(message = "密码不能为空") | ||
| 26 | + @Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间") | ||
| 27 | + @Schema(description = "密码", example = "password", required = true) | ||
| 28 | + private String password; | ||
| 29 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 菜单新增请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class MenuAddReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 父菜单ID | ||
| 21 | + */ | ||
| 22 | + private Long parentId; | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 菜单名称 | ||
| 26 | + */ | ||
| 27 | + @NotBlank(message = "菜单名称不能为空") | ||
| 28 | + @Size(max = 100, message = "菜单名称长度不能超过100个字符") | ||
| 29 | + private String menuName; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 菜单类型(0-目录/1-菜单/2-按钮) | ||
| 33 | + */ | ||
| 34 | + @NotNull(message = "菜单类型不能为空") | ||
| 35 | + private String menuType; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 菜单路径 | ||
| 39 | + */ | ||
| 40 | + @Size(max = 200, message = "菜单路径长度不能超过200个字符") | ||
| 41 | + private String path; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 菜单图标 | ||
| 45 | + */ | ||
| 46 | + @Size(max = 50, message = "菜单图标长度不能超过50个字符") | ||
| 47 | + private String icon; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 排序号 | ||
| 51 | + */ | ||
| 52 | + private Integer sort; | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 状态(0-隐藏/1-显示) | ||
| 56 | + */ | ||
| 57 | + private Integer status; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 权限标识 | ||
| 61 | + */ | ||
| 62 | + @Size(max = 100, message = "权限标识长度不能超过100个字符") | ||
| 63 | + private String perms; | ||
| 64 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 菜单查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class MenuQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 页码 | ||
| 17 | + */ | ||
| 18 | + private Long pageNum = 1L; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 每页大小 | ||
| 22 | + */ | ||
| 23 | + private Long pageSize = 10L; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 菜单名称 | ||
| 27 | + */ | ||
| 28 | + private String menuName; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 菜单ID | ||
| 32 | + */ | ||
| 33 | + private Long menuId; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 菜单路径 | ||
| 37 | + */ | ||
| 38 | + private String path; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 菜单类型(0-目录/1-菜单/2-按钮) | ||
| 42 | + */ | ||
| 43 | + private String menuType; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 状态(0-隐藏/1-显示) | ||
| 47 | + */ | ||
| 48 | + private Integer status; | ||
| 49 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 菜单更新请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class MenuUpdateReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 菜单ID | ||
| 21 | + */ | ||
| 22 | + @NotNull(message = "菜单ID不能为空") | ||
| 23 | + private Long menuId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 父菜单ID | ||
| 27 | + */ | ||
| 28 | + private Long parentId; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 菜单名称 | ||
| 32 | + */ | ||
| 33 | + @NotBlank(message = "菜单名称不能为空") | ||
| 34 | + @Size(max = 100, message = "菜单名称长度不能超过100个字符") | ||
| 35 | + private String menuName; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 菜单类型(0-目录/1-菜单/2-按钮) | ||
| 39 | + */ | ||
| 40 | + @NotNull(message = "菜单类型不能为空") | ||
| 41 | + private String menuType; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 菜单路径 | ||
| 45 | + */ | ||
| 46 | + @Size(max = 200, message = "菜单路径长度不能超过200个字符") | ||
| 47 | + private String path; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 菜单图标 | ||
| 51 | + */ | ||
| 52 | + @Size(max = 50, message = "菜单图标长度不能超过50个字符") | ||
| 53 | + private String icon; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 排序号 | ||
| 57 | + */ | ||
| 58 | + private Integer sort; | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 状态(0-隐藏/1-显示) | ||
| 62 | + */ | ||
| 63 | + private Integer status; | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 权限标识 | ||
| 67 | + */ | ||
| 68 | + @Size(max = 100, message = "权限标识长度不能超过100个字符") | ||
| 69 | + private String perms; | ||
| 70 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotBlank; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 刷新令牌请求对象 | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@Schema(description = "刷新令牌请求") | ||
| 17 | +public class RefreshTokenReq { | ||
| 18 | + | ||
| 19 | + @NotBlank(message = "刷新令牌不能为空") | ||
| 20 | + @Schema(description = "刷新令牌", example = "eyJhbGciOiJIUzUxMiJ9...", required = true) | ||
| 21 | + private String refreshToken; | ||
| 22 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.Size; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 角色新增请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class RoleAddReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 角色编码 | ||
| 21 | + */ | ||
| 22 | + @NotBlank(message = "角色编码不能为空") | ||
| 23 | + @Size(max = 50, message = "角色编码长度不能超过50个字符") | ||
| 24 | + private String roleCode; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 角色名称 | ||
| 28 | + */ | ||
| 29 | + @NotBlank(message = "角色名称不能为空") | ||
| 30 | + @Size(max = 50, message = "角色名称长度不能超过50个字符") | ||
| 31 | + private String roleName; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 状态(0-停用/1-启用) | ||
| 35 | + */ | ||
| 36 | + private Integer status; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 备注 | ||
| 40 | + */ | ||
| 41 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 42 | + private String remark; | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 菜单ID列表 | ||
| 46 | + */ | ||
| 47 | + private List<Long> menuIds; | ||
| 48 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 角色查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class RoleQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 角色编码 | ||
| 17 | + */ | ||
| 18 | + private String roleCode; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 角色名称 | ||
| 22 | + */ | ||
| 23 | + private String roleName; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 状态(0-停用/1-启用) | ||
| 27 | + */ | ||
| 28 | + private Integer status; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 页码 | ||
| 32 | + */ | ||
| 33 | + private Integer pageNum = 1; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 每页大小 | ||
| 37 | + */ | ||
| 38 | + private Integer pageSize = 10; | ||
| 39 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 角色更新请求DTO | ||
| 12 | + * | ||
| 13 | + * @author Apple ERP Team | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @since 2024-01-01 | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +public class RoleUpdateReq { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 角色ID | ||
| 22 | + */ | ||
| 23 | + @NotNull(message = "角色ID不能为空") | ||
| 24 | + private Long roleId; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 角色编码 | ||
| 28 | + */ | ||
| 29 | + @NotBlank(message = "角色编码不能为空") | ||
| 30 | + @Size(max = 50, message = "角色编码长度不能超过50个字符") | ||
| 31 | + private String roleCode; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 角色名称 | ||
| 35 | + */ | ||
| 36 | + @NotBlank(message = "角色名称不能为空") | ||
| 37 | + @Size(max = 50, message = "角色名称长度不能超过50个字符") | ||
| 38 | + private String roleName; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 状态(0-停用/1-启用) | ||
| 42 | + */ | ||
| 43 | + private Integer status; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 备注 | ||
| 47 | + */ | ||
| 48 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 49 | + private String remark; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 菜单ID列表 | ||
| 53 | + */ | ||
| 54 | + private List<Long> menuIds; | ||
| 55 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.Email; | ||
| 6 | +import javax.validation.constraints.NotBlank; | ||
| 7 | +import javax.validation.constraints.Pattern; | ||
| 8 | +import javax.validation.constraints.Size; | ||
| 9 | +import java.util.List; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 用户新增请求DTO | ||
| 13 | + * | ||
| 14 | + * @author Apple ERP Team | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @since 2024-01-01 | ||
| 17 | + */ | ||
| 18 | +@Data | ||
| 19 | +public class UserAddReq { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 用户名 | ||
| 23 | + */ | ||
| 24 | + @NotBlank(message = "用户名不能为空") | ||
| 25 | + @Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间") | ||
| 26 | + private String username; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 密码 | ||
| 30 | + */ | ||
| 31 | + @NotBlank(message = "密码不能为空") | ||
| 32 | + @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间") | ||
| 33 | + private String password; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 真实姓名 | ||
| 37 | + */ | ||
| 38 | + @NotBlank(message = "真实姓名不能为空") | ||
| 39 | + @Size(max = 100, message = "真实姓名长度不能超过100个字符") | ||
| 40 | + private String realName; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 手机号 | ||
| 44 | + */ | ||
| 45 | + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") | ||
| 46 | + private String phone; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 邮箱 | ||
| 50 | + */ | ||
| 51 | + @Email(message = "邮箱格式不正确") | ||
| 52 | + @Size(max = 100, message = "邮箱长度不能超过100个字符") | ||
| 53 | + private String email; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 状态(0-停用/1-启用) | ||
| 57 | + */ | ||
| 58 | + private Integer status; | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 备注 | ||
| 62 | + */ | ||
| 63 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 64 | + private String remark; | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 角色ID列表 | ||
| 68 | + */ | ||
| 69 | + private List<Long> roleIds; | ||
| 70 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotBlank; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import javax.validation.constraints.Size; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 用户密码重置请求DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +public class UserPasswordResetReq { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 用户ID | ||
| 21 | + */ | ||
| 22 | + @NotNull(message = "用户ID不能为空") | ||
| 23 | + private Long userId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 新密码 | ||
| 27 | + */ | ||
| 28 | + @NotBlank(message = "新密码不能为空") | ||
| 29 | + @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间") | ||
| 30 | + private String newPassword; | ||
| 31 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 用户查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class UserQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 用户名 | ||
| 17 | + */ | ||
| 18 | + private String username; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 真实姓名 | ||
| 22 | + */ | ||
| 23 | + private String realName; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 手机号 | ||
| 27 | + */ | ||
| 28 | + private String phone; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 邮箱 | ||
| 32 | + */ | ||
| 33 | + private String email; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 状态(0-停用/1-启用) | ||
| 37 | + */ | ||
| 38 | + private Integer status; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 页码 | ||
| 42 | + */ | ||
| 43 | + private Integer pageNum = 1; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 每页大小 | ||
| 47 | + */ | ||
| 48 | + private Integer pageSize = 10; | ||
| 49 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.Email; | ||
| 6 | +import javax.validation.constraints.NotBlank; | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | +import javax.validation.constraints.Pattern; | ||
| 9 | +import javax.validation.constraints.Size; | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 用户更新请求DTO | ||
| 14 | + * | ||
| 15 | + * @author Apple ERP Team | ||
| 16 | + * @version 1.0.0 | ||
| 17 | + * @since 2024-01-01 | ||
| 18 | + */ | ||
| 19 | +@Data | ||
| 20 | +public class UserUpdateReq { | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 用户ID | ||
| 24 | + */ | ||
| 25 | + @NotNull(message = "用户ID不能为空") | ||
| 26 | + private Long userId; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 用户名 | ||
| 30 | + */ | ||
| 31 | + @NotBlank(message = "用户名不能为空") | ||
| 32 | + @Size(min = 2, max = 50, message = "用户名长度必须在2-50个字符之间") | ||
| 33 | + private String username; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 真实姓名 | ||
| 37 | + */ | ||
| 38 | + @NotBlank(message = "真实姓名不能为空") | ||
| 39 | + @Size(max = 100, message = "真实姓名长度不能超过100个字符") | ||
| 40 | + private String realName; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 手机号 | ||
| 44 | + */ | ||
| 45 | + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") | ||
| 46 | + private String phone; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 邮箱 | ||
| 50 | + */ | ||
| 51 | + @Email(message = "邮箱格式不正确") | ||
| 52 | + @Size(max = 100, message = "邮箱长度不能超过100个字符") | ||
| 53 | + private String email; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 状态(0-停用/1-启用) | ||
| 57 | + */ | ||
| 58 | + private Integer status; | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 备注 | ||
| 62 | + */ | ||
| 63 | + @Size(max = 500, message = "备注长度不能超过500个字符") | ||
| 64 | + private String remark; | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 角色ID列表 | ||
| 68 | + */ | ||
| 69 | + private List<Long> roleIds; | ||
| 70 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 统一API响应对象 | ||
| 8 | + * | ||
| 9 | + * @author Apple ERP Team | ||
| 10 | + * @version 1.0.0 | ||
| 11 | + * @since 2024-01-01 | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +@Schema(description = "统一API响应") | ||
| 15 | +public class ApiRes<T> { | ||
| 16 | + | ||
| 17 | + @Schema(description = "响应码", example = "200") | ||
| 18 | + private Integer code; | ||
| 19 | + | ||
| 20 | + @Schema(description = "响应消息", example = "操作成功") | ||
| 21 | + private String message; | ||
| 22 | + | ||
| 23 | + @Schema(description = "响应数据") | ||
| 24 | + private T data; | ||
| 25 | + | ||
| 26 | + public ApiRes() {} | ||
| 27 | + | ||
| 28 | + public ApiRes(Integer code, String message, T data) { | ||
| 29 | + this.code = code; | ||
| 30 | + this.message = message; | ||
| 31 | + this.data = data; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 成功响应 | ||
| 36 | + */ | ||
| 37 | + public static <T> ApiRes<T> success(T data) { | ||
| 38 | + return new ApiRes<>(200, "操作成功", data); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 成功响应(带消息) | ||
| 43 | + */ | ||
| 44 | + public static <T> ApiRes<T> success(String message, T data) { | ||
| 45 | + return new ApiRes<>(200, message, data); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 失败响应 | ||
| 50 | + */ | ||
| 51 | + public static <T> ApiRes<T> error(Integer code, String message) { | ||
| 52 | + return new ApiRes<>(code, message, null); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 失败响应(默认401) | ||
| 57 | + */ | ||
| 58 | + public static <T> ApiRes<T> error(String message) { | ||
| 59 | + return new ApiRes<>(401, message, null); | ||
| 60 | + } | ||
| 61 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.time.LocalDateTime; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 字典项响应DTO | ||
| 9 | + * | ||
| 10 | + * @author Apple ERP Team | ||
| 11 | + * @version 1.0.0 | ||
| 12 | + * @since 2024-01-01 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class DictItemRes { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 字典项ID | ||
| 19 | + */ | ||
| 20 | + private Long dictItemId; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 字典类型ID | ||
| 24 | + */ | ||
| 25 | + private Long dictTypeId; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 字典类型 | ||
| 29 | + */ | ||
| 30 | + private String dictType; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 字典标签 | ||
| 34 | + */ | ||
| 35 | + private String dictLabel; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 字典值 | ||
| 39 | + */ | ||
| 40 | + private String dictValue; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 排序号 | ||
| 44 | + */ | ||
| 45 | + private Integer sort; | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 备注 | ||
| 49 | + */ | ||
| 50 | + private String remark; | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 创建者 | ||
| 54 | + */ | ||
| 55 | + private String createBy; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 创建时间 | ||
| 59 | + */ | ||
| 60 | + private LocalDateTime createTime; | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * 更新者 | ||
| 64 | + */ | ||
| 65 | + private String updateBy; | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 更新时间 | ||
| 69 | + */ | ||
| 70 | + private LocalDateTime updateTime; | ||
| 71 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.time.LocalDateTime; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 字典类型响应DTO | ||
| 9 | + * | ||
| 10 | + * @author Apple ERP Team | ||
| 11 | + * @version 1.0.0 | ||
| 12 | + * @since 2024-01-01 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class DictTypeRes { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 字典类型ID | ||
| 19 | + */ | ||
| 20 | + private Long dictTypeId; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 字典类型 | ||
| 24 | + */ | ||
| 25 | + private String dictType; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 字典名称 | ||
| 29 | + */ | ||
| 30 | + private String dictName; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 状态(0-停用/1-启用) | ||
| 34 | + */ | ||
| 35 | + private Integer status; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 状态描述 | ||
| 39 | + */ | ||
| 40 | + private String statusText; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 备注 | ||
| 44 | + */ | ||
| 45 | + private String remark; | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 创建者 | ||
| 49 | + */ | ||
| 50 | + private String createBy; | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 创建时间 | ||
| 54 | + */ | ||
| 55 | + private LocalDateTime createTime; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 更新者 | ||
| 59 | + */ | ||
| 60 | + private String updateBy; | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * 更新时间 | ||
| 64 | + */ | ||
| 65 | + private LocalDateTime updateTime; | ||
| 66 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 日志详情响应DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@Schema(description = "日志详情响应") | ||
| 17 | +public class LogDetailRes { | ||
| 18 | + | ||
| 19 | + @Schema(description = "日志ID") | ||
| 20 | + private Long logId; | ||
| 21 | + | ||
| 22 | + @Schema(description = "日志类型(0-操作日志/1-异常日志/2-登录日志)") | ||
| 23 | + private String logType; | ||
| 24 | + | ||
| 25 | + @Schema(description = "日志类型描述") | ||
| 26 | + private String logTypeText; | ||
| 27 | + | ||
| 28 | + @Schema(description = "用户ID") | ||
| 29 | + private Long userId; | ||
| 30 | + | ||
| 31 | + @Schema(description = "用户名") | ||
| 32 | + private String username; | ||
| 33 | + | ||
| 34 | + @Schema(description = "操作模块") | ||
| 35 | + private String module; | ||
| 36 | + | ||
| 37 | + @Schema(description = "操作内容") | ||
| 38 | + private String operation; | ||
| 39 | + | ||
| 40 | + @Schema(description = "操作IP") | ||
| 41 | + private String ip; | ||
| 42 | + | ||
| 43 | + @Schema(description = "日志时间") | ||
| 44 | + private LocalDateTime logTime; | ||
| 45 | + | ||
| 46 | + @Schema(description = "操作结果(0-失败/1-成功)") | ||
| 47 | + private Integer status; | ||
| 48 | + | ||
| 49 | + @Schema(description = "操作结果描述") | ||
| 50 | + private String statusText; | ||
| 51 | + | ||
| 52 | + @Schema(description = "错误信息") | ||
| 53 | + private String errorMsg; | ||
| 54 | + | ||
| 55 | + @Schema(description = "请求参数") | ||
| 56 | + private String requestParam; | ||
| 57 | + | ||
| 58 | + @Schema(description = "创建者") | ||
| 59 | + private String createBy; | ||
| 60 | + | ||
| 61 | + @Schema(description = "创建时间") | ||
| 62 | + private LocalDateTime createTime; | ||
| 63 | + | ||
| 64 | + @Schema(description = "更新者") | ||
| 65 | + private String updateBy; | ||
| 66 | + | ||
| 67 | + @Schema(description = "更新时间") | ||
| 68 | + private LocalDateTime updateTime; | ||
| 69 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 日志响应DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@Schema(description = "日志响应") | ||
| 17 | +public class LogRes { | ||
| 18 | + | ||
| 19 | + @Schema(description = "日志ID") | ||
| 20 | + private Long logId; | ||
| 21 | + | ||
| 22 | + @Schema(description = "日志类型(0-操作日志/1-异常日志/2-登录日志)") | ||
| 23 | + private String logType; | ||
| 24 | + | ||
| 25 | + @Schema(description = "日志类型描述") | ||
| 26 | + private String logTypeText; | ||
| 27 | + | ||
| 28 | + @Schema(description = "用户ID") | ||
| 29 | + private Long userId; | ||
| 30 | + | ||
| 31 | + @Schema(description = "用户名") | ||
| 32 | + private String username; | ||
| 33 | + | ||
| 34 | + @Schema(description = "操作模块") | ||
| 35 | + private String module; | ||
| 36 | + | ||
| 37 | + @Schema(description = "操作内容") | ||
| 38 | + private String operation; | ||
| 39 | + | ||
| 40 | + @Schema(description = "操作IP") | ||
| 41 | + private String ip; | ||
| 42 | + | ||
| 43 | + @Schema(description = "日志时间") | ||
| 44 | + private LocalDateTime logTime; | ||
| 45 | + | ||
| 46 | + @Schema(description = "操作结果(0-失败/1-成功)") | ||
| 47 | + private Integer status; | ||
| 48 | + | ||
| 49 | + @Schema(description = "操作结果描述") | ||
| 50 | + private String statusText; | ||
| 51 | + | ||
| 52 | + @Schema(description = "错误信息") | ||
| 53 | + private String errorMsg; | ||
| 54 | + | ||
| 55 | + @Schema(description = "请求参数") | ||
| 56 | + private String requestParam; | ||
| 57 | + | ||
| 58 | + @Schema(description = "创建者") | ||
| 59 | + private String createBy; | ||
| 60 | + | ||
| 61 | + @Schema(description = "创建时间") | ||
| 62 | + private LocalDateTime createTime; | ||
| 63 | + | ||
| 64 | + @Schema(description = "更新者") | ||
| 65 | + private String updateBy; | ||
| 66 | + | ||
| 67 | + @Schema(description = "更新时间") | ||
| 68 | + private LocalDateTime updateTime; | ||
| 69 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 登录响应对象 | ||
| 8 | + * | ||
| 9 | + * @author Apple ERP Team | ||
| 10 | + * @version 1.0.0 | ||
| 11 | + * @since 2024-01-01 | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +@Schema(description = "用户登录响应") | ||
| 15 | +public class LoginRes { | ||
| 16 | + | ||
| 17 | + @Schema(description = "访问令牌", example = "eyJhbGciOiJIUzUxMiJ9...") | ||
| 18 | + private String token; | ||
| 19 | + | ||
| 20 | + @Schema(description = "刷新令牌", example = "eyJhbGciOiJIUzUxMiJ9...") | ||
| 21 | + private String refreshToken; | ||
| 22 | + | ||
| 23 | + @Schema(description = "用户名", example = "admin") | ||
| 24 | + private String username; | ||
| 25 | + | ||
| 26 | + public LoginRes() {} | ||
| 27 | + | ||
| 28 | + public LoginRes(String token, String refreshToken, String username) { | ||
| 29 | + this.token = token; | ||
| 30 | + this.refreshToken = refreshToken; | ||
| 31 | + this.username = username; | ||
| 32 | + } | ||
| 33 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.time.LocalDateTime; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 菜单响应DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +public class MenuRes { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 菜单ID | ||
| 20 | + */ | ||
| 21 | + private Long menuId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 父菜单ID | ||
| 25 | + */ | ||
| 26 | + private Long parentId; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 菜单名称 | ||
| 30 | + */ | ||
| 31 | + private String menuName; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 菜单类型(0-目录/1-菜单/2-按钮) | ||
| 35 | + */ | ||
| 36 | + private String menuType; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 菜单类型描述 | ||
| 40 | + */ | ||
| 41 | + private String menuTypeText; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 菜单路径 | ||
| 45 | + */ | ||
| 46 | + private String path; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 菜单图标 | ||
| 50 | + */ | ||
| 51 | + private String icon; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 排序号 | ||
| 55 | + */ | ||
| 56 | + private Integer sort; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 状态(0-隐藏/1-显示) | ||
| 60 | + */ | ||
| 61 | + private Integer status; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 状态描述 | ||
| 65 | + */ | ||
| 66 | + private String statusText; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 权限标识 | ||
| 70 | + */ | ||
| 71 | + private String perms; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 创建者 | ||
| 75 | + */ | ||
| 76 | + private String createBy; | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 创建时间 | ||
| 80 | + */ | ||
| 81 | + private LocalDateTime createTime; | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 更新者 | ||
| 85 | + */ | ||
| 86 | + private String updateBy; | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * 更新时间 | ||
| 90 | + */ | ||
| 91 | + private LocalDateTime updateTime; | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 子菜单列表 | ||
| 95 | + */ | ||
| 96 | + private List<MenuRes> children; | ||
| 97 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.time.LocalDateTime; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 角色响应DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +public class RoleRes { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 角色ID | ||
| 20 | + */ | ||
| 21 | + private Long roleId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 角色编码 | ||
| 25 | + */ | ||
| 26 | + private String roleCode; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 角色名称 | ||
| 30 | + */ | ||
| 31 | + private String roleName; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 状态(0-停用/1-启用) | ||
| 35 | + */ | ||
| 36 | + private Integer status; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 状态描述 | ||
| 40 | + */ | ||
| 41 | + private String statusText; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 备注 | ||
| 45 | + */ | ||
| 46 | + private String remark; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 创建者 | ||
| 50 | + */ | ||
| 51 | + private String createBy; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 创建时间 | ||
| 55 | + */ | ||
| 56 | + private LocalDateTime createTime; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 更新者 | ||
| 60 | + */ | ||
| 61 | + private String updateBy; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 更新时间 | ||
| 65 | + */ | ||
| 66 | + private LocalDateTime updateTime; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 菜单列表 | ||
| 70 | + */ | ||
| 71 | + private List<MenuRes> menus; | ||
| 72 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import org.springframework.security.core.GrantedAuthority; | ||
| 6 | + | ||
| 7 | +import java.util.Collection; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 用户信息响应对象 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@Schema(description = "用户信息响应") | ||
| 18 | +public class UserInfoRes { | ||
| 19 | + | ||
| 20 | + @Schema(description = "用户名", example = "admin") | ||
| 21 | + private String username; | ||
| 22 | + | ||
| 23 | + @Schema(description = "用户权限列表") | ||
| 24 | + private Collection<? extends GrantedAuthority> authorities; | ||
| 25 | + | ||
| 26 | + public UserInfoRes() {} | ||
| 27 | + | ||
| 28 | + public UserInfoRes(String username, Collection<? extends GrantedAuthority> authorities) { | ||
| 29 | + this.username = username; | ||
| 30 | + this.authorities = authorities; | ||
| 31 | + } | ||
| 32 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.time.LocalDateTime; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 用户响应DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +public class UserRes { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 用户ID | ||
| 20 | + */ | ||
| 21 | + private Long userId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 用户名 | ||
| 25 | + */ | ||
| 26 | + private String username; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 真实姓名 | ||
| 30 | + */ | ||
| 31 | + private String realName; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 手机号 | ||
| 35 | + */ | ||
| 36 | + private String phone; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 邮箱 | ||
| 40 | + */ | ||
| 41 | + private String email; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 状态(0-停用/1-启用) | ||
| 45 | + */ | ||
| 46 | + private Integer status; | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 状态描述 | ||
| 50 | + */ | ||
| 51 | + private String statusText; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 备注 | ||
| 55 | + */ | ||
| 56 | + private String remark; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 创建者 | ||
| 60 | + */ | ||
| 61 | + private String createBy; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 创建时间 | ||
| 65 | + */ | ||
| 66 | + private LocalDateTime createTime; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 更新者 | ||
| 70 | + */ | ||
| 71 | + private String updateBy; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 更新时间 | ||
| 75 | + */ | ||
| 76 | + private LocalDateTime updateTime; | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 最后登录时间 | ||
| 80 | + */ | ||
| 81 | + private LocalDateTime lastLoginTime; | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 最后登录IP | ||
| 85 | + */ | ||
| 86 | + private String lastLoginIp; | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * 角色列表 | ||
| 90 | + */ | ||
| 91 | + private List<RoleRes> roles; | ||
| 92 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 系统字典项实体类 | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@TableName("t_sys_dict_item") | ||
| 17 | +public class SysDictItem { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 字典项ID | ||
| 21 | + */ | ||
| 22 | + @TableId(value = "dict_item_id", type = IdType.AUTO) | ||
| 23 | + private Long dictItemId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典类型ID | ||
| 27 | + */ | ||
| 28 | + @TableField("dict_type_id") | ||
| 29 | + private Long dictTypeId; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 字典标签 | ||
| 33 | + */ | ||
| 34 | + @TableField("dict_label") | ||
| 35 | + private String dictLabel; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 字典值 | ||
| 39 | + */ | ||
| 40 | + @TableField("dict_value") | ||
| 41 | + private String dictValue; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 排序号 | ||
| 45 | + */ | ||
| 46 | + @TableField("sort") | ||
| 47 | + private Integer sort; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 备注 | ||
| 51 | + */ | ||
| 52 | + @TableField("remark") | ||
| 53 | + private String remark; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 创建者 | ||
| 57 | + */ | ||
| 58 | + @TableField("create_by") | ||
| 59 | + private String createBy; | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 创建时间 | ||
| 63 | + */ | ||
| 64 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 65 | + private LocalDateTime createTime; | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 更新者 | ||
| 69 | + */ | ||
| 70 | + @TableField("update_by") | ||
| 71 | + private String updateBy; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 更新时间 | ||
| 75 | + */ | ||
| 76 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 77 | + private LocalDateTime updateTime; | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 删除标志(0代表存在 2代表删除) | ||
| 81 | + */ | ||
| 82 | + @TableField("del_flag") | ||
| 83 | + @TableLogic | ||
| 84 | + private String delFlag; | ||
| 85 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 系统字典类型实体类 | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP Team | ||
| 12 | + * @version 1.0.0 | ||
| 13 | + * @since 2024-01-01 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@TableName("t_sys_dict_type") | ||
| 17 | +public class SysDictType { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 字典类型ID | ||
| 21 | + */ | ||
| 22 | + @TableId(value = "dict_type_id", type = IdType.AUTO) | ||
| 23 | + private Long dictTypeId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 字典类型 | ||
| 27 | + */ | ||
| 28 | + @TableField("dict_type") | ||
| 29 | + private String dictType; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 字典名称 | ||
| 33 | + */ | ||
| 34 | + @TableField("dict_name") | ||
| 35 | + private String dictName; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 状态(0-停用/1-启用) | ||
| 39 | + */ | ||
| 40 | + @TableField("status") | ||
| 41 | + private Integer status; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 备注 | ||
| 45 | + */ | ||
| 46 | + @TableField("remark") | ||
| 47 | + private String remark; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 创建者 | ||
| 51 | + */ | ||
| 52 | + @TableField("create_by") | ||
| 53 | + private String createBy; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 创建时间 | ||
| 57 | + */ | ||
| 58 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 59 | + private LocalDateTime createTime; | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 更新者 | ||
| 63 | + */ | ||
| 64 | + @TableField("update_by") | ||
| 65 | + private String updateBy; | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 更新时间 | ||
| 69 | + */ | ||
| 70 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 71 | + private LocalDateTime updateTime; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 删除标志(0代表存在 2代表删除) | ||
| 75 | + */ | ||
| 76 | + @TableField("del_flag") | ||
| 77 | + @TableLogic | ||
| 78 | + private String delFlag; | ||
| 79 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 系统日志实体类 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = false) | ||
| 18 | +@TableName("t_sys_log") | ||
| 19 | +public class SysLog { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 日志ID | ||
| 23 | + */ | ||
| 24 | + @TableId(value = "log_id", type = IdType.AUTO) | ||
| 25 | + private Long logId; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 日志类型(0-操作日志/1-异常日志/2-登录日志) | ||
| 29 | + */ | ||
| 30 | + @TableField("log_type") | ||
| 31 | + private String logType; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 用户ID | ||
| 35 | + */ | ||
| 36 | + @TableField("user_id") | ||
| 37 | + private Long userId; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 用户名 | ||
| 41 | + */ | ||
| 42 | + @TableField("username") | ||
| 43 | + private String username; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 操作模块 | ||
| 47 | + */ | ||
| 48 | + @TableField("module") | ||
| 49 | + private String module; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 操作内容 | ||
| 53 | + */ | ||
| 54 | + @TableField("operation") | ||
| 55 | + private String operation; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 操作IP | ||
| 59 | + */ | ||
| 60 | + @TableField("ip") | ||
| 61 | + private String ip; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 日志时间 | ||
| 65 | + */ | ||
| 66 | + @TableField("log_time") | ||
| 67 | + private LocalDateTime logTime; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 操作结果(0-失败/1-成功) | ||
| 71 | + */ | ||
| 72 | + @TableField("status") | ||
| 73 | + private Integer status; | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * 错误信息 | ||
| 77 | + */ | ||
| 78 | + @TableField("error_msg") | ||
| 79 | + private String errorMsg; | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 请求参数 | ||
| 83 | + */ | ||
| 84 | + @TableField("request_param") | ||
| 85 | + private String requestParam; | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * 创建者 | ||
| 89 | + */ | ||
| 90 | + @TableField("create_by") | ||
| 91 | + private String createBy; | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 创建时间 | ||
| 95 | + */ | ||
| 96 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 97 | + private LocalDateTime createTime; | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * 更新者 | ||
| 101 | + */ | ||
| 102 | + @TableField("update_by") | ||
| 103 | + private String updateBy; | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 更新时间 | ||
| 107 | + */ | ||
| 108 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 109 | + private LocalDateTime updateTime; | ||
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * 删除标志(0代表存在 2代表删除) | ||
| 113 | + */ | ||
| 114 | + @TableField("del_flag") | ||
| 115 | + @TableLogic | ||
| 116 | + private String delFlag; | ||
| 117 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | +import java.util.ArrayList; | ||
| 9 | +import java.util.List; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 系统菜单实体类 | ||
| 13 | + * | ||
| 14 | + * @author Apple ERP Team | ||
| 15 | + * @version 1.0.0 | ||
| 16 | + * @since 2024-01-01 | ||
| 17 | + */ | ||
| 18 | +@Data | ||
| 19 | +@EqualsAndHashCode(callSuper = false) | ||
| 20 | +@TableName("t_sys_menu") | ||
| 21 | +public class SysMenu { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 菜单ID | ||
| 25 | + */ | ||
| 26 | + @TableId(value = "menu_id", type = IdType.AUTO) | ||
| 27 | + private Long menuId; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 菜单名称 | ||
| 31 | + */ | ||
| 32 | + @TableField("menu_name") | ||
| 33 | + private String menuName; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 父菜单ID | ||
| 37 | + */ | ||
| 38 | + @TableField("parent_id") | ||
| 39 | + private Long parentId; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 排序号 | ||
| 43 | + */ | ||
| 44 | + @TableField("sort") | ||
| 45 | + private Integer sort; | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 路由地址 | ||
| 49 | + */ | ||
| 50 | + @TableField("path") | ||
| 51 | + private String path; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 菜单类型(0-目录/1-菜单/2-按钮) | ||
| 55 | + */ | ||
| 56 | + @TableField("menu_type") | ||
| 57 | + private String menuType; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 菜单状态(0-隐藏/1-显示) | ||
| 61 | + */ | ||
| 62 | + @TableField("status") | ||
| 63 | + private String status; | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 权限标识 | ||
| 67 | + */ | ||
| 68 | + @TableField("perms") | ||
| 69 | + private String perms; | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 菜单图标 | ||
| 73 | + */ | ||
| 74 | + @TableField("icon") | ||
| 75 | + private String icon; | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * 创建者 | ||
| 79 | + */ | ||
| 80 | + @TableField("create_by") | ||
| 81 | + private String createBy; | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 创建时间 | ||
| 85 | + */ | ||
| 86 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 87 | + private LocalDateTime createTime; | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * 更新者 | ||
| 91 | + */ | ||
| 92 | + @TableField("update_by") | ||
| 93 | + private String updateBy; | ||
| 94 | + | ||
| 95 | + /** | ||
| 96 | + * 更新时间 | ||
| 97 | + */ | ||
| 98 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 99 | + private LocalDateTime updateTime; | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + * 删除标志(0代表存在 2代表删除) | ||
| 103 | + */ | ||
| 104 | + @TableField("del_flag") | ||
| 105 | + @TableLogic | ||
| 106 | + private String delFlag; | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 子菜单列表 | ||
| 110 | + * 用于构建菜单树结构,不映射到数据库字段 | ||
| 111 | + */ | ||
| 112 | + @TableField(exist = false) | ||
| 113 | + private List<SysMenu> children = new ArrayList<>(); | ||
| 114 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 系统角色实体类 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = false) | ||
| 18 | +@TableName("t_sys_role") | ||
| 19 | +public class SysRole { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 角色ID | ||
| 23 | + */ | ||
| 24 | + @TableId(value = "role_id", type = IdType.AUTO) | ||
| 25 | + private Long roleId; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 角色编码 | ||
| 29 | + */ | ||
| 30 | + @TableField("role_code") | ||
| 31 | + private String roleCode; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 角色名称 | ||
| 35 | + */ | ||
| 36 | + @TableField("role_name") | ||
| 37 | + private String roleName; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 状态(0-停用/1-启用) | ||
| 41 | + */ | ||
| 42 | + @TableField("status") | ||
| 43 | + private Integer status; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 备注 | ||
| 47 | + */ | ||
| 48 | + @TableField("remark") | ||
| 49 | + private String remark; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 创建者 | ||
| 53 | + */ | ||
| 54 | + @TableField("create_by") | ||
| 55 | + private String createBy; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 创建时间 | ||
| 59 | + */ | ||
| 60 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 61 | + private LocalDateTime createTime; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 更新者 | ||
| 65 | + */ | ||
| 66 | + @TableField("update_by") | ||
| 67 | + private String updateBy; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 更新时间 | ||
| 71 | + */ | ||
| 72 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 73 | + private LocalDateTime updateTime; | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * 删除标志(0代表存在 2代表删除) | ||
| 77 | + */ | ||
| 78 | + @TableField("del_flag") | ||
| 79 | + @TableLogic | ||
| 80 | + private String delFlag; | ||
| 81 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 角色菜单关联实体类 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = false) | ||
| 18 | +@TableName("t_sys_role_menu") | ||
| 19 | +public class SysRoleMenu { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 关联ID | ||
| 23 | + */ | ||
| 24 | + @TableId(value = "id", type = IdType.AUTO) | ||
| 25 | + private Long id; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 角色ID | ||
| 29 | + */ | ||
| 30 | + @TableField("role_id") | ||
| 31 | + private Long roleId; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 菜单ID | ||
| 35 | + */ | ||
| 36 | + @TableField("menu_id") | ||
| 37 | + private Long menuId; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 创建者 | ||
| 41 | + */ | ||
| 42 | + @TableField("create_by") | ||
| 43 | + private String createBy; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 创建时间 | ||
| 47 | + */ | ||
| 48 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 49 | + private LocalDateTime createTime; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 更新者 | ||
| 53 | + */ | ||
| 54 | + @TableField("update_by") | ||
| 55 | + private String updateBy; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 更新时间 | ||
| 59 | + */ | ||
| 60 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 61 | + private LocalDateTime updateTime; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 删除标志(0代表存在 2代表删除) | ||
| 65 | + */ | ||
| 66 | + @TableField("del_flag") | ||
| 67 | + @TableLogic | ||
| 68 | + private String delFlag; | ||
| 69 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 系统用户实体类 | ||
| 12 | + * | ||
| 13 | + * @author Apple ERP Team | ||
| 14 | + * @version 1.0.0 | ||
| 15 | + * @since 2024-01-01 | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +@EqualsAndHashCode(callSuper = false) | ||
| 19 | +@TableName("t_sys_user") | ||
| 20 | +public class SysUser { | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 用户ID | ||
| 24 | + */ | ||
| 25 | + @TableId(value = "user_id", type = IdType.AUTO) | ||
| 26 | + private Long userId; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 用户名 | ||
| 30 | + */ | ||
| 31 | + @TableField("username") | ||
| 32 | + private String username; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 真实姓名 | ||
| 36 | + */ | ||
| 37 | + @TableField("real_name") | ||
| 38 | + private String realName; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 密码 | ||
| 42 | + */ | ||
| 43 | + @TableField("password") | ||
| 44 | + private String password; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 手机号 | ||
| 48 | + */ | ||
| 49 | + @TableField("phone") | ||
| 50 | + private String phone; | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 邮箱 | ||
| 54 | + */ | ||
| 55 | + @TableField("email") | ||
| 56 | + private String email; | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 状态(0-停用/1-启用) | ||
| 60 | + */ | ||
| 61 | + @TableField("status") | ||
| 62 | + private Integer status; | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 最后登录时间 | ||
| 66 | + */ | ||
| 67 | + @TableField("last_login_time") | ||
| 68 | + private LocalDateTime lastLoginTime; | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * 最后登录IP | ||
| 72 | + */ | ||
| 73 | + @TableField("last_login_ip") | ||
| 74 | + private String lastLoginIp; | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * 备注 | ||
| 78 | + */ | ||
| 79 | + @TableField("remark") | ||
| 80 | + private String remark; | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * 创建者 | ||
| 84 | + */ | ||
| 85 | + @TableField("create_by") | ||
| 86 | + private String createBy; | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * 创建时间 | ||
| 90 | + */ | ||
| 91 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 92 | + private LocalDateTime createTime; | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 更新者 | ||
| 96 | + */ | ||
| 97 | + @TableField("update_by") | ||
| 98 | + private String updateBy; | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * 更新时间 | ||
| 102 | + */ | ||
| 103 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 104 | + private LocalDateTime updateTime; | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * 删除标志(0代表存在 2代表删除) | ||
| 108 | + */ | ||
| 109 | + @TableField("del_flag") | ||
| 110 | + @TableLogic | ||
| 111 | + private String delFlag; | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * 用户角色列表(非数据库字段) | ||
| 115 | + */ | ||
| 116 | + @TableField(exist = false) | ||
| 117 | + private List<SysRole> roles; | ||
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * 用户权限列表(非数据库字段) | ||
| 121 | + */ | ||
| 122 | + @TableField(exist = false) | ||
| 123 | + private List<String> permissions; | ||
| 124 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 用户角色关联实体类 | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP Team | ||
| 13 | + * @version 1.0.0 | ||
| 14 | + * @since 2024-01-01 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = false) | ||
| 18 | +@TableName("t_sys_user_role") | ||
| 19 | +public class SysUserRole { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 关联ID | ||
| 23 | + */ | ||
| 24 | + @TableId(value = "id", type = IdType.AUTO) | ||
| 25 | + private Long id; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 用户ID | ||
| 29 | + */ | ||
| 30 | + @TableField("user_id") | ||
| 31 | + private Long userId; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 角色ID | ||
| 35 | + */ | ||
| 36 | + @TableField("role_id") | ||
| 37 | + private Long roleId; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 创建者 | ||
| 41 | + */ | ||
| 42 | + @TableField("create_by") | ||
| 43 | + private String createBy; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 创建时间 | ||
| 47 | + */ | ||
| 48 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 49 | + private LocalDateTime createTime; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 更新者 | ||
| 53 | + */ | ||
| 54 | + @TableField("update_by") | ||
| 55 | + private String updateBy; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 更新时间 | ||
| 59 | + */ | ||
| 60 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 61 | + private LocalDateTime updateTime; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 删除标志(0代表存在 2代表删除) | ||
| 65 | + */ | ||
| 66 | + @TableField("del_flag") | ||
| 67 | + @TableLogic | ||
| 68 | + private String delFlag; | ||
| 69 | +} |
| 1 | +package com.apple.erp.exception; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 业务异常类 | ||
| 5 | + * | ||
| 6 | + * @author Apple ERP Team | ||
| 7 | + * @version 1.0.0 | ||
| 8 | + * @since 2024-01-01 | ||
| 9 | + */ | ||
| 10 | +public class BusinessException extends RuntimeException { | ||
| 11 | + | ||
| 12 | + private Integer code; | ||
| 13 | + | ||
| 14 | + public BusinessException() { | ||
| 15 | + super(); | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + public BusinessException(String message) { | ||
| 19 | + super(message); | ||
| 20 | + this.code = 500; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public BusinessException(Integer code, String message) { | ||
| 24 | + super(message); | ||
| 25 | + this.code = code; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + public BusinessException(String message, Throwable cause) { | ||
| 29 | + super(message, cause); | ||
| 30 | + this.code = 500; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public BusinessException(Integer code, String message, Throwable cause) { | ||
| 34 | + super(message, cause); | ||
| 35 | + this.code = code; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public Integer getCode() { | ||
| 39 | + return code; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public void setCode(Integer code) { | ||
| 43 | + this.code = code; | ||
| 44 | + } | ||
| 45 | +} |
| 1 | +package com.apple.erp.exception; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.vo.ResultVO; | ||
| 4 | +import org.slf4j.Logger; | ||
| 5 | +import org.slf4j.LoggerFactory; | ||
| 6 | +import org.springframework.http.HttpStatus; | ||
| 7 | +import org.springframework.security.access.AccessDeniedException; | ||
| 8 | +import org.springframework.security.authentication.BadCredentialsException; | ||
| 9 | +import org.springframework.security.core.AuthenticationException; | ||
| 10 | +import org.springframework.validation.BindException; | ||
| 11 | +import org.springframework.validation.FieldError; | ||
| 12 | +import org.springframework.web.bind.MethodArgumentNotValidException; | ||
| 13 | +import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| 14 | +import org.springframework.web.bind.annotation.ResponseStatus; | ||
| 15 | +import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
| 16 | + | ||
| 17 | +import javax.servlet.http.HttpServletRequest; | ||
| 18 | +import java.util.HashMap; | ||
| 19 | +import java.util.Map; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 全局异常处理器 | ||
| 23 | + * | ||
| 24 | + * @author Apple ERP Team | ||
| 25 | + * @version 1.0.0 | ||
| 26 | + * @since 2024-01-01 | ||
| 27 | + */ | ||
| 28 | +@RestControllerAdvice | ||
| 29 | +public class GlobalExceptionHandler { | ||
| 30 | + | ||
| 31 | + private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 业务异常处理 | ||
| 35 | + */ | ||
| 36 | + @ExceptionHandler(BusinessException.class) | ||
| 37 | + @ResponseStatus(HttpStatus.BAD_REQUEST) | ||
| 38 | + public ResultVO<Object> handleBusinessException(BusinessException e, HttpServletRequest request) { | ||
| 39 | + logger.warn("业务异常: {} - {}", request.getRequestURI(), e.getMessage()); | ||
| 40 | + return ResultVO.error(e.getCode(), e.getMessage()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 参数验证异常处理 | ||
| 45 | + */ | ||
| 46 | + @ExceptionHandler(MethodArgumentNotValidException.class) | ||
| 47 | + @ResponseStatus(HttpStatus.BAD_REQUEST) | ||
| 48 | + public ResultVO<Object> handleValidationException(MethodArgumentNotValidException e) { | ||
| 49 | + Map<String, String> errors = new HashMap<>(); | ||
| 50 | + e.getBindingResult().getAllErrors().forEach(error -> { | ||
| 51 | + String fieldName = ((FieldError) error).getField(); | ||
| 52 | + String errorMessage = error.getDefaultMessage(); | ||
| 53 | + errors.put(fieldName, errorMessage); | ||
| 54 | + }); | ||
| 55 | + return ResultVO.error(400, "参数验证失败", errors); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 绑定异常处理 | ||
| 60 | + */ | ||
| 61 | + @ExceptionHandler(BindException.class) | ||
| 62 | + @ResponseStatus(HttpStatus.BAD_REQUEST) | ||
| 63 | + public ResultVO<Object> handleBindException(BindException e) { | ||
| 64 | + Map<String, String> errors = new HashMap<>(); | ||
| 65 | + e.getBindingResult().getAllErrors().forEach(error -> { | ||
| 66 | + String fieldName = ((FieldError) error).getField(); | ||
| 67 | + String errorMessage = error.getDefaultMessage(); | ||
| 68 | + errors.put(fieldName, errorMessage); | ||
| 69 | + }); | ||
| 70 | + return ResultVO.error(400, "参数绑定失败", errors); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 认证异常处理 | ||
| 75 | + */ | ||
| 76 | + @ExceptionHandler(AuthenticationException.class) | ||
| 77 | + @ResponseStatus(HttpStatus.UNAUTHORIZED) | ||
| 78 | + public ResultVO<Object> handleAuthenticationException(AuthenticationException e, HttpServletRequest request) { | ||
| 79 | + logger.warn("认证异常: {} - {}", request.getRequestURI(), e.getMessage()); | ||
| 80 | + return ResultVO.error(401, "认证失败"); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 授权异常处理 | ||
| 85 | + */ | ||
| 86 | + @ExceptionHandler(AccessDeniedException.class) | ||
| 87 | + @ResponseStatus(HttpStatus.FORBIDDEN) | ||
| 88 | + public ResultVO<Object> handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) { | ||
| 89 | + logger.warn("授权异常: {} - {}", request.getRequestURI(), e.getMessage()); | ||
| 90 | + return ResultVO.error(403, "权限不足"); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 密码错误异常处理 | ||
| 95 | + */ | ||
| 96 | + @ExceptionHandler(BadCredentialsException.class) | ||
| 97 | + @ResponseStatus(HttpStatus.UNAUTHORIZED) | ||
| 98 | + public ResultVO<Object> handleBadCredentialsException(BadCredentialsException e, HttpServletRequest request) { | ||
| 99 | + logger.warn("密码错误: {} - {}", request.getRequestURI(), e.getMessage()); | ||
| 100 | + return ResultVO.error(401, "用户名或密码错误"); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + /** | ||
| 104 | + * 非法参数异常处理 | ||
| 105 | + */ | ||
| 106 | + @ExceptionHandler(IllegalArgumentException.class) | ||
| 107 | + @ResponseStatus(HttpStatus.BAD_REQUEST) | ||
| 108 | + public ResultVO<Object> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) { | ||
| 109 | + logger.warn("非法参数: {} - {}", request.getRequestURI(), e.getMessage()); | ||
| 110 | + return ResultVO.error(400, "参数错误: " + e.getMessage()); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * 空指针异常处理 | ||
| 115 | + */ | ||
| 116 | + @ExceptionHandler(NullPointerException.class) | ||
| 117 | + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
| 118 | + public ResultVO<Object> handleNullPointerException(NullPointerException e, HttpServletRequest request) { | ||
| 119 | + logger.error("空指针异常: {} - {}", request.getRequestURI(), e.getMessage(), e); | ||
| 120 | + return ResultVO.error(500, "系统内部错误"); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 通用异常处理 | ||
| 125 | + */ | ||
| 126 | + @ExceptionHandler(Exception.class) | ||
| 127 | + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
| 128 | + public ResultVO<Object> handleException(Exception e, HttpServletRequest request) { | ||
| 129 | + logger.error("系统异常: {} - {}", request.getRequestURI(), e.getMessage(), e); | ||
| 130 | + return ResultVO.error(500, "系统内部错误"); | ||
| 131 | + } | ||
| 132 | +} |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
backend/src/main/resources/application.yml
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
backend/src/test/resources/schema.sql
0 → 100644
This diff is collapsed. Click to expand it.
database/scripts/01_create_system_tables.sql
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
database/scripts/03_insert_system_data.sql
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
database/scripts/05_test_database_auth.sql
0 → 100644
This diff is collapsed. Click to expand it.
database/scripts/README.md
0 → 100644
This diff is collapsed. Click to expand it.
database/scripts/README_log_permissions.md
0 → 100644
This diff is collapsed. Click to expand it.
database/scripts/初始化数据脚本.sql
0 → 100644
This diff is collapsed. Click to expand it.
docs/design/技术架构设计.md
0 → 100644
This diff is collapsed. Click to expand it.
docs/design/数据库设计文档.md
0 → 100644
This diff is collapsed. Click to expand it.
docs/development/Apple经销商ERP前端开发计划.md
0 → 100644
This diff is collapsed. Click to expand it.
docs/development/Apple经销商ERP系统对接项目-两周开发计划.md
0 → 100644
This diff is collapsed. Click to expand it.
docs/requirements/Apple 经销商 ERP 系统对接项目.docx
0 → 100644
No preview for this file type
docs/requirements/Apple 经销商 ERP 系统对接项目.md
0 → 100644
This diff is collapsed. Click to expand it.
No preview for this file type
frontend/.eslintrc.cjs
0 → 100644
This diff is collapsed. Click to expand it.
frontend/.prettierrc
0 → 100644
This diff is collapsed. Click to expand it.
frontend/PROJECT_SUMMARY.md
0 → 100644
This diff is collapsed. Click to expand it.
frontend/README.md
0 → 100644
This diff is collapsed. Click to expand it.
frontend/auto-imports.d.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/components.d.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/env.d.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/env.development
0 → 100644
This diff is collapsed. Click to expand it.
frontend/env.production
0 → 100644
This diff is collapsed. Click to expand it.
frontend/package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
frontend/package.json
0 → 100644
This diff is collapsed. Click to expand it.
frontend/public/404.svg
0 → 100644
This diff is collapsed. Click to expand it.
frontend/public/index.html
0 → 100644
This diff is collapsed. Click to expand it.
frontend/public/logo.png
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/App.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/auth.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/dict.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/log.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/menu.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/role.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/api/user.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/assets/styles/common.scss
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
frontend/src/assets/styles/index.scss
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/assets/styles/mixins.scss
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/assets/styles/variables.scss
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/components/layout/Header.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/components/layout/Sidebar.vue
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
frontend/src/layouts/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/main.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/router/index.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/stores/modules/app.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/stores/modules/permission.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/stores/modules/user.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/types/index.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/utils/auth.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/utils/index.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/utils/request.ts
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/Test.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/dashboard/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/error/404.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/login/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/sys/dict/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/sys/menu/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/sys/role/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/src/views/sys/user/index.vue
0 → 100644
This diff is collapsed. Click to expand it.
frontend/tsconfig.json
0 → 100644
This diff is collapsed. Click to expand it.
frontend/tsconfig.tsbuildinfo
0 → 100644
This diff is collapsed. Click to expand it.
frontend/vite.config.ts
0 → 100644
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment