尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

停车场微信小程序毕设:SSM框架下从数据库到接口的全栈实战

发布时间:2026/9/23 18:18:51

资讯中心
01
ARTICLE

停车场微信小程序毕设:SSM框架下从数据库到接口的全栈实战

停车场微信小程序毕设:SSM框架下从数据库到接口的全栈实战
简介面向需要完成Java方向毕业设计或课程项目的学生这是一份停车场微信小程序完整源码与配套资料包。后台采用SSM框架管理端页面基于Vue小程序端结合微信小程序原生开发数据库使用MySQL兼容Eclipse、MyEclipse、STS、IDEA等主流开发工具。资源中提供管理员、车主、商家三种角色涵盖停车场信息管理、预约停车、取消预约、进场停车、商场收费、留言板等模块可支撑从需求分析到编码实践的全过程。包内共1247个文件总大小48.13MB以Vue页面、Java后台逻辑、微信小程序WXML/WXSS/JS文件为主同时包含PNG图片、SQL数据库脚本与PDF/Word论文材料另有操作录屏、环境工具包及相同框架项目的安装教程。目前已有107人浏览学习适合作为独立开发毕业设计或快速理解SSM小程序项目架构的参考资料。1. 停车场微信小程序这个毕设题目为什么值得用SSM认真做完很多人听到“停车场微信小程序”这个题目第一反应是“不就是个小程序页面吗”真做起来才发现小程序只是露在水面的一角底下是用户体系、车位状态流转、订单计费、后台管理一整套逻辑。这个选题的好处在于它同时覆盖了毕业设计最看重的三件事有完整的业务闭环、有真实的数据交互、有能拿出手的界面。技术栈上SSM框架Spring SpringMVC MyBatis是Java后端最常见的组合之一配置化程度高、资料多、出了问题也搜得到。做完它你手里拿到的不仅是一份能演示的源码包更是一条从数据库表设计到接口联调再到小程序端渲染的完整链路。接下来就按这条链路拆开讲先建模再写后端再写小程序最后是排坑和验收。2. 数据库先行停车场业务模型与五张核心表的设计2.1 角色与状态流转车主、管理员和系统先达成一致再动表停车场这个小程序里其实有三类角色。车主通过小程序查车位、绑定车牌、入场出场管理员在后台维护车位和费率、查看订单系统自身负责计时、计价和状态流转这部分是毕业设计里最好写进论文的“核心逻辑”。动手建表之前先把状态机想清楚。车位状态用0、1、2三个值表示空闲、占用、预约订单状态用0、1、2表示进行中、已完成、已取消。为什么要用整数而不是字符串一是查询快二是避免“FREE”“OCCUPIED”“occupied”这种大小写不一致的脏数据。状态流转的方向也要固定只有空闲车位能创建进行中订单只有进行中订单能结算成已完成取消操作必须发生在进行中之前或者入场后未结算时。把这张状态机画在论文里是数据库设计章节最加分的一页。费用模型也要提前定。临时车按小时计费不足一小时按一小时收月租车一次性付费入场出场不再结算。这两种车不能混在一张表里硬算不然订单表的金额字段语义会乱。建议在车位表上区分临时车位和月租车位订单表里只记本次入场出场的原始数据金额由后端计算后再落库。2.2 建表落地用户表、车位表、订单表的DDL与字段取舍数据库选MySQL 5.7以上字符集统一utf8mb4排序规则用utf8mb4_general_ci。下面这组DDL是这套项目最常见的表结构新建一个parking数据库后直接执行。用户表用来存车主和管理员车位表存车位基础信息订单表是整个系统的核心流水。CREATE TABLE t_user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, openid varchar(64) DEFAULT NULL COMMENT 微信openid未接入微信登录时为空, username varchar(50) NOT NULL COMMENT 登录名, password varchar(100) NOT NULL COMMENT 密码建议MD5或BCrypt存储, phone varchar(20) DEFAULT NULL COMMENT 手机号, user_type tinyint(4) DEFAULT 1 COMMENT 1车主 2管理员, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE t_parking_space ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 车位ID, space_no varchar(20) NOT NULL COMMENT 车位编号如A-01, floor varchar(20) DEFAULT NULL COMMENT 楼层或区域, status tinyint(4) DEFAULT 0 COMMENT 0空闲 1占用 2预约, space_type tinyint(4) DEFAULT 1 COMMENT 1临时车位 2月租车位, fee_rate decimal(6,2) DEFAULT 3.00 COMMENT 每小时费率单位元, PRIMARY KEY (id), UNIQUE KEY uk_space_no (space_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车位表; CREATE TABLE t_parking_order ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号唯一, user_id bigint(20) NOT NULL COMMENT 车主ID关联t_user.id, space_id bigint(20) NOT NULL COMMENT 车位ID关联t_parking_space.id, car_number varchar(20) NOT NULL COMMENT 车牌号冗余存储, enter_time datetime DEFAULT NULL COMMENT 入场时间, leave_time datetime DEFAULT NULL COMMENT 出场时间, duration_minutes int(11) DEFAULT 0 COMMENT 停车时长单位分钟, total_amount decimal(10,2) DEFAULT 0.00 COMMENT 应收金额, status tinyint(4) DEFAULT 0 COMMENT 0进行中 1已完成 2已取消, pay_time datetime DEFAULT NULL COMMENT 支付时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id), KEY idx_space_id (space_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT停车订单表;再补一张简单的系统配置表用来存免费分钟数、月租价格这类“经常要改但不想改代码”的参数管理员在后台改配置就能生效。另外给一张t_car车辆表支持一个用户绑定多个车牌注册时填一个默认车牌停车场入口处再手动选。字段设计上有几个容易被忽略的点。订单号用order_no而不是直接用id做主键因为对外接口不能把自增主键暴露出去且订单号有自己的生成规则。车牌号在订单表里冗余一份不要每次都去关联车辆表报表统计时少一次JOIN。金额全部用decimal不要用float或double浮点数在Java里做货币计算会出现0.10.2不等于0.3的问题这是java基础里反复强调的坑在毕设答辩现场提出来反而是加分项。2.3 计费规则前置阶梯计费在Java侧实现而不是塞给SQL计费逻辑是整个系统最容易被追问的部分。常见做法是临时车按小时向上取整计费假设免费时长15分钟超过免费时长后不足1小时按1小时算同时支持每个车位单独配置费率。这套规则不要写进SQL因为SQL难测试、难加分支比如“晚上10点后费率减半”这种规则塞进SQL会非常痛苦。放到Java工具类里用纯函数实现入参是入场时间、出场时间、费率出参是金额单元测试能覆盖所有边界。public class ParkingFeeUtil { private static final long FREE_MINUTES 15; // 免费分钟数 /** * 计算临时车停车费 * param enterTime 入场时间 * param leaveTime 出场时间 * param feeRate 每小时费率元 * return 应收金额保留两位小数 */ public static BigDecimal calcFee(Date enterTime, Date leaveTime, BigDecimal feeRate) { long diffMillis leaveTime.getTime() - enterTime.getTime(); long minutes diffMillis / (1000 * 60); if (minutes 0) { minutes 1; // 至少按1分钟计 } if (minutes FREE_MINUTES) { return new BigDecimal(0.00); } long billableMinutes minutes - FREE_MINUTES; long hours (billableMinutes 59) / 60; // 向上取整到小时 return BigDecimal.valueOf(hours).multiply(feeRate).setScale(2, RoundingMode.HALF_UP); } }这段代码里最关键的是(billableMinutes 59) / 60这个向上取整公式。Java的整数除法是向下取整想要不足一小时按一小时收就得先加59分钟再除60。如果你直接写billableMinutes / 60停了59分钟会算出0元答辩老师当场就能看出问题。免费时长的判断放在取整之前注意这个先后顺序不能反。边界情况也要想好入场时间晚于出场时间时diffMillis为负上面代码会兜底成1分钟跨天停车不影响计算因为用的是时间戳差值而不是日历字段月租车不走这个工具类直接在入场时判断车位类型月租车位不生成收费订单。这些边界在论文的“系统测试”章节里写进测试用例表比贴十页代码更有说服力。3. SSM后端工程落地从依赖清单到停车接口的全套代码3.1 四层分包与统一返回体Controller不写业务Mapper只管SQLSSM项目的分包习惯直接决定你后期改需求时的心情。常见做法是controller、service、mapper、entity四层controller只做参数接收和结果封装service写业务规则mapper只写SQL没有逻辑entity对应数据库表。很多同学图省事把业务写在Controller里写着写着就变成几百行的“神类”后来加一个需求要动三处地方。毕设框架搭好了后面填代码会顺手很多。目录结构按下面这样建包名可以换但层级不要变。com.parking ├── controller // 接收请求返回Result ├── service // 业务接口 ├── service.impl // 业务实现 ├── mapper // MyBatis的Mapper接口 ├── entity // 数据库实体类 ├── config // 配置类如WebMvcConfig、拦截器 └── common // 通用类如Result、异常处理所有接口统一返回一个Result对象这是前后端联调时最值得花时间做的一件事。前端只需要判断code是不是200不用每个接口都单独处理message和data的结构。这里给出最简单的实现public class ResultT { private Integer code; // 200成功 401未登录 500业务失败 private String msg; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.code 200; r.msg success; r.data data; return r; } public static T ResultT error(Integer code, String msg) { ResultT r new Result(); r.code code; r.msg msg; return r; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg msg; } public T getData() { return data; } public void setData(T data) { this.data data; } }这里约定code200是成功code401是未登录code500是业务失败。前端在请求封装层统一判断这两个值出现401就清理本地token并跳回登录页出现500就弹出msg提示。把code的语义定死在入口后面加新接口就不用纠结返回什么结构。另外注意Result里不要放静态工厂之外的对象构造逻辑否则序列化时机不一致容易出问题。3.2 工程骨架POM依赖、SpringMVC配置与登录拦截器SSM工程的起步成本主要在pom.xml和Spring配置上。下面这份依赖列表是跑通这套项目的最小集合Spring用5.2.xMyBatis用3.5.xJDK8完全兼容。不要一上来就上Spring Boot毕设要求SSM就老老实实把Spring配置写好这些配置本身也是论文里“系统环境搭建”章节的内容。dependencies !-- Spring核心与MVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.22.RELEASE/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version5.2.22.RELEASE/version /dependency !-- MyBatis与Spring整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.9/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency !-- MySQL驱动与连接池 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency !-- JSON序列化 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.12.4/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency /dependenciesSpringMVC配置里有两个参数要特别留意。数据源的URL必须带上useUnicodetruecharacterEncodingutf8否则中文车牌号存入数据库后取出来就是乱码如果MySQL驱动用的是8.0版本URL里还要追加serverTimezoneAsia/Shanghai并换成com.mysql.cj.jdbc.Driver5.1驱动可以不加这两个配置。连接池参数按实际场景给initialSize设为5maxActive设为20maxWait设为60000idleTimeout默认即可。别小看maxWait设太短数据库偶发慢查询时接口会批量报连接超时。bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/parking?useUnicodetrueamp;characterEncodingutf8/ property nameusername valueroot/ property namepassword valueroot/ property nameinitialSize value5/ property namemaxActive value20/ property namemaxWait value60000/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.parking.entity/ property nameconfiguration bean classorg.apache.ibatis.session.Configuration property namemapUnderscoreToCamelCase valuetrue/ /bean /property /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.parking.mapper/ /beanmapUnderscoreToCamelCase设为true之后数据库的space_no字段能自动映射到实体的spaceNo属性少写一堆resultMap。这是MyBatis最实用的开关没有之一。登录鉴权用SpringMVC拦截器实现拦截所有/api/parking/*请求从header里取token解析出userId放入request属性。毕设项目不需要引入JWT库用一个简单的签名工具类或者直接查库校验token都可以。拦截器里校验失败时直接输出JSON并返回false不再往后走public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(token); if (token null || !TokenUtil.validate(token)) { response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或登录已过期\,\data\:null}); return false; } request.setAttribute(userId, TokenUtil.getUserId(token)); return true; } }注意拦截器里手动输出的JSON字符串Content-Type必须带charsetUTF-8否则前端拿到的一串中文msg是乱码体。这一点在5.2节还会遇到一次。3.3 核心接口实现入场创建订单与出场结算释放车位入场接口是这套项目里业务最完整的接口。参数是车位ID和车牌号流程是查车位是否空闲再校验车牌是否为空然后生成订单号并插入订单最后把车位状态改成占用。这里把service实现写完整注意事务边界。Service public class ParkingServiceImpl implements ParkingService { Resource private ParkingSpaceMapper spaceMapper; Resource private ParkingOrderMapper orderMapper; Override Transactional(rollbackFor Exception.class) public ResultParkingOrder createOrder(Long userId, Long spaceId, String carNumber) { // 1. 查询车位是否存在且空闲 ParkingSpace space spaceMapper.selectById(spaceId); if (space null) { return Result.error(500, 车位不存在); } if (space.getStatus() ! 0) { return Result.error(500, 该车位已占用请选择其他车位); } // 2. 校验车牌 if (carNumber null || carNumber.trim().isEmpty()) { return Result.error(500, 请先填写车牌号); } // 3. 生成订单号并插入订单 ParkingOrder order new ParkingOrder(); order.setOrderNo(PK System.currentTimeMillis()); order.setUserId(userId); order.setCarNumber(carNumber.trim()); order.setSpaceId(spaceId); order.setEnterTime(new Date()); order.setStatus(0); orderMapper.insert(order); // 4. 车位改为占用 space.setStatus(1); spaceMapper.updateById(space); return Result.success(order); } }这段代码里最值得讲的是事务的边界问题。Transactional只有在方法抛出RuntimeException时才会回滚而上面这种return Result.error(...)的写法在Spring看来是“正常返回”事务会照常提交。这版实现好在所有校验都发生在写入之前返回error时数据库没有发生任何变更所以不会产生脏数据。但如果你在方法中间先执行了update后面又因为某个业务判断 return error前面的更新就已经提交了不会回滚。这是踩坑记录里必须写的一条。出场结算接口的流程正好相反查订单状态计算停车时长和金额更新订单信息释放车位。两个写操作之间有先后关系同样要用事务包起来。Override Transactional(rollbackFor Exception.class) public ResultPayResult settleOrder(Long orderId) { ParkingOrder order orderMapper.selectById(orderId); if (order null || order.getStatus() ! 0) { return Result.error(500, 订单状态异常无法结算); } Date now new Date(); // 计算时长至少1分钟 long diffMinutes (now.getTime() - order.getEnterTime().getTime()) / (1000 * 60); int minutes (int) Math.max(diffMinutes, 1); // 查车位费率月租车位费率为0 ParkingSpace space spaceMapper.selectById(order.getSpaceId()); BigDecimal feeRate space.getSpaceType() 2 ? new BigDecimal(0.00) : space.getFeeRate(); BigDecimal amount ParkingFeeUtil.calcFee(order.getEnterTime(), now, feeRate); order.setLeaveTime(now); order.setDurationMinutes(minutes); order.setTotalAmount(amount); order.setStatus(1); order.setPayTime(now); orderMapper.updateById(order); // 释放车位 space.setStatus(0); spaceMapper.updateById(space); PayResult result new PayResult(); result.setOrderNo(order.getOrderNo()); result.setDurationMinutes(minutes); result.setAmount(amount); return Result.success(result); }出场接口里把月租车费率直接置为0是比前端判断“这辆车是不是月租车”更稳妥的方案费率规则收敛在后端前端永远只拿最终金额。PayResult是独立返回对象不要直接把订单实体丢给前端订单表里userId、spaceId这些字段没必要暴露。Controller层只做参数提取和调用service代码两三行就够了。4. 微信小程序端页面结构、请求封装与联调三个关键环节4.1 页面目录与tabBar一个毕设项目该有的页面划分小程序端用原生开发不用uni-app理由是毕设要求里通常写着“微信原生小程序”而且原生开发调试起来问题面更小真机预览、开发者工具报错都能直接定位。页面划分建议三主三副tabBar放首页、订单、我的另外单独放登录页和停车详情页。{ pages: [ pages/index/index, pages/order/order, pages/mine/mine, pages/login/login, pages/parking/detail ], window: { navigationBarTitleText: 智慧停车场, navigationBarBackgroundColor: #2d8cf0, navigationBarTextStyle: white, backgroundColor: #f5f6f8 }, tabBar: { color: #999999, selectedColor: #2d8cf0, list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/order/order, text: 订单 }, { pagePath: pages/mine/mine, text: 我的 } ] } }tabBar最少2个、最多5个tabiconPath不是必填项没有图标也能编译通过但加上图标会更完整。注意tabBar里的页面跳转用wx.switchTab不能像跳普通页面那样用wx.navigateTo。停车详情页不放tabBar因为它是从首页点车位入场后带参数跳转的详情页需要接收orderId。导航栏标题颜色和自己的tabBar选中色保持一致界面上会更像一个完整作品。4.2 封装request把token、错误提示和登录跳转收口到一处小程序的wx.request是回调风格每个页面都写success回调会非常痛苦。把请求封装成一个Promise风格的独立模块统一处理BASE_URL、请求头、token注入、401跳转和错误提示这是联调阶段效率提升最大的一件事。下面这段直接放在utils/request.js里const BASE_URL http://localhost:8080; function request(method, url, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data || {}, header: { Content-Type: application/json, token: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200 res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { wx.removeStorageSync(token); wx.reLaunch({ url: /pages/login/login }); reject(res.data); } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(res.data); } }, fail() { wx.showToast({ title: 网络异常请检查后端服务是否启动, icon: none }); reject(); } }); }); } module.exports { get: (url) request(GET, url), post: (url, data) request(POST, url, data) };BASE_URL里有个关键细节在微信开发者工具里localhost指向的是电脑本机后端在电脑上跑时没问题但换成真机预览手机上的localhost指向手机自己必须把BASE_URL改成电脑在局域网里的IP比如http://192.168.1.100:8080。同时后端Tomcat的端口要放行防火墙里加一条入站规则。这个坑几乎每个做小程序联调的人都会踩一次。请求封装里还藏着一个设计选择401时统一清理token并跳转登录页但跳转用wx.reLaunch而不是wx.navigateTo因为reLaunch会清空页面栈避免用户按返回键又回到需要登录的旧页面。注意如果登录接口本身返回401就会在这里触发死循环跳转。解决方法是拦截器放行/api/user/**或者在前端跳转前判断当前页面是不是登录页。4.3 停车主流程从登录选车位到倒计时结算的页面逻辑小程序端的主流程是“登录-选车位-入场-停车中倒计时-结算”。登录页建议做成账号密码模式预留微信code登录入口。做毕设时通常还没有正式的小程序AppId和AppSecret微信登录的code2session流程在未认证的小程序上跑不完整账号密码登录能保证整个业务链路先通起来。const request require(../../utils/request.js); Page({ data: { username: , password: }, onLogin() { const { username, password } this.data; if (!username || !password) { wx.showToast({ title: 请输入账号和密码, icon: none }); return; } request.post(/api/user/login, { username, password }).then(data { wx.setStorageSync(token, data.token); wx.setStorageSync(userInfo, data.userInfo); wx.reLaunch({ url: /pages/index/index }); }); } });登录成功后把token和用户信息写进storage后续所有请求由request.js自动带上token。首页加载车位列表时后端返回车位数组前端在JS里把status翻译成可读文案再渲染到页面。onLoad() { this.loadSpaces(); }, loadSpaces() { request.get(/api/parking/spaces).then(list { const spaces list.map(item ({ id: item.id, spaceNo: item.spaceNo, statusText: item.status 0 ? 空闲 : item.status 1 ? 占用 : 预约, isFree: item.status 0 })); this.setData({ spaces }); }); }, onChooseSpace(e) { const space e.currentTarget.dataset; if (!space.isFree) { wx.showToast({ title: 该车位不可用, icon: none }); return; } // 调入场接口成功后跳转到停车详情页 request.post(/api/parking/enter, { spaceId: space.id, carNumber: wx.getStorageSync(userInfo).carNumber || }).then(order { wx.redirectTo({ url: /pages/parking/detail?orderId${order.id} }); }); }注意红框里的wx.redirectTo会把当前页面从栈里关掉用户从详情页点返回会直接回首页而不是回到车位列表再点一次入场。停车详情页用setInterval每秒刷新倒计时时间计算来自enterTime和后端返回的orderIdonLoad(options) { this.setData({ orderId: options.orderId }); this.loadOrder(); this.timer setInterval(() this.refreshDuration(), 1000); }, refreshDuration() { if (!this.data.enterTime) return; // iOS不支持 new Date(2025-01-01 12:00:00)必须把横杠替换成斜杠 const enter new Date(this.data.enterTime.replace(/-/g, /)); const diff Math.floor((Date.now() - enter.getTime()) / 1000); const h String(Math.floor(diff / 3600)).padStart(2, 0); const m String(Math.floor((diff % 3600) / 60)).padStart(2, 0); const s String(diff % 60).padStart(2, 0); this.setData({ durationText: ${h}:${m}:${s} }); }, onUnload() { if (this.timer) clearInterval(this.timer); }那段replace(/-/g, /)不是玄学是iOS上new Date(2025-01-01 12:00:00)会返回Invalid Date而安卓上正常。这是小程序端最常见的跨端时间解析差异。倒计时定时器一定要在onUnload里清掉否则页面关闭后定时器还在跑控制台会报无页面容器的警告严重时影响后续页面性能。5. 毕设排坑笔记从启动报错到演示翻车的六个高频点5.1 域名与端口为什么小程序连不上localhost现象是开发者工具里打开页面所有接口都报request:fail请求根本打不到后端后台Tomcat日志里一条请求记录都没有。原因分两层。第一层微信开发者工具的“详情-本地设置”里默认勾选了“校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”没有这个勾选小程序只能访问https://域名http://localhost:8080直接拦截掉了。第二层后端启动后监听的是8080端口但请求封装里BASE_URL写错了或者接口路径大小写对不上。解决方法是先勾选“不校验合法域名”再把BASE_URL和Controller的RequestMapping路径逐一核对。第一个接口用浏览器直接访问http://localhost:8080/api/parking/spaces验证后端通不通缩小排查范围。真机调试时BASE_URL换成本机局域网IP并且确保手机和电脑在同一个WiFi下。5.2 中文乱码Tomcat、过滤器、JDBC三处编码要一致现象是前端传过来的车牌号“苏A12345”入库后变成“??A12345”或者后端返回的msg在页面上显示为乱码。原因是一条中文数据的编码链条上任何一环不一致都会出问题。Tomcat接收POST请求默认用ISO-8859-1解码SpringMVC的CharacterEncodingFilter没配置时不会强制改UTF-8JDBC连接URL里没有characterEncodingutf8MySQL连接层面又错一次。解决分三步走。web.xml里配置CharacterEncodingFilter并设置encodingUTF-8、forceEncodingtrue数据源URL追加useUnicodetruecharacterEncodingutf8最后检查数据库表字符集是不是utf8mb4。排查时后端的响应头里看Content-Type是否带charset前端请求头里看Content-Type: application/json给拦截器手动输出JSON时同样要在writer之前设置UTF-8。5.3 MyBatis报错为什么同一条SQL突然说重复绑定现象是项目启动时报Mapped Statements collection already contains value for com.parking.mapper.ParkingSpaceMapper.selectById或者运行时第一次调用接口正常第二次直接抛异常。原因是同一个selectById的statementId被绑定了两次。最常见的场景是mapper目录下存在两份同名xml比如ParkingSpaceMapper.xml和ParkingSpaceMapper.xml.bakMyBatis扫描时把备份文件也当成mapper加载了。另一个原因是IDEA的target目录里残留了旧编译文件改了xml后没有clean旧的class和xml还在。解决时先在项目里全局搜索这个statementId找到所有定义它的文件删掉备份文件然后用mvn clean配合IDEA的 Build-Rebuild Project 强制清掉target目录。排查时看mybatis-spring配置项mapperLocations如果写了classpath:mapper/*.xml那mapper目录下所有xml都会被扫描不要放无关文件。5.4 日期返回1970LocalDateTime序列化到底缺了什么现象是接口返回的enterTime是1970-01-01 08:00:00而不是真实的入场时间。原因是在entity里用了LocalDateTime而SpringMVC默认的Jackson序列化工具不认识JDK8时间类型。Jackson老版本会把LocalDateTime当成没有时间字段的对象序列化结果要么是数组要么是时间戳配置不完整时直接显示成epoch时间的初始值。解决时引入jackson-datatype-jsr310依赖然后在ObjectMapper上注册JavaTimeModule并把默认日期格式设为yyyy-MM-dd HH:mm:ss。DAO层用好mapUnderscoreToCamelCase之后数据库datetime到LocalDateTime的转换由MyBatis自动处理。如果嫌麻烦entity直接改用java.util.Date也能规避这个问题但论文里解释起来不如注册JavaTimeModule有深度。5.5 token失效的跳转死循环封装层与应用层的边界现象是登录接口调用一次页面在登录页和小程序首页之间反复跳转或者登录成功了还被强制踢回登录页。原因是request.js里401时执行wx.reLaunch跳登录页但登录接口本身也带了token请求头后端拦截器没放行/api/user/**导致登录请求返回401前端又在成功后继续跳首页两边无限互相踢。解决方法是拦截器里明确放行登录相关路径在WebMvcConfig里配置排除项。另一个更隐蔽的来源是发起登录请求时用了wx.getStorageSync(token)而上次登出的token还在storage里后端校验旧token失败返回401。这里养成习惯登出时清空token401跳转前也先remove防止旧token被二次使用。后面遇到的坑越来越少时你会感激自己把封装层和应用层边界划清楚了。6. 答辩与验收一张自测清单和一套演示顺序让分数落地6.1 验收清单按主流程把六项功能过一遍答辩前一晚不要背稿子把下面这张验收清单在真实环境里过三遍比任何预演都有效。序号验收点操作路径预期结果1注册登录小程序端注册新账号并登录token写入storage进入首页2车位列表加载首页下拉刷新空闲、占用、预约状态正确显示3入场点空闲车位填写车牌生成订单车位变占用跳转详情页4停车中倒计时详情页停留一分钟倒计时每秒递增格式为00:00:005出场结算点“结束停车”金额正确车位释放生成订单记录6管理员后台修改车位费率后重新入场新费率生效旧订单金额不受影响每一条都要记录实际结果和截图。答辩时展示这六张截图比嘴上说“功能都实现了”有说服力得多。管理员后台如果是用网页实现的就开两个窗口左侧后端管理页右侧小程序模拟器两个窗口联动演示同一笔订单的数据变化。6.2 演示数据的准备与答辩节奏从登录开始到订单详情收尾演示数据要提前半个小时准备好不要现场造数据。最稳妥的组合是一个管理员账号、一个临时车账号、一个月租车账号车位表里预留3个空闲、1个占用、1个预约状态再预置一笔昨天的已支付订单。演示顺序从注册开始先走一遍停车主流程让评委看到小程序界面然后切到后台管理系统展示订单查询和费率修改改完费率立刻在小程序端验证新费率的计算这一步能体现前后端联调的真实性。答辩时讲到计费逻辑直接把ParkingFeeUtil的向上取整公式讲清楚说明为什么用Java而不是SQL实现讲到事务就提Transactional的回滚边界把你实际遇到过的问题和解决方案说出来。最后收尾时留一句给评委印象的话以后哪怕不做毕设了这套“先建模、再后端、再前端、最后排坑”的顺序也值得在泛目录下复现一次。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。