news 2026/9/8 10:44:40

SpringBoot + Vue 古建筑档案管理平台全栈开发实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot + Vue 古建筑档案管理平台全栈开发实战

别的不说,光“关中古建筑”这五个字,就够做出一套既有文化分量又有技术亮点的系统。项目题目是“springboot关中古建筑档案管理平台设计与实现,无论文,vue”,说白了就是一套典型的SpringBoot + Vue 前后端分离档案管理系统。很多同学看到这种题目的第一反应是“不就是增删改查吗”,但真上手做,你会发现古建筑档案这个业务场景,跟普通的学生管理、图书管理完全不是一个量级——它涉及大量图文档案、历史文保信息、测绘数据,甚至未来还可能接音视频素材。这篇文章我就围绕这个项目,把完整的设计思路、表结构、前后端核心逻辑、以及我在实际开发中踩过的坑,全部掰开揉碎讲清楚。无论你是拿它做毕设、课设,还是单纯想练手 SpringBoot + Vue 的全栈项目,这篇内容都适配。

1. 为什么说古建筑档案管理,比你想的更适合做成系统化项目

做项目最忌讳的就是选一个毫无业务深度的场景。很多同学选“图书管理”“商品管理”练手,写来写去就那么几张表,根本体现不出设计能力。古建筑档案管理这个场景天然适合作为系统开发题目,因为它自带几个特点:数据形态丰富(文本、图片、图纸、测绘数据)、查询条件复杂(朝代、类型、级别、地区、完损状态)、管理流程真实(建档、审核、更新、查阅),做出来之后既像回事,又能撑住技术深度。

关中地区古建筑资源密集,从周原遗址到西安城墙,从明清民居到宗教寺观,建筑类型跨度极大。不同类型的建筑,档案字段差异也很大。比如一座明代木结构戏楼,需要记录梁架结构、斗拱形制;而一处清代砖石牌坊,更关注雕刻题材和保存现状。这就要求档案系统不能只做一个“名称加简介”的简单录入,而需要设计出“通用字段 + 扩展信息”的弹性结构

从实际使用场景看,古建筑档案管理的核心用户是文保单位、规划部门和研究人员。他们日常的诉求无外乎几件事:查某座建筑的保护级别、调取某片区域的建筑分布、检索某个朝代的遗存列表、补充最新的修缮记录。这些操作落到底层,就是两大类接口:档案的CRUD多条件组合检索。所以系统不必做得花里胡哨,但要把“查得准、录得快、翻得顺”这三件事做扎实。

这套系统的整体业务流程,我个人建议这样划分:访客或普通用户负责浏览检索档案管理员负责新增和编辑档案系统管理员额外管理用户与操作日志。权限收敛清楚,后面做接口鉴权时逻辑就非常简单——三个角色三种权限,用拦截器就能搞定,不需要引入重型的权限框架。

2. 技术选型:SpringBoot 3 + Vue 3 的搭配逻辑与项目骨架搭建

2.1 后端为什么选 SpringBoot 而不是别的框架

SpringBoot 在这个场景里几乎没有悬念。第一,项目生态成熟,网上资料极多,你随便搜一个报错基本都有现成解答;第二,SpringBoot 的自动配置机制大幅降低了集成成本。做文件上传需要配置上传路径,做数据库访问需要配置数据源,做接口文档需要集成 Knife4j——这些在 SpringBoot 里都是配置项的事,写起来非常直接。

版本建议直接上SpringBoot 3.x,配套JDK 17。SpringBoot 3 相比 2.x 在性能和依赖管理上改进明显,而且现在很多教学资源、开源项目都已经切到 3.x。我的习惯是使用Spring Initializr创建基础工程,选好 Web、MySQL Driver、Lombok、Validation 这几个核心依赖。注意一定顺手勾上Spring Configuration Processor,后面写配置类时会有属性提示,能省不少事。

2.2 前端为什么建议 Vue 3 + Element Plus

Vue 3 的组合式 API(Composition API)写业务逻辑比选项式(Options API)更加集中。以档案表单为例,构建检索条件、重置表单、翻页查询这几个功能可以全部收敛到一个setup作用域里,逻辑上的关联比用methods分散写清晰得多。

UI 组件库选Element Plus,理由特别朴素——开箱即用。表格、分页、表单校验、日期选择器、图片上传预览,这些都是档案管理页面最常用的组件,Element Plus 都覆盖了。实际开发中可以节省大量时间。

另外,前端工程用Vite创建,命令是:

npm create vite@latest archive-frontend -- --template vue

创建完以后进入目录,安装项目依赖:

npm install

再单独安装路由、状态管理、UI 库和 HTTP 库:

npm install vue-router@4 pinia axios element-plus

2.3 前后端分离架构下的工程结构

工程上建议前后端分成两个独立目录管理:

archive-platform/ ├─ backend/ # SpringBoot 后端工程 ├─ frontend/ # Vue3 前端工程 └─ sql/ # 数据库初始化脚本

后端的包路径按功能模块划分,参考结构如下:

com.example.archiveplatform ├─ config/ # 跨域、文件上传、拦截器配置 ├─ controller/ # 控制层 ├─ service/ # 业务逻辑层 ├─ mapper/ # MyBatis-Plus 数据访问层 ├─ entity/ # 实体类 ├─ dto/ # 请求参数和返回参数对象 ├─ common/ # 统一返回结果、异常处理、常量 └─ utils/ # JWT、文件处理等工具类

这种分包方式强调“按技术层次分隔,按业务模块聚合”,开发时配合@RequiresPermissions这类注解做权限控制,也方便后期扩展。有一点值得提前说:controller 层尽量瘦,只负责接收参数、调用服务、返回结果;具体业务逻辑,比如文件存储、数据校验、分词检索的流程编排,全放到 service 层。这样写虽然初期代码量多一点,但排查问题时体验完全不同。

3. 数据库设计:从建筑档案的本质反推表结构

3.1 核心表 architecture_info 的设计思路

档案系统的核心表是建筑信息表,这个表的字段设计直接决定了整个系统的深度。我先把建议的表结构写出来,再逐一解释关键字段的考虑:

CREATE TABLE architecture_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', arch_code VARCHAR(64) NOT NULL COMMENT '建筑唯一编号', arch_name VARCHAR(128) NOT NULL COMMENT '建筑名称', arch_region VARCHAR(64) COMMENT '所在区域(如西安市碑林区)', arch_address VARCHAR(255) COMMENT '详细地址', dynasty VARCHAR(32) COMMENT '建造朝代', build_year VARCHAR(32) COMMENT '具体建造年代描述', arch_type VARCHAR(32) COMMENT '建筑类型(寺观/民居/楼阁/牌坊/城墙等)', protection_level VARCHAR(32) COMMENT '保护级别(全国重点/省级/市级/未定级)', structure_style VARCHAR(64) COMMENT '建筑结构(抬梁式/穿斗式/砖石结构等)', height_m DECIMAL(8,2) COMMENT '建筑高度(米)', area_sqm DECIMAL(10,2) COMMENT '占地面积(平方米)', condition_status VARCHAR(16) COMMENT '完损状况(完好/基本完好/局部损坏/严重损坏)', historical_bg TEXT COMMENT '历史沿革', cultural_value TEXT COMMENT '文化价值描述', protection_measure TEXT COMMENT '保护措施', survey_content TEXT COMMENT '测绘勘察记录', cover_image VARCHAR(255) COMMENT '封面图片路径', create_by BIGINT COMMENT '建档人ID', create_time DATETIME COMMENT '建档时间', update_time DATETIME COMMENT '更新时间', is_deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标记' ) COMMENT '古建筑基本信息表';

这里有几个字段需要特别说明。第一是arch_code建筑唯一编号,我推荐采用“区域首字母缩写 + 建筑类型缩写 + 序号”的组合规则,比如“GZ-MQ-00001”,意思是“关中-民居-第1号”。有了这个规则,即使建筑名称有重复,档案也能精确定位。第二是dynastybuild_year分开设计,因为很多古建筑只知道朝代,具体纪年已经不可考了。把建造年代描述单独拆成一个字符串字段,是为了兼容“约明正德年间”这种模糊表述,登记时不必为了填日期格式而卡壳。

condition_status这个字段就是管理者最常用的筛选依据。文保单位日常工作中经常要统计“区域内有多少濒危建筑需要抢修”,有了这个字段,一条条件查询就能拉出清单。所以不要为了少写几个字段而砍掉它,业务中的高频查询条件,都应该在表设计中直接预留字段

3.2 附属表:档案图片、用户与操作日志

古建筑不能只存一堆文字,照片和测绘图纸一样关键。建议建一张archive_image表,与建筑档案形成一对多关联:

CREATE TABLE archive_image ( id BIGINT AUTO_INCREMENT PRIMARY KEY, arch_id BIGINT NOT NULL COMMENT '关联建筑ID', image_name VARCHAR(128) COMMENT '图片说明', image_url VARCHAR(255) NOT NULL COMMENT '图片访问路径', image_type VARCHAR(16) DEFAULT 'photo' COMMENT 'photo-照片 drawing-图纸', upload_by BIGINT, upload_time DATETIME ) COMMENT '建筑档案图片表';

用户表不复杂,字段包括usernamepassword(BCrypt 加密)、real_namerole(ADMIN / ARCHIVER / VIEWER)、statuscreate_time。比较容易被忽略的是操作日志表,建议预留:

CREATE TABLE operation_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT, username VARCHAR(64), operation VARCHAR(64) COMMENT '操作类型:新增/编辑/删除/查询', detail VARCHAR(500) COMMENT '操作描述', create_time DATETIME ) COMMENT '操作日志表';

登录用户做新增、修改、删除操作时,顺手往日志表写一条记录。这个表不仅是文保系统的合规要求,也给你答辩和演示时增加一个“系统完整度高”的亮点——面试官或者老师问“你怎么保证数据可追溯”,这一张表就答上来了。

4. 后端核心开发:统一返回、异常处理、登录鉴权与文件上传

4.1 统一返回结构与全局异常处理

开发前后端分离项目,第一步不是写业务接口,而是先把“数据怎么往返”的规矩定下来。我推荐定义一个统一的Result类:

@Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }

配合全局异常处理器,把参数校验异常、业务异常、系统异常统一拦截,前端拿到非 200 的 code 时统一弹出提示。

4.2 登录鉴权:JWT 无状态认证实现

不引入 Spring Security 和 Sa-Token 这类重框架的话,自研一个轻量级 JWT 认证是最合适的方式。登录成功后生成 token 返回给前端,前端后续请求在请求头里带上Authorization: Bearer token。后端用拦截器解析 token,从Claims里取出用户ID和角色,塞进ThreadLocal供当前请求使用。

核心步骤如下:加依赖jjwt,写一个JwtUtil工具类负责生成和解析 token,然后写一个AuthInterceptor拦截器,放行/api/auth/login路径,拦截其余请求。角色权限检查,在拦截器里判断一下@RequireRole注解即可,普通用户和管理员的接口访问边界一下就清晰了。

4.3 档案多条件组合查询:MyBatis-Plus 的 QueryWrapper 实践

档案检索是整个系统用得最多的接口。前端传参数:关键词、朝代、类型、保护级别、完损状况、区域,后端在 service 层组装查询条件。MyBatis-Plus 的LambdaQueryWrapper写法非常直观:

public Page<ArchitectureInfo> searchArchives(ArchiveQueryDTO dto) { LambdaQueryWrapper<ArchitectureInfo> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(dto.getKeyword()), ArchitectureInfo::getArchName, dto.getKeyword()) .eq(StringUtils.hasText(dto.getDynasty()), ArchitectureInfo::getDynasty, dto.getDynasty()) .eq(StringUtils.hasText(dto.getArchType()), ArchitectureInfo::getArchType, dto.getArchType()) .eq(StringUtils.hasText(dto.getProtectionLevel()), ArchitectureInfo::getProtectionLevel, dto.getProtectionLevel()) .eq(StringUtils.hasText(dto.getConditionStatus()), ArchitectureInfo::getConditionStatus, dto.getConditionStatus()) .eq(StringUtils.hasText(dto.getArchRegion()), ArchitectureInfo::getArchRegion, dto.getArchRegion()) .orderByDesc(ArchitectureInfo::getCreateTime); Page<ArchitectureInfo> page = new Page<>(dto.getPageNum(), dto.getPageSize()); return architectureInfoMapper.selectPage(page, wrapper); }

这里每一个StringUtils.hasText()判断,都是为了“用户没填这个条件就不拼接” —— 这是组合查询的核心写法,逻辑清晰又防 SQL 注入,比手动写<script>里的动态 SQL 要轻便得多。

4.4 图片上传与访问映射

档案系统不可能不上传图片。实际开发中文件上传有两个容易踩的坑:第一是上传目录的持久化问题,第二是访问路径的映射问题。

上传保存逻辑建议单独写一个FileStorageService,把文件保存到服务器本地指定目录,同时把文件的相对访问路径写入数据库。SpringBoot 中做静态资源映射的配置如下:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { String uploadPath = System.getProperty("user.dir") + File.separator + "upload" + File.separator; registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + uploadPath); } }

这里必须注意:addResourceLocations的路径必须以file:协议开头,并且以/结尾,否则映射不生效。这个配置埋下的坑是最经典的异常之一:数据库里存了图片路径,前端能读到 URL,但浏览器访问 404。排查半天最后发现就是少了最后一个斜杠。

5. 文件上传的坑:Tomcat 默认请求体大小引发的血案

档案图片一个普遍场景是拍好的现场照片,手机拍出来的 JPG 动辄 5MB、8MB。这个时候很多人会踩到 SpringBoot 内置 Tomcat 的默认上传限制——默认单文件最大 1MB,请求体最大 10MB。一旦前端上传超过这个大小,后端直接报异常,而且异常信息还不直观。

解决方式是在application.yml中显式配置:

spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB

这两个配置的含义:max-file-size限制单个文件大小,max-request-size限制单次请求携带的文件总量。如果系统后续要支持多张现场照片一次性上传,max-request-size要按平均文件大小乘以预估张数来预留。比如每张 8MB,一次最多传 6 张,那请求体至少给 50MB 才稳妥。

这里还有个细节:前端的上传组件也要同步做大小限制和类型校验,别把校验压力全丢给后端。Element Plus 的el-upload组件中,before-upload钩子里判断文件类型和大小,不符合规则直接拦截并提示,这样既节省带宽,也减少后端无谓的异常记录。

6. 前端实战落地:从登录页到档案综合看板

6.1 路由与权限控制

前端路由设计上,我建议直接拆成两个层级:公开路由和需要登录的路由。/login是公开路由,其余如/dashboard(数据看板)、/archive/list(档案列表)、/archive/detail/:id(档案详情)、/archive/edit(档案编辑)全部挂到需要登录的父路由下。

路由守卫的逻辑很直接:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } })

前端权限控制只是提升用户体验,真正安全的边界在后端拦截器。这一点我在带新人开发时反复强调:前端可以给你看友好的页面跳转,但数据安全必须由后端保障,不能依赖前端隐藏按钮

6.2 档案列表页:表格、搜索表单与分页联动

档案列表页是整个前端最核心的页面,没有之一。左侧放检索条件区,右侧放表格和分页,这是最顺手的信息架构模式。检索条件区包含关键词输入框、朝代下拉、建筑类型下拉、保护级别下拉、完损状况下拉,再加“查询”和“重置”两个按钮。

页面逻辑上注意几点:搜索条件变化时要重置页码到 1,否则会出现“从第 8 页开始搜,搜出来只有一页数据”的奇怪场景。翻页时必须携带当前搜索条件,否则翻到第二页时搜索条件丢失,又回到全量列表。这两点属于细节,但真实使用场景中极其重要。

表格列建议展示:档案编号、建筑名称、图片缩略图、朝代、建筑类型、保护级别、所在区域、完损状况、建档时间、操作按钮。图片缩略图使用 Element Plus 的el-image组件,设置preview-src-list属性即可实现点击大图预览,不需要额外写弹窗代码。

6.3 档案录入编辑:一对多图片上传实战

录入编辑页面是体现系统专业度的地方。表单本身不复杂,关键是图片上传区域的联动。我的做法是:编辑时进入页面先从后端拉取该档案的全部图片列表回显,新增上传一张成功后,把后端返回的图片 URL 追加到列表尾部;编辑提交时,表单数据同时提交建筑基础信息字段 + 图片 URL 数组,后端先比对已有图片做增量保存。

el-upload组件不想使用自动上传,就设置:auto-upload="false",手动收集fileList后用FormData统一提交。实际操作中我用的是单个即时上传、表单同步保存的模式:选择好图片立刻传到后端,后端返回图片 URL,前端保存 URL 到表单变量里,最后点“提交”时只提交 URL 列表而不是二进制文件。这样做最大的好处是:即使后面用户放弃编辑,已经上传的图片也会保留在服务器上,不会因为浏览器刷新而丢失。

6.4 数据看板:让档案系统的“文化分量”可视化

为了体现这个平台的数据分析价值,建议做一个简单的数据看板,包含几个统计卡片:档案总数、全国重点文保数量、濒危建筑数量、按朝代分布的柱状图、按建筑类型分布的饼图。

实现方式不复杂,后端写一个统计接口,一次性返回多个维度的聚合数据:

@GetMapping("/stats/overview") public Result<Map<String, Object>> getOverview() { Map<String, Object> map = new HashMap<>(); map.put("total", architectureInfoMapper.selectCount(null)); map.put("nationalCount", architectureInfoMapper.selectCount( new LambdaQueryWrapper<ArchitectureInfo>() .eq(ArchitectureInfo::getProtectionLevel, "全国重点"))); map.put("endangeredCount", architectureInfoMapper.selectCount( new LambdaQueryWrapper<ArchitectureInfo>() .in(ArchitectureInfo::getConditionStatus, "局部损坏", "严重损坏"))); // 朝代分布、类型分布按照 group by 查询 return Result.success(map); }

前端拿数据后用 ECharts 渲染柱状图和饼图。这个页面做出来,整个系统的完成度和演示效果会有一个很明显的提升,问你“数据怎么用”的时候也有话可说。

7. 前后端联调中的几个实操经验

7.1 跨域问题的处理

开发环境前端跑在 5173 端口,后端跑在 8080 端口,必然存在跨域。方式之一是配置允许跨域,写一个CorsConfig

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

这里提醒一点:如果后端开启了allowCredentials(true),前端的 axios 请求也要设置withCredentials: true,否则携带 Cookie 的请求会被浏览器拦截。如果这个项目为了简化解耦没用 Cookie,用的是 localStorage 存 token,那就无所谓,按实际方案调整。

更推荐的做法是:不依赖后端跨域配置,而是在前端开发时用 Vite 的代理:

// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

这样前端请求/api/xxx时,开发服务器自动转发到后端8080端口,浏览器眼里就是“同源请求”,省去跨域配置的麻烦。生产环境部署时再让 Nginx 统一接收请求后转发到后端服务,天然规避跨域问题。这是目前主流的实践方式,也是面试常问的点,建议亲手配一遍。

7.2 axios 拦截器的封装

我通常会在src/utils/request.js里封装一个 axios 实例,统一配置baseURL和请求头,再设置请求拦截器和响应拦截器:

  • 请求拦截器:从 localStorage 取 token,存在则附加到请求头
  • 响应拦截器:拿到后端返回数据后判断 code,如果是 200 直接返回response.data.data,如果是 401 就清除本地 token 并跳转登录页,其他错误码统一弹 ElMessage 提示
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( response => { const res = response.data if (res.code === 200) { return res } if (res.code === 401) { localStorage.removeItem('token') router.push('/login') } ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message || '请求失败')) }, error => { ElMessage.error(error.message || '网络错误') return Promise.reject(error) } ) export default request

这套封装之后,业务代码里发起请求变得非常干净:

const res = await request.get('/archive/page', { params: query })

不用每个页面都写错误处理逻辑,体验统一且代码整洁。

8. 古建筑档案系统的特色功能:让项目脱颖而出

如果只是标准的增删改查,答辩时很难有亮点。这个系统由于业务领域的特殊性,有几个可以低成本实现但极具看点的功能。

8.1 朝代与类型组合筛选

古建筑区别于普通房屋的关键属性是“年代”和“类型”。系统里朝代下拉和类型下拉几乎可以覆盖所有筛选场景。在列表页和统计页均提供组合筛选入口,可以让业务人员快速回答诸如“关中地区清代的民居有多少”“明代戏楼分布在哪些区域”这些问题。这个功能的实现成本不高,但带来的产品价值感极强——因为它是贴合业务场景的查询模式。

8.2 档案修订历史记录

每一条档案从创建至今被谁修改过、修改了什么,都应该可追溯。实现上可以简单在operation_log表里记录操作类型、操作人、操作时间和操作描述。更细的做法是做一个archive_log表,每次编辑时用 JSON 记录变更前和变更后的关键字段值。这个版本记录功能在档案管理系统中属于硬需求,也是系统完整度的重要加分点。

8.3 濒危建筑看板

结合condition_status字段,数据看板中单独展示“完损状况分布”和“待修缮建筑清单”,并支持一键筛选。这个功能对文保单位的实际工作非常有用,也是展示系统“为业务服务”的关键证据。

9. 部署上线:从本地跑到服务器

课程设计和毕业设计做到本地能跑通通常就够用了,但如果你想把这个项目完整地部署到云服务器上,或者做一次技术分享,部署环境建议这样安排:

  • 前端构建:npm run build,产物是dist/目录下的静态文件
  • 后端打包:mvn clean package -DskipTests,产物是.jar文件
  • 服务器环境:CentOS 7 + Nginx + JDK 17 + MySQL 8

Nginx 配置的要点是:访问根路径或前端路由时,返回dist/index.html;访问/api路径时,反向代理到后端8080端口。同时配置静态资源缓存:

server { listen 80; server_name your-domain.com; location / { root /opt/archive-frontend/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /upload/ { alias /opt/archive-backend/upload/; } }

有一个部署时的关键坑是:SpringBoot 的 jar 包运行时,工作目录不一定是你解压后的目录。如果上传的图片存储在“当前目录/upload”下,启动 jar 包的命令需要先cd到约定目录再执行,或者直接用绝对路径指定上传目录,否则很容易出现“图片明明上传成功了,重启服务后全丢了”的情况。

10. 实测中绕不开的数据库和框架选型细节

10.1 版本兼容性:SpringBoot 3.x 与 MyBatis-Plus

SpringBoot 3 刚面世的时候,和 MyBatis-Plus 的兼容问题是热门大坑。使用mybatis-plus-spring-boot3-starter这个专用依赖就可以解决版本冲突。引入方式:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.5</version> </dependency>

同时注意spring-boot-starter-web中已经内置了 Jackson 来处理 JSON 序列化,不需要额外再引入 fastjson,减少依赖冲突的可能。时间字段的序列化建议配置统一格式:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

如果不配置时区,部署到云服务器(默认 UTC 时区)后会出现时间相差 8 小时的“灵异事件”。我当年第一次部署时没配这个,前端页面所有时间都差了 8 小时,查了好一阵才意识到是时区问题。这个坑属于一定提前规避的典型项目。

10.2 密码加密处理

用户密码绝对不能明文存入数据库。建议使用 Spring Security 自带的BCryptPasswordEncoder,尽管没有引入完整的 Spring Security 框架,单独引入spring-security-crypto依赖即可使用 BCrypt 加密:

String encoded = new BCryptPasswordEncoder().encode(rawPassword); boolean matches = new BCryptPasswordEncoder().matches(rawPassword, encodedPassword);

BCrypt 加密自带盐值,同一密码每次加密结果不同,安全性远高于固定 MD5。注册时存入密文,登录时用matches校验即可。这样既不引入庞大的安全检查链,又保证了密码存储的安全标准。

11. 如果再给我一次机会重做这个项目,我会怎么规划

这个项目做到最后的体会是:一个全栈项目的成败,不在于你用了多少新技术,而在于数据模型是否贴合业务、接口设计是否顺手、坑有没有提前避开。古建筑档案管理平台的复杂度,让整个开发过程覆盖了前后端分离项目的大多数典型问题——文件上传、权限校验、多条件检索、数据统计、静态资源映射、跨域配置、部署上线。把这些都走一遍之后,可以非常有底气地说:SpringBoot + Vue 全栈开发的常见套路和经典坑,你已经基本全部见过了。

如果还有余力,可以考虑在现有系统上增加一些进阶能力。最推荐的方向是接入 GIS 地图展示,用 Leaflet 或高德地图把建筑按经纬度标注出来,形成“关中古建筑分布一张图”。这个功能的价值和视觉效果,会让整个系统在同类项目中明显上个台阶。具体实现思路是:建筑信息表增加longitudelatitude两个字段,档案录入时选择或手动填写坐标,后端提供一个按坐标范围查询建筑的接口,前端在地图上渲染自定义图标,点击图标弹出建筑卡片,跳转详情页。这一步做完,系统从“电子档案柜”升级成了“可交互的遗产地图”,意义完全不同。

另外,如果团队里有做三维建模的技术储备,还可以考虑用 Three.js 加载古建筑的 3D 模型预览,比如把某座标志性建筑的简易模型放上去,配上旋转、缩放操作,这在成果展示环节会非常吸睛。但项目有交付周期有取舍,这点放到规划里量力而行就好。

最后说说我一直保留的一个习惯:数据库设计阶段花的时间越多,开发阶段的返工就越少。做这个系统时,我先画了一遍完整的 ER 图和接口清单,再开始写代码,后面几乎是“流水线式”地完成。如果你也准备做这个方向的系统,强烈建议先花一个晚上把表结构和接口定义琢磨清楚。结构不牢靠,后面每一行代码都在还债。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 10:42:59

企业IM选型指南:私有化部署、消息API与组织架构集成深度解析

最近几年&#xff0c;时不时就会有朋友来问我企业IM&#xff08;内部即时通讯工具&#xff09;到底该怎么选。钉钉、企业微信这些SaaS工具确实用起来省心&#xff0c;但一旦涉及到内部系统对接、敏感数据留痕、组织架构二次调整&#xff0c;你会发现传统IM工具的“手”根本伸不…

作者头像 李华
网站建设 2026/9/8 10:41:36

怎么做一个知识竞赛答题小程序?零代码搭建+自动判分出榜全流程

公司、学校搞知识竞赛&#xff0c;发个答题小程序比拉群刷题体面多了。用零代码答题工具&#xff0c;微信里就能答、自动判分、实时出排行榜。本文以龙艺秀为例&#xff0c;讲清怎么搭。一、选答题工具看哪几点&#xff1f;1. 上手快不快&#xff1a;能不能不培训就出题&#x…

作者头像 李华
网站建设 2026/9/8 10:40:56

把NLP大作业源码包变成自己的高分项目:从解压到二次开发

简介&#xff1a;面向计算机相关专业学生与课程设计实践者&#xff0c;这是一份经导师指导并获98分认可的自然语言处理课程高分项目。资源以源码、说明文档和辅助文本三种形式组合&#xff0c;适合作为期末大作业、课程设计的参考资料&#xff0c;也可用于二次开发与项目实战练…

作者头像 李华
网站建设 2026/9/8 10:37:05

DeepSeek替代Copilot、Claude、Codex:AI编程工具成本优化实战

最近在几个技术社群里&#xff0c;看到不少人在讨论AI编程工具的成本问题。一个典型的前端开发者&#xff0c;可能同时订阅着GitHub Copilot、用着Claude的付费版、偶尔还要调用OpenAI的Codex API——每月在AI编程工具上的开销轻松超过100美元。更让人头疼的是&#xff0c;这些…

作者头像 李华
网站建设 2026/9/8 10:35:05

时序逻辑硬核解析:从触发器到状态机的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华