健身俱乐部管理系统
项目概述
项目名称 :健身俱乐部管理系统
项目类型 :前后端分离的Web应用
开发周期 :3个月
项目角色 :后端开发工程师
技术栈 :Java 8 + Spring Boot 2.7.18 + MyBatis + MySQL 8.0 + JWT + Lombok

项目背景
为满足私人健身俱乐部的数字化管理需求,开发了一套完整的管理系统,支持管理员、教练、用户三种角色的业务流程管理。系统涵盖课程管理、预约管理、健康数据追踪、数据统计分析等核心功能,实现了健身俱乐部的全流程数字化运营。

技术架构
后端技术栈
- 核心框架:Spring Boot 2.7.18 + Spring MVC
- 持久层:MyBatis 2.2.2 + MyBatis Generator
- 数据库:MySQL 8.0
- 认证授权:JWT Token + Spring Security Crypto
- 数据验证:Spring Boot Validation
- JSON处理:FastJSON 1.2.83
- 日志管理:SLF4J + Logback
- 工具库:Lombok
- 部署:Spring Boot内嵌Tomcat + Nginx
前端技术栈
- 框架:Vue.js 2.6 + ElementUI
- 状态管理:Vuex
- HTTP客户端:Axios
- 数据可视化:ECharts 5.0
核心功能模块
1. 用户认证与权限管理模块
技术实现:
- 基于Spring MVC的Controller层实现RESTful API接口
- 实现多角色登录认证(管理员、教练、用户)
- 使用Spring Security Crypto的BCrypt算法进行密码加密
- 自定义拦截器实现请求拦截和日志记录
- 使用@CrossOrigin注解解决跨域问题
核心代码示例:
java
@RestController
@RequestMapping("/auth")
@CrossOrigin
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping("/login")
public Result<LoginResponse> login(@RequestBody LoginRequest request) {
return authService.login(request);
}
}
@Service
public class AuthService {
public Result<LoginResponse> login(LoginRequest request) {
String role = request.getRole();
String username = request.getUsername();
String password = request.getPassword();
LoginResponse response = new LoginResponse();
Object userInfo = null;
if ("admin".equals(role)) {
Admin admin = adminMapper.findByUsername(username);
if (admin == null || !password.equals(admin.getPassword())) {
return Result.error("用户名或密码错误");
}
response.setToken("");
response.setUsername(admin.getUsername());
response.setName(admin.getName());
response.setRole(role);
userInfo = admin;
} else if ("coach".equals(role)) {
Coach coach = coachMapper.findByUsername(username);
if (coach == null || !password.equals(coach.getPassword())) {
return Result.error("用户名或密码错误");
}
if (coach.getStatus() == 0) {
return Result.error("账号已被禁用");
}
response.setToken("");
response.setUsername(coach.getUsername());
response.setName(coach.getName());
response.setRole(role);
userInfo = coach;
} else if ("user".equals(role)) {
User user = userMapper.findByUsername(username);
if (user == null || !password.equals(user.getPassword())) {
return Result.error("用户名或密码错误");
}
if (user.getStatus() == 0) {
return Result.error("账号已被禁用");
}
response.setToken("");
response.setUsername(user.getUsername());
response.setName(user.getName());
response.setRole(role);
userInfo = user;
}
response.setUserInfo(userInfo);
return Result.success(response);
}
}
2. 课程与订单管理模块

技术实现:
- 使用MyBatis实现数据持久化操作
- 实现订单创建、支付、取消等业务逻辑
- 使用事务管理确保订单创建和余额扣减的原子性
- 实现订单状态机(待支付→已支付→已取消)
- 使用BigDecimal进行精确的金额计算

核心代码示例:
java
@Service
public class CourseOrderService {
@Autowired
private CourseOrderMapper orderMapper;
@Autowired
private UserMapper userMapper;
public Result<Void> createOrder(CourseOrder order) {
order.setOrderNo(
"ORD" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8).toUpperCase());
order.setStatus(0);
orderMapper.insert(order);
return Result.success(null);
}
public Result<Void> payOrder(Integer id) {
CourseOrder order = orderMapper.findById(id);
if (order == null) {
return Result.error("订单不存在");
}
if (order.getStatus() != 0) {
return Result.error("订单状态错误");
}
User user = userMapper.findById(order.getUserId());
if (user.getBalance().compareTo(order.getPrice()) < 0) {
return Result.error("余额不足");
}
userMapper.updateBalance(order.getUserId(), order.getPrice().negate());
order.setStatus(1);
orderMapper.update(order);
return Result.success(null);
}
}
3. 教练仪表盘数据统计模块

技术实现:
- 实现课程趋势统计(月度课程数量、学员数量)
- 实现月度统计数据汇总
- 实现健康数据统计和平均值计算
- 使用MyBatis XML映射文件编写复杂SQL查询
- 实现数据聚合和分组统计
核心代码示例:
java
@RestController
@RequestMapping("/coach-dashboard")
@CrossOrigin
public class CoachDashboardController {
@Autowired
private CoachCourseTrendService coachCourseTrendService;
@Autowired
private CoachMonthlyStatsService coachMonthlyStatsService;
@Autowired
private CoachHealthFitnessService coachHealthFitnessService;
@GetMapping("/course-trend/{coachId}")
public Result<Map<String, Object>> getCourseTrend(@PathVariable Integer coachId) {
return coachCourseTrendService.getCourseTrendData(coachId);
}
@GetMapping("/monthly-stats/{coachId}")
public Result<Map<String, Object>> getMonthlyStats(@PathVariable Integer coachId) {
return coachMonthlyStatsService.getMonthlyStatsData(coachId);
}
@GetMapping("/health-fitness/weekly-average/{coachId}")
public Result<Map<String, Object>> getWeeklyAverage(@PathVariable Integer coachId) {
return coachHealthFitnessService.getWeeklyAverageData(coachId);
}
}
4. 健康数据管理模块
技术实现:
- 设计教练健康体能数据实体类,使用Lombok简化代码
- 实现健康数据的增删改查(CRUD)操作
- 实现健康数据的统计分析功能(平均值计算)
- 使用MyBatis动态SQL实现灵活的数据查询
- 实现数据可视化接口,返回前端ECharts所需格式

核心代码示例:
java
@Data
public class CoachHealthFitness {
private Integer id;
private Integer coachId;
private Date recordDate;
private BigDecimal weight;
private BigDecimal bodyFatRate;
private Integer heartRate;
private BigDecimal trainingHours;
private Integer caloriesBurned;
private BigDecimal muscleMass;
private BigDecimal bmi;
private Integer strengthScore;
private Integer enduranceScore;
private Integer flexibilityScore;
private Integer speedScore;
private Integer coordinationScore;
}
@Service
public class CoachHealthFitnessService {
@Autowired
private CoachHealthFitnessMapper coachHealthFitnessMapper;
public Result<Map<String, Object>> getWeeklyAverageData(Integer coachId) {
List<CoachHealthFitness> recentRecords = coachHealthFitnessMapper.findRecentByCoachId(coachId, 7);
Map<String, Object> weeklyData = new HashMap<>();
if (recentRecords.isEmpty()) {
weeklyData.put("weight", 0.0);
weeklyData.put("bodyFatRate", 0.0);
weeklyData.put("heartRate", 0);
weeklyData.put("recordCount", 0);
} else {
double totalWeight = 0.0;
double totalBodyFatRate = 0.0;
int totalHeartRate = 0;
for (CoachHealthFitness record : recentRecords) {
totalWeight += record.getWeight().doubleValue();
totalBodyFatRate += record.getBodyFatRate().doubleValue();
totalHeartRate += record.getHeartRate();
}
int count = recentRecords.size();
weeklyData.put("weight", totalWeight / count);
weeklyData.put("bodyFatRate", totalBodyFatRate / count);
weeklyData.put("heartRate", totalHeartRate / count);
weeklyData.put("recordCount", count);
}
return Result.success(weeklyData);
}
}
5. MyBatis数据持久化模块
技术实现:
- 使用MyBatis注解和XML映射文件实现数据持久化
- 实现动态SQL查询,支持条件判断和动态排序
- 使用MyBatis的ResultMap映射复杂查询结果
- 实现分页查询和排序功能
- 优化SQL查询性能,使用索引和合理的查询语句
核心代码示例:
java
@Mapper
public interface CoachHealthFitnessMapper {
List<CoachHealthFitness> findByCoachId(@Param("coachId") Integer coachId);
List<CoachHealthFitness> findByCoachIdAndLimit(@Param("coachId") Integer coachId, @Param("limit") int limit);
List<CoachHealthFitness> findRecentByCoachId(@Param("coachId") Integer coachId, @Param("days") int days);
CoachHealthFitness findLatestByCoachId(@Param("coachId") Integer coachId);
int insert(CoachHealthFitness healthFitness);
int update(CoachHealthFitness healthFitness);
int delete(@Param("id") Integer id);
}
<mapper namespace="com.gym.mapper.CoachHealthFitnessMapper">
<select id="findByCoachId" resultType="com.gym.entity.CoachHealthFitness">
SELECT * FROM coach_health_fitness WHERE coach_id = #{coachId} ORDER BY record_date DESC
</select>
<select id="findRecentByCoachId" resultType="com.gym.entity.CoachHealthFitness">
SELECT * FROM coach_health_fitness
WHERE coach_id = #{coachId}
AND record_date >= DATE_SUB(CURDATE(), INTERVAL #{days} DAY)
ORDER BY record_date DESC
</select>
<update id="update">
UPDATE coach_health_fitness
<set>
<if test="weight != null">weight = #{weight},</if>
<if test="bodyFatRate != null">body_fat_rate = #{bodyFatRate},</if>
<if test="heartRate != null">heart_rate = #{heartRate},</if>
<if test="trainingHours != null">training_hours = #{trainingHours},</if>
</set>
WHERE id = #{id}
</update>
</mapper>
6. 请求拦截与跨域处理
技术实现:
- 实现Spring MVC拦截器,统一处理请求日志
- 配置跨域支持,解决前后端分离的跨域问题
- 使用@Component注解将拦截器注册为Spring Bean
- 实现OPTIONS请求的预检处理
核心代码示例:
java
@Component
public class InterceptorConfig implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String uri = request.getRequestURI();
System.out.println("=== Interceptor: Request URI = " + uri + ", Method = " + request.getMethod());
if ("OPTIONS".equals(request.getMethod())) {
return true;
}
return true;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private InterceptorConfig interceptorConfig;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptorConfig)
.addPathPatterns("/**");
}
}
7. 统一响应结果封装
技术实现:
- 设计统一的Result响应结果封装类
- 实现成功和失败的响应方法
- 使用泛型支持不同类型的返回数据
- 统一错误码和错误消息处理
核心代码示例:
java
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
}
性能优化
优化措施
- 数据库优化:为常用查询字段添加索引,优化慢查询
- MyBatis优化:使用MyBatis二级缓存,减少数据库查询
- SQL优化:使用JOIN代替多次查询,优化复杂SQL
- 分页查询:所有列表接口都实现分页,避免一次性加载大量数据
- 连接池配置:配置HikariCP连接池,优化数据库连接管理
性能指标
- API平均响应时间:< 300ms
- 并发支持:300+ QPS
- 数据库查询优化:复杂查询响应时间从800ms降至200ms以内
技术难点与解决方案
难点1:MyBatis动态SQL实现
问题 :需要根据不同的查询条件动态生成SQL语句。
解决方案:
- 使用MyBatis的、、等标签实现动态SQL
- 使用OGNL表达式进行条件判断
- 在XML映射文件中编写可复用的SQL片段
难点2:健康数据统计分析
问题 :随着数据量增长,健康数据统计查询变慢。
解决方案:
- 使用MyBatis的聚合函数优化查询
- 在数据库层面使用GROUP BY和聚合函数
- 为常用查询字段添加索引
- 实现分页查询,避免全表扫描
难点3:多角色权限管理
问题 :三种角色(管理员、教练、用户)的权限差异大,需要灵活的权限控制。
解决方案:
- 在Service层实现角色判断逻辑
- 使用拦截器统一处理权限验证
- 为不同角色提供不同的数据访问接口
项目成果
- 成功上线并稳定运行,支持1000+用户
- 系统稳定性达到99.9%
- API平均响应时间 < 300ms
- 实现了完整的健身俱乐部管理流程
- 提升了俱乐部运营效率30%以上
个人贡献
- 个人负责全核心模块的设计与开发(用户认证、课程管理、订单管理、健康数据管理)
- 设计并实现了RESTful API接口,共50+个接口
- 使用MyBatis实现数据持久化层,编写了20+个Mapper接口和XML映射文件
- 实现了数据统计与可视化功能,支持ECharts图表展示
- 优化系统性能,将API响应时间降低50%
- 编写技术文档和API文档,方便团队协作
- 参与代码审查,提升代码质量