简介面向微信小程序与Java Web开发者的SSM客运自助售票小程序整站源码基于SpringSpringMVCMyBatis经典框架涵盖前端小程序界面、后端接口与数据持久层实现车次查询、选座、支付等核心售票流程并配套数据库脚本与系统设计论文非常适合课程设计、毕业设计及项目复盘。资源含1276个文件压缩包约14.87MB以java后端、js逻辑、vue管理端页面、wxml/wxss小程序界面、png图片及sql脚本为主另含json配置、svg图标、xml映射、bat启动脚本等目录结构清晰便于按模块学习与二次开发。当前已有77人学习。整站源码经测试可正常运行代码注释较完整前端交互与后端接口一一对应sql脚本提供了建表及初始数据论文梳理了SSM整合思路与数据库设计规范能帮助快速复现环境、理解系统全貌适合想掌握SSM小程序全栈开发、需要完整参考项目的开发者深入研读。 看到这个压缩包名字基本就能判断它是什么了微信小程序做前端、SSM 做后端、配好 SQL 脚本和论文标准的毕业设计套餐。客运自助售票系统本质上是个典型的查票-购票-支付-出票业务闭环把这块拆开看单是数据库设计和支付对接这两块就够写一篇很扎实的技术复盘。下面我按自己平时做这类项目的习惯把这个项目的设计思路、核心代码片段和踩坑点完整梳理一遍适合正在做同类小程序毕设、或者想快速上手小程序 SSM 后端联调的同学参考。1. 项目整体拆解与需求分析1.1 这个系统的业务主线是什么别一上来就打开代码先想清楚业务。客运自助售票小程序面向两类角色乘客和管理员。乘客端的核心诉求很简单查线路、查班次、下单支付、退票、查看电子票管理员端则需要维护线路班次、设置票价、处理订单、看统计报表。把流程抽象出来其实就是一条主链路乘客选择出发城市和到达城市系统按日期查询当天可售班次乘客选班次、选乘车人、提交订单支付成功后生成电子票。到站后凭电子票扫码或人工核验乘车。这条链路里有两个最容易出问题的地方一个是余票扣减一个是支付回调。前者涉及并发后者涉及微信支付签名和回调验签这两块是整篇文章的重头戏。设计系统时我习惯先把这两处想清楚再去写代码。1.2 为什么是微信小程序 SSM这个组合很多同学会问现在新项目不都推荐 Spring Boot 吗为什么毕业设计里还大量出现 SSM原因很实际课程大纲和论文模板还停留在 Spring SpringMVC MyBatis 这套体系里同时 SSM 的配置过程对理解框架原理更有帮助手写 xml 配置一遍之后再去看 Spring Boot 的自动配置理解会深得多。微信小程序作为前端载体优势很明显不需要下载安装、用完即走适合客运站这种低频刚需场景。小程序通过 wx.request 请求后端 HTTP 接口后端返回 JSON 数据前后端通过这种标准方式联调。相比 Vue Spring Boot 的全栈项目小程序端不用关心浏览器兼容性开发体验反而更接近移动端原生应用。1.3 论文与源码的对应关系拿到项目以后别急着跑代码先把论文目录和源码结构对照一遍。常见论文目录是需求分析、系统设计、系统实现、系统测试。对应到源码里就是需求分析 - 数据库表结构设计 用例图搞清楚每个角色能做什么系统设计 - 架构图 接口定义 数据库物理模型系统实现 - Controller、Service、Mapper 三层代码以及小程序页面系统测试 - 功能测试用例和截图这种映射关系是写论文的关键。写论文的时候不要泛泛而谈直接把核心表结构、核心接口请求参数和返回结果截图放上去再配一段功能描述就能凑出很扎实的一章。后面调试时记录的问题和解决办法也可以原封不动搬进测试章节。2. 数据库设计与 SQL 脚本要点2.1 核心表怎么设计客运售票系统最少要六张核心表管理员表、用户表、线路表、班次表、订单表、乘车人表。这里的关键不是表的数量而是字段的约束。直接看我整理过的建表节选CREATE TABLE tb_schedule ( id INT PRIMARY KEY AUTO_INCREMENT, line_id INT NOT NULL COMMENT 线路ID, start_station VARCHAR(50) NOT NULL COMMENT 始发站, end_station VARCHAR(50) NOT NULL COMMENT 到达站, depart_date DATE NOT NULL COMMENT 发车日期, depart_time VARCHAR(10) NOT NULL COMMENT 发车时间 HH:mm, arrive_time VARCHAR(10) DEFAULT COMMENT 预计到达时间, total_seats INT NOT NULL DEFAULT 40 COMMENT 总座位数, remain_seats INT NOT NULL DEFAULT 40 COMMENT 余票数, price DECIMAL(10,2) NOT NULL COMMENT 票价, status TINYINT DEFAULT 1 COMMENT 1正常 0停运, UNIQUE KEY uk_line_date_time (line_id, depart_date, depart_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意几个容易被忽略的细节日期和时间分开存方便按日期查询班次价格用 DECIMAL 而不是 Float否则金额计算会产生精度缺失班次表加唯一索引防止同一辆车在同一时刻重复发布班次所有表统一使用 utf8mb4 字符集避免存 emoji 昵称时乱码。订单表是另一个重点CREATE TABLE tb_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 订单号, user_id INT NOT NULL COMMENT 用户ID, schedule_id INT NOT NULL COMMENT 班次ID, passenger_name VARCHAR(20) NOT NULL COMMENT 乘车人姓名, passenger_id_card VARCHAR(18) NOT NULL COMMENT 身份证号, price DECIMAL(10,2) NOT NULL COMMENT 实际支付金额, status TINYINT DEFAULT 0 COMMENT 0待支付 1已支付 2已退票 3已失效, create_time DATETIME NOT NULL, pay_time DATETIME DEFAULT NULL, UNIQUE KEY uk_user_schedule (user_id, schedule_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;订单号用业务编码生成比如日期加时间戳加随机数比如202501201530123456长度控制在 32 位以内避免用自增 id 直接暴露订单量。用户和班次加唯一索引防止同一用户对同一班次重复下单。2.2 余票和订单的关联设计余票扣减是整个系统并发风险最高的点。如果不加控制两个用户同时买最后一张票各自读到的余票都是 1各自插入订单成功最后余票变成负数超卖了。解决思路有两个层次。第一层靠数据库条件更新兜底MyBatis 的 update 语句写成这样update iddeductRemainSeats UPDATE tb_schedule SET remain_seats remain_seats - 1 WHERE id #{scheduleId} AND remain_seats 0 /update第二层在 Service 层用事务包裹先扣余票扣减影响行数为 0 直接抛异常回滚再插入订单。这里的关键是把扣减和插入订单放在同一个事务里而不是先插订单再扣票。先插订单的话一旦用户不支付还要额外处理订单和余票的一致性。后面的 SQL 脚本文件里记得把外键关系、索引和初始数据都写好。初始化数据至少要有几条线路、几个班次、一个管理员账号不然登录后台什么都看不到。2.3 SQL 脚本初始化最容易踩的坑拿到项目的 SQL 脚本直接导入 MySQL经常会报错。最常见的三个原因字符集问题。脚本文件里没有指定 CHARSET导入时报Invalid default value for create_time或中文乱码。用 Navicat 导入时编码选 utf8mb4命令行导入加--default-character-setutf8mb4。MySQL 版本差异。低版本数据库执行带ENGINEInnoDB的脚本没问题但遇到utf8mb4_unicode_ci排序规则可能报错可以全局替换成utf8mb4_general_ci。外键依赖顺序。先建子表再建主表会直接失败导入前检查脚本里的建表顺序或者干脆先去掉外键约束数据初始化完成后再手工加。如果你已经导乱了最快的处理方式是删库重建不要试图在报错状态下反复改表结构。3. 后端接口与核心业务实现3.1 用户登录与鉴权链路微信小程序登录不能直接拿用户名密码必须走微信的 openid 体系。流程是这样的小程序端调用 wx.login 拿到临时 code把 code 传给自己后端后端拿着 code 调用微信的 code2Session 接口换回 openid 和 session_key然后把 openid 对应的用户记录塞进数据库返回一个自定义 token后续请求都带上这个 token。SSM 后端这个接口大概长这样ResponseBody RequestMapping(/api/user/login) public Result login(RequestBody LoginRequest req) { // 1. 用 code 换 openid调用微信接口 String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code req.getCode() grant_typeauthorization_code; // 2. 用 RestTemplate 发起 GET 请求解析 openid // 3. 查 tb_user不存在则自动注册 // 4. 生成 UUID 作为 token存到缓存或数据库返回小程序端 }注意 appid 和 secret 不要硬编码在 JS 文件里要放在后端配置文件提交代码时把真实密钥替换成占位符。这虽然是个毕设项目泄露密钥的后果依然很严重。3.2 车次查询与下订单的并发控制车次查询比较简单一条 select 语句按日期过滤SELECT * FROM tb_schedule WHERE start_station #{startCity} AND end_station #{endCity} AND depart_date #{date} AND status 1下订单的并发控制才是关键。常规做法是把扣余票和插入订单包在事务里也就是前面提到的那段 update 语句。很多同学的代码如下Transactional public Order createOrder(OrderDTO dto) { // 1. 查班次是否存在 // 2. 扣余票影响行数为 0 则提示余票不足 // 3. 生成订单号插入订单表 // 4. 返回订单信息 }这套方案能解决绝大多数超卖问题。如果还想更稳可以在班次表加一个 version 乐观锁字段但实际项目中条件更新已经够用。真正要注意的是 Service 类必须通过 Spring 代理调用否则 Transactional 不生效自己 new 出来的对象方法上加注解是没用的。3.3 待支付订单的超时处理用户下单后不支付订单不能一直占着座位。正常做法是下单后 15 分钟未支付自动取消订单并回滚余票。SSM 项目里用 Spring 提供的定时任务最简单。在 Spring 配置文件中启用注解驱动task:annotation-driven /然后写一个定时任务类Component public class OrderTimeoutTask { Scheduled(fixedDelay 60000) // 每分钟执行一次 public void cancelTimeoutOrders() { // 1. 查出创建时间超过 15 分钟且状态为 0 的订单 // 2. 订单状态改成 3已失效 // 3. 对应班次的 remain_seats 加回去 } }这里容易忽略一个点回滚余票的时候必须判断当前订单处于待支付状态避免用户刚好在这分钟完成支付任务又把余票加回去导致数据错乱。定时任务里同样要用事务保护。3.4 微信支付 v3 对接经验支付功能是这类项目最麻烦的模块。微信支付 API v3 和 v2 的差别很大v3 用 JSON 格式交互所有敏感字段都要用商户私钥加签。小程序端调起支付前后端需要先调用微信统一下单接口拿到 prepay_id再用 prepay_id 生成小程序端需要的支付参数。小程序端再通过 wx.requestPayment 调起收银台。后端对接 v3 需要准备四样东西商户号 mchid商户 API 私钥apiclient_key.pem商户证书序列号APIv3 密钥32 位字符串不是商户平台登录密码这块最容易踩的坑有三个。第一个是签名结构不对微信返回 401 签名错误检查签名字符串拼接规则特别是换行符位置。第二个是证书序列号和私钥不匹配换过商户号之后证书和密钥没有同步替换。第三个是回调地址必须是公网可访问的 HTTPS 地址且与配置的支付回调域名一致。如果支付权限还没申请下来项目里预留一个模拟支付开关后端根据配置决定直接置为已支付还是走微信真实支付。这样在开发阶段可以验证完整链路不影响进度。4. 小程序前端页面与联调实践4.1 小程序目录结构与页面划分小程序端代码结构一般这样组织miniprogram/ ├── app.js // 全局逻辑App() 入口 ├── app.json // 页面注册、窗口配置 ├── utils/ │ ├── request.js // wx.request 封装 │ └── auth.js // 登录 token 管理 └── pages/ ├── index/ // 首页出发地目的地选择 ├── schedule/ // 班次列表 ├── confirm/ // 订单确认页 ├── order/ // 订单列表 ├── detail/ // 订单详情与电子票 └── mine/ // 个人中心页面之间用 wx.navigateTo 跳转传参尽量只传 id不要传整个对象。比如班次列表页跳转到确认页只传 scheduleId确认页再根据 id 请求后端拿完整数据这样可以避免页面间数据不同步。4.2 核心购票流程的前端实现封装的 request 请求是前端的基石。统一在请求头里带上 token响应码非 0 时统一弹出提示function request(url, data, method GET) { return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl url, data, method, header: { Content-Type: application/json, token: wx.getStorageSync(token) }, success: (res) { if (res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: reject }); }); }baseUrl 在开发阶段填局域网 IP模拟器可以访问真机调试要填电脑的局域网 IP 并且保证手机和电脑在同一网段或者直接用开发者工具的不校验合法域名选项配合本机调试。上线以后必须换成备案过的 HTTPS 域名。购票流程的交互有几点值得注意。选座这块如果系统不做图形化选座用简单的余票数字加乘车人信息表单就够了减少前端工作量。支付成功后不要直接跳回首页而是跳转到订单详情页展示电子票信息这样用户能立刻确认购买结果。4.3 联调阶段的高频问题联调最容易遇到的几个问题请求发出去但收不到响应。先用开发者工具的 Network 面板看请求是否发出、后端返回什么。如果是 404检查项目访问路径SSM 项目经常有 context-path 配置可能造成接口地址整体偏移。后端返回 JSON 但小程序解析失败。检查后端返回的 Content-Type 是否为 application/json;charsetUTF-8SpringMVC 如果漏配消息转换器可能返回的是 String 类型导致解析报错。键盘遮挡输入框。小程序页面底部有输入框时在页面的 json 配置里设置disableScroll: false并监听键盘弹起事件做位移或者直接用adjust-position属性控制页面整体上移。组件嵌套显示异常。如果项目里用到 swiper 组件嵌套 videoiOS 上会出现全屏错位的兼容性问题这个没有通用解法通常需要判断机型分别处理或者干脆避免嵌套改用其他布局方案。5. 常见问题与排查实录5.1 从开发到上线的踩坑清单我把这几年带项目过程中常遇到的问题整理成了一张表逐个对照着排查效率会高很多问题现象排查方向解决办法SQL 脚本导入报错字符集 / 外键顺序 / 版本指定 utf8mb4 重新导入检查建表顺序接口 404context-path 配置确认项目路径和请求地址拼接是否正确登录失败appid / secret 不匹配检查公众平台配置确认不是测试号中文乱码JDBC 连接串 / 文件编码连接串加 characterEncodingutf8支付签名错误签名串拼接 / 私钥不匹配按微信官方文档核对签名串格式开发工具真机失败局域网 / 域名白名单勾选不校验合法域名或配置 HTTPS定时任务不执行Spring 配置缺失检查是否开启 task 注解驱动表格里最后一项值得展开说说。SSM 项目里定时任务不执行十有八九是配置问题。要么没加task:annotation-driven /要么定时任务类没有被 Spring 扫描到。还有一种隐蔽情况任务类里调用了另一个类的方法事务不生效看起来像是任务没执行其实是执行到一半报错被吞掉了。排查时先看日志不要盲目改代码。5.2 小程序支付权限与审核限制很多同学做完功能卡在最后一步小程序审核不过或者后台申请支付权限时被驳回。这里面有几道硬门槛。支付权限是商户号的事和程序本身关系不大。小程序必须完成微信认证企业主体然后在小程序后台申请微信支付审核通过后才能拿到商户号。个人主体的小程序无法开通微信支付这是平台规则不是代码能绕过去的。如果你的账号属于这种情况演示的时候只能走模拟支付模式在后台把订单手工标记为已支付。另一方面小程序内容审核也常出问题。购票类小程序涉及旅游出行类目审核时需要提供相应资质。如果只是课程设计做演示可以用两个办法应付一种是把系统定位成演示版在页面显眼位置标注非商用另一种是提交审核时选择与项目无关的类目但这里存在违规风险如被判定为类目不符或涉嫌违规收款可能直接导致支付功能被暂停使用这就是标题里那句由于小程序违规支付功能暂时无法使用的常见原因。所以我会建议毕设项目不要强行上线微信支付提交一个带模拟支付功能的演示版本就足够真到了答辩现场用开发者工具演示完整支付流程即可。论文里把支付流程画清楚说明从统一下单到回调处理的完整链路再标注因账号资质限制采用模拟支付演示老师一般都能接受。5.3 部署上线时容易忽略的配置如果决定要把项目部署到真实服务器以下配置项逐个检查小程序后台配置 request 合法域名必须是 HTTPS且证书有效。后端服务器放行对应端口服务器安全组规则也要同步。数据库连接串里的密码不要用弱密码不要把生产数据库暴露到公网。微信支付回调地址需要支持 HTTPS并且回调接口不能被拦截器拦截否则微信服务器请求不到回调地址订单永远处于未支付状态。还有一点比较隐蔽SSM 项目打包后通常部署在 Tomcat 的 webapps 目录要注意 applicationContext.xml 里配置的数据库连接信息是否和服务器环境匹配很多人本地跑得好好的一上服务器就报数据库连接失败多半是配置文件里的 IP、端口、账号密码没有改。写在最后我做完这类小程序项目之后最大的体会是客运售票这种传统行业的信息化改造技术难度并不高难点全在业务流程的完整性和数据一致性上。把支付、余票、订单超时这几个关键环节想清楚代码写起来反而很顺。最后分享一个我自己的习惯每次动手写代码之前先把所有核心表结构建好再画一遍接口清单和字段说明磨刀不误砍柴工后面联调和写论文都会省很多力。本文还有配套的精品资源点击获取