资讯动态

公墓陵园管理系统源码:SpringBoot+Vue2实现墓位状态流转与安葬登记

发布时间:2026/10/18 16:31:09 来源:尧图企业网站定制
简介这份公墓陵园管理系统源码面向陵园运营单位、行业软件开发者及需要信息化改造项目的技术人员侧重销售提成管理与全面信息化业务可解决传统公墓管理软件操作复杂、管理不到位的问题。资源包共730个文件约3.31MB以364个gif与74个png界面素材、66个aspx页面、81个cs后台代码、32个css与22个js脚本为主另含dll组件、SQL Server数据库文件mdf、ldf、db及少量psd、swf等资源构成一套完整的C/S架构网络版系统。系统采用微软SQL Server 2008作为后台数据库具备较强的大数据处理性能可满足日益增长的使用需求。目前已有1817人学习下载。读者可从中获取完整的项目目录结构、页面与业务逻辑代码、数据库文件及服务接口示例适合用于二次开发、课程设计或陵园信息化方案的参考与排错思路借鉴。1. 公墓陵园管理系统源码从一块墓碑的排期说起去年帮一个做民政信息化的朋友看项目他吐槽说园区里最怕的不是清明而是平时零零散散的安葬预约——家属拿着纸质单子来业务员翻台账本找穴位一个不留神就把已经卖出去的墓位又排了一遍等到下葬当天才发现撞位场面非常难堪。这类问题不是靠加人就能解决的本质是墓位、墓区、碑型、安葬人、缴费记录这几张表之间没有强关联。这份公墓陵园管理系统源码就是冲着这个场景去的把墓区规划、墓位销售、安葬登记、碑文管理、费用缴纳和统计报表串成一条线让业务员在电脑前点几下就能确认某个穴位到底能不能用。它适合两类人一类是接民政、殡葬信息化外包的开发者想找个能直接改的业务底座另一类是想学一套完整 CRUD 权限 报表后台的练手者因为它的业务表设计比普通商城后台更贴近真实约束。2. 技术选型与目录结构一套能直接跑的后台骨架2.1 为什么是这套组合拿到源码先别急着改业务先看它选了什么。这类管理系统不需要高并发但要求部署简单、二次开发门槛低所以常见做法是SpringBoot MyBatis-Plus MySQL Vue2 ElementUI的前后端分离结构。后端负责权限、业务逻辑和数据校验前端用 ElementUI 的表格和表单快速搭出业务页面。数据库用 MySQL 5.7 或 8.0 都行字符集建议utf8mb4因为碑文和逝者姓名里偶尔会出现生僻字utf8存不下会直接报错这是血泪经验。选 SpringBoot 的另一个原因是它自带 Tomcat打成 jar 包丢到服务器上java -jar就能起不用折腾外置容器。MyBatis-Plus 则省掉了大量单表 CRUD 的 XML墓位、墓区这种标准增删改查基本不用手写 SQL只有统计报表才需要自己写查询。2.2 目录结构长什么样源码解压后一般分成两个顶层目录后端和前端各一个结构大致如下cemetery-admin/ ├── cemetery-backend/ # 后端服务 │ ├── src/main/java/com/cemetery/ │ │ ├── controller/ # 接口层墓位、墓区、安葬等 │ │ ├── service/ # 业务逻辑 │ │ ├── mapper/ # MyBatis-Plus 数据访问 │ │ ├── entity/ # 数据库实体 │ │ ├── config/ # 权限、跨域、拦截器配置 │ │ └── common/ # 统一返回、异常处理 │ ├── src/main/resources/ │ │ ├── application.yml # 数据库、端口配置 │ │ └── mapper/ # 自定义 SQL │ └── pom.xml ├── cemetery-frontend/ # 前端工程 │ ├── src/ │ │ ├── api/ # 接口封装 │ │ ├── views/ # 页面墓区管理、墓位管理、安葬登记 │ │ ├── router/ # 路由与菜单 │ │ └── store/ # 登录态、用户信息 │ └── package.json └── sql/ └── cemetery.sql # 建库建表脚本 初始数据这个结构是典型的若依风格如果你之前接触过若依上手会非常快。controller层只做参数接收和返回封装真正的校验放在service比如判断墓位是否已售、安葬人是否重复登记都写在 service 里方便复用。2.3 数据库表设计的几个关键点业务能不能跑顺八成看表设计。这套源码里核心表大概有这些表名作用关键字段cemetery_area墓区区名、行数、列数、单价cemetery_plot墓位所属区、排号、位号、状态deceased逝者/安葬人姓名、身份证、安葬时间burial_record安葬登记墓位ID、逝者ID、经办人payment缴费记录墓位ID、金额、缴费类型sys_user系统用户账号、角色、所属园区墓位表里有个status字段一般用 0 表示可售、1 表示已预订、2 表示已售、3 表示已安葬。这个状态机是整个系统的核心所有业务操作都要围着它转。墓区表存行数和列数是为了前端能自动生成墓位网格图不用一个个手动录入。3. 核心模块拆解墓位状态流转与安葬登记3.1 墓位状态流转的代码实现墓位从可售到安葬中间要经过预订、缴费、登记几个动作每一步都要改状态。如果直接update而不校验当前状态就会出现「已售墓位被再次预订」的翻车现场。源码里通常会在 service 层做状态校验下面是一段典型的实现Service public class PlotServiceImpl extends ServiceImplPlotMapper, Plot implements PlotService { Override Transactional(rollbackFor Exception.class) public void changeStatus(Long plotId, Integer targetStatus) { Plot plot this.getById(plotId); if (plot null) { throw new BusinessException(墓位不存在); } // 状态只能按 0-1-2-3 单向流转禁止回退 if (targetStatus plot.getStatus()) { throw new BusinessException(当前状态不允许此操作); } // 已安葬的墓位不允许再改 if (plot.getStatus() 3) { throw new BusinessException(该墓位已完成安葬无法变更); } plot.setStatus(targetStatus); this.updateById(plot); } }这段代码的关键在Transactional和状态判断。Transactional保证改状态和后续写记录在同一个事务里任何一步失败都回滚不会出现「状态改了但记录没写」的黑匣子。状态判断用targetStatus plot.getStatus()拦住回退和重复操作比在每个接口里单独写 if 更省心。参数plotId是墓位主键targetStatus是目标状态值调用方传 1 就是预订传 2 就是售出。3.2 安葬登记接口的完整链路安葬登记是业务最重的一步它要同时写burial_record、改plot状态、可能还要更新逝者信息。一个请求从进入到返回链路是这样的PostMapping(/burial/register) public AjaxResult register(RequestBody BurialDTO dto) { // 1. 校验墓位是否可安葬 Plot plot plotService.getById(dto.getPlotId()); if (plot null || plot.getStatus() 3) { return AjaxResult.error(墓位不可用); } // 2. 校验逝者是否已登记过安葬 Long count burialRecordService.count( new LambdaQueryWrapperBurialRecord() .eq(BurialRecord::getDeceasedId, dto.getDeceasedId())); if (count 0) { return AjaxResult.error(该逝者已有安葬记录); } // 3. 写入安葬记录 BurialRecord record new BurialRecord(); record.setPlotId(dto.getPlotId()); record.setDeceasedId(dto.getDeceasedId()); record.setBurialTime(dto.getBurialTime()); record.setOperator(SecurityUtils.getUsername()); burialRecordService.save(record); // 4. 更新墓位状态为已安葬 plotService.changeStatus(dto.getPlotId(), 3); return AjaxResult.success(登记成功); }BurialDTO是前端传过来的数据传输对象包含墓位 ID、逝者 ID、安葬时间。第 2 步的重复校验很关键现实里家属可能在不同窗口重复提交没有这层校验就会产生两条安葬记录后面统计报表直接对不上。第 4 步复用changeStatus而不是直接 update是为了让状态流转规则只有一处改起来不会漏。3.3 墓区网格图的前端渲染墓区管理页面通常要画一个网格让业务员直观看到哪些位子空着。前端拿到墓区的行数、列数和墓位列表后用 ElementUI 的el-row/el-col或者原生 table 渲染// 根据墓区行列数生成网格已售墓位标红 computed: { gridData() { const rows this.area.rowCount; const cols this.area.colCount; const map {}; this.plotList.forEach(p { map[${p.rowNo}-${p.colNo}] p; }); const grid []; for (let r 1; r rows; r) { const line []; for (let c 1; c cols; c) { line.push(map[${r}-${c}] || { rowNo: r, colNo: c, status: -1 }); } grid.push(line); } return grid; } }gridData把一维的墓位列表转成二维网格status为 -1 表示该位置还没建墓位前端渲染成灰色。点击某个格子时把plotId带到右侧详情面板业务员就能直接操作。这种「先建区、再生成位」的做法比让用户一个个录入墓位编号高效得多也是这类系统里最值得抄的设计之一。4. 环境搭建与启动从建库到登录后台4.1 后端启动步骤先把数据库准备好再改配置最后启动顺序别乱。第一步建库并导入脚本mysql -u root -p -e CREATE DATABASE cemetery DEFAULT CHARACTER SET utf8mb4; mysql -u root -p cemetery sql/cemetery.sql第二步改application.yml里的数据库连接和端口server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/cemetery?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.DriverserverTimezone一定要写不写在某些 MySQL 版本上会报时区错误这是最常见的启动失败原因。characterEncodingutf8配合库的utf8mb4保证生僻字能存进去。第三步编译启动cd cemetery-backend mvn clean package -DskipTests java -jar target/cemetery-backend.jar看到Started CemeteryApplication就说明后端起来了默认端口 8080。4.2 前端启动与联调前端用 npm 或 yarn 都行Node 版本建议 14 或 16太新的版本可能和 Vue2 的依赖打架。cd cemetery-frontend npm install npm run dev启动后访问控制台提示的地址一般是http://localhost:80。如果页面能打开但接口 404检查vue.config.js里的代理是否指向了后端 8080 端口。默认账号密码在sys_user表里常见的是admin/admin123登录后先改密码。4.3 初始数据要检查什么导入脚本后别急着点菜单先确认三张表有数据sys_user至少一条管理员、cemetery_area至少一个墓区、cemetery_plot有对应墓位。如果墓区是空的墓位管理页面会一片空白让人误以为功能坏了。我一般会先手动插一个测试墓区再生成几十个墓位把整条链路走一遍。5. 避坑与常见问题排查5.1 中文乱码碑文变问号现象录入逝者姓名或碑文后列表里显示成???。原因多半是数据库、表或连接串三处字符集不一致。解决确认建库用了utf8mb4连接串带characterEncodingutf8并且 MySQL 配置文件里character-set-serverutf8mb4。三处对齐后重启服务。5.2 墓位状态改了但记录没写现象安葬登记提示成功但burial_record表里查不到数据。原因通常是 service 方法没加Transactional或者异常被 catch 后没抛出事务没触发回滚。解决在写多表的 service 方法上加Transactional(rollbackFor Exception.class)并且不要在方法内部吞掉异常。5.3 前端跨域报错现象浏览器控制台报Access-Control-Allow-Origin。原因前后端端口不同后端没开跨域。解决在后端加全局跨域配置或者用前端vue.config.js的proxy代理。生产环境建议用 Nginx 统一入口避免跨域问题。5.4 统计报表数字对不上现象墓位总数和已售数加起来不等于总数。原因墓位状态值不统一有的地方用 1 表示已售有的用 2。解决把状态定义写进常量类所有查询引用常量不要在各处硬编码数字。这是后期维护最容易踩的坑。5.5 生僻字存不进去现象保存时报Incorrect string value。原因字段字符集是utf8而不是utf8mb4。解决ALTER TABLE deceased CONVERT TO CHARACTER SET utf8mb4;同时确认连接串字符集。殡葬业务里生僻字很常见这一步不能省。6. 二次开发把墓位预约做成可配置流程这套源码跑通之后真正有价值的是按自己园区的规矩改。我一般会从三个方向下手状态机、权限、报表。先说状态机。默认的 0-1-2-3 是通用流程但有的园区有「预留」和「续费」概念这时候不要直接改changeStatus里的数字而是把状态定义抽成枚举public enum PlotStatus { AVAILABLE(0, 可售), RESERVED(1, 已预订), SOLD(2, 已售), BURIED(3, 已安葬), EXPIRED(4, 已过期); private final int code; private final String desc; // 构造和 getter 省略 }抽成枚举后前端下拉框、报表条件、状态校验都引用同一份定义加一个状态只改一处。这是我在多个后台项目里验证过的做法比散落的魔法数字靠谱得多。再说权限。源码一般用角色控制菜单但墓位销售涉及金额建议细到按钮级。比如「修改墓位价格」这个按钮只给园区主管角色。实现上可以在sys_menu里加按钮权限标识前端用v-hasPermi指令控制显示后端在接口上加PreAuthorize校验。前后端都拦一道避免有人直接调接口改价。最后是报表。园区最关心的是「本月销售多少、回款多少、空置率多少」。默认的统计可能只有总数我一般会加一个按墓区、按时间段的汇总查询用 MyBatis 写自定义 SQLselect idsumByArea resultTypemap SELECT a.area_name, COUNT(p.id) AS total, SUM(CASE WHEN p.status 2 THEN 1 ELSE 0 END) AS sold, SUM(CASE WHEN p.status 2 THEN a.price ELSE 0 END) AS amount FROM cemetery_area a LEFT JOIN cemetery_plot p ON p.area_id a.id GROUP BY a.id, a.area_name /select这段 SQL 一次查出每个墓区的总数、已售数和销售额前端直接渲染成表格。CASE WHEN是统计类查询的常用写法比多次查库再在 Java 里拼装高效。改完之后记得用真实数据核对一遍我习惯拿一个已知墓区手动数一遍确认数字对得上再上线。验证改造是否成功最直接的办法是走一遍完整业务新建墓区、生成墓位、预订、缴费、安葬登记、查报表每一步都看数据库里的状态和记录是否一致。别只点页面看提示提示成功不代表数据对。从那以后我每次改完状态相关的逻辑都强制把这条链路手动走一遍再让测试用不同角色跑一遍权限才敢交付。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑