最近在指导计算机专业学生做毕业设计时发现很多同学对微信小程序开发既感兴趣又有些无从下手。特别是旅游类小程序项目既要考虑前端交互体验又要处理后端数据接口技术栈跨度较大。本文将完整分享一个开源的特色旅游小程序毕业设计方案从技术选型到代码实现带你一步步完成可落地的项目。这个项目采用微信小程序作为前端配合Spring Boot后端API实现景点展示、路线规划、在线预订等核心功能。无论是计算机专业的毕业设计还是想入门全栈开发的初学者都能通过本文获得完整的开发思路和可运行的代码示例。1. 项目背景与需求分析1.1 为什么选择旅游小程序作为毕业设计旅游类小程序是当前移动互联网的热门应用场景具有以下技术特点技术综合性涉及前端UI设计、后端API开发、数据库设计等多个技术层面业务完整性包含用户管理、数据展示、交易流程等典型业务模块实战价值高所学技术可直接应用于实际工作场景扩展性强可在基础功能上添加地图导航、智能推荐等进阶功能1.2 核心功能需求基于典型的旅游业务场景我们规划了以下核心功能模块用户系统微信授权登录、用户信息管理景点展示景点列表、详情介绍、图片展示路线推荐特色旅游路线、智能推荐算法预订功能门票预订、酒店预订、订单管理收藏评论景点收藏、用户评价互动地图导航基于位置服务的周边景点发现1.3 技术栈选型理由前端技术栈微信小程序原生框架开发门槛低文档完善生态成熟无需考虑跨平台兼容性问题毕业设计评审老师熟悉度较高后端技术栈Spring Boot MySQLSpring Boot简化了后端开发配置MySQL是成熟稳定的关系型数据库易于部署和演示适合毕业设计场景2. 开发环境准备2.1 硬件与软件要求开发设备配置操作系统Windows 10/11 或 macOS 10.14内存8GB及以上推荐16GB存储空间至少10GB可用空间必要软件安装微信开发者工具最新稳定版JDK 1.8或更高版本IntelliJ IDEA或EclipseMySQL 5.7或8.0版本Maven 3.6 或 GradlePostman用于API测试2.2 微信小程序环境配置首先需要注册微信小程序账号并完成开发者认证# 访问微信公众平台注册小程序账号 # 完成企业或个人主体认证 # 获取AppID用于开发调试在微信开发者工具中创建新项目项目名称特色旅游小程序目录选择本地开发目录AppID使用测试号或正式AppID开发模式小程序后端服务不使用云开发2.3 后端开发环境搭建创建Spring Boot项目结构# 使用Spring Initializr创建项目 # 选择依赖Web、JPA、MySQL、Lombok mvn archetype:generate -DgroupIdcom.tourism -DartifactIdtourism-app -DarchetypeArtifactIdmaven-archetype-quickstart -DinteractiveModefalse项目基础依赖配置pom.xml?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion groupIdcom.tourism/groupId artifactIdtourism-app/artifactId version1.0.0/version parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.0/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies /project3. 数据库设计与建模3.1 数据库表结构设计根据业务需求设计以下核心数据表用户表users存储用户基本信息CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, openid VARCHAR(100) UNIQUE NOT NULL COMMENT 微信openid, nickname VARCHAR(100) COMMENT 用户昵称, avatar_url VARCHAR(500) COMMENT 头像URL, phone VARCHAR(20) COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );景点表scenic_spots存储景点详细信息CREATE TABLE scenic_spots ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOT NULL COMMENT 景点名称, description TEXT COMMENT 景点描述, address VARCHAR(500) COMMENT 详细地址, latitude DECIMAL(10, 6) COMMENT 纬度, longitude DECIMAL(10, 6) COMMENT 经度, images TEXT COMMENT 图片URL列表, price DECIMAL(10, 2) DEFAULT 0 COMMENT 门票价格, open_time VARCHAR(100) COMMENT 开放时间, rating DECIMAL(3, 1) DEFAULT 0 COMMENT 评分, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );订单表orders管理用户预订信息CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 用户ID, spot_id BIGINT NOT NULL COMMENT 景点ID, order_number VARCHAR(100) UNIQUE NOT NULL COMMENT 订单号, quantity INT DEFAULT 1 COMMENT 购买数量, total_amount DECIMAL(10, 2) COMMENT 总金额, status TINYINT DEFAULT 0 COMMENT 订单状态, visit_date DATE COMMENT 游览日期, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (spot_id) REFERENCES scenic_spots(id) );3.2 实体类设计使用JPA注解定义数据实体// 用户实体类 Entity Table(name users) Data public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name openid, unique true, nullable false) private String openid; Column(name nickname) private String nickname; Column(name avatar_url) private String avatarUrl; Column(name phone) private String phone; CreationTimestamp Column(name create_time) private LocalDateTime createTime; UpdateTimestamp Column(name update_time) private LocalDateTime updateTime; } // 景点实体类 Entity Table(name scenic_spots) Data public class ScenicSpot { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(name name, nullable false) private String name; Column(name description, columnDefinition TEXT) private String description; Column(name address) private String address; Column(name latitude, precision 10, scale 6) private BigDecimal latitude; Column(name longitude, precision 10, scale 6) private BigDecimal longitude; Column(name images, columnDefinition TEXT) private String images; // JSON格式存储图片列表 Column(name price) private BigDecimal price; Column(name rating, precision 3, scale 1) private BigDecimal rating; CreationTimestamp Column(name create_time) private LocalDateTime createTime; }4. 后端API开发实战4.1 Spring Boot基础配置应用配置文件application.ymlserver: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://localhost:3306/tourism_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: true logging: level: com.tourism: DEBUG org.hibernate.SQL: DEBUG主启动类配置SpringBootApplication EnableJpaRepositories EntityScan(com.tourism.entity) public class TourismApplication { public static void main(String[] args) { SpringApplication.run(TourismApplication.class, args); } }4.2 用户认证接口实现微信小程序登录接口RestController RequestMapping(/auth) public class AuthController { Autowired private UserService userService; PostMapping(/wxlogin) public ApiResponse wxLogin(RequestBody WxLoginRequest request) { try { // 调用微信接口验证code String wxUrl https://api.weixin.qq.com/sns/jscode2session; MapString, String params new HashMap(); params.put(appid, appId); params.put(secret, appSecret); params.put(js_code, request.getCode()); params.put(grant_type, authorization_code); // 发送HTTP请求获取openid String response restTemplate.getForObject(wxUrl, String.class, params); JSONObject json JSONObject.parseObject(response); String openid json.getString(openid); if (openid ! null) { // 查找或创建用户 User user userService.findOrCreateUser(openid, request.getUserInfo()); String token jwtUtil.generateToken(user.getId().toString()); return ApiResponse.success(登录成功, new LoginResponse(token, user)); } else { return ApiResponse.error(微信登录失败); } } catch (Exception e) { return ApiResponse.error(登录异常 e.getMessage()); } } } // 登录请求DTO Data class WxLoginRequest { private String code; private WxUserInfo userInfo; } Data class WxUserInfo { private String nickName; private String avatarUrl; private Integer gender; }4.3 景点管理接口开发景点列表分页查询接口RestController RequestMapping(/spots) public class ScenicSpotController { Autowired private ScenicSpotService spotService; GetMapping(/list) public ApiResponse getSpotList( RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size, RequestParam(required false) String keyword) { Pageable pageable PageRequest.of(page - 1, size, Sort.by(createTime).descending()); PageScenicSpot spots spotService.findSpots(keyword, pageable); MapString, Object result new HashMap(); result.put(list, spots.getContent()); result.put(total, spots.getTotalElements()); result.put(pages, spots.getTotalPages()); return ApiResponse.success(查询成功, result); } GetMapping(/detail/{id}) public ApiResponse getSpotDetail(PathVariable Long id) { ScenicSpot spot spotService.findById(id); if (spot null) { return ApiResponse.error(景点不存在); } return ApiResponse.success(查询成功, spot); } } // 服务层实现 Service public class ScenicSpotService { Autowired private ScenicSpotRepository spotRepository; public PageScenicSpot findSpots(String keyword, Pageable pageable) { if (keyword ! null !keyword.trim().isEmpty()) { return spotRepository.findByNameContainingOrAddressContaining(keyword, keyword, pageable); } return spotRepository.findAll(pageable); } public ScenicSpot findById(Long id) { return spotRepository.findById(id).orElse(null); } }4.4 订单业务逻辑实现订单创建和状态管理Service public class OrderService { Autowired private OrderRepository orderRepository; Autowired private ScenicSpotRepository spotRepository; Transactional public Order createOrder(Long userId, Long spotId, Integer quantity, LocalDate visitDate) { // 验证景点存在性 ScenicSpot spot spotRepository.findById(spotId) .orElseThrow(() - new RuntimeException(景点不存在)); // 生成订单号 String orderNumber generateOrderNumber(); // 计算总金额 BigDecimal totalAmount spot.getPrice().multiply(BigDecimal.valueOf(quantity)); Order order new Order(); order.setUserId(userId); order.setSpotId(spotId); order.setOrderNumber(orderNumber); order.setQuantity(quantity); order.setTotalAmount(totalAmount); order.setVisitDate(visitDate); order.setStatus(0); // 待支付 return orderRepository.save(order); } private String generateOrderNumber() { return TO System.currentTimeMillis() RandomUtil.randomNumbers(4); } Transactional public boolean payOrder(Long orderId) { Order order orderRepository.findById(orderId) .orElseThrow(() - new RuntimeException(订单不存在)); if (order.getStatus() ! 0) { throw new RuntimeException(订单状态异常); } order.setStatus(1); // 已支付 orderRepository.save(order); return true; } }5. 微信小程序前端开发5.1 项目结构与配置小程序目录结构tourism-miniprogram/ ├── pages/ │ ├── index/ # 首页 │ ├── spots/ # 景点列表 │ ├── detail/ # 景点详情 │ ├── order/ # 订单页面 │ └── profile/ # 个人中心 ├── components/ # 公共组件 ├── utils/ # 工具类 ├── app.js # 小程序入口 ├── app.json # 全局配置 ├── app.wxss # 全局样式 └── project.config.json # 项目配置全局配置文件app.json{ pages: [ pages/index/index, pages/spots/list, pages/spots/detail, pages/order/create, pages/order/list, pages/profile/index ], window: { backgroundTextStyle: light, navigationBarBackgroundColor: #07c160, navigationBarTitleText: 特色旅游, navigationBarTextStyle: white, enablePullDownRefresh: true }, tabBar: { color: #666, selectedColor: #07c160, list: [ { pagePath: pages/index/index, text: 首页, iconPath: images/home.png, selectedIconPath: images/home-active.png }, { pagePath: pages/spots/list, text: 景点, iconPath: images/spots.png, selectedIconPath: images/spots-active.png }, { pagePath: pages/profile/index, text: 我的, iconPath: images/profile.png, selectedIconPath: images/profile-active.png } ] }, permission: { scope.userLocation: { desc: 你的位置信息将用于小程序位置接口的效果展示 } } }5.2 首页设计与实现首页页面结构index.wxmlview classcontainer !-- 搜索框 -- view classsearch-box input classsearch-input placeholder搜索景点名称或地址 bindinputonSearchInput / button classsearch-btn bindtaponSearch搜索/button /view !-- 轮播图 -- swiper classbanner-swiper indicator-dotstrue autoplaytrue interval3000 swiper-item wx:for{{banners}} wx:keyid image classbanner-image src{{item.imageUrl}} modeaspectFill / /swiper-item /swiper !-- 分类导航 -- view classcategory-nav view classcategory-item wx:for{{categories}} wx:keyid bindtaponCategoryTap>Page({ data: { banners: [], categories: [ { id: 1, name: 自然风光, icon: /images/nature.png }, { id: 2, name: 人文古迹, icon: /images/culture.png }, { id: 3, name: 休闲度假, icon: /images/relax.png }, { id: 4, name: 特色美食, icon: /images/food.png } ], recommendSpots: [], searchKeyword: }, onLoad: function() { this.loadBanners(); this.loadRecommendSpots(); }, onPullDownRefresh: function() { this.loadBanners(); this.loadRecommendSpots().then(() { wx.stopPullDownRefresh(); }); }, loadBanners: function() { // 模拟banner数据 this.setData({ banners: [ { id: 1, imageUrl: /images/banner1.jpg }, { id: 2, imageUrl: /images/banner2.jpg }, { id: 3, imageUrl: /images/banner3.jpg } ] }); }, loadRecommendSpots: function() { return new Promise((resolve) { wx.request({ url: http://localhost:8080/api/spots/list?page1size6, method: GET, success: (res) { if (res.data.code 200) { this.setData({ recommendSpots: res.data.data.list }); } resolve(); }, fail: () { // 失败时使用模拟数据 this.setData({ recommendSpots: this.getMockSpots() }); resolve(); } }); }); }, onSearchInput: function(e) { this.setData({ searchKeyword: e.detail.value }); }, onSearch: function() { if (this.data.searchKeyword.trim()) { wx.navigateTo({ url: /pages/spots/list?keyword${this.data.searchKeyword} }); } }, onCategoryTap: function(e) { const categoryId e.currentTarget.dataset.id; wx.navigateTo({ url: /pages/spots/list?category${categoryId} }); }, onSpotTap: function(e) { const spotId e.currentTarget.dataset.id; wx.navigateTo({ url: /pages/spots/detail?id${spotId} }); }, onMoreSpots: function() { wx.switchTab({ url: /pages/spots/list }); }, getMockSpots: function() { return [ { id: 1, name: 西湖风景区, address: 浙江省杭州市西湖区, price: 0, rating: 4.8, images: [/images/spot1.jpg] } // 更多模拟数据... ]; } });5.3 景点详情页开发详情页核心功能实现Page({ data: { spot: null, isCollected: false, currentImageIndex: 0 }, onLoad: function(options) { this.spotId options.id; this.loadSpotDetail(); this.checkCollectionStatus(); }, loadSpotDetail: function() { wx.showLoading({ title: 加载中... }); wx.request({ url: http://localhost:8080/api/spots/detail/${this.spotId}, method: GET, success: (res) { wx.hideLoading(); if (res.data.code 200) { const spot res.data.data; // 处理图片数据 if (typeof spot.images string) { spot.images JSON.parse(spot.images); } this.setData({ spot }); } else { wx.showToast({ title: 加载失败, icon: none }); } }, fail: () { wx.hideLoading(); wx.showToast({ title: 网络错误, icon: none }); } }); }, onImageChange: function(e) { this.setData({ currentImageIndex: e.detail.current }); }, onCollectTap: function() { if (!this.data.spot) return; const newStatus !this.data.isCollected; this.setData({ isCollected: newStatus }); // 调用收藏接口 wx.request({ url: http://localhost:8080/api/collection/toggle, method: POST, data: { spotId: this.spotId, action: newStatus ? add : remove }, header: { Authorization: wx.getStorageSync(token) } }); }, onBookTap: function() { if (!this.data.spot) return; wx.navigateTo({ url: /pages/order/create?spotId${this.spotId} }); }, onLocationTap: function() { const spot this.data.spot; if (spot spot.latitude spot.longitude) { wx.openLocation({ latitude: parseFloat(spot.latitude), longitude: parseFloat(spot.longitude), name: spot.name, address: spot.address }); } } });6. 项目部署与测试6.1 后端服务部署使用Docker简化部署流程# Dockerfile FROM openjdk:8-jre-slim WORKDIR /app COPY target/tourism-app-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar, --spring.profiles.activeprod]数据库部署配置# application-prod.yml spring: datasource: url: jdbc:mysql://mysql-server:3306/tourism_db?useSSLfalse username: prod_user password: ${DB_PASSWORD} jpa: hibernate: ddl-auto: validate server: port: 80806.2 小程序发布流程测试阶段在微信开发者工具中完成功能测试使用真机调试验证各功能模块提交体验版供导师或同学测试发布准备# 小程序代码上传 # 在开发者工具中点击上传 # 填写版本号和项目备注审核发布登录微信公众平台提交审核等待1-7个工作日审核结果审核通过后发布上线6.3 功能测试用例用户登录测试// 测试用例描述验证微信登录功能 // 前置条件小程序已授权获取用户信息 // 测试步骤 // 1. 点击登录按钮 // 2. 授权用户信息 // 3. 验证登录状态 // 预期结果登录成功显示用户昵称和头像景点浏览测试// 测试用例描述验证景点列表和详情浏览 // 前置条件网络连接正常 // 测试步骤 // 1. 进入景点列表页 // 2. 滑动浏览列表 // 3. 点击进入详情页 // 4. 查看图片和详细信息 // 预期结果数据加载正常图片显示清晰7. 常见问题与解决方案7.1 开发环境问题问题1微信开发者工具无法真机调试现象预览二维码无法扫描或提示网络错误原因网络设置问题或开发者账号权限不足解决方案检查电脑和手机是否在同一WiFi网络确认开发者工具登录账号与小程序的开发者权限一致尝试重启开发者工具或更换网络环境问题2Spring Boot服务无法连接MySQL现象应用启动时报数据库连接错误原因数据库配置错误或服务未启动解决方案# 检查application.yml配置 spring: datasource: url: jdbc:mysql://localhost:3306/tourism_db?useSSLfalseserverTimezoneAsia/Shanghai username: root password: 正确密码 driver-class-name: com.mysql.cj.jdbc.Driver确认MySQL服务已启动检查数据库名、用户名、密码是否正确验证MySQL版本与驱动兼容性7.2 业务逻辑问题问题3微信登录获取不到openid现象登录接口返回微信登录失败原因AppID和AppSecret配置错误或code失效解决方案// 确保小程序AppID配置正确 // 检查微信开发者工具中的AppID设置 // 验证后端配置的AppSecret是否正确问题4图片上传失败现象上传图片时提示权限错误或网络错误原因服务器配置问题或域名未备案解决方案确认服务器文件目录有写权限检查域名是否完成ICP备案验证图片大小是否符合限制7.3 性能优化建议数据库优化-- 为常用查询字段添加索引 CREATE INDEX idx_spot_name ON scenic_spots(name); CREATE INDEX idx_spot_address ON scenic_spots(address); CREATE INDEX idx_order_user ON orders(user_id);前端优化措施// 图片懒加载实现 Page({ onReachBottom: function() { // 分批加载数据避免一次性加载过多 this.loadMoreData(); }, // 使用缓存减少请求 getSpotDetail: function(id) { const cacheKey spot_${id}; let spot wx.getStorageSync(cacheKey); if (!spot) { // 从服务器获取并缓存 spot this.fetchSpotDetail(id); wx.setStorageSync(cacheKey, spot); } return spot; } });8. 项目扩展与进阶功能8.1 智能推荐功能基于用户行为实现个性化推荐Service public class RecommendationService { public ListScenicSpot recommendSpots(Long userId) { // 基于协同过滤算法 ListLong similarUsers findSimilarUsers(userId); ListLong recommendedSpotIds findPopularSpots(similarUsers); return spotRepository.findByIdIn(recommendedSpotIds); } private ListLong findSimilarUsers(Long userId) { // 实现用户相似度计算 // 基于浏览历史、收藏行为等 return Collections.emptyList(); } }8.2 地图导航集成集成腾讯地图实现导航功能// 地图组件集成 Page({ onNavigate: function() { const spot this.data.spot; wx.getLocation({ type: gcj02, success: (res) { const startLat res.latitude; const startLng res.longitude; const endLat spot.latitude; const endLng spot.longitude; // 调用地图导航 wx.openLocation({ latitude: endLat, longitude: endLng, name: spot.name, address: spot.address }); } }); } });8.3 后台管理系统使用VueElement UI开发管理后台template div classadmin-container el-table :dataspotList el-table-column propname label景点名称/el-table-column el-table-column propaddress label地址/el-table-column el-table-column propprice label价格/el-table-column el-table-column label操作 template slot-scopescope el-button clickeditSpot(scope.row)编辑/el-button el-button typedanger clickdeleteSpot(scope.row)删除/el-button /template /el-table-column /el-table /div /template这个特色旅游小程序项目涵盖了微信小程序开发的全流程从需求分析到技术实现再到部署测试为计算机专业毕业设计提供了完整的参考方案。项目采用主流技术栈代码结构清晰功能模块完整既适合作为学习练手项目也具备进一步商业化的潜力。在实际开发过程中建议先完成核心功能的最小可行版本再逐步添加扩展功能。遇到技术难题时可以查阅微信官方文档和Spring Boot官方指南这两个技术栈的社区资源都非常丰富。