AI摘要

本文详细介绍了如何使用Spring Boot 3.3.5、MyBatis-Flex和SQLite构建零工平台后端系统。项目支持工作发布、申请匹配、用户管理、支付结算等业务场景,强调高性能、高可用和易扩展。文章涵盖了架构设计、技术选型、核心功能实现和生产部署,分享了JWT认证体系、统一结果封装、工作管理核心业务、文件上传服务等关键功能的实现细节,以及数据库设计、部署实战、监控与运维、性能优化等方面的经验总结。

本文将详细分享我使用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?

  1. Java 21支持: 享受最新的语言特性和性能优化
  2. 原生镜像: 支持GraalVM原生编译,启动更快
  3. 响应式编程: 内置WebFlux支持
  4. 可观测性: 增强的监控和链路追踪
  5. 安全性: 升级的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) { log.error("系统异常", e); return Result.error("系统繁忙,请稍后重试"); } @ExceptionHandler(MethodArgumentNotValidException.class) public Result<String> handleValidationException(MethodArgumentNotValidException e) { String message = e.getBindingResult().getFieldError().getDefaultMessage(); return Result.error(220, message); } @ExceptionHandler(IllegalArgumentException.class) public Result<String> handleIllegalArgumentException(IllegalArgumentException e) { return Result.error(220, e.getMessage()); } }

3. 工作管理核心业务

@RestController @RequestMapping("/api/jobs") @Slf4j public class JobsController { @Autowired private JobsService jobsService; // 获取工作列表(支持多条件筛选) @GetMapping("/latest/list") public Result<Map<String, Object>> getJobList( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword, @RequestParam(required = false) Long categoryId, @RequestParam(required = false) String location, @RequestParam(required = false) String salaryRange) { try { Page<Job> jobPage = jobsService.getJobList( page, size, keyword, categoryId, location, salaryRange ); Map<String, Object> result = new HashMap<>(); result.put("list", jobPage.getRecords()); result.put("total", jobPage.getTotal()); result.put("pages", jobPage.getPages()); result.put("current", jobPage.getCurrent()); return Result.success(result); } catch (Exception e) { log.error("获取工作列表失败", e); return Result.error("获取工作列表失败"); } } // 申请工作 @PostMapping("/apply/{jobId}") public Result<String> applyForJob(@PathVariable Long jobId) { try { LoginUser currentUser = UserContext.getCurrentUser(); // 获取工人资料 WorkerProfile profile = workerProfilesService.getByUserId(currentUser.getUserId()); if (profile == null) { return Result.error(220, "请先完善工人资料"); } // 检查是否已申请 boolean hasApplied = jobApplicationsService.hasApplied(jobId, profile.getId()); if (hasApplied) { return Result.error(220, "您已申请过该工作"); } // 创建申请记录 JobApplication application = new JobApplication(); application.setJobId(jobId); application.setWorkerId(profile.getId()); application.setStatus("PENDING"); application.setApplyTime(new Date()); jobApplicationsService.save(application); // 更新工作申请数量 jobsService.incrementApplyCount(jobId); return Result.success("申请成功"); } catch (Exception e) { log.error("申请工作失败, jobId: {}", jobId, e); return Result.error("申请失败,请稍后重试"); } } } // 业务服务实现 @Service @Transactional public class JobsServiceImpl implements JobsService { @Autowired private JobsMapper jobsMapper; @Override public Page<Job> getJobList(int page, int size, String keyword, Long categoryId, String location, String salaryRange) { QueryWrapper query = QueryWrapper.create() .select(JOB.ALL_COLUMNS) .from(JOB) .where(JOB.STATUS.eq("ACTIVE")); // 关键词搜索 if (StringUtils.hasText(keyword)) { query.and(JOB.TITLE.like(keyword) .or(JOB.DESCRIPTION.like(keyword)) .or(JOB.COMPANY_NAME.like(keyword))); } // 分类筛选 if (categoryId != null) { query.and(JOB.CATEGORY_ID.eq(categoryId)); } // 地区筛选 if (StringUtils.hasText(location)) { query.and(JOB.LOCATION.like(location)); } // 薪资范围筛选 if (StringUtils.hasText(salaryRange)) { applySalaryFilter(query, salaryRange); } query.orderBy(JOB.CREATE_TIME.desc()); return jobsMapper.paginate(page, size, query); } private void applySalaryFilter(QueryWrapper query, String salaryRange) { switch (salaryRange) { case "0-3000": query.and(JOB.SALARY_MIN.le(3000)); break; case "3000-5000": query.and(JOB.SALARY_MIN.ge(3000).and(JOB.SALARY_MAX.le(5000))); break; case "5000-8000": query.and(JOB.SALARY_MIN.ge(5000).and(JOB.SALARY_MAX.le(8000))); break; case "8000+": query.and(JOB.SALARY_MIN.ge(8000)); break; } } }

4. 文件上传服务

@RestController @RequestMapping("/api/upload") @Slf4j public class UploadController { @Value("${file.upload.dir:uploads}") private String uploadDir; @Value("${file.upload.max-size:10485760}") // 10MB private long maxFileSize; @PostMapping("/image") public Result<Map<String, String>> uploadImage(@RequestParam("file") MultipartFile file) { try { // 验证文件 if (file.isEmpty()) { return Result.error(220, "文件不能为空"); } if (file.getSize() > maxFileSize) { return Result.error(220, "文件大小不能超过10MB"); } // 验证文件类型 String contentType = file.getContentType(); if (!isValidImageType(contentType)) { return Result.error(220, "只支持 jpg、png、gif 格式的图片"); } // 生成文件名 String originalFilename = file.getOriginalFilename(); String extension = getFileExtension(originalFilename); String filename = generateFileName() + "." + extension; // 创建目录 Path uploadPath = Paths.get(uploadDir); if (!Files.exists(uploadPath)) { Files.createDirectories(uploadPath); } // 保存文件 Path filePath = uploadPath.resolve(filename); Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); // 返回访问URL String fileUrl = "/uploads/" + filename; Map<String, String> result = new HashMap<>(); result.put("url", fileUrl); result.put("filename", filename); return Result.success(result); } catch (Exception e) { log.error("文件上传失败", e); return Result.error("文件上传失败"); } } private boolean isValidImageType(String contentType) { return contentType != null && ( contentType.equals("image/jpeg") || contentType.equals("image/png") || contentType.equals("image/gif") ); } private String generateFileName() { return System.currentTimeMillis() + "_" + ThreadLocalRandom.current().nextInt(1000, 9999); } }

数据库设计

核心表结构

-- 用户表 CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, email VARCHAR(100), phone VARCHAR(20) NOT NULL, user_type VARCHAR(20) NOT NULL, -- 'employer' 或 'employee' avatar VARCHAR(255), status VARCHAR(20) DEFAULT 'ACTIVE', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 工作表 CREATE TABLE jobs ( id INTEGER PRIMARY KEY AUTOINCREMENT, title VARCHAR(200) NOT NULL, description TEXT, category_id INTEGER, employer_id INTEGER NOT NULL, company_name VARCHAR(100), location VARCHAR(100), salary_min DECIMAL(10,2), salary_max DECIMAL(10,2), salary_type VARCHAR(20), -- 'hourly', 'daily', 'monthly' requirements TEXT, contact_info VARCHAR(255), status VARCHAR(20) DEFAULT 'ACTIVE', apply_count INTEGER DEFAULT 0, view_count INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (employer_id) REFERENCES users(id), FOREIGN KEY (category_id) REFERENCES categories(id) ); -- 工作申请表 CREATE TABLE job_applications ( id INTEGER PRIMARY KEY AUTOINCREMENT, job_id INTEGER NOT NULL, worker_id INTEGER NOT NULL, status VARCHAR(20) DEFAULT 'PENDING', -- PENDING, APPROVED, REJECTED apply_time DATETIME DEFAULT CURRENT_TIMESTAMP, response_time DATETIME, employer_comment TEXT, FOREIGN KEY (job_id) REFERENCES jobs(id), FOREIGN KEY (worker_id) REFERENCES worker_profiles(id), UNIQUE(job_id, worker_id) ); -- 工人资料表 CREATE TABLE worker_profiles ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL UNIQUE, real_name VARCHAR(50), age INTEGER, gender VARCHAR(10), education VARCHAR(50), experience_years INTEGER, skills TEXT, -- JSON格式存储技能列表 resume TEXT, certification VARCHAR(255), location VARCHAR(100), available_time VARCHAR(255), hourly_rate DECIMAL(8,2), rating DECIMAL(3,2) DEFAULT 0, completed_jobs INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );

索引优化

-- 性能优化索引 CREATE INDEX idx_jobs_status_created ON jobs(status, created_at); CREATE INDEX idx_jobs_category_location ON jobs(category_id, location); CREATE INDEX idx_jobs_employer ON jobs(employer_id); CREATE INDEX idx_applications_job_worker ON job_applications(job_id, worker_id); CREATE INDEX idx_applications_status ON job_applications(status); CREATE INDEX idx_worker_profiles_user ON worker_profiles(user_id); CREATE INDEX idx_worker_profiles_location ON worker_profiles(location);

部署实战

1. 宝塔面板部署

创建部署脚本:

#!/bin/bash # deploy.sh - 自动化部署脚本 echo "=== 智慧零工平台后端部署脚本 ===" # 1. 环境检查 check_environment() { echo "检查部署环境..." # 检查Java版本 if ! java -version 2>&1 | grep "21\." > /dev/null; then echo "错误: 需要Java 21" exit 1 fi # 检查PM2 if ! command -v pm2 &> /dev/null; then echo "安装PM2..." npm install -g pm2 fi echo "环境检查完成" } # 2. 备份数据 backup_data() { echo "备份数据库..." if [ -f "/www/wwwroot/smartgigplatform.db" ]; then cp /www/wwwroot/smartgigplatform.db \ /www/wwwroot/backup/smartgigplatform_$(date +%Y%m%d_%H%M%S).db echo "数据库备份完成" fi } # 3. 部署应用 deploy_app() { echo "部署应用..." # 停止旧版本 pm2 stop smart-gig-platform 2>/dev/null || true # 部署新版本 cp smart-gig-platformbackend-0.0.1-SNAPSHOT.jar /www/wwwroot/ cp application-prod.properties /www/wwwroot/ cp ecosystem.config.js /www/wwwroot/ # 设置权限 chown www:www /www/wwwroot/smart-gig-platformbackend-0.0.1-SNAPSHOT.jar chmod 755 /www/wwwroot/smart-gig-platformbackend-0.0.1-SNAPSHOT.jar # 启动应用 cd /www/wwwroot pm2 start ecosystem.config.js echo "应用部署完成" } # 4. 健康检查 health_check() { echo "进行健康检查..." sleep 10 # 检查进程状态 if pm2 show smart-gig-platform | grep "online" > /dev/null; then echo "✅ 应用启动成功" else echo "❌ 应用启动失败" pm2 logs smart-gig-platform --lines 20 exit 1 fi # 检查API接口 if curl -s http://localhost:8081/actuator/health | grep "UP" > /dev/null; then echo "✅ API健康检查通过" else echo "❌ API健康检查失败" exit 1 fi } # 执行部署 main() { check_environment backup_data deploy_app health_check echo "=== 部署完成 ===" echo "应用状态: http://localhost:8081/actuator/health" echo "日志查看: pm2 logs smart-gig-platform" } main "$@"

2. PM2进程管理

// ecosystem.config.js module.exports = { apps: [{ name: 'smart-gig-platform', script: '/www/server/java/jdk-21.0.2/bin/java', args: [ '-server', '-Xms512m', '-Xmx1024m', '-Dspring.profiles.active=prod', '-Dfile.encoding=UTF-8', '-jar', 'smart-gig-platformbackend-0.0.1-SNAPSHOT.jar' ], cwd: '/www/wwwroot', instances: 1, autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production' }, log_file: '/www/wwwroot/logs/app.log', out_file: '/www/wwwroot/logs/app-out.log', error_file: '/www/wwwroot/logs/app-error.log', log_date_format: 'YYYY-MM-DD HH:mm:ss', merge_logs: true }] }

3. Nginx配置

# lgpt.ybyq.wang.conf server { listen 80; server_name lgpt.ybyq.wang; root /www/wwwroot/lgpt.ybyq.wang/h5; index index.html; # API代理 location /api/ { proxy_pass http://127.0.0.1:8081/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时设置 proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; # 缓冲设置 proxy_buffering on; proxy_buffer_size 8k; proxy_buffers 8 8k; } # 文件上传代理 location /uploads/ { alias /www/wwwroot/uploads/; expires 1y; add_header Cache-Control "public, immutable"; } # SPA路由支持 location / { try_files $uri $uri/ /index.html; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } # 安全头 add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; add_header X-XSS-Protection "1; mode=block"; # Gzip压缩 gzip on; gzip_vary on; gzip_min_length 1k; gzip_comp_level 6; gzip_types application/json application/javascript text/css text/javascript text/plain; # 访问日志 access_log /www/wwwlogs/lgpt.ybyq.wang.log; error_log /www/wwwlogs/lgpt.ybyq.wang.error.log; }

监控与运维

1. 应用监控

// 自定义健康检查 @Component public class CustomHealthIndicator implements HealthIndicator { @Autowired private DataSource dataSource; @Override public Health health() { try { // 检查数据库连接 try (Connection connection = dataSource.getConnection()) { boolean isValid = connection.isValid(5); if (isValid) { return Health.up() .withDetail("database", "可用") .withDetail("connection_pool", getConnectionPoolInfo()) .build(); } } } catch (Exception e) { return Health.down() .withDetail("database", "不可用") .withDetail("error", e.getMessage()) .build(); } return Health.down().build(); } private Map<String, Object> getConnectionPoolInfo() { Map<String, Object> info = new HashMap<>(); info.put("active", "查询活跃连接数"); info.put("idle", "查询空闲连接数"); return info; } } // 自定义指标 @Component public class BusinessMetrics { private final MeterRegistry meterRegistry; private final Counter jobCreatedCounter; private final Counter jobAppliedCounter; public BusinessMetrics(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.jobCreatedCounter = Counter.builder("jobs.created") .description("创建的工作数量") .register(meterRegistry); this.jobAppliedCounter = Counter.builder("jobs.applied") .description("申请的工作数量") .register(meterRegistry); } public void incrementJobCreated() { jobCreatedCounter.increment(); } public void incrementJobApplied() { jobAppliedCounter.increment(); } }

2. 日志管理

# application-prod.properties logging: level: com.example.backend: INFO org.springframework.web: WARN org.springframework.security: WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" file: name: /www/wwwroot/logs/application.log max-size: 100MB max-history: 30

3. 备份策略

#!/bin/bash # backup.sh - 数据备份脚本 BACKUP_DIR="/www/wwwroot/backup" DB_FILE="/www/wwwroot/smartgigplatform.db" UPLOAD_DIR="/www/wwwroot/uploads" # 创建备份目录 mkdir -p $BACKUP_DIR # 数据库备份 backup_database() { local timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="$BACKUP_DIR/smartgigplatform_$timestamp.db" echo "备份数据库..." cp $DB_FILE $backup_file gzip $backup_file echo "数据库备份完成: $backup_file.gz" } # 文件备份 backup_uploads() { local timestamp=$(date +%Y%m%d_%H%M%S) local backup_file="$BACKUP_DIR/uploads_$timestamp.tar.gz" echo "备份上传文件..." tar -czf $backup_file -C /www/wwwroot uploads/ echo "文件备份完成: $backup_file" } # 清理旧备份(保留7天) cleanup_old_backups() { echo "清理旧备份文件..." find $BACKUP_DIR -name "*.gz" -mtime +7 -delete echo "清理完成" } # 执行备份 main() { echo "=== 开始备份 $(date) ===" backup_database backup_uploads cleanup_old_backups echo "=== 备份完成 $(date) ===" } main

性能优化

1. 数据库查询优化

// 使用MyBatis-Flex的批量操作 @Service public class JobsBatchService { @Autowired private JobsMapper jobsMapper; // 批量插入工作 @Transactional public void batchCreateJobs(List<Job> jobs) { // 使用MyBatis-Flex的批量插入 jobsMapper.insertBatch(jobs); } // 批量更新状态 @Transactional public void batchUpdateStatus(List<Long> jobIds, String status) { UpdateWrapper updateWrapper = UpdateWrapper.create() .set(JOB.STATUS, status) .set(JOB.UPDATED_AT, new Date()) .where(JOB.ID.in(jobIds)); jobsMapper.update(null, updateWrapper); } // 分页查询优化 public Page<JobVO> getJobsWithDetails(int page, int size) { // 使用关联查询减少N+1问题 QueryWrapper query = QueryWrapper.create() .select( JOB.ALL_COLUMNS, CATEGORY.NAME.as("categoryName"), USER.USERNAME.as("employerName") ) .from(JOB) .leftJoin(CATEGORY).on(JOB.CATEGORY_ID.eq(CATEGORY.ID)) .leftJoin(USER).on(JOB.EMPLOYER_ID.eq(USER.ID)) .where(JOB.STATUS.eq("ACTIVE")) .orderBy(JOB.CREATE_TIME.desc()); return jobsMapper.paginate(page, size, query); } }

2. 缓存策略

// Redis缓存配置 @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { RedisCacheManager.Builder builder = RedisCacheManager .RedisCacheManagerBuilder .fromConnectionFactory(redisConnectionFactory()) .cacheDefaults(cacheConfiguration()); return builder.build(); } private RedisCacheConfiguration cacheConfiguration() { return RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) .serializeKeysWith(RedisSerializationContext.SerializationPair .fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer())); } } // 使用缓存 @Service public class JobsCacheService { @Cacheable(value = "jobs", key = "#jobId") public Job getJobById(Long jobId) { return jobsMapper.selectOneById(jobId); } @Cacheable(value = "job-list", key = "#page + '_' + #size + '_' + #keyword") public Page<Job> getJobList(int page, int size, String keyword) { // 查询逻辑 return jobsMapper.paginate(page, size, query); } @CacheEvict(value = "jobs", key = "#job.id") public void updateJob(Job job) { jobsMapper.update(job); } @CacheEvict(value = {"jobs", "job-list"}, allEntries = true) public void clearAllCache() { // 清空所有缓存 } }

开发经验总结

最佳实践

  1. 分层架构: 严格按照Controller-Service-Mapper分层
  2. 异常处理: 统一异常处理和错误码管理
  3. 参数校验: 使用@Valid注解进行参数验证
  4. 事务管理: 合理使用@Transactional注解
  5. 日志记录: 关键操作添加日志记录
  6. API文档: 使用Swagger生成API文档

踩过的坑

  1. SQLite并发问题: 使用连接池解决并发访问
  2. JWT安全性: 定期更换密钥,设置合理过期时间
  3. 文件上传: 限制文件大小和类型,防止恶意上传
  4. 数据库备份: 定期备份,避免数据丢失
  5. 性能优化: 合理使用索引,避免N+1查询问题

结语

通过这次Spring Boot 3 + MyBatis-Flex的实践,我深入体验了现代Java开发的便利性。项目从零到生产部署,积累了丰富的实战经验。

关键收获包括:

  • 现代化技术栈能显著提升开发效率
  • 合理的架构设计是项目成功的基石
  • 性能优化需要在开发过程中持续关注
  • 监控运维对生产环境稳定性至关重要

希望这篇文章能为Java后端开发者提供一些参考和启发。


项目链接:

欢迎交流讨论!邮箱: 676567473@qq.com

如果觉得我的文章对你有用,请随意赞赏
END
本文作者:
文章标题:智慧零工平台后端开发实战:SpringBoot + MyBatis-Flex 现代化架构
本文地址:https://blog.ybyq.wang/archives/543.html
版权说明:若无注明,本文皆Xuan's blog原创,转载请保留文章出处。