多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

智慧乡村旅游小程序毕业设计:SSM+MySQL实现与避坑指南

智慧乡村旅游小程序毕业设计:SSM+MySQL实现与避坑指南 简介基于微信小程序与SSM框架的智慧乡村旅游服务平台毕业设计资源面向计算机相关专业学生及需要快速搭建同类项目的开发者提供了一套完整可运行的工程方案。资源包共895个文件约40.48MB涵盖Java后端代码、Vue前端组件、微信小程序页面、SQL数据库脚本、设计图标与界面截图以及安装运行批处理脚本可完整支撑从环境搭建到部署演示的全过程。系统设计了管理员、用户、商家三类角色实现了旅游景点管理、路线规划、购物车、订单处理、收藏与充值等业务模块具有较强的实用性。目前已有313人学习下载。资源同时包含毕业论文与视频演示论文涉及可行性分析、系统设计及数据库设计等关键章节便于理解开发思路和撰写毕业设计文档视频演示则可直观查看功能运行效果适合毕业设计参考与二次开发。1. 智慧乡村旅游小程序毕业设计这套SSMMySQL方案到底帮你交付什么毕业设计拿到一套小程序源码最慌的不是看不懂代码而是不知道从哪一步下手。智慧乡村旅游服务平台就是这样一套典型的微信小程序项目实例小程序端面向游客做景点浏览、民宿预订、路线推荐后端用 SSMSpringSpringMVCMyBatis提供接口MySQL 负责把用户、订单、景点、评论这些数据落库交付物里还带着数据库脚本、毕业论文和视频演示。这套方案解决的核心问题是用一套成熟的 Java 课程设计案例源码把乡村旅游预订闭环跑通。它适合正在做毕业设计的学生也适合想第一次完整接触小程序前后端联调的开发者。你要做的不是读懂每一行代码而是先弄清楚它怎么启动、怎么联调、怎么改成自己的题目。2. 数据库设计与 SSM 后端搭建九张表和一个接口的最小实现2.1 数据库设计九张表怎么串起乡村旅游业务拿到源码包后别急着启动项目先打开数据库脚本。这套项目用的是 MySQL脚本文件一般叫 rural.sql。用 Navicat 或 mysql workbench 导入后我会先数一遍表用户表、景点表、民宿表、路线表、订单表、评论表、收藏表、轮播图表、管理员表一共九张。业务关系不复杂用户对景点和民宿产生收藏、评论、订单三类行为后台管理员维护内容。用户表的设计有个细节小程序的用户不是靠用户名密码而是靠微信登录所以用户表里要有一列 openid。管理员不走小程序靠 username 和 password 登后台。两张核心表的建表语句如下-- 用户表小程序用户与后台管理员共用 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, openid VARCHAR(64) DEFAULT NULL COMMENT 微信唯一标识小程序登录用, username VARCHAR(50) DEFAULT NULL COMMENT 用户名管理员登录用, password VARCHAR(64) DEFAULT NULL COMMENT 密码存MD5值, nickname VARCHAR(50) DEFAULT NULL COMMENT 小程序端展示昵称, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像URL, phone VARCHAR(11) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 订单表订单业务流转的核心 CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单编号格式如R20240601120001, user_id INT NOT NULL COMMENT 下单用户ID, house_id INT DEFAULT NULL COMMENT 民宿ID下单类型是民宿时有值, scenic_id INT DEFAULT NULL COMMENT 景点ID, order_type TINYINT DEFAULT 1 COMMENT 1民宿 2门票 3路线, status TINYINT DEFAULT 0 COMMENT 订单状态0待支付 1已支付 2已取消 3已完成, amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 实付金额, pay_time DATETIME DEFAULT NULL COMMENT 支付时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;这两张表有一个共同点都用 InnoDB 引擎、utf8mb4 字符集。InnoDB 是为了订单表的事务utf8mb4 是为了能存用户昵称里的 emoji。为什么订单表单独设计 order_no 而不是直接用自增 id因为订单编号要展示给用户自增 id 太容易暴露业务量而且订单号要有可读性。我习惯用“业务字母日期流水号”的格式。参数说明openid 的 UNIQUE KEY 保证同一个微信号只能注册一次amount 用 DECIMAL 不用 FLOAT金额计算不能有浮点误差status 给默认值 0这样插入订单时不用显式传状态。如果你在建表时发现字段和我写的不一致以你拿到的 rural.sql 为准表结构没有标准答案只要业务闭环完整就行。2.2 Maven 工程与 SSM 装配pom 依赖和两份 Spring 配置这套项目是传统 SSM 结构不是 SpringBoot。不少读者会问现在都用 SpringBoot 了为什么还要 SSM两个原因一是不少课程大纲还沿 SSM 在讲答辩时老师问的也是这套二是 SSM 手写配置能把 Spring、SpringMVC、MyBatis 三者的协作关系讲清楚。如果你拿到的是 SpringBoot 改写版原理一样只是配置被自动装配替代了。先看 pom.xml。Maven 会把依赖管理好我一般会确认这几个关键依赖存在!-- pom.xml 关键依赖 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.2.15.RELEASE/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.15.RELEASE/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.6/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.26/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.9.10/version /dependency版本号不是死的。Spring 5.2.x 和 MySQL 8.0.x 驱动是目前 SSM 项目里兼容性较好的组合如果你本地 JDK 是 1.8就别硬上 Spring 6那需要 JDK 17。Druid 连接池的作用是管理数据库连接避免每次请求都新建连接initialSize 和 maxActive 这两个参数分别控制初始连接数和最大连接数毕设项目给 5 和 20 足够。然后是 SSM 里最容易配错的 spring-mybatis.xml。它负责三件事数据源、SqlSessionFactory、Mapper 扫描!-- spring-mybatis.xml 核心片段 -- context:property-placeholder locationclasspath:db.properties / bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver} / property nameurl value${jdbc.url} / property nameusername value${jdbc.username} / property namepassword value${jdbc.password} / /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource / property namemapperLocations valueclasspath:mapper/*.xml / property nametypeAliasesPackage valuecom.rural.entity / /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.rural.dao / /beandb.properties 里 jdbc.driver 要注意 MySQL 版本5.x 用 com.mysql.jdbc.Driver8.x 用 com.mysql.cj.jdbc.Driver。jdbc.url 里我习惯带上 useSSLfalse、serverTimezoneAsia/Shanghai、characterEncodingutf8 三个参数后面两个对 MySQL 8 是必填否则连库时报时区错误中文也可能乱码。上面的配置里mapperLocations 指向 classpath:mapper/*.xml意思是 MyBatis 的 SQL 语句写在 resources/mapper 目录下typeAliasesPackage 简化实体类包名这样在 XML 里写 resultTypeUser 而不是全限定名。MapperScannerConfigurer 自动扫描 com.rural.dao 包把接口和 XML 动态代理成 Bean。这一套如果配错最常见的报错就是 mapper 绑定异常后面避坑章会单独说。2.3 从 DAO 到 Controller一个景点列表接口的最小链路SSM 项目的接口链路是固定的Controller 接收参数→Service 处理业务→Mapper 查库→XML 写 SQL→JSON 返回。我建议先拿景点列表接口练手因为它只读不写最适合验证 SSM 是否装配成功。// ScenicController.java RestController RequestMapping(/api/scenic) public class ScenicController { Autowired private ScenicService scenicService; GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) String keyword) { PageHelper.startPage(page, size); ListScenic list scenicService.queryList(keyword); PageInfoScenic pageInfo new PageInfo(list); return Result.ok(pageInfo); } }几个参数要说明一下。page 是页码size 是每页条数keyword 是可选的关键词前端首页传“热门”这个关键词就能拿到热门景点排序。PageHelper.startPage 是 MyBatis 的分页插件它会在执行下一条查询 SQL 时自动拼接 limit不需要手写分页。pom.xml 里需要额外引入 pagehelper 依赖。Result 是统一的返回结构我一般设计成 {code:0, msg:success, data:...} 三段式code 非 0 表示业务错误小程序端根据 code 判断是否弹提示。Service 层和 Mapper 层不复杂Service 里做空值判断Mapper 接口定义 list 方法对应的 XML 里写 SQL!-- ScenicMapper.xml -- mapper namespacecom.rural.dao.ScenicMapper select idlist resultTypeScenic parameterTypemap SELECT id, name, level, address, price, image, description FROM t_scenic where if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY hot DESC, id DESC /select /mapper这个 XML 里有三个常见写法namespace 必须是 Mapper 接口的全限定名id 必须和接口方法名一致resultType 指向 Scenic 实体。这里用 where 标签而不是直接在 SQL 后面写 where 11是为了让动态 SQL 更干净。如果你改动后启动报绑定异常先查这三处是否对得上。3. 小程序端对接后端请求封装、登录态与页面渲染3.1 微信小程序请求封装BASE_URL、token 和缓存时间设置原生小程序没有 axios所有网络请求都走 wx.request。项目里一般会在 utils 目录下建一个 request.js 做统一封装这样页面里不用重复写 url 前缀、请求头和错误处理。// utils/request.js const BASE_URL http://192.168.1.100:8080/rural; // 联调地址真机要写局域网IP function request(url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); // GET请求做缓存5分钟有效减少重复请求 if (method GET) { wx.setStorage({ key: url, data: { value: res.data.data, expire: Date.now() 5 * 60 * 1000 } }); } } else if (res.statusCode 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); reject(new Error(登录已过期请重新登录)); } else { reject(new Error(res.data.msg || 请求失败)); } }, fail(err) { reject(err); } }); }); } module.exports { request, BASE_URL };这里的 BASE_URL 是后端 Tomcat 的访问地址端口 8080/rural 是项目上下文路径。本地联调时有个容易忽略的点真机上不能写 localhost 或 127.0.0.1那指向的是手机自己要改成电脑在局域网里的 IP。缓存时间设置这个小功能不算必须但加了之后用户反复进首页会明显感觉快答辩时能说成性能优化点。注意真机预览时BASE_URL 不能写 localhost要写成电脑的局域网 IP手机和电脑必须处于同一个 Wi-Fi。封装之后页面里调用就是一行 Promiseconst { request } require(../../utils/request); Page({ async loadScenics() { try { const data await request(/scenic/list?page1size10, GET); this.setData({ scenics: data.list }); } catch (e) { wx.showToast({ title: e.message, icon: none }); } } });3.2 登录鉴权wx.login 换 token后端如何识别用户小程序登录和网页不同用户没有账号密码输入环节。标准的流程是wx.login 拿到临时 code把 code 发给后端后端拿 code 去微信接口换 openid后端生成 token 返给前端前端把 token 存到 storage以后每次请求都带上。// pages/login/login.js const { request } require(../../utils/request); Page({ onLoad() { wx.login({ success: async (res) { const code res.code; try { const data await request(/auth/login, POST, { code: code }); wx.setStorageSync(token, data.token); wx.setStorageSync(userId, data.userId); wx.switchTab({ url: /pages/index/index }); } catch (e) { wx.showToast({ title: 登录失败, icon: none }); } } }); } });wx.login 的 code 有效期很短几分钟就会失效所以拿到 code 要立即发给后端。后端 AuthController 的处理思路是// AuthController.java 登录接口核心逻辑 PostMapping(/auth/login) public Result login(RequestBody MapString, String body) { String code body.get(code); // 用code换openid这里封装了微信接口调用 String openid wxService.code2Session(code); User user userService.findByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setNickname(微信用户 openid.substring(0, 8)); userService.add(user); } // 生成token并缓存后续接口通过token识别用户 String token UUID.randomUUID().toString().replace(-, ); tokenService.cache(token, user.getId()); return Result.ok(new LoginVO(token, user.getId(), user.getNickname())); }这个接口做了三件事换 openid、查用户、不存在则注册。openid 是微信给每个用户在单个小程序下的唯一标识同一个用户在你的小程序里 openid 永远不变但换成另一个小程序 openid 就不同所以不能拿 openid 当跨应用的全局 ID。token 我习惯用 UUID简单够用生产环境应该换 JWT 并控制过期时间毕设用 UUID 存内存或 Redis 表都行。3.3 首页渲染轮播图、热门景点和导航栏适配首页是用户打开小程序看到的第一屏数据来源是轮播图和热门景点两个接口。页面结构上swiper 组件负责轮播普通 view 配合 wx:for 渲染景点卡片。// pages/index/index.js const { request } require(../../utils/request); Page({ data: { banners: [], hotScenics: [], navBarHeight: 64 // 自定义导航栏高度默认64px }, async onLoad() { this.initNavBar(); this.loadBanners(); this.loadHotScenics(); }, initNavBar() { // 动态获取顶部状态栏和胶囊按钮高度适配不同机型 const menu wx.getMenuButtonBoundingClientRect(); this.setData({ navBarHeight: menu.top menu.height 4 }); }, async loadBanners() { const banners await request(/banner/list?typehome, GET); this.setData({ banners: banners }); }, async loadHotScenics() { const data await request(/scenic/list?sorthotlimit6, GET); this.setData({ hotScenics: data.list }); } });轮播图 banner 是后台管理端配置的小程序端只负责展示。这里有一个常见问题开发者工具里图片能看到真机上不显示。原因是轮播图 URL 必须能通过域名校验http 图片在小程序里会被拦。如果你用的是 http 图片要么换成 https要么在开发者工具里勾选不校验合法域名但真机预览依然不行。如果读者更熟悉 uniapp可以把小程序端用 uniapp 重写跨平台性更好但毕业设计模板大多是原生小程序改成 uniapp 要动页面所有标签工作量不划算。这个选择留到论文的创新点部分作为展望写一句即可。导航栏高度这个话题用 wx.getMenuButtonBoundingClientRect() 取胶囊按钮位置是自定义导航栏的标准做法不同机型差异明显不能写死。4. 订单与后台管理把业务流程跑通的完整闭环4.1 民宿预订与订单状态机订单是这类平台的核心。订单表里 status 字段我定了四个值0 待支付、1 已支付、2 已取消、3 已完成。状态只能单向流转已支付不能回到待支付已完成不能取消。这个规则要同时落到前端按钮和后台 Service 两个地方。// OrderService.java 订单状态更新核心方法 public boolean updateStatus(Integer orderId, Integer fromStatus, Integer toStatus) { // 更新时带上原状态条件防止并发下重复操作 int rows orderMapper.updateStatus(orderId, fromStatus, toStatus); return rows 1; }这段代码的关键是 update 语句里带了 fromStatus 条件!-- OrderMapper.xml -- update idupdateStatus UPDATE t_order SET status #{toStatus} WHERE id #{orderId} AND status #{fromStatus} /update为什么必须带 fromStatus因为用户在待支付页面点了取消同时后台管理员也在操作这条订单如果两边都不带状态条件后执行的会把先执行的结果覆盖掉。带条件之后只有一条 update 语法生效返回 1另一条返回 0从语法层面杜绝了并发脏数据。这个方法虽然只有几行却是我在答辩时最喜欢讲的点因为它能体现对并发控制的理解。4.2 后台管理端SSM 项目里的 JSP 管理页面很多毕业设计的小程序后台不是另一个小程序而是 SSM 框架自带的 JSP 页面跑在 Tomcat 的 /admin 路径下。管理员登录后可以管理景点、民宿、订单、轮播图。JSP 页面用 JSTL 标签循环渲染数据配合 PageInfo 做分页按钮。!-- admin/scenic_list.jsp 景点管理列表核心片段 -- % page contentTypetext/html;charsetUTF-8 languagejava % table classtable table-bordered thead tr thID/thth景点名称/thth等级/thth价格/thth状态/thth操作/th /tr /thead tbody c:forEach items${page.list} vars tr td${s.id}/td td${s.name}/td td${s.level}/td td${s.price}/td td${s.status 1 ? 上架 : 下架}/td td a href${ctx}/admin/scenic/edit?id${s.id}编辑/a a href${ctx}/admin/scenic/delete?id${s.id} onclickreturn confirm(确认删除?)删除/a /td /tr /c:forEach /tbody /tableJSP 这边的坑集中在两点。第一admin 路径下所有页面都要做登录校验常见做法是一个 HandlerInterceptor 拦截器判断 session 里有没有 admin 用户没有就重定向到登录页。第二JSP 里的 EL 表达式 ${page.list} 依赖后端 ModelAndView 里 put 的值如果你改了 Controller 方法的返回值类型这里也要同步改。上架状态在新增编辑页面一般用单选框 radio 绑定 status 字段注意回显时 checked 的判断逻辑。一个经常被忽略的细节是后台管理端的静态资源CSS 和 JS 如果加载不出来通常是 web.xml 里 DispatcherServlet 的映射把静态文件拦截了这个在避坑章会展开讲。4.3 模拟支付没有商户号时怎么跑通订单闭环毕业设计拿不到微信支付商户号这是常态。项目里常见做法是写一个 mockPay 接口前端在支付确认页直接调这个接口把订单状态从待支付置为已支付并记录支付时间。论文里要如实写这是模拟支付不要写真实接入了微信支付答辩时老师问支付逻辑要能说清楚。// OrderController.java 模拟支付接口 PostMapping(/order/mockPay) public Result mockPay(RequestParam Integer orderId, RequestParam Integer userId) { Order order orderMapper.findById(orderId); // 1. 订单必须存在且属于当前用户 if (order null || !order.getUserId().equals(userId)) { throw new BizException(订单不存在或无权操作); } // 2. 只有待支付状态才能支付 if (order.getStatus() ! 0) { throw new BizException(当前订单状态不可支付); } // 3. 更新为已支付并写支付时间 orderMapper.updateStatus(orderId, 0, 1); orderMapper.updatePayTime(orderId, new Date()); return Result.ok(模拟支付成功); }模拟支付接口的价值在于把整条业务链跑通用户下单选民宿生成待支付订单调 mockPay 变成已支付然后可以继续做评价、确认入住、完成订单。这里三个校验顺序不要乱先校验订单归属再校验状态最后执行更新。如果把状态校验放最后用户传一个不属于自己的订单号也能触发状态变更这是真实项目里必须避免的逻辑漏洞。5. 本地联调避坑手册SSM微信小程序最常见的几个坑5.1 真机请求不到接口合法域名和设备地址现象开发者工具里接口正常返回点击预览后用手机打开页面空白控制台报 wx.request:fail。原因有三个方向。第一小程序真机请求必须走校验通过的域名http 接口在真机上会被拦第二BASE_URL 写的是 localhost手机访问的是自己不是电脑第三电脑防火墙把 8080 端口拦了。解决开发阶段在微信开发者工具的“详情→本地设置”里勾选“不校验合法域名”这只是临时方案BASE_URL 改成电脑局域网 IP比如 192.168.x.x手机和电脑要连同一个 Wi-Fi防火墙入站规则放行 8080 端口。如果条件允许最稳妥是部署到有域名的服务器上但毕业设计本地演示往往没这个条件所以局域网 IP 是主要方案。5.2 MySQL 连接失败驱动、时区与 socket 路径现象Tomcat 启动时报 Cannot create PoolableConnectionFactory或 Access denied。本地命令行连接时报 error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。原因MySQL 服务没启动连接的是 socket 而不是 TCP 端口驱动类和 MySQL 版本不匹配密码错误MySQL 8 时区参数缺失。解决先确认 MySQL 装好并能启动。用 mysql -uroot -p 试一下报 2002 错误就说明服务没起来Linux 下 service mysqld startWindows 下到服务管理器启动。驱动方面MySQL 5.x 用 com.mysql.jdbc.Driver8.x 用 com.mysql.cj.jdbc.Driver别混。url 里的 serverTimezoneAsia/Shanghai 和 useSSLfalse 对 MySQL 8 是必填项。选版本时如果自己装的是 MySQL 8.0就用 mysql-connector-java 8.0.x按照 mysql 官方安装教程配置即可。5.3 mapper 绑定异常namespace、id 和路径现象项目启动后调用接口后台报 Invalid bound statement (not found): com.rural.dao.ScenicMapper.list。原因三处不一致。Mapper 接口的全限定名和 XML 里的 namespace 不一致接口方法名和 XML 里的 id 不一致spring-mybatis.xml 里 mapperLocations 没扫到 XML 文件。解决按顺序排查。先看 ScenicMapper.java 第一行 package 是不是 com.rural.dao再看 ScenicMapper.xml 里 namespace 是不是 com.rural.dao.ScenicMapper。确认 id 和方法名一致。前两项都对但还报错多数是 Maven 没把 src/main/java 下的 XML 打进 target 目录pom.xml 里在 build 节点加 resources把 *.xml 和 *.properties 都包含进去。这个坑在 SSM 项目里出现频率最高也是新手最容易卡住的地方。5.4 中文乱码连接、表结构和 Tomcat 三层现象小程序端景点名称显示一堆问号后台管理端中文正常但存进数据库变成乱码或者反过来。原因三层都有可能。第一层是连接 url 缺少 characterEncodingutf8中文在传输过程中被转成 ISO-8859-1第二层是表和库本身是 latin1 字符集存不了中文第三层是 Tomcat 接收 POST 参数时 URIEncoding 不对。解决连接 url 在 db.properties 里改成 jdbc:mysql://localhost:3306/rural?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8。数据库表统一用 utf8mb4和 2.1 节建表 SQL 保持一致。Tomcat 的 server.xml 里 Connector 标签加 URIEncodingUTF-8。改完这三处重启不要只改一处否则查来查去都是白费时间。5.5 后台 CSS 样式全丢DispatcherServlet 拦截静态资源现象登录后台管理端页面能打开但 CSS 和 JS 全加载不出来浏览器 F12 看到一堆 404。原因web.xml 把 DispatcherServlet 映射到 / 或者 /*所有请求都交给 SpringMVC 处理/admin/css/style.css 这种静态资源也被当成 Controller 路径去找了找不到就 404。解决三个方案选一个。web.xml 里把 DispatcherServlet 映射改成 *.do或者在 spring-mvc.xml 里加 mvc:default-servlet-handler 让 Tomcat 默认 Servlet 处理静态资源或者用 mvc:resources 单独放行。毕设项目里改动最小的是方案二加一行配置就能解决。这五个坑是我在做这类项目时按出现频率排出来的。其中 mapper 绑定异常和中文乱码是 SSM 老项目的高发区小程序真机请求是微信开发者特有的坑。遇到问题时别急着改代码先按现象定位到层连接层、配置层、编译层分开排查比盲目改要快得多。6. 论文、视频与验收把源码变成能过答辩的交付物源码跑通只是第一步毕业设计真正要交的是论文、视频和可演示的系统。论文部分ER 图可以直接从数据库脚本里逆向生成用 mysql workbench 的逆向工程会比手画快流程图要覆盖用户下单、后台审核两条主线不要画一幅包罗万象的大图分小节画更容易讲清楚。核心代码不要整段截图选订单状态更新这一段配上文字说明为什么用带条件的 update。视频演示我一般按两条线拍用户线先演示微信授权登录、浏览首页轮播、查看景点详情、预订民宿、模拟支付、查看订单状态管理线演示管理员登录、新增景点、上下架民宿、处理订单。每条线控制在 3 分钟以内用手机录屏加电脑端后台录屏拼接。拍摄前把演示数据准备好数据库里的管理员账号、测试用户、几条已支付订单都要存在不然现场造数据会手忙脚乱。验收环节容易被忽略的是数据库初始化。拿到 rural.sql 后把 admin 账号密码、测试用户的 openid、演示用的景点图片都核对一遍。我当初做这个项目最后悔的就是没提前检查数据库里有没有演示数据答辩前一天发现景点图片全是外链换成本地图片花了一晚上。所以建议你在写论文之前先把数据库脚本重新导入一遍在干净环境里走通用户下单全流程确认管理员后台能登录。希望帮到你。本文还有配套的精品资源点击获取
返回列表