私人健身房管理系统

健身俱乐部管理系统

项目概述

项目名称 :健身俱乐部管理系统
项目类型 :前后端分离的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文档,方便团队协作
  • 参与代码审查,提升代码质量
相关推荐
拼命_小李2 小时前
使用intro.js实现简单下一步引导demo
javascript
hdsoft_huge2 小时前
Java 实现高效查询海量 geometry 及 Protobuf 序列化与天地图前端分片加载
java·前端·状态模式
xiaowu0802 小时前
IEnumerable、IEnumerator接口与yield return关键字的相关知识
java·开发语言·算法
笨手笨脚の2 小时前
深入理解 Java 虚拟机-01 JVM 内存模型
java·jvm··虚拟机栈·方法区
王家视频教程图书馆3 小时前
android java 开发网路请求库那个好用请列一个排行榜
android·java·开发语言
花卷HJ3 小时前
Android 文件工具类 FileUtils(超全封装版)
android·java
rchmin3 小时前
ThreadLocal内存泄漏机制解析
java·jvm·内存泄露
黎雁·泠崖3 小时前
Java 方法栈帧深度解析:从 JIT 汇编视角,打通 C 与 Java 底层逻辑
java·c语言·汇编
java资料站3 小时前
springBootAdmin(sba)
java