微信小程序+SpringBoot+MySQL全栈项目实战:景点旅游攻略平台开发指南 📅 发布时间:2026/8/23 7:59:30 👁 浏览次数: 如果你正在为计算机专业的毕业设计选题发愁或者想找一个能串联起微信小程序、SpringBoot、MySQL三大主流技术的完整实战项目来提升简历那么这篇文章就是为你准备的。很多同学在选毕设题目时常常陷入两难选题太简单显得技术含量不足选题太复杂又怕自己无法在规定时间内完成。一个“景点旅游攻略交流平台”恰恰能很好地平衡这个矛盾。它听起来贴近生活容易理解但背后却涵盖了从前端小程序交互、后端API接口设计、数据库建模到服务器部署的全栈开发流程。更重要的是这个项目有明确的应用场景和用户价值不再是“为了做而做”的增删改查练习。本文将带你从零开始完整拆解一个基于微信小程序和SpringBoot的景点攻略系统。我不会只给你一个源码包让你自己琢磨而是会重点讲清楚为什么这个架构是当前企业级开发的主流选择在从零搭建的过程中有哪些看似不起眼却极易踩坑的关键环节以及如何将这个项目从“能运行”优化到“能写到简历里”的水平。无论你是零基础的初学者还是想巩固全栈技能的进阶者都能从中获得可落地的实操指南和工程化思考。1. 为什么“景点攻略平台”是绝佳的毕设与实战选题在开始敲代码之前我们有必要先想清楚投入时间做这个项目到底能收获什么。一个优秀的毕设或实战项目应该同时满足“技术栈主流”、“业务逻辑清晰”、“有扩展空间”和“易于演示”这几个特点。首先技术栈的含金量足够高。“微信小程序 SpringBoot MySQL”这个组合几乎覆盖了国内互联网公司中后台和移动端开发的半壁江山。微信小程序让你接触到移动端开发、组件化、云开发可选等概念SpringBoot让你快速搭建RESTful API理解控制层、业务层、数据访问层的分层架构MySQL则是关系型数据库的基石。掌握了这套组合拳你应对大多数校招和初级社招的后端或全栈岗位就有了扎实的基础。其次业务场景自带“故事性”。相比冷冰冰的“图书管理系统”或“员工信息管理”旅游攻略平台天然具备社交属性和内容属性。你可以设计用户发布攻略、收藏景点、点赞评论、基于地理位置推荐等丰富功能。这些功能能很好地串联起用户认证、内容审核、数据关联查询、事务处理等核心技术点。在答辩或面试时你也能更生动地阐述你的设计思路和解决的实际问题。最后项目具备良好的可扩展性。基础版本完成后你可以很容易地为它添加新模块比如引入Redis缓存热门攻略、集成第三方地图API如腾讯位置服务实现景点定位、甚至尝试用微服务架构拆分用户服务和内容服务。这能让你的项目持续生长体现你的学习能力和技术视野。所以这个项目不仅仅是一份作业更是一个能贯穿你学习、求职、甚至未来创业想法的技术试验田。2. 核心架构与技术选型解析在动手之前我们需要对整体架构有一个清晰的蓝图。本系统采用典型的前后端分离架构这是一种现代Web应用开发的主流模式能有效提升开发效率和系统可维护性。2.1 前端微信小程序微信小程序负责所有用户交互界面。选择小程序而非原生App或H5主要基于以下几点考虑开发成本低使用类Web技术WXML, WXSS, JS开发学习曲线平缓生态成熟。用户体验好接近原生应用的流畅体验且无需安装。生态完善拥有丰富的官方组件和API如地图、支付、客服消息等便于集成复杂功能。部署便捷提交审核后即可发布用户通过微信扫码或搜索即可使用分发门槛极低。2.2 后端SpringBootSpringBoot作为后端服务框架承担了核心业务逻辑处理、数据持久化和API提供的职责。快速启动通过SpringBootApplication一个注解和内置Tomcat可以秒级启动一个Web服务。约定大于配置大量默认配置减少了繁琐的XML编写让开发者更关注业务。生态强大无缝集成MyBatis数据访问、Spring Security安全、Spring Cache缓存等众多Spring家族优秀组件。RESTful API设计这是前后端通信的桥梁设计良好的API是项目成功的关键。2.3 数据层MySQLMySQL作为关系型数据库负责结构化数据的持久化存储。事务支持对于用户发布攻略、积分变更等需要数据一致性的操作事务机制至关重要。关联查询景点、攻略、用户、评论之间存在复杂的多对多、一对多关系SQL能高效处理这类关联查询。成熟稳定社区活跃资料丰富是初学者入门数据库的最佳选择之一。2.4 通信与数据格式HTTP/HTTPS小程序与后端服务通过HTTP协议进行通信生产环境必须使用HTTPS以保证数据传输安全。JSON前后端交互的数据格式统一为JSON轻量且易于解析。整个系统的数据流向可以概括为用户在小程序端操作 - 发起HTTP请求 - SpringBoot后端接收并处理请求可能涉及数据库CRUD- 将处理结果封装为JSON返回 - 小程序接收数据并更新视图。3. 开发环境与工具准备工欲善其事必先利其器。一个顺畅的开发环境能极大提升效率。以下是本项目的必备环境清单3.1 后端开发环境JDK:版本 8 或 11LTS长期支持版。推荐OpenJDK或Oracle JDK。安装后配置JAVA_HOME环境变量。Maven:版本 3.6。用于管理项目依赖、构建和打包。同样需要配置环境变量。IDE:IntelliJ IDEA社区版或旗舰版或 Eclipse需安装Spring插件。IDEA对SpringBoot的支持更友好推荐使用。MySQL:版本 5.7 或 8.0。安装MySQL服务器并记住root密码。建议安装一个图形化管理工具如Navicat、MySQL Workbench或IDEA自带的数据库工具。3.2 前端开发环境微信开发者工具:这是开发、调试、预览小程序的官方IDE必须安装。前往微信公众平台下载。Node.js:微信开发者工具的底层依赖同时一些构建工具可能需要。安装LTS版本即可。3.3 辅助工具Postman/Apifox:用于测试后端API接口模拟前端请求非常高效。Git:代码版本管理工具。推荐使用GitHub或Gitee托管你的项目代码。Redis (可选):如果你计划实现缓存或会话管理可以提前安装。环境验证步骤打开命令行CMD或Terminal依次执行以下命令确认安装成功java -version mvn -v node -v npm -v如果都能正确输出版本信息说明基础环境已就绪。4. 数据库设计与核心表结构数据库设计是系统的基石设计的好坏直接影响后续开发的复杂度和系统性能。我们围绕“用户”、“景点”、“攻略”、“互动”这几个核心实体进行设计。4.1 核心表关系图概念用户 (user) | |--- 发布 --- 攻略 (strategy) | | | |--- 属于 --- 景点 (scenic_spot) | |--- 收藏 --- 收藏关系 (favorite) |--- 点赞 --- 点赞关系 (like) |--- 评论 --- 评论 (comment)4.2 关键表结构SQL示例以下是几个核心表的建表语句体现了字段设计、索引和约束。-- 用户表 CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, openid varchar(100) DEFAULT NULL COMMENT 微信用户唯一标识, nickname varchar(100) DEFAULT NULL COMMENT 昵称, avatar_url varchar(500) DEFAULT NULL COMMENT 头像URL, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) COMMENT openid唯一索引 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 景点表 CREATE TABLE scenic_spot ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, name varchar(200) NOT NULL COMMENT 景点名称, description text COMMENT 景点描述, location varchar(500) COMMENT 地理位置, latitude decimal(10, 7) COMMENT 纬度, longitude decimal(10, 7) COMMENT 经度, cover_image varchar(500) COMMENT 封面图URL, view_count int(11) DEFAULT 0 COMMENT 浏览量, status tinyint(4) DEFAULT 1 COMMENT 状态1-正常0-下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_location (latitude, longitude) COMMENT 地理位置索引便于附近景点查询 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT景点表; -- 攻略表 (核心内容表) CREATE TABLE strategy ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID, user_id bigint(20) NOT NULL COMMENT 发布用户ID, spot_id bigint(20) NOT NULL COMMENT 关联景点ID, title varchar(200) NOT NULL COMMENT 攻略标题, content longtext NOT NULL COMMENT 攻略详情富文本/HTML, cover_image varchar(500) COMMENT 攻略封面图, like_count int(11) DEFAULT 0 COMMENT 点赞数, favorite_count int(11) DEFAULT 0 COMMENT 收藏数, comment_count int(11) DEFAULT 0 COMMENT 评论数, status tinyint(4) DEFAULT 1 COMMENT 状态1-审核通过0-待审核-1-审核失败, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_spot_id (spot_id), KEY idx_create_time (create_time DESC) COMMENT 按发布时间倒序索引用于最新攻略列表, CONSTRAINT fk_strategy_user FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE, CONSTRAINT fk_strategy_spot FOREIGN KEY (spot_id) REFERENCES scenic_spot (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT旅游攻略表; -- 评论表 CREATE TABLE comment ( id bigint(20) NOT NULL AUTO_INCREMENT, strategy_id bigint(20) NOT NULL COMMENT 所属攻略ID, user_id bigint(20) NOT NULL COMMENT 评论用户ID, parent_id bigint(20) DEFAULT 0 COMMENT 父评论ID0-顶级评论, content varchar(1000) NOT NULL COMMENT 评论内容, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_strategy_id (strategy_id), KEY idx_parent_id (parent_id), CONSTRAINT fk_comment_strategy FOREIGN KEY (strategy_id) REFERENCES strategy (id) ON DELETE CASCADE, CONSTRAINT fk_comment_user FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT评论表;设计要点解析openid唯一索引微信用户标识用于关联小程序用户与系统用户。status字段这是业务系统的常见设计用于软删除或状态控制避免物理删除数据。create_time与update_time自动记录创建和更新时间便于排查问题和数据分析。外键约束 (FOREIGN KEY):确保数据的一致性和完整性。例如删除一个用户其发布的攻略和评论也应级联删除ON DELETE CASCADE。索引设计在user_id,spot_id,create_time等查询频繁的字段上建立索引能大幅提升查询效率。idx_location是一个联合索引为后续实现“附近景点”功能做准备。5. SpringBoot后端工程搭建与核心代码实现接下来我们开始构建后端服务。我们将使用Spring Initializr快速生成项目骨架。5.1 创建SpringBoot项目使用IDEA的Spring Initializr选择以下依赖Spring Web:用于构建Web应用和RESTful API。MyBatis Framework:用于数据持久层操作。MySQL Driver:MySQL数据库连接驱动。Lombok:通过注解简化Java Bean的编写如自动生成getter/setter。生成项目后pom.xml文件会自动包含这些依赖。5.2 配置文件首先配置application.yml或application.properties连接数据库并设置MyBatis。# application.yml server: port: 8080 # 服务端口 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/travel_strategy_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root # 替换为你的数据库用户名 password: yourpassword # 替换为你的数据库密码 servlet: multipart: max-file-size: 10MB # 文件上传大小限制 max-request-size: 10MB mybatis: mapper-locations: classpath:mapper/*.xml # MyBatis映射文件位置 configuration: map-underscore-to-camel-case: true # 自动将数据库下划线命名转为Java驼峰命名 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL调试用生产环境关闭 # 自定义配置 app: wx: app-id: your-appid # 微信小程序AppID app-secret: your-secret # 微信小程序AppSecret5.3 实体类 (Entity)使用Lombok简化代码创建与数据库表对应的实体类。// 文件路径src/main/java/com/example/travel/entity/User.java package com.example.travel.entity; import lombok.Data; import java.time.LocalDateTime; Data public class User { private Long id; private String openid; // 微信用户唯一标识 private String nickname; private String avatarUrl; private LocalDateTime createTime; private LocalDateTime updateTime; } // 文件路径src/main/java/com/example/travel/entity/Strategy.java package com.example.travel.entity; import lombok.Data; import java.time.LocalDateTime; Data public class Strategy { private Long id; private Long userId; private Long spotId; private String title; private String content; // 富文本内容 private String coverImage; private Integer likeCount 0; private Integer favoriteCount 0; private Integer commentCount 0; private Integer status 1; // 1-正常 private LocalDateTime createTime; private LocalDateTime updateTime; // 关联查询时使用非数据库字段 private String authorName; private String authorAvatar; private String spotName; }5.4 数据访问层 (Mapper)使用MyBatis的注解或XML方式。这里展示注解方式复杂查询建议用XML。// 文件路径src/main/java/com/example/travel/mapper/StrategyMapper.java package com.example.travel.mapper; import com.example.travel.entity.Strategy; import org.apache.ibatis.annotations.*; import java.util.List; Mapper public interface StrategyMapper { Insert(INSERT INTO strategy(user_id, spot_id, title, content, cover_image, status) VALUES(#{userId}, #{spotId}, #{title}, #{content}, #{coverImage}, #{status})) Options(useGeneratedKeys true, keyProperty id) int insert(Strategy strategy); Select(SELECT s.*, u.nickname as author_name, u.avatar_url as author_avatar, sp.name as spot_name FROM strategy s LEFT JOIN user u ON s.user_id u.id LEFT JOIN scenic_spot sp ON s.spot_id sp.id WHERE s.id #{id} AND s.status 1) Strategy selectDetailById(Long id); Select(script SELECT s.*, u.nickname as author_name, u.avatar_url as author_avatar, sp.name as spot_name FROM strategy s LEFT JOIN user u ON s.user_id u.id LEFT JOIN scenic_spot sp ON s.spot_id sp.id WHERE s.status 1 if testspotId ! null AND s.spot_id #{spotId} /if ORDER BY s.create_time DESC LIMIT #{offset}, #{pageSize} /script) ListStrategy selectPage(Param(spotId) Long spotId, Param(offset) Integer offset, Param(pageSize) Integer pageSize); Update(UPDATE strategy SET like_count like_count 1 WHERE id #{id}) int incrementLikeCount(Long id); }5.5 业务逻辑层 (Service)在这里编写核心业务逻辑如发布攻略、点赞、收藏等。// 文件路径src/main/java/com/example/travel/service/StrategyService.java package com.example.travel.service; import com.example.travel.entity.Strategy; import com.example.travel.mapper.StrategyMapper; import com.example.travel.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; Service public class StrategyService { Autowired private StrategyMapper strategyMapper; Autowired private UserMapper userMapper; /** * 发布攻略 */ Transactional // 开启事务保证操作原子性 public Long publishStrategy(Strategy strategy) { // 1. 基础校验略 // 2. 插入攻略 strategyMapper.insert(strategy); // 3. 可以更新用户发帖数等略 return strategy.getId(); } /** * 分页查询攻略列表 */ public ListStrategy getStrategyList(Long spotId, Integer pageNum, Integer pageSize) { Integer offset (pageNum - 1) * pageSize; return strategyMapper.selectPage(spotId, offset, pageSize); } /** * 点赞攻略 */ Transactional public boolean likeStrategy(Long strategyId, Long userId) { // 1. 检查是否已点赞需要有一个user_like_strategy关系表 // 2. 如果未点赞则插入点赞关系并更新攻略点赞数 // 这里简化处理直接更新点赞数 int rows strategyMapper.incrementLikeCount(strategyId); return rows 0; } }5.6 控制层 (Controller)提供RESTful API接口供小程序调用。// 文件路径src/main/java/com/example/travel/controller/StrategyController.java package com.example.travel.controller; import com.example.travel.common.Result; import com.example.travel.entity.Strategy; import com.example.travel.service.StrategyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/api/strategy) public class StrategyController { Autowired private StrategyService strategyService; /** * 发布攻略 * param strategy 攻略数据JSON格式 * return 新攻略ID */ PostMapping(/publish) public ResultLong publish(RequestBody Strategy strategy, RequestHeader(X-WX-Openid) String openid) { // 根据openid获取userId需在拦截器或服务中实现 // strategy.setUserId(userId); Long id strategyService.publishStrategy(strategy); return Result.success(id); } /** * 分页获取攻略列表 * param spotId 景点ID可选 * param pageNum 页码从1开始 * param pageSize 每页大小 * return 攻略列表 */ GetMapping(/list) public ResultListStrategy list(RequestParam(required false) Long spotId, RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize) { ListStrategy list strategyService.getStrategyList(spotId, pageNum, pageSize); return Result.success(list); } /** * 点赞攻略 */ PostMapping(/{id}/like) public ResultBoolean like(PathVariable Long id, RequestHeader(X-WX-Openid) String openid) { // 根据openid获取userId boolean success strategyService.likeStrategy(id, userId); return success ? Result.success(true) : Result.error(点赞失败); } }5.7 统一返回结果封装定义一个通用的返回结果类使API响应格式标准化。// 文件路径src/main/java/com/example/travel/common/Result.java package com.example.travel.common; import lombok.Data; Data public class ResultT { private Integer code; // 状态码如 200成功500失败 private String message; // 提示信息 private T data; // 返回的数据 public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }6. 微信小程序前端核心功能实现后端API准备好后我们开始构建小程序前端。小程序开发的核心在于Page、Component、API和WXML模板。6.1 小程序项目结构miniprogram/ ├── pages/ │ ├── index/ # 首页 │ ├── strategy/ # 攻略列表/详情页 │ ├── publish/ # 发布攻略页 │ └── profile/ # 个人中心页 ├── components/ # 自定义组件 ├── utils/ │ └── request.js # 网络请求封装 ├── app.js # 小程序入口 ├── app.json # 全局配置 ├── app.wxss # 全局样式 └── project.config.json # 项目配置6.2 封装网络请求为了统一处理请求、响应和错误我们封装一个request工具。// 文件路径miniprogram/utils/request.js const BASE_URL https://your-domain.com/api; // 替换为你的后端服务地址 const request (url, method, data, header {}) { // 从本地存储获取token如果有 const token wx.getStorageSync(token); const defaultHeader { Content-Type: application/json, X-WX-Openid: wx.getStorageSync(openid) // 传递用户标识 }; if (token) { defaultHeader[Authorization] Bearer ${token}; } return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { ...defaultHeader, ...header }, success: (res) { const { code, data, message } res.data; if (code 200) { resolve(data); } else { wx.showToast({ title: message || 请求失败, icon: none }); reject(new Error(message)); } }, fail: (err) { wx.showToast({ title: 网络错误, icon: none }); reject(err); } }); }); }; // 导出快捷方法 export const get (url, data) request(url, GET, data); export const post (url, data) request(url, POST, data); export const put (url, data) request(url, PUT, data); export const del (url, data) request(url, DELETE, data);6.3 首页实现展示攻略列表首页通常展示最新的攻略流。!-- 文件路径miniprogram/pages/index/index.wxml -- view classcontainer !-- 搜索栏 -- view classsearch-bar input placeholder搜索景点或攻略 bindinputonSearchInput / /view !-- 攻略列表 -- scroll-view scroll-y classstrategy-list bindscrolltolowerloadMore block wx:for{{strategyList}} wx:keyid view classstrategy-item bindtapnavigateToDetail>// 文件路径miniprogram/pages/index/index.js import { get } from ../../utils/request.js; Page({ data: { strategyList: [], pageNum: 1, pageSize: 10, hasMore: true, loading: false, searchKeyword: }, onLoad() { this.loadData(); }, // 加载攻略数据 async loadData() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); try { const { pageNum, pageSize, searchKeyword } this.data; const params { pageNum, pageSize }; if (searchKeyword) params.keyword searchKeyword; const list await get(/strategy/list, params); if (list.length this.data.pageSize) { this.setData({ hasMore: false }); } this.setData({ strategyList: this.data.strategyList.concat(list), pageNum: this.data.pageNum 1, loading: false }); } catch (err) { this.setData({ loading: false }); console.error(加载数据失败:, err); } }, // 上拉加载更多 loadMore() { this.loadData(); }, // 搜索输入 onSearchInput(e) { const keyword e.detail.value.trim(); this.setData({ searchKeyword: keyword, pageNum: 1, strategyList: [], hasMore: true }, () { // 防抖处理避免频繁请求 clearTimeout(this.searchTimer); this.searchTimer setTimeout(() { this.loadData(); }, 500); }); }, // 跳转到攻略详情页 navigateToDetail(e) { const id e.currentTarget.dataset.id; wx.navigateTo({ url: /pages/strategy/detail?id${id} }); } });6.4 用户登录与身份识别小程序通过wx.login()获取code传给后端换取openid和session_key从而识别用户。// 文件路径miniprogram/app.js 或 单独的服务文件 import { post } from ./utils/request.js; App({ onLaunch() { this.checkLogin(); }, // 检查登录状态 async checkLogin() { const openid wx.getStorageSync(openid); if (!openid) { await this.wxLogin(); } }, // 微信登录 async wxLogin() { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { try { // 将code发送到自己的后端后端调用微信接口换取openid const result await post(/auth/login, { code: res.code }); // 假设后端返回openid和token wx.setStorageSync(openid, result.openid); wx.setStorageSync(token, result.token); resolve(result); } catch (err) { reject(err); } } else { reject(new Error(登录失败 res.errMsg)); } }, fail: (err) reject(err) }); }); } });7. 项目运行、部署与效果验证7.1 本地运行启动后端在IDEA中直接运行SpringBoot主类带有SpringBootApplication注解的类控制台看到Tomcat started on port(s): 8080即表示启动成功。启动小程序用微信开发者工具打开miniprogram目录。在工具中点击“编译”即可在模拟器中看到小程序界面。配置请求域名在小程序管理后台的“开发管理”-“开发设置”-“服务器域名”中将你的后端服务地址如https://localhost:8080用于开发调试添加到request合法域名中。注意线上环境必须使用已备案的HTTPS域名。7.2 核心功能验证清单运行项目后请按顺序测试以下核心流程确保系统基本通畅[ ]用户登录打开小程序能自动完成微信登录并能在“个人中心”看到自己的头像和昵称。[ ]攻略列表首页能正确显示攻略列表包含封面、标题、作者、景点等信息。[ ]攻略详情点击任意攻略能跳转到详情页完整显示攻略内容、评论等。[ ]发布攻略在发布页面能选择景点、填写标题和内容可尝试集成富文本编辑器组件上传图片并成功发布。[ ]点赞/收藏在攻略详情页点击点赞和收藏按钮图标状态和计数能正确变化。[ ]评论功能能在攻略详情页发表评论并看到自己和他人的评论。7.3 后端API测试使用Postman等工具对写好的Controller接口进行测试例如GET http://localhost:8080/api/strategy/list?pageNum1pageSize10POST http://localhost:8080/api/strategy/publish(Body中传入JSON格式的攻略数据)POST http://localhost:8080/api/strategy/1/like(Header中传入X-WX-Openid)8. 常见问题与排查思路在开发过程中你几乎一定会遇到下面这些问题。提前了解可以节省大量排查时间。问题现象可能原因排查方式解决方案小程序无法请求后端接口1. 后端服务未启动。2. 小程序未配置服务器域名。3. 本地开发时微信开发者工具未开启“不校验合法域名”。1. 检查后端控制台是否启动成功。2. 检查小程序开发者工具右上角详情-本地设置-“不校验合法域名”是否勾选仅开发环境。3. 使用浏览器或Postman直接访问后端接口看是否通。1. 启动后端服务。2. 开发环境勾选“不校验合法域名”。生产环境必须在后台配置合法域名。3. 确保网络可达。后端连接数据库失败1.application.yml中数据库配置错误IP、端口、库名、用户名、密码。2. MySQL服务未启动。3. 数据库驱动版本不匹配。1. 检查application.yml配置。2. 在命令行或工具中尝试连接MySQL。3. 查看启动日志中的具体错误信息。1. 修正配置文件。2. 启动MySQL服务。3. 检查pom.xml中的mysql-connector-java版本与MySQL服务器版本匹配。插入中文数据到MySQL乱码数据库、表、连接字符串的字符集不是utf8mb4。1. 执行SHOW CREATE DATABASE your_db;查看数据库字符集。2. 执行SHOW CREATE TABLE your_table;查看表字符集。1. 创建数据库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。2. 在JDBC连接URL中添加参数?useUnicodetruecharacterEncodingutf-8。MyBatis查询结果字段为null实体类属性名与数据库字段名未正确映射。1. 检查是否开启了map-underscore-to-camel-case。2. 检查SQL查询语句中返回的列名是否与实体类属性名对应考虑别名。1. 在application.yml中配置map-underscore-to-camel-case: true。2. 在SQL中使用AS为列起别名使其与实体类属性名一致。事务Transactional不生效1. 方法不是public。2. 异常在方法内部被捕获未抛出。3. 方法在同一个类内部调用未经过代理。1. 检查方法修饰符。2. 检查try-catch块。3. 从另一个Bean调用该方法。1. 确保方法是public。2. 如需捕获异常在catch块中抛出RuntimeException或使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。3. 通过Autowired注入自身或使用AopContext.currentProxy()。小程序真机预览白屏1. 请求域名未配置或配置错误。2. 代码包大小超过2MB限制。3. 基础库版本过低某些API不支持。1. 检查真机调试模式下的网络请求报错。2. 在开发者工具上传代码时查看大小。3. 在管理后台设置最低基础库版本。1. 确保真机网络可访问后端服务并配置合法域名。2. 优化图片等静态资源使用CDN或云存储。3. 在app.json中设置style: v2并提高最低基础库版本要求。9. 项目优化与进阶方向一个能运行的项目只是开始一个优秀的项目还需要考虑性能、安全、可维护性和扩展性。以下是一些你可以继续深入的方向9.1 性能优化数据库层面索引优化为WHERE、ORDER BY、JOIN条件中的字段添加合适索引。使用EXPLAIN分析慢查询。查询优化避免SELECT *只查询需要的字段。对大数据量表进行分页查询。引入缓存使用Redis缓存热点数据如热门攻略列表、用户信息。将点赞、收藏等计数先更新到缓存再异步持久化到数据库。后端层面连接池使用HikariCP等高性能数据库连接池。异步处理对于发送通知、记录日志等非核心操作使用Async或消息队列进行异步处理。静态资源分离用户上传的图片、视频等静态文件不要存储在服务器本地应使用对象存储服务如阿里云OSS、腾讯云COS并通过CDN加速访问。前端层面图片懒加载列表中的图片使用lazy-load属性。数据分页与虚拟列表列表数据必须分页加载。对于超长列表考虑使用虚拟列表技术只渲染可视区域内的元素。请求合并与防抖节流减少不必要的网络请求。9.2 安全加固接口鉴权使用JWTJSON Web Token或小程序自定义登录态对所有需要身份验证的API进行拦截校验。Spring Security或自定义拦截器是实现此功能的好帮手。SQL注入防护坚持使用MyBatis的#{}预编译占位符绝对不要使用字符串拼接SQL。XSS防护用户输入的富文本内容在存储和展示前要进行过滤或转义。可以使用Jsoup等库进行HTML清洗。敏感信息脱敏返回给前端的用户手机号、邮箱等敏感信息进行部分隐藏。文件上传安全校验文件类型、大小对上传的文件进行病毒扫描存储时重命名文件避免执行漏洞。9.3 功能扩展让你的项目脱颖而出智能推荐基于用户的浏览、点赞、收藏行为使用简单的协同过滤或基于内容的推荐算法在首页增加“猜你喜欢”模块。地图集成集成腾讯位置服务LBS在小程序地图组件上展示景点位置实现“附近景点”搜索功能。内容搜索集成Elasticsearch为攻略标题、内容、景点名称提供全文检索功能提升搜索体验。社交增强增加关注用户、私信、攻略分享到朋友圈、生成精美攻略海报等功能。后台管理系统使用VueElement UI或ReactAnt Design开发一个独立的管理后台用于管理景点、审核攻略、管理用户等。容器化部署学习使用Docker将SpringBoot应用和MySQL数据库容器化编写Dockerfile和docker-compose.yml实现一键部署。通过这个“景点旅游攻略交流平台”项目你不仅完成了一个功能完整的毕业设计更走通了一个现代Web应用从设计、开发、调试到部署上线的全流程。技术细节会不断更新但解决问题的思路和工程化的方法是相通的。建议你在实现基础功能后选择一两个进阶方向深入实践这会让你的项目经验和技术思考更加丰满。