本文将详细分享我使用Spring Boot 3.3.5 + MyBatis-Flex + SQLite构建零工平台后端系统的完整经验,包含架构设计、技术选型、核心功能实现和生产部署。
项目背景
随着零工经济的快速发展,我决定开发一个现代化的零工服务平台。后端系统需要支持工作发布、申请匹配、用户管理、支付结算等复杂业务场景,同时要保证高性能、高可用和易扩展。
项目概览
- 项目名称: 智慧零工平台后端系统
- 技术栈: Spring Boot 3.3.5 + Java 21 + MyBatis-Flex + SQLite
- 项目地址: GitHub - Gigplatform_backend
- API文档: 24个业务模块,100+接口
- 部署方式: 宝塔面板 + PM2 + Nginx
技术选型分析
为什么选择Spring Boot 3?
- Java 21支持: 享受最新的语言特性和性能优化
- 原生镜像: 支持GraalVM原生编译,启动更快
- 响应式编程: 内置WebFlux支持
- 可观测性: 增强的监控和链路追踪
- 安全性: 升级的Spring Security
MyBatis-Flex vs MyBatis-Plus
经过对比,我选择了MyBatis-Flex:
// MyBatis-Flex 优势示例
// 1. 类型安全的查询
List<Job> jobs = queryWrapper
.select(JOB.ALL_COLUMNS)
.from(JOB)
.where(JOB.STATUS.eq("ACTIVE"))
.and(JOB.SALARY.ge(5000))
.orderBy(JOB.CREATE_TIME.desc())
.limit(10)
.list();
// 2. 智能字段映射
@Table("t_job")
public class Job {
@Id(keyType = KeyType.Auto)
private Long id;
@Column("job_title")
private String title;
// 自动处理驼峰命名
private String companyName; // -> company_name
}
SQLite的选择
对于中小型项目,SQLite具有独特优势:
- 零配置: 无需独立数据库服务器
- 高性能: 本地文件访问,减少网络开销
- 事务支持: 完整的ACID特性
- 跨平台: 易于开发和部署
- 成本低: 降低运维复杂度
架构设计
整体架构
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端应用 │ │ API网关 │ │ 负载均衡 │
│ (uni-app) │◄───│ (Nginx) │◄───│ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────────────┐
│ Spring Boot │
│ (业务逻辑层) │
└─────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 认证授权 │ │ 业务服务 │ │ 数据访问 │
│ (JWT) │ │ (Service) │ │ (MyBatis) │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────────┐
│ SQLite 数据库 │
└─────────────────┘
项目模块结构
com.example.backend/
├── controller/ # 控制器层
│ ├── AuthController # 认证授权
│ ├── JobsController # 工作管理
│ ├── UsersController # 用户管理
│ └── ... # 其他业务控制器
├── service/ # 业务逻辑层
│ ├── impl/ # 实现类
│ └── interfaces/ # 接口定义
├── mapper/ # 数据访问层
├── entity/ # 实体类
├── config/ # 配置类
├── common/ # 公共组件
│ ├── Result.java # 统一返回结果
│ ├── JwtUtil.java # JWT工具
│ └── JwtFilter.java # JWT过滤器
└── BackendApplication.java # 启动类
核心功能实现
1. JWT认证体系
@Component
public class JwtUtil {
@Value("${jwt.secret:default-secret}")
private String secret;
@Value("${jwt.expiration:86400}")
private Long expiration;
public String generateToken(String username) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Claims getClaimsFromToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null;
}
}
public boolean validateToken(String token) {
try {
Claims claims = getClaimsFromToken(token);
return claims != null && !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
}
// JWT过滤器
@Component
public class JwtFilter implements Filter {
@Autowired
private JwtUtil jwtUtil;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 跳过认证的路径
String path = httpRequest.getRequestURI();
if (isPublicPath(path)) {
chain.doFilter(request, response);
return;
}
String token = extractToken(httpRequest);
if (token == null || !jwtUtil.validateToken(token)) {
httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
httpResponse.getWriter().write(
JSON.toJSONString(Result.error(102, "未登录,请先登录"))
);
return;
}
// 设置用户信息到上下文
Claims claims = jwtUtil.getClaimsFromToken(token);
LoginUser loginUser = new LoginUser();
loginUser.setUsername(claims.getSubject());
UserContext.setCurrentUser(loginUser);
chain.doFilter(request, response);
}
}
2. 统一结果封装
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success() {
return new Result<>(0, "调用成功", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(0, "调用成功", data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> error(String message) {
return new Result<>(200, message, null);
}
}
// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<String> handleException(Exception e