发布时间:2026/9/8 7:52:27
微信小程序与Spring Boot构建教学设备报修系统全攻略 设备坏了找不到人修、报修流程靠口头传达、维修进度无法跟踪这类问题在教学楼和实验室里其实非常常见。本文基于微信小程序 Spring Boot 技术栈完整实现一个教学设备报修系统覆盖需求分析、表结构设计、后端接口开发、小程序端页面搭建、消息通知以及上线排错全流程。系统采用原生微信小程序语法开发后端使用 Spring Boot 2.x 与 MyBatis Plus整体结构清晰、代码可直接复用既能作为毕业设计参考也能作为实际项目的初始版本。1. 教学设备报修系统的需求与整体设计在开始写代码之前先弄清楚系统到底要解决什么问题。只有把业务流程理清楚后端的表结构和接口设计才会有依据。1.1 传统设备报修方式的问题在很多学校场景中设备报修仍然依赖以下几种方式电话联系管理员管理员手工登记信息。填写纸质报修单流转效率低。通过微信群消息报修消息容易被刷掉维修结果无从追踪。这些方式的共同问题是报修信息不完整、维修人员响应慢、维修进度不透明、设备历史记录无归档。设备状态和使用寿命无法形成数据资产后续做设备维保统计分析时也没有数据支撑。1.2 系统需要覆盖的角色一个完整的教学设备报修系统至少要包含三类角色角色核心诉求主要操作学生/教师快速发起报修、查看处理进度选择设备、填写故障描述、提交报修单、查看本人记录维修人员及时接收任务、更新处理结果查看待处理工单、接单、填写处理意见、标记完成系统管理员掌握全局情况、分配任务、统计分析管理设备、管理用户、分配维修人员、查看统计报表1.3 系统核心流程整个报修流程可以用一组状态来描述待处理(0) - 处理中(1) - 已完成(2) | | | -- 已驳回(3) - 已驳回(3)用户进入小程序通过微信授权登录。用户选择故障设备填写故障描述和照片提交报修单。管理员或维修人员看到待处理的报修单更新状态为“处理中”。维修完成后更新状态为“已完成”并填写处理说明。用户随时在报修记录列表查看最新状态。1.4 技术选型说明这里采用的技术方案在校园类项目和毕设项目中非常常见技术成熟、参考资料多、开发效率高小程序端原生微信小程序使用 WXML、WXSS、JavaScript。后端Spring Boot简化配置内置 Tomcat方便快速部署。ORMMyBatis Plus代码量少分页和条件构造器都很方便。数据库MySQL存储用户、设备、报修单、处理记录等数据。工具微信开发者工具、IDEA、Navicat、Postman。2. 环境准备与项目结构2.1 环境版本版本需要根据你的实际环境调整下面给出的是常见组合JDK 1.8 或 11Spring Boot 2.3.x 或 2.7.xMySQL 5.7 或 8.0MyBatis Plus 3.4.xMaven 3.6微信开发者工具稳定版即可一个已注册的小程序 AppID测试阶段可用测试号2.2 后端项目目录结构建议按下面的包结构组织代码避免所有类堆在一个包下面src/main/java/com/example/repair/ ├── RepairApplication.java ├── common/ │ ├── Result.java │ ├── PageResult.java │ └── GlobalExceptionHandler.java ├── config/ │ └── MybatisPlusConfig.java ├── controller/ │ ├── UserController.java │ ├── DeviceController.java │ ├── RepairOrderController.java │ ├── WechatController.java │ └── UploadController.java ├── entity/ │ ├── SysUser.java │ ├── Device.java │ ├── RepairOrder.java │ └── RepairLog.java ├── mapper/ │ ├── SysUserMapper.java │ ├── DeviceMapper.java │ ├── RepairOrderMapper.java │ └── RepairLogMapper.java ├── service/ │ ├── UserService.java │ ├── DeviceService.java │ ├── RepairOrderService.java │ └── WechatService.java └── service/impl/ └── RepairOrderServiceImpl.java2.3 小程序项目目录结构miniprogram/ ├── app.js ├── app.json ├── app.wxss ├── utils/ │ ├── request.js │ └── auth.js ├── pages/ │ ├── index/ // 首页展示设备分类和快捷入口 │ ├── report/ // 发起报修页 │ ├── records/ // 报修记录列表 │ ├── detail/ // 报修详情页 │ └── mine/ // 个人中心3. 数据库表结构设计数据库设计是整个系统的地基。字段和索引设计不合理后面写接口时就会不断返工。3.1 用户表 sys_user用户表主要保存微信用户的 openid、昵称、头像以及角色信息。注意 openid 是微信体系下用户的唯一标识必须加唯一索引。CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, openid VARCHAR(64) DEFAULT NULL COMMENT 微信openid, nickname VARCHAR(64) DEFAULT NULL COMMENT 昵称, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像地址, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, role TINYINT NOT NULL DEFAULT 1 COMMENT 角色1-普通用户2-维修人员3-管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;3.2 设备表 device设备表记录资产位置、名称、分类和当前状态。设备状态和设备报修状态是两个不同概念需要区分开。CREATE TABLE device ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, device_code VARCHAR(64) DEFAULT NULL COMMENT 设备编号, device_name VARCHAR(128) NOT NULL COMMENT 设备名称, category VARCHAR(64) DEFAULT NULL COMMENT 设备分类如投影仪、电脑、空调, location VARCHAR(128) DEFAULT NULL COMMENT 存放位置如A栋201, status TINYINT NOT NULL DEFAULT 0 COMMENT 设备状态0-正常1-维修中2-报废, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_category (category), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT设备表;3.3 报修单表 repair_order报修单是系统的核心业务表。为了查询方便这里冗余了设备名称、报修人等信息避免每次关联多张表。CREATE TABLE repair_order ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, repair_no VARCHAR(32) NOT NULL COMMENT 报修单号, device_id BIGINT NOT NULL COMMENT 设备ID, device_name VARCHAR(128) DEFAULT NULL COMMENT 设备名称冗余, location VARCHAR(128) DEFAULT NULL COMMENT 设备位置冗余, reporter_id BIGINT NOT NULL COMMENT 报修人ID, reporter_name VARCHAR(64) DEFAULT NULL COMMENT 报修人姓名冗余, reporter_phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, description TEXT COMMENT 故障描述, images VARCHAR(1000) DEFAULT NULL COMMENT 图片地址多个用逗号分隔, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0-待处理1-处理中2-已完成3-已驳回, priority TINYINT NOT NULL DEFAULT 2 COMMENT 优先级1-紧急2-普通, assignee_id BIGINT DEFAULT NULL COMMENT 维修人员ID, assignee_name VARCHAR(64) DEFAULT NULL COMMENT 维修人员姓名, handle_remark VARCHAR(500) DEFAULT NULL COMMENT 处理说明, handle_time DATETIME 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 uk_repair_no (repair_no), KEY idx_reporter (reporter_id), KEY idx_status (status), KEY idx_assignee (assignee_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修单表;3.4 处理记录表 repair_log每一次状态变更都写入日志方便后续追溯“谁在什么时间做了什么操作”。CREATE TABLE repair_log ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, order_id BIGINT NOT NULL COMMENT 报修单ID, operator_id BIGINT DEFAULT NULL COMMENT 操作人ID, operator_name VARCHAR(64) DEFAULT NULL COMMENT 操作人姓名, action VARCHAR(32) DEFAULT NULL COMMENT 动作CREATE/ASSIGN/PROCESS/FINISH/REJECT, remark VARCHAR(500) DEFAULT NULL COMMENT 备注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修处理日志表;4. Spring Boot 后端核心接口实现后端的职责是提供小程序端需要的全部接口。下面重点讲解登录、报修单创建、状态流转、列表分页和统计接口。4.1 微信登录接口获取 openid小程序端通过wx.login获取临时 code后端拿 code 向微信接口换取 openid。真实的 appid 和 secret 要从微信公众平台获取生产环境不要硬编码在代码里建议放到配置文件或环境变量中。// 文件路径src/main/java/com/example/repair/controller/WechatController.java RestController RequestMapping(/api/wechat) public class WechatController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginRequest request) { // 1. 使用 code 调用微信接口获取 openid String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code request.getCode() grant_typeauthorization_code; RestTemplate restTemplate new RestTemplate(); String response restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(response); String openid json.getString(openid); // 2. 根据 openid 查找用户不存在则自动注册 SysUser user userService.findOrCreateUser(openid); return Result.success(user); } }这里有几个注意点code 是一次性的五分钟内有效只能使用一次。换取的 session_key 在小程序端有时需要用来解密手机号等敏感信息。后端返回给前端的不建议直接传 openid更推荐的做法是返回你自己生成的 token后续请求通过 token 识别用户。4.2 创建报修单接口创建报修单是整个系统中最核心的接口。需要注意的是设备状态要同步更新为“维修中”同时要写入一条日志记录方便追踪整个流程。// 文件路径src/main/java/com/example/repair/service/impl/RepairOrderServiceImpl.java Override Transactional(rollbackFor Exception.class) public RepairOrder createOrder(RepairOrderCreateRequest request, Long reporterId) { // 1. 校验设备是否存在 Device device deviceMapper.selectById(request.getDeviceId()); if (device null) { throw new BusinessException(设备不存在); } // 2. 生成报修单号规则日期 随机数 String repairNo BX DateUtil.format(new Date(), yyyyMMddHHmmss) RandomUtil.randomNumbers(4); // 3. 组装报修单 RepairOrder order new RepairOrder(); order.setRepairNo(repairNo); order.setDeviceId(device.getId()); order.setDeviceName(device.getDeviceName()); order.setLocation(device.getLocation()); order.setReporterId(reporterId); order.setDescription(request.getDescription()); order.setImages(request.getImages()); order.setPriority(request.getPriority()); order.setStatus(0); // 待处理 repairOrderMapper.insert(order); // 4. 同步更新设备状态为维修中 device.setStatus(1); deviceMapper.updateById(device); // 5. 写入操作日志 RepairLog log new RepairLog(); log.setOrderId(order.getId()); log.setAction(CREATE); log.setRemark(用户提交报修单); repairLogMapper.insert(log); return order; }这段代码使用了Transactional事务注解保证报修单、设备状态、日志三步操作要么全部成功要么全部回滚避免出现数据不一致。4.3 报修单状态流转接口状态流转接口是系统权限控制的关键点。普通用户只能提交报修不能随意修改状态维修人员可以将待处理改成处理中或已完成管理员可以分配维修人员也可以驳回报修单。// 文件路径src/main/java/com/example/repair/service/impl/RepairOrderServiceImpl.java Override Transactional(rollbackFor Exception.class) public void updateStatus(RepairOrderStatusRequest request, Long operatorId, Integer operatorRole) { RepairOrder order repairOrderMapper.selectById(request.getOrderId()); if (order null) { throw new BusinessException(报修单不存在); } // 状态机校验只允许合法的状态流转 int currentStatus order.getStatus(); int targetStatus request.getTargetStatus(); if (!canTransit(currentStatus, targetStatus, operatorRole)) { throw new BusinessException(当前状态不允许该操作); } if (FINISH.equals(request.getAction())) { order.setStatus(2); order.setHandleRemark(request.getHandleRemark()); order.setHandleTime(new Date()); // 同步恢复设备正常状态 Device device deviceMapper.selectById(order.getDeviceId()); if (device ! null) { device.setStatus(0); deviceMapper.updateById(device); } } else if (PROCESS.equals(request.getAction())) { order.setStatus(1); order.setAssigneeId(operatorId); order.setAssigneeName(operatorName); } else if (REJECT.equals(request.getAction())) { order.setStatus(3); order.setHandleRemark(request.getHandleRemark()); } repairOrderMapper.updateById(order); // 写入日志 RepairLog log new RepairLog(); log.setOrderId(order.getId()); log.setOperatorId(operatorId); log.setAction(request.getAction()); log.setRemark(request.getHandleRemark()); repairLogMapper.insert(log); }状态机校验可以抽成一个独立方法避免散落在业务代码中。下面是一个简单的状态流转表当前状态允许操作目标状态允许角色待处理(0)PROCESS处理中(1)维修人员、管理员待处理(0)REJECT已驳回(3)管理员处理中(1)FINISH已完成(2)维修人员、管理员4.4 报修单分页列表接口列表接口需要支持按用户查询、按状态筛选和分页。MyBatis Plus 提供的分页插件非常方便。// 文件路径src/main/java/com/example/repair/controller/RepairOrderController.java GetMapping(/list) public Result list( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer status, RequestParam(required false) Long reporterId, RequestParam(required false) Long assigneeId) { PageRepairOrder pageParam new Page(page, size); LambdaQueryWrapperRepairOrder wrapper new LambdaQueryWrapper(); wrapper.eq(status ! null, RepairOrder::getStatus, status); wrapper.eq(reporterId ! null, RepairOrder::getReporterId, reporterId); wrapper.eq(assigneeId ! null, RepairOrder::getAssigneeId, assigneeId); wrapper.orderByDesc(RepairOrder::getCreateTime); PageRepairOrder result repairOrderMapper.selectPage(pageParam, wrapper); return Result.success(result); }分页接口在设计时要把查询条件作为可选参数而不是每个条件都写一个接口。这样可以减少接口数量也方便前端组合筛选。4.5 报修统计接口统计接口服务于管理员首页展示待处理数量、处理中数量、已完成数量和总报修数量帮助管理层快速掌握当前设备维修态势。GetMapping(/statistics) public Result statistics() { MapString, Object result new HashMap(); result.put(pending, repairOrderMapper.selectCount( new LambdaQueryWrapperRepairOrder().eq(RepairOrder::getStatus, 0))); result.put(processing, repairOrderMapper.selectCount( new LambdaQueryWrapperRepairOrder().eq(RepairOrder::getStatus, 1))); result.put(finished, repairOrderMapper.selectCount( new LambdaQueryWrapperRepairOrder().eq(RepairOrder::getStatus, 2))); result.put(total, repairOrderMapper.selectCount(null)); return Result.success(result); }5. 微信小程序端完整实现后端接口就绪后开始编写小程序端页面。下面会给出关键页面的核心代码完整项目可以在此基础上继续扩展。5.1 全局配置 app.json小程序的全局配置里最核心的是页面路径、窗口样式和请求域名设置。开发阶段可以在开发者工具中勾选“不校验合法域名”但真机预览和发布时必须配置合法域名。{ pages: [ pages/index/index, pages/report/report, pages/records/records, pages/detail/detail, pages/mine/mine ], window: { backgroundTextStyle: light, navigationBarBackgroundColor: #4A90D9, navigationBarTitleText: 教学设备报修, navigationBarTextStyle: white }, tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/records/records, text: 报修记录 }, { pagePath: pages/mine/mine, text: 我的 } ] }, style: v2, sitemapLocation: sitemap.json }5.2 封装请求工具 request.js所有请求都应该走统一的 request 封装统一处理 baseURL、token、错误提示和加载状态。// 文件路径utils/request.js const BASE_URL https://your-server-domain.com/api function request(url, method GET, data {}) { return new Promise((resolve, reject) { const token wx.getStorageSync(token) wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: token ? Bearer ${token} : }, success: (res) { if (res.statusCode 200) { resolve(res.data) } else if (res.statusCode 401) { wx.showToast({ title: 登录已过期请重新登录, icon: none }) // 跳转登录逻辑 } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }) reject(res) } }, fail: (err) { wx.showToast({ title: 网络异常请稍后重试, icon: none }) reject(err) } }) }) } module.exports { request, BASE_URL }5.3 登录逻辑实现微信官方已调整了用户头像昵称的获取规则目前更推荐使用“头像昵称填写能力”由用户主动点击填写。下面代码演示了登录流程的核心逻辑。// 文件路径utils/auth.js function login() { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { try { const response await request(/wechat/login, POST, { code: res.code }) if (response.code 200) { const user response.data wx.setStorageSync(userInfo, user) resolve(user) } else { reject(new Error(登录失败)) } } catch (e) { reject(e) } } else { reject(new Error(wx.login 获取 code 失败)) } } }) }) } module.exports { login }5.4 首页与设备分类展示首页主要承担三个功能展示设备分类、展示待处理统计、提供快捷报修入口。这里以设备分类展示为例通过wx.cloud或后端接口获取分类数据后渲染到页面。!-- 文件路径pages/index/index.wxml -- view classindex-container view classstat-card view classstat-item wx:for{{statistics}} wx:keyname text classstat-num{{item.value}}/text text classstat-label{{item.name}}/text /view /view view classcategory-section view classsection-title设备分类/view view classcategory-grid view classcategory-item wx:for{{categories}} wx:keyid bindtapselectCategory >// 文件路径pages/index/index.js const { request } require(../../utils/request) const { login } require(../../utils/auth) Page({ data: { categories: [], statistics: [] }, async onShow() { const userInfo wx.getStorageSync(userInfo) if (!userInfo) { await login() } this.loadCategories() this.loadStatistics() }, async loadCategories() { const res await request(/device/categories, GET) if (res.code 200) { this.setData({ categories: res.data }) } }, async loadStatistics() { const res await request(/repair/statistics, GET) if (res.code 200) { const data res.data this.setData({ statistics: [ { name: 待处理, value: data.pending }, { name: 处理中, value: data.processing }, { name: 已完成, value: data.finished }, { name: 总数, value: data.total } ] }) } }, goReport() { wx.switchTab({ url: /pages/report/report }) } })5.5 报修表单页面报修表单是整个系统交互最复杂的页面包括设备选择、故障描述、图片上传、联系方式填写。图片上传使用wx.chooseMedia需要注意临时文件路径与正式上传路径的区别。!-- 文件路径pages/report/report.wxml -- view classform-container view classform-item text classlabel设备名称/text picker modeselector range{{deviceNames}} bindchangeonDeviceChange view classpicker-value{{selectedDeviceName || 请选择设备}}/view /picker /view view classform-item text classlabel故障描述/text textarea placeholder请详细描述设备故障现象 bindinputonDescriptionInput / /view view classform-item text classlabel故障图片/text view classupload-area view classupload-item wx:for{{images}} wx:key*this image src{{item}} modeaspectFill bindtappreviewImage>// 文件路径pages/report/report.js const { request, BASE_URL } require(../../utils/request) Page({ data: { deviceList: [], deviceNames: [], selectedDeviceId: null, selectedDeviceName: , description: , phone: , images: [] }, async onLoad() { const res await request(/device/list, GET) const deviceList res.data this.setData({ deviceList, deviceNames: deviceList.map(item item.deviceName) }) }, chooseImage() { const remain 3 - this.data.images.length wx.chooseMedia({ count: remain, mediaType: [image], sourceType: [album, camera], success: (res) { const tempFiles res.tempFiles.map(item item.tempFilePath) this.setData({ images: this.data.images.concat(tempFiles) }) } }) }, async submitOrder() { const { selectedDeviceId, description, phone, images } this.data if (!selectedDeviceId) { wx.showToast({ title: 请选择设备, icon: none }) return } if (!description.trim()) { wx.showToast({ title: 请填写故障描述, icon: none }) return } wx.showLoading({ title: 提交中... }) // 1. 先上传图片得到可访问的 URL const uploadTasks images.map((filePath) this.uploadImage(filePath)) const imageUrls await Promise.all(uploadTasks) // 2. 提交报修单 const res await request(/repair/create, POST, { deviceId: selectedDeviceId, description: description, phone: phone, images: imageUrls.join(,) }) wx.hideLoading() if (res.code 200) { wx.showToast({ title: 提交成功, icon: success }) setTimeout(() { wx.switchTab({ url: /pages/records/records }) }, 1500) } }, uploadImage(filePath) { return new Promise((resolve, reject) { wx.uploadFile({ url: BASE_URL /upload, filePath: filePath, name: file, success: (res) { const data JSON.parse(res.data) if (data.code 200) { resolve(data.data.url) } else { reject(new Error(上传失败)) } }, fail: reject }) }) } })5.6 报修记录列表与会话消息报修记录页面需要支持按状态切换查看使用wx:for渲染列表数据下拉刷新重新加载最新状态。// 文件路径pages/records/records.js const { request } require(../../utils/request) Page({ data: { activeTab: 0, tabs: [全部, 待处理, 处理中, 已完成, 已驳回], statusMap: [null, 0, 1, 2, 3], list: [], page: 1, size: 10, loading: false, hasMore: true }, onShow() { this.refreshList() }, onPullDownRefresh() { this.refreshList() }, onReachBottom() { if (this.data.hasMore) { this.loadList() } }, async refreshList() { this.setData({ page: 1, list: [], hasMore: true }) await this.loadList() wx.stopPullDownRefresh() }, async loadList() { if (this.data.loading || !this.data.hasMore) return this.setData({ loading: true }) const status this.data.statusMap[this.data.activeTab] const res await request(/repair/list, GET, { page: this.data.page, size: this.data.size, status: status }) const newList this.data.page 1 ? res.data.records : this.data.list.concat(res.data.records) this.setData({ list: newList, page: this.data.page 1, hasMore: newList.length res.data.total, loading: false }) }, switchTab(e) { const index e.currentTarget.dataset.index this.setData({ activeTab: index }) this.refreshList() } })5.7 订阅消息通知关键步骤维修进度提醒可以通过微信订阅消息实现整体流程如下在微信公众平台申请“报修进度通知”模板拿到模板 ID。小程序端在用户提交报修单成功后调用wx.requestSubscribeMessage请求用户授权订阅。后端在状态流转时调用订阅消息发送接口通知用户。小程序端订阅授权示例wx.requestSubscribeMessage({ tmplIds: [替换成你的模板ID], success(res) { // res[替换成你的模板ID] accept 表示用户同意 console.log(订阅结果, res) }, fail(err) { console.log(订阅失败, err) } })后端发送订阅消息时需要先获取 access_token再调用subscribeMessage.send接口。注意用户同意一次订阅后端只能发送一条消息无法反复通知所以订阅消息授权必须放在关键动作之后确保尽量触发一次有效发送。6. 运行验证与联调排错系统开发完成后需要在小程序开发者工具中完成全流程联调。按照下面的验证清单逐项检查可以快速定位问题。6.1 功能验证清单验证项操作步骤预期结果登录打开小程序自动触发 wx.login后端返回用户信息本地存储成功设备列表首页进入报修页能正常加载设备分类和设备列表发起报修选择设备、填写描述、上传图片、点击提交页面提示提交成功记录出现在“报修记录”列表状态流转管理员/维修人员操作报修单状态用户列表状态同步更新设备状态恢复权限控制使用普通用户账号操作驳回或分配接口提示无权限消息通知提交报修并订阅维修完成后收到服务通知6.2 常见问题表格问题现象常见原因解决思路wx.login 获取 code 失败小程序没有正确配置 AppID检查 AppID 是否填写测试号也可以使用请求接口报 401未携带 token 或 token 过期检查请求封装中的 header统一处理 401 跳转登录真机预览请求失败未配置合法域名或服务器未启用 HTTPS后端配置 HTTPS 证书小程序后台添加 request 合法域名报修单提交后设备状态未更新事务没有生效或 updateById 使用错误检查 Service 方法是否添加 Transactional检查设备 ID 是否正确上传图片失败上传接口路径错误或服务器目录无写权限检查 uploadFile 的 url确认服务器上传目录存在且有写权限订阅消息发送失败模板 ID 错误或用户未授权核对模板 ID检查 access_token 是否过期确认用户是否已取消订阅模拟器中正常但真机异常开发者工具开启了“不校验合法域名”真机没有在小程序管理后台配置合法域名官方要求必须 HTTPS6.3 后端排查建议后端联调时遇到问题先看日志再看数据库最后看接口文档。推荐在 application.yml 中开启 SQL 日志便于定位 SQL 执行问题。logging: level: com.example.repair.mapper: debug开启 SQL 日志后控制台会打印 MyBatis 执行的每条 SQL 语句能帮助快速定位查询条件写错、字段映射失败等问题。7. 工程落地中的最佳实践与扩展建议实际项目中如果只是把功能跑通离“可以上线”的标准还有距离。下面几个方向的工程化实践非常关键。7.1 状态机与业务流程固化报修单的状态变更如果不做校验很容易出现“已完成又变回待处理”之类的非法操作。建议在 Service 层建立状态流转表每次变更都校验操作角色和目标状态。可以考虑将状态机抽成独立的枚举类后续项目复用性更好。// 文件路径src/main/java/com/example/repair/common/OrderStatus.java public enum OrderStatus { PENDING(0, 待处理), PROCESSING(1, 处理中), FINISHED(2, 已完成), REJECTED(3, 已驳回); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } }状态机能够防止业务逻辑在迭代过程中被破坏越是多人协作的项目越需要把状态定义收敛到一处。7.2 接口安全与权限校验目前示例代码中只做了简单的逻辑判断。生产环境中必须有完整权限体系推荐使用 Spring Boot 拦截器或 Spring Security 做接口级权限控制。报修单操作权限用户只能操作自己的报修单。管理员权限设备管理、用户管理、状态驳回等接口只对管理员开放。登录态校验所有/api/**接口都校验 token 有效性。7.3 日志与监控报修系统虽然业务简单但涉及线下维修周期性长日志尤其重要。建议记录用户登录日志openid、登录时间、IP。操作日志谁在什么时间对哪张报修单做了什么操作。异常日志接口异常时记录完整堆栈和请求参数。7.4 数据库索引与性能优化当报修单数量增长到十万级以上时一定要关注索引设计。当前核心查询基本都围绕 reporter_id、status、assignee_id 三个字段过滤这三个字段已经在建表 SQL 中加入了索引。如果有按时间范围查询的需求建议增加(status, create_time)联合索引。7.5 后续可扩展功能消息通知优化除了订阅消息外可以增加短信或邮件通知。设备二维码为每一台设备生成独立二维码用户扫码直接进入报修页并自动携带设备 ID。备件库存管理维修人员领用备件时自动扣减库存。数据看板在管理端增加维修耗时、设备故障率、分类维修次数等图表统计。维修评价用户确认完成后可以对维修服务进行评价。7.6 上线发布注意事项小程序发布前必须配置合法域名且在“小程序后台 → 开发 → 开发设置 → 服务器域名”中添加 request 合法域名和 uploadFile 合法域名。版本发布要经过体验版测试使用不同型号手机进行真机验证重点检查图片上传和订阅消息。首次发布前可在开发者工具中开启“代码质量”扫描清理无用代码和未使用的资源文件。上线后注意观察服务器日志和数据库增长定期备份数据。教学设备报修系统的核心不只是“报修维修”两个动作而是一整套设备全生命周期管理能力。把报修单状态流转设计清楚把权限边界划分清楚把日志记录沉淀下来系统的价值会远远超过一个简单的表单提交工具。开发过程中如果遇到问题优先从前端请求参数、后端日志、数据库状态三个环节去排查大多数问题都能在这个链路里找到答案。

相关新闻

2026/9/8 7:52:27

OpenCode启动慢怎么办?多窗口复用与配置优化全攻略

你有没有遇到过这个场景:在终端里敲下opencode之后,光标停在那儿好几秒,界面才慢慢出来;开第二个窗口再来一遍,又是几秒;如果同时开着两三个项目,每个窗口都要等一轮,本来一句话就能…

2026/9/8 7:52:27

开源浏览器插件实现自媒体多平台分发:原理、价值与避坑指南

你写了一篇自己觉得还算满意的文章,配好图、选好封面、调好小标题,然后开始打开公众号后台、登录知乎、登录 CSDN、登录掘金、登录今日头条。接下来是每到一个平台就重复一遍:粘贴正文、上传封面、重新排版、设置标签、选择发布时间。运气好&…

2026/9/8 7:52:27

黑苹果安装工具链全解析:从EFI配置到驱动调试的完整指南

简介:面向想在非苹果硬件上运行 macOS 的黑苹果玩家和初次尝试者,这份工具包把安装过程中最常遇到的引导配置、驱动修补、分区读写、EFI 定制等问题集中到了一起,从制作安装介质到安装后驱动注入都有对应方案。包内共 20 个文件,压…

2026/9/8 9:07:39

储能设计核心认知:从母线拓扑到电芯参数的工程链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/8 9:07:39

汽车评论情感分析实战:数据集构建与模型训练全流程

简介:面向自然语言处理初学者与情感分析研究者的汽车评论情感分析数据集,聚焦消费者对汽车性能、外观、价格等方面的主观评价,提供评论文本与正面、负面、中性三类情感标签,可用于训练和评估文本情感分类模型,适用于文…

2026/9/8 9:07:39

AI舞蹈教练技术解析:从姿态估计到实时反馈的完整实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/8 9:07:39

从ARM MPU到龙芯Linux:驱动移植中的内存保护与DMA一致性实践

开头先说说背景。我们走马观碑组接到的任务是把一套原本跑在 ARM 平台上的驱动方案,原封不动地搬到龙芯 2K 系列平台上。老实讲,刚拿到任务清单时我心里是有预感的:这种跨架构移植,最怕的不是业务逻辑复杂,而是驱动里那…

2026/9/8 9:07:39

C#上位机与西门子PLC通信:S7.Net和Sharp7选型实战指南

简介:面向C#工业自动化开发人员及西门子PLC初学者,这是一份可直接运行的S7.Net与Sharp7连接PLC实例源码。资源实现C#与S7-1200 PLC通信,覆盖DB块数据读写,并补充了bool变量、string及Wstring类型读取,从基础连接到特定…

2026/9/8 9:02:38

PS5扩容实战:致态Ti600s 2TB加装与实测全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/8 0:01:49

踩多轮坑才跑通|OpenClaw 3.1.0 双平台本地 AI 自动化搭建实操实录

🔹 工具简述 OpenClaw 是一款备受开发者与办公人群青睐的开源本地智能工具,凭借离线本地运行、可视化图形面板、全流程自主任务处理三大核心特点,积累了众多忠实用户。与普通对话类 AI 产品不同,它能够直接调用电脑的软硬件操作权…

2026/9/8 0:01:50

拒绝复杂命令行,Hermes Agent 一键包快速解锁智能办公能力

🔍前言 不少想要体验 Hermes Agent 办公能力的使用者,往往会被复杂的环境配置拦住使用脚步。手动下载匹配依赖、反复调整系统目录、处理命令行持续报错、修复权限异常、补全丢失核心文件等一系列操作,对普通使用者而言门槛较高,很…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…