如果你正在准备计算机毕业设计,又恰好被“AI + 大模型”这类字眼吸引,那你大概率会卡在同一个问题上:AI功能到底怎么落地?是接一个现成接口,还是要自己训练模型?如果只是简单调用一个API,论文里怎么写才不像“硬蹭”?如果用传统Java技术栈,又怎么在答辩时把“智能”两个字讲清楚?
这篇文章就以一个非常典型的毕业设计课题——基于SpringBoot + Thymeleaf + AI的智能社区服务管理系统为例,把整个项目的选题逻辑、功能设计、环境配置、核心代码、验证方式、答辩准备全部拆开讲一遍。这不是一篇“观感很炫”的泛泛介绍,而是一条从零把系统跑通、把论文写厚、把答辩讲明白的完整路径。
文章适合三类读者:正在选毕业设计题目的在校生,已经买好或下载好源码但不知道怎么讲解的开发者,以及想用“SpringBoot + AI接口”做课程设计但停留在概念阶段的人。读完你会有两个直接收获:第一,知道这套系统里AI到底应该放在哪些功能点上才算合理;第二,能照着文章把项目跑起来,并理解每一块代码为什么这么写。
1. 为什么“SpringBoot + Thymeleaf + AI”是毕业设计的合理组合
先说结论:这个组合的价值在于,它用最低的工程复杂度,覆盖了毕业设计最看重的东西——完整的业务闭环、合理的技术栈、加分的前沿应用。
1.1 技术栈难度刚好踩在“能讲清”的位置
SpringBoot是Java后端开发的事实标准,用它可以快速实现REST接口、事务管理、参数校验、统一异常处理。Thymeleaf是SpringBoot官方推荐的模板引擎,它能让页面在后端渲染完成,不需要额外搭建Vue或React工程。对大多数计算机专业学生来说,这种“后端渲染页面”的模式更容易理解,也更容易在论文里画出清晰的请求流程图。
AI大模型部分,不需要自己训练模型,而是通过HTTP接口调用大模型服务,把社区的业务数据和大模型的生成能力结合起来。这一点非常重要:毕业设计评审老师看的不是你的模型表现,而是你有没有用工程手段把AI能力集成进一个真实系统。
1.2 它确实解决了社区管理的真实痛点
传统社区服务管理通常依赖人工处理报修、投诉、咨询、通知等事务。问题在于:报修分类靠人判断,投诉内容难以汇总分析,居民常见问题反复人工回答。引入AI之后,系统可以做三件可以写进论文的事情:
- 居民输入一段自然语言描述,AI自动识别报修类型并生成工单摘要。
- 居民向社区助手提问,AI基于预设的社区知识库给出回答。
- 系统对投诉建议做文本分类和情绪判断,帮助管理员优先处理紧急问题。
这三件事都围绕“社区服务”场景展开,既有业务价值,又不需要复杂的模型训练,适合在本科阶段完成。
1.3 对读者最实用的建议
如果你已经有一个现成的源码包,不要急着改功能,先按“管理员、工作人员、居民”三个角色把系统跑通,弄清楚每个角色的核心页面和数据流转。如果你打算自己从零做,就按本文第二章的功能清单去拆模块,不要一上来就写代码。
2. 系统功能结构与模块设计
一个合格的智能社区服务管理系统,至少要包含两个端:居民端和管理端。因为Thymeleaf是服务端渲染,所以两个端在同一个SpringBoot工程里,通过不同URL路径区分。
2.1 功能模块清单
| 模块名称 | 面向角色 | 核心功能 | AI介入点 |
|---|---|---|---|
| 居民信息管理 | 管理员 | 居民信息增删改查、房屋绑定、家庭成员管理 | 无 |
| 报修工单管理 | 居民、工作人员 | 提交报修、工单派发、维修进度、完工回访 | AI识别报修分类、生成工单摘要 |
| 投诉建议管理 | 居民、管理员 | 提交投诉、处理反馈、历史查询 | AI文本分类、情绪判断 |
| 智能问答助手 | 居民 | 社区政策问答、办事流程咨询 | 大模型对话接口 |
| 公告通知管理 | 管理员 | 发布公告、置顶、过期处理 | 无 |
| 缴费管理 | 居民、工作人员 | 物业费账单、缴费记录、欠费提醒 | 无 |
| 数据统计看板 | 管理员 | 工单量、投诉量、处理时长统计 | 无 |
这里的核心判断是:AI不要放在所有模块里。只要在报修、投诉、问答三个模块出现,论文里就能形成一条“AI应用”主线,其他模块负责撑起系统的完整性。
2.2 业务流程设计
以报修工单为例,推荐按这样的流程设计:
- 居民在页面提交报修,填写问题描述、联系方式和地址。
- 系统调用AI接口,对问题描述进行分类(如水电维修、设备故障、环境卫生)。
- 根据分类结果自动匹配维修人员,生成工单记录。
- 工作人员更新处理状态,系统记录处理时长。
- 居民查看进度,完成后进行评价。
这个流程的特点是:AI不是“锦上添花”,而是参与到了工单创建的前置环节,这让“智能”两个字有了具体落点。
2.3 数据库表设计思路
建议至少设计这些核心表:
community_resident:居民表,字段包括id、姓名、手机号、楼栋、房间号、状态。repair_order:报修工单表,字段包括id、居民id、问题描述、AI分类结果、工单摘要、状态、处理人、创建时间、完成时间。complaint_info:投诉建议表,字段包括id、居民id、投诉内容、AI情绪标签、处理状态、回复内容。qa_record:智能问答记录表,字段包括id、居民id、问题内容、回答内容、AI模型标识、创建时间。notice_info:公告表,字段包括id、标题、内容、发布时间、状态。payment_record:缴费表,字段包括id、居民id、费用类型、金额、状态、缴费时间。
表与表之间用resident_id关联,工单和投诉表都要冗余一个AI分析结果字段,这样在论文的数据库设计章节里可以专门说明“该字段用于存储AI接口返回的结构化结果”。
3. 核心概念与技术选型解析
这一节解释整个项目中容易被搞混的几个概念,理解了它们,你后面调试代码才不会一脸懵。
3.1 SpringBoot在项目中到底负责什么
SpringBoot负责的是“接住请求、处理业务、返回页面或数据”。当一个居民在浏览器点击“提交报修”,请求会经过Controller层,调用Service层的业务逻辑,再通过Mapper层操作数据库,最后把结果返回给Thymeleaf模板渲染成HTML。SpringBoot把这一整套流程用自动配置的方式简化了,你不需要自己搭建Tomcat、手动处理Spring容器的XML配置。
3.2 Thymeleaf模板引擎的核心逻辑
Thymeleaf的核心是在HTML标签里写服务端变量和逻辑。它和传统JSP的区别是:Thymeleaf模板本身是合法HTML,直接用浏览器打开也能看到静态效果,只有通过SpringBoot访问时才会把th:text、th:each这些属性解析成真实数据。
在智能社区系统里,你会在这些地方用到Thymeleaf:
- 工单列表页用
th:each遍历工单集合。 - 详情页用
th:text显示AI生成的分类结果。 - 新增页面用
th:action指定表单提交地址。 - 登录页用
th:if判断是否显示错误提示。
如果你之前只写过前后端分离项目,第一次接触Thymeleaf可能会不适应“页面里混Java变量”的写法。但只要记住一点:Thymeleaf可以帮助你在一个SpringBoot工程里完成页面跳转和数据展示,不用再单独启动一个前端项目。
3.3 AI大模型接入的三种理解层次
在毕业设计层面,AI接入有三种层次,对应不同的论文写法和工作量:
| 层次 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 第一层:接口调用 | 通过HTTP调用大模型API,发送提示词得到返回结果 | 实现简单,稳定可靠 | 需要联网,可能需要API密钥 |
| 第二层:本地部署轻量模型 | 在本地或服务器部署开源模型,通过服务调用 | 不依赖外部服务 | 对硬件有要求,部署成本高 |
| 第三层:微调垂直模型 | 用社区语料微调模型 | 效果更贴合场景 | 工作量过大,不适合毕设 |
对本科毕业设计来说,第一层是性价比最高的选择。论文里你可以这样写:“系统通过HTTP协议调用大模型开放接口,采用提示词工程引导模型输出结构化结果,无需自行训练模型,降低了工程实现难度。”这句话既诚实,又能体现你理解了大模型应用的基本范式。
3.4 为什么不用Vue + SpringBoot前后端分离
这里想给一个反向判断:前后端分离确实更像企业项目,但对多数毕业设计来说,会引入跨域配置、Token认证、前端打包、接口联调等一系列额外复杂度。如果你论文重点放在AI应用和业务流程上,用Thymeleaf做服务端渲染可以节省大量时间。答辩时,老师更关心你能不能把系统的核心流程讲清楚,而不是你是否用了Vue3。
4. 环境准备与项目初始化
开始写代码之前,先把环境确认一遍。版本号不需要追求最新,稳定能跑才是第一位。
4.1 推荐环境清单
| 工具 | 版本建议 | 说明 |
|---|---|---|
| JDK | 1.8 或 11 | 如果你用的是SpringBoot 2.x,JDK 1.8完全够用 |
| Maven | 3.6+ | 依赖管理工具 |
| MySQL | 5.7 或 8.0 | 数据库,字符集用utf8mb4 |
| IDEA | 2021及以上 | 推荐直接用IDEA打开项目 |
| Lombok插件 | 最新即可 | 简化实体类代码 |
注意,如果你的项目使用SpringBoot 3.x,JDK必须使用17及以上。材料里出现了“springboot版本太高”这个热词,现实中很多同学确实会遇到版本过高导致javax包找不到的问题。更稳妥的做法是:如果你的教材和网上教程大部分是javax开头,就用SpringBoot 2.7.x,对应的包名是javax;如果你希望用jakarta命名空间并体验更新特性,再选择SpringBoot 3.x。本文示例按SpringBoot 2.7.x的常见写法来展示,这也是当前毕业设计项目中使用最广的版本范围。
4.2 创建一个SpringBoot项目
使用IDEA新建工程,选择Spring Initializr,填写项目信息后,选择以下依赖:
- Spring Web
- Thymeleaf
- MyBatis Framework
- MySQL Driver
- Lombok
创建完成后,在pom.xml里确保包含类似下面的依赖(版本以你自己的Maven仓库为准):
<!-- 文件路径:pom.xml --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>4.3 配置文件准备
在src/main/resources/application.yml中写入基础配置:
spring: datasource: url: jdbc:mysql://localhost:3306/community_smart?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html mybatis: mapper-locations: classpath:/mapper/*.xml type-aliases-package: com.example.community.entity configuration: map-underscore-to-camel-case: true这里有两个建议:第一,thymeleaf.cache=false在开发阶段一定要开,否则修改页面模板后需要重启项目才能看到效果;第二,map-underscore-to-camel-case=true可以让数据库字段repair_type自动映射到实体类属性repairType,省去大量写ResultMap的时间。
4.4 AI接口配置
AI能力通过HTTP接口调用,不要在代码里硬编码密钥。在配置文件中单独维护:
ai: api-url: https://your-model-endpoint/v1/chat/completions api-key: your-api-key model: your-model-name timeout-seconds: 30当你在真实项目中接入时,只需要把这三项配置替换成你申请到的服务信息。这个小设计在论文里可以写成“系统采用配置化方式管理AI服务参数,便于后续更换不同模型供应商”。
5. 数据库建表与初始化脚本
准备好数据库,创建一个名为community_smart的数据库,然后执行下面这些核心建表SQL。这里只列出最关键的三张表,其他表按同样的风格扩展即可。
-- 居民表 CREATE TABLE `community_resident` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `name` varchar(50) NOT NULL COMMENT '姓名', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `building` varchar(20) DEFAULT NULL COMMENT '楼栋', `room_number` varchar(20) DEFAULT NULL COMMENT '房间号', `status` tinyint(4) DEFAULT 1 COMMENT '状态 1正常 0禁用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='居民表'; -- 报修工单表 CREATE TABLE `repair_order` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `resident_id` bigint(20) DEFAULT NULL COMMENT '居民id', `description` text COMMENT '问题描述', `ai_category` varchar(50) DEFAULT NULL COMMENT 'AI识别分类', `ai_summary` varchar(255) DEFAULT NULL COMMENT 'AI生成摘要', `handler` varchar(50) DEFAULT NULL COMMENT '处理人', `status` tinyint(4) DEFAULT 0 COMMENT '状态 0待处理 1处理中 2已完成', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `finish_time` datetime DEFAULT NULL COMMENT '完成时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='报修工单表'; -- 问答记录表 CREATE TABLE `qa_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `resident_id` bigint(20) DEFAULT NULL COMMENT '居民id', `question` text COMMENT '问题内容', `answer` text COMMENT '回答内容', `model_name` varchar(100) DEFAULT NULL COMMENT '模型标识', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问答记录表';建表之后,插入几条测试数据:
INSERT INTO `community_resident` (`name`, `phone`, `building`, `room_number`, `status`) VALUES ('张三', '13800001111', '1栋', '101', 1); INSERT INTO `repair_order` (`resident_id`, `description`, `ai_category`, `ai_summary`, `status`, `create_time`) VALUES (1, '厨房水管漏水,地面积水严重', '水暖维修', '厨房水管漏水需尽快上门处理', 0, NOW());6. 核心代码实现:从AI调用到页面展示
这一部分是整篇博文的核心。代码不一定需要完全照抄,但理解每一处实现逻辑能帮你应对答辩时的追问。
6.1 实体类定义
根据数据库表创建实体类。使用Lombok简化getter/setter:
// 文件路径:src/main/java/com/example/community/entity/RepairOrder.java package com.example.community.entity; import lombok.Data; import java.time.LocalDateTime; @Data public class RepairOrder { private Long id; private Long residentId; private String description; private String aiCategory; private String aiSummary; private String handler; private Integer status; private LocalDateTime createTime; private LocalDateTime finishTime; }6.2 AI服务调用层
编写一个AiService,负责调用大模型HTTP接口。这里使用Spring的RestTemplate,简单直接,容易在论文中解释:
// 文件路径:src/main/java/com/example/community/service/AiService.java package com.example.community.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; @Service public class AiService { private final RestTemplate restTemplate = new RestTemplate(); @Value("${ai.api-url}") private String apiUrl; @Value("${ai.api-key}") private String apiKey; @Value("${ai.model}") private String model; public String chat(String prompt) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); Map<String, Object> requestBody = new HashMap<>(); requestBody.put("model", model); // 这里构造一个简单的消息列表 Map<String, String> message = new HashMap<>(); message.put("role", "user"); message.put("content", prompt); requestBody.put("messages", new Object[]{message}); HttpEntity<Map<String, Object>> request = new HttpEntity<>(requestBody, headers); try { Map<String, Object> response = restTemplate.postForObject(apiUrl, request, Map.class); if (response != null && response.get("choices") != null) { Object choicesObj = response.get("choices"); if (choicesObj instanceof java.util.List<?> choices && !choices.isEmpty()) { Map<?, ?> firstChoice = (Map<?, ?>) choices.get(0); Map<?, ?> msg = (Map<?, ?>) firstChoice.get("message"); return (String) msg.get("content"); } } } catch (Exception e) { // 在生产项目中应该记录日志并做降级处理 return "AI服务暂时不可用,请稍后再试"; } return "AI服务返回为空"; } }这个类有两点值得在论文中说明:一是通过@Value读取配置,避免硬编码;二是用try-catch做降级,保证AI服务故障时主流程不崩溃。
6.3 报修工单业务实现:AI分类 + 摘要
在居民提交报修时,先调用AI把问题描述转成结构化的分类和摘要。提示词设计是这里的关键:
// 文件路径:src/main/java/com/example/community/service/RepairOrderService.java package com.example.community.service; import com.example.community.entity.RepairOrder; import com.example.community.mapper.RepairOrderMapper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.LocalDateTime; @Service public class RepairOrderService { @Resource private RepairOrderMapper repairOrderMapper; @Resource private AiService aiService; public void createOrder(RepairOrder order) { // 构造提示词,要求模型返回JSON格式 String prompt = "你是一个社区报修工单分类助手。请对以下问题描述进行分类," + "分类结果只能是:水暖维修、电力维修、环境卫生、设备故障、其他。" + "并生成一句不超过20字的工单摘要。" + "请以JSON格式返回:{\"category\":\"分类\",\"summary\":\"摘要\"}\n" + "问题描述:" + order.getDescription(); String aiResult = aiService.chat(prompt); // 对AI返回结果做最简单的解析,当前仅做演示 try { String category = "其他"; String summary = order.getDescription(); if (aiResult.contains("分类")) { int start = aiResult.indexOf("category"); if (start > 0) { category = aiResult.substring(aiResult.indexOf(":", start) + 2, aiResult.indexOf(",", start)).replace("\"", ""); } int summaryIndex = aiResult.indexOf("summary"); if (summaryIndex > 0) { summary = aiResult.substring(aiResult.indexOf(":", summaryIndex) + 2, aiResult.indexOf("}", summaryIndex)).replace("\"", ""); } } order.setAiCategory(category); order.setAiSummary(summary); } catch (Exception e) { order.setAiCategory("其他"); order.setAiSummary(order.getDescription()); } order.setStatus(0); order.setCreateTime(LocalDateTime.now()); repairOrderMapper.insert(order); } }这里的提示词设计有一个通用原则:要求模型输出JSON格式,而不是自然语言的自由文本。这样后续解析代码就简单、可控。如果AI返回的格式不对,代码会走catch分支兜底,保证工单依然能创建成功。
毕业设计答辩时,老师很可能会问:“如果AI接口请求失败怎么办?”你可以回答:系统在调用AI的环节做了异常捕获和降级处理,AI分类失败时工单仍然可以按“其他”分类正常提交,AI服务不可用不会阻塞居民的报修流程。这是一个非常加分的工程意识。
6.4 智能问答模块
智能问答模块的思路更简单:接收居民问题,拼接提示词,调用AI,返回答案,并把问题和答案保存到qa_record表:
@Controller @RequestMapping("/qa") public class QaController { @Resource private AiService aiService; @Resource private QaRecordMapper qaRecordMapper; @PostMapping("/ask") public String ask(String question, Long residentId, Model model) { String prompt = "你是一个社区智能助手。请结合社区服务场景回答以下问题:" + "如果问题涉及政策或流程,请先说明办理地点和所需材料;" + "如果不确定,请建议居民联系社区服务中心。\n" + "问题:" + question; String answer = aiService.chat(prompt); QaRecord record = new QaRecord(); record.setResidentId(residentId); record.setQuestion(question); record.setAnswer(answer); record.setModelName("community-ai-assistant"); record.setCreateTime(LocalDateTime.now()); qaRecordMapper.insert(record); model.addAttribute("question", question); model.addAttribute("answer", answer); return "qa_result"; } }6.5 Thymeleaf页面:工单提交页面
在src/main/resources/templates/repair_submit.html中写页面。这里只展示核心表单部分:
<!-- 文件路径:src/main/resources/templates/repair_submit.html --> <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>提交报修工单</title> </head> <body> <h2>提交报修工单</h2> <form th:action="@{/repair/submit}" method="post"> <input type="hidden" name="residentId" value="1" /> <div> <label>问题描述</label> <textarea name="description" rows="4" cols="50" required></textarea> </div> <div> <button type="submit">提交工单</button> </div> </form> </body> </html>6.6 Controller层整合
最后,写一个RepairController把页面和Service串起来:
// 文件路径:src/main/java/com/example/community/controller/RepairController.java package com.example.community.controller; import com.example.community.entity.RepairOrder; import com.example.community.service.RepairOrderService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; @Controller @RequestMapping("/repair") public class RepairController { @Resource private RepairOrderService repairOrderService; @GetMapping("/submit") public String submitPage() { return "repair_submit"; } @PostMapping("/submit") public String doSubmit(RepairOrder order) { repairOrderService.createOrder(order); return "redirect:/repair/list"; } @GetMapping("/list") public String list(org.springframework.ui.Model model) { // 这里简化处理:只查询全部工单,实际项目应该分页 model.addAttribute("orders", null); return "repair_list"; } }实际项目中,repair/list应该调用Mapper查询数据库并放入Model。由于不同项目的Mapper实现方式不同,这里留出扩展点,重点演示的是“Controller -> Service -> AI调用 -> 数据落库”的调用链路。
7. 运行验证与效果检查
项目写完后,按下面的顺序验证,不要跳步。
7.1 启动项目
在IDEA中直接运行启动类CommunityApplication。看到类似下面的日志说明启动成功:
Tomcat started on port(s): 8080 (http) Started CommunityApplication in 3.2 seconds7.2 验证居民信息功能
启动浏览器,访问http://localhost:8080/resident/list,如果能正常显示居民列表,说明数据库连接、MyBatis映射、Thymeleaf模板渲染整条链路是通的。
7.3 验证报修工单AI分类
访问http://localhost:8080/repair/submit,填写“厨房水管漏水,地面积水严重”,提交后进入工单列表。如果工单状态正常创建,说明AI调用和降级逻辑都没有阻塞主流程。
如果配置了大模型接口,可以在数据库中执行:
SELECT id, description, ai_category, ai_summary FROM repair_order;正常情况下,ai_category应显示“水暖维修”,ai_summary应是一句简短摘要。
7.4 验证智能问答
访问问答页面,输入“办理居住证明需要带什么材料?”,提交后查看返回页面。如果页面有正常回答,说明AI接口连通。
7.5 排查第一原则
如果某一个页面报错,优先看IDEA控制台的完整异常堆栈,同时检查数据库名、用户名、密码是否和application.yml中一致。大部分SpringBoot项目启动失败,都是这三个配置项写得不对。
8. 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
启动报Failed to configure a DataSource | 数据库连接配置错误或没有创建数据库 | 检查application.yml中的url、username、password | 先创建数据库,再检查账号密码 |
页面报Whitelabel Error Page | 请求路径没有匹配到Controller,或Controller未返回正确视图 | 查看控制台日志,确认请求URL | 检查@RequestMapping路径,确认模板文件名 |
| Thymeleaf页面修改不生效 | 模板缓存未关闭 | 确认spring.thymeleaf.cache=false,重启项目 | 修改配置并重启,或按Ctrl+F9重新编译 |
| AI返回内容无法解析 | 模型返回了自然语言而不是JSON | 在AiService中打印原始返回结果 | 调整提示词,或使用更严格的解析逻辑 |
| 中文字符乱码 | 数据库字符集不是utf8mb4,或连接未指定编码 | 检查数据库建表语句和jdbc-url | 统一使用utf8mb4,并在jdbc-url中加characterEncoding=utf8 |
| SpringBoot版本过高导致javax包不存在 | 使用了SpringBoot 3.x但代码里用了javax | 检查pom.xml中spring-boot-starter-parent版本 | 改用2.7.x,或者把javax改为jakarta |
| 项目能启动但页面样式丢失 | 静态资源路径问题 | 查看浏览器控制台404文件 | 把css/js文件放到src/main/resources/static目录,页面用th:href="@{/css/style.css}"引用 |
这里的排查思路可以总结成一个通用模板:先看日志,再查配置,最后看代码。不要一报错就打开搜索引擎搜完整报错,先自己定位到具体是哪一层出了问题。
9. 最佳实践、论文写作与答辩建议
代码跑通只是第一步,毕业设计更重要的是把工作转化成论文和答辩语言。
9.1 工程层面:给代码留出“可讲性”
建议在项目里做三件事:
第一,在AI调用的地方加日志。比如打印请求耗时、模型名称、返回结果,这样论文里可以写“系统对AI调用耗时进行了监控,平均响应时间为xx秒”。
第二,把AI提示词集中管理。不要散落在各个Service类中,可以建一个PromptTemplate类,统一维护每个场景的提示词。答辩时可以强调:提示词方案是经过多次测试调整的,体现了对模型输出稳定性的考虑。
第三,把AI密钥放到配置文件,并使用环境变量注入。不要把真实密钥提交到代码仓库。
9.2 论文层面:把“调用API”写出工程深度
很多同学担心“只是调用API,论文会不会显得太浅”。这里给出一个提升深度的思路:
- 在“系统设计”章节专门写AI模块设计,包括提示词设计、超时处理、失败降级、JSON解析方案。
- 在“系统实现”章节加入报修自动分类的时序图或流程图,展示居民提交、AI分析、工单生成、派单处理四个阶段。
- 在“系统测试”章节设计对比测试:人工分类和AI分类的准确率对比、AI服务可用和不可用两种场景下的功能表现。
这样写出来的论文重点不再是“我调了一个接口”,而是“如何把不可控的大模型输出,转化为可控的业务数据”。
9.3 答辩层面:准备高频追问
下面这些问题建议提前想好答案:
- 为什么选择大模型接口而不是自己训练模型?答:社区业务对实时性要求高,预训练大模型具备通用能力,通过提示词约束即可满足需求,自训练模型成本高且周期长。
- AI判断错了怎么办?答:系统保留人工修改入口,工作人员可以修改分类结果,同时将AI输出作为辅助参考,不直接替代人工决策。
- 如果AI服务不可用,系统还能用吗?答:可以。系统在AI调用处做了异常捕获和降级处理,AI故障时工单会以默认分类提交,不会影响核心报修流程。
- 如何保证数据安全?答:数据库中的敏感字段采用参数绑定防止SQL注入,AI请求中的密钥通过配置文件管理,不暴露在前端代码中。
9.4 后续功能扩展方向
如果时间充足,可以在现有基础上扩展这些功能,让项目更有竞争力:
- 工单超时提醒:定时任务扫描超过48小时未处理的工单,自动通知管理员。
- 缴费到期提醒:通过定时任务生成待缴费清单。
- 访客预约:居民填写访客信息,生成访客二维码。
- 数据看板:使用ECharts展示每周工单量趋势、投诉类型分布。
结语
智能社区服务管理系统是一个很适合毕业设计的题目,它的业务场景真实、技术栈成熟、AI落点清晰。真正拉开分数差距的地方,不在于你用了多新的框架,而在于你能不能把“AI能力如何与业务结合”这件事讲透。按照本文的路径,你不仅能把系统跑通,还能在论文和答辩里回答出每一步设计背后的原因。后续如果你想把AI部分做得更深入,可以从提示词工程、模型效果评测、流式输出这三个方向继续探索。建议收藏备用,等你要开题或者写论文的时候,再回来对照一遍。