ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring Boot实战:从零构建法律援助管理系统后端架构

Spring Boot实战:从零构建法律援助管理系统后端架构 大家好我是专注于分享后端实战经验的博主。在开发各类管理系统时我们常常需要将复杂的业务逻辑、数据管理、权限控制和安全规范整合到一个高效、可维护的系统中。今天我们就以“法律援助管理系统”这一具有社会价值的应用场景为例手把手带你从零开始基于 Spring Boot 这一主流框架设计并实现一个功能完整、结构清晰的后端系统。无论你是正在学习 Spring Boot 的在校学生还是需要快速搭建业务原型的开发者这篇文章都将为你提供一套可直接复用的“脚手架”和深入骨髓的工程化思考。1. 系统背景与核心需求分析在深入代码之前我们必须明确要解决什么问题。法律援助管理系统旨在为法律援助中心、律师事务所或相关公益组织提供一个数字化的管理平台核心目标是提升案件处理效率、规范工作流程、并保障受援人的信息安全。一个典型的系统需要涵盖以下核心模块用户与权限管理区分系统管理员、法律援助律师、案件受理员、受援人等不同角色实现精细化的功能与数据权限控制。案件全生命周期管理从案件申请、受理、指派律师、办理、结案到归档实现线上化流程跟踪。资源与知识库管理管理律师信息、法律法规库、典型案例等为案件办理提供支持。数据统计与报表为管理者提供案件数量、类型分布、律师工作量等可视化数据。选择 Spring Boot 作为后端框架是因为它能极大地简化 Spring 应用的初始搭建和开发过程通过自动配置和起步依赖让我们能快速聚焦于业务逻辑本身而非繁琐的 XML 配置。结合 MyBatis-Plus、Spring Security、JWT 等生态组件可以高效、稳健地构建出符合生产要求的系统。2. 技术选型与开发环境准备工欲善其事必先利其器。一个清晰的技术栈和稳定的开发环境是项目成功的基石。2.1 核心技术栈后端框架Spring Boot 2.7.x (一个长期支持版本稳定且社区资源丰富)安全框架Spring Security JWT (用于认证与授权)ORM框架MyBatis-Plus 3.5.x (极大简化单表CRUD同时保留MyBatis的灵活性)数据库MySQL 8.0 (关系型数据库存储核心业务数据)缓存Redis (用于存储会话、验证码、热点数据)API文档Knife4j (Swagger的增强UI便于前后端协作与接口调试)项目构建Maven 3.6JDK版本Java 11 或 17 (LTS版本)2.2 开发环境与工具操作系统Windows 10/11, macOS 或 Linux 均可。IDEIntelliJ IDEA (社区版或旗舰版) 或 Eclipse with STS。数据库工具Navicat, DBeaver 或 MySQL Workbench。API测试工具Postman 或 Insomnia。2.3 初始化Spring Boot项目最快捷的方式是使用 Spring Initializr 生成项目骨架。我们选择以下依赖Spring Web(构建Web应用包含Tomcat)Spring Security(安全框架)MyBatis Framework(基础MyBatis)MySQL Driver(数据库连接)Lombok(简化实体类代码)生成并解压后用IDE打开项目。接下来我们需要手动在pom.xml中添加一些关键的依赖。!-- pom.xml 补充依赖 -- dependencies !-- 之前Initializr生成的依赖... -- !-- MyBatis-Plus 增强 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency !-- JWT 支持 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Knife4j API文档 -- dependency groupIdcom.github.xiaoymin/groupId artifactIdknife4j-spring-boot-starter/artifactId version3.0.3/version /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies3. 项目架构设计与核心配置良好的目录结构是代码可维护性的第一步。我们采用常见的分层架构。3.1 项目目录结构src/main/java/com/example/legalaid/ ├── LegalAidApplication.java # 启动类 ├── config/ # 配置类 │ ├── MybatisPlusConfig.java │ ├── RedisConfig.java │ ├── SecurityConfig.java │ └── SwaggerConfig.java ├── controller/ # 控制层接收请求 ├── service/ # 业务逻辑层 │ └── impl/ # 业务逻辑实现层 ├── mapper/ # 数据访问层 (MyBatis Mapper接口) ├── entity/ # 实体类与数据库表对应 ├── dto/ # 数据传输对象用于前后端交互 ├── vo/ # 视图对象用于接口返回 ├── common/ # 通用组件 │ ├── constant/ # 常量类 │ ├── exception/ # 自定义异常 │ ├── result/ # 统一响应封装 │ └── utils/ # 工具类 (如JWT工具) └── filter/ # 过滤器 (如JWT认证过滤器)3.2 数据库设计与核心实体我们设计几个核心表来支撑基本功能。这里以用户表、案件表为例。-- 用户表 (sys_user) CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 加密后的密码, real_name varchar(20) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, email varchar(50) DEFAULT NULL COMMENT 邮箱, avatar varchar(255) DEFAULT NULL COMMENT 头像, role_code varchar(20) NOT NULL COMMENT 角色编码 (ADMIN, LAWYER, CLERK, APPLICANT), status tinyint DEFAULT 1 COMMENT 状态 (0:禁用1:正常), create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 案件表 (legal_case) CREATE TABLE legal_case ( id bigint NOT NULL AUTO_INCREMENT COMMENT 案件ID, case_no varchar(50) NOT NULL COMMENT 案件编号, title varchar(200) NOT NULL COMMENT 案件标题, applicant_id bigint DEFAULT NULL COMMENT 申请人ID (关联sys_user.id), applicant_name varchar(50) DEFAULT NULL COMMENT 申请人姓名, case_type varchar(20) DEFAULT NULL COMMENT 案件类型 (民事、刑事、行政), content text COMMENT 案情描述, status varchar(20) DEFAULT PENDING COMMENT 状态 (PENDING:待受理, ACCEPTED:已受理, ASSIGNED:已指派, PROCESSING:办理中, CLOSED:已结案), assigned_lawyer_id bigint DEFAULT NULL COMMENT 指派律师ID, assigned_lawyer_name varchar(50) DEFAULT NULL COMMENT 指派律师姓名, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_case_no (case_no), KEY idx_applicant_id (applicant_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT法律援助案件表;对应的Java实体类使用Lombok简化代码// entity/SysUser.java package com.example.legalaid.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; Data TableName(sys_user) public class SysUser { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String realName; private String phone; private String email; private String avatar; private String roleCode; // 角色编码 private Integer status; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }3.3 关键配置文件配置是Spring Boot的灵魂。我们需要配置数据库、Redis以及MyBatis-Plus。# application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/legal_aid_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password redis: host: localhost port: 6379 database: 0 # password: 如果Redis有密码则配置 # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 global-config: db-config: id-type: auto logic-delete-field: deleted # 全局逻辑删除字段名如果表中有此字段 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath:mapper/*.xml # XML映射文件位置 # 自定义应用配置 legal-aid: jwt: secret: yourJwtSecretKeyHereMakeItLongAndComplex # JWT密钥务必复杂且保密 expiration: 86400000 # token有效期单位毫秒 (24小时)4. 核心功能模块实现接下来我们实现几个最核心的功能模块统一响应封装、用户登录认证JWT、以及案件管理的CRUD。4.1 统一响应结果封装为了规范接口返回格式我们创建一个通用的结果类。// common/result/Result.java package com.example.legalaid.common.result; import lombok.Data; import java.io.Serializable; Data public class ResultT implements Serializable { private Integer code; private String message; private T data; public static T ResultT success() { return success(null); } public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } // 可以定义更多便捷方法如 error(String message) }4.2 JWT工具类与登录逻辑首先创建JWT工具类用于生成和解析Token。// common/utils/JwtUtil.java package com.example.legalaid.common.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; Component public class JwtUtil { Value(${legal-aid.jwt.secret}) private String secret; Value(${legal-aid.jwt.expiration}) private Long expiration; // 生成Token public String generateToken(String username, String roleCode) { MapString, Object claims new HashMap(); claims.put(username, username); claims.put(role, roleCode); return Jwts.builder() .setClaims(claims) .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } // 从Token中解析用户名 public String getUsernameFromToken(String token) { return getClaimsFromToken(token).getSubject(); } // 从Token中解析角色 public String getRoleFromToken(String token) { return (String) getClaimsFromToken(token).get(role); } // 验证Token是否过期 public Boolean isTokenExpired(String token) { Date expiration getClaimsFromToken(token).getExpiration(); return expiration.before(new Date()); } private Claims getClaimsFromToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } }然后实现用户登录的Service和Controller。// dto/LoginDTO.java package com.example.legalaid.dto; import lombok.Data; import javax.validation.constraints.NotBlank; Data public class LoginDTO { NotBlank(message 用户名不能为空) private String username; NotBlank(message 密码不能为空) private String password; }// service/impl/AuthServiceImpl.java package com.example.legalaid.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.example.legalaid.common.utils.JwtUtil; import com.example.legalaid.dto.LoginDTO; import com.example.legalaid.entity.SysUser; import com.example.legalaid.mapper.SysUserMapper; import com.example.legalaid.service.AuthService; import com.example.legalaid.vo.LoginVO; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; Service RequiredArgsConstructor // Lombok注解自动注入final字段 public class AuthServiceImpl implements AuthService { private final SysUserMapper userMapper; private final JwtUtil jwtUtil; private final BCryptPasswordEncoder passwordEncoder new BCryptPasswordEncoder(); Override public LoginVO login(LoginDTO loginDTO) { // 1. 查询用户 SysUser user userMapper.selectOne(new LambdaQueryWrapperSysUser() .eq(SysUser::getUsername, loginDTO.getUsername())); if (user null) { throw new RuntimeException(用户名或密码错误); } // 2. 校验密码 (存储时应为加密后的密码) if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { throw new RuntimeException(用户名或密码错误); } // 3. 校验状态 if (user.getStatus() ! 1) { throw new RuntimeException(账号已被禁用请联系管理员); } // 4. 生成JWT Token String token jwtUtil.generateToken(user.getUsername(), user.getRoleCode()); // 5. 构造返回结果 LoginVO loginVO new LoginVO(); loginVO.setUserId(user.getId()); loginVO.setUsername(user.getUsername()); loginVO.setRealName(user.getRealName()); loginVO.setRoleCode(user.getRoleCode()); loginVO.setToken(token); return loginVO; } }// controller/AuthController.java package com.example.legalaid.controller; import com.example.legalaid.common.result.Result; import com.example.legalaid.dto.LoginDTO; import com.example.legalaid.service.AuthService; import com.example.legalaid.vo.LoginVO; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; 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; RestController RequestMapping(/api/auth) RequiredArgsConstructor public class AuthController { private final AuthService authService; PostMapping(/login) public ResultLoginVO login(Validated RequestBody LoginDTO loginDTO) { LoginVO loginVO authService.login(loginDTO); return Result.success(loginVO); } }4.3 案件管理CRUD与分页查询使用MyBatis-Plus可以快速实现基础数据操作。首先创建Mapper接口。// mapper/LegalCaseMapper.java package com.example.legalaid.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.legalaid.entity.LegalCase; import org.apache.ibatis.annotations.Param; public interface LegalCaseMapper extends BaseMapperLegalCase { // 自定义复杂分页查询示例根据状态和标题关键词查询 IPageLegalCase selectCasePage(PageLegalCase page, Param(status) String status, Param(keyword) String keyword); }对应的XML映射文件如果使用XML方式!-- resources/mapper/LegalCaseMapper.xml -- ?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.legalaid.mapper.LegalCaseMapper select idselectCasePage resultTypecom.example.legalaid.entity.LegalCase SELECT * FROM legal_case where if teststatus ! null and status ! AND status #{status} /if if testkeyword ! null and keyword ! AND (title LIKE CONCAT(%, #{keyword}, %) OR case_no LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY create_time DESC /select /mapper接着实现Service和Controller。// service/LegalCaseService.java package com.example.legalaid.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.example.legalaid.entity.LegalCase; import com.example.legalaid.dto.CaseQueryDTO; public interface LegalCaseService extends IServiceLegalCase { IPageLegalCase queryPage(CaseQueryDTO queryDTO); }// service/impl/LegalCaseServiceImpl.java package com.example.legalaid.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.legalaid.dto.CaseQueryDTO; import com.example.legalaid.entity.LegalCase; import com.example.legalaid.mapper.LegalCaseMapper; import com.example.legalaid.service.LegalCaseService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; Service RequiredArgsConstructor public class LegalCaseServiceImpl extends ServiceImplLegalCaseMapper, LegalCase implements LegalCaseService { private final LegalCaseMapper legalCaseMapper; Override public IPageLegalCase queryPage(CaseQueryDTO queryDTO) { PageLegalCase page new Page(queryDTO.getPageNum(), queryDTO.getPageSize()); return legalCaseMapper.selectCasePage(page, queryDTO.getStatus(), queryDTO.getKeyword()); } }// controller/LegalCaseController.java package com.example.legalaid.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.example.legalaid.common.result.Result; import com.example.legalaid.dto.CaseQueryDTO; import com.example.legalaid.entity.LegalCase; import com.example.legalaid.service.LegalCaseService; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/case) RequiredArgsConstructor public class LegalCaseController { private final LegalCaseService legalCaseService; PostMapping(/page) public ResultIPageLegalCase queryByPage(Validated RequestBody CaseQueryDTO queryDTO) { IPageLegalCase page legalCaseService.queryPage(queryDTO); return Result.success(page); } GetMapping(/{id}) public ResultLegalCase getDetail(PathVariable Long id) { LegalCase legalCase legalCaseService.getById(id); return Result.success(legalCase); } PostMapping public ResultVoid create(Validated RequestBody LegalCase legalCase) { // 业务逻辑生成案件编号、设置初始状态等 legalCase.setCaseNo(LA System.currentTimeMillis()); // 简单示例 legalCase.setStatus(PENDING); legalCaseService.save(legalCase); return Result.success(); } PutMapping public ResultVoid update(Validated RequestBody LegalCase legalCase) { legalCaseService.updateById(legalCase); return Result.success(); } DeleteMapping(/{id}) public ResultVoid delete(PathVariable Long id) { legalCaseService.removeById(id); return Result.success(); } }5. 集成Spring Security与JWT认证过滤器现在我们需要保护我们的API确保只有携带有效Token的请求才能访问受保护的资源。5.1 配置Spring Security创建一个Security配置类放行登录接口其他接口需要认证。// config/SecurityConfig.java package com.example.legalaid.config; import com.example.legalaid.filter.JwtAuthenticationFilter; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) // 启用方法级安全注解 RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http // 禁用CSRF因为使用JWT无状态认证 .csrf().disable() // 基于Token不需要Session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 公开接口允许匿名访问 .antMatchers(/api/auth/login, /doc.html, /webjars/**, /swagger-resources/**, /v2/api-docs).permitAll() // 其他所有请求都需要认证 .anyRequest().authenticated() .and() // 添加JWT过滤器 .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }5.2 实现JWT认证过滤器该过滤器会拦截请求从Header中提取Token并进行验证。// filter/JwtAuthenticationFilter.java package com.example.legalaid.filter; import com.example.legalaid.common.utils.JwtUtil; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; Component RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; private final UserDetailsService userDetailsService; // 需要实现这个Service来加载用户权限 Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authHeader request.getHeader(Authorization); String username null; String jwt null; if (authHeader ! null authHeader.startsWith(Bearer )) { jwt authHeader.substring(7); try { username jwtUtil.getUsernameFromToken(jwt); } catch (Exception e) { // Token解析失败可能是过期或伪造直接放行后续Security会处理为未认证 logger.error(JWT Token解析失败, e); } } if (username ! null SecurityContextHolder.getContext().getAuthentication() null) { // 从数据库或缓存加载用户信息这里简化为从数据库查 UserDetails userDetails this.userDetailsService.loadUserByUsername(username); // 验证Token有效性 if (jwtUtil.isTokenExpired(jwt)) { // Token已过期可以在这里抛出异常或返回特定响应 logger.warn(JWT Token已过期); } else { // Token有效设置认证信息到Security上下文 UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } }你需要实现一个UserDetailsService来根据用户名加载用户信息和权限角色。6. 集成Knife4j生成API文档清晰的API文档是前后端协作的桥梁。Knife4j的集成非常简单。// config/SwaggerConfig.java package com.example.legalaid.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; Configuration EnableSwagger2WebMvc public class SwaggerConfig { Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() // 指定扫描的包路径 .apis(RequestHandlerSelectors.basePackage(com.example.legalaid.controller)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(法律援助管理系统 API 文档) .description(基于Spring Boot MyBatis-Plus Spring Security的后端API) .contact(new Contact(开发者, , developerexample.com)) .version(1.0) .build(); } }启动应用后访问http://localhost:8080/doc.html即可看到美观的API文档界面。7. 常见问题与排查思路在开发过程中你可能会遇到以下典型问题问题现象可能原因排查思路与解决方案应用启动失败报DataSource相关错误1. 数据库连接URL、用户名、密码错误。2. MySQL服务未启动。3. 数据库驱动版本不匹配。1. 检查application.yml中的数据库配置。2. 使用客户端工具如Navicat测试连接。3. 确认MySQL版本与mysql-connector-java依赖版本兼容。调用接口返回403 Forbidden或401 Unauthorized1. 请求未携带Token或Token已过期。2. Token格式错误未以Bearer开头。3. 接口路径未被Security放行但用户权限不足。1. 检查请求头Authorization: Bearer your_token。2. 使用登录接口获取新Token。3. 检查SecurityConfig中的antMatchers配置和控制器上的PreAuthorize注解。MyBatis-Plus 插入/更新时自动填充字段如create_time不生效1. 实体类字段未加TableField(fill ...)注解。2. 未配置元对象处理器MetaObjectHandler。1. 确认实体类字段注解正确。2. 创建配置类实现MetaObjectHandler重写insertFill和updateFill方法。分页查询失效返回所有数据未添加MyBatis-Plus的分页插件配置。在MybatisPlusConfig配置类中注册PaginationInterceptor(3.4.x) 或MybatisPlusInterceptor(3.5.x) 分页插件。Knife4j 页面能打开但接口列表为空1.EnableSwagger2WebMvc注解缺失或位置不对。2.DocketBean中扫描的包路径不正确。1. 确保配置类有EnableSwagger2WebMvc。2. 检查RequestHandlerSelectors.basePackage是否指向了你的Controller包。JWT Token 解析失败报SignatureException1. 生成Token和解析Token使用的密钥(secret)不一致。2. Token被篡改。1. 确保application.yml中的legal-aid.jwt.secret在应用启动后没有变化。2. 密钥需要足够复杂且保密。8. 工程最佳实践与扩展建议完成基础功能后一个健壮的生产级系统还需要考虑更多。密码安全永远不要明文存储密码。使用BCryptPasswordEncoder进行哈希加密。在用户注册或修改密码时调用passwordEncoder.encode(rawPassword)进行加密后再存入数据库。接口幂等性对于创建、支付等关键接口需考虑幂等设计防止重复提交。可以使用Token机制或数据库唯一约束。数据权限控制不同角色的用户能看到的数据范围不同。例如律师只能看到指派给自己的案件。可以在Service层通过查询条件动态添加WHERE子句来实现。全局异常处理使用ControllerAdvice和ExceptionHandler定义全局异常处理器将各种异常如业务异常、参数校验异常、认证异常转换为统一的Result对象返回避免暴露服务器内部错误信息。日志记录使用SLF4J Logback记录详细的业务日志和操作日志便于问题追踪和审计。尤其要记录敏感操作如案件状态变更、用户权限修改。配置分离将application.yml拆分为application-dev.yml(开发)、application-test.yml(测试)、application-prod.yml(生产)通过spring.profiles.active指定激活的环境。API版本管理在URL路径如/api/v1/case或请求头中引入版本号为后续接口不兼容升级留有余地。前端分离部署本系统为纯后端API前端可使用Vue、React等框架独立开发通过Nginx进行反向代理和部署解决跨域问题。数据库优化为频繁查询的字段如status,create_time,assigned_lawyer_id建立索引。对于大数据量表考虑历史数据归档或分库分表。部署与监控使用Docker容器化部署配合Jenkins或GitLab CI/CD实现自动化构建和部署。集成Spring Boot Actuator和Prometheus、Grafana进行应用监控。从零到一构建一个完整的Spring Boot后端系统关键在于理解各组件Spring MVC, Security, MyBatis-Plus如何协同工作并遵循分层和模块化的设计思想。本文提供的代码和思路是一个坚实的起点你可以在此基础上根据具体的法律援助业务需求扩展如文件上传证据材料、消息通知短信/邮件、工作流引擎审批流程等更复杂的模块。动手将代码跑起来在调试和扩展中你会对Spring Boot生态有更深刻的理解。
返回列表