ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot 2.7 + PostgreSQL 14 搭建景区导览后端实战

Spring Boot 2.7 + PostgreSQL 14 搭建景区导览后端实战 简介本资源是一套面向计算机专业本科生的毕业设计实战项目聚焦旅游信息化场景实现基于微信小程序的乌鲁木齐景区导览系统适用于Java后端开发、小程序前端及数据库综合实践学习。压缩包共51.5MB含完整Java后端源码、MySQL数据库脚本、微信小程序前端代码及系统演示视频覆盖PC后台管理与小程序双端交互逻辑支持权限分级管理员/注册用户与景区地图导览核心功能。已有182人学习下载内容体现典型业务过程分析方法包含用例图、功能模块划分与安全设计考量便于理解需求梳理、界面交互优化及权限控制落地。读者可直接部署运行掌握小程序JavaMySQL全栈开发流程并复现景区信息管理、位置导航、用户反馈等真实业务模块。1. 为什么乌鲁木齐景区导览小程序要用 Java 做后端不是 Node.js 或 Python你打开一个微信小程序点进“天山天池”页面地图加载、语音导览切换、实时人流热力图刷新——这些动作背后90% 的请求最终都打到一台运行着 Spring Boot 的 Linux 服务器上。它不处理前端渲染也不管用户手机型号只做三件事校验微信 openid、查 PostgreSQL 里景区坐标与讲解文本、把 JSON 数据按毫秒级响应吐回去。这就是 Java 在这个毕业设计里的真实角色稳、可测、易交接、能扛住校庆期间突发的 3000 并发扫码请求。很多同学第一反应是“小程序前端 云开发”但乌鲁木齐本地高校对毕业设计有明确要求必须体现三层架构能力表现层/业务逻辑层/数据访问层且后端代码需独立部署、数据库需本地建模、接口需手写 RESTful 规范。Java 生态的 Spring Boot MyBatis-Plus PostgreSQL 组合恰好卡在“够用不炫技、可调试可审计、文档齐全易答辩”的黄金区间。它不像 Go 那样需要额外解释协程调度也不像 Python Flask 那样在事务隔离和连接池配置上容易踩坑——而这些恰恰是答辩老师翻你application.yml和Transactional注解时最常问的点。如果你正用 HBuilderX 或微信开发者工具写 WXML 页面却卡在“怎么把景区介绍从数据库取出来传给小程序”那这篇就是为你写的不讲抽象概念只拆解从mvn clean package打包出 jar 包到java -jar启动服务再到小程序wx.request成功拿到{code:200,data:[{name:红山塔,lng:87.6212,lat:43.8521}]}的完整链路。所有命令、配置项、SQL 表结构、甚至pom.xml里必须保留的 7 个依赖坐标全部可复制粘贴。2. 搭建 Java 后端服务Spring Boot 2.7.x PostgreSQL 14 的最小可行配置2.1 为什么选 Spring Boot 2.7.x 而非 3.xSpring Boot 3.x 默认要求 JDK 17 且全面弃用 Jakarta EE 8而新疆多所高校实验室服务器仍运行 CentOS 7 JDK 8。实测发现若强行升级spring-boot-starter-web会因jakarta.servlet.http.HttpServletRequest类路径冲突导致启动失败而 Spring Boot 2.7.182023 年 10 月最后维护版完美兼容 JDK 8–17且内置 Tomcat 9.0.x 对 WebSocket 支持稳定——这对后续实现“导游语音实时推送”至关重要。提示不要用start.spring.io自动生成项目它默认勾选 Spring Boot 3.x。手动下载 https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/2.7.18/ 的 pom 文件或直接在pom.xml中声明 parentparent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent2.2 PostgreSQL 14 数据库建模景区、景点、导览内容三张表的设计逻辑毕业设计答辩中ER 图和字段注释是硬性评分项。本系统采用三张核心表避免过度范式化导致联表查询变慢小程序首屏加载要求 ≤ 800ms表名字段说明索引策略为什么这样设scenic_area景区主表idBIGSERIAL PK、nameVARCHAR(50) NOT NULL、cover_img_urlTEXT、introTEXTCREATE INDEX idx_name ON scenic_area USING btree (name);乌鲁木齐市文旅局提供 12 个 A 级景区名称固定全文检索用不到btree 索引足够快scenic_spot景点子表id、area_idBIGINT FK、nameVARCHAR(30)、lng/latNUMERIC(10,7)、order_numSMALLINTCREATE INDEX idx_area_lnglat ON scenic_spot USING gist (ST_Point(lng, lat));PostGIS 扩展支持地理围栏查询如“距离当前定位 500 米内景点”guide_content导览内容id、spot_idFK、content_typetext/audio/image、content_urlTEXT、duration_secINTEGERCREATE INDEX idx_spot_type ON guide_content (spot_id, content_type);小程序按景点 ID 类型文字/音频分页加载复合索引避免排序执行建表 SQL 前确保已启用 PostGIS-- 在 PostgreSQL 14 中执行 CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS postgis_topology;2.3 Spring Boot 连接 PostgreSQL 的application.yml关键参数别再用localhost:5432乌鲁木齐高校机房通常将数据库部署在独立服务器IP 地址为10.12.34.56且要求密码加密传输spring: datasource: url: jdbc:postgresql://10.12.34.56:5432/urumqi_tour?ssltruesslmoderequirecurrentSchemapublic username: tour_app password: EncryptedPassw0rd2024! # 实际使用时需通过 Jasypt 加密 driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: validate # 严禁 use create/update答辩时会被质疑数据安全性 show-sql: false properties: hibernate: format_sql: false dialect: org.hibernate.dialect.PostgreSQLDialect注意sslmoderequire是强制要求。若省略PostgreSQL 会拒绝来自非 SSL 客户端的连接——这是新疆政务云平台的安全基线。测试阶段可用?sslmodedisable临时绕过但提交源码前必须删掉。3. 微信小程序前端对接 Java 后端从登录态校验到景点列表渲染3.1 小程序端获取 code 并换取 openid 的标准流程微信小程序不直接暴露用户敏感信息必须通过wx.login()获取临时 code再由 Java 后端调用微信接口换取openid。关键点在于code 只能使用一次且 5 分钟过期所以小程序必须在onLoad生命周期内立即发起登录请求// pages/index/index.js Page({ data: { spots: [] }, onLoad() { wx.login({ success: (res) { // 将 code 发送给 Java 后端 wx.request({ url: https://api.urumqi-tour.edu.cn/auth/login, method: POST, data: { code: res.code }, success: (authRes) { if (authRes.data.code 200) { wx.setStorageSync(token, authRes.data.data.token); this.loadSpots(); // 登录成功后加载景点 } } }); } }); }, loadSpots() { wx.request({ url: https://api.urumqi-tour.edu.cn/api/spot/list?areaId1, header: { Authorization: Bearer wx.getStorageSync(token) }, success: (res) { this.setData({ spots: res.data.data }); } }); } });3.2 Java 后端实现/auth/login接口调用微信接口并生成 JWT Token微信官方接口https://api.weixin.qq.com/sns/jscode2session返回的openid是用户唯一标识但不能直接存入数据库微信可能变更规则。正确做法是用openid生成 JWT Token并设置 7 天有效期// com.urumqi.tour.controller.AuthController.java PostMapping(/auth/login) public ResultVO login(RequestBody MapString, String req) { String code req.get(code); // 1. 调用微信接口换取 openid String wxUrl https://api.weixin.qq.com/sns/jscode2session? appidwx1234567890abcdef secretyour_app_secret js_code code grant_typeauthorization_code; String wxResp restTemplate.getForObject(wxUrl, String.class); JSONObject wxJson JSONObject.parseObject(wxResp); String openid wxJson.getString(openid); // 2. 生成 JWT Token使用 jjwt-api 0.11.5 String token Jwts.builder() .setSubject(openid) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 3600 * 1000)) .signWith(SignatureAlgorithm.HS256, UrumqiTour2024SecretKey) .compact(); return ResultVO.success(Map.of(token, token)); }提示UrumqiTour2024SecretKey必须替换为 32 位以上随机字符串并存入application.yml的jwt.secret配置项。硬编码密钥会导致答辩时被扣分。3.3 景点列表接口/api/spot/list的分页与地理排序实现小程序首页需展示“按距离排序”的景点Java 后端不能把全部数据查出来再内存排序乌鲁木齐有 200 景点。必须让 PostgreSQL 直接计算距离并分页// com.urumqi.tour.mapper.SpotMapper.java Select(SELECT s.id, s.name, s.lng, s.lat, ST_Distance(ST_Point(#{lng}, #{lat}), ST_Point(s.lng, s.lat)) AS distance FROM scenic_spot s WHERE s.area_id #{areaId} ORDER BY distance ASC LIMIT #{size} OFFSET #{offset}) ListSpotDistanceDTO listByAreaAndLocation( Param(areaId) Long areaId, Param(lng) BigDecimal lng, Param(lat) BigDecimal lat, Param(size) Integer size, Param(offset) Integer offset);对应 DTOpublic class SpotDistanceDTO { private Long id; private String name; private BigDecimal lng; private BigDecimal lat; private Double distance; // 单位米 }调用时传入用户当前定位小程序端通过wx.getLocation获取# 示例请求小程序端构造 GET /api/spot/list?areaId1lng87.6212lat43.8521size10offset04. 数据库课程设计必交材料SQL 脚本、ER 图、演示视频录制要点4.1 导出可直接运行的 PostgreSQL 初始化脚本答辩老师会要求你提供“一键初始化数据库”的 SQL 文件。不要只导出表结构必须包含测试数据乌鲁木齐市文旅局公开的 5 个 A 级景区 20 个核心景点-- init_db.sql -- 创建 schema CREATE SCHEMA IF NOT EXISTS public; -- 插入景区数据红山公园、天山天池、国际大巴扎等 INSERT INTO scenic_area (name, cover_img_url, intro) VALUES (红山公园, https://example.com/hongshan.jpg, 乌鲁木齐地标红山塔始建于清代...), (天山天池, https://example.com/tianchi.jpg, 世界自然遗产海拔1980米高山湖泊...); -- 插入景点数据每个景区至少 3 个景点 INSERT INTO scenic_spot (area_id, name, lng, lat, order_num) VALUES (1, 红山塔, 87.6212, 43.8521, 1), (1, 远眺楼, 87.6225, 43.8533, 2), (2, 定海神针, 87.9234, 43.8912, 1); -- 插入导览内容文字音频 INSERT INTO guide_content (spot_id, content_type, content_url, duration_sec) VALUES (1, text, 红山塔建于乾隆年间高10.5米..., NULL), (1, audio, https://cdn.urumqi-tour.edu.cn/audio/hongshan_1.mp3, 98);提示content_url必须是公网可访问地址。本地开发时可用http://localhost:8080/static/但提交前要替换为 Nginx 静态资源路径否则演示视频里音频无法播放。4.2 ER 图绘制规范用 draw.io 导出 PNG PDF 双格式不要用 Word 画表格答辩要求 ER 图必须体现主外键关系、基数约束1:N、属性完整性。推荐用 draw.io 在线绘制导出时勾选PNG 格式嵌入毕业论文 Word 文档分辨率设为 300dpiPDF 格式单独提交用于打印展板关键标注scenic_area.id→scenic_spot.area_id标注 “1” 和 “N”scenic_spot.id→guide_content.spot_id标注 “1” 和 “0..*”一个景点可无音频所有NOT NULL字段加粗显示如scenic_area.name4.3 演示视频录制聚焦 3 个高分场景总时长控制在 3 分钟内评审专家平均观看每个视频不超过 90 秒必须前 10 秒抓住注意力。按此顺序录制时间段操作旁白台词技术亮点0:00–0:12小程序首页 → 点击“天山天池” → 地图自动居中“这是天山天池景区系统根据 GPS 定位自动计算您与各景点距离”展示ST_Distance地理排序实时生效0:13–0:35点击“定海神针” → 播放音频导览 → 进度条同步跳转“点击播放后端返回预签名 URLCDN 加速保障 100ms 内加载”展示guide_content.content_url的 CDN 集成0:36–2:50后台管理页/admin→ 新增一个“博格达峰”景点 → 小程序首页立即刷新“新增景点后小程序下拉刷新即可看到无需发版”展示 RESTful API 的松耦合特性注意视频开头加 2 秒黑屏字幕“乌鲁木齐景区导览系统 · 毕业设计 · 基于 Spring Boot 2.7 PostgreSQL 14”。结尾加 1 秒学校 Logo不加背景音乐。5. 答辩高频问题预判与 Java 面试题映射从源码到八股文5.1 “为什么用 MyBatis-Plus 而不用纯 JDBC”——直击事务与代码复用本质这个问题本质在考察你对 DAO 层抽象的理解。不能只答“方便”要指向具体技术决策事务一致性MyBatis-Plus 的Transactional注解能精准控制scenic_spot插入与guide_content批量插入的原子性。若用 JDBC需手动管理Connection.setAutoCommit(false)和conn.rollback()极易遗漏异常分支。动态 SQL 安全性QueryWrapper自动生成 WHERE 条件避免字符串拼接导致的 SQL 注入。例如wrapper.eq(area_id, areaId)编译后是WHERE area_id ?参数经 PreparedStatement 预编译。分页性能PageHelper.startPage(1,10)底层调用SELECT COUNT(*)和LIMIT/OFFSET比手写两次查询先查总数再查数据减少 1 次网络往返。// 正确示范MyBatis-Plus 分页查询 IPageScenicSpot page new Page(1, 10); QueryWrapperScenicSpot wrapper new QueryWrapper(); wrapper.eq(area_id, 1); IPageScenicSpot result scenicSpotService.page(page, wrapper); // result.getRecords() 返回第 1 页 10 条数据 // result.getTotal() 返回总记录数自动执行 COUNT 查询5.2 “数据库连接池用 HikariCP它的connection-timeout和max-lifetime怎么设”这题考的是生产环境调优经验不是背参数。结合乌鲁木齐高校服务器实际connection-timeout: 3000030 秒校园网延迟波动大DNS 解析偶尔超时30 秒足够容错max-lifetime: 180000030 分钟PostgreSQL 默认tcp_keepalive_time72002 小时设为 30 分钟可主动淘汰老化连接避免Connection reset异常minimum-idle: 5保证空闲时至少 5 个连接待命应对课间突发扫码高峰spring: datasource: hikari: connection-timeout: 30000 max-lifetime: 1800000 minimum-idle: 5 idle-timeout: 600000 # 空闲 10 分钟回收5.3 源码中必须保留的 3 个 Java 面试八股文锚点答辩老师可能突然问“这段代码体现了什么设计模式”——提前在源码中埋好答案文件位置代码片段对应八股文考点答辩话术com.urumqi.tour.config.JwtConfig.javaBean public JwtParser jwtParser() { return Jwts.parser().setSigningKey(...); }Spring Bean 生命周期管理“parser 是单例 Bean容器启动时创建避免每次解析都新建密钥对象符合享元模式思想”com.urumqi.tour.interceptor.AuthInterceptor.javaif (!token.startsWith(Bearer )) throw new AuthException();责任链模式雏形“拦截器链中认证拦截器只处理 Token 校验权限拦截器处理角色判断职责分离清晰”com.urumqi.tour.service.impl.SpotServiceImpl.javaOverride Transactional public boolean addSpot(ScenicSpot spot) { ... }Spring 事务传播机制“方法内调用其他 service 方法时若未加Transactional事务上下文会丢失这里显式声明确保原子性”最后检查你的src/main/resources/static/目录确保demo-video.mp4演示视频、er-diagram.pngER 图、init_db.sql数据库脚本三个文件与pom.xml同级。压缩为.zip时根目录必须是urumqi-tour-backend/内部结构为urumqi-tour-backend/src/、urumqi-tour-backend/target/、urumqi-tour-backend/init_db.sql—— 这是答辩材料形式审查的硬性要求。本文还有配套的精品资源点击获取
返回列表