ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+微信小程序宿舍报修系统:从数据库设计到全栈联调实战

SpringBoot+微信小程序宿舍报修系统:从数据库设计到全栈联调实战 简介这份毕业设计资源包面向计算机相关专业学生提供一套基于微信小程序、SpringBoot后端与MySQL数据库的宿舍报修系统完整方案。系统解决传统报修信息整理混乱、安全性差、劳动强度大等问题覆盖用户报修提交、后台处理、状态跟踪等典型业务场景适合用于毕业设计参考或小程序开发入门实践。包体共744个文件压缩包大小约43.59MB包含101个Java后端源码、128个Vue前端文件、30个JSON配置、23个WXML与22个WXSS页面文件、1个SQL数据库脚本以及多份mp4视频教程和文档能从源码、数据、论文、演示视频四个维度支撑整个项目还原。目前已有262人学习下载。整套资料除了可直接运行的源代码外还搭配数据库初始化脚本、论文视频和视频教程便于理解系统设计思路与开发流程同时附带自动化安装、编译、启动的bat脚本可帮助读者快速搭建本地环境缩短从部署到跑通项目的周期。1. 宿舍报修系统小程序一套代码串起学生、宿管、维修工三类角色如果你正在为毕业设计找方向又不想碰那种只改个页面皮、一问业务就露馅的“花瓶项目”宿舍报修系统微信小程序是少数能把全栈链路走完的选题前端是微信小程序后端是 SpringBoot数据落在 MySQL 里三类用户学生报修、宿管派单、维修工接单在同一个工单状态机上流转。它没有复杂的算法但很考工程能力——表怎么设计、接口怎么拆、小程序怎么调后端这些正好是答辩时老师最爱追问的点。这套项目通常随源码附上数据库脚本和论文我按自己做过类似工单系统的经验把这套东西从建库到联调完整拆一遍新手照步骤能跑通熟手可以直接拿来当骨架改造。2. SpringBoot MySQL 后端从工单状态机到接口分层的设计底座2.1 报修工单的数据模型为什么至少要拆四张表很多学生拿到需求第一反应是建一张大表字段全塞进去最后发现加个图片、查个操作记录都无从下手。宿舍报修的核心实体是“工单”但它不是孤立存在的谁报修的、上传了什么照片、谁处理过、处理过程干了什么这些都是独立变化的数据。常见做法是拆成四张核心表再加一张公告表工作量够展示关系也清晰。第一张是用户表字段含 openid微信登录后由后端写入、昵称头像、手机号、角色学生/宿管/维修工、宿舍楼栋和房间号。openid 是用户唯一标识不要拿自增 id 当登录凭证。第二张是报修单表这是全局主表字段包括报修标题、描述、状态、期望上门时间、创建时间、更新时间。第三张是报修图片表和主表一对多因为用户可能一次传多张照片单表塞 text 字段存逗号分隔的 URL 虽然简单但查询某一张图、删除某一帧都很难受。第四张是工单流转日志表记录状态变更历史这是答辩时能讲出彩的设计——它让你随时能回答“你这单子为什么拖了三天”这种问题。状态流转建议用整数枚举0 待受理、1 待维修、2 维修中、3 已完成、4 已关闭。不要用字符串存状态因为“待处理”“处理中”“已完成”这种命名在前后端容易不一致整数加枚举注释反而不会认错。宿管把待受理改成待维修就是“派单”维修工把待维修改成维修中就是“接单”每次变更往流转日志表插入一条记录。2.2 数据库脚本建表、索引与初始化数据的一次到位我给你一份可以直接套用的建库脚本注意字符集和排序规则否则后面中文乱码查起来很玄学。MySQL 8.0 和 5.7 在这份脚本上都能跑唯一区别是 5.7 不用写 utf8mb4_0900_ai_ci用 utf8mb4_general_ci 就行。-- 建库utf8mb4 是必须的纯 utf8 存不了 Emoji小程序昵称里很常见 CREATE DATABASE IF NOT EXISTS dorm_repair DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm_repair; -- 用户表 CREATE TABLE user ( id BIGINT NOT NULL AUTO_INCREMENT, openid VARCHAR(64) NOT NULL COMMENT 微信用户唯一标识, nickname VARCHAR(64) DEFAULT COMMENT 昵称, avatar VARCHAR(255) DEFAULT COMMENT 头像URL, phone VARCHAR(20) DEFAULT COMMENT 手机号, role TINYINT NOT NULL DEFAULT 0 COMMENT 0学生 1宿管 2维修工, building VARCHAR(16) DEFAULT COMMENT 楼栋, room VARCHAR(16) DEFAULT COMMENT 房间号, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB COMMENT用户表; -- 报修单主表 CREATE TABLE repair_order ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 报修人, title VARCHAR(64) NOT NULL COMMENT 报修标题, description TEXT COMMENT 问题描述, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待受理 1待维修 2维修中 3已完成 4已关闭, appointment_time VARCHAR(32) DEFAULT COMMENT 期望上门时间, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status (status), KEY idx_user_id (user_id) ) ENGINEInnoDB COMMENT报修单表; -- 图片表一对多 CREATE TABLE repair_image ( id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, image_url VARCHAR(255) NOT NULL, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB COMMENT报修图片表; -- 流转日志表 CREATE TABLE repair_log ( id BIGINT NOT NULL AUTO_INCREMENT, order_id BIGINT NOT NULL, operator_id BIGINT NOT NULL COMMENT 操作人ID, content VARCHAR(255) NOT NULL COMMENT 日志内容, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB COMMENT工单流转日志表;这份脚本里有几个参数值得注意。索引不要乱加查询最多的是“按状态查工单列表”和“按用户查我的报修”所以只有idx_status和idx_user_id不要给 description 这种 text 字段加索引没用还拖慢写入。update_time用了ON UPDATE CURRENT_TIMESTAMP这是 MySQL 的自动更新机制改行数据时它自己刷新省一行业务代码。初始化数据至少要插三个角色各一个账号学生、宿管、维修工都有一条能登录的 openid。但 openid 是微信登录后才有的所以初始化脚本里只插角色信息openid 留空等真登录时第一次走自动注册逻辑写入。论文的系统测试章节里写“通过预置账号验证三种角色流转”时讲的就是这套初始化数据。2.3 SpringBoot 工程结构Controller 只做翻译Service 不碰数据库SpringBoot 工程的包结构按controller → service → mapper分层entity 对应表、dto 对应请求体。一个常见的反面教材是 Controller 里直接写业务逻辑几百行的接口谁看谁头疼答辩时老师问“这个接口的事务边界在哪”你就答不上来。规范做法是Controller接收参数、调 Service、包装返回体。只做翻译不写 if 嵌套。Service业务规则比如状态机校验“只有待受理的工单才能派单”。Mapper用 MyBatis-Plus 的话连 XML 都省一大半单表 CRUD 不需要写 SQL。看一个登录接口的完整链路这是小程序端调的第一个接口。小程序wx.login()拿到临时 code后端拿 code 去微信接口换 openid然后查用户表查不到就自动注册。RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; /** * 小程序登录code 换 openid用户不存在则自动注册 */ PostMapping(/login) public ResultUserVO login(RequestBody LoginDTO dto) { return Result.success(userService.login(dto.getCode())); } }Service 实现类的核心逻辑是拿 code 调微信接口。这里有个关键参数grant_type必须写死为authorization_code这是微信开放接口的硬性要求。实际项目里不要在前端传 openid 或者 user_id 过来因为前端传什么都能被伪造一定要后端通过 code 换。public UserVO login(String code) { // 1. 请求微信接口code2session 换取 openid String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code code grant_typeauthorization_code; String res restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(res); String openid json.getString(openid); if (openid null) { throw new BizException(微信登录失败 json.getString(errmsg)); } // 2. 查库不存在则自动注册 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setRole(0); // 新用户默认学生 userMapper.insert(user); } // 3. 生成业务 token 返回给前端 return userService.buildUserVO(user); }调用微信接口这一步常见翻车点是 appid 和 secret 配置错了或者用错接口名。记住获取 openid 用的是jscode2session不是getuserinfo后者是旧版接口现在小程序已经不支持了。restTemplate 的依赖在 spring-boot-starter-web 里自带不要重复引包。工单接口是对状态机的直接体现。派单操作翻译成代码就是校验当前状态是 0改成 1插一条日志。如果校验不通过直接抛业务异常由全局异常处理器转成统一返回体前端弹 toast。这就是 Service 层的价值——状态机的规则只允许在这一个地方被修改其他途径一律堵死。Transactional(rollbackFor Exception.class) public void dispatch(Long orderId, Long operatorId) { RepairOrder order repairOrderMapper.selectById(orderId); if (order null) { throw new BizException(工单不存在); } if (order.getStatus() ! 0) { throw new BizException(当前状态不可派单); } order.setStatus(1); repairOrderMapper.updateById(order); RepairLog log new RepairLog(); log.setOrderId(orderId); log.setOperatorId(operatorId); log.setContent(宿管已派单); repairLogMapper.insert(log); }注意Transactional(rollbackFor Exception.class)这个事务注解必须加在 Service 的公开方法上实现类里写。它保证改状态和插日志要么同时成功要么一起回滚不然就变成“状态改了但没有记录”答辩演示时被老师翻出这种逻辑漏洞就很被动。另外控制器的返回体ResultT是统一包装code200表示成功code500表示业务异常前端小程序里只判断 code不要 HTTP 状态码和业务码混着来。3. 微信小程序端登录、报修发布与工单列表的完整交互3.1 页面结构与登录流程只有四个 Tab 的干净骨架小程序端常见的目录结构是pages/下按业务分目录首页报修入口、工单列表、工单详情、我的个人中心。App 的app.json里注册这四个 Tab 页底部导航栏就出来了。强烈建议不要用自定义 tabBar原生 tabBar 的适配成本最低新手折腾自定义导航栏的顶部高度问题纯属浪费时间热搜里“微信小程序顶部导航栏高度”的提问一大片就是因为自找麻烦。登录流程在小程序端只有三步wx.login()拿 codewx.request()发给后端/login接口拿到 token 存到wx.setStorageSync。但要注意wx.login()和wx.getUserProfile()是两回事。前者拿 code 换 openid静默完成不需要用户点按。后者拿用户头像昵称从基础库 2.27.1 版本开始已经不能自动弹窗了必须通过button open-typechooseAvatar让用户主动触发。// utils/auth.js const login () { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { const resData await request.post(/api/user/login, { code: res.code }); wx.setStorageSync(token, resData.token); wx.setStorageSync(userInfo, resData.userInfo); resolve(resData); } else { reject(new Error(微信登录失败)); } }, fail: reject }); }); };代码里request是一个封装了wx.request的工具模块统一在请求头带上 token响应时对code ! 200的做全局 toast。这个封装值得写完整因为整个项目几十个接口都靠它收口。// utils/request.js const request (method, url, data) { const token wx.getStorageSync(token); return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method, data, header: { Authorization: token ? Bearer ${token} : }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail: reject }); }); };BASE_URL 在开发环境填http://localhost:8080上线前改成你的 HTTPS 域名。这里有个隐藏坑开发工具里勾了“不校验合法域名”就能用 localhost 调本地后端但真机预览一打开就白屏报错所有请求带errMsg: request:fail原因就是真机没有跳过域名校验。常见做法是开发阶段用本地 IP真机调试要在详情里勾“不校验合法域名”。3.2 报修发布图片上传的完整链路与临时路径陷阱发布报修是小程序端交互最重的页面标题输入、问题描述、期望上门时间用 picker 组件、上传图片用 chooseMedia 选照片。图片上传不能直接拿着本地临时路径去调后端报修接口——小程序给的临时路径只在当次生命周期有效后端拿它去存数据库毫无意义。必须先调wx.uploadFile把图片传给后端的上传接口拿到永久 URL 后再随表单数据提交。// pages/publish/publish.js const uploadImages async (tempFilePaths) { const uploadTasks tempFilePaths.map((path, index) { return new Promise((resolve, reject) { wx.uploadFile({ url: ${BASE_URL}/api/upload/image, filePath: path, name: file, success: (res) { const data JSON.parse(res.data.trim()); // 注意返回体可能是字符串必须 trim 再 parse resolve(data.data.url); }, fail: reject }); }); }); return Promise.all(uploadTasks); }; const submitOrder async (formData) { const urls await uploadImages(formData.images); const res await request.post(/api/repair/order, { title: formData.title, description: formData.description, images: urls, appointmentTime: formData.appointmentTime }); if (res) { wx.showToast({ title: 提交成功 }); wx.navigateBack(); } };这里有两个细节。第一后台上传接口的返回体一定要设计成{code:200, data:{url:https://...}}的结构但wx.uploadFile的 success 回调里 res.data 是字符串不是对象很多新手忘了 JSON.parse 直接 res.data.url结果一直取不到值。第二wx.chooseMedia的count参数建议限制为 3一是省存储二是后端保存时也干净。sizeType建议compressed报修场景的照片不需要原图省流量也省服务端带宽。上传接口的后端实现要处理MultipartFile把文件写到服务器本地目录或对象存储然后拼一个可访问的 URL 返回。如果你用的 MySQL 是本地跑的后端也跑在同一台机器最省事的方式是配一个静态资源映射把上传目录映射成/images/**这样 URL 就是http://ip:8080/images/xxx.jpg。生产环境则建议用对象存储但你只需要在 Service 里替换实现类接口签名不用动。3.3 工单列表下拉刷新、滚动加载与角色视角切换列表页是小程序端碰到“微信小程序页面列表加载更多”热搜词最多的地方。报修工单列表按角色区分视角学生看“我的报修”宿管看“全部待受理”维修工看“我的待办”。接口统一走/api/repair/order/list?pageNum1pageSize10status0后端返回分页对象{total, records}小程序端做触底加载。// pages/order-list/order-list.js const loadPage async (isRefresh) { if (this.data.loading) return; this.setData({ loading: true }); const pageNum isRefresh ? 1 : this.data.pageNum 1; const res await request.get(/api/repair/order/list, { pageNum, pageSize: this.data.pageSize, status: this.data.currentStatus }); const records res.records || []; this.setData({ orders: isRefresh ? records : this.data.orders.concat(records), pageNum, total: res.total, loading: false }); wx.stopPullDownRefresh(); };触底加载用onReachBottom生命周期监听前提是当前页面是滚动容器而不是某个内层 scroll-view。判断是否还有更多数据用this.data.orders.length this.data.total。这是最土但最稳的写法不要靠hasMore这个布尔值它容易在一次请求失败后失去和真实数据的同步。列表项用微信官方wx:for渲染即可wx:key用 orderId 而不是默认的 index否则列表项的重排序会导致整个列表重新渲染性能断崖。view classorder-card wx:for{{orders}} wx:keyid view classorder-title{{item.title}}/view view classorder-status{{item.statusText}}/view view classorder-time{{item.createTime}}/view /viewstatusText是后端返回的展示字段前端不要自己根据数字去映射文案。原因是后端如果改了状态枚举前端不更新就全部错乱这又是一个前后端契约问题。后端在拼 VO 的时候顺手把statusText填好前端只负责显示责任边界清晰。4. 本地联调与部署把 MySQL、SpringBoot 和小程序三端跑通4.1 本地开发环境三个必须提前配好的关键参数别急着写代码先把环境准备扎实。MySQL 安装后第一件事是改 root 密码和确认端口常见坑是安装时用的 3306 端口被占用或者密码带特殊字符导致配置文件里解析失败。我一般会在application.yml里把数据库密码单独放到环境变量引用避免把密码写死在提交到 Git 的配置里。spring: datasource: url: jdbc:mysql://localhost:3306/dorm_repair?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai username: root password: ${DB_PASSWORD:root} driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 30MB mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl server: port: 8080DSN 里的serverTimezoneAsia/Shanghai必须加否则 MySQL 8.0 默认返回的时间和你本地差 8 小时实现里排查半天发现是时区问题这就是典型黑匣子。map-underscore-to-camel-case让数据库的create_time自动映射到 Java 的createTime省掉写一堆 resultMap。max-file-size限制单图 10MB小程序端反正压缩过足够了。log-impl在开发期打开 SQL 日志上线前关掉这个调试的时候太管用了。启动后端前先确认 MySQL 里能成功执行那份脚本然后直接 IDEA 里运行主类看到 “Started Application” 字样说明后端就绪。如果端口被占用用netstat -ano | findstr :8080查占用进程不要改个端口假装没事——小程序端的 BASE_URL 就错了。4.2 小程序开发者工具四个配置项勾对了直接省两小时小程序开发者工具导入工程后先做四件事第一AppID 选择“测试号”或你自己的小程序 AppID第二在“详情-本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”第三检查BASE_URL是http://127.0.0.1:8080而不是localhost:8080——某些机器上localhost解析成::1走 IPv6 回环后端只听 IPv4请求就失败了第四确认调试基础库版本不要选 too low建议 2.30.0 附近太老的基础库连wx.chooseMedia都不支持。手机号相关功能button open-typegetPhoneNumber在开发者工具里能拿到模拟数据但真机预览会失败。如果你的选题需要“微信小程序登录获取手机号”这个功能必须提前知道个人主体小程序无权调用该接口需要企业或个体工商户认证的小程序还需要提前在后台申请开通手机号快速验证组件。毕业设计演示时容易被这一环卡住演示流程常见做法是做一个“手动输入手机号”的兜底入口或者后端留一个 mock 开关演示阶段直接跳过微信授权。这不算偷懒是保证现场演示稳定性的务实选择。4.3 后端打包与部署从 IDEA 到服务器的一次成型后端部署要用打包命令而非 IDEA 里直接运行否则 nobody 知道哪个进程占着端口。在项目根目录执行mvn clean package -DskipTests然后java -jar target/dorm-repair-0.0.1.jar。放到服务器上跑要用 nohupmvn clean package -DskipTests nohup java -jar target/dorm-repair-0.0.1.jar \ --spring.datasource.password你的密码 \ --server.port8080 \ app.log 21 # 确认进程存活 tail -f app.log命令里的 app.log 21 把标准输出和错误输出都引入日志文件后台运行。每次发新版本的循环是打包、备份旧 jar、停旧进程kill 进程号、启动新 jar、看启动日志。很多新手漏了看日志部署上去没反应就去问“为什么不行”其实日志里已经打出了数据库连不上或端口占用。小程序端上线前的准备清单包括后端域名必须是 HTTPS 且有 ICP 备案在小程序公众平台后台配置 request 合法域名把BASE_URL从 localhost 改成正式域名。用 Nginx 做反向代理是最常见的方案前端请求/api开头的路径全部转发到后端 8080 端口即可。4.4 用 Charles 抓包定位接口慢和参数错位问题接口问题排查先看请求到底发出去没有、后端到底返回了什么。微信开发者工具的 Network 面板已经能看大部分情况但有些问题必须在手机上抓包。Windows 上常见抓包工具装好后手机设代理、信任 CA 证书请求就能明文可见。抓包主要确认三点请求头里的 Content-Type 是不是application/jsonPOST body 的字段名和后端 DTO 的字段名是否完全一致返回体里的 JSON 是否能被前端正确解析。字段名拼错是最容易翻车的地方例如appointmentTime前端写成了appointment_time后端 DTO 就收不到值报修单创建成功但期望时间永远为空。这类问题抓包一眼就能定位比反复猜强太多。5. 常见问题与避坑四个新手翻车点每条都是血泪经验5.1 微信登录成功但用户表没有数据openid 为空现象前端wx.login返回了 code后端也打印出了请求微信接口的响应但用户表里没插入新记录。原因errmsg显示invalid appid或appid and secret not match多为小程序的 AppID 和密钥不一致或用了别人的 secret。解决去微信公众平台确认 AppID 与 AppSecret 是一对且后端配置里的两处上下文不能串。如果用的是测试号Secret 在测试号管理页生成有效期需要关注。再次强调openid 是后端换来的前端传过来的任何标识都不能作为用户身份唯一依据。5.2 报修单状态随便一拖就到已完成状态机校验缺失现象前端直连后端接口把任意工单的状态改为 3接口返回成功。原因后端只写了updateById没有在 Service 层校验状态流转的合法性。解决所有状态变更必须走 Service 的专用方法方法内部校验当前状态 目标状态的组合是否在白名单里。这条不用代码块直接记住结论状态机是毕业设计答辩中体现工程意识的最重要设计没有之一。扩展思路还可以引入流程模板让每种角色只能执行其角色允许的若干操作。5.3 图片上传后无法预览临时路径存进了数据库现象小程序提交报修后图片区域显示空白或裂开但 Network 里看请求返回的 URL 明明是地址。原因前端没有先wx.uploadFile换取永久 URL而是直接把wx.chooseMedia返回的tempFilePath提交给了后端或者后端原样存了下来而临时文件在小程序端启动时已经被清理。解决统一走“先上传、后建单”的顺序上传接口返回的 URL 是以http://开头的完整地址入库前检查前缀。SQL 文件或后端 Service 里加一个正则校验^https?:\/\/.不符合就拒绝入库并返回错误。这个校验能挡住一大半脏数据。5.4 本地图片访问 404静态资源映射没有配置现象后台上传接口返回成功但浏览器直接打开返回的 URL 得到 404。原因SpringBoot 默认只映射/static/**、/public/**等目录到 classpath你上传到服务器的/data/images/不在默认映射里。解决加一个 WebMvc 配置类做资源映射。配置如下Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 将上传目录下的文件映射到 /images/** 访问路径 registry.addResourceHandler(/images/**) .addResourceLocations(file: uploadDir /); } }这里的uploadDir建议放在配置文件中比如file.upload-dir/data/images部署到 Linux 上时用绝对路径开发时用相对路径。注意 addResourceLocations 的写法必须file:开头后面以/结尾少了任何一部分都会导致映射失败。这个坑非常隐蔽因为写错不会报编译错误只有运行时 404。5.5 跨域问题后端要配置允许跨域并且别忽略预检现象前端请求后端接口控制台报Access-Control-Allow-Origin相关错误。原因小程序请求后端不是浏览器同源策略的范畴跨域问题在真机上其实不存在。如果你在浏览器里调试管理后台时遇到这类报错才需要后端加跨域配置。解决如果确实是浏览器调试在 SpringBoot 里加一个 CORS 配置Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedHeaders(*) .allowedMethods(*) .allowCredentials(true) .maxAge(3600); }注意allowCredentials(true)和allowedOrigins(*)在 SpringBoot 高版本中不能同时使用用allowedOriginPatterns(*)替代allowedOrigins(*)即可。这不是玄学是 Cookie 安全校验的规则。6. 进阶方向把毕业设计从“能用”做到“出彩”的三个能力点如果说前面这些内容让你的系统“能跑通”那接下来这几个方向能让你在答辩时多讲三分钟而且都是基于现有代码能稳扎稳打扩展的消息订阅、数据统计和权限控制。宿舍报修系统做完基础功能后能力上限就看这三个点。6.1 消息订阅从“学生反复刷新”到“服务通知主动触达”小程序有个订阅消息能力用户主动订阅后后端能给他发一条微信服务通知。放在报修场景里就是学生提交报修单后弹一次订阅授权维修工接单时后端给他推一条“你有一单新的维修任务”状态完成时再给学生推“你的报修已完成”。这个功能是基于现有代码能稳扎稳打扩展的不需要引入任何新组件只需在小程序端wx.requestSubscribeMessage调起授权再把模板 ID 和表单数据传给后端。wx.requestSubscribeMessage({ tmplIds: [模板ID], success(res) { if (res.errMsg requestSubscribeMessage:ok) { // 用户点了允许后端就能在下单成功后发送订阅通知 } } });实现时要注意“一次性订阅”的特性用户每点一次允许后端只能用一次推送第二次推送前必须让用户再次点击授权。这是官方限制替代方案是引导用户点击“长期订阅”开关但这需要特定类目资质。毕业设计无需过度纠结能在答辩时展示“推送成功回调事件”就够了。6.2 数据统计用一张报表看透整个宿舍的报修热区第二个加分项是数据统计。宿舍报修系统的数据价值在于哪栋楼的报修最多、哪类故障占比最高、平均处理时长是多少。用 SQL 的GROUP BY就能做出直观统计不需要大数据组件。SELECT building, COUNT(*) AS total FROM repair_order o JOIN user u ON o.user_id u.id WHERE o.status IN (2, 3) GROUP BY building ORDER BY total DESC LIMIT 10;把这个查询包成一个/api/statistic/building-ranking接口前端用ec-canvas渲染柱状图在“我的”页面里加一个入口教师看演示时会有画面感。这里务必注意 SQL 里status IN (2, 3)是已完成和维修中别把已关闭的计入维修量否则数据口径会被问倒。6.3 给后来者的三个建议最后以我的经验给你三条直接可用的建议。第一拿到源码别急着跑先用 Navicat 执行数据库脚本再启动后端最后开小程序顺序不能乱顺序乱了任何一个环节报错都会让你误判是源码有问题。第二不要一上来就改业务先跑通一条完整链路登录 → 提交报修 → 宿管列表 → 派单 → 维修工接单 → 完成。这条链路通了你才真正理解了三张表之间是怎么协作的论文里的流程图也画得出来。第三如果时间允许至少自己写一遍“提交报修”的页面和后端接口这是整个系统里最核心、也最能展现工作量的一段。配对的视频教程我建议按“环境搭建 10 分钟 → 数据库导入 5 分钟 → 后端启动 5 分钟 → 小程序跑通 5 分钟”的顺序来看先有全局地图再动细节不要一上来就盯着某一集死磕。这套项目做到这里你已经拥有的不仅是一个能答辩的系统而是一套“前端交互 后端服务 数据库建模”的组合拳。我当年做完类似项目后最大的感受是毕业设计不是要做出多惊艳的产品而是要证明你具备从头到尾把一个需求落地的能力。哪怕只是在这个骨架上改一两个模块也能学到比照抄多得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表