Compare commits
5 Commits
98e6d6a557
...
7c35cd11ac
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c35cd11ac | |||
| d1be97a366 | |||
| 1eafeacf36 | |||
| 6c0ed89397 | |||
| d03897c525 |
8
.claude/settings.local.json
Normal file
8
.claude/settings.local.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
66
CLAUDE.md
Normal file
66
CLAUDE.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Build & Run Commands
|
||||
|
||||
```bash
|
||||
# Build the project
|
||||
./mvnw clean package
|
||||
|
||||
# Run the application
|
||||
./mvnw spring-boot:run
|
||||
|
||||
# Run all tests
|
||||
./mvnw test
|
||||
|
||||
# Run a single test class
|
||||
./mvnw test -Dtest=TestClassName
|
||||
|
||||
# Run a single test method
|
||||
./mvnw test -Dtest=TestClassName#methodName
|
||||
```
|
||||
|
||||
## Project Overview
|
||||
|
||||
- **Framework**: Spring Boot 3.2.3 with Java 21
|
||||
- **ORM**: MyBatis Plus 3.5.5
|
||||
- **Database**: MySQL 8.0.33
|
||||
- **Cache**: Redis (Spring Data Redis)
|
||||
- **API Docs**: Swagger UI at `/swagger-ui.html`
|
||||
- **Auth**: JWT (jjwt 0.11.5)
|
||||
|
||||
## Architecture
|
||||
|
||||
Base package: `icu.sunway.ai_spring_example`
|
||||
|
||||
This project follows a layered architecture with MyBatis Plus:
|
||||
|
||||
| Layer | Location | Pattern |
|
||||
|-------|----------|---------|
|
||||
| Controller | `Controller/{EntityName}Controller/` | One subdirectory per entity |
|
||||
| Service | `Service/` | Interface `I{Name}Service` extends `IService<Entity>` |
|
||||
| Service Impl | `Service/Implements/` | `{Name}ServiceImpl` extends `ServiceImpl<Mapper, Entity>` |
|
||||
| Mapper | `Mapper/` | `{Name}Mapper` extends `BaseMapper<Entity>` |
|
||||
| Entity | `Entity/` | Lombok + MyBatis Plus annotations |
|
||||
| Config | `Config/` | CORS, Security, Redis, OpenAPI |
|
||||
| Utils | `Utils/` | Utility classes |
|
||||
|
||||
## Key Utilities
|
||||
|
||||
- **ResponseUtils**: Standard API responses - `ResponseUtils.success(data)` / `ResponseUtils.fail(message)`
|
||||
- **PageUtils**: Pagination - `PageUtils.createPage(page, limit)` returns `Page<T>` for MyBatis Plus
|
||||
- **RedisUtils**: Redis operations (inject with `@Resource`)
|
||||
- **JsonUtils**: JSON serialization - `JsonUtils.toJson()` / `JsonUtils.toObject()`
|
||||
- **ValidationUtils**: Input validation (email, phone, URL, IP, password strength, etc.)
|
||||
|
||||
## Configuration
|
||||
|
||||
- Copy `application-example.yaml` to `application.yaml` and fill in database/Redis credentials
|
||||
- `application.yaml` is gitignored (contains sensitive data)
|
||||
- Uses dynamic multi-datasource (primary datasource named "master")
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Current `SecurityConfig` is permissive (all requests allowed) - configure for production
|
||||
- Stateless session management configured for JWT authentication
|
||||
48
pom.xml
48
pom.xml
@@ -33,7 +33,7 @@
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
<mybatis-plus.version>3.5.5</mybatis-plus.version>
|
||||
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- 核心依赖 -->
|
||||
@@ -43,14 +43,14 @@
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库相关 -->
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
@@ -70,26 +70,26 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<!-- 添加验证依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
</dependency>
|
||||
<!-- 添加验证依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!-- API文档 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package icu.sunway.ai_spring_example.Controller.AuthController;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Controller.AuthController.vo.LoginByEmailPasswordReq;
|
||||
import icu.sunway.ai_spring_example.Controller.AuthController.vo.RegisterByEmailReq;
|
||||
import icu.sunway.ai_spring_example.Exception.BusinessException;
|
||||
import icu.sunway.ai_spring_example.Service.AuthService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
public class AuthController {
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@PostMapping("/login")
|
||||
public Object loginByEmailPassword(@RequestBody LoginByEmailPasswordReq req) {
|
||||
try {
|
||||
String token = authService.loginByEmailPassword(req.getEmail(), req.getPassword());
|
||||
return ResponseUtils.success(token);
|
||||
} catch (BusinessException e) {
|
||||
return ResponseUtils.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public Object registerByEmail(@RequestBody RegisterByEmailReq req) {
|
||||
try {
|
||||
authService.registerByEmail(req.getEmail(), req.getPassword(), req.getVerifyCode());
|
||||
return ResponseUtils.success();
|
||||
} catch (BusinessException e) {
|
||||
return ResponseUtils.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package icu.sunway.ai_spring_example.Controller.AuthController.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginByEmailPasswordReq {
|
||||
private String email;
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package icu.sunway.ai_spring_example.Controller.AuthController.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RegisterByEmailReq {
|
||||
private String email;
|
||||
private String password;
|
||||
private String verifyCode;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package icu.sunway.ai_spring_example.Controller.ExampleEntityController;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import icu.sunway.ai_spring_example.Service.IExampleEntityService;
|
||||
|
||||
@Controller
|
||||
@RestController
|
||||
@RequestMapping("/example")
|
||||
public class EaxmpleEntityController {
|
||||
@Resource
|
||||
private IExampleEntityService exampleEntityService;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package icu.sunway.ai_spring_example.Controller.PermissionController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.Permission;
|
||||
import icu.sunway.ai_spring_example.Service.IPermissionService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/permission")
|
||||
public class PermissionController {
|
||||
|
||||
@Resource
|
||||
private IPermissionService permissionService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getPermissionById(@PathVariable Long id) {
|
||||
Permission permission = permissionService.getById(id);
|
||||
return ResponseUtils.success(permission);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object getAllPermissions(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<Permission> permissionPage = new Page<>(page, limit);
|
||||
Page<Permission> resultPage = permissionService.page(permissionPage);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Object createPermission(@RequestBody Permission permission) {
|
||||
boolean result = permissionService.save(permission);
|
||||
if (result) {
|
||||
return ResponseUtils.success(permission);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to create permission");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Object updatePermission(@PathVariable Long id, @RequestBody Permission permission) {
|
||||
permission.setId(id);
|
||||
boolean result = permissionService.updateById(permission);
|
||||
if (result) {
|
||||
return ResponseUtils.success(permission);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to update permission");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Object deletePermission(@PathVariable Long id) {
|
||||
boolean result = permissionService.removeById(id);
|
||||
if (result) {
|
||||
return ResponseUtils.success("Permission deleted successfully");
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to delete permission");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/name/{name}")
|
||||
public Object getPermissionByName(@PathVariable String name) {
|
||||
QueryWrapper<Permission> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("name", name);
|
||||
Permission permission = permissionService.getOne(wrapper);
|
||||
return ResponseUtils.success(permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package icu.sunway.ai_spring_example.Controller.RoleController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.Role;
|
||||
import icu.sunway.ai_spring_example.Service.IRoleService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/role")
|
||||
public class RoleController {
|
||||
|
||||
@Resource
|
||||
private IRoleService roleService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getRoleById(@PathVariable Long id) {
|
||||
Role role = roleService.getById(id);
|
||||
return ResponseUtils.success(role);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object getAllRoles(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<Role> rolePage = new Page<>(page, limit);
|
||||
Page<Role> resultPage = roleService.page(rolePage);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Object createRole(@RequestBody Role role) {
|
||||
boolean result = roleService.save(role);
|
||||
if (result) {
|
||||
return ResponseUtils.success(role);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to create role");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Object updateRole(@PathVariable Long id, @RequestBody Role role) {
|
||||
role.setId(id);
|
||||
boolean result = roleService.updateById(role);
|
||||
if (result) {
|
||||
return ResponseUtils.success(role);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to update role");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Object deleteRole(@PathVariable Long id) {
|
||||
boolean result = roleService.removeById(id);
|
||||
if (result) {
|
||||
return ResponseUtils.success("Role deleted successfully");
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to delete role");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/name/{name}")
|
||||
public Object getRoleByName(@PathVariable String name) {
|
||||
QueryWrapper<Role> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("name", name);
|
||||
Role role = roleService.getOne(wrapper);
|
||||
return ResponseUtils.success(role);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package icu.sunway.ai_spring_example.Controller.RolePermissionController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.RolePermission;
|
||||
import icu.sunway.ai_spring_example.Service.IRolePermissionService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/role-permission")
|
||||
public class RolePermissionController {
|
||||
|
||||
@Resource
|
||||
private IRolePermissionService rolePermissionService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getRolePermissionById(@PathVariable Long id) {
|
||||
RolePermission rolePermission = rolePermissionService.getById(id);
|
||||
return ResponseUtils.success(rolePermission);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object getAllRolePermissions(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<RolePermission> rolePermissionPage = new Page<>(page, limit);
|
||||
Page<RolePermission> resultPage = rolePermissionService.page(rolePermissionPage);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Object createRolePermission(@RequestBody RolePermission rolePermission) {
|
||||
boolean result = rolePermissionService.save(rolePermission);
|
||||
if (result) {
|
||||
return ResponseUtils.success(rolePermission);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to create role permission relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Object updateRolePermission(@PathVariable Long id, @RequestBody RolePermission rolePermission) {
|
||||
rolePermission.setId(id);
|
||||
boolean result = rolePermissionService.updateById(rolePermission);
|
||||
if (result) {
|
||||
return ResponseUtils.success(rolePermission);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to update role permission relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Object deleteRolePermission(@PathVariable Long id) {
|
||||
boolean result = rolePermissionService.removeById(id);
|
||||
if (result) {
|
||||
return ResponseUtils.success("Role permission relationship deleted successfully");
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to delete role permission relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/role/{roleId}")
|
||||
public Object getRolePermissionsByRoleId(@PathVariable Long roleId) {
|
||||
QueryWrapper<RolePermission> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("role_id", roleId);
|
||||
List<RolePermission> rolePermissions = rolePermissionService.list(wrapper);
|
||||
return ResponseUtils.success(rolePermissions);
|
||||
}
|
||||
|
||||
@GetMapping("/permission/{permissionId}")
|
||||
public Object getRolePermissionsByPermissionId(@PathVariable Long permissionId) {
|
||||
QueryWrapper<RolePermission> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("permission_id", permissionId);
|
||||
List<RolePermission> rolePermissions = rolePermissionService.list(wrapper);
|
||||
return ResponseUtils.success(rolePermissions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package icu.sunway.ai_spring_example.Controller.UserController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.User;
|
||||
import icu.sunway.ai_spring_example.Service.IUserService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/user")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private IUserService userService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getUserById(@PathVariable Long id) {
|
||||
User user = userService.getById(id);
|
||||
return ResponseUtils.success(user);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object getAllUsers(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<User> userPage = new Page<>(page, limit);
|
||||
Page<User> resultPage = userService.page(userPage);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Object createUser(@RequestBody User user) {
|
||||
boolean result = userService.save(user);
|
||||
if (result) {
|
||||
return ResponseUtils.success(user);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to create user");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Object updateUser(@PathVariable Long id, @RequestBody User user) {
|
||||
user.setId(id);
|
||||
boolean result = userService.updateById(user);
|
||||
if (result) {
|
||||
return ResponseUtils.success(user);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to update user");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Object deleteUser(@PathVariable Long id) {
|
||||
boolean result = userService.removeById(id);
|
||||
if (result) {
|
||||
return ResponseUtils.success("User deleted successfully");
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to delete user");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/username/{username}")
|
||||
public Object getUserByUsername(@PathVariable String username) {
|
||||
QueryWrapper<User> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("username", username);
|
||||
User user = userService.getOne(wrapper);
|
||||
return ResponseUtils.success(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package icu.sunway.ai_spring_example.Controller.UserRoleController;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.UserRole;
|
||||
import icu.sunway.ai_spring_example.Service.IUserRoleService;
|
||||
import icu.sunway.ai_spring_example.Utils.ResponseUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/user-role")
|
||||
public class UserRoleController {
|
||||
|
||||
@Resource
|
||||
private IUserRoleService userRoleService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getUserRoleById(@PathVariable Long id) {
|
||||
UserRole userRole = userRoleService.getById(id);
|
||||
return ResponseUtils.success(userRole);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object getAllUserRoles(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<UserRole> userRolePage = new Page<>(page, limit);
|
||||
Page<UserRole> resultPage = userRoleService.page(userRolePage);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public Object createUserRole(@RequestBody UserRole userRole) {
|
||||
boolean result = userRoleService.save(userRole);
|
||||
if (result) {
|
||||
return ResponseUtils.success(userRole);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to create user role relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public Object updateUserRole(@PathVariable Long id, @RequestBody UserRole userRole) {
|
||||
userRole.setId(id);
|
||||
boolean result = userRoleService.updateById(userRole);
|
||||
if (result) {
|
||||
return ResponseUtils.success(userRole);
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to update user role relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public Object deleteUserRole(@PathVariable Long id) {
|
||||
boolean result = userRoleService.removeById(id);
|
||||
if (result) {
|
||||
return ResponseUtils.success("User role relationship deleted successfully");
|
||||
} else {
|
||||
return ResponseUtils.fail("Failed to delete user role relationship");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/user/{userId}")
|
||||
public Object getUserRolesByUserId(@PathVariable Long userId) {
|
||||
QueryWrapper<UserRole> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("user_id", userId);
|
||||
List<UserRole> userRoles = userRoleService.list(wrapper);
|
||||
return ResponseUtils.success(userRoles);
|
||||
}
|
||||
|
||||
@GetMapping("/role/{roleId}")
|
||||
public Object getUserRolesByRoleId(@PathVariable Long roleId) {
|
||||
QueryWrapper<UserRole> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("role_id", roleId);
|
||||
List<UserRole> userRoles = userRoleService.list(wrapper);
|
||||
return ResponseUtils.success(userRoles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
# 接口开发通用规范文档
|
||||
|
||||
## 文档说明
|
||||
|
||||
本规范为项目所有接口的通用开发准则,适用于前后端接口对接、后端接口开发、接口文档编写等场景,所有业务接口均需遵循本规范。
|
||||
|
||||
### 基础约定
|
||||
|
||||
- 接口基础路径:`/api/v1`(各业务模块在基础路径后追加,如Auth模块:`/api/v1/auth`)
|
||||
- 数据交互格式:JSON
|
||||
- 字符编码:UTF-8
|
||||
- 时间戳单位:毫秒(整数类型)
|
||||
- 接口版本管理:通过URL路径版本号(如v1)区分,不使用请求参数/请求头区分
|
||||
|
||||
## 一、通用请求规范
|
||||
|
||||
### 1. 请求方式(RESTful规范)
|
||||
|
||||
| 操作类型 | 请求方法 | 典型场景 | 示例 |
|
||||
| ------------- | -------- | -------------------------------- | ----------------------------- |
|
||||
| 新增/提交数据 | POST | 登录、发送验证码、创建资源 | `/api/v1/auth/login` |
|
||||
| 查询数据 | GET | 获取详情、校验状态、列表查询 | `/api/v1/auth/user/info` |
|
||||
| 修改数据 | PUT | 全量更新(如重置密码、修改信息) | `/api/v1/auth/reset-password` |
|
||||
| 部分更新 | PATCH | 局部更新(如修改用户昵称) | `/api/v1/user/nickname` |
|
||||
| 删除数据 | DELETE | 退出登录、删除资源 | `/api/v1/auth/logout` |
|
||||
|
||||
### 2. 请求头规范
|
||||
|
||||
| 字段名 | 类型 | 必填性 | 描述 | 示例 |
|
||||
| --------------- | ------ | ------ | --------------------------------------------------------- | -------------------------------- |
|
||||
| Authorization | String | 非必选 | 登录令牌,格式为`Bearer + 空格 + token`(需登录接口必传) | "Bearer eyJhbGciOiJIUzI1NiJ9..." |
|
||||
| Content-Type | String | 必选 | 请求体格式,仅支持`application/json` | "application/json" |
|
||||
| Accept-Language | String | 非必选 | 语言类型,默认zh-CN | "zh-CN" |
|
||||
|
||||
### 3. 分页请求参数(列表接口必传)
|
||||
|
||||
所有列表查询接口均需支持分页,分页参数统一通过`GET`请求参数传递(POST请求可放在Body中),参数规范如下:
|
||||
|
||||
| 字段名 | 类型 | 必填性 | 默认值 | 描述 | 示例 |
|
||||
| ------ | ------- | ------ | ------ | ----------------------------- | ---- |
|
||||
| page | Integer | 否 | 1 | 当前页码(从1开始) | 1 |
|
||||
| limit | Integer | 否 | 10 | 每页显示条数(最大不超过100) | 10 |
|
||||
|
||||
#### 示例
|
||||
|
||||
```plain
|
||||
GET /api/v1/user/list?current=1&size=10
|
||||
```
|
||||
|
||||
### 4. 请求参数命名规范
|
||||
|
||||
- 字段名使用**小驼峰命名**(如`userName`、`phoneNumber`),禁止使用下划线/连字符;
|
||||
- 布尔类型字段命名:使用`isXXX`格式(如`isRemember`、`isEnabled`);
|
||||
- 时间类字段:统一使用`createTime`、`updateTime`等,禁止使用`create_at`;
|
||||
- 参数值:手机号、身份证号等字符串类型参数,值为纯数字时仍以字符串传递。
|
||||
|
||||
## 二、通用响应规范
|
||||
|
||||
### 1. 基础响应格式(所有接口统一)
|
||||
|
||||
所有接口响应必须包含`code`、`message`、`timestamp`字段,`data`字段为可选(无业务数据时返回`null`)。
|
||||
|
||||
#### 格式定义
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200, // 整数,业务状态码
|
||||
"message": "操作成功", // 字符串,提示信息(前端可直接展示)
|
||||
"timestamp": 1744238900000, // 整数,服务器响应时间戳(毫秒)
|
||||
"data": {} // 任意类型,业务数据(无数据时为null)
|
||||
}
|
||||
```
|
||||
|
||||
#### 字段说明
|
||||
|
||||
| 字段名 | 类型 | 必填性 | 描述 |
|
||||
| --------- | ------- | ------ | --------------------------------------------------------------------------- |
|
||||
| code | Integer | 是 | 业务状态码(200表示成功,非200表示异常) |
|
||||
| message | String | 是 | 响应提示信息(成功/失败描述,需友好、简洁) |
|
||||
| timestamp | Integer | 是 | 服务器处理请求的时间戳(毫秒),前端可用于时间展示/校验 |
|
||||
| data | Any | 否 | 业务数据体:1. 无数据时为null;2. 单条数据为Object;3. 列表数据遵循分页规范 |
|
||||
|
||||
### 2. 分页响应格式(列表接口专用)
|
||||
|
||||
列表接口的`data`字段必须遵循以下结构,禁止自定义分页字段名/格式。
|
||||
|
||||
#### 格式定义
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "查询成功",
|
||||
"timestamp": 1744238900000,
|
||||
"data": {
|
||||
"records": [], // 数组,当前页数据列表
|
||||
"total": 0, // 整数,符合条件的总记录数
|
||||
"size": 10, // 整数,每页显示条数(实际返回条数)
|
||||
"current": 1, // 整数,当前页码
|
||||
"pages": 0 // 整数,总页数(total/size向上取整)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 字段说明
|
||||
|
||||
| 字段名 | 类型 | 必填性 | 描述 |
|
||||
| ------- | ------- | ------ | ----------------------------------------- |
|
||||
| records | Array | 是 | 当前页数据列表(无数据时为空数组) |
|
||||
| total | Integer | 是 | 总记录数(用于计算总页数/分页展示) |
|
||||
| size | Integer | 是 | 每页请求条数(与请求参数size一致) |
|
||||
| current | Integer | 是 | 当前页码(与请求参数current一致) |
|
||||
| pages | Integer | 是 | 总页数(计算公式:Math.ceil(total/size)) |
|
||||
|
||||
#### 分页响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "用户列表查询成功",
|
||||
"timestamp": 1744238900000,
|
||||
"data": {
|
||||
"records": [
|
||||
{"id": 1, "username": "admin", "role": "ADMIN"},
|
||||
{"id": 2, "username": "user1", "role": "USER"}
|
||||
],
|
||||
"total": 20,
|
||||
"size": 10,
|
||||
"current": 1,
|
||||
"pages": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 通用状态码规范
|
||||
|
||||
| 状态码 | 含义 | 核心场景 |
|
||||
| ------ | -------------- | -------------------------------------- |
|
||||
| 200 | 操作成功 | 所有接口正常响应 |
|
||||
| 400 | 参数错误 | 请求参数缺失/格式错误/校验不通过 |
|
||||
| 401 | 未授权 | token失效/未登录/令牌格式错误 |
|
||||
| 403 | 禁止访问 | 账号被封禁/权限不足/IP受限 |
|
||||
| 404 | 资源不存在 | 用户不存在/接口路径错误/数据记录不存在 |
|
||||
| 409 | 资源冲突 | 用户名重复/手机号已绑定/操作重复提交 |
|
||||
| 429 | 请求过于频繁 | 验证码发送频率超限/接口调用频率超限 |
|
||||
| 500 | 服务器内部错误 | 服务端逻辑异常/第三方接口调用失败 |
|
||||
|
||||
## 三、接口命名规范
|
||||
|
||||
### 1. URL路径命名
|
||||
|
||||
- 路径全部使用**小写字母**,多个单词用连字符(-)分隔(如`/api/v1/auth/refresh-token`);
|
||||
- 路径使用名词而非动词(RESTful规范),如`/api/v1/user`(用户资源)而非`/api/v1/getUser`;
|
||||
- 资源ID放在路径末尾,如`/api/v1/user/123`(查询ID为123的用户)。
|
||||
|
||||
### 2. 响应字段命名
|
||||
|
||||
- 与请求参数一致,使用**小驼峰命名**;
|
||||
- 时间字段:优先返回时间戳(Integer,毫秒),如需返回格式化时间,字段名加`Str`后缀(如`createTimeStr`);
|
||||
- 金额字段:以分为单位返回整数(避免浮点精度问题),字段名加`Cent`后缀(如`amountCent`)。
|
||||
|
||||
## 四、安全规范(必遵循)
|
||||
|
||||
### 1. 密码/敏感信息处理
|
||||
|
||||
- 敏感信息返回:手机号、身份证号等脱敏展示(如`138****8000`);
|
||||
- Token处理:JWT Token不存储敏感信息(如密码),Redis存储的Token设置合理过期时间。
|
||||
|
||||
### 2. 接口频率限制
|
||||
|
||||
- 验证码接口:同一手机号1分钟内最多1次,1小时内最多5次;
|
||||
- 登录接口:同一账号5分钟内连续失败5次,临时锁定15分钟;
|
||||
- 通用接口:单IP/单用户接口调用频率不超过QPS 10。
|
||||
|
||||
### 3. 数据校验
|
||||
|
||||
- 所有入参必须做合法性校验(格式、长度、范围);
|
||||
|
||||
## 五、异常响应示例
|
||||
|
||||
### 1. 参数错误(400)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "手机号格式错误",
|
||||
"timestamp": 1744238900000,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 未授权(401)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 401,
|
||||
"message": "Token已过期,请重新登录",
|
||||
"timestamp": 1744238900000,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 服务器错误(500)
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"message": "服务器内部错误,请稍后重试",
|
||||
"timestamp": 1744238900000,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
1. 所有接口必须遵循**统一响应格式**(包含code/message/timestamp),列表接口强制分页并使用指定分页结构;
|
||||
2. 接口命名、参数/字段命名需符合RESTful规范和小驼峰/小写连字符约定,保证可读性;
|
||||
3. 安全规范是核心底线,密码、Token、频率限制等规则必须严格执行,避免安全漏洞;
|
||||
4. 本规范为基础准则,各业务模块可在本规范基础上补充模块专属规则,但不得与本规范冲突。
|
||||
@@ -0,0 +1,245 @@
|
||||
# 接口开发规范
|
||||
|
||||
## 0. 与API文档规范对照说明
|
||||
|
||||
本开发规范与`Controller/api-doc-standard.md`互为补充:
|
||||
|
||||
- **api-dev-standard.md**:指导**代码实现**(如何编写符合规范的代码)
|
||||
- **api-doc-standard.md**:定义**接口契约**(请求/响应格式标准)
|
||||
|
||||
---
|
||||
|
||||
## 1. 整体架构规范
|
||||
|
||||
### 1.1 分层结构
|
||||
|
||||
遵循CLAUDE.md定义的分层架构,特别注意:
|
||||
|
||||
- **Controller层**:业务逻辑的主要编写位置
|
||||
- **Service层**:仅包含接口定义和MyBatis Plus继承实现
|
||||
- 接口:`I{Name}Service` 继承 `IService<Entity>`
|
||||
- 实现:`{Name}ServiceImpl` 继承 `ServiceImpl<Mapper, Entity>`
|
||||
- **业务逻辑不应下沉到Service层**,Controller直接调用Service提供的CRUD方法
|
||||
|
||||
### 1.2 目录规范
|
||||
|
||||
- Controller按实体划分目录:`Controller/{EntityName}Controller/`
|
||||
- **Entity目录**:`Entity/` 下直接存放实体类(**无需子目录**)
|
||||
- **Mapper目录**:`Mapper/` 下直接存放Mapper接口(**无需子目录**)
|
||||
- **Service目录**:
|
||||
- 接口:`Service/`
|
||||
- 实现:`Service/Implements/`
|
||||
- 示例:
|
||||
- `User.java` → `Entity/User.java`
|
||||
- `UserMapper.java` → `Mapper/UserMapper.java`
|
||||
|
||||
## 2. Controller编写规范
|
||||
|
||||
### 2.1 服务注入
|
||||
|
||||
```java
|
||||
@Resource
|
||||
private IUserService userService;
|
||||
```
|
||||
|
||||
- **必须**使用`@Resource`而非`@Autowired`
|
||||
- **不得**在Service层编写业务逻辑
|
||||
|
||||
### 2.2 响应处理(必须遵守)
|
||||
|
||||
**所有接口必须使用ResponseUtils统一响应格式**:
|
||||
|
||||
对于 Controller 中的函数返回类型,统一使用 Object 类型,而不是具体的实体类。
|
||||
|
||||
如果需要编写具体 VO 实体类,在对应的 Controller 目录下创建 vo 文件夹并在其中定义。
|
||||
|
||||
```java
|
||||
// 成功响应
|
||||
return ResponseUtils.success(data);
|
||||
|
||||
// 失败响应(需指定状态码)
|
||||
return ResponseUtils.fail(400, "用户名格式错误");
|
||||
```
|
||||
|
||||
- **必须包含**:
|
||||
- `code`:按[api-doc-standard#状态码规范](Controller/api-doc-standard.md#三、通用状态码规范)设置
|
||||
- `timestamp`:当前时间戳(`System.currentTimeMillis()`)
|
||||
- `data`:业务数据(列表接口需返回标准分页结构)
|
||||
- **禁止**直接返回原始对象
|
||||
|
||||
### 2.3 分页处理
|
||||
|
||||
**必须使用MyBatis Plus原生Page对象**:
|
||||
|
||||
```java
|
||||
@GetMapping
|
||||
public Object getAllUsers(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
Page<User> pageObj = new Page<>(page, limit);
|
||||
return ResponseUtils.success(userService.page(pageObj));
|
||||
}
|
||||
```
|
||||
|
||||
- **关键点**:
|
||||
- Page对象字段与API响应结构完全匹配(records/total/size/current/pages)
|
||||
- **禁止**手动构建分页响应
|
||||
- 分页参数**代码中命名为`page`/`limit`**
|
||||
|
||||
### 2.4 查询操作
|
||||
|
||||
**简单查询**直接使用Service方法:
|
||||
|
||||
```java
|
||||
@GetMapping("/{id}")
|
||||
public Object getUserById(@PathVariable Long id) {
|
||||
return ResponseUtils.success(userService.getById(id));
|
||||
}
|
||||
```
|
||||
|
||||
**复杂查询**使用QueryWrapper:
|
||||
|
||||
```java
|
||||
@GetMapping("/username/{username}")
|
||||
public Object getUserByUsername(@PathVariable String username) {
|
||||
QueryWrapper<User> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("username", username);
|
||||
return ResponseUtils.success(userService.getOne(wrapper));
|
||||
}
|
||||
```
|
||||
|
||||
- **注意**:查询条件构建应在Controller层完成
|
||||
|
||||
## 3. Entity层规范
|
||||
|
||||
### 3.1 基础结构
|
||||
|
||||
```java
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@TableName("user")
|
||||
public class User {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
// ...其他字段
|
||||
}
|
||||
```
|
||||
|
||||
- **必须使用**:
|
||||
- Lombok注解:`@Data` + `@NoArgsConstructor` + `@AllArgsConstructor`
|
||||
- MyBatis Plus注解:`@TableName` + `@TableId`
|
||||
- **字段命名**:小驼峰命名(`createTime`而非`create_time`)
|
||||
|
||||
### 3.2 时间字段处理
|
||||
|
||||
- **Entity层**:使用`LocalDateTime`类型(如`createTime`)
|
||||
|
||||
## 4. Mapper层规范
|
||||
|
||||
### 4.1 基础结构
|
||||
|
||||
```java
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
// 仅允许MyBatis Plus内置方法,自定义SQL需走Service层
|
||||
}
|
||||
```
|
||||
|
||||
- **必须**继承`BaseMapper<Entity>`
|
||||
- **禁止**在Mapper接口中添加方法(自定义SQL需通过Service层)
|
||||
|
||||
## 5. Service层规范
|
||||
|
||||
### 5.1 接口定义
|
||||
|
||||
```java
|
||||
public interface IUserService extends IService<User> {
|
||||
// 仅允许扩展MyBatis Plus未提供的方法
|
||||
}
|
||||
```
|
||||
|
||||
- **必须**继承`IService<Entity>`
|
||||
- **仅当需要**扩展MyBatis Plus未提供的方法时才添加方法
|
||||
|
||||
### 5.2 实现类
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
}
|
||||
```
|
||||
|
||||
- **必须**继承`ServiceImpl<Mapper, Entity>`
|
||||
- **禁止**在实现类中编写业务逻辑(仅用于注入Mapper)
|
||||
|
||||
## 6. 禁止行为清单
|
||||
|
||||
| 类型 | 错误示例 | 正确做法 |
|
||||
| ------------ | ----------------------------------- | ------------------------------------ |
|
||||
| 业务逻辑位置 | 在ServiceImpl中编写业务 | 所有业务逻辑放在Controller |
|
||||
| 响应格式 | 直接返回`new ResponseEntity<>(...)` | 用ResponseUtils生成带timestamp的响应 |
|
||||
| 分页参数 | 使用`offset`/`pageSize` | 统一用`page`/`limit`参数 |
|
||||
| 状态码 | 所有错误返回500 | 按[api-doc-standard]返回400/401等 |
|
||||
| 时间字段 | 直接返回LocalDateTime | 转换为时间戳或格式化字符串 |
|
||||
|
||||
## 7. 必须使用的工具类
|
||||
|
||||
| 工具类 | 用途 | 示例 |
|
||||
| --------------- | -------------- | ------------------------------------- |
|
||||
| `ResponseUtils` | 生成标准化响应 | `ResponseUtils.fail(400, "参数错误")` |
|
||||
|
||||
## 8. 接口设计原则
|
||||
|
||||
1. **RESTful规范**:
|
||||
- 路径使用**复数名词**:`/users`而非`/user`
|
||||
- **参数命名**:代码用`page`/`limit`,文档用`current`/`size`
|
||||
|
||||
2. **字段转换**:
|
||||
- Entity的`LocalDateTime` → 接口响应需转为时间戳
|
||||
- 敏感字段(如密码)需在Controller层过滤:`user.setPassword(null)`
|
||||
|
||||
3. **错误处理**:
|
||||
- 按[api-doc-standard#通用状态码规范](Controller/api-doc-standard.md#三、通用状态码规范)返回精确错误
|
||||
- 示例:`return ResponseUtils.fail(409, "用户名已存在");`
|
||||
|
||||
## 9. 完整示例
|
||||
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private IUserService userService;
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Object getUser(@PathVariable Long id) {
|
||||
User user = userService.getById(id);
|
||||
// 过滤敏感字段
|
||||
user.setPassword(null);
|
||||
// 转换时间格式
|
||||
return ResponseUtils.success(user);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Object listUsers(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer limit) {
|
||||
|
||||
// 参数校验
|
||||
if (limit > 100) {
|
||||
return ResponseUtils.fail(400, "每页数量不能超过100");
|
||||
}
|
||||
|
||||
Page<User> pageObj = new Page<>(page, limit);
|
||||
Page<User> resultPage = userService.page(pageObj);
|
||||
return ResponseUtils.success(resultPage);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
package icu.sunway.ai_spring_example.Entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("permission")
|
||||
public class Permission {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
private Long parentId;
|
||||
private Integer type; // 1: menu, 2: button, 3: api
|
||||
private String path;
|
||||
private Integer sort;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
26
src/main/java/icu/sunway/ai_spring_example/Entity/Role.java
Normal file
26
src/main/java/icu/sunway/ai_spring_example/Entity/Role.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package icu.sunway.ai_spring_example.Entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("role")
|
||||
public class Role {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private String code;
|
||||
private String description;
|
||||
private Integer status; // 0: disabled, 1: enabled
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package icu.sunway.ai_spring_example.Entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("role_permission")
|
||||
public class RolePermission {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long roleId;
|
||||
private Long permissionId;
|
||||
}
|
||||
29
src/main/java/icu/sunway/ai_spring_example/Entity/User.java
Normal file
29
src/main/java/icu/sunway/ai_spring_example/Entity/User.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package icu.sunway.ai_spring_example.Entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("user")
|
||||
public class User {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String avatar;
|
||||
private Integer status; // 0: disabled, 1: enabled
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private LocalDateTime lastLoginTime;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -11,9 +11,10 @@ import lombok.NoArgsConstructor;
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@TableName("example_entity")
|
||||
public class ExampleEntity {
|
||||
@TableName("user_role")
|
||||
public class UserRole {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String name;
|
||||
private LocalDateTime createTime;
|
||||
private Long userId;
|
||||
private Long roleId;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package icu.sunway.ai_spring_example.Exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 自定义业务异常类
|
||||
*/
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
/**
|
||||
* 业务错误码(区分不同异常场景)
|
||||
*/
|
||||
private int code;
|
||||
|
||||
// 仅错误消息
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.code = 500; // 默认业务异常码
|
||||
}
|
||||
|
||||
// 错误码 + 错误消息
|
||||
public BusinessException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
// 错误码 + 错误消息 + 根因异常
|
||||
public BusinessException(int code, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package icu.sunway.ai_spring_example.Mapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.ExampleEntity;
|
||||
import icu.sunway.ai_spring_example.Entity.Permission;
|
||||
|
||||
@Mapper
|
||||
public interface ExampleEntityMapper extends BaseMapper<ExampleEntity> {
|
||||
public interface PermissionMapper extends BaseMapper<Permission> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package icu.sunway.ai_spring_example.Mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.Role;
|
||||
|
||||
@Mapper
|
||||
public interface RoleMapper extends BaseMapper<Role> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package icu.sunway.ai_spring_example.Mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.RolePermission;
|
||||
|
||||
@Mapper
|
||||
public interface RolePermissionMapper extends BaseMapper<RolePermission> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package icu.sunway.ai_spring_example.Mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.User;
|
||||
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package icu.sunway.ai_spring_example.Mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import icu.sunway.ai_spring_example.Entity.UserRole;
|
||||
|
||||
@Mapper
|
||||
public interface UserRoleMapper extends BaseMapper<UserRole> {
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import icu.sunway.ai_spring_example.Entity.User;
|
||||
import icu.sunway.ai_spring_example.Exception.BusinessException;
|
||||
import icu.sunway.ai_spring_example.Service.Implements.UserServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Utils.JwtUtils;
|
||||
import icu.sunway.ai_spring_example.Utils.RedisUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
|
||||
@Resource
|
||||
private UserServiceImpl userService;
|
||||
@Resource
|
||||
private MailService mailService;
|
||||
@Resource
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
public void registerByEmail(String email, String password, String verifyCode) {
|
||||
// 校验邮箱是否已存在
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("email", email);
|
||||
if (userService.getOne(queryWrapper) != null) {
|
||||
throw new BusinessException("邮箱已存在");
|
||||
}
|
||||
// 校验验证码正确性
|
||||
if (!mailService.checkVerifyCode(email, verifyCode)) {
|
||||
throw new BusinessException("验证码错误");
|
||||
}
|
||||
User user = new User();
|
||||
user.setEmail(email);
|
||||
// 用户名是 not null,暂时设置为邮箱
|
||||
user.setUsername(email);
|
||||
user.setPassword(password);
|
||||
userService.save(user);
|
||||
}
|
||||
|
||||
public String loginByEmailPassword(String email, String password) {
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("email", email);
|
||||
// 校验邮箱是否存在
|
||||
if (userService.getOne(queryWrapper) == null) {
|
||||
throw new BusinessException("邮箱不存在");
|
||||
}
|
||||
queryWrapper.eq("password", password);
|
||||
User user = userService.getOne(queryWrapper);
|
||||
if (user == null) {
|
||||
throw new BusinessException("邮箱或密码错误");
|
||||
}
|
||||
String token = JwtUtils.generateToken(user.getId().toString());
|
||||
// 存放 token 到 Redis
|
||||
redisUtils.set("token:" + token, token, JwtUtils.getTime(), TimeUnit.MILLISECONDS);
|
||||
return token;
|
||||
}
|
||||
|
||||
public boolean checkToken(String token) {
|
||||
return redisUtils.hasKey("token:" + token);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.ExampleEntity;
|
||||
|
||||
public interface IExampleEntityService extends IService<ExampleEntity> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.Permission;
|
||||
|
||||
public interface IPermissionService extends IService<Permission> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.RolePermission;
|
||||
|
||||
public interface IRolePermissionService extends IService<RolePermission> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.Role;
|
||||
|
||||
public interface IRoleService extends IService<Role> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.UserRole;
|
||||
|
||||
public interface IUserRoleService extends IService<UserRole> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import icu.sunway.ai_spring_example.Entity.User;
|
||||
|
||||
public interface IUserService extends IService<User> {
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.ExampleEntity;
|
||||
import icu.sunway.ai_spring_example.Mapper.ExampleEntityMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IExampleEntityService;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ExampleEntityServiceImpl extends ServiceImpl<ExampleEntityMapper, ExampleEntity>
|
||||
implements IExampleEntityService {
|
||||
|
||||
@Resource
|
||||
private ExampleEntityMapper exampleEntityMapper;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.Permission;
|
||||
import icu.sunway.ai_spring_example.Mapper.PermissionMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IPermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {
|
||||
|
||||
@Resource
|
||||
private PermissionMapper permissionMapper;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.RolePermission;
|
||||
import icu.sunway.ai_spring_example.Mapper.RolePermissionMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IRolePermissionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class RolePermissionServiceImpl extends ServiceImpl<RolePermissionMapper, RolePermission>
|
||||
implements IRolePermissionService {
|
||||
|
||||
@Resource
|
||||
private RolePermissionMapper rolePermissionMapper;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.Role;
|
||||
import icu.sunway.ai_spring_example.Mapper.RoleMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IRoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {
|
||||
|
||||
@Resource
|
||||
private RoleMapper roleMapper;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.UserRole;
|
||||
import icu.sunway.ai_spring_example.Mapper.UserRoleMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IUserRoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements IUserRoleService {
|
||||
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package icu.sunway.ai_spring_example.Service.Implements;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import icu.sunway.ai_spring_example.Entity.User;
|
||||
import icu.sunway.ai_spring_example.Mapper.UserMapper;
|
||||
import icu.sunway.ai_spring_example.Service.IUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
|
||||
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package icu.sunway.ai_spring_example.Service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import icu.sunway.ai_spring_example.Utils.RedisUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class MailService {
|
||||
@Resource
|
||||
private RedisUtils redisUtils;
|
||||
|
||||
public Boolean checkVerifyCode(String to, String toBeCheckedCode) {
|
||||
// if (redisUtils.hasKey("email:verify:" + to)) {
|
||||
// String verifyCode = redisUtils.get("email:verify:" + to).toString();
|
||||
// return verifyCode.equals(toBeCheckedCode);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
|
||||
// 暂时没有发邮件,默认全部通过
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -145,19 +145,6 @@ public class JsonUtils {
|
||||
return toObject(json, Map.class, keyClazz, valueClazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转换为Map
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, Object> toMap(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.convertValue(obj, Map.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JSON字符串是否有效
|
||||
*
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package icu.sunway.ai_spring_example.Utils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import lombok.Getter;
|
||||
|
||||
public class JwtUtils {
|
||||
|
||||
@Getter
|
||||
private static final long time = 1000 * 3600 * 24 * 7; // 7天过期
|
||||
private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
|
||||
|
||||
public static String generateToken(String id) {
|
||||
JwtBuilder jwtBuilder = Jwts.builder();
|
||||
return jwtBuilder
|
||||
.setHeaderParam("typ", "JWT")
|
||||
.setHeaderParam("alg", "HS256")
|
||||
.claim("id", id)
|
||||
.claim("date", new Date())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + time))
|
||||
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.setSubject("user-check")
|
||||
.compact();
|
||||
}
|
||||
|
||||
public static String parseTokenAsId(String token) {
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(SECRET_KEY)
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
try {
|
||||
if (claims.getExpiration().before(new Date())) {
|
||||
return null;
|
||||
}
|
||||
return claims.get("id", String.class);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package icu.sunway.ai_spring_example.Utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LogUtils {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LogUtils.class);
|
||||
|
||||
public static void info(String message) {
|
||||
logger.info("------------ debug info start -----------");
|
||||
logger.info(message);
|
||||
logger.info("------------ debug info end -----------");
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
public static void error(String message, Throwable throwable) {
|
||||
logger.error(message, throwable);
|
||||
}
|
||||
|
||||
public static void debug(String message) {
|
||||
logger.debug(message);
|
||||
}
|
||||
|
||||
public static void debug(String message, Throwable throwable) {
|
||||
logger.debug(message, throwable);
|
||||
}
|
||||
|
||||
public static void trace(String message) {
|
||||
logger.trace(message);
|
||||
}
|
||||
|
||||
public static void trace(String message, Throwable throwable) {
|
||||
logger.trace(message, throwable);
|
||||
}
|
||||
|
||||
public static void warn(String message) {
|
||||
logger.warn(message);
|
||||
}
|
||||
|
||||
public static void warn(String message, Throwable throwable) {
|
||||
logger.warn(message, throwable);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package icu.sunway.ai_spring_example.Utils;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*/
|
||||
public class PageUtils {
|
||||
|
||||
/**
|
||||
* 默认页码
|
||||
*/
|
||||
public static final Integer DEFAULT_PAGE = 1;
|
||||
|
||||
/**
|
||||
* 默认每页数量
|
||||
*/
|
||||
public static final Integer DEFAULT_LIMIT = 10;
|
||||
|
||||
/**
|
||||
* 最大每页数量
|
||||
*/
|
||||
public static final Integer MAX_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* 分页结果类
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class PaginationResult<T> {
|
||||
private Integer currentPage;
|
||||
private Integer pageSize;
|
||||
private Long totalItems;
|
||||
private Integer totalPages;
|
||||
private List<T> list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证和调整分页参数
|
||||
*
|
||||
* @param page 页码
|
||||
* @param limit 每页数量
|
||||
* @return 调整后的分页参数数组 [page, limit]
|
||||
*/
|
||||
public static Integer[] validatePageParams(Integer page, Integer limit) {
|
||||
// 验证页码
|
||||
if (page == null || page < 1) {
|
||||
page = DEFAULT_PAGE;
|
||||
}
|
||||
|
||||
// 验证每页数量
|
||||
if (limit == null || limit < 1 || limit > MAX_LIMIT) {
|
||||
limit = DEFAULT_LIMIT;
|
||||
}
|
||||
|
||||
return new Integer[] { page, limit };
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Page对象
|
||||
*
|
||||
* @param page 页码
|
||||
* @param limit 每页数量
|
||||
* @return Page对象
|
||||
*/
|
||||
public static <T> Page<T> createPage(Integer page, Integer limit) {
|
||||
Integer[] params = validatePageParams(page, limit);
|
||||
return new Page<>(params[0], params[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将MyBatis Plus的Page结果转换为分页结果
|
||||
*
|
||||
* @param pageResult MyBatis Plus的Page结果
|
||||
* @return 分页结果
|
||||
*/
|
||||
public static <T> PaginationResult<T> createPaginationResult(IPage<T> pageResult) {
|
||||
return new PaginationResult<>(
|
||||
(int) pageResult.getCurrent(),
|
||||
(int) pageResult.getSize(),
|
||||
pageResult.getTotal(),
|
||||
(int) pageResult.getPages(),
|
||||
pageResult.getRecords());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的分页结果
|
||||
*
|
||||
* @param page 页码
|
||||
* @param limit 每页数量
|
||||
* @return 空的分页结果
|
||||
*/
|
||||
public static <T> PaginationResult<T> createEmptyPaginationResult(Integer page, Integer limit) {
|
||||
Integer[] params = validatePageParams(page, limit);
|
||||
return new PaginationResult<>(
|
||||
params[0],
|
||||
params[1],
|
||||
0L,
|
||||
0,
|
||||
List.of());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user