作为一名有着10年全栈开发经验的程序员,我最近完成了一个基于Spring Boot的宠物成长记录平台项目。这个系统不仅实现了基础的宠物信息管理功能,还创新性地加入了成长轨迹记录、健康数据分析和社交分享等特色模块。从技术选型到架构设计,再到最后的测试部署,整个过程我都采用了当前最主流的技术栈和开发模式。
这个项目特别适合计算机相关专业的同学作为毕业设计选题,因为它涵盖了企业级应用开发的完整流程:前端使用Vue.js构建响应式界面,后端采用Spring Boot+MyBatis Plus框架,数据库选用MySQL,整体架构遵循MVC模式。项目难度适中但又不失技术深度,能够全面展示一个开发者的技术能力。
在项目启动阶段,我花了大量时间进行技术选型评估。最终确定的技术组合经过了多方面的考量:
后端框架:Spring Boot 2.7.x
持久层:MyBatis Plus 3.5.x
前端框架:Vue 3 + Element Plus
数据库:MySQL 8.0
系统采用经典的三层架构设计,各层职责明确:
code复制表现层(View)
├── 用户界面
├── 管理后台
└── 移动端适配
业务逻辑层(Service)
├── 宠物服务
├── 用户服务
├── 健康记录服务
└── 社交服务
数据访问层(DAO)
├── MyBatis Plus映射
├── 实体关系映射
└── 数据库连接池
这种分层架构带来了几个显著优势:
数据库设计遵循第三范式,主要包含以下核心表:
用户表(user)
sql复制CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
宠物表(pet)
sql复制CREATE TABLE `pet` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '所属用户ID',
`name` varchar(50) NOT NULL COMMENT '宠物名称',
`type` tinyint NOT NULL COMMENT '宠物类型(1-狗,2-猫,3-其他)',
`breed` varchar(50) DEFAULT NULL COMMENT '品种',
`birthday` date DEFAULT NULL COMMENT '生日',
`gender` tinyint DEFAULT '0' COMMENT '性别(0-未知,1-公,2-母)',
`weight` decimal(5,2) DEFAULT NULL COMMENT '体重(kg)',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物表';
成长记录表(growth_record)
sql复制CREATE TABLE `growth_record` (
`id` bigint NOT NULL AUTO_INCREMENT,
`pet_id` bigint NOT NULL COMMENT '宠物ID',
`record_date` date NOT NULL COMMENT '记录日期',
`weight` decimal(5,2) DEFAULT NULL COMMENT '体重(kg)',
`height` decimal(5,2) DEFAULT NULL COMMENT '身高(cm)',
`body_length` decimal(5,2) DEFAULT NULL COMMENT '体长(cm)',
`description` varchar(500) DEFAULT NULL COMMENT '描述',
`images` varchar(1000) DEFAULT NULL COMMENT '图片(多张,逗号分隔)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_pet_id` (`pet_id`),
KEY `idx_record_date` (`record_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长记录表';
数据库设计时特别注意了以下几点:
用户认证采用了JWT(JSON Web Token)方案,相比传统的Session认证有以下优势:
认证流程实现代码:
java复制// JWT工具类
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public static String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public static boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private static boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}
安全配置类:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
成长记录是本系统的核心功能,实现了以下特性:
后端接口实现:
java复制@RestController
@RequestMapping("/api/pets/{petId}/records")
public class GrowthRecordController {
@Autowired
private GrowthRecordService recordService;
@PostMapping
public Result addRecord(@PathVariable Long petId,
@RequestBody GrowthRecordDTO recordDTO) {
recordDTO.setPetId(petId);
return Result.success(recordService.addRecord(recordDTO));
}
@GetMapping
public Result getRecords(@PathVariable Long petId,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
List<GrowthRecordVO> records = recordService.getRecords(petId, startDate, endDate);
return Result.success(records);
}
@GetMapping("/statistics")
public Result getStatistics(@PathVariable Long petId) {
GrowthStatisticsVO statistics = recordService.getStatistics(petId);
return Result.success(statistics);
}
}
前端使用ECharts实现成长趋势图:
javascript复制// 在Vue组件中
methods: {
initChart() {
const chart = echarts.init(this.$refs.chart);
const option = {
title: { text: '体重变化趋势' },
tooltip: {},
xAxis: {
type: 'category',
data: this.chartData.dates
},
yAxis: { type: 'value' },
series: [{
name: '体重',
type: 'line',
data: this.chartData.weights,
smooth: true,
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
}
}]
};
chart.setOption(option);
}
}
系统支持多图片上传,采用以下方案:
后端实现代码:
java复制@RestController
@RequestMapping("/api/upload")
public class UploadController {
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping
public Result upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.fail("请选择文件");
}
try {
// 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String fileExt = originalFilename.substring(originalFilename.lastIndexOf("."));
String filename = UUID.randomUUID().toString() + fileExt;
// 创建目录
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 保存文件
File dest = new File(uploadDir + filename);
file.transferTo(dest);
// 返回访问路径
String fileUrl = "/uploads/" + filename;
return Result.success(fileUrl);
} catch (IOException e) {
return Result.fail("上传失败");
}
}
}
前端实现:
vue复制<template>
<el-upload
action="/api/upload"
list-type="picture-card"
:on-success="handleSuccess"
:before-upload="beforeUpload"
multiple>
<i class="el-icon-plus"></i>
</el-upload>
</template>
<script>
export default {
methods: {
beforeUpload(file) {
const isImage = file.type.startsWith('image/');
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isImage) {
this.$message.error('只能上传图片!');
}
if (!isLt5M) {
this.$message.error('图片大小不能超过5MB!');
}
return isImage && isLt5M;
},
handleSuccess(response, file) {
this.$emit('uploaded', response.data);
}
}
}
</script>
项目采用了多层次的测试策略:
测试覆盖率达到了85%以上,关键业务代码达到100%。
用户登录测试用例
| 测试编号 | 测试场景 | 输入数据 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|---|
| TC-001 | 正确用户名密码 | username: test, password: 123456 | 登录成功,返回token | 登录成功,返回token | 是 |
| TC-002 | 错误密码 | username: test, password: wrong | 登录失败,返回错误信息 | 登录失败,返回"密码错误" | 是 |
| TC-003 | 不存在的用户 | username: notexist, password: 123456 | 登录失败,返回错误信息 | 登录失败,返回"用户不存在" | 是 |
宠物信息添加测试用例
| 测试编号 | 测试场景 | 输入数据 | 预期结果 | 实际结果 | 是否通过 |
|---|---|---|---|---|---|
| TC-101 | 正常添加 | 完整宠物信息 | 添加成功,返回宠物ID | 添加成功,返回宠物ID | 是 |
| TC-102 | 缺少必填字段 | 不填写宠物名称 | 添加失败,返回错误信息 | 添加失败,返回"宠物名称不能为空" | 是 |
| TC-103 | 非法字符 | 宠物名称包含特殊字符 | 添加失败,返回错误信息 | 添加失败,返回"名称包含非法字符" | 是 |
系统支持多种部署方式:
开发环境运行
bash复制# 后端
mvn spring-boot:run
# 前端
npm run serve
生产环境部署
bash复制mvn clean package -DskipTests
bash复制npm run build
dockerfile复制# Dockerfile示例
FROM openjdk:11-jre
COPY target/pet-growth-system.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/frontend/dist;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /uploads {
alias /path/to/upload/dir;
}
}
在开发这个宠物成长记录平台的过程中,我积累了一些宝贵的经验,分享给大家:
技术选型要务实:不要盲目追求新技术,选择成熟稳定、社区活跃的技术栈。Spring Boot+Vue的组合虽然不算最新,但生态完善,遇到问题容易找到解决方案。
数据库设计要前瞻:即使初期功能简单,也要考虑未来可能的扩展。比如宠物表预留了type字段,方便以后增加更多宠物类型。
接口设计要规范:保持统一的返回格式和错误处理机制。我们定义了标准的Result对象,包含code、message和data三个字段。
前端性能优化:
后端性能优化:
异常处理要全面:不仅处理业务异常,还要考虑系统异常、网络异常等各种边界情况。我们实现了全局异常处理器,统一处理各种异常。
日志记录要详细:完善的日志系统对排查问题至关重要。我们使用SLF4J+Logback,对不同级别的日志进行分类记录。
测试要尽早进行:不要等到开发完成再测试,应该边开发边测试。我们使用JUnit编写单元测试,保证核心逻辑的正确性。
这个项目从技术难度上来说非常适合作为毕业设计选题,它涵盖了企业级应用开发的完整流程,包括需求分析、系统设计、编码实现、测试部署等各个环节。同学们可以根据自己的实际情况调整功能复杂度,比如简化前端界面或减少一些非核心功能。