尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SSM+微信小程序客运售票系统实战指南

SSM+微信小程序客运售票系统实战指南 简介本资源是一套完整的微信小程序实战项目——SSM架构的客运自助售票系统面向Java后端与小程序前端开发者尤其适合希望掌握SSM框架整合、小程序前后端联调及交通类业务系统设计的学习者。资源包含整站源码、可直接执行的SQL建表脚本及配套论文覆盖车次查询、座位选择、在线支付等核心业务流程助力快速理解高并发场景下的数据库优化索引、缓存与小程序UI规范落地。压缩包共1276个文件含127个Java后端逻辑文件、140个Vue组件、187个JS交互脚本、94个WXML页面结构及96个WXSS样式文件辅以SVG图标、PNG素材与配置类JSON整体14.87MB结构清晰、注释详尽便于模块化学习与二次开发。目前已有77人学习下载提供从环境搭建含install/run批处理、数据库初始化到接口调试的完整链路支持是深入理解SpringSpringMVCMyBatis协同机制与小程序工程化实践的优质参考。1. 这不是又一个“Hello World”小程序它是一套能直接跑通的客运售票闭环系统含完整 SSM 后端、微信前端、数据库初始化脚本和可答辩论文你下载的这个压缩包名字里带【微信小程序】和 SSM但别被“小程序”三个字骗了——它根本不是纯前端 demo。整套工程是典型的「前后端分离 微信生态嵌入」架构微信小程序作为用户触点买票、查班次、支付回调后端用 Spring SpringMVC MyBatisSSM搭建 RESTful 接口服务MySQL 存储车次、座位、订单、用户等全量业务数据SQL 脚本一键建库建表论文部分甚至包含 ER 图、用例图、系统部署拓扑和测试用例表。我去年帮交通职院带毕设时学生拿这套源码改了 3 天就上线了校际定制班线试运行系统。它解决的不是“怎么显示一个按钮”而是“如何在微信里完成实名购票、座位锁定、超时释放、订单状态机流转、退票资金原路退回”这一整条链路。适合两类人一是需要快速交付真实业务场景小程序的 Java 开发者尤其熟悉 Spring Boot 但想补 SSM 底层细节的二是计算机专业做毕业设计的学生——论文结构、代码注释密度、SQL 建模规范度都按本科毕设硬指标来打磨过。注意它不依赖云开发、不走小程序云函数所有接口调用直连你本地或服务器上的 Tomcat。2. 搭建前必须厘清的三件事为什么选 SSM 而非 Spring Boot微信小程序如何与 Java 后端安全通信SQL 脚本到底初始化了哪些关键表2.1 SSM 选型不是怀旧而是为教学穿透性和运维可控性让路很多新手看到“SSM”第一反应是“过时”但在这套客运系统里它恰恰是优势。Spring Boot 自动装配虽快但当学生要理解“拦截器怎么拦登录态”“MyBatis 的 SqlSession 如何被 Spring 管理”“DispatcherServlet 怎么分发请求到 Controller”时SSM 的 XML 配置spring-mvc.xml、applicationContext.xml就像一张透明地图。比如web.xml里明确声明了ContextLoaderListener加载根上下文DispatcherServlet加载 Web 上下文这种分层清晰度对调试 session 共享、事务传播边界至关重要。而 Spring Boot 的SpringBootApplication把这些全藏进自动配置初学者遇到No bean named xxxMapper时往往无从下手。本项目中spring-mvc.xml里mvc:interceptors显式配置了LoginInterceptorapplicationContext.xml中tx:annotation-driven /和bean classorg.springframework.jdbc.datasource.DataSourceTransactionManager组合确保购票下单时“查库存→扣库存→生成订单→更新状态”四步操作原子性。这不是技术债是教学友好型设计。2.2 微信小程序不直接连数据库所有通信必须经由后端 API 层且需严格校验 openid 与 session_key小程序端所有请求如wx.request({ url: https://your-server.com/api/ticket/list })都指向你的 SSM 后端域名。关键在于不能把数据库账号密码写进小程序代码也不能让小程序直接调 MySQL。本项目采用标准微信鉴权流程小程序调wx.login()获取临时 code → 发给后端/api/auth/login接口后端用该 code appidappsecret向微信服务器换取openid和session_key后端生成自定义登录态 token存 Redis有效期 2 小时返回给小程序后续所有请求携带Authorization: Bearer token后端LoginInterceptor解析 token 并校验openid是否匹配当前用户。提示session_key仅用于解密微信加密数据如手机号绝不能传给前端。本项目WxUtil.java中decryptPhoneNumber()方法严格在服务端完成解密解密后才存入user_info表的phone字段。2.3 SQL 脚本不是简单建表而是按客运业务实体关系建模含外键约束与索引优化解压后你会看到db_init.sql它不是CREATE TABLE user(...)的堆砌而是按 ER 关系落地bus_line表存线路起点/终点/里程/票价bus_schedule表存班次关联bus_line_id含发车时间、车型、总座位数seat_layout表存座位图JSON 格式存储每排座位号及状态A1:0,A2:10空闲1已售order_master主订单表含schedule_id,user_id,total_amount,statusorder_detail明细表关联order_master_id记录具体座位号seat_no。所有外键均启用ON UPDATE CASCADE ON DELETE RESTRICT避免误删班次导致订单数据孤儿化。关键字段如bus_schedule.depart_time、order_master.create_time均建 BTree 索引支撑“查今日所有班次”“查某用户历史订单”高频查询。执行脚本前请确认 MySQL 版本 ≥ 5.7因用到 JSON 类型字段seat_layout.layout_data。3. 五步跑通整站从 JDK 配置到小程序真机扫码每个环节都有可验证的输出信号3.1 环境准备JDK 8 Tomcat 8.5 MySQL 5.7 微信开发者工具必须用稳定版不要用 JDK 11 或 17 —— SSM 项目中spring-webmvc-4.3.29.RELEASE.jar依赖javax.servlet-api-3.1.0.jarJDK 9 移除了javax.xml.bind包会导致XmlBeanDefinitionReader初始化失败。Tomcat 必须选 8.5.x非 9 或 10因其 Servlet 规范匹配web.xml中web-app version3.1。MySQL 用 5.7.32 以上确保 JSON 字段支持。微信开发者工具选Stable 1.06.2304060官网下载页标注“稳定版”新版工具对wx.request的 HTTPS 证书校验更严若后端用自签名证书会报net::ERR_CERT_AUTHORITY_INVALID此时需在开发者工具设置中勾选“不校验合法域名、HTTPS 证书”。3.2 后端部署修改三处配置启动 Tomcat 后访问http://localhost:8080/api/test返回{code:200,msg:ok}即成功修改src/main/resources/jdbc.propertiesjdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/bus_ticket?useUnicodetruecharacterEncodingutf8serverTimezoneGMT%2B8allowPublicKeyRetrievaltrueuseSSLfalse jdbc.usernameroot jdbc.password123456注意serverTimezoneGMT%2B8解决时区错误useSSLfalse避免 MySQL 8.0 默认 SSL 强制allowPublicKeyRetrievaltrue兼容新版本认证插件。执行db_init.sql创建数据库bus_ticket并导入初始数据含测试班次、座位图、管理员账号Eclipse/IDEA 导入项目为 Dynamic Web ProjectTarget Runtime 选 Tomcat 8.5Deployment Assembly 中确保src/main/webapp映射到/启动 Tomcat在浏览器访问http://localhost:8080/api/test返回 JSON 即证明 SpringMVC 容器加载成功访问http://localhost:8080/api/user/login用 Postman 发送 POST 请求Body 选 x-www-form-urlencoded填usernameadminpassword123456返回{code:200,data:{token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...}}即登录模块就绪。3.3 小程序端配置替换app.js中的baseUrl真机扫码前必须通过wx.request调通一个接口打开miniprogram/app.js找到globalData对象globalData: { userInfo: null, baseUrl: https://your-domain.com/api/ // ← 修改此处本地调试用 http://localhost:8080/api/ }注意真机扫码调试时baseUrl必须是 HTTPS 域名微信强制不能是http://localhost。本地调试用开发者工具“预览”功能即可此时baseUrl设为http://localhost:8080/api/。在miniprogram/pages/index/index.js的onLoad中添加测试请求wx.request({ url: getApp().globalData.baseUrl ticket/list, method: GET, success: (res) { console.log(班次列表获取成功, res.data) that.setData({ scheduleList: res.data.data }) }, fail: (err) { console.error(请求失败, err) } })在开发者工具控制台看到班次列表获取成功日志且 Network 面板中ticket/list返回状态码 200、响应体含[{ id:1, lineName:杭州-上海, departTime:08:00 }]即前后端联调成功。3.4 数据库初始化验证检查bus_schedule与seat_layout的关联是否生效执行完db_init.sql后手动验证两个关键点查bus_schedule表确认id1的班次line_id1depart_time2024-05-20 08:00:00查seat_layout表确认schedule_id1的记录中layout_data字段为 JSON 字符串如{A1:0,A2:0,B1:0,B2:0}表示 4 个座位全空闲。若seat_layout.layout_data是空字符串或 NULL说明 JSON 字段插入失败需检查 MySQL 是否启用了local_infileON某些云主机默认关闭或改用INSERT INTO seat_layout (schedule_id, layout_data) VALUES (1, {A1:0});手动插入。3.5 真机扫码部署微信开发者工具生成体验版二维码手机微信扫描后输入管理员账号登录在开发者工具点击「上传」填写版本号如1.0.0、项目备注如“客运售票 V1”上传成功后登录 微信公众平台 → 开发管理 → 开发版本 → 提交审核学生毕设可跳过审核直接生成体验版在开发管理页点击「体验二维码」用手机微信扫描首页输入管理员账号admin密码123456点击登录成功进入首页后点击“今日班次”应看到杭州-上海、杭州-南京等线路列表点击任一班次进入选座页座位图应渲染出 A1/A2/B1/B2 等格子点击可切换选中状态。若扫码后白屏检查手机网络是否能访问你的服务器 IP若提示“request:fail ssl hand shake error”说明域名未配 HTTPS 或证书无效此时需回退到开发者工具预览模式调试。4. 避坑指南这五个翻车点90% 的人在部署第三步就栽了4.1 现象Tomcat 启动后访问/api/test返回 404原因Eclipse/IDEA 中项目未正确发布到 Tomcat 的webapps目录或web.xml中servlet-mapping的url-pattern与实际请求路径不匹配。解决检查web.xml中servlet-mapping的url-pattern是否为/api/*本项目是再确认项目 Properties → Deployment Assembly 中src/main/webapp是否映射到/且target runtime已选 Tomcat 8.5。若仍 404在 Tomcatlogs/catalina.out中搜索Caused by: java.lang.ClassNotFoundException定位缺失的 JAR 包常见为mysql-connector-java-5.1.47.jar未放入WEB-INF/lib。4.2 现象小程序调ticket/list返回 500日志报java.lang.NullPointerExceptionatTicketController.list()原因TicketController中Autowired private TicketService ticketService;注入失败通常因TicketService实现类未加Service注解或applicationContext.xml中未开启context:component-scan base-packagecom.xxx.service /。解决检查TicketServiceImpl.java是否有Service(ticketService)打开applicationContext.xml确认context:component-scan的base-package值为com.bus.ticket本项目实际包名而非com.xxx。若用 IDEA按CtrlShiftT搜索TicketServiceImpl看类上方是否有绿色灯泡提示“Add Service annotation”。4.3 现象MySQL 执行db_init.sql报错ERROR 1064 (42000): You have an error in your SQL syntax原因SQL 文件编码为 UTF-8 BOMMySQL 客户端解析时将 BOM 字节EF BB BF当作非法字符或seat_layout.layout_data字段类型在 MySQL 5.6 下不支持 JSON。解决用 Notepad 打开db_init.sql→ 编码 → 转为 UTF-8 无 BOM 格式若 MySQL 版本 5.7将JSON类型字段改为TEXT并在 Java 实体类SeatLayout.java中private String layoutData;保持 String 类型Column(name layout_data)注解不变。4.4 现象小程序真机扫码后登录页空白控制台无任何日志原因app.js中baseUrl写成http://192.168.1.100:8080/api/但手机与电脑不在同一局域网或路由器防火墙阻止了 8080 端口。解决手机浏览器直接访问http://192.168.1.100:8080/api/test若打不开说明网络不通。此时需① 电脑关闭防火墙② 手机连接与电脑相同的 WiFi③ 在 Tomcatconf/server.xml中Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 address0.0.0.0/address0.0.0.0允许外部访问。4.5 现象选座页点击座位无反应Network 面板中无seat/update请求发出原因miniprogram/pages/seat/seat.js中bindtapselectSeat对应的selectSeat方法未绑定到 Page 的methods对象或 WXML 中>GetMapping(/list) public Result list( RequestParam(defaultValue 1) Min(1) Integer page, RequestParam(defaultValue 10) Max(100) Integer size, RequestParam(required false) String keyword) { PageHelper.startPage(page, size); // ← MyBatis-PageHelper 插件启动分页 ListTicketVO list ticketService.listByKeyword(keyword); PageInfoTicketVO pageInfo new PageInfo(list); return Result.success(pageInfo); }注意Min(1)和Max(100)防止page-1或size9999导致全表扫描PageHelper.startPage()必须在ticketService.listByKeyword()调用前执行否则分页失效。5.2 Service 层SQL 改写为动态查询用if标签处理 keyword 为空的情况TicketMapper.xml中原 SQLselect idlist resultTypeTicketVO SELECT s.id, s.line_name, s.depart_time, s.arrive_time FROM bus_schedule s /select改为动态 SQLselect idlistByKeyword resultTypeTicketVO SELECT s.id, s.line_name, s.depart_time, s.arrive_time FROM bus_schedule s where if testkeyword ! null and keyword ! AND (s.line_name LIKE CONCAT(%, #{keyword}, %) OR s.depart_place LIKE CONCAT(%, #{keyword}, %) OR s.arrive_place LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY s.depart_time ASC /select关键点where标签自动处理 AND 前缀避免WHERE AND ...语法错误CONCAT(%, #{keyword}, %)防止 SQL 注入用#{}而非${}ORDER BY必须显式声明否则 PageHelper 分页结果顺序不可控。5.3 前端页面在index.wxml中加搜索框index.js中onPullDownRefresh时重置 page1!-- index.wxml -- view classsearch-bar input bindconfirmonSearch placeholder搜索线路、出发地、目的地 / /view scroll-view scroll-ytrue bindscrolltolowerloadMore !-- 班次列表 -- /scroll-view// index.js Page({ data: { scheduleList: [], page: 1, size: 10, keyword: }, onSearch(e) { const keyword e.detail.value.trim() this.setData({ keyword, page: 1 }) // ← 搜索时重置页码 this.loadScheduleList() }, loadScheduleList() { wx.request({ url: getApp().globalData.baseUrl ticket/list, data: { page: this.data.page, size: this.data.size, keyword: this.data.keyword }, success: (res) { if (this.data.page 1) { this.setData({ scheduleList: res.data.data.list }) } else { this.setData({ scheduleList: this.data.scheduleList.concat(res.data.data.list) }) } } }) } })血泪经验onPullDownRefresh下拉刷新时必须调用wx.stopPullDownRefresh()手动停止动画否则用户会以为卡死。我在loadScheduleList()的success回调末尾加wx.stopPullDownRefresh()并用try-catch包裹避免stopPullDownRefresh在非下拉状态报错。从那以后我每次给学生讲分页都强制他们先手写一遍PageHelper.startPage()的调用位置再对比LIMIT #{start}, #{size}的原始写法——不是为了炫技是让他们看清框架帮你省的不是代码行数而是对数据库执行计划的理解成本。希望帮到你。本文还有配套的精品资源点击获取
返回列表