
实际做智能社区服务管理系统这类毕业设计时真正决定项目分数的不只是界面能跑通而是业务是否完整、技术栈是否匹配、AI 功能能否在答辩现场稳定演示。基于 SpringBoot Thymeleaf AI 的智能社区服务管理系统就是把住户管理、物业公告、报修工单、访客登记和缴费记录这些传统社区场景整合到一套 Web 应用中再通过大模型接口实现智能问答、办事引导和政策咨询。对于计算机相关专业的学生来说这套组合的价值在于SpringBoot 提供后端工程骨架Thymeleaf 承担服务端页面渲染AI 模块让项目具备当前招聘和答辩都比较看重的智能化亮点而且每个部分都能独立讲解、独立测试。本文按一个可复现的毕业设计项目来组织从需求拆解、环境准备、数据库建模、工程搭建、核心模块实现到 AI 助手接入、运行验证、常见问题排查和答辩准备完整说明这套系统应该怎么落地。文章中的代码和配置以 SpringBoot 2.7.x JDK 1.8 为基准同时会说明选择这个版本的原因以及升级到 SpringBoot 3.x 时需要改哪些地方。项目最终交付通常包含源码、设计论文LW、答辩 PPT 和讲解视频本文也会在最后给出每个交付物应该重点写什么、讲什么。1. 先理解智能社区服务管理系统要解决什么问题1.1 核心业务范围智能社区服务管理系统面向的是社区物业和业主两类用户。物业人员需要发布公告、处理业主提交的报修、审核访客申请、记录缴费信息业主需要查看公告、提交报修、预约访客、查询缴费记录遇到办事流程不清楚时能直接向系统提问。如果只做简单的增删改查项目在答辩时很容易被追问业务深度。建议至少划分出以下模块用户模块业主、物业、管理员三类角色登录注册、个人信息维护。公告模块物业发布社区通知业主查看公告列表和详情。报修模块业主提交报修工单物业处理并回填处理结果状态可跟踪。访客登记业主登记来访人员信息物业审核进出记录。缴费记录物业录入费用业主查询历史账单。AI 服务助手业主输入自然语言问题系统返回社区办事流程、常见政策或引导到对应功能。AI 服务助手是项目区别于普通 CRUD 系统的关键模块也是论文中“创新点”和“系统特色”部分最值得展开的内容。1.2 技术选型为什么是 SpringBoot Thymeleaf AI这个组合很适合毕业设计场景原因有三点。SpringBoot 是目前 Java 后端入门最友好的框架。它把 Tomcat、Spring MVC、数据源、事务等配置做了大量自动装配学生不需要手动写一堆 XML 配置就能把 Web 工程跑起来。同时 SpringBoot 也是企业招聘面试中的高频话题选择它意味着论文技术背景部分有足够多内容可写。Thymeleaf 是 SpringBoot 官方推荐的模板引擎采用服务端渲染。页面数据由 Controller 通过 Model 传入HTML 中通过th:text、th:each等属性渲染。相比前后端分离的 Vue 项目Thymeleaf 少了跨域、Token 鉴权、CORS 配置这些额外概念开发链路更短更适合作为毕业设计的第一套完整 Web 项目。AI 模块是大模型应用在垂直场景的典型实践。这里的“AI”不是要自己去训练模型而是通过调用大模型 API把用户问题和服务知识库组织成提示词让模型以社区助手的角色返回答案。对毕业设计来说这个思路既能规避训练成本又能完整展示“大模型应用开发”的流程。下表是三种页面技术路线在毕业设计场景下的对比技术路线学习成本答辩技术点适用情况Thymeleaf 服务端渲染低Spring MVC、模板引擎、Session 认证快速完成全栈演示适合时间紧的项目Vue 前后端分离中高JWT、CORS、接口设计、前端构建项目周期充足想体现前后端分离能力纯 JSP Servlet低但偏旧技术传统难以体现新意学校指定要求时使用1.3 毕业设计的交付物边界这类项目常见的完整交付清单包括四项源码、论文常简称为 LW、答辩 PPT、讲解视频。源码负责可运行论文负责解释设计过程和结果PPT 负责在有限时间内呈现重点讲解视频用于无法现场演示时的补充说明。论文建议按“绪论、需求分析、系统设计、系统实现、系统测试、总结”的结构组织。其中系统设计要包含用例图、功能模块图、数据库 E-R 图和表结构说明系统实现部分要写清楚关键技术点例如登录拦截器如何工作、AI 请求报文如何组装、报修状态如何流转。测试部分不需要写大量页面截图更值得写的是测试用例表、边界输入和异常场景的验证结果。2. 环境准备与数据库建模2.1 开发环境版本选择版本选择是第一个容易踩坑的地方。搜索“SpringBoot 版本太高”出现的问题绝大多数是 SpringBoot 3.x 与旧版依赖不兼容导致的。SpringBoot 3.x 要求 JDK 17 及以上并且把javax.servlet迁移到了jakarta.servlet如果项目里用了旧版本的 MyBatis-Plus、Shiro 或其它第三方库直接升级会出现大量编译错误。推荐按下面这套组合搭建组件推荐版本说明JDK1.8 或 17SpringBoot 2.7 建议用 JDK 1.83.x 必须用 17SpringBoot2.7.18最后一个支持 JDK 1.8 的稳定线生态兼容性最好MyBatis-Plus3.5.3.1与 SpringBoot 2.7 兼容良好MySQL5.7 或 8.0开发环境 5.7生产环境建议 8.0Maven3.6.3 以上版本过低可能导致依赖解析异常IDEA2022 及以上支持 Lombok 插件和 Spring 工程初始化这套组合的好处是资料多、报错少、网上解决方案丰富。如果学校明确要求新版本再迁移到 SpringBoot 3.x迁移重点就是javax到jakarta的包名替换以及 MyBatis-Plus 使用适配新版本的mybatis-plus-spring-boot3-starter。2.2 数据库表结构设计数据库名建议使用community_service统一使用utf8mb4字符集避免中文乱码。核心表至少包括用户表、公告表、报修工单表、访客登记表和 AI 问答记录表。用户表设计如下CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 登录密码加密后存储, real_name VARCHAR(50) DEFAULT COMMENT 真实姓名, phone VARCHAR(20) DEFAULT COMMENT 联系电话, user_type TINYINT DEFAULT 0 COMMENT 用户类型0 业主1 物业2 管理员, avatar VARCHAR(255) DEFAULT COMMENT 头像地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;报修工单表是业务核心状态字段建议使用字符串枚举值相比数字类型更直观CREATE TABLE repair_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 工单ID, user_id BIGINT NOT NULL COMMENT 提交人ID, title VARCHAR(100) NOT NULL COMMENT 报修标题, content VARCHAR(500) DEFAULT COMMENT 问题描述, address VARCHAR(200) DEFAULT COMMENT 维修地址, status VARCHAR(20) DEFAULT PENDING COMMENT 状态PENDING待处理PROCESSING处理中DONE已完成CANCELED已取消, handler_id BIGINT DEFAULT NULL COMMENT 处理人ID, handle_note VARCHAR(500) DEFAULT COMMENT 处理回执, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 提交时间, handle_time DATETIME DEFAULT NULL COMMENT 处理时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修工单表;AI 问答记录表用于保存用户提问和模型回答既能做历史记录展示也能为论文中的“数据统计”提供素材CREATE TABLE ai_chat_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, user_id BIGINT NOT NULL COMMENT 提问用户ID, question VARCHAR(500) NOT NULL COMMENT 用户问题, answer TEXT COMMENT AI回答, cost_ms BIGINT DEFAULT 0 COMMENT 接口耗时单位毫秒, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 提问时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENTAI问答记录表;注意表设计中不要把password字段设计成明文存储。论文里如果写到“用户密码加密存储”就要在实现中真的使用 BCrypt 或其它哈希算法否则答辩时被追问代码会很难收场。2.3 项目目录分层设计建议采用常见的 Controller、Service、Mapper 三层结构并在此基础上增加 vo 包用于接口返回对象、config 包用于配置类、interceptor 包用于拦截器community-service ├── src/main/java/com/community │ ├── CommunityApplication.java │ ├── config │ │ ├── WebConfig.java │ │ └── AiConfig.java │ ├── controller │ │ ├── LoginController.java │ │ ├── AnnouncementController.java │ │ ├── RepairController.java │ │ ├── VisitorController.java │ │ └── AiController.java │ ├── service │ │ ├── UserService.java │ │ ├── RepairService.java │ │ ├── AiChatService.java │ │ └── ServiceQuestionService.java │ ├── mapper │ │ ├── UserMapper.java │ │ ├── RepairMapper.java │ │ └── AiChatLogMapper.java │ ├── entity │ │ ├── User.java │ │ ├── Announcement.java │ │ └── RepairOrder.java │ ├── vo │ │ └── Result.java │ └── interceptor │ └── LoginInterceptor.java └── src/main/resources ├── application.yml ├── mapper │ └── RepairMapper.xml ├── static │ ├── css │ └── js └── templates ├── login.html ├── index.html ├── repair-list.html └── ai-chat.html分层设计不只是为了好看。论文里需要画“系统架构图”分层结构可以直接对应绘制答辩时被问到“一个请求从页面到数据库经过了哪些层”也能按 Controller、Service、Mapper 的顺序讲清楚。3. 搭建 SpringBoot Thymeleaf 基础工程3.1 创建工程并配置 pom.xml使用 IDEA 的 Spring Initializr 创建工程时Group 可以填com.communityArtifact 填community-service。依赖勾选 Spring Web、Thymeleaf、Lombok再手动加入 MySQL 驱动和 MyBatis-Plus。pom.xml 的核心依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent properties java.version1.8/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意mysql-connector-java在 SpringBoot 2.7 中由父依赖统一管理不需要写版本号。如果使用 SpringBoot 3.xMySQL 驱动的坐标要改成com.mysql:mysql-connector-j。3.2 application.yml 核心配置在src/main/resources/application.yml中配置端口、数据源、Thymeleaf 和 AI 相关参数server: port: 8080 servlet: encoding: charset: UTF-8 force: true spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/community_service?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: root thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true global-config: db-config: id-type: auto ai: endpoint: ${AI_ENDPOINT:https://api.example.com/v1/chat/completions} api-key: ${AI_API_KEY:} model: ${AI_MODEL:qwen-plus}spring.thymeleaf.cache: false是开发阶段必须保留的配置。如果设置成 true修改 HTML 后需要重启应用才能看到变化排查问题时非常浪费时间。mybatis-plus.configuration.map-underscore-to-camel-case: true可以让数据库的create_time自动映射到 Java 实体类的createTime字段减少手写映射配置。AI 参数使用${AI_ENDPOINT:默认值}的形式目的是让 API 地址和密钥从环境变量读取而不是硬编码在配置文件中。3.3 用户登录与登录拦截器登录功能是后续所有业务模块的前置条件。使用 Session 保存登录用户是最直接的方式配合拦截器统一校验未登录请求。UserServiceImpl 中校验登录的核心逻辑Service public class UserServiceImpl implements UserService { Resource private UserMapper userMapper; Override public User login(String username, String password) { User user userMapper.selectOne(new LambdaQueryWrapperUser() .eq(User::getUsername, username)); if (user ! null BCrypt.checkpw(password, user.getPassword())) { return user; } return null; } }密码使用BCrypt校验注册时使用BCrypt.hashpw(password, BCrypt.gensalt())生成密文。这里不要使用自定义的 MD5 加盐方式BCrypt 内置随机盐是更被认可的做法。登录 ControllerController public class LoginController { Resource private UserService userService; GetMapping(/login) public String loginPage() { return login; } 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(loginUser, user); return redirect:/index; } GetMapping(/logout) public String logout(HttpSession session) { session.invalidate(); return redirect:/login; } }登录拦截器Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getSession().getAttribute(loginUser) null) { response.sendRedirect(/login); return false; } return true; } }注册拦截器并排除登录、静态资源路径Configuration public class WebConfig implements WebMvcConfigurer { Resource private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**, /error); } }拦截器这一段必须写在论文里并且要理解它的执行原理preHandle在 Controller 方法执行前被调用返回false时请求中断。拦截器在很多场景都有应用比如权限校验、日志记录、接口耗时统计答辩中被问到扩展性时可以这样回答。4. 核心业务模块实现4.1 公告模块完整 CRUD 的标准写法公告模块适合作为论文中的第一个完整功能案例展示 Controller、Service、Mapper 的标准写法。公告实体类Data TableName(community_announcement) public class Announcement { TableId(type IdType.AUTO) private Long id; private String title; private String content; private Long createBy; private LocalDateTime createTime; }公告 ControllerController RequestMapping(/announcement) public class AnnouncementController { Resource private AnnouncementService announcementService; GetMapping(/list) public String list(Model model) { model.addAttribute(announcements, announcementService.listLatest()); return announcement-list; } GetMapping(/detail/{id}) public String detail(PathVariable Long id, Model model) { model.addAttribute(announcement, announcementService.getById(id)); return announcement-detail; } }Service 中的分页查询使用 MyBatis-Plus 的Page对象public PageAnnouncement pageList(int pageNum, int pageSize) { PageAnnouncement page new Page(pageNum, pageSize); LambdaQueryWrapperAnnouncement wrapper new LambdaQueryWrapper(); wrapper.orderByDesc(Announcement::getCreateTime); return announcementMapper.selectPage(page, wrapper); }这里要理解LambdaQueryWrapper的两个优点第一通过方法引用直接指定字段避免字符串拼写错误第二类型安全重构字段名时编译器能直接发现错误。4.2 报修工单状态流转与权限控制报修工单是业务流程最完整的模块。业主提交工单后状态为PENDING物业人员把状态改为PROCESSING处理完成时填写处理回执并置为DONE业主也可以取消PENDING状态的工单。报修状态流转逻辑Service public class RepairServiceImpl implements RepairService { Resource private RepairMapper repairMapper; Override public boolean updateStatus(Long orderId, String targetStatus, User operator, String handleNote) { RepairOrder order repairMapper.selectById(orderId); if (order null) { return false; } // 普通业主只能取消自己的 PENDING 工单 if (CANCELED.equals(targetStatus)) { if (!order.getUserId().equals(operator.getId())) { throw new BusinessException(只能取消自己的报修单); } if (!PENDING.equals(order.getStatus())) { throw new BusinessException(当前状态不允许取消); } } // 物业和管理员才能进入处理环节 if (PROCESSING.equals(targetStatus) || DONE.equals(targetStatus)) { if (operator.getUserType() 0) { throw new BusinessException(业主不能执行该操作); } } order.setStatus(targetStatus); order.setHandlerId(operator.getId()); order.setHandleNote(handleNote); order.setHandleTime(LocalDateTime.now()); return repairMapper.updateById(order) 0; } }业务规则写在 Service 而不是 Controller是这套项目的关键设计。答辩时如果被问到“为什么 Controller 里没有业务代码”可以直接回答Controller 负责参数接收和视图跳转业务规则和权限判断放到 Service 层一方面便于复用另一方面也方便编写单元测试。这里建议引入统一的业务异常类BusinessException配合全局异常处理器返回友好提示而不是在 Controller 里写大量if/else。4.3 AI 服务助手大模型接口的通用调用方式AI 模块是项目的技术亮点。实现思路是业主访问 AI 助手页面输入自然语言问题后端收到问题后先做常见问题匹配命中则直接返回预设答案未命中则调用大模型 API。这样做的好处很明显常见问题不走模型接口响应快且零成本同时避免了大模型接口不可用时整个页面不可用的问题。先定义统一响应类Data public class Result { private Integer code; private String message; private Object data; public static Result ok(Object data) { Result result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static Result error(String message) { Result result new Result(); result.setCode(500); result.setMessage(message); return result; } }AiChatService 使用 RestTemplate 调用大模型接口。这里以当前大模型平台通用的chat/completions接口格式为例实际项目需要根据所选平台调整请求地址和字段Service Slf4j public class AiChatService { private final RestTemplate restTemplate; Value(${ai.endpoint}) private String endpoint; Value(${ai.api-key}) private String apiKey; Value(${ai.model}) private String model; private static final int CONNECT_TIMEOUT 5000; private static final int READ_TIMEOUT 30000; public AiChatService() { SimpleClientHttpRequestFactory factory new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(CONNECT_TIMEOUT); factory.setReadTimeout(READ_TIMEOUT); this.restTemplate new RestTemplate(factory); } private MapString, Object buildMessage(String role, String content) { MapString, Object message new HashMap(); message.put(role, role); message.put(content, content); return message; } public String chat(String userMessage, String systemPrompt) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); MapString, Object payload new HashMap(); payload.put(model, model); payload.put(messages, Arrays.asList( buildMessage(system, systemPrompt), buildMessage(user, userMessage) )); payload.put(temperature, 0.7); HttpEntityMapString, Object request new HttpEntity(payload, headers); try { ResponseEntityMap response restTemplate.exchange( endpoint, HttpMethod.POST, request, Map.class); if (response.getStatusCode().is2xxSuccessful() response.getBody() ! null) { Map body response.getBody(); List choices (List) body.get(choices); if (choices ! null !choices.isEmpty()) { Map firstChoice (Map) choices.get(0); Map message (Map) firstChoice.get(message); return String.valueOf(message.get(content)); } } } catch (Exception e) { log.error(AI 接口调用失败endpoint{}, endpoint, e); } return AI 服务暂时不可用请稍后再试。; } }这个实现里有几个必须理解的点setConnectTimeout(5000)控制建立连接的超时时间setReadTimeout(30000)控制等待模型返回的最长时间。大模型生成回答通常需要几秒到几十秒读超时不能设置太短。headers.setBearerAuth(apiKey)是多数大模型平台的标准认证方式也有平台使用Authorization: Bearer之外的格式需要按平台文档调整。temperature参数控制回答的随机性值越小输出越稳定做客服助手建议设置在 0.3 到 0.7 之间。4.4 提示词设计与常见问题匹配AI 助手的表现不只取决于模型更取决于系统提示词。系统提示词的作用是给模型设定角色、边界和输出规范。String systemPrompt 你是智能社区服务系统的 AI 助手。你可以回答与社区生活相关的问题 包括物业报修流程、访客登记、缴费查询、社区公告、常见政策等。 回答要简洁、准确、口语化。如果用户的问题不在你的知识范围内 请引导用户联系物业服务中心不要编造政策信息。;提示词设计有三个要点明确角色定位告诉模型“你是社区助手”比直接让模型回答通用问题效果更好。限定回答范围避免模型输出与社区无关甚至违规的信息。规定兜底策略不知道就引导联系人工而不是编造内容。在调用大模型之前可以先用关键词匹配常见问题表。例如用户输入“怎么报修”系统先查service_question表找到匹配度高的问题后直接返回预设答案。这样既节省接口调用成本也能保证高频问题在演示时稳定秒回。AiController 完整逻辑Controller RequestMapping(/ai) public class AiController { Resource private AiChatService aiChatService; Resource private AiChatLogService aiChatLogService; GetMapping(/chat) public String chatPage() { return ai-chat; } PostMapping(/chat) ResponseBody public Result chat(RequestParam(message) String message, HttpSession session) { User user (User) session.getAttribute(loginUser); if (message null || message.trim().isEmpty()) { return Result.error(请输入问题); } if (message.length() 200) { return Result.error(问题长度不能超过200字); } long start System.currentTimeMillis(); String answer aiChatService.chatWithKnowledge(user.getId(), message.trim()); long cost System.currentTimeMillis() - start; aiChatLogService.saveLog(user.getId(), message.trim(), answer, cost); return Result.ok(answer); } }在AiChatService中补充常见问题匹配逻辑public String chatWithKnowledge(Long userId, String message) { String answer questionService.matchByKeyword(message); if (answer ! null) { return answer; } return chat(message, buildSystemPrompt()); }这样 AI 模块就有了“本地规则兜底 大模型智能生成”的完整链路论文里可以起名为“基于知识库缓存与大模型生成的混合式问答策略”比单纯调用接口更有设计感。5. Thymeleaf 页面集成5.1 页面布局与公共片段Thymeleaf 支持通过th:fragment抽取公共页面片段例如导航栏和页脚。创建一个layout.html!DOCTYPE html html xmlns:thhttp://www.thymeleaf.org header th:fragmentnavbar nav classnavbar navbar-expand-lg navbar-dark bg-primary a classnavbar-brand th:href{/index}智能社区服务管理系统/a div classnavbar-nav a classnav-link th:href{/announcement/list}社区公告/a a classnav-link th:href{/repair/list}报修管理/a a classnav-link th:href{/visitor/list}访客登记/a a classnav-link th:href{/ai/chat}AI 助手/a /div /nav /header /html业务页面通过th:replace引入公共片段!DOCTYPE html html xmlns:thhttp://www.thymeleaf.org langzh-CN head meta charsetUTF-8 titleAI 助手/title link relstylesheet th:href{/css/bootstrap.min.css} /head body div th:replacelayout :: navbar/div div classcontainer mt-4 h4智能社区服务助手/h4 div idchatBox classborder rounded p-3 stylemin-height: 400px;/div textarea idmessage classform-control mt-2 rows3 placeholder请输入您的问题例如如何报修水电问题/textarea button idsendBtn classbtn btn-primary mt-2 onclicksendMessage()发送/button /div script th:src{/js/ai-chat.js}/script /body /html5.2 列表页与表单提交公告列表页使用th:each遍历数据table classtable table-hover thead tr th标题/th th发布时间/th th操作/th /tr /thead tbody tr th:eachannouncement : ${announcements} td th:text${announcement.title}公告标题/td td th:text${#temporals.format(announcement.createTime, yyyy-MM-dd HH:mm)}2025-01-01 10:00/td td a th:href{/announcement/detail/{id}(id${announcement.id})}查看详情/a /td /tr /tbody /tableth:href{/announcement/detail/{id}(id${announcement.id})}是 Thymeleaf 的 URL 表达式写法会自动做路径参数拼接和 URL 编码不要在 HTML 里手动拼字符串。表单提交要注意 CSRF 问题。Spring Security 默认开启 CSRF 防御但本项目的简单拦截器方案并没有引入 Spring Security所以表单里不需要额外添加_csrf字段。如果后续接入 Spring Security所有POST表单必须加入隐藏的 CSRF Token否则会报 403。5.3 AI 助手页面的异步请求AI 页面适合使用 JavaScript 的fetch发送异步请求避免整个页面刷新。新建static/js/ai-chat.jsasync function sendMessage() { const input document.getElementById(message); const message input.value.trim(); if (!message) { return; } const chatBox document.getElementById(chatBox); chatBox.innerHTML div classalert alert-secondary用户 escapeHtml(message) /div; input.value ; try { const response await fetch(/ai/chat, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded; charsetUTF-8 }, body: new URLSearchParams({message: message}) }); const data await response.json(); const answer data.code 200 ? data.data : data.message; chatBox.innerHTML div classalert alert-primaryAI 助手 escapeHtml(answer) /div; } catch (error) { chatBox.innerHTML div classalert alert-danger请求失败请检查网络或稍后再试。/div; } chatBox.scrollTop chatBox.scrollHeight; } function escapeHtml(text) { const div document.createElement(div); div.appendChild(document.createTextNode(text)); return div.innerHTML; }这里使用escapeHtml对用户输入和模型输出做 HTML 转义防止 XSS 注入。这是很多毕业设计容易忽略的点写到论文的安全设计部分很有说服力。6. 运行验证与功能测试6.1 启动步骤按以下顺序启动项目在 MySQL 中执行数据库初始化脚本创建community_service库和所有表。在 IDEA 中导入 Maven 工程等待依赖下载完成。修改application.yml中的数据库用户名和密码。运行CommunityApplication主类。浏览器访问http://localhost:8080/login使用初始化管理员账号登录。如果需要打包部署使用 Maven 命令mvn clean package -DskipTests生成的 jar 包位于target/community-service-0.0.1-SNAPSHOT.jar运行命令java -jar target/community-service-0.0.1-SNAPSHOT.jar启动成功后在控制台会看到 Tomcat 启动日志。访问http://localhost:8080/login能看到登录页说明工程骨架正常。6.2 功能验证清单验收时建议按下面的清单逐项测试每项都记录实际结果和截图作为论文测试章节的材料功能模块测试步骤预期结果登录认证输入正确账号密码跳转首页Session 保存用户信息登录失败输入错误密码停留登录页并显示错误提示未登录拦截直接访问/repair/list重定向到登录页公告发布物业账号新增公告列表页出现新公告报修提交业主账号提交报修工单工单状态为 PENDING报修处理物业账号处理工单状态变为 DONE填写处理回执权限控制业主账号尝试处理他人工单提示无权操作AI 常见问题输入“如何报修”秒回预设答案AI 模型调用输入“社区最近有什么活动”返回模型生成内容AI 接口异常关闭 AI 配置或断网返回友好提示页面不崩溃实际验收不能只看“页面能打开”还要验证异常分支。答辩演示时最怕的就是 AI 接口突然超时或报错提前把异常场景测试清楚演示过程才会稳定。7. 常见问题与排查毕业设计开发过程中最常遇到的问题集中在版本、配置、AI 接口三个方向。下面按“现象、原因、检查、解决”的顺序整理。问题现象常见原因检查方式解决方案项目启动报ClassNotFoundException: javax.servlet.*SpringBoot 3.x 下依赖仍引用旧版包查看 pom 中 SpringBoot 版本和第三方依赖版本降级到 SpringBoot 2.7.x或把javax改为jakarta页面修改后刷新不生效Thymeleaf 缓存未关闭或浏览器缓存检查spring.thymeleaf.cache开发环境设为false强制刷新浏览器登录时中文用户名乱码数据库字符集或请求编码配置错误检查库表字符集、server.servlet.encoding创建库时使用utf8mb4配置编码过滤器MyBatis-Plus 查询不到数据表名与实体名映射不一致查看控制台 SQL 日志添加TableName注解或配置table-prefixAI 接口调用超时RestTemplate 未设置超时时间观察接口耗时和异常堆栈设置连接超时和读超时增加 try/catchAI 返回内容包含多余格式响应结构解析不正确打印完整响应 JSON按平台文档解析choices[0].message.content控制台 SQL 不显示MyBatis-Plus 日志未开启检查application.yml配置log-impl: StdOutImpl版本兼容问题需要单独强调。如果项目一开始就选择了较新的 SpringBoot 3.3、3.4而 MyBatis-Plus、Shiro、FastJSON 等依赖没有同步升级运行期会出现各种奇怪的错误。建议固定使用一套经过验证的版本组合不要盲目追新。AI 接口排错按这个顺序执行确认配置项是否生效在启动日志中搜索ai.endpoint、ai.api-key确认是从配置文件中读取还是在环境变量中注入。确认网络连通性用 curl 直接测试接口地址确认能否返回响应。确认请求报文把payload打印出来检查模型名称、消息格式是否符合平台要求。确认响应解析接口可能返回 200 但结构不是预期格式打印完整响应体定位问题。确认超时设置模型生成内容较长时读超时 30 秒可能仍然不够需要结合实际情况调整。8. 毕业设计规范与实践建议8.1 答辩环节最该准备的技术点答辩时评委通常不会逐行读代码而是围绕几个关键点提问项目整体架构画清楚“浏览器 → Controller → Service → Mapper → MySQL”的请求链路说明每个层的作用。登录认证原理解释 Session 会话保持机制、拦截器如何拦截未登录请求、为什么不用 JWT。数据库设计说明用户表、报修表、AI 日志表之间的关联关系以及状态字段为什么用字符串枚举。AI 模块实现讲清楚提示词设计、接口调用、超时和降级策略。这是项目最有可能出彩的部分。项目可以改进的方向提前准备两三个扩展点比如引入工作流引擎 Flowable 处理报修审批流、使用 WebSocket 实现处理状态实时推送、将服务端渲染升级为前后端分离。答辩 PPT 建议控制在 15 到 20 页重点放需求分析、架构设计、核心功能截图、AI 模块链路图和测试结果。不要贴大段代码评委关心的是你能否讲清楚设计思路而不是代码数量。8.2 从学习项目到生产项目的差距如果只是完成毕业设计现有实现已经足够。但如果想把这个项目写成简历项目或者后续作为个人作品继续发展以下几点必须补上配置外置化数据库密码、AI 密钥通过环境变量或配置中心管理不能出现在仓库里。日志与监控使用 Logback 输出结构化日志记录接口耗时和错误堆栈生产环境接入监控看板。统一异常处理使用RestControllerAdvice处理业务异常和系统异常避免页面暴露堆栈信息。接口限流AI 接口有成本需要按用户或 IP 限制调用频率避免被恶意刷接口。数据脱敏用户手机号、身份证号在列表中脱敏展示。系统备份数据库定期备份部署服务器做好回滚方案。8.3 扩展方向建议这个项目可以沿着三条路线扩展。路线一是把 AI 能力做深。当前实现是“一问一答”的单轮对话可以扩展为多轮会话让模型记住上下文或者使用 RAG检索增强生成方案把社区政策文档、物业条款向量化后存储到向量数据库用户提问时先检索相关段落再交给模型生成回答的准确性和可解释性都会明显提升。路线二是把业务流程做重。报修工单目前只有简单的状态流转可以引入 Flowable 工作流引擎设计工单提交、派单、处理、验收、超时提醒的完整审批流程。这是企业级系统常见的需求写到简历上比纯粹的 CRUD 更有分量。路线三是做前后端分离升级。把 Thymeleaf 页面替换为 Vue 3 前端工程后端提供 REST API登录改为 JWT 认证。这个方向需要额外处理跨域、Token 刷新、前端路由守卫等问题适合项目周期充足的情况。对学习价值而言建议把 AI 模块的链路吃透用户输入如何预处理、本地知识库如何匹配、请求如何组装、超时如何处理、日志如何记录、失败如何降级。这条链路包含了大模型应用开发的通用思维学会了它不管是换一个模型平台还是换一个业务场景都能快速迁移。