ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring Boot财务管理系统实战:凭证、报表与权限设计

Spring Boot财务管理系统实战:凭证、报表与权限设计 简介面向Java毕业设计场景的一套财务管理系统完整项目采用Spring Boot框架搭配JDK1.8、MySQL5.7与Tomcat7包含前后端源码、毕业论文、答辩PPT及演示视频。资源共452个文件压缩后约93.17MB文件类型以java后端逻辑、vue前端页面、svg图标、js交互脚本、xml配置、sql数据库脚本和mp4演示为主目录按管理员功能与员工功能模块划分并覆盖功能结构设计、数据库E/R图、数据表、系统测试等章节内容结构清晰便于对照学习。包内附带install/run/build等bat启动脚本开发环境要求明确可帮助减少JDK、Maven、数据库版本等配置成本快速在eclipse或idea中运行部署。目前已有179人学习下载适合需要完整参考毕业设计、完成系统开发或准备论文答辩的本专科学生也可作为基于Spring Boot的管理系统二次开发基础。1. 基于springboot开发的财务管理系统是毕设、更是能跑通全流程的Java工程一个基于springboot开发的财务管理系统能在一次毕业设计里把Java后端、MySQL建模、权限控制、报表输出和答辩PPT全部串成一条线这是它成为热门选题的真正原因。它不要求你懂高深算法也不依赖分布式中间件但足够让你把springboot项目的完整生命周期走一遍从建表、写Service、做权限到打成jar包跑起来。这类题目的边界特别干净。登录、科目、凭证、账簿、报表、年末结转前几个模块都是标准CRUD后面跟着事务和统计SQL难度刚好卡在“能独立完成”和“有技术深度”之间。对于想拿现成源代码改一改交差的同学它有现成骨架对于想在答辩时讲清楚设计亮点的人它也有足够的安全感。如果你正对着题目不知道从哪里动手下面的路径是实际做过一遍的顺序先立表结构再写凭证事务接着补权限和报表最后用内存库跑冒烟测试。每一章都会给出关键代码和参数也会把最容易翻车的地方提前拦下来。2. 先把骨架立住财务系统的业务域、表结构与选型理由2.1 财务管理系统的最小功能域科目、凭证、账簿、报表财务系统再复杂落到功能上一般就五块系统管理、基础资料、凭证管理、账簿与报表、期末处理。系统管理管用户角色菜单基础资料里最核心的是会计科目凭证管理管录入、审核、过账账簿与报表负责把凭证汇总成总账、明细账和月度报表期末处理则承担结账和损益结转。凭证是整张数据流的中心。没有凭证后面的账簿和报表都是空的凭证错了报表也跟着错。所以设计时先想清楚凭证怎么存而不是先做漂亮的页面。科目这块建议按二级或三级结构设计编码要有规律。比如1001 库存现金1002 银行存款1122 应收账款2202 应付账款6001 主营业务收入科目表要允许子节点并且用parent_id维护层级。报表汇总时经常需要按一级科目汇总所以科目编码最好固定长度分段比如 1002 是二级科目100201、100202 是它的明细科目。这样 SQL 里用LEFT(code, 4)就能把二级科目汇总出来非常顺手。2.2 选型理由springboot MyBatis-Plus MySQL 在毕设里为什么最稳常见做法是 springboot 2.7.x JDK8 MyBatis-Plus MySQL 5.7/8.0。这一套组合在毕设里最稳原因不是性能而是资料多、排错容易、模板代码少。组件选择理由基础框架Spring Boot 2.7.x网上教程和现成代码最多javax.*命名空间老项目通用ORMMyBatis-Plus单表 CRUD 不用写 SQL条件构造器适合动态查询数据库MySQL 5.7 或 8.0环境常见DECIMAL和GROUP BY行为稳定权限自写拦截器财务系统权限模型简单引入 Spring Security 学习成本太高缓存Redis 可选没 Redis 也能跑加了显得架构完整但会增加部署难度MyBatis-Plus 和 JPA 之间我选前者。财务系统的查询条件非常动态日期区间、科目编码、凭证状态、制单人经常是用户勾选几个条件组合查询。JPA 写动态条件要用 Specification远没有 MyBatis-Plus 的LambdaQueryWrapper来得直观。不建议在毕设里引入 Shiro 或 Spring Security。后面会发现真正扣分的是业务逻辑而不是权限框架用得有多高级。自己写一个拦截器把登录态和角色判断放在HandlerInterceptor里完全够用。2.3 四张核心表的 DDL把财务数据规则写进数据库建表是整个项目的地基。下面这套 DDL 我一般直接作为项目初始化脚本索引和唯一键都按财务场景设置。CREATE DATABASE IF NOT EXISTS financial DEFAULT CHARSET utf8mb4; USE financial; -- 会计科目表 CREATE TABLE acc_subject ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 科目ID, code VARCHAR(20) NOT NULL COMMENT 科目编码, name VARCHAR(50) NOT NULL COMMENT 科目名称, parent_id BIGINT DEFAULT 0 COMMENT 父科目ID0为根, direction TINYINT DEFAULT 1 COMMENT 余额方向1借 2贷, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, PRIMARY KEY (id), UNIQUE KEY uk_code (code) ) ENGINEInnoDB COMMENT会计科目表; -- 凭证主表 CREATE TABLE acc_voucher ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 凭证ID, voucher_no VARCHAR(32) NOT NULL COMMENT 凭证号, voucher_date DATE NOT NULL COMMENT 凭证日期, state TINYINT DEFAULT 0 COMMENT 0草稿 1已审核 2已过账, total_debit DECIMAL(16,2) DEFAULT 0 COMMENT 借方合计, total_credit DECIMAL(16,2) DEFAULT 0 COMMENT 贷方合计, created_by BIGINT DEFAULT 0 COMMENT 制单人, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_voucher_no (voucher_no), KEY idx_voucher_date (voucher_date) ) ENGINEInnoDB COMMENT凭证主表; -- 凭证明细表 CREATE TABLE acc_voucher_detail ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 明细ID, voucher_id BIGINT NOT NULL COMMENT 凭证ID, subject_code VARCHAR(20) NOT NULL COMMENT 科目编码, summary VARCHAR(200) DEFAULT COMMENT 摘要, debit DECIMAL(16,2) DEFAULT 0 COMMENT 借方金额, credit DECIMAL(16,2) DEFAULT 0 COMMENT 贷方金额, PRIMARY KEY (id), KEY idx_voucher_id (voucher_id) ) ENGINEInnoDB COMMENT凭证明细表; -- 部门预算表 CREATE TABLE acc_budget ( id BIGINT NOT NULL AUTO_INCREMENT, dept_id BIGINT NOT NULL COMMENT 部门ID, budget_month VARCHAR(7) NOT NULL COMMENT 预算月份格式2025-07, amount DECIMAL(16,2) DEFAULT 0 COMMENT 预算额, used_amount DECIMAL(16,2) DEFAULT 0 COMMENT 已用额, PRIMARY KEY (id), UNIQUE KEY uk_dept_month (dept_id, budget_month) ) ENGINEInnoDB COMMENT部门预算表;几个关键参数要注意。金额字段全部用DECIMAL(16,2)精确到分最大可以表示万亿级别对大多数业务够用。voucher_no加了唯一键这是后端并发防重的最后一道防线。voucher_date加普通索引因为报表按日期间隔查没有索引数据量上来后就变慢。acc_voucher_detail只存subject_code不直接存科目名称。名称随时可能改改了也不影响历史凭证查询时再 JOIN 科目表取值。这样设计能避免“科目改名导致历史报表错乱”的问题。3. 用 Spring Boot 落地记账、结账与报表事务边界、金额精度、汇总SQL3.1 凭证录入接口用事务把“借贷平衡”变成硬约束凭证录入是财务系统的主入口。前端把多条明细一次性提交过来后端要做两件事校验借贷平衡、保证主表和明细表同时落库。Service public class VoucherServiceImpl implements VoucherService { Override Transactional(rollbackFor Exception.class) public Long createVoucher(VoucherCreateDTO dto) { // 1. 校验日期不能落在已结账月份 if (financePeriodService.isClosed(dto.getVoucherDate())) { throw new BusinessException(该会计期间已结账不能录入凭证); } // 2. 计算借贷合计必须相等 BigDecimal debitSum BigDecimal.ZERO; BigDecimal creditSum BigDecimal.ZERO; for (VoucherDetailDTO item : dto.getDetails()) { debitSum debitSum.add(item.getDebit() null ? BigDecimal.ZERO : item.getDebit()); creditSum creditSum.add(item.getCredit() null ? BigDecimal.ZERO : item.getCredit()); } if (debitSum.compareTo(creditSum) ! 0) { throw new BusinessException(借贷不平衡借方合计 debitSum 贷方合计 creditSum); } // 3. 写主表 Voucher voucher new Voucher(); voucher.setVoucherNo(buildVoucherNo(dto.getVoucherDate())); voucher.setVoucherDate(dto.getVoucherDate()); voucher.setState(0); this.save(voucher); // 4. 写明细表 ListVoucherDetail details new ArrayList(); for (VoucherDetailDTO item : dto.getDetails()) { VoucherDetail detail new VoucherDetail(); detail.setVoucherId(voucher.getId()); detail.setSubjectCode(item.getSubjectCode()); detail.setSummary(item.getSummary()); detail.setDebit(item.getDebit()); detail.setCredit(item.getCredit()); details.add(detail); } this.saveBatch(details); return voucher.getId(); } }Transactional(rollbackFor Exception.class)是这里最重要的参数。Spring 默认只在抛出RuntimeException时回滚事务如果你在代码里抛的是自定义业务异常或者受检异常不加rollbackFor主表可能已经插入成功明细表插入失败最后留下一张残凭证。借贷校验用的是compareTo不是equals。BigDecimal的equals会同时比较数值和精度1.0和1.00会被判为不相等但财务上它们是一样的所以比较大小一律用compareTo。saveBatch是 MyBatis-Plus 提供的方法内部会分批插入默认批次 1000 条。凭证明细一般十几条一两条 SQL 就完成了不需要调整批大小。3.2 金额精度BigDecimal 之外的三个配置缺一个都可能在 JSON 层丢精度用过double存金额的同学应该都见过0.010000000000000009这种数字。财务系统里金额精度翻车往往不是数据库的问题而是 JSON 序列化和反序列化那一层。public class VoucherDetailDTO { JsonFormat(shape JsonFormat.Shape.STRING) private BigDecimal debit; JsonFormat(shape JsonFormat.Shape.STRING) private BigDecimal credit; }JsonFormat(shape JsonFormat.Shape.STRING)的作用是让 Jackson 把BigDecimal序列化成字符串123.45而不是数字123.45。前端 JavaScript 的数字类型对超过Number.MAX_SAFE_INTEGER的整数精度会丢失金额一般不会到那个量级但如果 JSON 里存的是 double反序列化时可能先转成二进制浮点再转回BigDecimal精度就脏了。application.yml 里还要配合两行配置spring: jackson: generator: write_bigdecimal_as_plain: truewrite_bigdecimal_as_plain保证1E2这样的科学计数法被写成100避免前端把大数字字符串误判成科学计数法。后端在入库前我一般再统一做一次精度规整public static BigDecimal toMoney(BigDecimal value) { if (value null) { return BigDecimal.ZERO; } return value.setScale(2, RoundingMode.HALF_UP); }setScale(2, RoundingMode.HALF_UP)表示保留两位小数四舍五入。财务上常见的舍入模式是HALF_UP也就是传统的四舍五入HALF_EVEN是银行家舍入只有做复杂利息计算时才需要考虑。毕设和一般业务系统用HALF_UP完全够。3.3 月度科目汇总报表一条 GROUP BY 能顶三个页面总账和科目汇总表的本质就是把凭证明细按科目和时间聚合。下面这条 SQL 是财务系统里最常用的月度科目汇总。SELECT DATE_FORMAT(v.voucher_date, %Y-%m) AS month, d.subject_code, s.name AS subject_name, SUM(d.debit) AS month_debit, SUM(d.credit) AS month_credit FROM acc_voucher_detail d JOIN acc_voucher v ON v.id d.voucher_id JOIN acc_subject s ON s.code d.subject_code WHERE v.state 2 AND v.voucher_date 2025-01-01 AND v.voucher_date 2025-07-01 GROUP BY month, d.subject_code, s.name ORDER BY month, d.subject_code;日期区间用和组合是前闭后开区间这样不会漏掉当天数据也不会把下个月 1 号的记录算进来。v.state 2表示只统计已过账的凭证草稿和已审核但未过账的凭证不进报表这是财务的硬规则。MySQL 5.7 以上默认开启了ONLY_FULL_GROUP_BY所以 SELECT 里的非聚合字段s.name必须写进 GROUP BY否则直接报错。如果报表还要展示本月发生额和本年累计MySQL 8.0 可以用窗口函数SELECT month, subject_code, subject_name, month_debit, SUM(month_debit) OVER (PARTITION BY subject_code ORDER BY month) AS cum_debit FROM (...)窗口函数在毕设里属于加分项写进论文和答辩 PPT 都有内容可讲而且实现成本很低。3.4 月末结转把“结账”做成一张可追溯的凭证期末结转和普通凭证录入最大的区别是结转凭证不能手工改必须按规则生成。常见做法是写一个FinancePeriodService月末时把损益类科目的余额汇总生成一张“结转本年利润”的凭证。Transactional(rollbackFor Exception.class) public void closePeriod(String yearMonth) { // 1. 检查该期间是否已有凭证未过账 // 2. 归集损益类科目余额 ListSubjectBalance balances voucherDetailMapper.sumBalanceBySubjects( yearMonth -01, yearMonth -31, PROFIT_LOSS_SUBJECTS); // 3. 生成一张新的结账凭证借贷方分别记录利润归集 Voucher voucher buildTransferVoucher(yearMonth, balances); // 4. 更新预算使用额 budgetService.refreshUsedAmount(yearMonth); }这里的核心原则是“生成凭证”不是“直接更新科目余额”。直接 UPDATE 余额表当然省事但结果不可追溯审计也过不了。每一笔变动都要有凭证作依据这是财务系统和其他管理系统最不一样的地方。结转跑完之后期间状态改成“已关闭”。凭证录入接口里面第一步查的就是这个状态一旦已结账该月份的凭证一律禁止录入或删除。这个状态可以单独建一张表也可以用一张配置表存yearMonth state。4. 开发与答辩中的常见问题排查财务数据出错的5个现场4.1 JSON 反序列化把金额变成科学计数法导致入账金额少了分现象前端传了一笔金额123456789012.34数据库里存成了123456789012.33用户投诉账对不上。原因前端把金额放在 JSON 里不带引号Jackson 默认把数字解析成double再转成BigDecimal时已经产生了二进制浮点误差。金额越大、小数位越多误差越明显。解决前端金额字段统一传字符串后端 DTO 和实体上加JsonFormat(shape JsonFormat.Shape.STRING)入库前再执行一次setScale(2, RoundingMode.HALF_UP)。这个坑在金额小于一万时基本看不见测试时一定要用大数测一遍。4.2 两个用户同时录凭证后提交的覆盖了先提交的现象财务办公室两个人同时点保存生成两张凭证号相同的记录或者一个提示成功一个提示失败但列表里出现重复。原因凭证号在应用层生成逻辑是日期加当天序号两个请求同时拿到同一个序号又没有做互斥校验数据库也没有唯一键就双双入库了。解决acc_voucher.voucher_no加唯一索引保存时捕获DuplicateKeyException。try { voucherService.save(voucher); } catch (DuplicateKeyException e) { throw new BusinessException(凭证号重复请重试); }加了唯一键之后并发请求里后提交的那个会直接撞索引报错数据库拒绝第二条重复凭证入库。这里不要用synchronized单机环境能防部署多实例后就失效了。给数据表加唯一约束是最终防线。4.3 springboot 版本太高代码全是 javax 改 jakarta现象按教程新建 springboot 3.x 项目把旧代码粘进去所有javax.servlet.*都红了或者启动时报ClassNotFoundException: javax.servlet.Filter。原因springboot 3.x 把 Java EE 的命名空间从javax.*迁移到了jakarta.*这是底层变化不是包名替换那么简单。解决如果手里参考代码都是老写法直接退回 springboot 2.7.x JDK8这是最省事的路线。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent properties java.version1.8/java.version /properties如果你已经用了 springboot 3.x那就统一 JDK17 起步把所有javax.servlet替换成jakarta.servlet拦截器、过滤器、文件上传相关的配置都会受到影响。答辩前临时改这个非常痛苦所以一开始就要定好版本路线中途别换。4.4 Excel 导出中文文件名乱码或文件损坏现象用 Apache POI 导出报表下载下来的文件在 Windows 上名字全是问号或者 Excel 打开提示文件已损坏。原因Content-Disposition头里的中文文件名没有做 URL 编码另一种情况是代码里同时操作了FileOutputStream和response.getOutputStream()最后把第一个流也 close 了导致文件被截断。解决统一用 response 输出流文件名先编码。response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); String fileName URLEncoder.encode(财务月度报表, StandardCharsets.UTF_8.name()); response.setHeader(Content-Disposition, attachment; filename fileName .xlsx);POI 5.x 之后只需要引入poi-ooxml一个依赖它会带基础 poi 包。如果同时手动引了poi和poi-ooxml版本不一致会出现NoSuchMethodError这也算一个隐藏坑。4.5 查询凭证详情报 LazyInitializationException或 JSON 序列化死循环现象Controller 直接返回查询到的实体对象列表功能正常详情功能报failed to lazily initialize a collection或者直接把实体序列化成 JSON 时栈溢出。原因实体里有ListVoucherDetail默认懒加载。事务在 Service 层已经结束Controller 再去访问这个集合时Session 已经关了双向引用还会导致 Jackson 循环递归。解决Controller 不返回实体返回 VO。在 Service 事务内把需要的明细手动拷贝出来事务结束后 VO 已经是独立对象不再依赖懒加载。public VoucherVO getVoucherDetail(Long voucherId) { Voucher voucher getById(voucherId); VoucherVO vo new VoucherVO(); BeanUtils.copyProperties(voucher, vo); ListVoucherDetailVO details voucherDetailMapper .selectList(new LambdaQueryWrapperVoucherDetail() .eq(VoucherDetail::getVoucherId, voucherId)); vo.setDetails(details); return vo; }不要依赖全局配置spring.jackson.serialization.fail-on-empty-beansfalse去掩盖问题那不是修复是延迟爆炸。5. 权限模型与可复现部署这个 springboot 系统怎么从本地跑到服务器5.1 RBAC 五张表一张菜单表撑起权限树财务系统的权限不需要太花哨RBAC 三件套足够用户、角色、菜单。一般要五张表用户和角色多一张关联表角色和菜单多一张关联表。表名作用关键字段sys_user用户表id, username, password, dept_id, statussys_role角色表id, role_name, role_codesys_menu菜单表id, parent_id, name, path, perm_codesys_user_role用户角色关联user_id, role_idsys_role_menu角色菜单关联role_id, menu_id菜单表的perm_code字段很有用它对应按钮级别的权限标识比如voucher:audit、voucher:delete。后端拦截器里判断用户是否有这个权限码有就放行没有就返回 403。权限初始化建议直接写在 SQL 脚本里答辩前重置数据库后一键跑脚本系统就能登录不用在页面上手工配置权限。这也是为什么很多源代码带的data.sql里会看到大量菜单 INSERT 语句。5.2 数据权限拦截会计只能看本部门凭证财务系统里经常要求会计只能查看自己部门的凭证财务经理能看全部。这个叫数据权限不是 RBAC 能直接解决的要做数据行级别的过滤。我一般会在 Service 层统一加条件不把过滤逻辑散落在各个 Controller。public ListVoucherVO pageVoucher(VoucherQueryDTO query) { LoginUser loginUser LoginUserHolder.get(); LambdaQueryWrapperVoucher wrapper new LambdaQueryWrapper(); wrapper.eq(Voucher::getState, query.getState()); wrapper.between(Voucher::getVoucherDate, query.getStartDate(), query.getEndDate()); // 非管理员只能看本部门 if (!loginUser.hasRole(admin)) { wrapper.eq(Voucher::getDeptId, loginUser.getDeptId()); } return voucherService.list(wrapper); }这里最关键的一点deptId必须从登录上下文里取不能信任前端传过来的参数。否则用户改一下请求参数就能查别人的部门账这在答辩时是致命的漏洞。如果你想让数据权限做得更彻底可以用 MyBatis-Plus 的TenantLineInnerInterceptor它会自动给所有 SQL 拼接租户条件。但要小心这个拦截器会作用在全部表上如果某张表没有对应字段就得写忽略规则维护成本不低。毕设场景下在 Service 层统一过滤已经够讲清楚了。5.3 application-prod.yml 配置与 jar 包启动开发环境用 dev部署环境用 prod配置要分开。prod 配置里数据库密码不要写明文用环境变量注入。spring: datasource: url: jdbc:mysql://localhost:3306/financial?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: financial password: ${DB_PASSWORD} hikari: maximum-pool-size: 20 minimum-idle: 5 mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: banner: false server: port: 8080username: financial不要用 root单独建一个业务账号给足权限就行。${DB_PASSWORD}从系统环境变量读取服务器上执行export DB_PASSWORD你的密码再启动避免密码出现在代码仓库里。打 jar 包启动看后端日志第一眼先确认是哪个 profile 生效mvn clean package -DskipTests java -jar target/financial-system.jar --spring.profiles.activeprod如果前后端都打包在同一个 Spring Boot 项目里前端静态文件放到src/main/resources/static下mvn 打包后会自动包含浏览器直接访问http://ip:8080就能打开页面不需要单独部署 Nginx。这个方式最适合答辩演示一台服务器一个 jar 包全搞定。5.4 论文素材与答辩 PPT 对应的技术点标题里的毕业论文和 PPT 不是额外负担而是把前面这些设计转成文档的过程。论文第 4 章可以写 E-R 图、表结构设计、五个模块的接口流程PPT 按“系统管理 → 基础资料 → 凭证录入 → 报表输出 → 期末结转”这条主线走一遍截图。技术亮点放在三处讲Transactional(rollbackFor)保证凭证一致性、BigDecimal加 JSON 字符串化防精度丢失、数据权限按部门隔离。这三处都是答辩时能被追问出深度的地方比堆功能列表强得多。6. 用 H2 内存库给这个 springboot 财务系统做冒烟测试答辩现场快速验证答辩前最怕的事是当着评委的面打开系统结果某张表数据被改坏了或者数据库连不上。我的做法是准备一套 H2 内存库测试配置不依赖外部 MySQL一键启动就能跑完整流程。spring: datasource: url: jdbc:h2:mem:financial;MODEMySQL;DB_CLOSE_DELAY-1 driver-class-name: org.h2.Driver username: sa password: sql: init: mode: always schema-locations: classpath:db/schema.sqlMODEMySQL是 H2 的兼容模式让大部分 MySQL 语法可以直接跑。注意第 2 章的建表脚本里ENGINEInnoDB和COMMENT在 H2 的某些版本下会解析失败需要单独准备一份 H2 用的精简版schema.sql把引擎声明和注释去掉。用这套配置启动项目再跑下面这张用例表基本覆盖了财务系统最核心的检查项。用例操作预期结果登录输入管理员账号密码返回 token能访问受保护接口创建科目新增 1122 应收账款成功重复创建同一编码时提示唯一键冲突录入平凭证借方 1001 100元贷方 2202 100元保存成功借贷合计均为 100录入不平凭证借方 100贷方 90接口报“借贷不平衡”主表明细表均无数据重复凭证号手工指定相同凭证号两次第二次保存失败提示凭证号重复月度报表录入跨月凭证后按月份查询只有已过账凭证进入汇总金额正确导出 Excel下载月度报表文件头正确文件名非乱码这套用例我一般放在工程根目录下的docs/smoke-test.md里。答辩前一天不去点页面先把这些场景在本地跑一遍它能快速告诉我哪个环节被我改坏了。之前调经费模块时我就是卡在第三条用例上发现 JSON 金额反序列化的问题前端传进去的金额经过 JS 的 Number 再转回来分位被截掉处理得越晚越难定位。后来把这个用例固定成每次改动后的必跑项才彻底摆脱了这种“数据悄悄错”的焦虑。希望帮到你。本文还有配套的精品资源点击获取
返回列表