个人技术博客——spring boot

222200228曹伟 2024-12-21 15:14:35
这个作业属于哪个课程FZU_SE_teacherW_4
这个作业要求在哪里软件工程实践总结&个人技术博客
这个作业的目标个人技术总结
其他参考文献构建之法

目录

  • 1.技术概括
  • 2.技术详述
  • 2.1 控制层(DietController)
  • 2.2 DietRecord
  • 2.3 DietRecordRepository
  • 2.4 JWTUtil
  • 2.5 NutritionCalculator
  • 2.6 UserProfile
  • 2.7 UserProfileRepository
  • 2.8 WebConfig
  • 3.问题与解决
  • 3.1 测试Vue项目时运行报错
  • 3.2 springboot项目依赖导入问题
  • 4.总结
  • 4.1后端技术栈
  • 4.1.1. Spring Boot
  • 4.1.2. Spring MVC
  • 4.1.3. Spring JDBC
  • 4.1.4. JWT(JSON Web Token)
  • 4.1.5. 日志框架 (SLF4J + Logback)
  • 4.1.6. Jackson
  • 4.1.7. Spring 的 CORS 配置
  • 4.2数据库
  • 4.2.1. MySQL
  • 4.3前端技术栈
  • 1. Vue.js
  • 2. Axios
  • 3. HTML + CSS
  • 4.4其他
  • 4.4.1. ObjectMapper
  • 4.4.2. 静态资源
  • 5.参考文献

1.技术概括

Spring Boot 是基于 Spring 框架的快速开发框架,旨在简化 Spring 应用的开发过程。它的核心优势在于 简化开发、提高效率,同时提供了生产级的功能和灵活的扩展性,是现代 Java 应用开发的首选框架。它特别适合快速开发和部署微服务架构的应用。

2.技术详述

2.1 控制层(DietController)

@GetMapping("/records")
public ResponseEntity<List<DietRecord>> getDietRecords(
        @RequestHeader(value = "Authorization", required = false) String token) {
    try {
        // 从token中获取用户ID
        String userId = getUserIdFromToken(token);
        if (userId == null) {
            log.warn("Unauthorized access attempt to get diet records");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        log.info("Fetching diet records for user: {}", userId);
        List<DietRecord> records = dietRecordRepository.findAllByUserId(userId);
        return ResponseEntity.ok(records);
    } catch (Exception e) {
        log.error("Error fetching diet records", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

@PostMapping("/record")
public ResponseEntity<?> addDietRecord(
        @RequestBody String jsonBody,
        @RequestHeader(value = "Authorization", required = false) String token) {
    try {
        // 从token中获取用户ID
        String userId = getUserIdFromToken(token);
        if (userId == null) {
            log.warn("Unauthorized access attempt to add diet record");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        log.info("Received diet record JSON for user {}: {}", userId, jsonBody);
        DietRecord dietRecord = objectMapper.readValue(jsonBody, DietRecord.class);
        dietRecord.setUserId(userId);

        DietRecord savedRecord = dietRecordRepository.addDietRecord(dietRecord);
        log.info("Successfully added diet record for user {}", userId);
        return ResponseEntity.ok(savedRecord);
    } catch (Exception e) {
        log.error("Error adding diet record", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error: " + e.getMessage());
    }
}

@GetMapping("/nutrition-needs")
public ResponseEntity<?> getNutritionNeeds(
        @RequestHeader(value = "Authorization", required = false) String token) {
    try {
        // 从token中获取用户ID
        String userId = getUserIdFromToken(token);
        if (userId == null) {
            log.warn("Unauthorized access attempt to get nutrition needs");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        log.info("Fetching nutrition needs for user: {}", userId);
        UserProfile userProfile = userProfileRepository.getUserProfileByUserId(userId);
        if (userProfile == null) {
            log.warn("User profile not found for user: {}", userId);
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body("User profile not found");
        }

        NutritionCalculator.NutritionNeeds needs = nutritionCalculator.calculateNutritionNeeds(
                userProfile.getWeight(),
                userProfile.getHeight(),
                userProfile.getAge(),
                userProfile.getGender(),
                "中度活动",  // 这里可以根据实际需求从请求参数获取或从用户配置中获取
                "保持体重"   // 同上
        );
        return ResponseEntity.ok(needs);
    } catch (Exception e) {
        log.error("Error calculating nutrition needs", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Error: " + e.getMessage());
    }
}

/**
 * 从token中解析用户ID
 * @param token Bearer token
 * @return 用户ID,如果token无效则返回null
 */
private String getUserIdFromToken(String token) {
    try {
        if (token == null || !token.startsWith("Bearer ")) {
            log.debug("Invalid token format");
            return null;
        }

        // 移除"Bearer "前缀
        token = token.substring(7);
        Claims claims = JwtUtil.fromToken(token);

        if (claims == null) {
            log.warn("Failed to parse token claims");
            return null;
        }

        String userId = claims.get("user_id", String.class);
        log.debug("Successfully extracted user_id from token: {}", userId);
        return userId;
    } catch (Exception e) {
        log.error("Error parsing token", e);
        return null;
    }
}

定义了实际页面中按下不同按钮对应调用的方法

2.2 DietRecord

 public Integer getDietId() {
    return dietId;
}

public void setDietId(Integer dietId) {
    this.dietId = dietId;
}

public String getUserId() {
    return userId;
}
//后续相似代码省略

提供了后续代码逻辑中需要用到的字段的获取方法

2.3 DietRecordRepository

@Repository
public class DietRecordRepository {

@Autowired
private JdbcTemplate jdbcTemplate;

public DietRecord addDietRecord(DietRecord record) {
    String sql = "INSERT INTO diet_record (user_id, food_name, calories, meal_time, meal_type) VALUES (?, ?, ?, ?, ?)";
    KeyHolder keyHolder = new GeneratedKeyHolder();

    jdbcTemplate.update(connection -> {
        PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        ps.setString(1, record.getUserId());
        ps.setString(2, record.getFoodName());
        ps.setBigDecimal(3, record.getCalories());
        ps.setTimestamp(4, Timestamp.from(record.getMealTime().toInstant()));
        ps.setString(5, record.getMealType());
        return ps;
    }, keyHolder);

    int generatedId = keyHolder.getKey().intValue();
    record.setDietId(generatedId);
    return record;
}

public List<DietRecord> findAllByUserId(String userId) {
    String sql = "SELECT * FROM diet_record WHERE user_id = ? ORDER BY meal_time DESC";
    return jdbcTemplate.query(sql, new Object[]{userId}, (rs, rowNum) -> mapRowToDietRecord(rs));
}

private DietRecord mapRowToDietRecord(ResultSet rs) throws SQLException {
    DietRecord record = new DietRecord();
    record.setDietId(rs.getInt("diet_id"));
    record.setUserId(rs.getString("user_id"));
    record.setFoodName(rs.getString("food_name"));
    record.setCalories(rs.getBigDecimal("calories"));
    record.setMealTime(rs.getTimestamp("meal_time").toInstant().atOffset(ZoneOffset.UTC));
    record.setMealType(rs.getString("meal_type"));
    record.setCreatedAt(rs.getTimestamp("created_at").toInstant().atOffset(ZoneOffset.UTC));
    record.setUpdatedAt(rs.getTimestamp("updated_at").toInstant().atOffset(ZoneOffset.UTC));
    return record;
}

}
提供了将相关数据插入数据库对应表的操作

2.4 JWTUtil

package com.example.health;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
public class JwtUtil {
private static final String SECRET_KEY = "d8c986df-8512-42b5-906f-eeea9b3acf86";
private static final SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8));

public static Claims fromToken(String token) {
    try {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    } catch (Exception e) {
        return null;
    }
}

处理 JWT以及token

2.5 NutritionCalculator

@Component
public class NutritionCalculator {
public static class NutritionNeeds {
    private double calories;
    private double protein;
    private double carbs;
    private double fat;

    // Getter和Setter方法
    public double getCalories() { return calories; }
    public void setCalories(double calories) { this.calories = calories; }

    public double getProtein() { return protein; }
    public void setProtein(double protein) { this.protein = protein; }

    public double getCarbs() { return carbs; }
    public void setCarbs(double carbs) { this.carbs = carbs; }

    public double getFat() { return fat; }
    public void setFat(double fat) { this.fat = fat; }
}

public NutritionNeeds calculateNutritionNeeds(double weight, double height, int age, String gender, String activityLevel, String goal) {
    double bmr = calculateBMR(gender, weight, height, age);
    double tdee = calculateTDEE(bmr, activityLevel);
    return calculateDailyNutritionNeeds(tdee, goal, weight);
}

private double calculateBMR(String gender, double weight, double height, int age) {
    if (gender.equalsIgnoreCase("男")) {
        return 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age);
    } else {
        return 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age);
    }
}

private double calculateTDEE(double bmr, String activityLevel) {
    double activityMultiplier = getActivityMultiplier(activityLevel);
    return bmr * activityMultiplier;
}

private NutritionNeeds calculateDailyNutritionNeeds(double tdee, String goal, double weight) {
    NutritionNeeds needs = new NutritionNeeds();
    double adjustedCalories = adjustCaloriesForGoal(tdee, goal);
    needs.setCalories(adjustedCalories);
    needs.setProtein(weight * 2.2); // 每公斤体重2.2克蛋白质
    needs.setCarbs(adjustedCalories * 0.45 / 4); // 45%的卡路里来自碳水,1克碳水4卡路里
    needs.setFat(adjustedCalories * 0.25 / 9); // 25%的卡路里来自脂肪,1克脂肪9卡路里
    return needs;
}

private double getActivityMultiplier(String activityLevel) {
    switch (activityLevel.toLowerCase()) {
        case "久坐":
            return 1.2;
        case "轻度活动":
            return 1.375;
        case "中度活动":
            return 1.55;
        case "高度活动":
            return 1.725;
        case "非常活跃":
            return 1.9;
        default:
            return 1.2;
    }
}

private double adjustCaloriesForGoal(double tdee, String goal) {
    switch (goal.toLowerCase()) {
        case "减重":
            return tdee - 500; // 每天减少500卡路里
        case "增重":
            return tdee + 500; // 每天增加500卡路里
        case "保持体重":
        default:
            return tdee;
    }
}

包含bmr、每日营养需求、每日能量消耗的计算逻辑

2.6 UserProfile

public class UserProfile {
private String userId;
private String name;
private String gender;
private LocalDate birthday;
private double height;
private double weight;

public UserProfile(String userId, String name, String gender, LocalDate birthday, double height, double weight) {
    this.userId = userId;
    this.name = name;
    this.gender = gender;
    this.birthday = birthday;
    this.height = height;
    this.weight = weight;
}

public String getUserId() { return userId; }
public String getName() { return name; }
public String getGender() { return gender; }
public LocalDate getBirthday() { return birthday; }
public double getHeight() { return height; }
public double getWeight() { return weight; }

public int getAge() {
    return Period.between(birthday, LocalDate.now()).getYears();
}

提供了后续代码逻辑中需要用到的用户数据相关的获取方法

2.7 UserProfileRepository

@Repository
public class UserProfileRepository {

private final JdbcTemplate jdbcTemplate;

@Autowired
public UserProfileRepository(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}

public UserProfile getUserProfileByUserId(String userId) {
    String sql = "SELECT * FROM user_profile WHERE user_id = ?";
    try {
        return jdbcTemplate.queryForObject(sql, new Object[]{userId}, (rs, rowNum) -> {
            LocalDate birthday = null;
            Date birthdayDate = rs.getDate("birthday");
            if (birthdayDate != null) {
                birthday = birthdayDate.toLocalDate();
            }

            return new UserProfile(
                    rs.getString("user_id"),
                    rs.getString("name"),
                    rs.getString("gender"),
                    birthday,
                    rs.getDouble("height"),
                    rs.getDouble("weight")
            );
        });
    } catch (EmptyResultDataAccessException e) {
        return null;
    } catch (Exception e) {
        // 捕获其他异常
        e.printStackTrace(); // 或者使用日志记录错误
        return null;
    }
}

从数据库中获取相关用户的基础数据并保存到一个对象中

2.8 WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowedOrigins(
                    "http://123.60.177.200:8083", // 登录系统域名
                    "http://localhost:8084"        // 本地开发环境
            )
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("*")
            .allowCredentials(true)
            .maxAge(3600);
}
}

用于配置跨域访问规则

3.问题与解决

3.1 测试Vue项目时运行报错

报错:Syntax Error:TypeError: ICannot read properties of undefined (reading:'parseComponent')
解决:命令行输入npm add vue-template-compiler

3.2 springboot项目依赖导入问题

报错:无法识别某某字段
解决:pom.xml中依赖未加载或依赖版本不正确,首先解决网络问题,更改maven为国内镜像,找到maven的settings.xml文件,找到 标签,添加或替换如阿里云镜像:

img

随后从新加载依赖,一般就可以解决这个问题,若还未解决,可以手动从官网下载对应依赖并添加

4.总结

4.1后端技术栈

4.1.1. Spring Boot

用于快速构建基于 Spring 的独立、生产级应用。提供自动化配置,减少开发工作量。内置嵌入式服务器(如 Tomcat),无需额外配置。生态体系完善,支持多种扩展。

4.1.2. Spring MVC

提供 RESTful API 的开发框架。简化了 HTTP 请求的处理和响应。提供注解支持(如 @RestController, @RequestMapping),代码更简洁。

4.1.3. Spring JDBC

用于与数据库交互,执行 SQL 查询和更新。提供模板化的数据库操作(如 JdbcTemplate),简化了数据库访问代码。自动管理资源(如连接关闭),减少内存泄漏风险。

4.1.4. JWT(JSON Web Token)

用于用户身份验证,解析和生成用户令牌。无需服务器存储会话信息,减少存储开销。提高安全性,支持跨平台认证。

4.1.5. 日志框架 (SLF4J + Logback)

用于记录系统运行时的日志信息。便于调试和问题排查。支持日志级别控制(如 INFO、WARN、ERROR)。

4.1.6. Jackson

用于 JSON 数据的序列化和反序列化。高效处理 JSON 数据。支持复杂对象的映射。

4.1.7. Spring 的 CORS 配置

允许跨域请求。支持前后端分离的开发模式。提供灵活的跨域策略配置。

4.2数据库

4.2.1. MySQL

性能优异,适合中小型项目。社区支持丰富,生态完善。

4.3前端技术栈

1. Vue.js

构建动态的、响应式的单页面应用 (SPA)。数据绑定简单,组件化开发高效。支持 MVVM 模型,视图和数据逻辑分离。

2. Axios

处理 HTTP 请求,用于与后端 API 通信。提供更简洁的 API 调用方式。支持请求拦截器和响应拦截器。

3. HTML + CSS

构建页面结构和样式。使用现代化的 CSS(如 Flexbox 和 Grid)创建响应式布局。加载 Google 字体和自定义样式,提升用户体验。

4.4其他

4.4.1. ObjectMapper

在后端将 JSON 转换为 Java 对象,或将 Java 对象转换为 JSON。简化数据格式转换。减少手动解析 JSON 的复杂性。

4.4.2. 静态资源

通过 index.html 提供用户界面。使用 Vue.js 和 Axios 实现动态交互。提供直观的用户体验。

5.参考文献

springboot官方文档
SpringBoot(看这一篇就够了)
【如何修改配置Maven镜像?】
CSS彈性盒子用法

...全文
81 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

239

社区成员

发帖
与我相关
我的任务
社区管理员
  • FZU_SE_teacherW
  • 助教赖晋松
  • D's Honey
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧