去年帮一位学弟把关毕业设计选题他最初报的题目是“商旅出行小程序”我直接让他改了。原因很简单不带B2B、不做企业差旅管控这个题目做出来就是一个普通酒店机票聚合页要么被答辩老师追问“业务复杂度在哪”而卡壳要么论文里写不出有区分度的东西。后来我们把题目定成“基于Spring Boot的出差商旅B2B商务出行住行订购小程序”围绕企业差旅审批、协议价、对公结算这几个关键词重新设计整个项目的深度和答辩话题一下子就不一样了。这篇博文就把这套方案完整拆开讲从需求边界、技术选型、数据库设计到下单审批结算三条核心流程的落地再到联调阶段最容易踩的坑最后附上答辩演示和讲解的加分技巧。如果你正在做类似的Java Web 小程序的毕业设计或者刚入职被分到企业服务类系统的开发任务这篇内容可以直接当一份“从选题到答辩”的实操笔记来用。1. 需求拆解B2B差旅和C端买票根本不是一回事1.1 真实企业场景里的三个核心痛点很多人一听到“出差商旅”第一反应是携程、飞猪那种C端应用搜酒店、比价格、下单、支付就完了。但企业场景完全不同。我自己接触过一家做制造业的企业行政负责人她当时吐槽得最多的三件事第一员工出差订票先自己垫钱回来贴发票报销流程走一个月员工怨气很大第二行政没法提前管控超标消费有人明明可以订经济舱结果订了全价公务舱钱花了才知道第三月底对账要对几十家平台的订单记录电话确认、邮件核对人肉对账能对到凌晨。这三个痛点正好对应B2B出差商旅系统的三个核心模块企业统一结算员工不需要垫资、差旅标准管控下单前就拦截超标、自动对账单按企业维度生成月结账单。这也是“B2B”这个前缀的灵魂所在——系统服务的主体不是出差员工而是企业本身。投标答辩的时候只要能把这三句话讲清楚评委就知道你这套系统不是“套了个壳的小程序”而是真的理解了企业差旅的业务逻辑。1.2 毕业设计的需求边界怎么圈选题最忌讳的是贪大。有的同学一上来就想做“全流程差旅管控平台”既要对接真实票务API又要接支付网关还要做电子发票。结果做了三个月支付商户号没申请下来机票接口的签约资质也没搞定最后草草收尾。我当时给学弟的建议是把“住”和“行”两条主链路做透把“审批”和“结算”这两个B2B特色模块做成亮点其他环节砍掉或者用模拟方案代替。具体来说行的部分不接真实中航信或12306接口自己维护一张模拟班次表含航班号、航司、出发到达时间、舱位、价格预订后不产生真实票号但订单状态流转待审批→已出票→已退票要完整。住的部分维护协议酒店库每个酒店挂房型、协议价、剩余房间数订购后生成入住单状态包括已确认、已入住、已退房。B2B部分员工提交出差申请单含事由、城市、起止日期、预算审批通过后才能下单企业对公账户预存余额用预存款支付系统按订单自动生成月度结算单。这个范围做下来小程序端约6到8个页面后端约15到20张表、30到40个接口对于毕业设计来说体量刚好论文也写得出东西。2. 技术选型与系统架构为什么是Spring Boot2.1 后端选型的三层考量现在Java后端毕业设计的主流选项无非三种传统的SSH/SSM、Spring Boot、Spring Cloud微服务。对毕业设计这个场景Spring Boot是性价比最高的中间选项。对比一下你就明白了SSM的问题是配置繁琐一个XML配置文件反复调试光环境搭建就能耗掉一周留给业务开发的时间被压缩得很厉害。Spring Cloud的问题正好相反它强调的是服务治理、熔断、网关、分布式事务这些能力但你的业务规模根本用不上——强行引入只会让答辩老师觉得你在“为了技术而技术”。Spring Boot则处在中间内嵌Tomcat、自动装配、零XML配置把环境成本降到最低同时保留分层架构、依赖注入、AOP切面这些Spring生态的核心能力足够支撑一个模块清晰的单体应用。从答辩角度说选题是Spring Boot还有一个隐性优势市面上资料极多踩过的坑网上都有答案遇到诡异报错不会卡死。而且绝大多数企业现在的新项目就是Spring Boot评委看到这个技术栈会觉得你的技术方向是实用且跟得上行业的。2.2 小程序端用原生还是uni-app小程序端有两个路线可选微信原生小程序和uni-app。我建议用原生。原因有两条第一原生小程序不用考虑跨端兼容wxml、wxss、js三件套写起来直来直去不需要理解Vue语法和微信小程序语法之间的映射关系学起来快。第二答辩演示的时候就在微信开发者工具里跑真机预览也顺手少一层编译转换出问题的概率小很多。有的同学想用uni-app是因为“以后找工作多一门技能”这个理由没错但毕业设计的核心目的是保质保量地完成一个闭环项目。如果你对Vue本身就很熟用uni-app也完全可行代码组织上会更舒服如果你Vue只是停留在“听过”的程度我强烈建议直接用原生别在框架选型上给自己加戏。2.3 核心表结构设计思路数据库设计是论文里占篇幅最多的部分也是答辩老师最喜欢追问的地方。我这套方案里大概设计了18张表按照业务域可以分成四组第一组是组织与人员域企业表、部门表、员工表、岗位表。员工表是关键它要挂企业ID、部门ID、职级职级直接决定差旅标准普通员工只能订经济舱和快捷酒店管理层可以订公务舱和四星以上。第二组是差旅管控域出差申请单表、审批规则表、审批记录表。出差申请单相当于订单的“前置条件”包含出差城市、开始日期、结束日期、事由、预计花费。审批规则表用来配置什么金额区间、什么职级需要谁审批。第三组是资源与订单域酒店表、房型表、班次表、订单主表、订单明细表。这里有个很关键的设计订单主表要带一个order_type字段区分是酒店订单还是交通订单同时要有一个unique_key字段做幂等控制防止用户双击导致重复下单。第四组是结算与财务域企业账户表、流水表、月度结算单表、发票表。B2B的核心体现就在这组表企业预存一笔钱到账户员工下单时从企业账户扣款系统每晚定时任务跑月结单财务人员在后台可以按企业维度查看并核销。注意订单表设计时一定不要把酒店或班次的信息直接冗余成一大串文本。虽然这样做查询确实方便但答辩老师追问“订单和订单明细怎么拆”、你这张表为什么没有外键关系时你会解释得很吃力。规范的第三范式设计虽然多几次联表查询但逻辑上无懈可击。3. 核心业务流程拆解住、行、审批与结算3.1 交通出行流从申请到出票的四个状态交通出行的核心流程可以概括为“申请—审批—出票—可退改”。前端表现是这样的员工进入小程序先填写出差申请单选择出发城市、到达城市、出发日期、出差天数、事由系统根据员工职级自动带出差旅标准并预估本次出差的交通预算是否超标。申请单提交后进入审批环节。审批通过后员工才能在“出行预订”页面搜索班次。搜索结果的每条记录会标记“符合差旅标准”或“超出差旅标准”如果当前员工的职级标准是经济舱商务舱的班次会被置灰并提示“当前账号无权预订此舱位”。这里有个实现细节值得说差旅标准的判断一定要放在后端不能只在前端控制。虽然小程序端看起来只是隐藏了一个按钮但请求直接打到后端接口的话你不在后端校验等于没有管控。下单成功后生成交通订单订单状态从“待出票”变为“已出票”。因为不接真实票务这里我建议用一个定时任务模拟出票每30秒扫描一次状态为“待出票”的订单随机更新为“已出票”并生成一个模拟票号。这样演示的时候评审能看到订单状态自己变化比静态写死的状态更有“活”的感觉。3.2 酒店住宿流协议价与房态控制酒店的预订流程比交通要简单一点但多了一个B2B特色的逻辑协议价。协议价的含义是酒店和平台方签订协议企业员工通过平台预订时享受一个比门市价低的价格且这个价格不展示给普通C端用户。在系统里的实现方式就是在酒店表或房型表上增加一个protocol_price字段普通门市价一个字段企业协议价另一个字段。员工端看到的永远是协议价这会让员工觉得“公司给的福利还不错”而实际扣款也按协议价从企业账户划扣。房态控制这块建议在房型表上维护一个remaining_rooms字段下单时用乐观锁的方式扣减库存。具体SQL怎么写后面实操部分会提到。这里先提醒一个坑不要只在程序里先查询再更新并发场景下会超卖。毕业设计虽然不会真有高并发但答辩老师几乎必问“如果两个人同时下单最后一件房怎么办”你答出“用始Update语句做条件更新”就是加分项。3.3 B2B核心审批流与对公结算审批流和结算模块是整套系统区别于“单机版小程序”的两大支柱。审批流不建议做成复杂的流程引擎比如Flowable或者Activiti毕业设计用它们太重了配置和学习成本都极高。我倾向于用一个简洁的“角色金额阈值”方案审批规则表里维护rule_type出行or住宿、min_amount、max_amount、approve_role下单时获取订单金额和当前员工职级按规则找出对应的审批人。如果金额在2000以内直属主管审批超过2000需要部门经理审批。按金额分级的规则虽然简单但演示效果很好答辩也容易解释。结算模块的核心是一张结算单。每天晚上定时任务跑一次按企业维度汇总当月的已出票订单和已确认酒店订单生成一张月度结算单包含订单数、总金额、明细列表。企业财务在后台看到这张单子后可以进行“确认收款”或“发起开票”操作。这里可以增加一个状态字段未确认→待收款→已收款→已开票。给财务人员设计一个简洁的对账工作台把所有待确认的结算单列出来支持按企业名称搜索这些都是论文里能写、答辩时能演示的功能点。4. 从零搭建实操关键环节实现4.1 项目初始化与包结构规划后端项目用Spring Initializr生成即可Java版本选8或11Spring Boot版本选2.5.x或2.7.x千万不要选Spring Boot 3.x。原因很现实3.x基于Jakarta EE资料没有2.x多很多老教程里的依赖坐标都用不了毕业设计没必要在这个上面冒险。依赖方面只需要引入Spring Web、MyBatis-Plus、MySQL Driver、Lombok、JWT相关的jjwt、Knife4j文档工具。包结构我建议这样规划com.demo.travel ├── controller (接口层) │ ├── admin (后台管理端接口) │ └── app (小程序端接口) ├── service (业务层接口impl) ├── mapper (MyBatis-Plus的Mapper) ├── entity (实体类) ├── dto (入参/出参封装) ├── config (配置拦截器、跨域、Knife4j) ├── common (统一返回结果、异常处理、常量) └── utils (JWT工具、日期工具等)按这个结构写论文时“系统的分层架构设计”这一章直接有素材了不用现编。4.2 登录鉴权与权限控制小程序端登录走的是微信登录流程wx.login拿到code请求后端/auth/code2session接口后端拿着code调用微信的jscode2session接口换取openid和session_key。因为毕业设计没有企业认证的appid建议做一个“模拟登录开关”如果当前环境是开发者工具且没有真实appidcode2session直接根据传进来的code解析出openid如果配了真实appid就走完整流程。这个开关可以用application.yml里的一个布尔配置控制。拿到openid后查employee表找到对应的员工记录生成JWT Token返回给小程序端。之后每次请求在Header里带Authorization字段后端用一个HandlerInterceptor统一做三件事第一解析Token校验是否过期第二把当前登录用户的employeeId放进ThreadLocal第三根据接口上标注的权限注解判断用户是否有访问权限。权限这块建议自定义一个RequireRole注解参数传“ADMIN”、“APPROVER”、“EMPLOYEE”等角色标识。Controller方法上标注后在拦截器里用反射读取注解做判断。这样做的好处是权限逻辑集中在拦截器里Controller本身保持简洁论文里的“基于注解的声明式权限控制”写起来也漂亮。4.3 小程序端与服务端联调小程序端用原生框架写建议至少包含以下页面首页出差申请入口常用功能、出差申请页、出行搜索页、酒店搜索页、订单列表页、订单详情页、个人中心页。页面太多会导致工期压力大页面太少则撑不起“小程序”这个关键词上面这7个页面刚好够。联调阶段最常见的问题是域名白名单和request合法域名校验。微信开发者工具里如果你没有配置合法域名请求会被拦下来。解决办法是在开发者工具右上角“详情” - “本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。这里务必记得写在文档里因为答辩时如果换了电脑现场演示不勾这一项整个页面就是一片空白演示直接翻车。后端接口开发时一定要统一返回体。我习惯用一个Result 类结构是code200/500、message、data。小程序端也对应封装一个request.js统一处理code为200的情况和数据解析。这点非常重要如果不做每个页面的每个请求都要写一遍错误处理代码冗余严重而且答辩评委翻你代码时看到满屏重复代码印象分会受影响。4.4 模拟数据准备与演示脚本系统要演示出效果光有代码不够必须准备一套“看起来真实”的业务数据。我建议写一个DataInitializer类项目启动时自动往数据库里插入2家企业、5个部门、10名员工覆盖普通员工/主管/部门经理三个职级、8家协议酒店、20条班次数据。所有数据用中文真实风格命名比如酒店就叫“城市便捷酒店人民广场店”班次就用“MU5137 上海-北京”这样演示时截图放到论文里一看就像真系统。还有一个加分操作把演示流程写成一份脚本。开场先以管理员身份登录后台展示企业列表和差旅标准设置然后切到员工端提交一张出差申请走审批审批通过后搜索并预订航班和酒店最后切回后台找到当月的结算单展示对账数据。这个脚本总共5分钟基本覆盖了所有核心功能答辩时按脚本走节奏完全可控。5. 常见坑与排查方法实录5.1 微信登录code换session返回失败真机调试时报“jscode2session返回errcode”大概率原因是appid和secret不匹配或者你的appid不是小程序类型。排查方式很简单去微信公众平台检查账号类型个人主体只能申请个人小程序部分接口权限受限。毕业设计阶段直接用模拟登录开关跑通全流程在论文里注明“实际生产环境需替换为企业认证小程序”这个说法答辩老师一般都能接受。5.2 重复下单与幂等控制用户快速点了两次“提交订单”小程序发了两个请求后端如果只是简单地insert就会出现两笔一模一样的订单。解决思路是在订单主表加一个unique_key字段前端在提交订单时生成一个UUID后端insert前先按unique_key查一次存在则直接返回已存在的订单。更稳妥的做法是给unique_key加唯一索引数据库层面兜底。这个细节虽然小但“幂等设计”四个字写进论文里是一个实打实的技术点。5.3 金额计算出现精度问题订单金额涉及小数如果数据库字段用的double或float累计对账时会出现0.10.2不等于0.3的尴尬情况。正确做法是数据库用decimal(10,2)Java实体类用BigDecimal所有涉及金额计算的地方都用BigDecimal严禁用Double运算。另外MyBatis-Plus插入BigDecimal时要注意在实体类字段上加NumberFormat和精度相关的注解否则查询结果显示出来可能是科学计数法。5.4 联调时小程序请求报401请求带上了Token但还是返回401最常见的原因有三个Token过期、Authorization头拼写错误、拦截器里放行了OPTIONS请求但没放行业务请求。第三个原因比较隐蔽小程序发请求时如果后端配置了跨域预检请求会被拦截器拦截。解决办法是在拦截器的preHandle里加一行代码如果是OPTIONS请求直接放行。5.5 答辩时数据被清空的应急方案这是最尴尬的演示事故答辩前夜手滑把开发库的清空脚本跑了第二天打开系统全是空数据。应急方案有两个第一提前把.sql文件准备好答辩前检查一遍数据确保企业管理端能展示出内容第二写一个“一键重置演示数据”的后台按钮点了就执行DataInitializer重新灌数据这个按钮虽然在真实系统里不该存在但当演示保障工具非常好用可以在文档里说明它的用途。6. 论文与答辩讲解的几点个人体会最后分享几点我实际带项目过程中反复强调的东西。论文不要按代码顺序写要按“业务模块-技术实现-结果验证”来组织每个模块配一张流程图或时序图图片放上去后讲清楚节点之间的调用关系。答辩时被问到“系统还有什么不足”不要慌这正是展现你思考深度的机会比如坦诚地讲“当前版本的房态扣减用的是乐观锁极端高并发下会有少量超卖风险生产环境可以引入Redis预扣库存方案”这比硬着头皮说“系统非常完美”要好得多。另外有一点要提醒虽然这套系统叫“小程序”但技术栈权重里Spring Boot后端占了八成小程序只是前端表现层。论文不要花大篇幅讲WXML怎么布局重点要放在后端业务逻辑和系统设计上毕竟是Java方向的毕业设计评委主要看的是你的工程能力和业务建模能力。如果你正在做这个方向或者已经做出来了回头再看当时选“B2B商务出行”这个定位的决定应该会认同我说的那句话毕业设计的价值不在于你用了多前沿的技术而在于你真正理解了你做的那个业务并且能用系统的结构把业务串起来。把审批、结算、差标这三块设计讲透这个项目就真的立住了。