基于SpringBoot+Vue的电子印章管理系统设计与实现
发布时间:2026/9/19 15:51:40 作者:尧图编辑部 阅读量:1,286

简介这是一份基于JavaVueSpringBoot框架的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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;这里有几个可以讨论的点。mima 字段用 varchar(200) 是给加密后的密文留空间MD5 密文 32 位其实用不着这么长但如果后期换成 BCrypt60 位长度就需要了。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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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审核回复申请表里把申请人姓名和经理姓名都冗余进去而不是只存账号是因为审批列表、下发台账、历史记录都要直接显示姓名。如果每次都去关联用户表查列表页会有大量 N1 查询更重要的是审批通过后经理账号和姓名的快照应该保留在申请记录里将来即使部门管理调整了经理人选历史记录依然能还原当时的审批上下文。这是报表型业务的常见取舍。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 servicebaseURL 配成 /api开发时在 vue.config.js 里配 devServer 的 proxy 把 /api 转发到后端端口避免前端页面直接跨域。生产环境由 Nginx 做同样的事。token 放在自定义请求头而不是 Authorization 里也行两边的约定保持一致即可。401 统一跳登录这个逻辑放在拦截器里比在每个页面 catch 里写一遍干净得多。4.3 印章申请页面的表单与提交申请页面的核心是一个表单加一个提交方法。表单字段要和后端的申请提交表对齐我用 Element UI 的 el-form 来做。template el-form :modelform label-width90px el-form-item label印章名称 el-input v-modelform.yinzhangmingcheng placeholder如销售合同章 / /el-form-item el-form-item label印章类型 el-select v-modelform.yinzhangleixing el-option label公章 value公章 / el-option label合同章 value合同章 / el-option label财务章 value财务章 / /el-select /el-form-item el-form-item label申请说明 el-input typetextarea v-modelform.shenqingshuoming rows3 / /el-form-item el-button typeprimary clicksubmitApply提交申请/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 改为 warspring-boot-starter-tomcat 的 scope 改成 provided启动类继承 SpringBootServletInitializer 并重写 configure 方法。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope /dependencyprovided 的作用是打包时排除内嵌 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 的代理对象这两个原因占了事务失效问题的大头。本文还有配套的精品资源点击获取