Spring Boot+Thymeleaf+AI大模型:智能天气出行服务系统从零搭建
之前用传统方式开发天气类系统时往往只能做到“展示今天温度”用户想知道“明天适不适合爬山”“下雨天怎么安排行程”这类问题时系统完全答不上来。把 AI 大模型接进来之后整个体验就完全不一样了。本文围绕基于 Spring Boot Thymeleaf AI 的智能天气出行服务系统完整拆解从架构设计、数据库表结构、天气接口接入、大模型接口调用到前端页面渲染的整套实现方案。后端使用 Spring Boot 提供接口与业务逻辑前端采用 Thymeleaf 模板引擎做服务端渲染再结合大模型接口生成天气解读与出行建议。虽然这套代码常被用作计算机毕业设计但底层思路同样适合企业小工具、内部服务平台等场景。为了兼顾初学者和有一定基础的读者文中会给出完整可运行的示例代码、核心配置说明、常见报错排查思路以及生产环境下的工程建议。只要跟着步骤走就能把系统从零搭起来。1. 系统定位与核心功能拆解在动手写代码之前先要把“智能天气出行服务系统”到底做什么讲清楚。传统天气 App 只会给你一张温度曲线和降水概率而本系统的核心价值在于把天气数据与出行决策结合起来借助 AI 大模型生成更贴近自然语言的建议。1.1 系统解决什么问题举个常见场景用户准备周末去郊外露营他需要知道“周六是否会下雨”“气温适不适合过夜”“有没有大风预警”。普通天气接口只能返回原始数据用户还要自己分析。而本系统可以让用户输入一句自然语言比如“周末去爬山怎么样”系统先拿到该地区未来几天的天气数据再把这些结构化数据作为上下文发送给大模型由大模型生成一段完整的天气分析和出行建议。这样设计的好处有两个对普通用户来说不用理解“相对湿度”“气压”等专业指标直接看一段人话即可。对开发者来说技术点非常清晰天气数据获取、数据持久化、大模型 API 调用、前端展示每一步都可以单独扩展。1.2 用户端功能模块整个系统从用户视角可以划分为以下几个核心模块模块名称功能说明用户注册登录支持用户注册、登录、退出保存用户的个性化出行记录天气查询支持按城市查询实时天气和未来 3-7 天预报AI 出行建议将天气数据传入大模型生成“穿衣建议”“出行适宜度”“注意事项”出行方案管理用户可以把 AI 生成的建议保存为“出行方案”方便后续查看历史记录查看自己查询过的天气和 AI 问答历史个人中心修改基础信息、管理收藏城市1.3 后台管理模块如果扩展成完整项目后台管理可以包括用户管理、天气数据健康度监控、大模型调用日志、系统参数配置等。对于毕业设计或中小型项目至少要实现数据统计和日志查看两个基础功能。本文以下示例会聚焦在用户端核心流程后台只做必要的保留。2. 技术栈与项目环境准备整个系统采用前后端不分离的服务端渲染模式使用 Thymeleaf 直接在后端拼装页面。这样做的好处是开发速度快不用单独部署前端工程。对新手友好不用处理跨域、Token 鉴权等复杂问题。核心业务逻辑全部在 Spring Boot 中方便理解整体流程。2.1 技术选型与版本说明由于每个人的开发环境不同这里给出本文中使用的基础环境你在实际搭建时按自己的环境微调即可JDK1.8 或 11建议 11语法更现代Spring Boot2.7.xThymeleafSpring Boot 2.7 默认集成 3.0.xMyBatis-Plus3.5.xMySQL5.7 或 8.0Maven3.6IDEIntelliJ IDEA注意如果你的项目使用的是 Spring Boot 3.x部分依赖和配置会有差异比如javax.*会变成jakarta.*下面的代码需要做对应调整。2.2 数据库准备先创建数据库这里以weather_travel_db为例CREATE DATABASE IF NOT EXISTS weather_travel_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;核心表有用户表、天气查询历史表、出行方案表。下面给出简化后的建表 SQL。-- 用户表 CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码加密存储, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4; -- 天气查询历史表 CREATE TABLE weather_history ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL COMMENT 用户ID, city VARCHAR(50) NOT NULL COMMENT 城市名, weather_data TEXT COMMENT 天气JSON数据, ai_advice TEXT COMMENT AI建议内容, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 出行方案表 CREATE TABLE travel_plan ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, plan_name VARCHAR(100) NOT NULL COMMENT 方案名称, city VARCHAR(50) NOT NULL COMMENT 目的地, travel_date VARCHAR(50) COMMENT 出行日期, content TEXT COMMENT AI生成的完整建议, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;2.3 Spring Boot 项目初始化可以通过 Spring Initializr 生成项目也可以在 IDEA 中直接创建 Spring Initializr 项目。选择依赖时至少添加Spring WebThymeleafMyBatis-PlusMySQL DriverLombok可选生成的pom.xml关键依赖如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies如果你的 Spring Boot 版本是 2.7.xmysql-connector-java的版本不需要手动指定但建议固定避免 Maven 拉取时出现不一致。2.4 配置文件在src/main/resources/application.yml中配置数据源和 MyBatis-Plus。server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/weather_travel_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 thymeleaf: cache: false # 自定义配置天气 API 和大模型 API weather: api: key: 你的天气API Key # 以和高天气为例实际要按服务商文档调整 url: https://api.qweather.com/v7/weather/now ai: api: key: 你的大模型API Key url: https://api.example.com/v1/chat/completions model: qwen-plus安全提醒application.yml中不要直接写真实密钥建议使用环境变量或者jasypt加密。文章为了方便演示直接写在配置中。3. 数据库表结构设计与实体类数据库表结构已经在上一节给出这里重点说明为什么要这样设计。3.1 用户表设计要点用户表包含username和password。密码必须加密存储推荐使用 BCrypt。很多毕业设计会把密码明文存在数据库这是非常危险的做法。实体类代码如下使用 MyBatis-Plus 注解映射package com.example.weather.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.time.LocalDateTime; Data TableName(sys_user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String nickname; private String phone; private LocalDateTime createTime; }3.2 天气历史表设计要点weather_data和ai_advice都使用TEXT类型。为什么不单独建表存字段因为不同天气服务商返回字段差异很大存 JSON 可以避免频繁改表也能保留原始数据用于复盘。当需要展示历史记录时直接读取weather_data字段在后端解析成对象再传给 Thymeleaf。4. 核心后端业务逻辑实现后端业务是系统的核心。我们按照“用户查询天气 - 保存历史记录 - 调用 AI 生成建议 - 保存出行方案”这条主线来拆分代码。4.1 天气接口调用的封装为了简化代码先创建一个WeatherApiClient组件负责调用第三方天气 API。这里使用 Spring 的RestTemplate发起 HTTP 请求。package com.example.weather.client; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; Component public class WeatherApiClient { Value(${weather.api.key}) private String apiKey; Value(${weather.api.url}) private String apiUrl; private final RestTemplate restTemplate new RestTemplate(); public String getWeatherNow(String cityId) { String url UriComponentsBuilder.fromHttpUrl(apiUrl) .queryParam(location, cityId) .queryParam(key, apiKey) .toUriString(); return restTemplate.getForObject(url, String.class); } }注意不同的天气服务商 API 有不同的响应格式和认证方式。以上代码以“城市ID Key”作为示例真实项目中需要根据所选服务商要求调整。4.2 AI 大模型接口封装AI 大模型一般提供 HTTP 接口我们同样封装一个AiChatClient。这里采用 OpenAI 兼容的chat/completions接口格式方便接入通义千问、DeepSeek、OpenAI 等模型。具体 URL、模型名和鉴权方式按你使用的服务商修改。package com.example.weather.client; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; Component public class AiChatClient { Value(${ai.api.key}) private String apiKey; Value(${ai.api.url}) private String apiUrl; Value(${ai.api.model}) private String model; private final RestTemplate restTemplate new RestTemplate(); private final ObjectMapper objectMapper new ObjectMapper(); /** * 根据天气数据和用户问题生成AI回复 */ public String chatWithWeather(String weatherInfo, String userQuestion) { // 构建提示词 String systemPrompt 你是智能天气出行助手请根据天气信息为用户提供出行建议回答要简洁、实用。; String userContent 天气数据 weatherInfo \n用户问题 userQuestion; MapString, Object requestBody new HashMap(); requestBody.put(model, model); requestBody.put(messages, List.of( Map.of(role, system, content, systemPrompt), Map.of(role, user, content, userContent) )); requestBody.put(temperature, 0.7); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); HttpEntityString entity new HttpEntity(toJsonString(requestBody), headers); ResponseEntityString response restTemplate.exchange(apiUrl, HttpMethod.POST, entity, String.class); return parseContent(response.getBody()); } private String toJsonString(Object obj) { try { return objectMapper.writeValueAsString(obj); } catch (Exception e) { throw new RuntimeException(JSON序列化失败, e); } } private String parseContent(String responseBody) { try { JsonNode root objectMapper.readTree(responseBody); return root.path(choices).get(0).path(message).path(content).asText(); } catch (Exception e) { throw new RuntimeException(解析大模型返回失败, e); } } }在调用大模型时需要把“天气原始数据”作为上下文传进去。这里的关键点是提示词设计要让模型知道你提供的是结构化天气信息并要求它只基于这些信息作答避免模型“编造天气”。4.3 用户业务逻辑 Service创建WeatherService负责编排完整流程。package com.example.weather.service; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.example.weather.client.AiChatClient; import com.example.weather.client.WeatherApiClient; import com.example.weather.entity.WeatherHistory; import com.example.weather.entity.TravelPlan; import com.example.weather.mapper.WeatherHistoryMapper; import com.example.weather.mapper.TravelPlanMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.List; Service public class WeatherService { Autowired private WeatherApiClient weatherApiClient; Autowired private AiChatClient aiChatClient; Autowired private WeatherHistoryMapper weatherHistoryMapper; Autowired private TravelPlanMapper travelPlanMapper; /** * 查询天气并生成AI建议 */ public JSONObject queryWeather(Long userId, String cityId, String cityName, String userQuestion) { // 1. 获取天气原始JSON String weatherData weatherApiClient.getWeatherNow(cityId); JSONObject weatherJson JSON.parseObject(weatherData); // 2. 调用AI生成建议 String aiAdvice aiChatClient.chatWithWeather(weatherData, userQuestion); // 3. 保存历史记录 WeatherHistory history new WeatherHistory(); history.setUserId(userId); history.setCity(cityName); history.setWeatherData(weatherData); history.setAiAdvice(aiAdvice); history.setCreateTime(LocalDateTime.now()); weatherHistoryMapper.insert(history); // 4. 组装返回结果 JSONObject result new JSONObject(); result.put(weather, weatherJson); result.put(aiAdvice, aiAdvice); return result; } /** * 保存出行方案 */ public void saveTravelPlan(Long userId, String planName, String city, String travelDate, String content) { TravelPlan plan new TravelPlan(); plan.setUserId(userId); plan.setPlanName(planName); plan.setCity(city); plan.setTravelDate(travelDate); plan.setContent(content); plan.setCreateTime(LocalDateTime.now()); travelPlanMapper.insert(plan); } /** * 查看历史记录 */ public ListWeatherHistory getHistory(Long userId) { QueryWrapperWeatherHistory queryWrapper new QueryWrapper(); queryWrapper.eq(user_id, userId).orderByDesc(create_time); return weatherHistoryMapper.selectList(queryWrapper); } }4.4 Controller 层设计Controller 负责接收页面请求调用 Service 后再返回 Thymeleaf 视图。这里需要实现两个主要页面首页搜索页index.html查询结果页result.html历史记录页history.html以首页为例package com.example.weather.controller; import com.alibaba.fastjson2.JSONObject; import com.example.weather.service.WeatherService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpSession; Controller public class WeatherController { Autowired private WeatherService weatherService; GetMapping(/) public String index() { return index; } PostMapping(/query) public String query( RequestParam String cityId, RequestParam String cityName, RequestParam String question, HttpSession session, Model model) { Long userId (Long) session.getAttribute(userId); if (userId null) { return redirect:/login; } JSONObject result weatherService.queryWeather(userId, cityId, cityName, question); model.addAttribute(weather, result.getJSONObject(weather)); model.addAttribute(aiAdvice, result.getString(aiAdvice)); model.addAttribute(cityName, cityName); return result; } GetMapping(/history) public String history(HttpSession session, Model model) { Long userId (Long) session.getAttribute(userId); if (userId null) { return redirect:/login; } model.addAttribute(histories, weatherService.getHistory(userId)); return history; } }5. 前端页面与 Thymeleaf 渲染Thymeleaf 的核心优势是服务端直接渲染 HTML在页面中通过th:text、th:each、th:if等标签展示数据。5.1 用户登录页面登录页面使用Bootstrap美化并加入一个简单的分享按钮。代码示例如下!DOCTYPE html html langzh xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title登录 - 智能天气出行服务系统/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet /head body classbg-light div classcontainer mt-5 stylemax-width: 400px; div classcard shadow div classcard-body h3 classcard-title text-center mb-4用户登录/h3 form th:action{/login} methodpost div classmb-3 label classform-label用户名/label input typetext nameusername classform-control required /div div classmb-3 label classform-label密码/label input typepassword namepassword classform-control required /div div th:if${error} classalert alert-danger th:text${error}/div button typesubmit classbtn btn-primary w-100登录/button /form p classmt-3 text-center 还没有账号a th:href{/register}去注册/a /p /div /div /div /body /html5.2 首页天气查询首页提供城市选择和问题输入。为了让用户直观地选择城市可以使用下拉框也可以集成前端城市级联选择插件。这里先用一个简单的下拉框演示!DOCTYPE html html langzh xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title智能天气出行服务系统/title link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.3.0/dist/css/bootstrap.min.css relstylesheet /head body div classcontainer mt-4 div classtext-center mb-4 h1智能天气出行服务系统/h1 p classtext-muted接入 AI 大模型帮你解读天气规划出行/p /div div classcard shadow-sm div classcard-body form th:action{/query} methodpost div classrow div classcol-md-4 label classform-label选择城市/label select namecityId idcityId classform-select required option value101010100北京/option option value101020100上海/option option value101280101广州/option option value101280601深圳/option option value101040100重庆/option /select /div div classcol-md-4 label classform-label城市名称/label input typetext namecityName idcityName classform-control value北京 required /div div classcol-md-4 label classform-label你的出行问题/label input typetext namequestion classform-control value明天适合出去玩吗 required /div /div div classmt-3 text-end button typesubmit classbtn btn-primary获取 AI 出行建议/button /div /form /div /div /div /body /html5.3 结果页展示结果页需要同时展示天气卡片和 AI 建议卡片。天气卡片解析后端传入的 JSON 对象展示温度、天气现象、风速等。div classcard mb-3 div classcard-header实时天气 - span th:text${cityName}/span/div div classcard-body div classrow text-center div classcol-3 div classfs-1 th:text${weather.now.temp} °C/div span classtext-muted温度/span /div div classcol-3 div classfs-4 th:text${weather.now.text}/div span classtext-muted天气/span /div div classcol-3 div classfs-4 th:text${weather.now.windDir} ${weather.now.windScale}/div span classtext-muted风/span /div div classcol-3 div classfs-4 th:text${weather.now.humidity} %/div span classtext-muted湿度/span /div /div /div /div div classcard div classcard-headerAI 出行建议/div div classcard-body p classtext-muted span th:text${aiAdvice}/span /p form th:action{/savePlan} methodpost input typehidden namecity th:value${cityName} input typehidden namecontent th:value${aiAdvice} div classmb-2 input typetext nameplanName classform-control placeholder给方案起个名字例如周末露营计划 required /div button typesubmit classbtn btn-success保存为出行方案/button /form /div /div5.4 历史记录页历史记录页使用th:each遍历列表用户可以查看之前查询过的内容和 AI 回复。div classcontainer mt-4 h3历史查询记录/h3 table classtable table-striped thead tr th城市/th thAI建议/th th查询时间/th /tr /thead tbody tr th:eachh : ${histories} td th:text${h.city}/td td th:text${h.aiAdvice}/td td th:text${#temporals.format(h.createTime, yyyy-MM-dd HH:mm)}/td /tr /tbody /table a th:href{/} classbtn btn-primary返回查询/a /div6. AI 大模型提示词设计要点本系统最容易被忽视的地方就是提示词。很多开发者以为把天气 JSON 丢给大模型就能返回满意答案实际上模型很容易被一堆字段搞晕输出过于啰嗦或者偏离主题。推荐把天气数据先“格式化”成一段人类可读的文字再拼入用户问题。下面是一个更专业的提示词构造示例String weatherSummary String.format( 当前城市%s实时温度%s℃天气现象%s风向%s风力%s级相对湿度%s%%。, cityName, weatherJson.getJSONObject(now).getString(temp), weatherJson.getJSONObject(now).getString(text), weatherJson.getJSONObject(now).getString(windDir), weatherJson.getJSONObject(now).getString(windScale), weatherJson.getJSONObject(now).getString(humidity) );然后调用chatWithWeather时把所有信息拼起来请根据以下天气情况回答用户的问题{weatherSummary} 用户问题{question} 要求 1. 只基于给出的天气信息回答 2. 先给出结论再给出具体建议 3. 建议控制在100字以内。这样大模型生成的回答会稳定很多也更适合直接展示在页面上。7. 用户注册登录模块7.1 密码加密与登录校验密码加密使用BCryptPasswordEncoder。在注册时加密密码在登录时用matches方法校验。依赖 Spring Security 的 crypto 包只需要引入spring-security-crypto不需要把整个 Spring Security 引入进来避免自动拦截所有请求。在pom.xml中加入dependency groupIdorg.springframework.security/groupId artifactIdspring-security-crypto/artifactId /dependency注意Spring Boot 2.7.x 会自动管理该依赖的版本。注册逻辑示例Service public class UserService { Autowired private UserMapper userMapper; private final BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); public boolean register(String username, String password, String nickname) { User user new User(); user.setUsername(username); user.setPassword(encoder.encode(password)); user.setNickname(nickname); return userMapper.insert(user) 0; } public User login(String username, String rawPassword) { User user userMapper.selectOne(new QueryWrapperUser().eq(username, username)); if (user ! null encoder.matches(rawPassword, user.getPassword())) { return user; } return null; } }7.2 Session 登录状态管理由于使用 Thymeleaf 服务端渲染最方便的做法是把用户 ID 存在 Session 中。PostMapping(/login) public String doLogin(String username, String password, HttpSession session, Model model) { User user userService.login(username, password); if (user null) { model.addAttribute(error, 用户名或密码错误); return login; } session.setAttribute(userId, user.getId()); session.setAttribute(nickname, user.getNickname()); return redirect:/; }注意Session 有超时时间生产环境建议配置合理的超时时长也可以在 Controller 中用拦截器校验登录状态避免每个方法重复判断。8. 系统运行与验证完成以上代码后就可以启动项目验证功能了。8.1 启动 Spring Boot 项目在 IDEA 中运行主启动类看到类似下面的日志表示启动成功Tomcat started on port(s): 8080 (http) Started WeatherApplication in 3.2 seconds然后访问http://localhost:8080/login使用注册好的账号登录。8.2 验证天气查询流程在首页选择城市输入问题点击查询。如果所有配置正确页面会展示当天的天气数据同时下方显示 AI 生成的出行建议。如果 AI 返回超时或报错优先检查application.yml中的ai.api.url是否正确、API Key 是否有效。可用 Postman 单独测试该接口。8.3 验证历史记录在结果页点击“保存为出行方案”后历史记录页应能看到一条新增记录。刷新页面后数据不应该消失证明数据库写入成功。9. 常见问题与排查思路问题现象常见原因解决思路前端页面没有样式Bootstrap CDN 无法访问将样式文件下载到本地静态目录天气数据加载失败天气 API Key 错误或接口地址错误先用浏览器直接访问天气 API 的 URL检查返回结果AI 回复内容为空大模型接口返回格式不符合代码预期打印原始响应的 JSON确认choices[0].message.content是否存在AI 回复很慢大模型 API 本身响应慢或网络环境问题设置合理的超时时间或在服务端增加缓存中文乱码数据库连接 URL 没有设置characterEncodingutf8检查application.yml中的 JDBC 连接参数时间字段显示不正确时区未配置数据库URL添加serverTimezoneAsia/Shanghai9.1 天气 API 参数常见问题不同天气服务商对城市编码要求不同。比如和风天气使用LocationID或以经度,纬度查询而高德地图使用adcode。如果选择的城市返回空白大概率是城市编码不匹配可以先请求一次 API 查看返回的status和code字段。9.2 大模型接口鉴权格式目前主流大模型 API 大多采用Authorization: Bearer API_KEY的鉴权方式。但不同厂商也有兼容差异例如部分平台要求自定义api-key头。最好的办法是查看服务商的官方文档先使用 curl 或其他工具测试成功后再集成到 Java 代码中。10. 最佳实践与工程建议10.1 不要把密钥提交到代码仓库application.yml中如果写死了天气 API Key 和大模型 API Key一旦代码提交到 GitHub 很容易被泄露。建议weather: api: key: ${WEATHER_API_KEY} ai: api: key: ${AI_API_KEY}在 IDEA 的运行配置中设置环境变量或者在服务器部署时通过export设置。10.2 使用缓存减少 API 调用天气数据实时性要求不高可以缓存 10-30 分钟。大模型 API 每次调用都有成本建议对相同城市和相同问题做缓存命中缓存时直接返回结果。简单实现可以使用CaffeineCacheable(cacheNames weather, key #cityId) public String getWeatherCache(String cityId) { return weatherApiClient.getWeatherNow(cityId); }10.3 日志记录完整调用链建议在 AI 调用前后记录日志包括用户 ID、城市、请求耗时、返回状态等。这不仅能排查问题也能统计大模型使用成本。log.info(开始调用大模型用户ID{}城市{}, userId, cityName); long start System.currentTimeMillis(); String aiAdvice aiChatClient.chatWithWeather(weatherData, userQuestion); log.info(大模型调用完成耗时{}ms, System.currentTimeMillis() - start);10.4 防止 SQL 注入与恶意参数虽然示例使用了 MyBatis-Plus 的QueryWrapper但如果有自定义 SQL一定要使用#{}参数占位符避免拼接字符串。10.5 生产环境建议使用 HTTPS涉及用户登录和 Session 的网站生产环境必须配置 HTTPS防止密码和 Session ID 被中间人截获。10.6 关于毕业设计的扩展方向如果这个项目是用来做毕业设计可以在基础功能上增加ECharts 图表展示未来一周温度趋势。使用 WebSocket 推送恶劣天气预警。增加 Redis 缓存和 RabbitMQ 异步生成 AI 建议。使用 JWT 替换 Session支持小程序端访问。集成地图 API展示出行路线和周边景点。这些扩展点都能明显提升项目的创新性和工程能力在答辩时是很好的加分项。11. 总结这套基于 Spring Boot Thymeleaf AI 的智能天气出行服务系统核心链路并不复杂前端页面提交城市与问题 - 后端请求天气 API 获取结构化数据 - 调用大模型生成出行建议 - 保存并展示结果。真正的难点在于把天气数据转换成人话、把大模型接口封装得足够健壮、以及在实际项目中处理好密钥、日志、缓存等工程细节。如果你是从零开始学习 Spring Boot建议先完全跑通本文的示例再尝试把 AI 提示词优化得更好比如结合未来 3 天预报进行出行决策。当你把核心流程跑通后加一个“收藏城市”或“导出方案”功能会非常顺手。如果这篇文章对你有帮助可以收藏备用也欢迎在实践中遇到问题后继续查缺补漏。技术就是这样多动手跑通一个完整闭环比看十遍教程都有效。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →