基于“SpringBoot+uniapp的考研书库微信小程序设计与实现7000字论文

摘要

本文设计并实现了一个基于 SpringBoot 后端和 uniapp 前端的考研书库微信小程序,旨在为考研学生提供便捷的电子资料查询、在线学习和社交交流平台。系统采用前后端分离架构,通过 RESTful API 实现数据交互,支持用户注册登录、资料分类检索、笔记共享、学习计划管理等功能。论文详细阐述了系统需求分析、架构设计、数据库设计、功能模块实现及测试评估过程,最终验证了系统的可行性和实用性。

关键词

SpringBoot;uniapp;微信小程序;考研书库;在线学习平台

1 引言

1.1 研究背景与意义

随着考研人数的逐年增长,考生对优质学习资源的需求日益迫切。传统的考研资料获取方式存在信息分散、更新不及时、检索困难等问题,无法满足考生高效备考的需求。开发一个集资料整合、学习管理和社交交流于一体的考研书库平台,具有重要的现实意义:

  • 整合优质考研资源,解决信息碎片化问题
  • 提供个性化学习支持,提高备考效率
  • 构建学习社区,促进考生间的交流与互助
  • 降低学习成本,实现资源共享与优化配置
1.2 国内外研究现状

国内外针对在线学习平台的研究已经取得了一定进展:

  • 国外:Coursera、edX 等平台提供了丰富的课程资源,但针对考研的专业化平台较少
  • 国内:考研帮、新东方在线等平台主要以课程服务为主,资料共享功能相对薄弱
  • 技术层面:SpringBoot 框架在后端开发中广泛应用,uniapp 在跨平台小程序开发中展现出显著优势

然而,现有的考研学习平台在资料整合深度、个性化服务和社交互动方面仍存在不足。

1.3 研究内容与目标

本文研究内容包括:

  1. 考研书库微信小程序的需求分析与功能设计
  2. 基于 SpringBoot 的后端架构设计与实现
  3. 基于 uniapp 的前端界面开发与交互实现
  4. 数据库设计与优化
  5. 系统测试与性能评估

研究目标是开发一个功能完善、性能稳定、界面友好的考研书库微信小程序,满足考研学生的学习需求。

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

七、其他案例:

相关推荐
Maitians1 小时前
微信小程序 - 保存手机号等信息到通讯录
微信小程序·小程序
Maitians3 小时前
微信小程序 - 手机震动
微信小程序·小程序
像素之间4 小时前
在微信小程序中使用骨架屏
微信小程序·小程序
moxiaoran57534 小时前
uni-app学习笔记三十六--分段式选项卡组件的使用
笔记·学习·uni-app
moxiaoran57536 小时前
uni-app项目实战笔记1--创建项目和实现首页轮播图功能
笔记·uni-app
!win !7 小时前
uni-app项目怎么实现多服务环境切换
前端·uni-app
xw57 小时前
uni-app项目怎么实现多服务环境切换
前端·uni-app
对酒当歌丶人生几何8 小时前
Uniapp实现多选下拉框
前端·javascript·uni-app
浩星9 小时前
uniapp请求接口封装
uni-app
满分观察网友z10 小时前
uni-app 滚动视图scroll-view从入门到精通
uni-app