摘要
本文设计并实现了一个基于 SpringBoot 后端和 uniapp 前端的考研书库微信小程序,旨在为考研学生提供便捷的电子资料查询、在线学习和社交交流平台。系统采用前后端分离架构,通过 RESTful API 实现数据交互,支持用户注册登录、资料分类检索、笔记共享、学习计划管理等功能。论文详细阐述了系统需求分析、架构设计、数据库设计、功能模块实现及测试评估过程,最终验证了系统的可行性和实用性。
关键词
SpringBoot;uniapp;微信小程序;考研书库;在线学习平台
1 引言
1.1 研究背景与意义
随着考研人数的逐年增长,考生对优质学习资源的需求日益迫切。传统的考研资料获取方式存在信息分散、更新不及时、检索困难等问题,无法满足考生高效备考的需求。开发一个集资料整合、学习管理和社交交流于一体的考研书库平台,具有重要的现实意义:
- 整合优质考研资源,解决信息碎片化问题
- 提供个性化学习支持,提高备考效率
- 构建学习社区,促进考生间的交流与互助
- 降低学习成本,实现资源共享与优化配置
1.2 国内外研究现状
国内外针对在线学习平台的研究已经取得了一定进展:
- 国外:Coursera、edX 等平台提供了丰富的课程资源,但针对考研的专业化平台较少
- 国内:考研帮、新东方在线等平台主要以课程服务为主,资料共享功能相对薄弱
- 技术层面:SpringBoot 框架在后端开发中广泛应用,uniapp 在跨平台小程序开发中展现出显著优势
然而,现有的考研学习平台在资料整合深度、个性化服务和社交互动方面仍存在不足。
1.3 研究内容与目标
本文研究内容包括:
- 考研书库微信小程序的需求分析与功能设计
- 基于 SpringBoot 的后端架构设计与实现
- 基于 uniapp 的前端界面开发与交互实现
- 数据库设计与优化
- 系统测试与性能评估
研究目标是开发一个功能完善、性能稳定、界面友好的考研书库微信小程序,满足考研学生的学习需求。
2 相关技术与理论基础
2.1 SpringBoot 框架
SpringBoot 是基于 Spring 的快速应用开发框架,具有以下特点:
- 自动配置,减少 XML 配置文件
- 内嵌 Tomcat 等服务器,简化部署
- 提供 Actuator 组件,便于监控系统运行状态
- 支持各种数据库和缓存技术
2.2 uniapp 框架
uniapp 是一个使用 Vue.js 开发所有前端应用的框架,具有以下优势:
- 一套代码可同时发布到微信小程序、H5、APP 等多个平台
- 兼容微信小程序原生组件和 API
- 性能接近原生应用
- 丰富的 UI 组件库和插件市场
2.3 微信小程序开发
微信小程序是一种轻量级应用,具有以下特点:
- 无需下载安装,触手可及
- 基于微信生态,用户基数大
- 提供丰富的 API 接口,如支付、分享、位置等
- 开发成本低,维护方便
2.4 数据库技术
本系统采用 MySQL 作为关系型数据库,Redis 作为缓存数据库:
- MySQL:支持事务处理,适合存储结构化数据
- Redis:高性能内存数据库,适合缓存热点数据,提高系统响应速度
3 系统需求分析
3.1 功能需求
3.1.1 用户管理模块
- 用户注册与登录
- 个人信息管理
- 学习偏好设置
- 账号安全管理
3.1.2 资料管理模块
- 资料分类与标签
- 资料上传与审核
- 资料检索与预览
- 资料下载与收藏
3.1.3 学习管理模块
- 学习计划制定与跟踪
- 学习进度记录
- 学习笔记管理
- 学习数据分析
3.1.4 社交互动模块
- 评论与回复
- 点赞与分享
- 关注与粉丝管理
- 私信交流
3.1.5 系统管理模块
- 管理员账号管理
- 资料审核管理
- 用户权限管理
- 系统日志管理
3.2 非功能需求
3.2.1 性能需求
- 系统响应时间不超过 3 秒
- 支持至少 1000 个并发用户
- 资料下载速度不低于 1MB/s
3.2.2 安全需求
- 用户信息加密存储
- 资料访问权限控制
- 防止 SQL 注入和 XSS 攻击
- 数据定期备份
3.2.3 可用性需求
- 系统 7×24 小时可用
- 故障恢复时间不超过 1 小时
- 操作界面简洁直观,易于使用
3.2.4 兼容性需求
- 兼容主流微信版本
- 适配不同屏幕尺寸的移动设备
4 系统设计
4.1 总体架构设计
系统采用前后端分离的三层架构:
plaintext
┌─────────────────────────────────────────────┐
│ 表示层 │
│ (uniapp前端、微信小程序界面) │
└─────────────────────┬───────────────────────┘
│ RESTful API
┌─────────────────────┼───────────────────────┐
│ 业务逻辑层 │
│ (SpringBoot、Service、Repository、Controller) │
└─────────────────────┬───────────────────────┘
│ JDBC/MyBatis
┌─────────────────────┼───────────────────────┐
│ 数据访问层 │
│ (MySQL数据库、Redis缓存) │
└─────────────────────────────────────────────┘
4.2 数据库设计
4.2.1 数据库表结构
用户表 (users)
sql
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码(加密存储)',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`phone` varchar(20) DEFAULT NULL COMMENT '手机号',
`gender` tinyint(1) DEFAULT NULL COMMENT '性别(0:未知,1:男,2:女)',
`school` varchar(100) DEFAULT NULL COMMENT '学校',
`major` varchar(100) DEFAULT NULL COMMENT '专业',
`target_school` varchar(100) DEFAULT NULL COMMENT '目标院校',
`target_major` varchar(100) DEFAULT NULL COMMENT '目标专业',
`role` varchar(20) NOT NULL DEFAULT 'user' COMMENT '角色(user/admin)',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
资料表 (resources)
sql
CREATE TABLE `resources` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '资料ID',
`title` varchar(100) NOT NULL COMMENT '资料标题',
`description` text COMMENT '资料描述',
`category_id` bigint(20) NOT NULL COMMENT '分类ID',
`uploader_id` bigint(20) NOT NULL COMMENT '上传者ID',
`file_path` varchar(255) NOT NULL COMMENT '文件路径',
`file_size` bigint(20) NOT NULL COMMENT '文件大小(字节)',
`file_type` varchar(20) NOT NULL COMMENT '文件类型(pdf/doc/xls等)',
`download_count` int(11) NOT NULL DEFAULT '0' COMMENT '下载次数',
`view_count` int(11) NOT NULL DEFAULT '0' COMMENT '浏览次数',
`score` decimal(3,1) DEFAULT '0.0' COMMENT '评分',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态(0:待审核,1:已通过,2:已拒绝)',
`is_free` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否免费(0:付费,1:免费)',
`price` decimal(10,2) DEFAULT '0.00' COMMENT '价格',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_category_id` (`category_id`),
KEY `idx_uploader_id` (`uploader_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资料表';
资料分类表 (resource_categories)
sql
CREATE TABLE `resource_categories` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`name` varchar(50) NOT NULL COMMENT '分类名称',
`parent_id` bigint(20) DEFAULT NULL COMMENT '父分类ID',
`level` int(11) NOT NULL DEFAULT '1' COMMENT '分类级别',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资料分类表';
资料标签表 (resource_tags)
sql
CREATE TABLE `resource_tags` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '标签ID',
`name` varchar(50) NOT NULL COMMENT '标签名称',
`count` int(11) NOT NULL DEFAULT '0' COMMENT '使用次数',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资料标签表';
资料 - 标签关联表 (resource_tag_relations)
sql
CREATE TABLE `resource_tag_relations` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '关联ID',
`resource_id` bigint(20) NOT NULL COMMENT '资料ID',
`tag_id` bigint(20) NOT NULL COMMENT '标签ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_resource_tag` (`resource_id`,`tag_id`),
KEY `idx_tag_id` (`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资料-标签关联表';
资料评论表 (resource_comments)
sql
CREATE TABLE `resource_comments` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '评论ID',
`resource_id` bigint(20) NOT NULL COMMENT '资料ID',
`user_id` bigint(20) NOT NULL COMMENT '评论用户ID',
`parent_id` bigint(20) DEFAULT NULL COMMENT '父评论ID',
`content` text NOT NULL COMMENT '评论内容',
`like_count` int(11) NOT NULL DEFAULT '0' COMMENT '点赞数',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_resource_id` (`resource_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资料评论表';
学习计划表 (study_plans)
sql
CREATE TABLE `study_plans` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '计划ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`title` varchar(100) NOT NULL COMMENT '计划标题',
`description` text COMMENT '计划描述',
`start_date` date NOT NULL COMMENT '开始日期',
`end_date` date NOT NULL COMMENT '结束日期',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态(0:进行中,1:已完成,2:已取消)',
`progress` int(11) NOT NULL DEFAULT '0' COMMENT '进度(0-100)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学习计划表';
学习计划项表 (study_plan_items)
sql
CREATE TABLE `study_plan_items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '计划项ID',
`plan_id` bigint(20) NOT NULL COMMENT '计划ID',
`title` varchar(100) NOT NULL COMMENT '计划项标题',
`description` text COMMENT '计划项描述',
`plan_date` date NOT NULL COMMENT '计划日期',
`start_time` time DEFAULT NULL COMMENT '开始时间',
`end_time` time DEFAULT NULL COMMENT '结束时间',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态(0:未完成,1:已完成)',
`priority` tinyint(1) NOT NULL DEFAULT '2' COMMENT '优先级(1:高,2:中,3:低)',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_plan_id` (`plan_id`),
KEY `idx_plan_date` (`plan_date`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学习计划项表';
学习笔记表 (study_notes)
sql
CREATE TABLE `study_notes` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '笔记ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`title` varchar(100) NOT NULL COMMENT '笔记标题',
`content` text NOT NULL COMMENT '笔记内容',
`resource_id` bigint(20) DEFAULT NULL COMMENT '关联资料ID',
`category_id` bigint(20) DEFAULT NULL COMMENT '分类ID',
`is_public` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否公开(0:私密,1:公开)',
`like_count` int(11) NOT NULL DEFAULT '0' COMMENT '点赞数',
`view_count` int(11) NOT NULL DEFAULT '0' COMMENT '浏览数',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_resource_id` (`resource_id`),
KEY `idx_category_id` (`category_id`),
KEY `idx_is_public` (`is_public`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学习笔记表';
用户关注表 (user_follows)
sql
CREATE TABLE `user_follows` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '关注ID',
`follower_id` bigint(20) NOT NULL COMMENT '关注者ID',
`followed_id` bigint(20) NOT NULL COMMENT '被关注者ID',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_follower_followed` (`follower_id`,`followed_id`),
KEY `idx_followed_id` (`followed_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户关注表';
4.3 系统架构图
4.4 部署架构图
4.5 用例图
4.6 界面原型
4.6.1 首页
首页展示热门资料、推荐分类、学习日历等信息,提供搜索和导航功能。
4.6.2 资料列表页
按分类或标签展示资料列表,支持筛选和排序功能。
4.6.3 资料详情页
展示资料详细信息,包括标题、描述、下载次数、评分等,提供下载和评论功能。
4.6.4 学习计划页
展示用户的学习计划和进度,支持创建、编辑和删除计划。
4.6.5 学习笔记页
展示用户的学习笔记,支持创建、编辑和分享笔记。
4.6.6 个人中心页
展示用户个人信息、收藏、关注等内容,提供账号管理功能。
5 系统实现
5.1 后端实现
5.1.1 项目结构
plaintext
kaoyan-bookstore/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── kaoyan/
│ │ │ ├── KaoyanApplication.java
│ │ │ ├── config/ # 配置类
│ │ │ ├── controller/ # 控制器
│ │ │ ├── service/ # 服务层
│ │ │ ├── repository/ # 数据访问层
│ │ │ ├── model/ # 实体类
│ │ │ ├── dto/ # 数据传输对象
│ │ │ ├── exception/ # 异常处理
│ │ │ ├── util/ # 工具类
│ │ │ └── security/ # 安全配置
│ │ └── resources/
│ │ ├── application.yml # 配置文件
│ │ ├── mapper/ # MyBatis映射文件
│ │ ├── static/ # 静态资源
│ │ └── templates/ # 模板文件
│ └── test/ # 测试代码
└── pom.xml # Maven配置文件
5.1.2 核心代码实现
用户认证与授权
java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/auth/**", "/api/resources/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
资料服务实现
java
@Service
@Transactional
public class ResourceServiceImpl implements ResourceService {
@Autowired
private ResourceRepository resourceRepository;
@Autowired
private ResourceCategoryRepository categoryRepository;
@Autowired
private FileStorageService fileStorageService;
@Override
public ResourceDTO uploadResource(MultipartFile file, ResourceCreateDTO resourceDTO, Long userId) {
// 验证分类是否存在
ResourceCategory category = categoryRepository.findById(resourceDTO.getCategoryId())
.orElseThrow(() -> new ResourceNotFoundException("分类不存在"));
// 保存文件
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = "/api/resources/download/" + fileName;
// 创建资源
Resource resource = new Resource();
resource.setTitle(resourceDTO.getTitle());
resource.setDescription(resourceDTO.getDescription());
resource.setCategory(category);
resource.setUploaderId(userId);
resource.setFilePath(fileName);
resource.setFileSize(file.getSize());
resource.setFileType(file.getContentType());
resource.setStatus(ResourceStatus.PENDING);
resource.setIsFree(resourceDTO.getIsFree());
resource.setPrice(resourceDTO.getPrice());
// 保存资源
Resource savedResource = resourceRepository.save(resource);
// 处理标签
if (resourceDTO.getTagIds() != null && !resourceDTO.getTagIds().isEmpty()) {
// 标签关联逻辑
}
return convertToDTO(savedResource);
}
@Override
public Page<ResourceDTO> getAllResources(Pageable pageable) {
Page<Resource> resources = resourceRepository.findAllByStatus(ResourceStatus.APPROVED, pageable);
return resources.map(this::convertToDTO);
}
@Override
public ResourceDTO getResourceById(Long id) {
Resource resource = resourceRepository.findByIdAndStatus(id, ResourceStatus.APPROVED)
.orElseThrow(() -> new ResourceNotFoundException("资源不存在"));
// 更新浏览次数
resource.setViewCount(resource.getViewCount() + 1);
resourceRepository.save(resource);
return convertToDTO(resource);
}
@Override
public ResourceDTO updateResource(Long id, ResourceUpdateDTO resourceDTO, Long userId) {
Resource resource = resourceRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("资源不存在"));
// 验证权限
if (!resource.getUploaderId().equals(userId)) {
throw new AccessDeniedException("无权修改此资源");
}
// 更新资源信息
resource.setTitle(resourceDTO.getTitle());
resource.setDescription(resourceDTO.getDescription());
resource.setStatus(resourceDTO.getStatus());
resource.setIsFree(resourceDTO.getIsFree());
resource.setPrice(resourceDTO.getPrice());
// 处理分类更新
if (resourceDTO.getCategoryId() != null) {
ResourceCategory category = categoryRepository.findById(resourceDTO.getCategoryId())
.orElseThrow(() -> new ResourceNotFoundException("分类不存在"));
resource.setCategory(category);
}
// 保存更新
Resource updatedResource = resourceRepository.save(resource);
// 处理标签更新
if (resourceDTO.getTagIds() != null) {
// 标签更新逻辑
}
return convertToDTO(updatedResource);
}
@Override
public void deleteResource(Long id, Long userId) {
Resource resource = resourceRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("资源不存在"));
// 验证权限
if (!resource.getUploaderId().equals(userId)) {
throw new AccessDeniedException("无权删除此资源");
}
// 删除文件
fileStorageService.deleteFile(resource.getFilePath());
// 删除资源
resourceRepository.delete(resource);
}
private ResourceDTO convertToDTO(Resource resource) {
ResourceDTO dto = new ResourceDTO();
BeanUtils.copyProperties(resource, dto);
dto.setCategoryId(resource.getCategory().getId());
dto.setCategoryName(resource.getCategory().getName());
// 设置标签
List<ResourceTag> tags = resource.getTags();
if (tags != null && !tags.isEmpty()) {
dto.setTagNames(tags.stream().map(ResourceTag::getName).collect(Collectors.toList()));
}
return dto;
}
}
学习计划服务实现
java
@Service
@Transactional
public class StudyPlanServiceImpl implements StudyPlanService {
@Autowired
private StudyPlanRepository planRepository;
@Autowired
private StudyPlanItemRepository itemRepository;
@Override
public StudyPlanDTO createPlan(StudyPlanCreateDTO planDTO, Long userId) {
// 创建学习计划
StudyPlan plan = new StudyPlan();
plan.setTitle(planDTO.getTitle());
plan.setDescription(planDTO.getDescription());
plan.setStartDate(planDTO.getStartDate());
plan.setEndDate(planDTO.getEndDate());
plan.setUserId(userId);
plan.setStatus(PlanStatus.IN_PROGRESS);
plan.setProgress(0);
// 保存计划
StudyPlan savedPlan = planRepository.save(plan);
// 处理计划项
if (planDTO.getItems() != null && !planDTO.getItems().isEmpty()) {
List<StudyPlanItem> items = planDTO.getItems().stream()
.map(itemDTO -> {
StudyPlanItem item = new StudyPlanItem();
item.setPlan(savedPlan);
item.setTitle(itemDTO.getTitle());
item.setDescription(itemDTO.getDescription());
item.setPlanDate(itemDTO.getPlanDate());
item.setStartTime(itemDTO.getStartTime());
item.setEndTime(itemDTO.getEndTime());
item.setStatus(ItemStatus.UNFINISHED);
item.setPriority(itemDTO.getPriority());
return item;
})
.collect(Collectors.toList());
itemRepository.saveAll(items);
}
return convertToDTO(savedPlan);
}
@Override
public Page<StudyPlanDTO> getPlansByUser(Long userId, Pageable pageable) {
Page<StudyPlan> plans = planRepository.findByUserId(userId, pageable);
return plans.map(this::convertToDTO);
}
@Override
public StudyPlanDTO getPlanById(Long id, Long userId) {
StudyPlan plan = planRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("计划不存在"));
// 验证权限
if (!plan.getUserId().equals(userId)) {
throw new AccessDeniedException("无权查看此计划");
}
return convertToDTO(plan);
}
@Override
public StudyPlanDTO updatePlan(Long id, StudyPlanUpdateDTO planDTO, Long userId) {
StudyPlan plan = planRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("计划不存在"));
// 验证权限
if (!plan.getUserId().equals(userId)) {
throw new AccessDeniedException("无权修改此计划");
}
// 更新计划信息
plan.setTitle(planDTO.getTitle());
plan.setDescription(planDTO.getDescription());
plan.setStartDate(planDTO.getStartDate());
plan.setEndDate(planDTO.getEndDate());
plan.setStatus(planDTO.getStatus());
// 保存更新
StudyPlan updatedPlan = planRepository.save(plan);
// 更新进度
updatePlanProgress(updatedPlan.getId());
return convertToDTO(updatedPlan);
}
@Override
public void deletePlan(Long id, Long userId) {
StudyPlan plan = planRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("计划不存在"));
// 验证权限
if (!plan.getUserId().equals(userId)) {
throw new AccessDeniedException("无权删除此计划");
}
// 删除计划项
itemRepository.deleteByPlanId(id);
// 删除计划
planRepository.delete(plan);
}
@Override
public StudyPlanItemDTO createPlanItem(StudyPlanItemCreateDTO itemDTO, Long planId, Long userId) {
StudyPlan plan = planRepository.findById(planId)
.orElseThrow(() -> new ResourceNotFoundException("计划不存在"));
// 验证权限
if (!plan.getUserId().equals(userId)) {
throw new AccessDeniedException("无权添加计划项");
}
// 创建计划项
StudyPlanItem item = new StudyPlanItem();
item.setPlan(plan);
item.setTitle(itemDTO.getTitle());
item.setDescription(itemDTO.getDescription());
item.setPlanDate(itemDTO.getPlanDate());
item.setStartTime(itemDTO.getStartTime());
item.setEndTime(itemDTO.getEndTime());
item.setStatus(ItemStatus.UNFINISHED);
item.setPriority(itemDTO.getPriority());
// 保存计划项
StudyPlanItem savedItem = itemRepository.save(item);
// 更新计划进度
updatePlanProgress(planId);
return convertToDTO(savedItem);
}
private void updatePlanProgress(Long planId) {
StudyPlan plan = planRepository.findById(planId)
.orElseThrow(() -> new ResourceNotFoundException("计划不存在"));
List<StudyPlanItem> items = itemRepository.findByPlanId(planId);
if (items.isEmpty()) {
plan.setProgress(0);
} else {
long finishedCount = items.stream()
.filter(item -> item.getStatus() == ItemStatus.FINISHED)
.count();
int progress = (int) (finishedCount * 100 / items.size());
plan.setProgress(progress);
}
// 如果所有项都完成,将计划状态设为已完成
if (plan.getProgress() == 100 && plan.getStatus() == PlanStatus.IN_PROGRESS) {
plan.setStatus(PlanStatus.COMPLETED);
}
planRepository.save(plan);
}
private StudyPlanDTO convertToDTO(StudyPlan plan) {
StudyPlanDTO dto = new StudyPlanDTO();
BeanUtils.copyProperties(plan, dto);
// 获取计划项
List<StudyPlanItem> items = itemRepository.findByPlanId(plan.getId());
if (items != null && !items.isEmpty()) {
dto.setItems(items.stream().map(this::convertItemToDTO).collect(Collectors.toList()));
}
return dto;
}
private StudyPlanItemDTO convertItemToDTO(StudyPlanItem item) {
StudyPlanItemDTO dto = new StudyPlanItemDTO();
BeanUtils.copyProperties(item, dto);
dto.setPlanId(item.getPlan().getId());
return dto;
}
}
5.2 前端实现
前端使用 uniapp 框架,采用组件化开发方式,主要实现以下功能:
5.2.1 登录注册页面
- 微信一键登录
- 手机号 + 验证码登录
- 密码登录
- 注册功能
5.2.2 资料列表与详情页面
- 分类导航
- 资料列表展示
- 搜索功能
- 资料详情展示
- 下载功能
- 评论功能
5.2.3 学习计划页面
- 计划列表
- 计划详情
- 计划项管理
- 进度统计
- 日历视图
5.2.4 学习笔记页面
- 笔记列表
- 笔记详情
- 新建笔记
- 编辑笔记
- 分享笔记
5.2.5 个人中心页面
- 个人信息展示
- 资料收藏
- 关注用户
- 学习统计
- 设置功能
以下是 uniapp 前端的部分核心代码:
登录页面实现
javascript
<template>
<view class="login-container">
<view class="logo">
博主介绍:硕士研究生,专注于信息化技术领域开发与管理,会使用java、标准c/c++等开发语言,以及毕业项目实战✌
从事基于java BS架构、CS架构、c/c++ 编程工作近16年,拥有近12年的管理工作经验,拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经验。
先后担任过技术总监、部门经理、项目经理、开发组长、java高级工程师及c++工程师等职位,在工业互联网、国家标识解析体系、物联网、分布式集群架构、大数据通道处理、接口开发、远程教育、办公OA、财务软件(工资、记账、决策、分析、报表统计等方面)、企业内部管理软件(ERP、CRM等)、arggis地图等信息化建设领域有较丰富的实战工作经验;拥有BS分布式架构集群、数据库负载集群架构、大数据存储集群架构,以及高并发分布式集群架构的设计、开发和部署实战经验;拥有大并发访问、大数据存储、即时消息等瓶颈解决方案和实战经验。
拥有产品研发和发明专利申请相关工作经验,完成发明专利构思、设计、编写、申请等工作,并获得发明专利1枚。
-----------------------------------------------------------------------------------
大家在毕设选题、项目升级、论文写作,就业毕业等相关问题都可以给我留言咨询,非常乐意帮助更多的人或加w 908925859。
相关博客地址:
csdn专业技术博客:https://blog.csdn.net/mr_lili_1986?type=blog
Iteye博客: https://www.iteye.com/blog/user/mr-lili-1986-163-com
门户:http://www.petsqi.cn
七、其他案例: