1. 整体架构设计

1.1 前后端分离架构

健康管理系统采用前后端分离的架构设计:

后端

前端

HTTP/REST + JWT

Views
视图层

Components
组件层

Services
API封装层

Axios HTTP Client
拦截器 + 错误处理 + JWT

Controller
控制层

Service
业务层

Mapper
数据层

MySQL Database

1.2 数据流设计

用户操作 → Vue 组件 → Service 层 → Axios 请求 → 后端 Controller
                                              ↓
用户界面 ← 状态更新 ← 响应处理 ← 数据返回 ← Service 层

2. API 设计规范

2.1 RESTful API 设计原则

系统遵循 RESTful API 设计规范:

HTTP 方法 用途 示例
GET 获取资源 GET /api/v1/users/info
POST 创建资源 POST /api/v1/food-records
PUT 更新资源 PUT /api/v1/users/update
DELETE 删除资源 DELETE /api/v1/food-records/123

2.2 统一响应格式

所有 API 返回统一的响应结构:

{
  "success": true,
  "message": "操作成功",
  "data": { }
}

响应字段说明

  • success: 操作是否成功(boolean)
  • message: 提示信息(string)
  • data: 返回数据(object/array,可选)

2.3 API 版本控制

API 路径包含版本号,便于后续升级:

/api/v1/users/login      # 当前版本
/api/v2/users/login      # 未来新版本

3. 认证与授权机制

3.1 JWT Token 认证

系统使用 JWT(JSON Web Token)进行身份认证:

认证流程

1. 用户登录
   POST /api/v1/users/login
   { username: "xxx", password: "xxx", captcha: "xxx" }

2. 后端验证成功,返回 Token
   { success: true, data: { token: "eyJhbG..." } }

3. 前端存储 Token(localStorage)

4. 后续请求携带 Token
   Authorization: Bearer eyJhbG...

5. 后端验证 Token 有效性

后端 JWT 工具类JwtUtil.java):

@Component
public class JwtUtil {
    // 使用 HS256 算法生成密钥
    private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

    /**
     * 生成 JWT Token
     * @param username 用户名
     * @return JWT 令牌字符串
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", username);
        
        return Jwts.builder()
                .setClaims(claims)           // 设置自定义声明
                .setSubject(username)        // 设置主题(用户名)
                .setIssuedAt(new Date())     // 设置签发时间
                .setExpiration(new Date(System.currentTimeMillis() + expirationTime))  // 设置过期时间
                .signWith(key)               // 使用密钥签名
                .compact();                  // 生成令牌
    }

    /**
     * 从 Token 中提取用户名
     * @param token JWT 令牌
     * @return 用户名
     */
    public String extractUsername(String token) {
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key)          // 设置验证密钥
                .build()
                .parseClaimsJws(token)       // 解析令牌
                .getBody();                   // 获取声明体
        return claims.getSubject();
    }

    /**
     * 验证 Token 是否有效
     * @param token JWT 令牌
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;  // 解析成功,令牌有效
        } catch (Exception e) {
            return false; // 解析失败,令牌无效或过期
        }
    }
}

3.2 前端请求拦截器

前端通过 Axios 拦截器自动添加认证头:

// api.js - 请求拦截器:在请求发送前自动添加 JWT Token
api.interceptors.request.use(
  config => {
    // 从 localStorage 获取登录时保存的 token
    const token = localStorage.getItem('token')
    if (token) {
      // 在请求头中添加 Authorization 字段
      config.headers.Authorization = `Bearer ${token}`
    } else {
      // 对于AI小助手相关接口,允许无token访问
      const aiAssistantPaths = ['/api/v1/ai-assistant/status', '/api/v1/ai-assistant/chat']
      const isAiAssistantPath = aiAssistantPaths.some(path => config.url.includes(path))

      // 如果不是AI助手接口且没有token,清空数据并跳转登录页
      if (!isAiAssistantPath) {
        clearUserLocalStorage()
        window.location.href = '/'
      }
    }
    return config
  },
  error => Promise.reject(error)
)

3.3 权限控制

  • 大部分接口需要认证才能访问
  • AI 小助手部分接口(如状态检查)允许匿名访问
  • 401/403 错误自动跳转登录页

4. 核心 API 模块

4.1 用户认证模块

基础路径: /api/v1/users

接口 方法 功能 认证
/login POST 用户登录
/register POST 用户注册
/info GET 获取用户信息
/update PUT 更新用户信息
/update-avatar POST 更新头像
/change-password POST 修改密码
/delete DELETE 注销账户
/security-question GET 获取密保问题
/reset-password POST 重置密码
/ai-assistant-settings GET/POST AI助手设置
/ai-assistant-avatar POST AI助手头像

后端 Controller 代码片段

/**
 * 用户控制器 - 处理用户相关的所有请求
 * 路径前缀: /api/v1/users
 */
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    // 注入用户服务层
    @Autowired
    private UserService userService;
    
    // 注入 JWT 工具类
    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 用户登录接口
     * @param request 包含用户名、密码、验证码的登录请求
     * @return 包含 JWT Token 的用户响应
     */
    @PostMapping("/login")
    public UserResponse login(@Valid @RequestBody UserLoginRequest request) {
        // 调用服务层验证用户身份
        User user = userService.login(request.getUsername(), request.getPassword());
        
        // 生成 JWT Token
        String token = jwtUtil.generateToken(user.getUsername());
        
        // 构建响应对象
        UserResponse response = new UserResponse();
        response.setToken(token);
        response.setUsername(user.getUsername());
        return response;
    }
}

前端 Service 代码片段

// userService.js - 用户相关 API 封装
const userService = {
  // 用户登录:传入用户名密码,返回包含 token 的用户信息
  login: async (credentials) => api.post(apiPaths.user.login, credentials),
  
  // 用户注册:传入用户名、密码、邮箱等信息
  register: async (userData) => api.post(apiPaths.user.register, userData),
  
  // 获取用户信息:根据用户名查询用户详细信息
  getUserInfo: async (username) => api.get(`${apiPaths.user.info}?username=${username}`),
  
  // 更新用户信息:传入用户资料进行更新
  updateUserInfo: async (userData) => api.put(apiPaths.user.update, userData)
}

export default userService

4.2 AI 小助手模块

基础路径: /api/v1/ai-assistant

接口 方法 功能 认证
/chat POST AI 对话
/status GET 获取AI状态
/history POST 保存对话历史
/history/{username} GET 获取历史列表
/history/{username}/{sessionId} DELETE 删除对话
/history/{username}/{sessionId}/title PUT 更新对话标题

前端 Service 代码片段

// healthService.js - AI小助手相关 API
aiAssistant: {
  /**
   * 发送聊天消息
   * @param message 用户输入的消息内容
   * @param username 当前用户名
   * @param context 上下文信息(如AI性格、名称等)
   * @returns 返回AI的回复内容
   */
  chat: async (message, username, context) => {
    return api.post(apiPaths.aiAssistant.chat, { message, username, context }, { timeout: 30000 })
  },
  
  // 获取用户的对话历史列表
  getHistoryList: async (username) => {
    return api.get(`${apiPaths.aiAssistant.history}/${encodeURIComponent(username)}`)
  },
  
  // 删除指定的对话历史
  deleteHistory: async (username, sessionId) => {
    return api.delete(`${apiPaths.aiAssistant.history}/${encodeURIComponent(username)}/${encodeURIComponent(sessionId)}`)
  }
}

4.3 AI 健康分析模块

基础路径: /api/v1/ai-analysis

接口 方法 功能 认证
/generate POST 生成分析报告
/save POST 保存分析记录
/ GET 获取分析记录列表
/favorited GET 获取收藏记录
/{id}/favorite POST 切换收藏状态
/{id} DELETE 删除记录
/batch-delete DELETE 批量删除

前端 Service 代码片段

// healthService.js - AI健康分析相关 API
aiAnalysis: {
  /**
   * 生成健康分析报告
   * @param username 用户名
   * @param startDate 分析开始日期
   * @param endDate 分析结束日期
   * @returns 返回AI生成的分析报告
   */
  generateAnalysis: async (username, startDate, endDate) => {
    return api.post(apiPaths.aiAnalysis.generate, null, {
      params: { username, startDate, endDate },
      timeout: 120000  // AI生成耗时较长,设置120秒超时
    })
  },
  
  // 切换分析记录的收藏状态
  toggleFavorite: async (recordId, favorited) => {
    return api.post(apiPaths.aiAnalysis.favorite(recordId), { favorited })
  }
}

4.4 健康记录模块

所有健康记录模块(饮食、运动、睡眠、情绪、体重)采用统一的 API 设计模式:

饮食记录 (/api/v1/food-records):

接口 方法 功能
/ GET 获取记录列表
/ POST 添加记录
/{id} DELETE 删除记录
/ PUT 更新记录
/{id}/favorite POST 切换收藏
/batch-delete DELETE 批量删除

前端 Service 代码片段(以饮食记录为例)

// healthService.js - 饮食记录相关 API
food: {
  // 获取用户的饮食记录列表,支持分页和筛选参数
  getRecords: async (username, params) => api.get(`${apiPaths.food.records}?username=${username}`, { params }),
  
  // 创建新的饮食记录
  createRecord: async (record) => api.post(apiPaths.food.records, record),
  
  // 更新已有的饮食记录
  updateRecord: async (recordId, record) => api.put(`${apiPaths.food.records}/${recordId}`, record),
  
  // 删除指定的饮食记录
  deleteRecord: async (recordId) => api.delete(`${apiPaths.food.records}/${recordId}`),
  
  // 切换记录的收藏状态
  toggleFavorite: async (recordId, favorited) => api.post(apiPaths.food.favorite(recordId), { favorited })
}

运动记录 (/api/v1/workouts)、睡眠记录 (/api/v1/sleep-records)、情绪记录 (/api/v1/mood-records)、体重记录 (/api/v1/weight-records) 均采用相同的 API 设计模式。

5. 前端 API 封装

5.1 API 路径管理

所有 API 路径集中管理在 apiPaths.js

// API 基础路径
const API_BASE = '/api/v1';

// API 路径配置对象
const apiPaths = {
  // 用户相关接口
  user: {
    login: `${API_BASE}/users/login`,           // 登录
    register: `${API_BASE}/users/register`,     // 注册
    info: `${API_BASE}/users/info`,             // 获取用户信息
    update: `${API_BASE}/users/update`          // 更新用户信息
  },
  // 饮食记录接口
  food: {
    records: `${API_BASE}/food-records`,        // 记录增删改查
    favorite: (id) => `${API_BASE}/food-records/${id}/favorite`  // 动态路径:切换收藏
  },
  // AI小助手接口
  aiAssistant: {
    chat: `${API_BASE}/ai-assistant/chat`,      // AI对话
    history: `${API_BASE}/ai-assistant/history` // 对话历史
  },
  // AI健康分析接口
  aiAnalysis: {
    generate: `${API_BASE}/ai-analysis/generate`, // 生成分析
    favorite: (id) => `${API_BASE}/ai-analysis/${id}/favorite`   // 动态路径:切换收藏
  }
};

export default apiPaths;

优点

  • 路径统一管理,便于维护
  • 支持动态路径参数
  • 避免硬编码 URL

5.2 Axios 实例配置

// api.js - Axios 配置和拦截器
import axios from 'axios'

// 创建 axios 实例,配置基础参数
const api = axios.create({
  baseURL: 'http://localhost:8080',  // 后端服务地址
  timeout: 10000,                     // 请求超时时间(10秒)
  headers: { 'Content-Type': 'application/json' }  // 默认请求头
})

// 请求拦截器:在请求发送前处理(如添加 Token)
api.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  error => Promise.reject(error)
)

// 响应拦截器:在收到响应后处理(如统一错误处理)
api.interceptors.response.use(
  response => response.data,  // 直接返回响应数据
  error => {
    // 401/403 未授权,清空数据并跳转登录页
    if (error.response?.status === 401 || error.response?.status === 403) {
      clearUserLocalStorage()
      window.location.href = '/'
    }
    return Promise.reject(error)
  }
)

export default api

6. 错误处理机制

6.1 后端错误处理

全局异常处理器GlobalExceptionHandler.java):

/**
 * 全局异常处理器 - 统一处理所有 Controller 抛出的异常
 * @ControllerAdvice 注解标识这是一个全局异常处理类
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理自定义业务异常
     * @param ex 自定义异常对象
     * @return 包含错误信息的响应实体
     */
    @ExceptionHandler(BaseException.class)
    public ResponseEntity<Map<String, Object>> handleBaseException(BaseException ex) {
        logger.error("Base exception: {}", ex.getMessage());
        return createErrorResponse(HttpStatus.valueOf(ex.getStatusCode()), ex.getMessage(), ex.getClass().getSimpleName());
    }

    /**
     * 处理运行时异常
     * @param ex 运行时异常对象
     * @return 400 错误响应
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Map<String, Object>> handleRuntimeException(RuntimeException ex) {
        logger.error("Runtime exception: ", ex);
        return createErrorResponse(HttpStatus.BAD_REQUEST, ex.getMessage(), ex.getClass().getSimpleName());
    }

    /**
     * 处理所有其他未捕获的异常
     * @param ex 异常对象
     * @return 500 错误响应
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleException(Exception ex) {
        logger.error("Unexpected exception: ", ex);
        return createErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "服务器内部错误", ex.getMessage());
    }

    /**
     * 创建统一的错误响应结构
     * @param status HTTP 状态码
     * @param message 错误消息
     * @param error 异常类型
     * @return ResponseEntity 对象
     */
    private ResponseEntity<Map<String, Object>> createErrorResponse(HttpStatus status, String message, String error) {
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("status", status.value());   // HTTP 状态码
        errorResponse.put("message", message);          // 错误消息
        errorResponse.put("error", error);              // 异常类型
        return ResponseEntity.status(status).body(errorResponse);
    }
}

自定义异常基类

/**
 * 自定义异常基类 - 所有业务异常都继承此类
 * 包含状态码字段,用于返回对应的 HTTP 状态码
 */
public class BaseException extends RuntimeException {
    private int statusCode;  // HTTP 状态码

    /**
     * 构造方法
     * @param message 错误消息
     * @param statusCode HTTP 状态码
     */
    public BaseException(String message, int statusCode) {
        super(message);
        this.statusCode = statusCode;
    }

    public int getStatusCode() { return statusCode; }
}

错误响应格式

{
  "status": 400,
  "message": "请求参数错误",
  "error": "RuntimeException"
}

6.2 前端错误处理

项目采用两层错误处理机制:

第一层:Axios 响应拦截器api.js

拦截器处理全局错误,如 401/403 跳转登录页:

// 响应拦截器:统一处理响应错误
api.interceptors.response.use(
  response => response.data,  // 成功响应直接返回数据
  error => {
    // 401: 未授权,403: 禁止访问,都跳转到登录页
    if (error.response?.status === 401 || error.response?.status === 403) {
      clearUserLocalStorage()  // 清除本地存储的用户数据
      window.location.href = '/'  // 跳转到登录页
    }
    return Promise.reject(error)
  }
)

第二层:错误处理工具函数errorHandler.js

组件层使用 handleApiError 处理具体错误并提示用户:

/**
 * 统一处理 API 错误
 * @param error 错误对象(Axios 抛出的错误)
 * @param setErrorMessage 设置错误消息的回调函数(用于UI显示)
 * @param defaultMessage 默认错误消息
 * @returns 处理后的错误消息
 */
export const handleApiError = (error, setErrorMessage, defaultMessage = '操作失败') => {
  let errorMessage = defaultMessage;
  
  if (error.response) {
    // 服务器返回了错误状态码
    switch (error.response.status) {
      case 400: errorMessage = '请求参数错误'; break;
      case 401: errorMessage = '未授权,请重新登录'; break;
      case 403: errorMessage = '禁止访问'; break;
      case 404: errorMessage = '请求的资源不存在'; break;
      case 409: errorMessage = '资源冲突,可能已存在'; break;
      case 500: errorMessage = '服务器内部错误'; break;
      default: errorMessage = error.response.data?.message || defaultMessage;
    }
  } else if (error.request) {
    // 请求已发出但没有收到响应(网络错误)
    errorMessage = '网络错误,无法连接到服务器';
  } else {
    // 请求配置出错
    errorMessage = error.message || defaultMessage;
  }
  
  // 如果提供了回调函数,设置错误消息到UI
  if (setErrorMessage) setErrorMessage(errorMessage);
  return errorMessage;
};

6.3 错误码说明

状态码 含义 处理方式
200 请求成功 正常处理
400 请求参数错误 提示用户检查输入
401 未授权 跳转登录页
403 禁止访问 提示无权限
404 资源不存在 提示资源不存在
409 资源冲突 提示资源已存在
500 服务器错误 提示服务器错误

7. 数据持久化策略

7.1 前端数据缓存

存储位置 用途 示例
localStorage 长期缓存 token、用户设置、主题
sessionStorage 会话缓存 临时表单数据
Pinia Store 应用状态 当前用户信息、健康数据
内存 临时状态 组件内部状态

7.2 数据同步机制

用户操作 → 更新前端状态 → 调用 API → 后端处理 → 数据库
                ↑                              ↓
                └────── 响应成功,更新状态 ←────┘

7.3 离线处理

  • 关键操作(如添加记录)失败后提示用户
  • 支持重试机制
  • 后端断开时清空本地缓存并跳转登录页

8. 安全设计

8.1 传输安全

  • 使用 HTTPS 加密传输(生产环境)
  • 敏感信息(密码)加密传输

8.2 认证安全

  • JWT Token 设置过期时间
  • Token 失效自动跳转登录
  • 登出时清空所有缓存数据

8.3 数据安全

  • 用户只能访问自己的数据
  • 后端进行权限校验
  • 敏感操作需要重新认证
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐