简介这是一套基于Spring Boot的城市地名地址信息管理系统项目压缩包内含完整源码与配套文档面向Java开发者、毕业设计学生及相关业务部门的项目实施人员。系统已完成调试支持多用户操作与权限管理能够高效处理城市地址的录入、检索、修改、删除等核心业务同时提供了清晰的请求/响应数据模型帮助使用者理解后端逻辑。压缩包共1718个文件大小约15.74MB其中包括Java源码、编译后的class文件、XML与yml配置、SQL初始化脚本、前端HTML/CSS/JS及图片图标资源另附较完整的论文文档、PPT演示文稿和运行视频便于对照学习与系统演示视频中可直观看到系统启动、地址数据维护、权限分配等操作过程降低上手门槛。资源中还包含Dockerfile、yaml等部署相关文件适合需要快速搭建容器化环境的场景。目前已有32人学习浏览无论是用于课程设计、毕业设计还是快速获取一套可运行的管理系统都能有效节约开发时间。1. 先看明白这个 springboot 地名地址系统到底是什么货色值不值得继续花时间“带论文和PPT《已调试》springboot城市地名地址信息管理系统源码sql.zip”——我猜你拿到这个压缩包九成是冲着《已调试》三个字来的。先泼盆冷水作者机器上调试好的跟你的机器大概率两回事。这套东西的本质是一个典型的 java 课程设计案例源码后端用 springboot 框架做城市地名、区划、地址、楼栋这类基础数据的维护与检索压缩包里配好了 sql 脚本还带论文和 PPT省掉你写文档的功夫。适合三类人要交课设或毕设的学生、想搞懂 springboot 项目结构的新手、想快速搭一个管理后台再改业务的老手。这三类人的第一步都一样——把它真正跑起来。2. 技术选型与项目骨架springboot 项目结构拆成三层调用链2.1 为什么这条技术主线最稳springboot mybatis-plus mysql 的组合套路我拿到这种地名地址系统压缩包第一件事是看 pom.xml九成能看到 spring-boot-starter-web、mybatis-plus-boot-starter 和 mysql-connector-java 三件套。这不是巧合。城市地名地址信息管理系统的业务边界非常固定区划维护、地名录入与检索、地址要素匹配、楼栋门牌管理。核心操作是条件分页查询和树形结构组装没有高并发、没有复杂事务、没有跨服务调用springboot 单体完全够。你要是为了这个项目硬上 springcloud、上 flink或者塞一个 activemq 进去课设答辩会变成一场自爆——老师一问“为什么用消息队列”你答不上来。mybatis-plus 能成为这类源码的标配是因为它把单表 CRUD 直接封装在 BaseMapper 里。一个地名列表页用原生 mybatis 要写 mapper 接口、xml、resultMap 三件东西用 mybatis-plus 只需要继承 BaseMapper再配一个 LambdaQueryWrapper 拼动态条件代码量少一半。对赶工期的课设来说这个差距意味着你能省出两个晚上写论文。mysql 8 则是图省心可视化工具全、sql 脚本通用性最强压缩包里的脚本大多数是按 mysql 8 语法写的导入成本最低。选型上还有一个容易被忽略的点权限模块。地名地址系统一般有两类用户管理员和录入员登录后按角色决定能不能改数据。springboot 生态里可选方案很多常见做法是 shiro 或 jwt但后面我会细说课设场景一个简单拦截器加 session 最不容易翻车。先把主链路跑通再谈权限这是我调试这类项目一直坚持的顺序。2.2 三层调用链怎么走从 controller 到 mapper 的请求路径与代码拆解这类源码解压后springboot 项目结构通常长这样controller 包放对外接口service 包放业务逻辑mapper 包放数据库访问entity 包对应表结构。看代码不要从头读从一条请求链路看最快。我一般先找一个地名查询接口顺着 controller → service → mapper 走一遍整个项目就懂了一半。RestController RequestMapping(/api/place) public class PlaceController { private final PlaceService placeService; public PlaceController(PlaceService placeService) { this.placeService placeService; } GetMapping(/page) public Result page(PlaceQuery query) { return Result.ok(placeService.pageByCondition(query)); } }controller 这层只做三件事接收参数、调用 service、包装返回结果。注意 PlaceQuery 不是实体类是专门接收查询条件的 DTO字段有 name、districtCode、pageNum、pageSize。把查询参数单独拆出来的好处是前端多传的参数不会直接污染实体这个习惯在答辩里是加分项。如果源码里直接把 HttpServletRequest 的参数一个个手动读取再塞给 service说明作者写得很急你后期维护会很难受。Service public class PlaceServiceImpl extends ServiceImplPlaceMapper, Place implements PlaceService { Override public PagePlace pageByCondition(PlaceQuery query) { LambdaQueryWrapperPlace wrapper Wrappers.lambdaQuery(); // 地名不为空才拼模糊查询避免空条件扫全表 if (StrUtil.isNotBlank(query.getName())) { wrapper.like(Place::getName, query.getName()); } // 区划编码有值才按精确条件过滤 if (StrUtil.isNotBlank(query.getDistrictCode())) { wrapper.eq(Place::getDistrictCode, query.getDistrictCode()); } wrapper.orderByDesc(Place::getCreateTime); return this.page(query.toPage(), wrapper); } }这段是典型代码注意两个细节。第一wrapper 里的条件都用 if 包了一层地名和区划编码没传时不拼条件避免用户点“查询”不带任何条件时生成一个没有 where 的查询几万条地名全查出来。第二排序字段我放在最后如果有多个条件顺序影响索引命中地名表量一大orderBy 一个没索引的字段会肉眼可见地变慢。参数上Page 是 mybatis-plus 的分页对象query.toPage() 内部把 pageNum、pageSize 转成 current 和 size分页插件底层自动拼 limit不需要手写分页 sql。mapper 层更简单接口继承 BaseMapperPlace 后大部分方法不用自己写。只有多表关联才需要额外写 mapper xml比如按街道名称查地名要 join 区划表这种我会单独定义方法并在 xml 里写 sql 语句。有一个安全细节要提醒xml 里拼条件参数用 #{}不要用 ${}前者是预编译占位符后者是字符串拼接。地名系统虽然是内部系统但登录接口一旦出现 ${} 拼接sql 注入的万能密码绕过测试就能直接打穿登录页。这个点答辩被问到的概率不低你自己写代码时也养成习惯。区块划分的逻辑懂了之后你会注意到 service 层还有一个高频操作区划树的组装。城市地名地址系统的区划是典型的多级树省市区县街道前端下拉框要按层级展开。常见做法是先查全部区划然后在 service 层用 parentCode 组装成树或者用 mybatis-plus 的树形结构递归查。答辩老师最喜欢问“区划树是怎么查出来的”建议你重点看源码里这块因为它决定了你对“业务逻辑到底写在哪一层”有没有真正理解。2.3 登录和权限怎么拦shiro、jwt、拦截器三种做法里课设怎么选权限部分这类源码里三种做法都见过。shiro 功能全认证授权、会话管理、记住我都封装好了但配置项多realm、过滤器链一多新手很容易配出一个“所有接口都放行”的假安全。jwt 思路清爽登录发 token前端每次请求带 token后端过滤器解析适合前后端分离但 token 过期、刷新、注销这些逻辑要么自己写要么引一堆工具类很多源码在这一层写得很半吊子。最朴素也最稳的是 spring 拦截器加 session登录成功往 session 里塞用户拦截器拦所有请求只放行登录页和静态资源。Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /api/login, /static/**, /error); } }这段配置看懂了权限模块就通了一半。addPathPatterns(/**) 表示拦截所有请求excludePathPatterns 是放行名单。放行名单是踩坑高发区静态资源必须放行否则页面 css、js 全被拦页面打开是光的登录接口必须放行否则你还没登录就被打回登录页形成死循环如果项目配置了 context-path比如所有接口都带 /geo 前缀这里放行路径也要跟着改后面避坑章节我会单独讲。课设项目里我更推荐先把拦截器这套跑通每一行配置都能讲清楚答辩老师不会追问 session 分布式问题。shiro 虽然看起来专业但你把过滤器链配错了启动都启动不起来得不偿失。3. sql 脚本是命根子地名地址数据模型和建表导入的四个细节3.1 核心表拆解区划、地名、地址、楼栋四张表怎么关联地名地址信息管理系统最核心的资产不是代码是数据模型。我见过不少同学代码跑通了但不懂为什么地名要单独建表答辩被问“为什么要分这么多表”就卡住。这套系统的数据模型一般围绕四张表展开区划表、地名表、地址表、楼栋表。表名核心字段作用区划表 t_districtdistrict_code、parent_code、district_name、level维护省市区县街道的树形层级地名表 t_placeplace_name、district_code、place_type、longitude、latitude存储自然地名、兴趣点名称及坐标地址表 t_addressaddress_no、place_id、full_address、road_name存储标准地址文本关联到地名楼栋表 t_buildingbuilding_name、address_id、floor_count、unit_count在地址下挂出门牌楼栋明细先看区划表。district_code 是区划编码parent_code 指父级区划编码level 是层级。省这条记录 parent_code 为空市这条记录 parent_code 为省级编码这样一条链就能把树拼出来。地名表通过 district_code 关联区划目的是快速按区划过滤地名地址表通过 place_id 关联地名一个地名下可以有多个标准地址楼栋表再挂在 address_id 下面形成“区划 → 地名 → 地址 → 楼栋”的层级结构。CREATE TABLE t_district ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, district_code VARCHAR(20) NOT NULL COMMENT 区划编码, parent_code VARCHAR(20) DEFAULT NULL COMMENT 父级区划编码, district_name VARCHAR(100) NOT NULL COMMENT 区划名称, level TINYINT NOT NULL COMMENT 层级 1省 2市 3区县 4街道, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT行政区划表;这张表的 sql 写法可以当范本看。主键用自增 bigint适合单机课设区划编码单独建字段而不当主键是因为区划编码属于业务主键有标准编码规则但导入导出时可能出现调整跟自增主键职责分离开更稳妥。层级用 tinyint 而不是字符串节省空间也方便排序。每一列都写了 COMMENT这个习惯在论文写数据库设计章节时非常省事ER 图直接照抄注释老师一看就觉得项目规范。有的源码里只有一张大表把地名、地址、楼栋全部塞进一个字段里比如 full_address 直接存“xx省xx市xx区xx街道xx路xx号xx栋”这样做单表查询确实简单但当你需要按楼栋统计、按门牌号筛选时sql 语句会写成一长串 like性能差且没法维护。四张表的拆分本质是把“位置描述”拆成“区划关系 地名实体 门牌细节”这是地名地址系统最常见的设计套路你答辩时能讲清楚这套拆分逻辑比背十个 springboot 注解有用。3.2 mysql 执行 sql 脚本的姿势编码、use database 和执行顺序拿到压缩包里的 sql 文件很多人双击导入然后跑起来报 Table doesnt exist回头一看表建错库了。我导入 sql 脚本从来不用图形工具的“运行 SQL 文件”而是先打开脚本头部看二十行确认有没有 CREATE DATABASE 和 USE 语句再看每张表的字符集。# 如果 sql 脚本里已经包含 CREATE DATABASE 和 USE直接重定向导入 mysql -uroot -p --default-character-setutf8mb4 city_geo.sql # 如果脚本里没有建库语句先手工建库再导入 mysql -uroot -p -e CREATE DATABASE IF NOT EXISTS city_geo DEFAULT CHARSET utf8mb4; mysql -uroot -p --default-character-setutf8mb4 city_geo city_geo.sql这两条命令的差别要搞清楚。加 --default-character-setutf8mb4 是为了让中文不乱码尤其脚本注释里有中文时默认字符集不对导入过程不报错但数据全是乱码。第二种情况手工建库时指定了字符集再指定库名导入此时脚本里的 USE 语句会覆盖你指定的库名如果脚本头部没有 USE才会用到你命令里写的 city_geo。这是很多人踩过但没想明白的点。还有一种是已经进了 mysql 命令行用 source 导入好处是能实时看到每张表的执行结果mysql source /path/to/city_geo.sql;source 和重定向的区别在于source 在 mysql 会话内执行报错会直接打到终端你能立刻看到哪张表哪一行挂了重定向导入遇到错误时很多版本会跳过继续执行最后你也不知道哪些表建成功了。所以我的习惯是第一次导入用 source确认全部成功后再考虑重定向。导入完别急着关终端先执行 show tables; 数一数表数量再 desc t_place; 抽查一两张表字段跟项目文档对一下。如果 sql 脚本里除了建表还有初始数据导入顺序也要看先建表后插数据这是自然的但有的脚本把字典表、用户表、区划数据混在一起中途插数据依赖前面某张表的主键顺序乱了就会报外键错误。这种问题不用慌看报错找到断点把依赖的表先建好再重跑一次脚本。sql 脚本是幂等资源多执行几次不丢人保证最终库结构正确才算数。3.3 mybatis-plus 反向生成实体实体与建表 sql 的反向误区很多人拿到 sql 之后第一反应是用 mybatis-plus 的代码生成器从数据库反向生成实体、mapper、service这也是“mybatisplus 根据 java 实体类生成创建表的 sql 语句”这个热搜背后的真实需求。这里必须先说清楚方向mybatis-plus 代码生成器的标准能力是从表结构反向生成 Java 代码而不是从 Java 实体正向生成建表 sql。正向生成建表语句它也有但覆盖不了索引、注释、字段顺序这些细节生成的 sql 跟手写的差很远我不建议课设走这条路。我一般会这样反向生成先在 mysql 里把 sql 脚本执行成功然后用 idea 的 database 面板连上库对着表右键用工具生成实体类。生成的实体字段名是下划线转驼峰比如 district_code 自动变成 districtCode字段类型映射需要人工核对一遍。关键要检查三个地方TableName(t_place) public class Place { TableId(type IdType.AUTO) private Long id; TableField(place_name) private String placeName; TableField(district_code) private String districtCode; private BigDecimal longitude; private BigDecimal latitude; }这个实体类对应 t_place 表有两点值得注意。TableName 明确指定表名因为类名 Place 和表名 t_place 对不上不指定的话运行时会报 Table place doesnt existTableId 指定主键字段和自增策略mybatis-plus 默认主键策略是雪花 id不改成 AUTO插入数据时主键会被填成一串雪花数而表主键是自增的两边一冲突就会出现各种诡异问题。longitude 和 latitude 用 BigDecimal 而不是 double是因为经纬度是精度敏感字段double 的浮点误差在坐标展示上会出问题。反向生成完之后建议把实体类和表结构对照检查一遍字段比表少、类型对不上是常态。尤其是 tinyint 类型生成工具可能映射成 Integer也可能映射成 Boolean要看源码里 0 和 1 具体代表什么业务含义别到了写查询条件判断时才反应过来类型错了。实体和 sql 表结构对齐是“已调试”项目里最容易被忽略却最影响后续开发的一步。4. 把“已调试”变成“本地可运行”配置修改与启动自测全流程4.1 压缩包三步体检目录、配置、数据库先别急着启动“已调试”三个字是最有迷惑性的。作者调试好的环境跟你的环境大概率不一致他 mysql 密码是 root你的不是他 jdk 是 1.8你装的是 17他接口跑在 8080你 8080 被占了。我拿到任何 springboot 源码包第一步是体检不是启动。第一步看目录。标准 springboot 项目结构应该包含 src/main/java、src/main/resources外加 sql 或 db 目录、doc 目录。先搞清楚哪些是代码、哪些是文档、sql 文件有几份。有的压缩包 sql 分 init 和 update 两份别只导一份。第二步读配置。打开 src/main/resources/application.yml记下数据库名、端口、上传路径、context-path再去 mysql 里建对应名字的库。第三步导数据。按照第 3 章的 source 方式把 sql 执行完确认表建好了再回头启动项目。这三步顺序不能反我见过太多人跳过体检直接 mvn spring-boot:run然后花一小时排查根本不是代码的问题。提示如果压缩包里附带 README 或部署文档先读它。很多作者会把数据库版本、jdk 版本、导入顺序写在里面照着做能省一半时间。体检的时候顺带看一眼 pom.xml 的父版本号。如果 springboot 是 3.x 开头本机 jdk 至少要 17如果是 2.x 开头jdk 1.8 或 11 都行。这个前置条件不满足后面全是编译报错属于启动前就能排掉的雷。项目结构上还要看一眼前端是不是 vue 工程如果源码里有单独的 frontend 目录且 static 目录下没有 index.html说明前端产物还没打进去这一步在第 5 章会详细说。4.2 application.yml 里必须改的参数数据源、端口、日志、上传路径配置是整个项目能不能在别人机器上跑起来的关键我每次改配置都按数据源、端口、日志、上传路径四类过一遍。下面是这类系统中很常见的一份 application.ymlserver: port: 8080 servlet: context-path: / spring: datasource: url: jdbc:mysql://localhost:3306/city_geo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 20MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl逐条说必改项。url 里数据库名 city_geo 要改成你实际建的库名serverTimezoneAsia/Shanghai 必须保留mysql 8 的驱动对时区敏感不指定会直接报连接超时的错useSSLfalse 是为了去掉本地连接时的 ssl 警告日志不影响功能但能让日志清爽。password 我特意加了双引号如果密码里有特殊字符比如 或 #不带引号会被 yaml 解析成别的含义这是一个特别隐蔽的坑。driver-class-name 在 mysql 8 下必须写 com.mysql.cj.jdbc.Driver老的 com.mysql.jdbc.Driver 在新驱动里已经移除写了启动时直接报找不到类。日志配置里 log-impl 指定 mybatis-plus 把执行的 sql 打印到控制台这是排查问题最重要的手段一般我会建议保留等确认稳定了再改成不打印不然日志量太大。上传路径如果源码里用绝对路径比如上传图片到 /var/www/upload你要检查本机有没有这个目录没有就创建或者改成相对路径否则传文件时接口直接报 IOException。context-path 这一项如果配了 /geo所有接口都要带 /geo 前缀前端请求和后端拦截器都要对齐不然后面全是 404。4.3 启动和自测的命令与顺序maven、idea、swagger 一条线配置改完启动有两种方式。命令行方式适合确认依赖和环境idea 方式适合断点调试。先看命令行# 先编译打包跳过测试避免单元测试卡住构建 mvn clean package -DskipTests # 直接启动 java -jar target/city-geo-0.0.1-SNAPSHOT.jarmvn clean package 会把项目打成一个可执行 jar-DskipTests 是跳过测试类这个参数非常实用因为课设源码里的测试类很多是作者写了一半的不跳过很容易编译失败。如果你的 maven 本地仓库是空的第一次执行会下载大量依赖耗时较长且可能因为网络原因失败多执行两次是正常的不是代码问题。我实际调试时用的是 idea。导入方式File → Open选中解压后的文件夹idea 识别到 pom.xml 会把它当成 maven 项目右侧 maven 面板刷新后等依赖下载完然后找到主类也就是带 SpringBootApplication 的那个类右键 Run。启动成功后看控制台最后的日志找到 Tomcat started on port(s): 8080 这一行。看到这行才算启动成功之前都是白等。如果一直卡在日志里刷 sql 语句说明连接数据库成功了但可能在做初始化别急着关。自测我按三步走先用浏览器访问登录页确认页面能打开再用 swagger 或直接 curl 打一个需要登录的接口确认拦截器逻辑正常最后在页面上做一次完整的增删改查验证数据库写入和读取。这三个动作各覆盖一层页面通只能说明静态资源和路由没问题接口通才能说明 service 和 mapper 没问题。# 接口自测登录后拿 cookie curl -X POST http://localhost:8080/api/login -H Content-Type: application/json -d {username:admin,password:123456} # 带 session 访问分页接口 curl -b cookies.txt http://localhost:8080/api/place/page?pageNum1pageSize10用 curl 的好处是能直接看到返回的 json 结构不需要开 postman。如果项目用的是 jwt那就把返回的 token 放到 Authorization 请求头里。自测时看到返回的 json 里 total 字段跟数据库里表行数一致数据链路就算真正通了。如果登录一直失败先看控制台里打印出的 sql 语句是不是真的把参数传进去了很多“接口报错”最后都是参数名对不上导致的。5. 避坑记录springboot 项目跑不起来时问题多半在这五个位置这里讲的五条全是“已调试”源码换环境后最容易踩的坑。每一条我都按现象、原因、解决三步写你照着排就行。5.1 springboot 版本太高javax/jakarta 跨代翻车现场现象编译或启动时报错一片红字里能看到 cannot find symbol指向 servlet、annotation 这类包。 原因springboot 3.x 把原来的 javax 命名空间整体迁移到了 jakarta源码里 import javax.servlet.*在 3.x 环境下直接找不到类。你的 maven 父工程如果配的是 spring-boot-starter-parent 3.x而源码是按 2.x 写的就会出现跨代翻车。 解决降回 2.x 是最省事的做法也是课设里最常用的方案。在 pom.xml 里把 parent 版本改成 2.7 系刷新 maven 后全项目重新编译。jdk 是 17 以上时2.7 也能正常运行。如果坚持用 3.x就得全局替换 javax 为 jakarta工作量不小而且容易漏改不建议赶工期时做。这种问题光看报错会觉得像玄学最后发现就是版本不匹配。遇到 springboot 项目先问三件事jdk 版本、springboot 版本、mybatis-plus 版本。这三个不对齐后面所有操作都建立在沙子上。5.2 数据库连接失败时区、驱动版本和密码里的特殊字符现象启动时提示 Access denied for user或者 Unknown database或者 Communications link failure还有一种在 mysql 8 驱动下报 The server time zone value 错误。 原因三类原因完全不同。Access denied 是用户名密码不对注意源码里的密码可能是作者本机密码Unknown database 是你没建库或 application.yml 里库名跟实际建的不一致时区报错是 url 里少了 serverTimezone 参数。 解决Access denied 时先去 mysql 用你手头的账号试一下能不能登录别想当然改配置。Unknown database 就回到第 4 章用建库命令把库建出来。时区问题在 url 后面追加 serverTimezoneAsia/Shanghai。另外密码如果有 #、、% 这类特殊字符yaml 里一定要加引号否则 yaml 解析会把密码截断报的错还是 Access denied排查方向却全错。注意数据库连接失败时只看第一个报错。springboot 启动失败时错误信息会连在一起刷屏真正有用的只有最前面几行。往上翻日志找到 Exception 开头第一句再对症下药。5.3 sql 脚本执行“成功”了但表是空的现象sql 导入没有任何报错但打开数据库看表数量不对或者表建了但字段缺几个还有一种情况是表全建好但中文数据全是问号。 原因三种情况对应三个原因。第一种脚本里没有 USE 语句或 USE 的库名跟你连接的库不是同一个表建到了别的库第二种脚本用了当前 mysql 版本已废弃的字段类型导入报错被工具跳过但工具最后告诉你“成功”第三种字符集不对脚本本身是 utf8但导入时默认字符集是 latin1。 解决导入前先看脚本头部二十行确认有没有 CREATE DATABASE 和 USE导入时必须指定 --default-character-setutf8mb4导入完成后立即用 show tables 对照脚本里的建表语句数量。如果已经有数据且乱码了不要手动一条条改最干净的做法是 drop 库重新导入一遍。sql 脚本是可重复执行的资源重新来比修修补补快得多。5.4 页面打不开或样式全丢vue 打包产物没有放对地方现象后端启动正常接口用 curl 能返回数据但浏览器访问首页 404或者页面能打开但所有样式、图片全丢。 原因这个项目的前端多半是 vue 做的编译后的产物是 dist 里的 index.html 和一堆 js、css 文件。springboot 默认会去 src/main/resources/static 下找静态资源如果开发时前端跑在 node 的 9527 端口而后端打包时没有把 dist 复制进 static就会出现接口有、页面没有的情况。样式全丢则是 index.html 能加载但里面引用的 js/css 路径是绝对路径 /assets/xxx.js而项目部署时带了 context-path路径对不上。 解决把 vue 工程里 dist 目录下的文件全部复制到 src/main/resources/static 下启动后端后访问 http://localhost:8080/ 应该能看到登录页。如果资源路径带 /assets 前缀而项目有 context-path要么把 vue 的 publicPath 改成相对路径后重新打包要么把 context-path 去掉。检查顺序先看 static 里有没有 index.html再看 index.html 里引用的资源路径两步能定位九成问题。5.5 接口全部 401 或 404拦截器放行路径和上下文路径没对齐现象登录页能打开但输入账号密码点登录接口返回 401 未授权或 404已经登录成功调其他接口也是 401。 原因401 是拦截器没有放行登录接口登录请求被拦下来还没进 controller 就被打回去404 通常是项目配置了 context-path比如 context-path: /geo而前端请求还是发到 http://localhost:8080/api/login少了 /geo 前缀。 解决401 回去看第 2 章的 excludePathPatterns把 /api/login 加进去如果是登录后才能用的接口也 401那是登录后没有把信息写入 session拦截器判断不了要查登录逻辑里 session 或 token 有没有正确写入。404 则检查 application.yml 里 context-path 和前端 axios 的 baseURL 是否一致这两个必须对齐。我遇到过最隐蔽的情况是拦截器放行了 /api/login但 context-path 是 /geo实际请求路径是 /geo/api/login放行路径里没写 /geo照样 401。这种问题最快的排查方式还是看启动日志里实际打印的请求路径别凭记忆猜。6. 答辩前的最后一道工序改名、补数据、备演示把这套源码变成你自己的拿到源码和论文 PPT如果只是把服务跑起来就交上去答辩老师很容易看出这是下载的。我给你的建议是答辩前做三件事改名、补数据、备演示。改名不是改一个文件名就够。先用 idea 的全局替换把包名里的示例前缀改成你自己命名的包然后搜页面底部的版权字符串、网页 title、登录页 logo 文字这些暴露得最多。代码里搜“示例”“test”“admin”这三个词该换换该删删。这一步做完项目才看起来像你自己的。# 全局搜索常见的暴露字符串一条命令列出来 grep -r 示例\|Powered by\|Copyright --include*.html --include*.vue --include*.java .补数据比想象中重要。源码里的 sql 一般会附初始化数据但内容多半是作者随便填的。建议把你学校周边真实的地名、街道、小区数据替换进去数量不用多30 条左右就够演示但一定要覆盖“区划树下两层 地名 地址 楼栋”的完整链路。演示时老师会随手点一个区划节点如果你数据只有省一级下面全是空印象分直接没了。备演示指的是写一份演示脚本按“登录 → 按区划树下钻 → 录入一条新地名 → 列表检索 → 编辑”的顺序走一遍每个步骤提前想好讲什么。论文里不要贴大段代码放 ER 图、接口设计表和几个关键流程截图。我当年最惨的教训就是忘了改页面底部那一行作者信息答辩老师点开页面第一句就是“这项目是网上找的吧”从那以后我拿到“已调试”的源码第一件事就是全局搜版权和示例关键词。这套 springboot 地名地址系统的数据模型和接口套路很适合作为你第一个能完整讲明白的项目把上面三件事做完它才算真正是你的。希望帮到你。本文还有配套的精品资源点击获取