ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot+Vue+MySQL律所案件管理系统源码拆解与启动实战

Spring Boot+Vue+MySQL律所案件管理系统源码拆解与启动实战 最近把一个律师事务所案件管理系统源码完整跑通了Spring Boot后端、Vue前端、MySQL数据库这套组合可以说是目前中小型管理系统里最稳的搭配之一。系统不是那种只有登录注册的壳子而是把案件登记、客户管理、律师分配、开庭提醒、文书归档、费用登记这些真实业务全部做了进去解压之后按顺序执行SQL脚本、启动后端、启动前端浏览器里就能看到登录页。如果你正在找毕业设计项目或者律所内部想上一套轻量级信息化系统这份工程都能直接作为底子来用。下面我把整个项目的业务逻辑、技术选型、启动步骤和实际踩坑过程都拆开讲。1. 业务理解先行律所到底需要一个什么样的案件管理系统做管理系统的第一件事不是写代码而是把业务里的人、事、流程理清楚。律所业务跟普通企业的最大区别在于案件是核心资产但案件信息往往散落在律师个人电脑、纸质卷宗和微信聊天记录里。合伙人想了解“这个月收了多少案子、哪个律师手里压了多少案件、哪些案件快过审限了”通常要挨个找律师要Excel花费大量时间且数据口径不一致。这套源码之所以有参考价值是因为它把这些问题抽象成了标准化的系统功能。1.1 律所管理中的信息孤岛和现实痛点先看几个真实场景案件登记靠Word和Excel没有唯一编号后期想找一份三年前的代理词得翻半天文件夹客户信息存在律师手机里人走了客户跟着走开庭时间靠个人日历提醒忙起来很容易错过排期律师费到账情况没有统一台账财务和合伙人各有一份数字。这些问题单看都不严重但叠加在一起直接导致律所无法做数据化决策。这套系统解决的核心就是把“人找事”变成“事找人”。案件一进来就有唯一编号所有信息集中存储客户资料统一管理律师离职时客户归属可以走流程交接开庭日期进入日程模块系统按时间提醒每一笔律师费都关联到案件财务台账自动生成。简单说系统的目标不是消灭律师的个人能力而是把律所从依赖个人记忆的管理模式推向依赖流程和数据的管理模式。1.2 从案件生命周期推导核心功能模块案件在律所里是有生命周期的一般包括咨询登记、利益冲突检索、委托代理、律师分配、案件办理、结案归档这几个阶段。系统功能模块正是围绕这条链路设计的。案件管理案件的创建、分派、进展录入、结案和归档。客户管理自然人和企业的基本信息、历史案件记录。律师管理所内律师信息、办案数量统计、专业领域标签。日程与提醒开庭日期、会见时间、立案期限的提醒逾期高亮。费用管理律师费、差旅费、风险代理费记录以及到账状态跟踪。文书管理起诉状、合同、判决书等文件上传、预览和下载。这些模块不是独立存在的每个都围绕案件主线和角色权限来组织。比如助理上传一份证据材料系统会自动记录上传时间和上传人案件主表不需要手动更新所有操作都有日志痕迹。这正好是律所合规管理需要的审计能力。1.3 角色权限矩阵谁可以看什么、改什么权限是律所管理系统区别于普通业务系统的关键。律师之间是协作关系但案件信息又非常敏感授权模型必须精确到操作级别。这套源码使用了典型的RBAC模型数据库里有用户表、角色表、权限表和关联表前端路由根据角色动态渲染后端接口通过Spring Security做二次校验。角色数据范围典型操作权限系统管理员全部数据用户管理、角色配置、案件删除、全局看板合伙人全所案件查看所有案件、审批费用、分配律师主办律师本人负责的案件录入进展、上传文书、修改案件信息律师助理协助案件上传资料、维护日程禁止删除和结案这里有一个容易被忽略的细节角色只是第一层控制数据范围才是更重要的第二层。同样是“查看案件”主办律师只能看到自己作为承办人的案件合伙人能看到全所的案件。这套系统在后端查询时通过参数拼接了当前用户的ID或部门ID而不是把案件全查出来再做内存过滤。这个设计保证了数据量大时权限控制依然有效也避免了越权读取。2. 技术选型复盘Spring BootVueMySQL为什么能扛住这套业务技术选型没有最好的只有最合适的。对于一个律师事务所案件管理系统来说开发周期要短、后期维护要简单、招人要好招Spring Boot Vue MySQL恰好全部满足。Java在后端领域的统治力不用多说Vue在后台管理界面开发中的效率也是经过大量项目验证的MySQL则足够支撑几万人规模律所的数据量。2.1 Spring Boot后端的优势与MyBatis-Plus数据访问Spring Boot能做到“可直接运行”靠的是内嵌Tomcat和自动配置。开发者不需要单独装Tomcat也不需要写一堆XML配置Maven打包后直接java -jar xxx.jar就能启动服务。对于律所这种内部系统这种部署方式非常省事。数据持久层方面项目使用MyBatis-Plus而不是纯MyBatis。原因是案件管理系统里有大量单表CRUD操作比如案件列表分页查询、客户信息增删改查用MyBatis-Plus内置的BaseMapper可以直接省掉重复方法。复杂的多表联查比如“统计每个律师本月结案数”再手写XML里的大SQL这样组合的好处是灵活度和效率都保住了。同时Spring Boot生态里的参数校验Validation、定时任务Scheduled、邮件发送Mail等都是现成的starter二次开发时可以直接引入。比如开庭提醒功能只需要在Service层写好任务逻辑再加一个Scheduled(cron 0 0 8 * * *)注解就能每天早上八点自动扫描当天需要开庭的案件。2.2 Vue前端的组件化开发与后台界面搭建Vue的好处在于组件化。一个后台管理系统说白了就是一堆表单、表格、弹窗、标签页这些东西在Element UI或Element Plus里都有现成组件。前端工程里可以明显看到代码按“页面-组件-接口”三层来组织Views目录放页面Components目录放公共组件比如上传文件框、日期选择器Api目录统一封装axios请求。前端路由使用Vue Router并在路由守卫中读取用户信息和菜单权限。未登录时跳转登录页登录后根据角色动态注册可访问的路由。菜单不用写死后端返回一份菜单树前端用router.addRoutes动态挂载这样不同角色登录后看到的菜单天然不同。文件上传、PDF预览、表格导出这些高频能力也都有现成库支持。这里要强调一下前后端分离的协作方式后端只负责返回JSON数据一般约定响应格式是{ code: 200, data: {}, message: success }。前端在axios拦截器里统一处理code如果是401就跳登录页如果是500就弹错误提示。这样后端接口可以不用频繁处理页面跳转逻辑职责边界非常清晰。2.3 MySQL表结构设计案件、客户、费用、文书如何关联数据库设计直接决定系统扩展性。这套系统的主表是case_info案件表它像一根主线把客户、律师、费用、文书全部串起来。核心字段包括案件编号、案件名称、案件类型、标的金额、承办律师、协办助理、当前状态、开庭日期等。为了方便理解我简化一下核心表的关系client客户表客户名称、联系方式、客户类型、证件号。case_info案件表外键关联client_id同时用lawyer_id字段指认承办律师。case_document文书表外键关联case_id记录文件路径、上传人、上传时间。fee_record费用表外键关联case_id包含费用类型、金额、到账状态。sys_user用户表通过sys_user_role关联角色角色再关联权限菜单。案件表里有一组字段要特别留意就是状态字段status注释里写着“1咨询、2已委托、3办理中、4已结案、5已归档”。像这类状态字段我建议使用tinyint而不是varchar避免传空字符串或者大小写不一致的问题。业务层再配合常量类定义状态值代码里不出现裸数字。举个例子建表语句中案件表的关键部分大概是这样的CREATE TABLE case_info ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, case_no VARCHAR(32) NOT NULL COMMENT 案件编号, case_name VARCHAR(200) NOT NULL COMMENT 案件名称, client_id BIGINT DEFAULT NULL COMMENT 客户ID, lawyer_id BIGINT DEFAULT NULL COMMENT 承办律师ID, assistant_id BIGINT DEFAULT NULL COMMENT 助理ID, case_type VARCHAR(50) DEFAULT NULL COMMENT 案由, amount DECIMAL(12,2) DEFAULT 0.00 COMMENT 标的金额, status TINYINT DEFAULT 1 COMMENT 1咨询 2已委托 3办理中 4已结案 5已归档, court_date DATETIME DEFAULT NULL COMMENT 开庭日期, remark VARCHAR(500) DEFAULT NULL COMMENT 备注, create_time DATETIME DEFAULT NULL, update_time DATETIME DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT案件信息表;这里用逻辑外键而不是物理外键是我一直推荐的做法。物理外键在删除客户、迁移数据时会带来很多麻烦而代码层面通过JOIN或业务校验来控制引用关系灵活得多。MySQL对大数据量并发写入时物理外键也会产生额外的锁开销逻辑外键更适合这种业务系统。3. 可运行源码的完整启动指南从解压到浏览器出现登录页拿到源码后第一件事不是改代码而是先把环境跑通。这套工程的启动流程并不复杂但有不少细节容易翻车尤其是版本选择和配置文件。我按实际执行顺序一步步说。3.1 环境准备JDK、Node、MySQL的版本选择在启动之前先确认本机环境。后端是Spring Boot前端是Vue数据库是MySQL工具版本直接决定能不能顺利跑起来。工具推荐版本注意事项JDK8或11工程如果是Spring Boot 2.xJDK8完全够用如果是3.x必须JDK17Maven3.6也可以用IDE内置MavenNode.js14或16Vue2项目建议Node14Vue3项目建议Node16及以上npm随Node自带安装依赖前确认npm镜像源MySQL5.7或8.08.0的驱动和时区配置与5.7不同后面会讲有一个很多人忽略的点MySQL版本差异。如果你本机装的是MySQL 8.0但源码里还写着旧的驱动类com.mysql.jdbc.Driver启动时一定会报ClassNotFoundException。后面第5章我会专门讲这个坑。建议先统一把MySQL客户端和服务端都装成8.0并且用Navicat或命令行能正常连上。3.2 后端启动数据库初始化与Spring Boot服务跑起来后端启动主要分三步。第一步是建库和导入数据。源码包里一般会有sql文件夹里面是一个完整的初始化脚本init.sql包含建库、建表、插入初始数据以及默认的管理员账号。我用命令行举例mysql -u root -p init.sql执行之后数据库里会出现law_db这样的库名各表自动建立初始数据也会插入。此时打开后端配置文件application.yml修改数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/law_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver这里driver-class-name在MySQL 8.0下一定要写成com.mysql.cj.jdbc.Driver同时serverTimezone必须加上否则会报时区错误。修改保存后在IDE里找到带有SpringBootApplication注解的主类直接运行main方法控制台看到Tomcat started on port(s) 8080就表示后端启动成功。如果后端端口被占用启动会报Port already in use可以改用命令行启动时指定端口java -jar law-case-system.jar --server.port80813.3 前端启动依赖安装与本地代理配置前端是标准的Vue工程需要先进入前端目录安装依赖。这里强烈建议使用npm的国内镜像否则下载依赖会等到崩溃。npm install -g cnpm --registryhttps://registry.npmmirror.com cnpm install依赖装好后启动开发服务器npm run serve默认情况下Vue开发服务器会运行在localhost:8080但后端已经占用了8080端口所以需要在vue.config.js里修改前端端口同时配置代理把/api开头的请求转发到后端地址module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };配置代理之后前端代码里请求统一写成/api/login这样的相对路径而不是写死http://localhost:8080这样开发环境和生产环境可以无缝切换只需要改代理或Nginx配置。3.4 登录验证与常见启动问题的快速定位完成上述步骤后浏览器访问http://localhost:3000会跳转到登录页。源码里一般都内置了管理员账号比如admin/123456。输入后如果能进入首页看板看到案件统计、待办提醒等数据说明整套系统已经跑通。如果登录后页面一直转圈优先按顺序排查先看后端控制台是否打印了Token相关错误再看浏览器F12的Network面板请求是否发出、返回什么状态码。常见状态码对应关系404后端接口地址不存在检查代理路径是否正确。401token缺失或过期清除本地缓存重新登录。500后端代码异常直接看控制台堆栈。这一套排查思路比盲改代码高效得多。我见过不少人因为一个跨域问题折腾半天结果只是前端代理没配好。4. 核心功能实现拆解案件状态机、JWT鉴权与文件上传运行只是起点真正写出这套系统的价值在于里面的业务逻辑封装。我挑三个核心模块详细拆解这三个点理解了其他功能都是顺着这个思路长出来的。4.1 案件状态流转用状态字段还是状态机案件状态的流转不是随意的。比如一个“办理中”的案件不能直接跳回“咨询”一个“已结案”的案件也不能重新打开。这套系统在Service层定义了一套状态校验规则核心逻辑是public void changeStatus(Long caseId, Integer targetStatus) { CaseInfo caseInfo caseInfoMapper.selectById(caseId); if (caseInfo null) { throw new BizException(案件不存在); } // 办理中的案件只能转到已结案或已归档 if (caseInfo.getStatus() 3 targetStatus ! 4 targetStatus ! 5) { throw new BizException(当前状态不允许直接操作); } // 已结案的案件不能再修改案件信息 if (caseInfo.getStatus() 4 targetStatus ! 5) { throw new BizException(已结案案件只能归档); } caseInfo.setStatus(targetStatus); caseInfoMapper.updateById(caseInfo); }这里没有引入复杂的状态机框架而是用简单的状态校验方法因为律所案件的状态数量有限状态跳转规则也相对固定。我的建议是业务复杂到需要可视化流转时再考虑Flowable或Activiti工作流引擎否则硬上框架只会增加维护成本。另外每次状态变更都要落一条操作日志。源码里有一个case_log表记录案件ID、操作人、从什么状态变到什么状态、操作时间。这样将来审计时有据可查律师执业合规要求里很重要的一条就是案卷全流程可追溯。4.2 基于JWT的接口权限控制关键代码这个系统的登录鉴权用的是JWT方案。用户登录成功后后端生成一个带有用户ID和角色信息的token前端把这个token存到localStorage里。此后每次请求axios拦截器都会自动加上Authorization: Bearer token请求头。后端通过一个过滤器统一拦截请求代码逻辑大致是这样Component public class JwtAuthenticationFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); // 解析token如果合法则把用户信息放入Spring Security上下文 LoginUser loginUser JwtUtil.parseToken(token); if (loginUser ! null) { UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } }Spring Security默认会拦住所有请求所以登录接口必须放行。配置类的写法如下http.authorizeRequests() .antMatchers(/api/auth/login, /api/auth/captcha).permitAll() .anyRequest().authenticated();另外接口级权限通过PreAuthorize(hasRole(PARTNER))这样的注解控制合伙人专属的审批接口只有合伙人角色能调用。需要注意的是Spring Security默认的角色前缀是ROLE_数据库或JWT里存的角色值要对应上否则会出现明明有权限却还是403的情况。4.3 案件材料上传与静态资源映射律所系统里最常用的功能之一就是案件材料管理包括起诉状、答辩状、判决书、证据材料等。文件管理如果设计得好后面接对象存储、接预览功能都会很顺。这个系统的上传接口是典型的MultipartFile方式PostMapping(/api/case/document/upload) public R upload(RequestParam(file) MultipartFile file, RequestParam(caseId) Long caseId, RequestParam(docType) String docType) { // 保存原文件名按案件ID分目录 String dirPath System.getProperty(user.dir) /uploads/case/ caseId; File dir new File(dirPath); if (!dir.exists()) { dir.mkdirs(); } String newFileName UUID.randomUUID().toString().replaceAll(-, ) _ file.getOriginalFilename(); file.transferTo(new File(dir, newFileName)); // 只保存文件路径到数据库不存base64 CaseDocument doc new CaseDocument(); doc.setCaseId(caseId); doc.setDocType(docType); doc.setFilePath(/uploads/case/ caseId / newFileName); doc.setUploadUser(CurrentUser.getId()); caseDocumentMapper.insert(doc); return R.ok(); }这里有两个关键点。第一数据库只存相对路径不存文件二进制内容这样数据库表体积不会膨胀备份也快。第二磁盘目录按案件ID分文件夹找文件非常方便。为了能通过URL访问这些文件Spring Boot里还要做一个静态资源映射registry.addResourceHandler(/uploads/**) .addResourceLocations(file: System.getProperty(user.dir) /uploads/);前端拿到文件路径后直接拼在baseURL后面就能预览或下载。如果以后要迁移到OSS或MinIO只需要把这个上传接口的内部存储逻辑替换掉对外API不用变这就是分层设计的好处。5. 实测中踩到的坑从数据库驱动到跨域拦截这部分是我实际跑项目时遇到的问题每一个都实实在在卡过项目。如果你启动失败优先对照这里排查大概率能直接找到原因。5.1 MySQL驱动与时区配置引起的启动失败这是最经典的报错没有之一。很多老项目用的连接驱动是com.mysql.jdbc.Driver但这个类在MySQL 8.0中已经移除了必须改成com.mysql.cj.jdbc.Driver。如果你的application.yml里没改启动时会直接抛出ClassNotFoundException。同时连接URL里要加serverTimezoneAsia/Shanghai否则会报The server time zone value й׼ʱ is unrecognized这样的乱码时区错误。正确的URL我在前面已经写过这里再强调一次jdbc:mysql://localhost:3306/law_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai不要使用GMT%2B8这种写法在跨操作系统部署时容易出问题直接用Asia/Shanghai最安全。5.2 Vue开发服务器的跨域代理与后端CORS冲突另一个高频问题是跨域。很多人在后端写了一个全局CORS配置允许所有源跨域前端又使用vue.config.js的代理转发。这两个方案同时存在就会导致登录请求被处理两次有时候请求会莫名多出一层OPTIONS预检前端看到的是CORS error。我建议开发环境里只保留一种方案。最省事的做法是后端注释掉CORS配置类完全依靠前端代理转发。因为代理的原理是让浏览器以为请求是同源的浏览器先发到localhost:3000再由Vue开发服务器转发到localhost:8080后端看到的是来自服务器的请求天然不存在跨域问题。生产环境则用Nginx做反向代理把/api请求转发到后端服务同时处理好静态文件这套源码部署到云服务器上就是这么做的。5.3 中文乱码、金额精度与日期格式化中文乱码可能出现在三个层面数据库、连接、IDE。数据库层面要把表和库的字符集统一为utf8mb4连接层面在URL里加characterEncodingutf8IDE层面保证源码文件本身就是UTF-8编码。很多源码压缩包是从Windows环境导出的用记事本打开再另存为UTF-8是无效的反而会把BOM带进去最稳妥的是用IDE直接操作。金额字段必须用DECIMAL(12,2)不能用float或double否则出现0.10.20.30000000000000004这类问题。前端展示金额时也不要直接拿double做运算要转换成字符串或者乘以100用整数分存储。日期格式化也比较坑。前后端分离后后端返回给前端的日期默认是带T的ISO格式类似2025-04-12T10:30:00直接显示在页面上很丑。一般在application.yml里统一配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8这样所有接口返回的LocalDateTime都会自动格式化不需要每个字段手写JsonFormat。5.4 案件编号并发生成逻辑的隐患案件编号是律所管理中的面子工程格式通常是LS20250412001这样的分段编号。如果生成逻辑是SELECT MAX(case_no) FROM case_info WHERE create_time LIKE 2025-04-12%然后加1就会存在并发问题。两个律师同时录案件时可能查到同一个最大值生成重复编号插入数据直接主键冲突。这套源码的解决方案比这个稳妥一些用的是时间戳加随机数作为后缀虽然没有数据库序列那么严谨但对于中小型律所已经够用。如果你要二次开发建议要么用Redis的INCR命令生成每日自增编号要么直接用UUID作为逻辑主键表结构里再加一个带唯一索引的业务编号字段。6. 拿到源码后如何做二次开发而不破坏原有结构跑通只是开始大部分人拿到源码是想改造成适合自己的项目。但改代码之前如果没看清结构很容易把系统搞成一团乱麻。我分享一些自己的经验。6.1 先从数据字典和表关系建立全局认知我拿到任何一套源码第一件事永远是看SQL脚本里的注释和表关系。先打开数据库工具把表按业务分组基础表、业务表、权限表。然后找到几根关键主线比如案件主表关联了哪些表用户表怎么关联角色。理清这些之后再打开前端路由看每个页面对应哪个接口就知道系统的大致脉络了。这一步不要跳过。很多人急着改页面改了三天发现数据对不上就是因为没看表结构。案件管理系统最核心的表就是case_info、sys_user、case_document、fee_record只要把这四张表和它们之间的关联吃透后面加功能不会迷路。6.2 新增业务模块时的代码放置规范新增模块时不要图省事把代码堆在已有Controller里。后端按controller/service/mapper/entity四层分包每个业务模块建自己的一套包。比如新增一个“利益冲突检索”模块就新建conflict相关的Controller、Service和实体类前端也对应新建views下的conflict目录和api的conflict.js。这种方法最大的好处是隔离性强。改一个模块时不会影响到其他接口后面代码合并时也几乎不会出现冲突。如果你只是外行改代码至少也要保证新增的方法不修改已有的接口签名否则改一处坏了三处排查起来极其痛苦。6.3 引入工作流和消息通知的扩展思路最后说说这套系统可以往哪个方向扩展。如果律所需要更严格的审批流比如大额风险代理需要合伙人层层审批可以集成Flowable工作流引擎。但我不建议一开始就上工作流先用当前源码里的状态字段加审批表的模式99%的业务需求都能覆盖等人多流程复杂了再升级。消息通知方面可以利用Spring Boot自带的定时任务每天早上扫描当天的开庭提醒调企业微信或钉钉webhook推送给承办律师。源码里已有的court_date字段就是现成的任务数据源。这块扩展价值很高律所最怕的就是忘记开庭时间导致当事人投诉一个及时的消息提醒能直接提升服务质量。我个人在实际操作中的体会是这种前后端分离的“可直接运行”工程跑通只是完成了最基础的一步。真正有价值的是后面的业务抽象能力建议拿到源码后先耐心梳理数据库表和权限模型理解每条业务规则的来龙去脉再动手改功能。把案件状态流转和权限控制这两个主心骨吃透后面加再多模块都是往骨架上挂附件不会伤筋动骨。希望这篇拆解能帮你省下自己摸坑的时间。
返回列表