ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信小程序+SSM设备故障报修系统实战设计

2026/9/20 9:25:25 拓冰建站 浏览量
微信小程序+SSM设备故障报修系统实战设计 简介本资源是一套完整的微信小程序SSM后端的设备故障报修管理系统实战项目面向Java Web开发初学者、全栈学习者及高校课程设计/毕业设计学生解决企业或校园场景中传统报修流程低效、信息滞后、响应慢等痛点。压缩包共1288个文件含127个Java后端核心代码、181个JS与138个Vue前端逻辑、90个WXML与92个WXSS小程序视图文件、232张PNG界面截图及162个SVG图标资源另有数据库脚本db.sql、完整论文文档含系统架构、模块划分、测试方案及双bat一键部署脚本整体43.36MB。已有148人学习下载开发者可直接导入IDE运行快速掌握微信小程序与SSM整合开发全流程复用其多角色权限设计、报修工单流转逻辑、前后端联调规范及标准化目录结构具备良好二次开发与教学演示价值。1. 微信小程序 SSM 构建设备故障报修系统为什么不用 uni-app 或 Vue3而选这套「微信原生 Java 后端」组合产线设备突然停机维修工还在翻纸质登记表行政人员收到微信消息才去抄录故障信息再手动录入 OAIT 部门接到投诉说“小程序提交后没反应”查日志发现是前端传参字段名和 SSM 控制器接收对象不一致——这类场景在中小制造、物业、高校后勤部门真实高频发生。本项目标题里的「微信小程序项目基于微信的设备故障报修管理系统设计与实现ssm.zip」不是课程作业的空泛命名而是典型落地型技术选型前端用微信原生小程序非跨端框架后端用 Spring SpringMVC MyBatisSSM经典 Java 栈数据库用 MySQL部署在 Linux 服务器。它不追求炫技但要求「扫码即用、报修留痕、审批可溯、数据可导」四点全部闭环。适合已有 Java 开发能力、需快速对接企业微信或内部 OA 的团队也适合高校实训中强调前后端职责分离的教学场景。注意这里的小程序不依赖云开发所有业务逻辑、权限校验、文件上传均走自建 SSM 接口zip 包里含完整前后端代码结构但无加密或混淆可直接导入 IDEA 和微信开发者工具调试。2. 微信小程序端从 wx.login 到故障单提交的最小可行链路2.1 小程序登录态设计为什么不用 openId 直接当用户标识而要加一层 session_key 解密校验微信小程序登录不是简单调wx.login()拿 code 就完事。真实生产环境必须防止 code 被重放或伪造。标准流程是前端调wx.login()获取临时登录凭证 code → 通过wx.request()发送到 SSM 后端/api/login接口 → 后端用该 code appidappsecret向微信接口https://api.weixin.qq.com/sns/jscode2session换取openid、session_key和unionid若绑定公众号→关键一步后端生成一个 32 位随机字符串作为本次会话的token将openid和token存入 Redis过期时间 2 小时同时把session_key也存入 Redis 并关联该 token → 最后返回token给小程序端后续所有请求都在 header 中携带Authorization: Bearer token。// 小程序端 login.js wx.login({ success: res { wx.request({ url: https://your-domain.com/api/login, method: POST, data: { code: res.code }, success: resp { if (resp.data.code 200) { wx.setStorageSync(token, resp.data.data.token); // 后续请求统一加 header wx.request({ url: https://your-domain.com/api/fault/submit, header: { Authorization: Bearer resp.data.data.token }, data: { equipmentId: EQ-2024-001, description: 电机异响 } }); } } }); } });提示session_key是解密敏感数据如用户手机号的密钥绝不能返回给前端。SSM 后端需用它配合encryptedDataiv解密wx.getPhoneNumber()获取的真实号码这是微信合规要求。2.2 故障单提交接口SSM Controller 如何接收并校验小程序传参小程序提交故障单时常因字段缺失、类型错误、空字符串导致后端 500 或静默失败。SSM 层需做三层防护① SpringMVCRequestBody绑定自动校验② Service 层业务规则拦截③ MyBatis 插入前 SQL 级非空约束。以FaultReportDTO为例// FaultReportDTO.java public class FaultReportDTO { NotBlank(message 设备编号不能为空) private String equipmentId; NotBlank(message 故障描述不能为空) Size(max 500, message 故障描述不能超过500字) private String description; NotNull(message 故障等级必须选择) private Integer level; // 1-紧急2-重要3-一般 private String photoUrls; // JSON 字符串如 [https://.../1.jpg, https://.../2.jpg] }// FaultController.java PostMapping(/fault/submit) public Result submitFault(Valid RequestBody FaultReportDTO dto, RequestHeader(Authorization) String authHeader) { String token authHeader.replace(Bearer , ); String openid redisTemplate.opsForValue().get(token: token); if (StringUtils.isEmpty(openid)) { return Result.fail(登录已过期请重新进入小程序); } // 业务校验检查设备是否存在、是否在保修期内等 if (!equipmentService.existsById(dto.getEquipmentId())) { return Result.fail(设备编号不存在请核对后重试); } faultService.submit(dto, openid); return Result.success(); }注意Valid注解触发 JSR-303 校验错误信息由全局异常处理器捕获并转为统一 JSON 格式返回给小程序避免前端写大量 if-else 判断。2.3 小程序页面跳转与参数传递如何安全传递设备 ID 并避免 URL 污染小程序页面间传参常用wx.navigateTo({ url: /pages/report/report?eqIdEQ-2024-001 })但eqId明文暴露在 URL 中存在被篡改风险。更稳妥做法是在首页设备列表页点击某设备时先调用 SSM 接口/api/equipment/detail?eqIdEQ-2024-001获取设备完整信息含唯一equipmentCode将equipmentCode存入wx.setStorageSync跳转到报修页时URL 不带参数报修页onLoad中读取缓存并校验有效性// report.js onLoad onLoad() { const eqCode wx.getStorageSync(currentEquipmentCode); if (!eqCode || eqCode.length ! 12) { wx.showToast({ title: 设备信息异常, icon: error }); return; } this.setData({ equipmentCode: eqCode }); }SSM 后端/api/equipment/detail接口需校验该eqId是否属于当前登录用户可操作的设备范围如按部门、区域授权而非仅查数据库存在性。3. SSM 后端核心模块MyBatis 动态 SQL 与审批流状态机设计3.1 设备故障单主表与附件表的 MyBatis 多表插入策略故障单提交需同时写入主表fault_report和附件表fault_attachment一对多。SSM 中不推荐用Transactional包裹两个insert语句而应使用 MyBatis 的foreach标签实现单次 SQL 批量插入。主表fault_report结构含id,openid,equipment_id,status,create_time附件表fault_attachment含id,report_id,file_url,file_typeimage/video。!-- FaultReportMapper.xml -- insert idinsertWithAttachments parameterTypemap selectKey keyPropertyid resultTypejava.lang.Long orderBEFORE SELECT LAST_INSERT_ID() /selectKey INSERT INTO fault_report (openid, equipment_id, description, level, status, create_time) VALUES (#{openid}, #{equipmentId}, #{description}, #{level}, 0, NOW()); INSERT INTO fault_attachment (report_id, file_url, file_type) VALUES foreach collectionphotoUrls itemurl separator, (#{id}, #{url}, image) /foreach /insert// FaultService.java Transactional public void submit(FaultReportDTO dto, String openid) { MapString, Object params new HashMap(); params.put(openid, openid); params.put(equipmentId, dto.getEquipmentId()); params.put(description, dto.getDescription()); params.put(level, dto.getLevel()); params.put(photoUrls, parsePhotoUrls(dto.getPhotoUrls())); // 解析 JSON 字符串为 ListString faultReportMapper.insertWithAttachments(params); }提示MySQL 8.0 支持INSERT ... ON DUPLICATE KEY UPDATE但此处主键为自增id无需冲突处理附件表report_id为外键若主表插入失败整个事务回滚附件不会残留。3.2 审批状态流转用枚举 状态机避免 if-else 堆砌故障单状态从「待受理」→「已派单」→「处理中」→「已关闭」传统写法是if (status 0) { ... } else if (status 1) { ... }难以维护。SSM 中推荐定义FaultStatus枚举并在 Service 层用策略模式驱动状态变更public enum FaultStatus { PENDING(0, 待受理, Arrays.asList(1)), ASSIGNED(1, 已派单, Arrays.asList(2, 3)), PROCESSING(2, 处理中, Arrays.asList(3, 4)), CLOSED(3, 已关闭, Collections.emptyList()), REJECTED(4, 已驳回, Arrays.asList(0)); private final int code; private final String desc; private final ListInteger nextStatuses; FaultStatus(int code, String desc, ListInteger nextStatuses) { this.code code; this.desc desc; this.nextStatuses nextStatuses; } public boolean canTransitionTo(int targetCode) { return nextStatuses.contains(targetCode); } }// FaultService.java 状态更新方法 public void updateStatus(Long reportId, Integer fromStatus, Integer toStatus, String operatorOpenid) { FaultReport report faultReportMapper.selectById(reportId); if (!FaultStatus.fromCode(report.getStatus()).canTransitionTo(toStatus)) { throw new BusinessException(状态变更非法当前 report.getStatus() 不可转为 toStatus); } // 更新状态 记录操作日志 report.setStatus(toStatus); report.setUpdateTime(new Date()); faultReportMapper.updateById(report); FaultLog log new FaultLog(); log.setReportId(reportId); log.setOperatorOpenid(operatorOpenid); log.setFromStatus(fromStatus); log.setToStatus(toStatus); log.setCreateTime(new Date()); faultLogMapper.insert(log); }3.3 微信模板消息推送SSM 调用微信接口发送维修进度通知当状态变为「已派单」或「已关闭」时需向报修人微信推送模板消息。SSM 后端需调用微信https://api.weixin.qq.com/cgi-bin/message/template/send接口。关键点① 模板 ID 需在微信公众平台申请并审核通过②form_id来自小程序wx.requestPayment或wx.showModal的formId字段有效期 7 天③data中字段名必须与模板中定义的 keyword 严格一致如keyword1.DATA。// WechatTemplateService.java public void sendRepairStatusNotice(String openid, String templateId, String formId, Long reportId) { String accessToken getAccessToken(); // 从 Redis 缓存获取避免频繁调用 String url https://api.weixin.qq.com/cgi-bin/message/template/send?access_token accessToken; MapString, Object payload new HashMap(); payload.put(touser, openid); payload.put(template_id, templateId); payload.put(form_id, formId); // 必须是用户最近一次交互产生的有效 form_id payload.put(page, /pages/detail/detail?id reportId); // 点击消息跳转的小程序页面 MapString, MapString, String data new HashMap(); data.put(keyword1, Map.of(value, 设备故障报修)); data.put(keyword2, Map.of(value, 已派单)); data.put(keyword3, Map.of(value, new SimpleDateFormat(yyyy-MM-dd HH:mm).format(new Date()))); payload.put(data, data); String response restTemplate.postForObject(url, payload, String.class); JSONObject json JSONObject.parseObject(response); if (!0.equals(json.getString(errcode))) { log.error(模板消息发送失败{}, response); } }注意form_id是一次性使用且必须是用户主动触发如点击按钮产生的后台无法生成。因此小程序端需在提交故障单、确认派单等关键节点调用wx.getFormId()并上传至 SSM 存储供后续推送使用。4. 微信开发者工具与 SSM 联调本地 HTTPS 代理与常见 401/403 排查路径4.1 微信开发者工具无法访问本地 SSM 接口用 Nginx 反向代理解决跨域与 HTTPS 问题微信小程序强制要求request接口必须是 HTTPS 协议而本地开发 SSM 服务默认是 HTTP如http://localhost:8080。开发者工具会直接拦截并报错net::ERR_CONNECTION_REFUSED。解决方案不是买 SSL 证书而是用 Nginx 做反向代理在本地安装 Nginx配置server块监听https://localhost:8081将请求转发到http://localhost:8080并启用自签名证书。# /etc/nginx/conf.d/weixin-proxy.conf server { listen 8081 ssl; server_name localhost; ssl_certificate /path/to/self-signed.crt; ssl_certificate_key /path/to/self-signed.key; location /api/ { proxy_pass http://localhost:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }生成自签名证书命令openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /path/to/self-signed.key \ -out /path/to/self-signed.crt \ -subj /CNlocalhost配置完成后小程序app.json中request域名改为https://localhost:8081即可绕过 HTTPS 限制。4.2 小程序报 401 Unauthorized三步定位 token 校验失效根源401 错误表示 SSM 后端拒绝了请求通常因 token 过期或无效。排查顺序必须严格按以下三步检查小程序端 token 是否为空或格式错误在console.log中打印wx.getStorageSync(token)确认非空且为 32 位字符串。若为空说明登录流程未执行或失败。验证 Redis 中 token 对应的 openid 是否存在登录服务器执行redis-cli127.0.0.1:6379 GET token:abc123... # 替换为实际 token若返回(nil)说明 token 已过期或未正确写入 Redis。检查 SSM 的LoginController是否成功执行redisTemplate.opsForValue().set(...)。确认 SSM 拦截器是否放行 OPTIONS 预检请求微信开发者工具在发送 POST 请求前会先发 OPTIONS 请求。若拦截器未处理直接返回 401。在WebMvcConfigurer中添加Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowCredentials(true); }4.3 SSM 日志中出现Invalid bound statement (not found)MyBatis Mapper XML 路径与 namespace 不匹配此错误表明 MyBatis 找不到对应 SQL 映射。常见原因有三①Mapper接口类名与 XML 文件名不一致如FaultReportMapper.java对应FaultReportMapper.xml②namespace写错必须是接口全限定名③ XML 文件未被 Maven 正确打包进classes目录。检查FaultReportMapper.xml头部mapper namespacecom.example.fault.mapper.FaultReportMapper !-- SQL 语句 -- /mapper确保该字符串与 Java 接口类路径完全一致。若使用 IDEA右键src/main/resources→Reload project并确认pom.xml中resources配置包含*.xmlbuild resources resource directorysrc/main/resources/directory includes include**/*.xml/include include**/*.properties/include /includes /resource /resources /build5. 生产部署与微信备案Nginx 负载均衡 小程序服务器域名白名单配置要点5.1 SSM 应用部署到 Ubuntu 22.04JDK 17 Tomcat 10 的兼容性避坑Ubuntu 22.04 默认源安装的 OpenJDK 版本可能为 11而 SSM 项目若使用 Spring Boot 3.x 则强制要求 JDK 17。需手动安装# 下载 JDK 17 LTS wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.deb sudo apt install ./jdk-17_linux-x64_bin.deb # 验证 java -version # 应输出 openjdk version 17.0.1 # 设置 JAVA_HOME echo export JAVA_HOME/usr/lib/jvm/java-17-oracle | sudo tee -a /etc/environment source /etc/environmentTomcat 10 与 Servlet 4.0 兼容但需注意若 SSM 项目web.xml中声明version2.5则必须降级到 Tomcat 9。推荐方式是移除web.xml用SpringServletContainerInitializer启动完全基于注解配置。5.2 微信小程序服务器域名配置HTTPS 证书、ICP 备案与 request 合法域名三重校验微信要求小程序request请求的域名必须满足① 已备案ICP② 部署 HTTPSSSL 证书③ 在微信公众平台「开发管理 → 服务器域名」中显式添加。三者缺一不可。ICP 备案通过阿里云/腾讯云等服务商提交主体信息企业营业执照审核约 20 个工作日。HTTPS 证书推荐免费 Lets Encrypt用 Certbot 自动续签sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your-domain.com # 自动修改 Nginx 配置并启用 HTTPS微信后台配置登录 mp.weixin.qq.com 进入「开发」→「开发管理」→「服务器域名」填入https://your-domain.com注意不能带路径不能是 IP不能是端口。提示若测试阶段无备案域名可用「微信开发者工具 → 详情 → 本地开发 → 不校验合法域名」临时绕过但上线前必须关闭此选项否则真机无法请求。5.3 Nginx 负载均衡配置当报修并发量超 500 QPS 时的平滑扩容方案单台 Tomcat 服务器在高并发下易成为瓶颈。Nginx 可作为反向代理将请求分发到多台 SSM 实例。关键配置如下upstream ssm_backend { ip_hash; # 保证同一用户请求落到同一台 Tomcat避免 session 丢失 server 192.168.1.10:8080 weight3; # 主服务器权重高 server 192.168.1.11:8080 weight2; # 备服务器 server 192.168.1.12:8080; # 默认权重 1 } server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location /api/ { proxy_pass http://ssm_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键透传 Authorization header proxy_set_header Authorization $http_authorization; } }SSM 后端需改造为无状态① 移除HttpSession依赖全部用 Redis 存储用户会话② 文件上传路径统一指向 NFS 或对象存储如 MinIO避免各节点文件不一致。5.4 微信小程序跳转链接weixin://dl/business的生成与触发限制说明标题中未涉及但热词高频出现的weixin://dl/business是微信内部协议用于跳转到微信「服务市场」或特定企业应用。普通小程序开发者无法生成或触发该链接。它仅限微信官方合作服务商在特定场景如企业微信管理员后台使用且需微信侧白名单授权。试图在小程序wx.navigateTo中调用会直接失败并报错fail navigateTo:fail invalid url。替代方案是若需引导用户添加企业微信应用应使用wx.openCustomerContact打开客服二维码若需跳转公众号用wx.navigateToMiniProgram调起公众号关联的小程序。任何尝试伪造weixin://协议的行为均违反微信《运营规范》可能导致小程序被封禁。注意网络热词中提到的「php伪造微信浏览器头信息」属于违规黑产手段本项目严格遵循微信开放平台规范所有接口调用均使用合法access_token和js_code绝不模拟 User-Agent 或篡改请求头。本文还有配套的精品资源点击获取