个人博客系统后端 - 用户信息管理功能实现指南(上)

本文记录了如何实现用获取户信息,用户信息更新,用户头像上传三大基础功能
先上接口实现截图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、项目结构概览
先介绍一下
个人博客系统采用了标准的 Spring Boot 项目结构,用户功能相关的文件主要分布在以下几个目录:

WeblogSystem/
├── src/main/java/com/zxy/weblogsystem/
│   ├── controller/        # 控制器层,处理HTTP请求
│   ├── service/           # 服务层,实现业务逻辑
│   │   └── impl/          # 服务实现类
│   ├── repository/        # 数据访问层,与数据库交互
│   ├── entity/            # 实体类,映射数据库表
│   ├── dto/               # 数据传输对象,用于API交互
│   ├── exception/         # 自定义异常类
│   └── config/            # 配置类
├── src/main/resources/
│   ├── static/            # 静态资源
│   ├── templates/         # 模板文件
│   ├── application.properties  # 应用配置
│   ├── schema.sql         # 数据库表结构
│   └── data.sql           # 初始数据
└── docs/                  # 项目文档

二、用户信息功能实现

  1. 实体类定义
    文件位置:src/main/java/com/zxy/weblogsystem/entity/User.java

功能说明:定义用户实体类,映射数据库中的 users 表。

主要内容:

@Data  // Lombok注解,自动生成getter/setter等方法
@NoArgsConstructor  // 无参构造函数
@AllArgsConstructor  // 全参构造函数
@Entity  // JPA实体类注解
@Table(name = "users")  // 指定表名
public class User {@Id  // 主键@GeneratedValue(strategy = GenerationType.IDENTITY)  // 自增策略private Long id;@Column(nullable = false, unique = true, length = 50)private String username;@Column(nullable = false, length = 100)private String password;@Column(nullable = false, unique = true, length = 100)private String email;@Column(length = 50)private String nickname;@Column(length = 255)private String avatarUrl;  // 头像URL@Column(nullable = false, length = 20)private String role = "USER";@Column(nullable = false)private Integer status = 1;@Column(name = "created_at", nullable = false, updatable = false)private LocalDateTime createdAt;@Column(name = "updated_at", nullable = false)private LocalDateTime updatedAt;@PrePersistprotected void onCreate() {createdAt = LocalDateTime.now();updatedAt = LocalDateTime.now();}@PreUpdateprotected void onUpdate() {updatedAt = LocalDateTime.now();}
}
  1. 数据传输对象(DTO)
    2.1 用户信息DTO
    文件位置:src/main/java/com/zxy/weblogsystem/dto/UserInfoDto.java

功能说明:用于返回用户信息的数据传输对象。

主要内容:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDto {private Long id;private String username;private String nickname;private String email;private String avatarUrl;private String role;private Integer status;private Integer followersCount;  // 粉丝数private Integer followingCount;  // 关注数private Integer articleCount;    // 文章数private LocalDateTime createdAt;
}

2.2 用户更新DTO
文件位置:src/main/java/com/zxy/weblogsystem/dto/UserUpdateDto.java

功能说明:用于接收用户信息更新请求的数据传输对象。

主要内容:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserUpdateDto {@Size(max = 50, message = "昵称长度不能超过50个字符")private String nickname;@URL(message = "头像URL格式不正确")private String avatarUrl;
}

2.3 API响应DTO
文件位置:src/main/java/com/zxy/weblogsystem/dto/ApiResponse.java

功能说明:统一的API响应格式。

主要内容:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {private Integer code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(200, "OK", data);}public static <T> ApiResponse<T> success(String message, T data) {return new ApiResponse<>(200, message, data);}public static <T> ApiResponse<T> error(Integer code, String message) {return new ApiResponse<>(code, message, null);}
}
  1. 数据访问层
    文件位置:src/main/java/com/zxy/weblogsystem/repository/UserRepository.java

功能说明:用户数据访问接口,提供数据库操作方法。

主要内容:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {Optional<User> findByUsername(String username);Optional<User> findByEmail(String email);boolean existsByUsername(String username);boolean existsByEmail(String email);
}
  1. 服务层
    4.1 用户服务接口
    文件位置:src/main/java/com/zxy/weblogsystem/service/UserService.java

功能说明:定义用户相关的业务逻辑接口。

主要内容:

public interface UserService {/*** 根据用户ID获取用户信息* @param userId 用户ID* @return 用户详细信息DTO*/UserInfoDto getUserInfo(Long userId);/*** 根据用户ID更新用户信息* @param userId 用户ID* @param userUpdateDto 用户更新信息DTO* @return 更新后的用户信息DTO*/UserInfoDto updateUserInfo(Long userId, UserUpdateDto userUpdateDto);/*** 更新用户头像* @param userId 用户ID* @param avatarUrl 头像URL* @return 更新后的用户信息DTO*/UserInfoDto updateUserAvatar(Long userId, String avatarUrl);
}

4.2 用户服务实现类
文件位置:src/main/java/com/zxy/weblogsystem/service/impl/UserServiceImpl.java

功能说明:实现用户服务接口中定义的业务逻辑。

主要内容:

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {private final UserRepository userRepository;private final UserFollowRepository userFollowRepository;private final ArticleRepository articleRepository;@Override@Transactional(readOnly = true)public UserInfoDto getUserInfo(Long userId) {// 1. 查询用户基本信息User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID: " + userId));// 2. 查询统计数据Integer followersCount = userFollowRepository.countByFollowedId(userId);Integer followingCount = userFollowRepository.countByFollowerId(userId);Integer articleCount = articleRepository.countByAuthorId(userId);// 3. 构建并返回DTOreturn UserInfoDto.builder().id(user.getId()).username(user.getUsername()).nickname(user.getNickname()).email(user.getEmail()).avatarUrl(user.getAvatarUrl()).role(user.getRole()).status(user.getStatus()).followersCount(followersCount).followingCount(followingCount).articleCount(articleCount).createdAt(user.getCreatedAt()).build();}@Override@Transactionalpublic UserInfoDto updateUserInfo(Long userId, UserUpdateDto userUpdateDto) {// 1. 查询用户是否存在User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID: " + userId));// 2. 更新用户信息boolean isUpdated = false;// 更新昵称if (userUpdateDto.getNickname() != null && !userUpdateDto.getNickname().isEmpty()) {user.setNickname(userUpdateDto.getNickname());isUpdated = true;}// 3. 保存更新后的用户信息if (isUpdated) {user = userRepository.save(user);}// 4. 查询统计数据并构建返回DTOreturn getUserInfo(userId);}@Override@Transactionalpublic UserInfoDto updateUserAvatar(Long userId, String avatarUrl) {// 1. 查询用户是否存在User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用户不存在,ID: " + userId));// 2. 更新用户头像URLuser.setAvatarUrl(avatarUrl);// 3. 保存更新后的用户信息userRepository.save(user);// 4. 查询统计数据并构建返回DTOreturn getUserInfo(userId);}
}
  1. 控制器层
    文件位置:src/main/java/com/zxy/weblogsystem/controller/UserController.java

功能说明:处理用户相关的HTTP请求。

主要内容:

@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {private final UserService userService;private final FileService fileService;/*** 获取用户信息*/@GetMapping("/{id}")public ApiResponse<UserInfoDto> getUserInfo(@PathVariable Long id) {UserInfoDto userInfo = userService.getUserInfo(id);return ApiResponse.success(userInfo);}/*** 更新用户信息*/@PutMapping("/{id}")public ApiResponse<UserInfoDto> updateUserInfo(@PathVariable Long id, @Valid @RequestBody UserUpdateDto userUpdateDto) {UserInfoDto updatedUserInfo = userService.updateUserInfo(id, userUpdateDto);return ApiResponse.success("更新成功", updatedUserInfo);}/*** 上传用户头像*/@PostMapping("/{id}/avatar")public ApiResponse<Map<String, String>> uploadAvatar(@PathVariable Long id, @RequestParam("file") MultipartFile file) {// 1. 调用文件服务上传头像String avatarUrl = fileService.uploadAvatar(id, file);// 2. 更新用户头像信息userService.updateUserAvatar(id, avatarUrl);// 3. 返回头像URLMap<String, String> result = new HashMap<>();result.put("avatarUrl", avatarUrl);return ApiResponse.success("上传成功", result);}
}

三、文件上传功能实现

  1. 文件上传配置
    文件位置:src/main/java/com/zxy/weblogsystem/config/FileUploadConfig.java

功能说明:配置文件上传相关参数和静态资源访问。

主要内容:

@Configuration
public class FileUploadConfig implements WebMvcConfigurer {@Value("${file.upload.path:uploads}")private String uploadPath;@Value("${file.access.path:/uploads/}")private String accessPath;@Beanpublic MultipartResolver multipartResolver() {return new StandardServletMultipartResolver();}@Overridepublic void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {// 确保上传目录存在File uploadDir = new File(uploadPath);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 获取上传目录的绝对路径String absolutePath = uploadDir.getAbsolutePath();// 添加资源处理器,将上传路径映射到访问路径registry.addResourceHandler(accessPath + "**").addResourceLocations("file:" + absolutePath + "/");}
}
  1. 文件上传异常
    文件位置:src/main/java/com/zxy/weblogsystem/exception/FileUploadException.java

功能说明:自定义文件上传异常类。

主要内容:

public class FileUploadException extends RuntimeException {public FileUploadException(String message) {super(message);}public FileUploadException(String message, Throwable cause) {super(message, cause);}
}
  1. 文件服务接口
    文件位置:src/main/java/com/zxy/weblogsystem/service/FileService.java

功能说明:定义文件上传相关的业务逻辑接口。

主要内容:

public interface FileService {/*** 上传头像文件* * @param userId 用户ID* @param file 头像文件* @return 头像访问URL*/String uploadAvatar(Long userId, MultipartFile file);
}
  1. 文件服务实现类
    文件位置:src/main/java/com/zxy/weblogsystem/service/impl/FileServiceImpl.java

功能说明:实现文件上传相关的业务逻辑。

主要内容:

@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {@Value("${file.upload.path:uploads}")private String uploadPath;@Value("${file.access.path:/uploads/}")private String accessPath;private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList("image/jpeg", "image/png");@Overridepublic String uploadAvatar(Long userId, MultipartFile file) {// 1. 校验文件是否为空if (file == null || file.isEmpty()) {throw new FileUploadException("上传文件不能为空");}// 2. 校验文件类型String contentType = file.getContentType();if (contentType == null || !ALLOWED_IMAGE_TYPES.contains(contentType)) {throw new FileUploadException("只支持JPG和PNG格式的图片");}// 3. 校验文件大小if (file.getSize() > 2 * 1024 * 1024) { // 2MBthrow new FileUploadException("文件大小不能超过2MB");}try {// 4. 确保上传目录存在File uploadDir = new File(uploadPath);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 5. 创建用户头像目录String userAvatarDir = uploadPath + "/avatars/" + userId;File userDir = new File(userAvatarDir);if (!userDir.exists()) {userDir.mkdirs();}// 6. 生成唯一文件名String originalFilename = file.getOriginalFilename();String fileExtension = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg";String newFilename = UUID.randomUUID().toString() + fileExtension;// 7. 保存文件Path targetPath = Paths.get(userAvatarDir, newFilename);Files.copy(file.getInputStream(), targetPath);// 8. 返回文件访问URLreturn accessPath + "avatars/" + userId + "/" + newFilename;} catch (IOException e) {throw new FileUploadException("文件上传失败: " + e.getMessage());}}
}

四、配置文件

  1. 应用配置
    文件位置:src/main/resources/application.properties

功能说明:配置应用参数,包括数据库连接、文件上传等。

主要内容:

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/weblog?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# JPA配置
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect# 文件上传配置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.file-size-threshold=0# 文件存储路径配置
file.upload.path=e:/个人博客系统/WeblogSystem/uploads
file.access.path=/uploads/
  1. 数据库表结构
    文件位置:src/main/resources/schema.sql

功能说明:定义数据库表结构。

主要内容:

-- 用户表
CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',password VARCHAR(100) NOT NULL COMMENT '密码(加密)',email VARCHAR(100) NOT NULL UNIQUE COMMENT '邮箱',nickname VARCHAR(50) COMMENT '昵称',avatar_url VARCHAR(255) COMMENT '头像URL',role VARCHAR(20) NOT NULL DEFAULT 'USER' COMMENT '角色',status INT NOT NULL DEFAULT 1 COMMENT '状态(0=禁用,1=启用)',created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',INDEX idx_email(email),INDEX idx_username(username)
) COMMENT '用户表';

五、功能测试

  1. 获取用户信息
    请求方法:GET

URL:/users/{id}

示例:GET http://localhost:8080/users/1

响应示例:

{"code": 200,"message": "OK","data": {"id": 1,"username": "admin","nickname": "管理员","email": "admin@example.com","avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg","role": "ADMIN","status": 1,"followersCount": 0,"followingCount": 0,"articleCount": 0,"createdAt": "2025-04-13T12:00:00"}
}
  1. 更新用户信息
    请求方法:PUT

URL:/users/{id}

请求体:

{"nickname": "新昵称"
}

示例:PUT http://localhost:8080/users/1

响应示例:

{"code": 200,"message": "更新成功","data": {"id": 1,"username": "admin","nickname": "新昵称","email": "admin@example.com","avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg","role": "ADMIN","status": 1,"followersCount": 0,"followingCount": 0,"articleCount": 0,"createdAt": "2025-04-13T12:00:00"}
}
  1. 上传用户头像
    请求方法:POST

URL:/users/{id}/avatar

Content-Type:multipart/form-data

请求参数:

file: 图片文件(支持jpg、png,最大2MB)
示例:POST http://localhost:8080/users/1/avatar

响应示例:

{"code": 200,"message": "上传成功","data": {"avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg"}
}

六、总结
通过以上实现,完成了用户信息管理的三个主要功能:
获取用户信息:通过用户ID获取用户详细信息,包括基本资料和统计数据
更新用户信息:支持更新用户昵称等基本信息
上传用户头像:支持上传JPG、PNG格式的头像图片,并自动更新用户头像URL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901770.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

趣味编程之分布式系统:负载均衡的“雨露均沾“艺术

#此篇文章由Deepseek大力支持&#x1f60b; 凌晨三点&#xff0c;西二旗某火锅店后厨—— “羊肉卷走3号桌&#xff01;” “肥牛卷去7号&#xff01;” “虾滑优先给VIP区&#xff01;” 我蹲在传菜口的监控屏幕前&#xff0c;看着机器人服务生们忙而不乱地穿梭。突然间&am…

Linux——信号(1)信号的产生

我们在讲进程的多种状态时提到过&#xff0c;一个进程的退出有三种情况&#xff1a;正常退出&#xff0c;结果出错退出&#xff08;代码也执行完了&#xff09;&#xff0c;异常终止退出&#xff08;代码未执行完&#xff09;&#xff0c;其中最后一种退出相当于进程在运行时&a…

LeetCode 2919 使数组变美的最小增量运算数

动态规划解题&#xff1a;最小操作次数使数组变为美丽数组 问题描述 给定一个下标从0开始、长度为n的整数数组nums和一个整数k。你可以对数组中的任意一个元素进行加1操作&#xff0c;操作次数不限。如果数组中任意长度大于或等于3的子数组的最大值都大于或等于k&#xff0c;…

计算生物学在中国的发展情况?

李升伟 整理 计算生物学在中国的发展呈现出多方面积极态势&#xff0c;具体表现如下&#xff1a; 发展概述&#xff1a; 上海发布了医用AI发展的专项方案&#xff0c;特别强调了脑科学与计算生物学的前沿领域。这表明政府有意推动该领域的技术进步和技术合作平台建设。国内的…

Linux之文件内容显示(cat、grep、cut、sort、uniq、tr)

&#x1f3af; 本文专栏&#xff1a;Linux &#x1f680; 作者主页&#xff1a;小度爱学习 1、浏览普通文件内容 命令常用选项说明cat-n 对输出内容中的所有行标注行号&#xff1b;-b 对输出内容中的非空行标注行号。查看文本文件的内容head-num 指定需要显示文件num行的内容。…

3DS 转 STL 全攻略:传统工具与迪威模型网在线转换深度解析

在 3D 建模与 3D 打印的技术领域中&#xff0c;常常会遇到需要将不同格式的文件进行转换的情况。其中&#xff0c;把 3DS 文件转换为 STL 格式是较为常见的操作。3DS 文件作为一种旧版 Autodesk 3D Studio 使用的 3D 图像格式&#xff0c;存储着丰富的信息&#xff0c;包括网格…

IoT FEM射频前端模组芯片(2.4G PA)三伍微电子GSR2401 兼容替代RFX2401

型号&#xff1a;GSR2401应用&#xff1a;适用于蓝牙&#xff08;BT&#xff09;、ZigBee及物联网&#xff08;IoT&#xff09;设备 功能&#xff1a;集成了功率放大器&#xff08;PA&#xff09;、开关&#xff08;Switch&#xff09;和低噪声放大器&#xff08;LNA&#xff…

Missashe考研日记-day22

Missashe考研日记-day22 1 专业课408 学习时间&#xff1a;3h学习内容&#xff1a; 先把昨天关于进程调度的课后习题做了&#xff0c;然后花了挺长时间预习OS的最最最最重要的一部分——同步与互斥问题&#xff0c;这部分大二上课的时候就懵懵懂懂的&#xff0c;得认真再领悟…

2025年最新Web安全(面试题)

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…

Qt QML - qmldir使用方法详解

以实际例子看qmldir的使用 1.搞一个qmldir2.让QML找到你的qmldir &#xff08;重点&#xff09;.pro 工程文件QQmlApplicationEngine加载主QML处 3.用起来你的模块 qmldir是Qt QML模块化的基石&#xff0c;其设计初衷是为解决QML文件的组织、复用和依赖管理问题,。只需要在每个…

# Shell脚本参数设计规范(DeepSeek指导)

Shell脚本参数设计规范&#xff08;DeepSeek指导&#xff09; 文章目录 Shell脚本参数设计规范&#xff08;DeepSeek指导&#xff09;A 我问&#xff1a;Q DeepSeek回答&#xff1a;**命令行参数表示规范****标准化表示示例**情况1&#xff1a;必选选项参数值情况2&#xff1a;…

MQTT协议:IoT通信的轻量级选手

文章总结&#xff08;帮你们节约时间&#xff09; MQTT协议是一种轻量级的发布/订阅通信协议。MQTT通信包括连接建立、订阅、发布和断开等过程。MQTT基于TCP/IP&#xff0c;其通信过程涉及多种控制包和数据包。ESP32S3可以通过MQTT协议接收消息来控制IO9引脚上的LED。 想象一…

数据结构——反射、枚举以及lambda表达式

1. 反射 Java的反射&#xff08;reflection&#xff09;机制是在运⾏时检查、访问和修改类、接⼝、字段和⽅法的机制&#xff1b;这种动态获取信息以及动态调⽤对象⽅法的功能称为java语⾔的反射&#xff08;reflection&#xff09;机制。 用途 1. 框架开发 2. 注解处理 3.…

C语言教程(十):C 语言函数详解

一、引言 在 C 语言中&#xff0c;函数是一组执行特定任务的代码块。通过将复杂的程序逻辑划分为多个函数&#xff0c;不仅能提高代码的可读性、可维护性&#xff0c;还便于代码的复用。无论是简单的数学计算&#xff0c;还是复杂的系统操作&#xff0c;函数都发挥着核心作用。…

力扣面试150题--有效的字母异位词和字母异位词分组

Day 24 题目描述 思路 初次思路&#xff1a;如果两个字符串为异位词&#xff0c;说明它们长度相同并且字母出现的次数相同&#xff0c;于是有以下做法&#xff1a; 定义一个map&#xff0c;来保存s中每个字符的出现次数处理特殊情况&#xff0c;如果长度不同&#xff0c;直接…

数理逻辑(Mathematical Logic)综论与跨学科应用

李升伟 整理 数理逻辑&#xff08;Mathematical Logic&#xff09;是现代逻辑学与数学交叉的核心学科&#xff0c;以严格的数学方法研究逻辑推理的形式与规律。其发展深刻影响了数学基础、计算机科学、语言哲学等领域。以下从多个维度综论数理逻辑&#xff1a; 1. 核心分支 命…

高性能内存kv数据库Redis(续)

目录 四.主从同步与对象模型 1.Redis 淘汰策略 2.Redis 如何做到 持久化 2.1 redis为什么要实现持久化 2.2fork进程的写时复制机制 2.3大Key的影响 2.4redis做持久化的方式 2.5 aof 2.6 rdb 2.7 redis 持久化方式的优缺点 3.redis里面的高可用体现在哪里&#xff1f; 3.1r…

泛型算法——只读算法(一)

在 C 标准库中&#xff0c;泛型算法的“只读算法”指那些 不会改变它们所操作的容器中的元素&#xff0c;仅用于访问或获取信息的算法&#xff0c;例如查找、计数、遍历等操作。 accumulate std::accumulate()是 C 标准库**numeric**头文件中提供的算法&#xff0c;用于对序列…

SvelteKit 最新中文文档教程(21)—— 最佳实践之图片

前言 Svelte&#xff0c;一个语法简洁、入门容易&#xff0c;面向未来的前端框架。 从 Svelte 诞生之初&#xff0c;就备受开发者的喜爱&#xff0c;根据统计&#xff0c;从 2019 年到 2024 年&#xff0c;连续 6 年一直是开发者最感兴趣的前端框架 No.1&#xff1a; Svelte …

健康养生:开启活力生活的密钥

当我们在健身房看到年逾六旬却身形矫健的老人&#xff0c;在公园偶遇精神矍铄、步伐轻快的长者&#xff0c;总会惊叹于他们的健康状态。其实&#xff0c;这些都得益于长期坚持科学的养生之道。健康养生并非遥不可及的玄学&#xff0c;而是融入生活细节的智慧。​ 在饮食的世界…