Spring Boot + 微信小程序:高校教师成果管理系统的全栈实现
简介:面向计算机相关专业毕业设计的高校教师成果管理小程序项目,基于SpringBoot框架开发,用于实现教师教学、科研、社会服务等成果的高效录入、查询与统计,为高校管理层提供准确数据支持。压缩包共1055个文件,大小16.21MB,核心内容包括Java后端源码、Vue前端工程、WXML/WXSS小程序页面、JSON配置文件、SQL数据库脚本,以及论文、开发文档、数据库设计文档等,类型覆盖完整,便于按模块对照学习。目前已有39人学习下载。项目附带了经导师认可的高分设计论文与说明文档,源码可本地编译运行,适合正在完成大作业、毕业设计的计算机专业学生,也适合项目实战练习者。通过实际部署与调试,可系统掌握SpringBoot自动配置与接口开发、微信小程序前后端交互、数据库表结构设计等关键技能,是理论与实践结合的综合性学习资料。
1. 高校教师成果管理小程序:三个角色、四类模块与这套方案的适用场景
每年毕业设计旺季,都会有一类下载量很高的压缩包,标题就是“高校教师成果管理小程序的设计与实现springboot”,里面通常塞着源码、论文、说明文档和数据库文档。但很多人解压之后第一反应是懵:数据库脚本导不进去,后端一启动就报红,小程序端打开是白屏。它本质不是一个“开箱即用的产品”,而是一套教学型全栈项目:微信小程序承担教师端的成果填报、查询与审核状态展示,Spring Boot 提供接口、权限和文件存储,论文和数据库文档用来梳理设计思路并支撑答辩。适合两类人——一类是 Java 方向做课题设计的在校生,另一类是院系或科研处想快速搭一套成果登记工具、又不想从零开始的技术人员。这套方案的价值在于业务链路短、角色分明、前后端分离清晰,能在一周内跑通并讲明白。
2. 从业务到表结构:成果类型、角色权限与数据库文档的阅读顺序
拿到压缩包先别急着启动项目,第一步该做的是把数据库文档和 SQL 脚本理清楚。高校教师成果管理的业务不复杂,但涉及的类型和状态很多,表结构设计得是否合理,直接决定后面 Spring Boot 代码和小程序端好不好写,也决定论文里“数据库设计”这一章有没有内容可写。
2.1 成果类型与表字段映射
教师成果通常被拆成这么几类:论文、专利、纵向课题、横向课题、获奖、专著、软件著作权。每类成果的字段差异很大——论文要期刊名和收录级别,专利要专利号和授权日期,课题要立项编号和经费金额。常见做法是建一张ts_achievement主表存公共字段,类型用枚举值标记,差异化信息或留空、或用统一备注字段保存,不再为每个类型单独建表。这样可以避免后期加“标准制定”“教学成果奖”这类新类型时还要改表结构。
CREATE TABLE `ts_achievement` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL COMMENT '提交人ID,关联ts_user', `type` tinyint(4) NOT NULL COMMENT '1论文 2专利 3课题 4获奖 5专著 6软著', `title` varchar(200) NOT NULL COMMENT '成果名称', `journal` varchar(200) DEFAULT NULL COMMENT '期刊/出版社/授权单位', `approve_date` date DEFAULT NULL COMMENT '认定日期', `level` varchar(50) DEFAULT NULL COMMENT '级别:SCI/EI/核心/省级等', `attachment_path` varchar(255) DEFAULT NULL COMMENT '附件相对路径', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0待审核 1通过 2退回', `submit_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user` (`user_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;这里的type字段用 tinyint 而不是字符串,是为了减少存储开销、方便做统计分组。status是成果流转的核心,教师端所有列表页都围绕它做筛选。attachment_path只存相对路径,不存完整 URL,是因为部署环境切换时域名和端口会变,相对路径配合配置文件里的upload.dir前缀拼装,能少踩很多环境相关的坑。idx_user和idx_status两个索引,是为了应对“某教师查看我的成果列表”和“秘书按状态过滤待审核列表”这两条高频查询路径。
2.2 三套角色与权限边界
这类系统最常见的是三角色模型:管理员、院系秘书、普通教师。管理员负责维护用户、配置学院和成果分类;秘书负责审核本院教师提交的成果、打回并填写意见;教师负责填报成果、修改被退回的记录、查看已通过列表。权限控制不推荐上 Spring Security 全家桶,毕设项目或内部工具用拦截器 +role字段判断就够,单点逻辑更好答辩。
| 角色 | 可访问模块 | 关键操作 | 后端鉴权方式 |
|---|---|---|---|
| 管理员 | 用户管理、学院管理、成果统计 | 新增教师账号、修改密码、全院数据导出 | role=1放行 |
| 院系秘书 | 成果审核、本院统计 | 通过/退回成果、填写审核意见 | role=2放行 |
| 普通教师 | 成果填报、我的成果 | 提交新成果、修改退回记录 | role=3放行 |
表结构上需要有ts_user表承载账号和角色,college_id字段挂学院。很多项目还会单独建一张ts_college学院表,秘书和教师都关联到学院,这样院系级统计直接按college_id分组就行,不用在代码里做字符串匹配。
CREATE TABLE `ts_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(128) NOT NULL COMMENT 'BCrypt或MD5加盐后的密文', `role` tinyint(4) NOT NULL DEFAULT '3', `college_id` bigint(20) DEFAULT NULL, `real_name` varchar(50) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;注意uk_username唯一索引,避免重复账号。密码字段长度给到 128,是因为如果用 BCrypt 加密,密文加上盐值会超过传统 32 位长度,这是新手最容易栽倒的小地方。role给默认值 3,表示新增账号默认是普通教师,管理员在后台再调整。
2.3 数据库文档和 SQL 脚本在项目里的角色
压缩包里的“数据库文档”一般包含三样东西:建库建表 SQL 脚本、数据库设计说明书(Word 或 Markdown)、ER 图。阅读顺序有个习惯:先看说明书里的“数据字典”,确认每张表是干什么的;再用 Navicat 或 MySQL Workbench 导入 SQL 脚本;最后对照 ER 图理解表之间的外键关系,特别是ts_achievement和ts_review_log审核日志表的关系。
审核这件事容易被忽略,但论文里“系统详细设计”部分要画时序图,没有审核日志表是很难讲清楚闭环的。常见设计是建一张ts_review_log,记录谁在什么时间审核了哪个成果、结论是什么、意见写了什么,这样教师端能展示“退回原因”,管理员也能追溯审核过程。
CREATE TABLE `ts_review_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `achievement_id` bigint(20) NOT NULL, `reviewer_id` bigint(20) NOT NULL, `result` tinyint(4) NOT NULL COMMENT '1通过 2退回', `opinion` varchar(500) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_achievement` (`achievement_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;导入 SQL 时如果报错,九成是版本或编码问题:MySQL 5.7 与 8.0 对utf8mb4排序规则的处理略有差异,脚本头最好有SET NAMES utf8mb4;,字符集统一用utf8mb4_general_ci。另外要注意脚本里是否已包含DROP TABLE IF EXISTS,没有的话重复导入会直接报“表已存在”,这也是学生最常来问的问题。
3. Spring Boot 后端实现:登录鉴权、成果 CRUD 与附件上传的最小链路
后端是整个系统的“黑匣子”,小程序端所有数据都从这里来。先从工程结构入手,再看三条核心链路:登录鉴权、成果增删改查、附件上传。熟悉这三条,其他模块基本可以照着扩展。
3.1 工程结构与配置:先认清 src 下面有哪些东西
Spring Boot 工程一般是 Maven 结构,拿到源码后先看pom.xml里引了哪些依赖。这类项目常见组合是spring-boot-starter-web做接口、mybatis-plus或mybatis做数据访问、jjwt做登录令牌、fastjson或jackson做 JSON 处理。如果依赖里出现spring-boot-starter-security,有多种可能——有些作者只是引了依赖但没启用,有些则配了登录过滤链,需要看config包下有没有SecurityConfig。
工程内部一般按controller、service、mapper、entity、config、common分包。排错时先看application.yml,这是整个 Spring Boot 配置的入口:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/teacher_achievement?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 servlet: multipart: max-file-size: 20MB max-request-size: 50MB mybatis-plus: mapper-locations: classpath*:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl upload: dir: ./upload/serverTimezone=Asia/Shanghai必须写,MySQL 8 默认时区是 UTC,Java 侧拿到的时间会差 8 小时,报错信息往往不是“时区不对”,而是日期字段莫名其妙变了。multipart配置控制附件上传大小,教师成果的 PDF 或截图一般不会超过 20MB,给太小则传大文件时前端直接失败。upload.dir是自定义配置项,对应代码里用@Value("${upload.dir}")注入,方便部署时改成服务器绝对路径。
3.2 JWT 登录与拦截器:小程序每次请求都带身份
小程序没有 Cookie 概念,最通用的做法是登录时把用户名密码发给后端,后端校验通过后生成 JWT 令牌返回,小程序把令牌存到本地存储,后续所有请求在 Header 里带Authorization。后端用一个拦截器统一校验,而不是在每个 Controller 方法里重复判断。
public class JwtInterceptor implements HandlerInterceptor { @Value("${jwt.secret:mySecretKey}") private String secretKey; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains("/login") || request.getRequestURI().contains("/register")) { return true; } String token = request.getHeader("Authorization"); if (token == null || token.isEmpty()) { response.setStatus(401); return false; } try { Claims claims = Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token) .getBody(); request.setAttribute("userId", claims.get("userId")); request.setAttribute("role", claims.get("role")); return true; } catch (Exception e) { response.setStatus(401); return false; } } }这个拦截器的逻辑很简单:登录接口直接放行,其他请求必须带合法令牌,解析成功后把userId和role放进 request 属性,Controller 里直接通过@RequestAttribute取。生产环境密钥要放到配置文件中、甚至用环境变量注入,而且至少 32 位,硬编码在代码里属于安全隐患,答辩时也会被老师追问。另外,jjwt的版本差异会导致 API 不同——0.9.x 用setSigningKey,0.11.x 用Keys.hmacShaKeyFor方法,源码里如果编译报错,先检查依赖版本和代码是否匹配。
3.3 成果 CRUD 与审核状态流转
成果模块是核心,接口一般有:提交成果、分页查询我的成果、修改未审核成果、删除指定成果、秘书端审核通过/退回。列表查询最大的坑是分页。MyBatis-Plus 提供了Page对象,但要在配置类里加分页插件,否则分页不生效、接口返回全部数据。
@PostMapping("/submit") public R submit(@RequestBody Achievement achievement, @RequestAttribute("userId") Long userId) { achievement.setUserId(userId); achievement.setStatus(0); achievementService.save(achievement); return R.ok(); } @GetMapping("/myList") public R myList(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer size, @RequestAttribute("userId") Long userId) { Page<Achievement> p = new Page<>(page, size); LambdaQueryWrapper<Achievement> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Achievement::getUserId, userId) .orderByDesc(Achievement::getSubmitTime); return R.ok(achievementService.page(p, wrapper)); }status字段是审核状态的唯一依据:教师提交时置 0,秘书通过后置 1,退回置 2。修改逻辑要注意——只有status=0或status=2的记录才允许修改,已通过的要锁定,否则教师改了已审核数据,秘书那里看到的还是旧内容,这种前后端状态不一致是这类系统最容易出现的“玄学 bug”。退回时那条ts_review_log里的意见要能在教师端原样展示,常见做法是小程序查询成果列表时,顺带查最近一条审核日志。
3.4 附件上传:文件落地与防重命名
成果附件包括论文 PDF、专利证书扫描件、获奖证明图片,上传接口要注意三点:文件重命名、目录创建、返回相对路径。直接用原始文件名保存会导致两个问题——同名文件互相覆盖、文件名含中文或空格时 URL 拼接出错。常见做法是用 UUID 重命名,保留原扩展名。
@PostMapping("/upload") public R upload(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return R.error("文件为空"); } String originalFilename = file.getOriginalFilename(); String ext = ""; if (originalFilename != null && originalFilename.contains(".")) { ext = originalFilename.substring(originalFilename.lastIndexOf(".")); } String newName = UUID.randomUUID().toString().replace("-", "") + ext; File dir = new File(uploadDir); if (!dir.exists()) { dir.mkdirs(); } File dest = new File(dir, newName); try { file.transferTo(dest); } catch (IOException e) { return R.error("上传失败"); } return R.ok(newName); }目录创建不能省,很多服务器上项目运行时的工作目录是临时目录,new File(uploadDir)不会自动建目录,上传第一份文件就报FileNotFoundException。transferTo是 Spring 封装的方法,底层处理了临时文件复制,不要自己再用FileOutputStream写一遍流,容易导致文件不完整。返回的newName就是相对路径,小程序端拿到后在前端拼成完整下载地址。之前见过一个翻车现场:后台上传路径写的是D:/upload/,部署到 Linux 服务器后直接启动失败,路径这种配置应该统一放到application.yml里,不要散落在代码各处。
4. 小程序端对接微信 API:登录态、成果提交与列表加载的完整闭环
小程序端的代码量不大,核心是四件事:登录态管理、请求封装、列表加载、表单提交。微信开发者工具里导入项目后,第一件事不是看页面,而是先确认app.js里的接口地址指向的是不是本机后端。
4.1 登录态管理:从 wx.login 到 token 存入 Storage
微信小程序的登录和传统网页不同,wx.login拿到的code是一串临时凭证,需要发给后端,由后端调用微信接口换取openid。但这类毕业设计项目通常为了省事,直接用小程序的账号密码登录——后端在ts_user表里匹配用户名和密码,把用户信息封装成 JWT 返回,前端存到wx.setStorageSync。两种方式都可以,如果答辩被问“为什么不接微信登录”,可以解释为成果数据涉及教师身份认证,用校内账号体系更合适。
// app.js 中封装登录方法 login(username, password) { return new Promise((resolve, reject) => { wx.request({ url: `${this.globalData.baseUrl}/api/login`, method: 'POST', data: { username, password }, success: (res) => { if (res.data.code === 0) { wx.setStorageSync('token', res.data.data.token) wx.setStorageSync('userInfo', res.data.data.userInfo) resolve(res.data.data) } else { reject(res.data.msg) } }, fail: reject }) }) }baseUrl要放在globalData里,因为开发环境是http://localhost:8080,真机预览时localhost指向手机自己,必须改成电脑在局域网内的 IP,比如http://192.168.1.100:8080。这是小程序联调最常见的坑——开发工具里正常,手机上一请求就失败。登录成功后把 token 和用户信息都存起来,后续每个页面要用用户角色时直接从 storage 取。
4.2 请求封装与列表分页加载
小程序里不能直接使用 axios,最通用的是封装wx.request。所有请求统一带上 token 头、统一处理 401 跳转登录页、统一解析返回结构,这样页面代码会干净很多。
function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: `${getApp().globalData.baseUrl}${url}`, method, data, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') || '' }, success: (res) => { if (res.data.code === 0) { resolve(res.data.data) } else if (res.data.code === 401) { wx.removeStorageSync('token') wx.navigateTo({ url: '/pages/login/login' }) reject(res.data.msg) } else { reject(res.data.msg) } }, fail: reject }) }) }列表页的“加载更多”是微信小程序很常见的需求,对应热搜词里的“页面列表加载更多”。实现上依赖两个能力:onReachBottom页面触底事件和分页参数。页面 data 里维护page、size、totalPage三个变量,每次触底把page+1再请求,后端返回的总记录数除以每页条数得到总页数。
onReachBottom() { if (this.data.page >= this.data.totalPage) { wx.showToast({ title: '没有更多了', icon: 'none' }) return } this.setData({ page: this.data.page + 1 }) this.loadMyList() }这里有一个新手很容易踩的坑:onReachBottom会被反复触发,请求还没返回时用户又滑到底部,会出现重复请求。常见解决办法是在 data 里加一个loading标志位,请求完成后才置为false,触底时判断如果正在加载中就直接 return。
4.3 成果表单提交、附件上传与动态标题
提交成果页是小程序端最复杂的部分,因为它既有普通字段又有文件上传。普通字段用input和picker就行,类型选择用picker组件从数组里选,提交时组装成 JSON 对象。文件上传不能和普通请求混在一起,必须用wx.uploadFile,因为它走的是 multipart 表单格式。
wx.uploadFile({ url: `${getApp().globalData.baseUrl}/api/upload`, filePath: filePath, name: 'file', header: { 'Authorization': wx.getStorageSync('token') }, success: (res) => { const data = JSON.parse(res.data) if (data.code === 0) { this.setData({ attachmentPath: data.data }) } } })wx.uploadFile的header里同样要带 token,否则后端拦截器直接 401。返回的res.data是字符串,必须JSON.parse之后才能取code和data。上传完成拿到相对路径后,表单提交时把这个路径放进成果对象里一起 POST 到/api/submit。页面标题可以用wx.setNavigationBarTitle动态设置,比如秘书登录后标题显示“审核管理”,教师登录后显示“我的成果”,这种细节写在论文的功能展示里很加分,实现也不过一行代码。另外微信小程序顶部导航栏高度在 iPhone X 以上机型有刘海适配问题,但毕设项目一般用默认导航栏,不用自定义,这个坑可以先避开。
4.4 审核状态展示:让教师知道卡在哪一步
成果列表里每条记录都要有状态标识:待审核用黄色,已通过用绿色,已退回用红色,退回的要能看到审核意见。常见做法是列表接口返回的每条数据里带上status和opinion,前端根据状态值渲染不同样式。审核意见后端从ts_review_log最近一条记录里取,小程序端展示时要注意意见可能为 null——后端要做空判断,或者前端opinion || '未填写'这样兜底。这种“状态流转闭环”是答辩时最能体现系统完整性的点,建议论文里画一张状态图。
5. 启动与联调避坑:版本、端口、路径与答辩环节的 5 类高频问题
这类项目打包下载后,跑不起来的原因翻来覆去就那么几个。如果按下面这些逐一排查,九成问题十分钟内能定位。
5.1 现象:Spring Boot 版本太高,启动直接报错或类找不到
原因:压缩包里 pom 使用的是旧写法(比如javax.servlet),而你本地的 Maven 拉到了 Spring Boot 新版依赖。Spring Boot 3.x 起把javax改成jakarta,旧代码里大量import javax.servlet.*会全部编译失败。还有 MyBatis-Plus 等第三方依赖,版本跟不上 Spring Boot 3.x 时会出现ClassNotFoundException。
解决:先看pom.xml里的spring-boot-starter-parent版本,确认是 2.x 还是 3.x,然后统一。如果工程是 2.x 的写法,就不要手动改到 3.x。更稳妥的做法是直接用压缩包作者锁定的版本号,不要轻易升版本——很多项目的“升级失败”都是 Maven 依赖传递和代码写法不一致造成的。
5.2 现象:数据库连接失败,报Public Key Retrieval is not allowed
原因:MySQL 8 默认使用caching_sha2_password加密方式,旧版本 JDBC 驱动连接时会因为无法获取公钥而拒绝认证。
解决:application.yml的数据库 URL 里加allowPublicKeyRetrieval=true,驱动类用com.mysql.cj.jdbc.Driver而不是com.mysql.jdbc.Driver,同时确认 MySQL 服务端口是 3306、不是 3307 之类。如果改了这些还连不上,在命令行用mysql -uroot -p试一下密码是否正确,别在代码层面反复挣扎——密码错是口水问题,不是代码问题。
5.3 现象:开发工具里请求正常,手机真机上一请求就失败
原因:微信小程序的真机调试默认要求请求地址是 HTTPS,而且域名要备案并在小程序后台配置白名单。开发阶段访问本机http://192.168.x.x:8080,真机上会直接报“不在以下 request 合法域名列表中”。
解决:开发阶段在微信开发者工具右上角“详情 - 本地设置”勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。但注意这个选项只对开发工具和“调试模式”下的真机预览生效,想要完整预览必须点预览按钮后,在手机上打开右上角菜单“打开调试”才能跳过域名校验。这个“调试开关”自己会消失,每次重新预览都要再打开一次。
5.4 现象:附件上传成功,但列表页图片和文档打不开
原因:附件存到了上传目录,但返回给前端的访问路径是http://localhost:8080/upload/xxx.pdf这类硬编码地址。换一台机器运行、或者后端端口改了,老地址全部失效。更隐蔽的是,Spring Boot默认不暴露upload目录为静态资源,直接拼路径访问会 404。
解决:一是把上传目录作为静态资源映射,在配置类里registry.addResourceHandler("/upload/**").addResourceLocations("file:" + uploadDir)。二是前端统一从接口拿附件地址,不要手拼。另外部署时上传目录最好放在 jar 包同级目录,这样换服务器时整个目录拷走就行,路径也不会断。
5.5 现象:答辩时老师对着论文画出来的表和代码里的表对不上
原因:写论文时是设计图,写代码时是实现稿,两边没有同步更新。常见差异有:论文里字段叫achievement_name,代码里叫title;论文里说状态有四种,代码里只有三种。这是压缩包项目最容易被挑出问题的地方,也是很多人在答辩现场翻车的高频点。
解决:拿到源码后先跑一个“作弊利器”——把数据库文档里的表名、字段名和entity实体类、mapperXML 里的列名逐一对比。发现不一致时,优先改代码去适配论文,因为论文的评审周期长,改动成本高。改实体类字段映射要小心:MyBatis-Plus 默认开启驼峰转换,approve_date自动映射到approveDate,如果手动改表名反而容易引入新问题。
6. 部署验证与二次开发:从 zip 到能跑的系统的落地节奏
整套项目跑通后,真正考验人的是部署顺序和验证手段。最忌讳的是直接敲mvn spring-boot:run,然后盯着日志干等。按顺序来能省很多时间。
6.1 部署顺序与验证清单
| 步骤 | 操作 | 预期结果 |
|---|---|---|
| 1 | Navicat 执行teacher_achievement.sql | 表结构创建成功,无报错 |
| 2 | 修改application.yml数据库密码为本机值 | 配置无语法错误 |
| 3 | 执行mvn clean package -DskipTests打包 | target 下生成 jar 包 |
| 4 | 启动后端并访问http://localhost:8080/api/login附带测试数据 | 返回 JSON 结构而非 404 |
| 5 | 微信开发者工具导入小程序目录,修改 baseUrl 为本机 IP | 登录页可提交账号 |
| 6 | 提交测试成果并走一遍审核流 | 教师端状态从“待审核”变为“已通过” |
验证的核心是“最小闭环”:先通账号登录,再提一条成果,然后审核通过,最后在列表看到状态变化。这条链路通了,说明后端、数据库、小程序三端都正常。
6.2 给系统加一个“年度统计报表”功能的动刀思路
最常见的二次开发需求是统计模块,比如“按学院统计年度论文数量”。实现上无非三层:Controller 接参数、Mapper 写查询、小程序画图表。Mapper 里用 GROUP BY 一条 SQL 就能搞定:
SELECT college_name, COUNT(*) AS total FROM ts_achievement a JOIN ts_user u ON a.user_id = u.id JOIN ts_college c ON u.college_id = c.id WHERE a.type = 1 AND YEAR(a.approve_date) = #{year} GROUP BY c.id ORDER BY total DESC;Controller 接收year参数,Service 调 Mapper 返回List<Map<String, Object>>,小程序端用wx-charts或 canvas 画柱状图。加功能的原则是从“能跑”到“能讲”:不要贪多,加一个小而完整的模块并更新论文里的设计说明,比堆十个半成品功能更有价值。
6.3 二次开发的三个习惯
一是改代码前先把原工程打包备份,特别是数据库——mysqldump导一份 SQL 存着,这就是后悔药。二是每改一个接口就用浏览器或 Apifox 测一次,不要攒一堆再统一联调,出问题时根本不知道是哪一步引入的。三是改表结构时先看有没有别的表引用了它,比如给ts_achievement加字段问题不大,但改ts_user.id的类型或长度,关联表全部会受影响。
我自己的血泪经验是:这类全栈项目最花时间的不是写代码,是“环境一致化”。数据库版本、JDK 版本、Node 版本、微信开发者工具版本,任何一项和环境不匹配都会冒出新问题。所以拿到压缩包后,先看pom.xml里锁定的 Java 版本,再决定本机要不要装 JDK 8 还是 JDK 17,不要拿新版 JDK 硬跑旧项目。这套项目作为学习材料是完整的:从论文的结构、数据库的三范式设计,到 Spring Boot 的接口分层、小程序端的请求封装,都是业界通用做法。你照着跑通一遍,再往里面加一个自己的小功能,就能把“下载的代码”变成“自己的作品”。希望帮到你。
本文还有配套的精品资源,点击获取