资讯动态

Spring Boot来访管理系统:从源码拆解到JWT改造实战

发布时间:2026/10/9 22:18:46 来源:尧图企业网站定制
简介一套基于Spring Boot框架的来访管理系统完整源码适合Java后端学习者、毕业设计开发者以及需要快速搭建访客登记与预约管理场景的人员使用。项目围绕管理员与访客双角色展开包含个人中心、访客管理、访客预约管理、访客来访管理等模块覆盖登录注册、密码重置、分页查询、统计筛选等常见业务操作可直接作为单体Web应用的开发参考。压缩包共361个文件约9.07MB核心代码以Java后端、Vue前端为主另有SVG图标、SQL脚本、构建批处理及Maven配置文件结构清晰便于二次开发。目前已有27人学习下载适合用于课程设计、项目实战或快速理解前后端分离开发流程。透过源码可掌握Spring Boot与Vue整合方式了解访客预约统计、分组分析等功能的实现思路并借助附带脚本快速启动运行。1. Spring Boot 来访管理系统一个能直接落地的入门级企业项目源码如果你是刚学完 Spring Boot 基础、想找一个能写进简历又不怕被面试官深挖的项目这套“来访管理系统”源码包很值得下一份。它解决的是最实在的问题单位门卫处还在用纸质登记本访客信息一多就乱查记录靠翻本子没台账、没审核、没统计。这套系统把「访客预约 → 来访登记 → 门卫审核 → 记录归档 → 黑名单管理」这一整条链路做成了 Web 应用基于 Spring Boot MyBatis MySQL 的经典组合前端用 Thymeleaf 模板直接渲染不需要你另外搭前后端分离的环境。适合两类人一是想练手 Spring Boot 全流程的开发者能看懂 MVC 分层、MyBatis 映射、拦截器鉴权这些实际写法二是单位内部 IT 人员想快速部署一套内网办公系统改改配置就能用。2. 拆解项目结构与 30 分钟跑通依赖、配置、启动三板斧拿到压缩包解压后第一反应别急着点启动按钮先看目录结构。这套源码是标准的 Maven 单模块工程不是多模块聚合好处是导入简单、没有父子模块之间的依赖地狱。根目录下有pom.xml、src/main/java、src/main/resources其中 Java 代码按controller/service/mapper/entity分层resources 下放着application.yml和mapper/xml目录。这个结构对新手很友好你能直观看到一条请求从 Controller 到 Mapper 的完整路径。2.1 依赖选型与版本陷阱为什么这套源码用 MyBatis 而不是 JPA打开pom.xml核心依赖是spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、lombok还有spring-boot-starter-thymeleaf。这里有个经验之谈很多企业老项目到现在仍然用 MyBatis 而不是 JPA原因不是 JPA 不好而是 MyBatis 的 SQL 可控性强尤其这套系统里涉及多表联查、按条件动态统计来访次数写 XML 里的映射比 JPQL 更容易调优。你在简历上写熟悉 MyBatis面试官普遍认可度比写 JPA 更高因为国内中小型公司存量项目大多都是 MyBatis 体系。版本上有个容易翻车的地方mybatis-spring-boot-starter的版本号必须和 Spring Boot 主版本匹配。这套源码用的 Spring Boot 2.x对应 MyBatis starter 是 2.x 系列。如果你换成 Spring Boot 3.xMyBatis 需要升级到 3.0 以上同时javax包名要改成jakarta否则启动直接报ClassNotFoundException。看源码时注意pom.xml里的parent标签如果 Spring Boot 版本是 2.5 或 2.7别手滑改成 3.0。2.2 从压缩包到跑起来三步启动法第一步准备环境。需要 JDK 1.8 或 11MySQL 5.7 或 8.0IDE 用 IDEA 或 Eclipse 都行。注意如果 JDK 版本高于源码编译版本可能出现major version 52这类错误。第二步导入并改配置。IDEA 里选择File - New - Project from Existing Sources选中解压后的文件夹Maven 会自动下载依赖。如果下载慢换成阿里巴巴镜像仓库mirror idaliyun/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirror然后编辑src/main/resources/application.yml核心改动在数据源server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/visit_system?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.visit.entity configuration: map-underscore-to-camel-case: true逻辑说明serverTimezoneAsia/Shanghai是解决 MySQL 8 时区报错的关键没有它会在连接时抛The server time zone value Öйú±ê׼ʱ¼ä这类乱码时区异常。map-underscore-to-camel-case能让你数据库里的visit_time自动映射到 Java 的visitTime字段少写一堆resultMap列映射。第三步初始化数据库。源码包里一般带sql/visit_system.sql脚本在 MySQL 里执行mysql -u root -p visit_system.sql这个脚本会建库建表并插入一个默认管理员账号。然后启动VisitSystemApplication.java浏览器访问http://localhost:8080/login。如果看到登录页就说明跑通了默认账号通常是admin/admin123具体以源码里sys_user表中的数据为准。这里注意有些源码把初始密码用 MD5 加密存在库里你直接看表是看不出来明文密码的得去DataInitializer或者sql注释里找找不到就问原作者要默认密码说明文档。3. 核心功能逐个拆登记、审核、黑名单与记录查询的完整链路这套系统不是花架子四个核心功能全对应真实业务访客现场登记、门卫审核放行、黑名单拦截、记录多条件查询。理解了这四块你就掌握了这套源码的精髓。下面按数据流向一层层拆。3.1 数据库表设计五张表如何撑起来访管理打开sql脚本表结构很清晰核心是visitor_record、sys_user、blacklist、visit_type、department这五张表。visitor_record是主表字段包括id、visitor_name、phone、id_card脱敏显示、visit_reason、visit_status、expected_time、actual_time、blacklist_flag。这里的visit_status是灵魂字段用整数存状态0 表示待审核1 表示已通过2 表示已拒绝3 表示已签离。关联关系上visitor_record通过apply_user_id关联sys_user表示哪个保安或前台创建的记录通过dept_id关联department表示被访部门。没有用外键约束只是在业务层维护逻辑关联这符合企业开发习惯——加了物理外键后面分库分表、批量导入数据时都是坑。MyBatis 里用了association和collection做嵌套查询比如查询来访记录时带出申请人的姓名用的是延迟加载resultMap idRecordResultMap typecom.example.visit.entity.VisitorRecord id propertyid columnid/ result propertyvisitorName columnvisitor_name/ result propertyvisitStatus columnvisit_status/ association propertyapplyUser javaTypecom.example.visit.entity.SysUser id propertyid columnapply_user_id/ result propertyrealName columnreal_name/ /association /resultMap逻辑说明association用于一对一关联这里查记录同时把申请人的真实姓名捞出来避免了再开一条 SQL。注意columnapply_user_id是查询结果集里的列名如果你 SQL 里取了别名这里也要对应改否则映射出来是 null。除此之外还有一张visit_audit_log表记录每次审核的操作人、操作时间、审核意见。这是很多课程设计没有的亮点但企业里必须要有——万一出现纠纷得能追溯是哪个门卫在几点放行的。这套源码把审核日志落库算是有工程意识的设计。3.2 预约与审核的状态机流转状态字段怎么控制业务流程来访流程不是简单的增删改查核心是状态流转。源码里VisitService中定义了状态变更方法比如applyVisit()把状态设为 0approveVisit()把状态从 0 改为 1rejectVisit()改为 2signOutVisit()把 1 改为 3。这里有个常见的写法缺陷直接用if (status 0) { status 1; }判断没有做状态机校验。比如已经拒绝的记录再次点击通过理论上应该拦截但很多源码会直接更新成 1导致数据逻辑混乱。我一般会建议你改成枚举加状态机校验public boolean approveVisit(Long recordId, Long operatorId) { VisitorRecord record visitorRecordMapper.selectById(recordId); if (!record.getVisitStatus().equals(VisitStatusEnum.WAITING.getCode())) { throw new BizException(当前状态不允许审核操作); } record.setVisitStatus(VisitStatusEnum.APPROVED.getCode()); record.setApproverId(operatorId); record.setApproveTime(new Date()); return visitorRecordMapper.updateById(record) 0; }参数说明VisitStatusEnum是枚举类把 0/1/2/3 换成可读名称这样后续在 Service 里判断状态可读性高查 bug 时不用对着数字猜。这套源码虽然没写得这么规范但你自己读代码的时候可以动手改一改面试时能说出这个优化点比单纯说“我做过来访管理系统”要加分得多。审核动作里还涉及短信或微信通知源码里大概率没有对接第三方接口只是预留了visit_audit_log的记录。如果你想加真实推送可以用钉钉机器人 Webhook审核通过后往特定群发个消息提醒访客。3.3 记录查询模块多条件拼接 SQL 与时间范围的处理技巧查询页是门卫使用频率最高的页面。条件有来访人姓名、身份证号、来访时间范围、状态。直接用 MyBatis 的动态 SQL 实现select idselectRecordList resultMapRecordResultMap SELECT r.*, u.real_name FROM visitor_record r LEFT JOIN sys_user u ON r.apply_user_id u.id where if testvisitorName ! null and visitorName ! AND r.visitor_name LIKE CONCAT(%, #{visitorName}, %) /if if testphone ! null and phone ! AND r.phone #{phone} /if if teststatus ! null AND r.visit_status #{status} /if if teststartTime ! null AND r.expected_time gt; #{startTime} /if if testendTime ! null AND r.expected_time lt; #{endTime} /if /where ORDER BY r.create_time DESC /select逻辑说明where标签会自动去掉第一个AND避免 SQL 语法错误。CONCAT(%, #{visitorName}, %)是模糊查询注意别写%${visitorName}%那会导致 SQL 注入。时间范围这里有个坑#{endTime}如果传的是2025-05-20而数据库字段是datetime那么当天2025-05-20 23:59:59的记录不会被查出来因为2025-05-20 00:00:00 2025-05-20 23:59:59不成立。常见做法是在 Service 层给 endTime 加一天或者让前端传2025-05-20 23:59:59if (query.getEndTime() ! null) { Calendar calendar Calendar.getInstance(); calendar.setTime(query.getEndTime()); calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); query.setEndTime(calendar.getTime()); }这段是查来访记录最常见的血泪经验。你不处理就会发现“明明当天有数据为何查询结果少了一半”。4. 权限控制与前端页面登录拦截、角色区分和模板渲染来访管理系统肯定有角色概念管理员能看全部记录、管理黑名单、配置部门保安只能登记和审核。这套源码用 Spring Boot 拦截器实现登录控制和角色判断没用 Spring Security因为你不需要那么重的框架。前端用 Thymeleaf 模板页面在src/main/resources/templates下。4.1 登录态与角色权限拦截器里怎么用注解和 Session 控制看WebConfig类它注册了一个LoginInterceptor拦截/admin/**路径public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(login_user); if (loginUser null) { response.sendRedirect(/login); return false; } // 检查角色 if (handler instanceof HandlerMethod) { HandlerMethod method (HandlerMethod) handler; RequiresRole role method.getMethodAnnotation(RequiresRole.class); if (role ! null) { SysUser user (SysUser) loginUser; if (!role.value().equals(user.getRole())) { response.setStatus(403); return false; } } } return true; } }逻辑说明RequiresRole是自定义注解Tag 在 Controller 方法上比如RequiresRole(ADMIN)标注只有管理员能调用的接口。用拦截器加注解的好处是你不必在每个方法第一行都写if (session.getAttribute(xxx) null)这种重复代码新加接口时忘记鉴权的问题也能规避一部分。注意这里role.value().equals(user.getRole())如果用户角色为 null 会抛 NPE所以登录时一定要保证sys_user.role非空。4.2 Thymeleaf 模板绑定后端数据表格循环与状态显示的写法前端列表页是visit_list.html表格通过 Thymeleaf 遍历后台传来的recordListtable classtable table-hover thead tr th访客姓名/th th手机号/th th来访事由/th th状态/th th操作/th /tr /thead tbody tr th:eachrecord : ${recordList} td th:text${record.visitorName}/td td th:text${record.phone}/td td th:text${record.visitReason}/td td span th:if${record.visitStatus 0} classlabel label-warning待审核/span span th:if${record.visitStatus 1} classlabel label-success已通过/span span th:if${record.visitStatus 2} classlabel label-danger已拒绝/span /td td a th:if${record.visitStatus 0} th:href{/admin/visit/approve/{id}(id${record.id})} classbtn btn-sm btn-primary审核/a /td /tr /tbody /table参数说明th:each是循环渲染{/路径/{id}}是 Thymeleaf 的 URL 表达式。重点讲一下状态显示这坑很多人踩后台返回的visitStatus是 Integer但如果你的实体字段类型是int且从数据库查出 null 值 0判断会变成null 0导致页面不显示任何状态。解决方法是实体类用Integer而不是int这样 null 和 0 能区分并且 th:if 判断时自己确认空值逻辑。另一个细节是列表中“审核”按钮只在待审核状态显示避免保安重复操作。这个前端逻辑配合后端的状态机校验才算是完整的闭环。5. 常见问题排查与避坑跑不起来时按这个顺序查源码下载后大部分人的问题集中在环境配置和数据库连接上。下面五条是我复盘过程中遇到最多的按「现象 → 原因 → 解决」写清。5.1 数据库连接报错 Communications link failure现象启动项目控制台报Communications link failure后面跟着Connection refused。原因MySQL 服务没启动或者端口不是 3306或者application.yml里配置的 url 写错主机名。解决先用mysql -u root -p在本机试连能通说明 MySQL 正常。再看 url 里的localhost和端口是否匹配。注意如果你 MySQL 用的是 Docker 容器端口可能映射成了 33060需要配置成真正宿主机端口。另外 MySQL 8 的驱动名称必须写com.mysql.cj.jdbc.Driver写旧的com.mysql.jdbc.Driver会直接启动失败。5.2 中文乱码访客姓名和事由全是问号现象录入中文后页面显示???数据库表里存进去的也是问号。原因数据库连接串没有加characterEncodingutf8或者建表语句里表的默认字符集是 latin1。解决确认application.yml中已经有characterEncodingutf8同时检查 MySQL 建表脚本CREATE TABLE visitor_record ( ... ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;utf8mb4比utf8更全能存 emoji 表情。如果你数据库已经建好用ALTER TABLE visitor_record CONVERT TO CHARACTER SET utf8mb4;改。这个坑最隐蔽因为项目本地测试可能没中文一到正式环境录入就翻车。5.3 日期时间差 8 小时列表里显示的时间比实际早 8 小时现象查询出来预登记时间比当前时间少了 8 小时或者多了 8 小时。原因服务器的时区设置和 MySQL 时区不一致。application.yml里已经写了serverTimezoneAsia/Shanghai但 JVM 默认时区可能还是 UTC。解决在启动类上加PostConstruct方法设置 JVM 时区PostConstruct public void setTimezone() { TimeZone.setDefault(TimeZone.getTimeZone(Asia/Shanghai)); }或者启动命令加-Duser.timezoneAsia/Shanghai。写这个的时候要注意如果线上服务器是 UTC前端展示时要统一用后端转好的字符串不要直接把Date对象序列化给前端。5.4 端口被占用8080 已经被别的项目占了现象启动报Port 8080 was already in use。原因本机有别的服务占用 8080。解决两种方案第一种杀掉占用进程第二种改application.yml里的server.port比如改成 8888。但注意改完以后前端模板里所有写死localhost:8080的 Ajax 请求都要跟着改Thymeleaf 模板里一般用{/}相对路径还好如果用了原生 JS 拼绝对地址就得多处替换。我一般建议保留默认端口直接 kill 进程避免连锁问题。5.5 Maven 依赖下载失败或报红色波浪线现象导入项目后IDEA 里pom.xml一直报红提示Cannot resolve symbol springframework或者下载失败。原因本地 Maven 仓库没有这些依赖且默认中央仓库下载慢甚至被墙。解决按前面提到的在settings.xml里配置阿里云镜像。还有一点如果pom.xml引用了本地私有依赖比如某个 jar 是手动 install 的你解压的源码不一定包含这个 jar需要从本地仓库手动mvn install:install-file安装。判断源码完整性有个办法如果报错集中在某一个包名上极有可能是缺私有依赖直接看项目里有没有lib目录或者 README 说明。6. 进阶改造把 Thymeleaf 换成前后端分离 API并加上 JWT 登录如果你不满足于跑通页面想把这套系统改造成能写进简历的亮点项目最值得做的一步是抽离 REST API 层。原来的VisitController返回的是ModelAndView这在前后端未分离时代没问题但现在主流公司都在用前后端分离。改造思路很简单Controller 不再返回视图只返回 JSON登录认证从 Session 改成 JWT。具体步骤分三步走。第一步Controller 层改造。新增一个ApiVisitController只处理数据接口返回ResultT统一包装RestController RequestMapping(/api/visit) public class ApiVisitController { Autowired private VisitService visitService; PostMapping(/apply) public Result? apply(RequestBody VisitorRecord record) { visitService.applyVisit(record); return Result.success(); } GetMapping(/list) public ResultListVisitorRecord list(RequestParam(required false) Integer status, RequestParam(required false) String visitorName) { return Result.success(visitService.queryList(status, visitorName)); } }这里RequestParam(required false)对应原来 XML 动态 SQL 里的if test前端不传参数时后端不会拼条件。第二步登录认证换成 JWT。在pom.xml里加入jjwt依赖然后写一个拦截器从请求头Authorization里取 token 校验Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { response.setStatus(401); return false; } try { Claims claims Jwts.parser() .setSigningKey(your-secret-key) .parseClaimsJws(token.replace(Bearer , )) .getBody(); request.setAttribute(user_id, claims.get(user_id)); return true; } catch (Exception e) { response.setStatus(401); return false; } } }这段代码要解释一下Jwts.parser()解析 token.setSigningKey()必须和登录时生成 token 用的密钥一致项目里密钥不要写在代码里可以用ConfigurationProperties从配置文件读取。解析成功后把user_id放到 request attribute 里后续 Controller 拿当前操作人直接request.getAttribute(user_id)不用再查 Session。第三步前端换成 Vue 或原生 Ajax。你不需要上全套 Node 工程直接在一个 html 页面里用 Vue 2 的 CDN 结合 Axios 就能调通。核心是请求拦截器统一带 tokenaxios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Bearer token; } return config; });同时后端要配置跨域Spring Boot 里写一个CorsConfig允许 localhost 的任意端口访问否则前端 DevServer 调/api会被浏览器拦截。把每一步改完你会发现这套项目的代码量从几千行变成明显更有层次。原来用 Thymeleaf 写的页面改造成 API 后扩展移动端 app 或者小程序访客预约就非常简单只要前端复用一套接口后端不用动。这也是我从这套源码里学到的最大实战习惯拿到任何 Spring Boot 项目先分清“哪些是页面渲染逻辑、哪些是纯数据逻辑”把数据逻辑尽量先做成接口再做页面后续功能翻倍不加价。从那以后我每次接手老项目都强制先抽 API 层再谈改前端流程跑顺了坑就少了希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑