news 2026/9/19 15:51:32

基于SpringBoot+Vue的电子印章管理系统设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于SpringBoot+Vue的电子印章管理系统设计与实现

简介:这是一份基于Java+Vue+SpringBoot框架的EE电子印章管理系统设计与实现毕业论文,适配计算机软件、信息管理等专业毕业设计,也适合需要快速搭建同类管理系统论文框架的开发者参考。文档围绕人、设备、场景的立体连接理念,完整呈现了从需求分析、系统设计到技术实现的全部过程,详细说明了用户信息、部门信息、审批流程、印章信息、印章类型、印章申请与下发等核心模块的增删改查逻辑,并阐述了SpringBoot控制层、业务层、持久层的分层架构以及MySQL、Tomcat的选型优势。包内仅含1个docx文件,大小3.15MB,内容覆盖中英文摘要、关键词、目录与正文内容,结构完整,可作为论文骨架和设计规范参考。目前已有234人学习下载,能为读者提供电子印章管理系统的业务梳理方法和技术实现思路,提升毕业设计论文的撰写效率。

1. 从一枚合同章开始:电子印章系统到底在管什么

部门要盖一个合同章,先找行政填单子,再找经理签字,最后翻柜子找章,盖完还要记台账。这套流程在印章少的时候还行,一旦分公司多、印章类型杂,就变成三件事永远说不清:章在哪个抽屉、谁拿走过、上次盖了什么文件。电子印章管理系统就是把这条"申请-审批-盖章-留痕"链路搬到线上。这个基于 Java + Vue + SpringBoot 的项目,核心不是把印章做成图片,而是把用户、部门、审批流程、印章申请、印章下发组织成一条可追溯的数据流。系统用典型的 Controller-Service-Dao 三层结构,MySQL 存储,前端 Vue 单页应用。从表设计到前后端联调,再到部署排错,适合做 SpringBoot 管理系统的开发者,也适合要自建轻量用印审批后台的人参考。

2. SpringBoot 与 MySQL 数据建模:把印章、申请、审批串成一张网

2.1 三层架构在电子印章场景中怎么落地

原设计里已经把分层写得很明确:控制层 Controller、业务处理层 Service、持久层 dao。这个分层在电子印章场景里不是用来凑架构的,而是为了把"判断"和"存取"分开。Controller 只做三件事:接收前端参数、调用 Service、把结果封装成统一返回体。Service 层放审批规则、权限校验、状态流转这些真正的业务逻辑。Dao 层只负责表和 Java 对象的映射。

举个例子,用户提交印章申请时,Controller 拿到 yinzhangmingcheng、yinzhangleixing、zhanghao 这些字段,不能直接往表里 insert。它得先让 Service 判断申请账号是否存在、这个印章类型有没有被禁用、当前用户的部门是否匹配。将来规则变了,比如新增"部门经理必须一审、总经办二审",只需要在 Service 里调整方法,Controller 的接口签名不变,Vue 前端也不用改。这就是分层的直接收益:规则在变,入口稳定。

Dao 层用 MyBatis-Plus 而不是原生 MyBatis,原因很实际:这套系统的表结构以单表查询为主,MyBatis-Plus 的 BaseMapper 自带增删改查,实体类上加一个 @TableName("yonghu") 就能用,省去写大量 XML 的重复劳动。要注意表名和字段名都是拼音,实体类属性要保持一致,IDEA 里建议装 MyBatisX 插件,Mapper 接口和 XML 跳转方便,排查问题能快不少。

2.2 用户、印章、申请三张核心表的设计

数据库设计里最核心的是用户表、印章信息表、申请提交表。用户表是典型的账号体系,字段和原设计保持一致:

CREATE TABLE `yonghu` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `zhanghao` varchar(200) DEFAULT NULL COMMENT '账号', `mima` varchar(200) DEFAULT NULL COMMENT '密码', `xingming` varchar(200) DEFAULT NULL COMMENT '姓名', `xingbie` varchar(200) DEFAULT NULL COMMENT '性别', `bumen` varchar(200) DEFAULT NULL COMMENT '部门', `zhiwu` varchar(200) DEFAULT NULL COMMENT '职务', `dianhua` varchar(200) DEFAULT NULL COMMENT '电话', `touxiang` longtext COMMENT '头像', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

这里有几个可以讨论的点。mima 字段用 varchar(200) 是给加密后的密文留空间,MD5 密文 32 位其实用不着这么长,但如果后期换成 BCrypt,60 位长度就需要了。touxiang 用 longtext 而不是 varchar,因为头像可能以 base64 字符串直接入库,一张头像几十 KB 很常见。字段名用拼音是这个项目的历史习惯,不改动的好处是前后端字段完全对齐,坏处是代码可读性差一些,生产环境建议用规范的英文命名并做数据库迁移,但那是另一件事。

印章信息表负责维护可用的印章资源,申请时要从这里校验印章是否存在:

CREATE TABLE `yinzhangxinxi` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `yinzhangmingcheng` varchar(200) DEFAULT NULL COMMENT '印章名称', `yinzhangleixing` varchar(200) DEFAULT NULL COMMENT '印章类型', `yinzhangdengji` varchar(200) DEFAULT NULL COMMENT '印章等级', `yinzhangtupian` longtext COMMENT '印章图片', `zuoyong` varchar(500) DEFAULT NULL COMMENT '印章作用', `addtime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='印章信息表';

yinzhangtupian 我习惯存图片的 URL 而不是图片内容。存 URL 的好处是列表页加载快,存 base64 的好处是迁移简单、不依赖文件服务器。原项目字段类型是 longtext,说明设计时考虑的是直接存内容,这种方案在印章图片量小的时候完全够用,但如果印章数量上千,建议改成文件路径加单独的图片服务。印章等级这个字段可以用作字典维护,不同等级对应不同的审批策略。

申请提交表是整个流程的枢纽,字段设计如下:

字段名类型说明
idbigint主键,自增
yinzhangmingchengvarchar(200)印章名称
yinzhangleixingvarchar(200)印章类型
yinzhangdengjivarchar(200)印章等级
shenqingshijiandatetime申请时间,默认当前时间
shenqingshuomingvarchar(200)申请事由
zhanghaovarchar(200)申请人账号
xingmingvarchar(200)申请人姓名
jinglizhanghaovarchar(200)经理账号
jinglixingmingvarchar(200)经理姓名
sfshvarchar(200)审核状态:待审核/通过/拒绝
shhflongtext审核回复

申请表里把申请人姓名和经理姓名都冗余进去,而不是只存账号,是因为审批列表、下发台账、历史记录都要直接显示姓名。如果每次都去关联用户表查,列表页会有大量 N+1 查询;更重要的是,审批通过后经理账号和姓名的快照应该保留在申请记录里,将来即使部门管理调整了经理人选,历史记录依然能还原当时的审批上下文。这是报表型业务的常见取舍。

2.3 审批状态字段 sfsh 与审核回复的设计逻辑

sfsh 是"是否审核"的拼音缩写,取值用字符串而不是 int 状态码,好处是前端表格直接显示"待审核",详情页不需要做状态码翻译。坏处是字符串约束弱,一旦有人写入"审核通过"和"通过"两种写法,列表过滤就失效。所以我一般在 Service 里定义常量或枚举,把三个取值管起来,禁止在业务代码里散写字符串。

shhf 用 longtext 而不是 varchar,是因为审核回复可能写一长段拒绝原因。这个字段在设计上承担了"审批意见"的职责,如果后面要做多级审批,建议把它升级成独立的审批记录表,每一级审批插入一条记录,包含审批人、审批意见、审批时间、审批结果,申请表只保留当前状态。论文里的审批流程表存的是流程名称和流程内容,这种结构适合做流程说明展示,比如"部门经理审核用印事由,总经办复核,盖章归档"这类描述,不适合直接驱动状态机。

3. 后端实现:从登录鉴权到印章申请审批的完整链路

3.1 登录接口与 token 机制

先看登录。这套系统里管理端和用户端共用一个登录接口,区别在注册来源,管理员账号在数据库里预置,普通用户走注册。登录的核心逻辑是查用户表、比对密码、生成 token、存入 token 表。

@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping("/login") public Result login(@RequestBody User user) { // 1. 按账号和密码查用户表,密码做 MD5 后比较 User dbUser = userService.findByZhanghaoAndMima( user.getZhanghao(), DigestUtils.md5DigestAsHex(user.getMima().getBytes(StandardCharsets.UTF_8))); if (dbUser == null) { return Result.error("账号或密码错误"); } // 2. 生成随机 token,写入 token 表,记录登录态 String token = UUID.randomUUID().toString().replace("-", ""); userService.saveToken(dbUser.getId(), token, dbUser.getZhanghao(), "用户"); return Result.ok(token); } }

说明几点。密码在输入框里是明文,传到后端先做 MD5 再查库,避免数据库里存明文密码。MD5 是教学项目里最常见的做法,但生产环境我一般用 BCryptPasswordEncoder,因为 MD5 可以撞库,BCrypt 每次生成的结果都带随机盐,安全性高一个量级。token 用 UUID 生成,写入 token 表时同时记录 userid、username、role,以及 addtime 和 expiratedtime。这个表的设计本质上是一个简易会话存储,适合单机部署;如果系统要横向扩展,就把 token 挪到 Redis,用 expire 控制过期时间,接口校验逻辑不用变。

有了 token 表,后续每次请求都需要经过一个拦截器,从 Header 里拿 token,去 token 表查记录,校验没被删除、没有过期,再把当前用户信息放进请求上下文。登录接口本身要放行,否则会死循环。

3.2 印章申请接口与状态流转

用户提交印章申请,后端不能直接把前端参数写进数据库,Service 层要做校验和默认值填充。

@Service public class ShenqingServiceImpl implements ShenqingService { @Autowired private ShenqingMapper shenqingMapper; @Autowired private UserMapper userMapper; @Autowired private DeptMapper deptMapper; @Override public void submit(Shenqing shenqing) { // 1. 校验申请人账号存在 User user = userMapper.selectByZhanghao(shenqing.getZhanghao()); Assert.notNull(user, "申请人不存在"); // 2. 根据用户所属部门带出经理账号和姓名,避免用户手填 Dept dept = deptMapper.selectByBumen(user.getBumen()); if (dept != null) { shenqing.setJinglizhanghao(dept.getJinglizhanghao()); shenqing.setJinglixingming(dept.getJinglixingming()); } // 3. 后端强制设置申请时间和初始审批状态 shenqing.setSfsh("待审核"); shenqing.setShenqingshijian(new Date()); shenqingMapper.insert(shenqing); } }

这里有两个容易被忽略的点。sfsh 必须在后端设置而不是信任前端传值,否则有人直接调接口传一个"通过"就能绕过审批,这是越权问题的典型入口。shenqingshijian 用后端当前时间而不是前端传的时间,是因为客户端时钟不可信,统一用数据库所在服务器的时钟,后续统计"每日申请量"才不会出现时间漂移。经理账号和经理姓名在提交时自动从部门管理表带出,部门管理的字段里正好有经理账号、经理姓名、负责部门,这个关联关系把用户、部门、申请串在了一起。

审批状态流转可以整理成一张表:

操作原状态新状态附带动作
用户提交申请待审核写入申请时间和申请说明
管理员通过待审核通过写入审核回复,生成印章下发记录
管理员驳回待审核拒绝写入审核回复
重复审批通过/拒绝不变拒绝操作,提示状态已变更

注意"通过"和"拒绝"是终态,不允许从终态再翻回"待审核",也不允许重复审批。这个约束要在 Service 里显式判断:先从库里查出旧状态,如果不是"待审核",直接抛异常。如果只更新表而不查旧状态,并发环境下两个管理员同时审批同一条记录,最后一次写库生效,状态就乱套了。简单做法是先 select 再 update,数据库行锁会保证同一时刻只有一个事务能改这条记录。

提示:审批状态从"待审核"变为"通过"或"拒绝"后即为终态,不允许回退,后端要在 Service 里先查询旧状态再更新,不能只写一条 update 语句。

3.3 管理员审批与印章下发

管理员点"通过"时,事务里要做两件事:更新申请表的审批状态和审核回复,同时往印章下发表插入一条下发记录。

@Transactional public void approve(Long id, String result, String reply) { // 1. 查出申请记录,检查当前状态 Shenqing s = shenqingMapper.selectById(id); if (s == null) { throw new RuntimeException("申请记录不存在"); } if (!"待审核".equals(s.getSfsh())) { throw new RuntimeException("该申请已被处理"); } // 2. 更新审批状态和审核回复 s.setSfsh(result); s.setShhf(reply); shenqingMapper.updateById(s); // 3. 审批通过时,生成下发记录,字段与申请表对齐 if ("通过".equals(result)) { YinzhangXiafa xf = new YinzhangXiafa(); xf.setZhanghao(s.getZhanghao()); xf.setXingming(s.getXingming()); xf.setYinzhangmingcheng(s.getYinzhangmingcheng()); xf.setYinzhangleixing(s.getYinzhangleixing()); xf.setYinzhangdengji(s.getYinzhangdengji()); xf.setXiafashijian(new Date()); xiafaMapper.insert(xf); } }

@Transactional 解决的是数据一致性问题:更新审批状态和生成下发记录必须同时成功,或者同时回滚。如果先更新了审批状态,下发记录插入失败,用户那边看到的是"已通过",但用印台账里没有记录,事后审计就对不上。回滚之后用户重新提交或管理员重新审批,数据还是干净的。

下发表的字段刻意复制了申请表里的印章名称、类型、等级、账号、姓名,而不是存一个申请 id 去关联。这么设计的理由是下发记录是操作台账,要能独立查询,如果表里只存 shenqing_id,将来要导出"某部门全年的用印记录"就要一路 join 回去。台账表冗余业务快照,是在这种管理系统里非常实用的模式。

4. Vue 前端联调:路由守卫、Axios 与印章申请页

4.1 路由配置与角色控制

前端用 Vue CLI 创建项目,views 目录按模块组织:用户管理、部门管理、印章信息、印章申请、印章审批、印章下发。页面一多,路由就要做权限控制。我的做法是把角色写进路由的 meta 字段,在全局前置守卫里统一判断。

const routes = [ { path: '/login', component: Login, meta: { public: true } }, { path: '/seal/apply', component: SealApply, meta: { roles: ['用户'] } }, { path: '/seal/approve', component: SealApprove, meta: { roles: ['管理员'] } }, { path: '/seal/dispatch', component: SealDispatch, meta: { roles: ['管理员', '部门管理'] } } ]

路由守卫:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const role = localStorage.getItem('role') if (to.meta.public) { next() } else if (!token) { next('/login') } else if (to.meta.roles && !to.meta.roles.includes(role)) { next('/403') } else { next() } })

路由和角色对应关系整理如下:

路径视图组件允许角色
/seal/applySealApply.vue用户
/seal/approveSealApprove.vue管理员
/seal/dispatchSealDispatch.vue管理员、部门管理
/user/manageUserManage.vue管理员

有几个坑要说清楚。角色存在 localStorage 里只是方便前端做显示控制,不是安全边界,用户改一下本地存储就能进页面,所以后端每个接口必须再次校验角色,前端守卫只能防误入,不能防攻击。另外 Vue 项目打包后如果出现布局异常或者路由刷新 404,通常不是代码问题,而是静态资源路径和服务器 rewrite 没配好,部署时要在 Nginx 里把非静态资源请求都 rewrite 到 index.html。

注意:localStorage 里的角色可以被用户直接修改,前端路由守卫只负责页面显示控制,真正的权限校验必须落在后端接口上,否则改一下本地存储就能进管理员页面。

4.2 Axios 封装与请求拦截器

前后端分离后,每个请求都要带 token,响应要统一处理错误码,这些逻辑不能散落在每个页面里,所以我会封装一个 axios 实例。

import axios from 'axios' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:从本地存储取 token,放到请求头 service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['token'] = token } return config }) // 响应拦截器:统一处理后端返回结果和 401 跳转 service.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') router.push('/login') } return Promise.reject(error) } ) export default service

baseURL 配成 /api,开发时在 vue.config.js 里配 devServer 的 proxy 把 /api 转发到后端端口,避免前端页面直接跨域。生产环境由 Nginx 做同样的事。token 放在自定义请求头而不是 Authorization 里也行,两边的约定保持一致即可。401 统一跳登录,这个逻辑放在拦截器里,比在每个页面 catch 里写一遍干净得多。

4.3 印章申请页面的表单与提交

申请页面的核心是一个表单加一个提交方法。表单字段要和后端的申请提交表对齐,我用 Element UI 的 el-form 来做。

<template> <el-form :model="form" label-width="90px"> <el-form-item label="印章名称"> <el-input v-model="form.yinzhangmingcheng" placeholder="如:销售合同章" /> </el-form-item> <el-form-item label="印章类型"> <el-select v-model="form.yinzhangleixing"> <el-option label="公章" value="公章" /> <el-option label="合同章" value="合同章" /> <el-option label="财务章" value="财务章" /> </el-select> </el-form-item> <el-form-item label="申请说明"> <el-input type="textarea" v-model="form.shenqingshuoming" rows="3" /> </el-form-item> <el-button type="primary" @click="submitApply">提交申请</el-button> </el-form> </template> <script> import service from '@/api/request' export default { data() { return { form: { yinzhangmingcheng: '', yinzhangleixing: '', shenqingshuoming: '', zhanghao: localStorage.getItem('zhanghao'), xingming: localStorage.getItem('xingming') } } }, methods: { submitApply() { // 提交申请,后端会强制设置为“待审核”状态 service.post('/shenqing/submit', this.form).then(res => { this.$message.success('申请已提交,等待管理员审批') this.$router.push('/seal/apply/list') }) } } } </script>

印章类型下拉框在完整项目里应该从后端接口读取,比如 /seal/type/list,而不是写死,否则印章类型表就失去了维护的意义。zhanghao 和 xingming 从登录后存进 localStorage 的数据里取,保证后端能识别当前申请人。一个常见的错误是只把 token 存本地,用户基本信息不存,提交表单时让用户重新输入账号,这样既不友好也容易写错,登录时把账号、姓名、角色一起存下来是更省事的做法。

5. 部署验证与排错:从 IDEA 到 Tomcat 的几个关键点

5.1 外置 Tomcat 的打包调整

SpringBoot 默认内嵌 Tomcat,直接 mvn clean package 打 jar 就能运行。但如果运行环境要求外置 Tomcat,需要把项目打成 war 包。改动集中在三处:pom.xml 里 packaging 改为 war,spring-boot-starter-tomcat 的 scope 改成 provided,启动类继承 SpringBootServletInitializer 并重写 configure 方法。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>

provided 的作用是打包时排除内嵌 Tomcat 的 jar,避免和外置 Tomcat 的类冲突。前端 Vue 打包后的 dist 目录可以交给 Nginx 托管,也可以丢进 Tomcat 的 webapps/ROOT,两种方式都需要把 /api 请求反向代理到后端服务端口,否则一刷新页面就出现跨域或 404。

5.2 一条命令验证审批链路

页面点来点去不如直接调接口验得快。登录拿到 token 后,模拟用户提交一条申请:

curl -X POST http://localhost:8080/shenqing/submit \ -H "Content-Type: application/json" \ -H "token: 登录后拿到的token" \ -d '{"yinzhangmingcheng":"销售合同章","yinzhangleixing":"合同章","zhanghao":"zhangsan","xingming":"张三"}'

然后查数据库:

SELECT id, yinzhangmingcheng, sfsh, shhf FROM shenqing ORDER BY id DESC LIMIT 5;

确认这条记录的状态是"待审核"。再调用管理员的审批接口传"通过"和审核回复,重新查库,确认 sfsh 变成"通过",同时 yinzhangxiafa 表多了一条下发记录。这一套能一次性验证申请写入、状态更新、事务提交、下发生成四个环节。如果下发表没有数据但 sfsh 已经是"通过",优先检查 @Transactional 是不是没生效:要么启动类没加 @EnableTransactionManagement,要么 Service 方法被同类内部方法调用绕过了 Spring 的代理对象,这两个原因占了事务失效问题的大头。

本文还有配套的精品资源,点击获取

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

用python-pptx实现培训课件的工程化生成与版本管理

简介&#xff1a;企业文化及跨文化管理PPT课件&#xff0c;围绕企业文化内涵、特征、构成要素、功能层次展开&#xff0c;并对比中日美企业文化差异&#xff0c;引入松下、三洋等跨文化管理经典案例&#xff0c;适合财务管理类课堂授课、企业内训或自我学习使用。内容以“企业人…

作者头像 李华
网站建设 2026/9/19 15:47:29

集团财务数字化规划:架构分层、数据主线与落地验证

简介&#xff1a;这份集团公司财务管理数字化规划方案&#xff08;88页PPT&#xff09;面向企业财务管理者、数字化转型规划人员及咨询顾问&#xff0c;系统展示了集团财务数字化转型的整体蓝图。内容涵盖业务流程体系设计、以用户体验为中心的全面需求调研、业务能力提升机会识…

作者头像 李华
网站建设 2026/9/19 15:45:18

C1科目一2025题库答案:用Python解析docx生成错题本与模拟卷

简介&#xff1a;2025年C1驾照科目一必考题库附含答案&#xff0c;面向正在备考C1驾驶证理论考试的学员&#xff0c;聚焦科目一高频考点与紧急驾驶应对策略。压缩包为docx格式&#xff0c;内含1个Word文档&#xff0c;整体大小仅49KB&#xff0c;便于下载后随时在电脑或手机上查…

作者头像 李华
网站建设 2026/9/19 15:44:57

EtherCAT星型拓扑断线故障解析:HotConnect配置与验证指南

简介&#xff1a;一份关于倍福EtherCAT HotConnect设置方法的PDF技术资料&#xff0c;面向工业自动化现场工程师与倍福控制器开发人员&#xff0c;解决设备热插拔或物理线路变动导致EtherCAT网络通讯中断、IO值停止刷新的常见问题。资源为单个PDF文件&#xff0c;压缩包大小119…

作者头像 李华
网站建设 2026/9/19 15:44:27

Emotion-LLaMA:面向情感识别的多模态LLaMA端到端构建实战

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

作者头像 李华