Files
lanecarford_back/ARCHITECTURE.md
2025-10-20 16:13:39 +08:00

22 KiB
Raw Permalink Blame History

Lane Carford 基础架构功能说明

🏗️ 架构概述

这是一个基于 Spring Boot 3.1.6Java 21 的纯净架构模板,提供了完整的企业级应用基础设施,可以快速开发各种类型的业务应用。

📋 核心功能清单

1. 🛡️ 全局异常处理

功能描述: 统一处理应用程序中的各种异常,提供一致的错误响应格式

实现类: GlobalExceptionHandler

支持的异常类型:

  • 业务异常 (BusinessException)
  • 参数验证异常 (MethodArgumentNotValidException)
  • 文件上传大小超限异常 (MaxUploadSizeExceededException)
  • 数据库操作异常 (DataAccessException, SQLException)
  • HTTP方法不支持异常 (HttpRequestMethodNotSupportedException)
  • 404异常 (NoHandlerFoundException)
  • 运行时异常 (RuntimeException)
  • 其他通用异常

使用示例:

// 1. 创建自定义业务异常
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @PostMapping
    public ApiResponse<User> createUser(@Valid @RequestBody CreateUserRequest request) {
        // 业务逻辑验证
        if (userService.existsByEmail(request.getEmail())) {
            throw new BusinessException("USER_EXISTS", "用户邮箱已存在");
        }
        
        User user = userService.createUser(request);
        return ApiResponse.success("用户创建成功", user);
    }
    
    @GetMapping("/{id}")
    public ApiResponse<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if (user == null) {
            throw new BusinessException("USER_NOT_FOUND", "用户不存在");
        }
        return ApiResponse.success(user);
    }
}

// 2. 参数验证异常自动处理
public class CreateUserRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度必须在2-20个字符之间")
    private String username;
    
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @NotNull(message = "年龄不能为空")
    @Min(value = 18, message = "年龄不能小于18岁")
    @Max(value = 100, message = "年龄不能大于100岁")
    private Integer age;
}

// 异常响应格式示例:
{
    "success": false,
    "code": "VALIDATION_ERROR",
    "message": "参数验证失败",
    "timestamp": 1640995200000,
    "path": "/api/users",
    "errors": {
        "username": "用户名不能为空",
        "email": "邮箱格式不正确"
    }
}

2. 📤 统一API响应格式

功能描述: 提供标准化的API响应格式确保前后端接口的一致性

实现类: ApiResponse<T>

使用示例:

@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    // 成功响应 - 返回数据
    @GetMapping("/{id}")
    public ApiResponse<Product> getProduct(@PathVariable Long id) {
        Product product = productService.findById(id);
        return ApiResponse.success("查询成功", product);
    }
    
    // 成功响应 - 无数据
    @DeleteMapping("/{id}")
    public ApiResponse<Void> deleteProduct(@PathVariable Long id) {
        productService.deleteById(id);
        return ApiResponse.success("删除成功");
    }
    
    // 分页查询响应
    @GetMapping
    public ApiResponse<PageResult<Product>> getProducts(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        PageResult<Product> result = productService.findByPage(page, size);
        return ApiResponse.success("查询成功", result);
    }
    
    // 错误响应
    @PostMapping
    public ApiResponse<Product> createProduct(@Valid @RequestBody CreateProductRequest request) {
        if (productService.existsByName(request.getName())) {
            return ApiResponse.businessError("PRODUCT_EXISTS", "产品名称已存在");
        }
        
        Product product = productService.create(request);
        return ApiResponse.success("创建成功", product);
    }
}

// 响应格式示例:
{
    "success": true,
    "code": "SUCCESS",
    "message": "查询成功",
    "data": {
        "id": 1,
        "name": "产品名称",
        "price": 99.99
    },
    "timestamp": 1640995200000
}

3. 📊 分页查询支持

功能描述: 提供标准化的分页查询结果封装

实现类: PageResult<T>

使用示例:

@Service
public class ProductService {
    
    @Autowired
    private ProductMapper productMapper;
    
    public PageResult<Product> findByPage(int page, int size) {
        // 使用MyBatis-Plus分页
        Page<Product> pageParam = new Page<>(page, size);
        Page<Product> result = productMapper.selectPage(pageParam, null);
        
        return PageResult.<Product>builder()
                .records(result.getRecords())
                .total(result.getTotal())
                .current(result.getCurrent())
                .size(result.getSize())
                .pages(result.getPages())
                .build();
    }
    
    // 带条件的分页查询
    public PageResult<Product> findByCondition(ProductQueryRequest request) {
        Page<Product> pageParam = new Page<>(request.getPage(), request.getSize());
        
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        if (StringUtils.hasText(request.getName())) {
            queryWrapper.like("name", request.getName());
        }
        if (request.getMinPrice() != null) {
            queryWrapper.ge("price", request.getMinPrice());
        }
        if (request.getMaxPrice() != null) {
            queryWrapper.le("price", request.getMaxPrice());
        }
        
        Page<Product> result = productMapper.selectPage(pageParam, queryWrapper);
        return PageResult.fromPage(result);
    }
}

4. 🔄 Bean转换工具

功能描述: 提供对象之间的转换功能简化DTO和Entity之间的转换

实现类: BeanUtil

使用示例:

@Service
public class UserService {
    
    // 单个对象转换
    public UserDTO convertToDTO(User user) {
        return BeanUtil.convert(user, UserDTO.class);
    }
    
    // 列表转换
    public List<UserDTO> convertToDTOList(List<User> users) {
        return BeanUtil.convertList(users, UserDTO.class);
    }
    
    // 使用Supplier转换适用于复杂对象
    public UserDetailDTO convertToDetailDTO(User user) {
        return BeanUtil.convert(user, () -> {
            UserDetailDTO dto = new UserDetailDTO();
            // 可以在这里设置默认值
            dto.setStatus("ACTIVE");
            return dto;
        });
    }
    
    // 忽略null值的属性复制
    public void updateUser(Long id, UpdateUserRequest request) {
        User existingUser = findById(id);
        // 只复制非null的属性避免覆盖现有数据
        BeanUtil.copyPropertiesIgnoreNull(request, existingUser);
        userMapper.updateById(existingUser);
    }
}

// 实体类示例
@Data
@TableName("users")
public class User extends BaseEntity {
    private String username;
    private String email;
    private String phone;
    private Integer age;
    private String avatar;
}

// DTO示例
@Data
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    private String phone;
    private Integer age;
    private String avatar;
    private LocalDateTime createdTime;
}

5. 📝 日志切面

功能描述: 自动记录Controller方法的调用日志包括请求参数和响应结果

实现类: LoggingAspect

使用示例:

// 无需额外配置自动对所有Controller方法生效
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @PostMapping
    public ApiResponse<Order> createOrder(@RequestBody CreateOrderRequest request) {
        // 自动记录日志:
        // [INFO] 开始执行方法: OrderController.createOrder
        // [INFO] 请求参数: {"productId":1,"quantity":2,"address":"北京市朝阳区"}
        
        Order order = orderService.createOrder(request);
        
        // [INFO] 方法执行完成: OrderController.createOrder, 耗时: 150ms
        // [INFO] 响应结果: {"success":true,"data":{"id":1,"orderNo":"ORD20240101001"}}
        
        return ApiResponse.success("订单创建成功", order);
    }
    
    @GetMapping("/{id}")
    public ApiResponse<Order> getOrder(@PathVariable Long id) {
        // 自动记录日志,包括路径参数
        Order order = orderService.findById(id);
        return ApiResponse.success(order);
    }
}

// 日志输出示例:
// 2024-01-01 10:30:15.123 [http-nio-8080-exec-1] INFO  c.a.l.aspect.LoggingAspect - 开始执行方法: OrderController.createOrder
// 2024-01-01 10:30:15.124 [http-nio-8080-exec-1] INFO  c.a.l.aspect.LoggingAspect - 请求参数: [{"productId":1,"quantity":2}]
// 2024-01-01 10:30:15.275 [http-nio-8080-exec-1] INFO  c.a.l.aspect.LoggingAspect - 方法执行完成: OrderController.createOrder, 耗时: 151ms
// 2024-01-01 10:30:15.276 [http-nio-8080-exec-1] INFO  c.a.l.aspect.LoggingAspect - 响应结果: {"success":true,"code":"SUCCESS"}

6. 性能监控切面

功能描述: 监控Controller、Service、Mapper层方法的执行性能超过阈值时发出警告

实现类: PerformanceAspect

使用示例:

// 无需额外配置,自动监控所有层的方法性能
@Service
public class ProductService {
    
    @Autowired
    private ProductMapper productMapper;
    
    public List<Product> findExpensiveProducts() {
        // 如果这个方法执行超过5秒会自动记录警告日志
        return productMapper.selectExpensiveProducts();
    }
    
    public void batchUpdateProducts(List<Product> products) {
        // 批量操作可能耗时较长,会被监控
        for (Product product : products) {
            productMapper.updateById(product);
        }
    }
}

@Repository
public interface ProductMapper extends BaseMapper<Product> {
    
    // 复杂查询可能耗时较长,会被监控
    @Select("SELECT * FROM products WHERE price > 1000 ORDER BY price DESC")
    List<Product> selectExpensiveProducts();
}

// 性能监控日志示例:
// 2024-01-01 10:30:15.123 [http-nio-8080-exec-1] WARN  c.a.l.aspect.PerformanceAspect - [性能警告] Service层方法执行缓慢: ProductService.findExpensiveProducts, 耗时: 6543ms (阈值: 5000ms)
// 2024-01-01 10:30:15.124 [http-nio-8080-exec-1] INFO  c.a.l.aspect.PerformanceAspect - [性能监控] Mapper层方法执行: ProductMapper.selectExpensiveProducts, 耗时: 234ms

// 配置性能阈值 (application.properties):
app.performance.warning.controller=5000  # Controller层警告阈值5秒
app.performance.warning.service=3000     # Service层警告阈值3秒  
app.performance.warning.mapper=500       # Mapper层警告阈值500毫秒

7. 🗄️ MyBatis-Plus集成

功能描述: 提供强大的ORM功能包括基础CRUD、分页查询、条件构造器等

实现类: BaseMapper<T>, BaseEntity

使用示例:

// 1. 实体类继承BaseEntity
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("products")
public class Product extends BaseEntity {
    
    @TableField("name")
    private String name;
    
    @TableField("description")
    private String description;
    
    @TableField("price")
    private BigDecimal price;
    
    @TableField("category_id")
    private Long categoryId;
    
    @TableField("stock")
    private Integer stock;
    
    @TableField("status")
    private Integer status; // 0-下架, 1-上架
}

// 2. Mapper接口继承BaseMapper
@Repository
public interface ProductMapper extends BaseMapper<Product> {
    
    // 自定义查询方法
    @Select("SELECT * FROM products WHERE category_id = #{categoryId} AND status = 1")
    List<Product> findByCategoryAndActive(@Param("categoryId") Long categoryId);
    
    // 复杂查询
    @Select("SELECT p.*, c.name as category_name FROM products p " +
            "LEFT JOIN categories c ON p.category_id = c.id " +
            "WHERE p.price BETWEEN #{minPrice} AND #{maxPrice}")
    List<ProductVO> findByPriceRange(@Param("minPrice") BigDecimal minPrice, 
                                   @Param("maxPrice") BigDecimal maxPrice);
}

// 3. Service层使用示例
@Service
public class ProductService {
    
    @Autowired
    private ProductMapper productMapper;
    
    // 基础CRUD操作
    public Product save(Product product) {
        productMapper.insert(product); // 自动填充创建时间
        return product;
    }
    
    public Product findById(Long id) {
        return productMapper.selectById(id);
    }
    
    public void deleteById(Long id) {
        productMapper.deleteById(id); // 逻辑删除
    }
    
    public Product update(Product product) {
        productMapper.updateById(product); // 自动更新修改时间
        return product;
    }
    
    // 条件查询
    public List<Product> findByCondition(String name, BigDecimal minPrice, Integer status) {
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        
        if (StringUtils.hasText(name)) {
            queryWrapper.like("name", name);
        }
        if (minPrice != null) {
            queryWrapper.ge("price", minPrice);
        }
        if (status != null) {
            queryWrapper.eq("status", status);
        }
        
        return productMapper.selectList(queryWrapper);
    }
    
    // 分页查询
    public PageResult<Product> findByPage(int page, int size, String keyword) {
        Page<Product> pageParam = new Page<>(page, size);
        
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        if (StringUtils.hasText(keyword)) {
            queryWrapper.and(wrapper -> wrapper
                .like("name", keyword)
                .or()
                .like("description", keyword)
            );
        }
        queryWrapper.eq("status", 1); // 只查询上架商品
        queryWrapper.orderByDesc("created_time");
        
        Page<Product> result = productMapper.selectPage(pageParam, queryWrapper);
        return PageResult.fromPage(result);
    }
    
    // 批量操作
    public void batchInsert(List<Product> products) {
        // MyBatis-Plus会自动优化批量插入
        for (Product product : products) {
            productMapper.insert(product);
        }
    }
}

9. 🔒 Spring Security集成

功能描述: 提供基础的安全配置,支持认证和授权

配置类: SecurityConfig

使用示例:

// 当前配置允许所有请求访问,可以根据需要自定义安全规则
@Configuration
@EnableWebSecurity
public class CustomSecurityConfig {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(authz -> authz
                // 公开接口
                .requestMatchers("/api/auth/**", "/api/public/**").permitAll()
                .requestMatchers("/actuator/**", "/api-docs/**", "/swagger-ui/**").permitAll()
                
                // 需要认证的接口
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/api/auth/login")
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler())
            )
            .logout(logout -> logout
                .logoutUrl("/api/auth/logout")
                .logoutSuccessHandler(logoutSuccessHandler())
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .maximumSessions(1)
                .maxSessionsPreventsLogin(false)
            );
            
        return http.build();
    }
    
    // 认证成功处理器
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return (request, response, authentication) -> {
            response.setContentType("application/json;charset=UTF-8");
            ApiResponse<UserInfo> result = ApiResponse.success("登录成功", getUserInfo(authentication));
            response.getWriter().write(JSON.toJSONString(result));
        };
    }
}

// 用户认证Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @PostMapping("/login")
    public ApiResponse<UserInfo> login(@RequestBody LoginRequest request, HttpServletRequest httpRequest) {
        // Spring Security会自动处理认证
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserInfo userInfo = getUserInfo(authentication);
        return ApiResponse.success("登录成功", userInfo);
    }
    
    @PostMapping("/logout")
    public ApiResponse<Void> logout(HttpServletRequest request) {
        SecurityContextHolder.clearContext();
        request.getSession().invalidate();
        return ApiResponse.success("退出成功");
    }
    
    @GetMapping("/current")
    public ApiResponse<UserInfo> getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return ApiResponse.error("UNAUTHORIZED", "未登录");
        }
        UserInfo userInfo = getUserInfo(authentication);
        return ApiResponse.success(userInfo);
    }
}

10. 📚 Swagger API文档

功能描述: 自动生成API文档支持在线测试

配置类: SwaggerConfig

使用示例:

// 在Controller中添加Swagger注解
@RestController
@RequestMapping("/api/products")
@Tag(name = "商品管理", description = "商品相关的API接口")
public class ProductController {
    
    @Operation(summary = "创建商品", description = "创建一个新的商品")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "创建成功"),
        @ApiResponse(responseCode = "400", description = "参数错误"),
        @ApiResponse(responseCode = "500", description = "服务器错误")
    })
    @PostMapping
    public ApiResponse<Product> createProduct(
            @Parameter(description = "商品信息", required = true)
            @Valid @RequestBody CreateProductRequest request) {
        
        Product product = productService.createProduct(request);
        return ApiResponse.success("创建成功", product);
    }
    
    @Operation(summary = "获取商品详情", description = "根据商品ID获取商品详细信息")
    @GetMapping("/{id}")
    public ApiResponse<Product> getProduct(
            @Parameter(description = "商品ID", required = true, example = "1")
            @PathVariable Long id) {
        
        Product product = productService.findById(id);
        return ApiResponse.success(product);
    }
    
    @Operation(summary = "商品列表查询", description = "分页查询商品列表,支持关键词搜索")
    @GetMapping
    public ApiResponse<PageResult<Product>> getProducts(
            @Parameter(description = "页码", example = "1")
            @RequestParam(defaultValue = "1") int page,
            
            @Parameter(description = "每页大小", example = "10")
            @RequestParam(defaultValue = "10") int size,
            
            @Parameter(description = "搜索关键词")
            @RequestParam(required = false) String keyword) {
        
        PageResult<Product> result = productService.findByPage(page, size, keyword);
        return ApiResponse.success("查询成功", result);
    }
}

// DTO类添加Swagger注解
@Data
@Schema(description = "创建商品请求")
public class CreateProductRequest {
    
    @Schema(description = "商品名称", example = "iPhone 15 Pro", required = true)
    @NotBlank(message = "商品名称不能为空")
    private String name;
    
    @Schema(description = "商品描述", example = "最新款iPhone手机")
    private String description;
    
    @Schema(description = "商品价格", example = "8999.00", required = true)
    @NotNull(message = "商品价格不能为空")
    @DecimalMin(value = "0.01", message = "商品价格必须大于0")
    private BigDecimal price;
    
    @Schema(description = "商品分类ID", example = "1", required = true)
    @NotNull(message = "商品分类不能为空")
    private Long categoryId;
    
    @Schema(description = "库存数量", example = "100", required = true)
    @NotNull(message = "库存数量不能为空")
    @Min(value = 0, message = "库存数量不能为负数")
    private Integer stock;
}

// 访问Swagger UI: http://localhost:8080/swagger-ui.html
// 访问API文档JSON: http://localhost:8080/api-docs

🚀 快速开始

1. 创建实体类

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("your_table")
public class YourEntity extends BaseEntity {
    private String name;
    private String description;
    // 其他字段...
}

2. 创建Mapper接口

@Repository
public interface YourEntityMapper extends BaseMapper<YourEntity> {
    // 自定义查询方法
}

3. 创建Service类

@Service
public class YourEntityService {
    @Autowired
    private YourEntityMapper mapper;
    
    // 业务逻辑方法
}

4. 创建Controller类

@RestController
@RequestMapping("/api/your-entity")
public class YourEntityController {
    @Autowired
    private YourEntityService service;
    
    // API接口方法
}

📝 配置说明

数据库配置

# MySQL数据库连接
spring.datasource.url=jdbc:mysql://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password

文件上传配置

# 文件上传大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=50MB
app.upload.dir=./uploads/

性能监控配置

# 性能警告阈值(毫秒)
app.performance.warning.controller=5000
app.performance.warning.service=3000
app.performance.warning.mapper=500

这个架构模板提供了完整的企业级应用基础设施,您可以在此基础上快速开发各种类型的业务应用!