ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

律师事务所案件管理系统:SpringBoot+Vue+MySQL全栈落地实践

律师事务所案件管理系统:SpringBoot+Vue+MySQL全栈落地实践 每年毕业季都有大量学生拿“管理系统”当课题但同样叫管理系统SpringBootVueMySQL这套技术栈放在律师事务所案件管理这个场景下就显得特别有说头。原因很简单它不是一个纯粹的增删改查而是带着业务主线的——从收案、分案、立案、开庭到结案归档中间还夹着客户信息、律师分配、案件费用、状态流转和统计报表。用这个题目做毕业设计既能展示后端对状态机、权限、多条件查询的处理能力又能在前端做出时间线、报表这类有视觉感的东西数据库设计也天然有多表关联工作量很容易撑起来。我当时带过好几个用这个题目的学生自己也动手把整个系统从零搭过一遍。这篇文章就把整个项目的落地方案拆开讲从功能范围怎么定、数据库表怎么设计到后端的关键代码怎么写、前端页面怎么组织再到论文和部署文档怎么准备全部按实际动手的顺序走。不管你是刚拿到这个题目的本科生还是想帮学生把系统做得更完整的指导老师照着这条线走能省掉很多弯路上的时间。1. 选型评估这类课题为什么值得做又容易在哪里翻车1.1 业务域优势案件有生命周期系统才不是空壳普通的管理系统比如图书管理、商品管理核心就是一张表的CRUD做完一点成就感都没有。案件管理不一样一个案件从咨询到收案再到结案归档中间有明确的状态变迁登记、分案、立案、审理、结案、归档。这意味着系统里必须有一张状态字段甚至是一张状态流转日志表。有了这条业务主线你的后台逻辑就不是简单的save和update而是要根据当前状态判断下一步能做什么。答辩的时候这一块是最容易展开讲“系统设计”的地方。另外案件系统天然有角色区分。前台接待员负责录入客户和案件基础信息合伙人负责分案律师负责查看自己名下的案件并更新进展管理员能看到全部数据。这种多角色的权限控制是毕业设计评分表里“功能完整性”和“系统设计”两个维度的高频加分项。1.2 技术栈合理性学校教程覆盖率高风险最低SpringBootVueMySQL这个组合几乎是国内高校软件工程专业的标准配置。Spring Boot把SSM那套繁琐的配置压缩到了极简Vue 2或Vue 3配合Element UI/Element Plus做后台界面效率极高MySQL是各个学校机房都熟悉的数据库。三个技术点在网上都有海量教程遇到问题搜索成本很低。对需要同时写论文和准备答辩的学生来说选一套资料多、踩坑记录全的技术栈比选一个“看起来很前沿”但周围没人会用的方案重要得多。1.3 真正容易翻车的点把系统做成“堆菜单”我见过好几个案件管理系统功能菜单列得很满客户管理、律师管理、案件管理、合同管理、统计报表样样都有点进去全是表格没有任何业务逻辑判断。这种系统看上去功能齐全但答辩老师随便问一句“案件状态是怎么流转的”“结案之后还能不能改承办人”学生就答不上来。所以这篇文章后面讲功能设计、表结构和代码实现时都会围绕状态流转和角色权限这两条主线展开。功能可以少但业务逻辑必须成立。宁可做六个菜单也不要凑二十个空壳菜单。2. 功能范围怎么定从收案到归档的闭环我建议第一版系统不要贪多把下面几个模块做扎实就足够支撑一篇质量不错的毕业论文了。2.1 基础数据模块客户表包括个人客户和单位客户。个人客户有姓名、电话、证件号单位客户有单位名称、统一社会信用代码、法定代表人。设计时可以做成一张表加一个客户类型字段区分。律师表姓名、执业证号、电话、专业方向如民商事、刑事、行政、所属合伙人。这里注意律师表最好带上“是否可用”状态因为分案时要判断这个律师手上案件数量是否过多或者是否处于休假状态。法律事务类型表比如合同纠纷、劳动争议、刑事诉讼、知识产权、行政诉讼等。这个表不是必须的但有了它可以做案由分类统计前端报表能多一张饼图工作量又好看一点。2.2 案件主流程模块收案登记前台录入客户信息和案件基础信息包括案件名称、案由、案件金额、对方当事人、受理法院等。系统自动生成案号比如 2024-民初-001案号必须唯一。分案管理合伙人或管理员把一个案件分配给某个承办律师。分完之后要记录操作人和时间。立案信息维护律师拿到案件后补充立案日期、案号法院案号、承办法官、审判庭等信息。开庭记录一个案件可能有多条开庭记录包括开庭时间、地点、审理情况、调解结果等。这个用子表保存。结案与归档案件结束后填写结案方式判决、调解、撤诉等、结案日期然后进入归档状态。归档后的案件默认不可编辑只能查看。这个“归档后锁定”的业务规则是你后端的亮点。2.3 辅助功能模块费用管理每个案件可以登记律师费、诉讼费、差旅费等收费记录并区分是否已收。这里不做太复杂的财务逻辑弄一张费用记录表就行。文档上传给案件挂附件比如起诉状、判决书PDF、证据材料扫描件。文件上传后保存到服务器目录数据库只存文件路径和名称。这个功能实现起来简单但对律师事务所场景非常贴合。统计报表按月份统计收案数量按案由类型统计案件占比按律师统计在办案件数量。用ECharts画柱状图和饼图。公告栏管理员发布通知用户登录后能看到。这个小模块主要是为了让首页不空着。2.4 角色与权限设计我的建议是设三种角色管理员admin、合伙人partner、律师lawyer、前台receptionist。实际上前台也可以用律师角色账号但为了论文好写我常建议四个角色分开。权限控制策略说简单点管理员全功能前台可以登记案件、查看客户但不能分案和结案合伙人可以分案、查看全所案件律师只能看到分配给自己的案件并维护立案、开庭、结案信息。后端用拦截器检查角色权限前端菜单根据角色动态渲染两侧都要做不能只隐藏按钮。3. 数据库设计实战表关系是第一道门槛数据库设计是这类毕业设计论文里占篇幅最多的部分也是实际开发中返工最频繁的部分。我直接把最终版本的表结构分享出来并解释每一张表存在的原因。3.1 核心表清单表名用途对应用户角色sys_user登录用户admin/partner/lawyer/receptionistclient客户信息前台、律师lawyer律师信息合伙人case_info案件主表所有角色case_status_log案件状态流转日志后台自动court_record开庭记录律师fee_record费用记录律师、前台case_doc案件附件律师case_type案由类型基础数据把sys_user和lawyer拆开是因为律师可能在某个时间点离职或停职但账号还存在所以用户和律师信息不要强关联用user_id字段对应即可。3.2 案件主表的字段设计CREATE TABLE case_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, case_no VARCHAR(64) NOT NULL UNIQUE COMMENT 律所内部案号, case_name VARCHAR(200) NOT NULL COMMENT 案件名称, case_type_id BIGINT NOT NULL COMMENT 案由类型id, client_id BIGINT NOT NULL COMMENT 客户id, client_name VARCHAR(100) NOT NULL COMMENT 冗余客户姓名列表显示用, opparty_name VARCHAR(200) NULL COMMENT 对方当事人, court_name VARCHAR(200) NULL COMMENT 受理法院, case_amount DECIMAL(14,2) NULL DEFAULT 0 COMMENT 涉案金额, lawyer_id BIGINT NULL COMMENT 承办律师id, assign_time DATETIME NULL COMMENT 分案时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0草稿 1待分案 2在办 3结案 4归档, -- 法院立案信息也可放主表简化设计 court_case_no VARCHAR(64) NULL COMMENT 法院案号, judge_name VARCHAR(50) NULL COMMENT 承办法官, filing_time DATE NULL COMMENT 立案日期, close_type VARCHAR(32) NULL COMMENT 结案方式, close_time DATETIME NULL COMMENT 结案时间, create_time DATETIME NOT NULL, update_time DATETIME NOT NULL );这里有几个细节值得说明。案号是唯一标识必须做UNIQUE约束否则前后端校验拦不住并发插入时数据库层面的重复。case_name和client_id相比我更推荐在列表页增加一个client_name冗余字段因为多表关联查询在数据量大时会拖慢速度冗余一个名字字段在毕业设计里不算违规反而体现了对查询性能的考虑。律师不是必填字段因为一个案件在刚登记时可能还没有分案。status字段用TINYINT不要用VARCHAR存中文状态因为程序里判断状态用数字更可靠页面展示时再映射成中文。3.3 状态流转日志表案件为什么要单独一张状态日志表这个问题值得在论文里大写特写。因为业务上需要追踪每个关键节点是什么时候、由谁操作的。比如一个案件在3月1日结案了但后来被律师撤销结案重新打开那到底结案过几次都发生在什么时候如果不记录日志这种事情永远讲不清楚。CREATE TABLE case_status_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, case_id BIGINT NOT NULL, from_status TINYINT NULL, to_status TINYINT NOT NULL, operator_id BIGINT NOT NULL COMMENT 操作人user_id, operator_name VARCHAR(50) NOT NULL, remark VARCHAR(255) NULL, create_time DATETIME NOT NULL, INDEX idx_case_id (case_id) );这张表在插入时由后端service统一写入不在mapper里散落着写。后面讲后端时会给出具体代码。3.4 用户表、客户表、费用表的设计要点用户表就是标准的id、username、password、real_name、role_id、status、create_time。密码存储建议用BCrypt加密不是明文。毕业设计用MD5也不是不行但懂行一点的答辩老师看到明文密码会觉得不太舒服我建议用Spring Security自带的BCryptPasswordEncoder代码量很少。客户表预留一个contact_phone和id_card字段方便论文里写“客户信息唯一性校验”。费用表带上case_id外键和pay_status字段。附件表带上file_path、original_name和file_size。注意文件上传后建议把文件存放在部署目录之外的uploads文件夹数据表里只记录相对路径避免打包jar之后找不到路径。3.5 索引设计的思路除了主键索引建议在case_info表上建这些索引case_no唯一索引、status、lawyer_id、client_id。在case_status_log表上建case_id索引。创建关联查询时尽量走索引。4. SpringBoot后端落地不是会写CRUD就行4.1 分层与项目结构后端我习惯用标准的三层结构加controller包装com.example.lawfirm ├── controller ├── service ├── mapper ├── entity ├── dto ├── common └── configentity放数据库映射实体dto放前端交互对象比如登录请求、分页查询条件。common里放统一返回结果Result和异常处理类。这里需要注意分页查询的入参建议单独建一个QueryDTO不要直接在Controller里接收Page、Size、Keyword一堆散参数那样后续加过滤条件会很痛苦。4.2 统一返回体和异常处理前端Vue里请求接口最舒服的是所有接口都返回同一个结构体。我通常定义成这样public class ResultT { private Integer code; private String msg; private T data; public static T ResultT ok(T data) { ... } public static T ResultT error(String msg) { ... } }然后写一个全局异常处理器用RestControllerAdvice捕获业务异常和参数校验异常统一包装成Result返回不要在每个Controller里写try-catch。这个做法在论文里可以写在“系统异常处理模块设计”章节是一个实打实的设计点。4.3 登录认证JWT加拦截器Spring Security配置起来复杂如果是毕业设计我认为用JWT加HandlerInterceptor是完全够的而且代码更直观答辩时容易讲清楚。核心逻辑分三步用户登录成功后根据用户ID、用户名、角色id生成token用JWT工具类签名。写一个AuthInterceptor在preHandle中从请求头Authorization取出token解析并放入ThreadLocal。注册拦截器拦截不需要放行的路径如除/login、/register以外的所有/api/**请求。ThreadLocal存储当前登录用户信息Controller里通过UserContext.get()获取当前用户再判断角色权限。这样写起来非常简洁。4.4 多条件分页查询案号、客户、承办人、状态组合过滤案件列表是系统最核心的查询接口用MyBatis或MyBatis-Plus写一个动态SQL。这里给出MyBatis-Plus的LambdaQueryWrapper版本代码量最少Override public IPageCaseInfo queryCasePage(CaseQueryDTO dto) { PageCaseInfo page new Page(dto.getPageNum(), dto.getPageSize()); LambdaQueryWrapperCaseInfo wrapper Wrappers.lambdaQuery(CaseInfo.class); wrapper.like(StringUtils.hasText(dto.getCaseNo()), CaseInfo::getCaseNo, dto.getCaseNo()); wrapper.like(StringUtils.hasText(dto.getClientName()), CaseInfo::getClientName, dto.getClientName()); wrapper.eq(dto.getStatus() ! null, CaseInfo::getStatus, dto.getStatus()); wrapper.eq(dto.getLawyerId() ! null, CaseInfo::getLawyerId, dto.getLawyerId()); // 非管理员只能看到自己名下的案件 wrapper.apply(handleDataScope()); wrapper.orderByDesc(CaseInfo::getCreateTime); return baseMapper.selectPage(page, wrapper); }注意注释里提到“非管理员只能看到自己名下的案件”。这里就是数据权限控制不是简单地查询全部再在前端过滤。后端务必做这个过滤否则前端改一下接口参数就能看到别人的案件这在答辩时会被指出安全隐患。我的做法是在service层判断当前登录用户的角色id如果是律师就在wrapper上加eq条件lawyer_id当前用户关联的律师id如果是前台则限制为只能看到status为草稿和待分案的案件管理员和合伙人不限制。4.5 状态流转的实现更新主表加写日志两步走案件状态不能直接update status了事。正确做法是在service里定义状态流转方法比如submit(草稿-待分案)、assign(待分案-在办)、close(在办-结案)、archive(结案-归档)。以分案为例核心逻辑Transactional public void assignCase(Long caseId, Long lawyerId) { CaseInfo dbCase getById(caseId); if (dbCase null) throw new BizException(案件不存在); if (dbCase.getStatus() ! CASE_STATUS_UNASSIGNED) { throw new BizException(当前状态不能分案); } Lawyer lawyer lawyerMapper.selectById(lawyerId); if (lawyer null || lawyer.getStatus() ! 1) { throw new BizException(承办律师不可用); } CaseInfo update new CaseInfo(); update.setId(caseId); update.setLawyerId(lawyerId); update.setAssignTime(new Date()); update.setStatus(CASE_STATUS_IN_PROGRESS); updateById(update); CaseStatusLog log new CaseStatusLog(); log.setCaseId(caseId); log.setFromStatus(CASE_STATUS_UNASSIGNED); log.setToStatus(CASE_STATUS_IN_PROGRESS); log.setOperatorId(UserContext.getUser().getId()); log.setOperatorName(UserContext.getUser().getRealName()); log.setRemark(分案给: lawyer.getName()); caseStatusLogMapper.insert(log); }Transactional一定要加因为更新主表状态和插入日志表必须作为一个原子操作。String state if update fails, log shouldnt remain.结案同理在办状态才能结案结案时要校验开庭记录是否已登记业务规则。归档操作则限制为只有管理员和合伙人能操作。这些规则在答辩时都是极好的防守点。4.6 文件上传与预览文件上传使用MultipartFile接口保存到服务器固定目录文件名用UUID重命名。预览时直接返回文件流并设置Content-Type。注意上传接口要限制文件大小SpringBoot的配置文件里设置spring.servlet.multipart.max-file-size10MB即可。还有一个坑是部署到服务器后localhost路径问题建议前端保存出的访问地址用相对路径拼接否则换服务器就裂了。5. Vue前端开发让页面真正能用起来前端我推荐用Vue 2加Element UI如果你对Vue 3熟悉就选Vue 3加Element Plus。两者写法差别不大重点是别混着用组件库。下面以Vue 2为例说明。5.1 项目起始结构与路由守卫前端项目结构大概是views页面、router路由、apiaxios请求封装、components组件、storeVuex用于保存用户信息和token。路由需要做登录守卫在main.js里配置beforeEach检查本地localStorage有没有token没有token又进入了需要登录的页面就跳转到/login。这个用一句话就能完成router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path /login) { next(); } else { if (!token) { next(/login); } else { next(); } } });要记住前端守卫只是体验优化真正的安全校验在后端拦截器。5.2 axios封装与token携带axios实例需要统一设置baseURL和请求头。建议把baseURL配在env环境变量里或者通过vue.config.js的proxy代理到后端避免开发时跨域。拦截器统一做两件事请求前带token响应后统一处理401和业务错误。service.interceptors.request.use(config { if (localStorage.getItem(token)) { config.headers[Authorization] localStorage.getItem(token); } return config; }); service.interceptors.response.use( res { if (res.data.code 401) { router.push(/login); return Promise.reject(res.data); } return res.data; }, err { return Promise.reject(err); } );5.3 案件列表页表格加条件查询加操作按钮案件列表是前端工作量最大的页面包含搜索区、案件表格、分页器和操作按钮。搜索区条件有案号、客户姓名、案件状态、承办律师。操作按钮根据状态显示不同内容待分案显示“分案”在办显示“结案”结案显示“归档”。这里用到el-table的列render函数或通过template判断el-table-column label操作 width240 template slot-scopescope el-button v-ifscope.row.status1 typewarning clickopenAssignDialog(scope.row)分案/el-button el-button v-ifscope.row.status2 typesuccess clickcloseCase(scope.row.id)结案/el-button el-button v-ifscope.row.status3 isAdmin typeinfo clickarchive(scope.row.id)归档/el-button /template /el-table-column这个条件按钮的设计后端一定也要有对应的状态校验不能只靠前端隐藏。5.4 案件详情页时间线展示状态流转案件详情页除了基本信息表单外强烈建议加一个时间线展示案件从登记到当前每一步操作记录。正好对应后端case_status_log表。Element UI自带el-timeline组件el-timeline el-timeline-item v-for(log, index) in statusLogs :keyindex :timestamplog.createTime :typeindex 0 ? primary : {{ log.operatorName }} 将案件状态从 {{ statusText(log.fromStatus) }} 变更为 {{ statusText(log.toStatus) }}备注{{ log.remark }} /el-timeline-item /el-timeline这个页面在答辩演示时扫一眼就能看到“这个系统的状态流转是真实记录的”比任何口头解释都有说服力。5.5 统计报表ECharts图表首页放两张图一个柱状图展示最近6个月收案数量变化一个饼图展示案由分布。接口后端做聚合查询返回两个数组前端用ECharts渲染。ECharts的引入建议通过npm安装不要用CDN因为答辩现场如果没有网络图表会直接空白。5.6 前端环境配置的几个大坑第一个坑是Node版本过高导致node-sass安装失败如果是Vue2项目建议Node 14或16或者干脆改用sassdart-sass替代node-sass。第二个坑是vue.config.js的devServer.proxy没有配置前端请求后端出现跨域报错。下面是一段可用的配置module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };把后端的Controller统一加一个/api前缀开发时就不会有跨域问题。6. 交付物怎么准备数据库脚本、部署文档、论文与答辩防守6.1 数据库脚本必须包含初始化和演示数据交付的数据库脚本不能只有建表语句还要插入测试数据否则老师导入数据库后打开系统所有列表都是空白的体验非常差。测试数据建议覆盖多个状态几条草稿案件、几条在办案件、一条结案案件、一条归档案件并把律师、客户、费用记录、状态日志都补齐。这样演示时可以直接演示“分案”和“结案”的完整流程不用先录入数据。账号方面至少准备四个admin、partner、lawyer、receptionist密码统一初始化为123456并在部署文档里写清楚。6.2 部署文档怎么写一份合格的部署文档要包含环境要求JDK 1.8、Node 14、MySQL 5.7/8.0、Maven 3.6、数据库导入步骤、后端启动步骤、前端启动步骤、默认账号清单。我特别建议把后端和前端启动命令都写在文档里并且注明端口比如后端8080前端80或8081。如果你是打包成jar部署记得写清楚jar包启动命令和前端build后的静态资源配置。很多学生在部署环节栽跟头往往就是没写清楚MySQL是否需要新建一个专用用户、数据库字符集是utf8mb4还是utf8。6.3 论文结构建议论文题目就可以叫《基于SpringBoot和Vue的律师事务所案件管理系统的设计与实现》。章节结构按标准来第1章 绪论背景、国内外研究现状、目的意义。重点写“当前很多律所仍用Excel管理案件存在信息孤岛、统计困难、状态不透明”等痛点。第2章 相关技术介绍Spring Boot、Vue、MySQL、MyBatis-Plus、JWT。不要抄博客用自己的话写。第3章 需求分析功能需求、角色需求、用例图。第4章 系统设计总体架构、功能模块划分、数据库表设计、类设计。第5章 系统实现每块功能的核心代码截图加解释。第6章 系统测试测试用例表、结果分析。答辩演示环节两个问题要提前准备一是“归档后的案件数据怎么查”可以答“归档只是锁定编辑权限查询列表仍可查并可在统计报表中纳入归档案件”二是“如果同一个案号重复录入怎么办”可以答“数据库唯一约束后端主动查重前端即时提示三层保障”。这两个问题都直接对应你设计里的细节答得越具体越好。6.4 容易被忽略的安全与异常处理点最后提醒三个收尾细节。一是统一异常处理必须覆盖参数校验失败的情况不能直接抛500给前端要返回友好提示。二是对密码进行加密存储登录接口返回token时不要把密码字段序列化进JSON可以在实体类密码字段上标注JsonIgnore。三是所有文件上传接口要校验文件扩展名防止上传jsp、exe这类危险文件。这些点都是论文“系统实现”章节的加分素材答辩时主动提出来比被动问出来强太多。我在实际带项目的过程中发现学生最容易把时间耗在系统中那些“锦上添花”的部分比如公告栏的富文本编辑、消息通知、复杂的权限粒度设计。我的建议是先把案件管理主流程跑通再把统计报表和时间线做出来最后有余力再考虑美化页面。毕业设计的核心是逻辑闭环清晰可见而不是功能数量无限膨胀。按这条路线走大约一周多的时间就能完成从建表到答辩文档的全部工作量。希望这份拆解能让你少走几段弯路。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进