首页 / 资讯中心 / 文章详情

SpringBoot+Vue财务系统源码实战:从跑通到生产级改造

SpringBoot+Vue财务系统源码实战:从跑通到生产级改造 ★ FEATURED ARTICLE
简介这是一套面向Java初学者与毕业设计学生的SpringBoot财务管理系统完整源码适用于课程设计、毕设开发及企业级财务管理原型验证。系统覆盖员工管理、工资核算、固定资产管理、序时账与年度利润分析等12个核心模块采用前后端分离架构JavaVue兼顾业务逻辑完整性与工程实践规范性。资源包共449个文件含123个Java后端业务类、47个Vue组件页面、27个JPG/PNG界面截图、21个JS交互脚本及16个XML配置文件辅以SQL建表语句、YML配置、BAT部署脚本和说明文档结构清晰便于分层学习与二次开发压缩包大小为17.71MB。已有138人下载学习读者可直接导入IDEA或Eclipse运行结合Navicat操作MySQL数据库并通过配套文档快速掌握环境搭建、模块调用与功能扩展路径显著降低毕业项目落地门槛。1. 这不是又一个“学生毕设模板”SpringBootVue财务系统源码包为什么能跑通、怎么改得动、哪些模块真能进生产你下载了一个叫“基于SpringBoot的财务管理系统源代码JavaVueMySQL说明文档LW.zip”的压缩包解压后看到backend/和frontend/两个文件夹、一堆.sql文件、一份 Word 格式的“论文LW”和一个README.md——但打开浏览器 localhost:8080 页面空白控制台报Failed to load resource: net::ERR_CONNECTION_REFUSEDnpm run serve启动后 Vue 页面能刷出来却卡在登录页F12 看 Network 里/login接口一直 pendingMySQL 导入finance_db.sql提示Unknown collation: utf8mb4_0900_ai_ci……这不是玄学是典型“可运行但不可维护”的财务系统源码包的真实切面。它不是玩具而是真实业务逻辑的浓缩体凭证录入需校验借贷平衡、科目树支持多级展开与禁用状态、报表生成依赖期初/期末/发生额三重数据聚合、权限粒度细到“只看不导出”——这些都不是 CRUD 模板能覆盖的。本文不讲“如何安装 Node.js”而是带你从 ZIP 包落地那一刻起把这套代码真正变成你手里的工具识别哪些模块已稳定可用、哪些必须重构、MySQL 字符集怎么降级兼容、Vue 打包后如何嵌入 SpringBoot 静态资源路径、MyBatis-Plus 的TableName注解为何在某些表上失效、以及最关键的——当客户突然要求增加“银行流水自动对账”功能时你该在哪几个文件里动刀、改几行 SQL、加哪两个接口。适合刚接手这类项目的新手快速建立掌控感也适合想复用其财务核心模型的老手跳过重复造轮子。2. 后端启动SpringBoot 项目结构解析与 MySQL 兼容性修复2.1 识别真实项目结构别被pom.xml里的spring-boot-starter-web蒙蔽打开backend/pom.xml先看parent标签下的 SpringBoot 版本号。常见陷阱是压缩包里写的是2.7.18但你的本地 Maven 仓库缓存了3.2.0导致RestController注解找不到。真实判断依据不是 pom.xml而是src/main/resources/application.yml中的spring.profiles.active值——如果配置为dev则实际加载application-dev.yml若该文件存在且定义了server.port: 8081那mvn spring-boot:run默认端口就是 8081而非 8080。再检查src/main/java/com/example/finance/下的主启动类通常叫FinanceApplication.java确认SpringBootApplication是否包含scanBasePackages com.example.finance。很多源码包为省事直接写scanBasePackages com.example但实际 mapper 接口在com.example.finance.mapperservice 在com.example.finance.service漏扫会导致Autowired报NoSuchBeanDefinitionException。提示不要盲目执行mvn clean install。先mvn dependency:tree -Dincludesorg.springframework.boot查看实际生效的 SpringBoot 版本再比对application.yml中spring-boot-starter-parent的版本声明是否一致。不一致时强制指定本地构建版本mvn clean compile -Dspring-boot.version2.7.182.2 MySQL 8.0 兼容性血泪现场utf8mb4_0900_ai_ci必须降级导入finance_db.sql失败90% 是字符集问题。MySQL 8.0 默认排序规则utf8mb4_0900_ai_ci在 5.7 及部分云数据库如阿里云 RDS MySQL 5.7上不识别。不要重装 MySQL直接改 SQL 文件# 在 finance_db.sql 文件开头插入以下两行位置CREATE DATABASE 语句之前 SET NAMES utf8mb4; SET CHARACTER SET utf8mb4; # 将所有出现的 utf8mb4_0900_ai_ci 替换为 utf8mb4_unicode_ci sed -i s/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g finance_db.sql更关键的是建表语句中的COLLATE子句。例如原 SQLCREATE TABLE t_account_subject ( id bigint NOT NULL AUTO_INCREMENT, code varchar(32) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;必须改为CREATE TABLE t_account_subject ( id bigint NOT NULL AUTO_INCREMENT, code varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;注意utf8mb4_unicode_ci是 MySQL 5.7 安全兼容的排序规则支持 emoji 且排序逻辑稳定。utf8_general_ci已废弃勿用。2.3 MyBatis-Plus 配置验证TableName与TableId的隐式陷阱财务系统中t_voucher凭证主表和t_voucher_detail凭证分录表必然存在一对多关系。检查TAccountVoucher.java实体类TableName(t_voucher) // ✅ 显式指定表名避免 MP 自动转下划线失败 public class TAccountVoucher { TableId(type IdType.ASSIGN_ID) // ✅ 使用雪花算法非数据库自增 private Long id; TableField(voucher_no) private String voucherNo; // ✅ 字段映射正确 // ❌ 错误示范未标注 TableField 的 createTime 字段 private Date createTime; // MP 默认按驼峰转 create_time但数据库字段名是 create_time还是 created_at }验证方法启动项目后访问/vouchers?page1size10查看日志中打印的 SQL Preparing: SELECT id,voucher_no,create_time,... FROM t_voucher WHERE deleted 0 LIMIT ?如果 SQL 中字段名是create_time但实体类字段是createTime说明 MP 驼峰转换生效如果 SQL 中是create_time但数据库字段是created_at则必须加TableField(created_at)。血泪经验财务系统时间字段命名极不统一——create_time/created_at/input_date都可能出现。逐个表检查application.yml中mybatis-plus.configuration.map-underscore-to-camel-case: true是否开启并对异常字段显式标注TableField。3. 前端联调Vue 项目启动、跨域配置与静态资源嵌入 SpringBoot3.1 Vue 环境验证绕过vue-cli-service serve的代理黑洞frontend/package.json中scripts: { serve: vue-cli-service serve }是标准启动方式但财务系统常因 API 基地址硬编码导致联调失败。先检查src/utils/request.js或src/api/index.js// ❌ 危险写法绝对路径写死 const BASE_URL http://localhost:8080/api; // ✅ 安全写法读取 .env.development const BASE_URL process.env.VUE_APP_BASE_API;对应frontend/.env.development应为VUE_APP_BASE_API /api此时vue-cli-service serve会将/api/**请求代理到http://localhost:8080SpringBoot 后端。但代理配置藏在vue.config.jsmodule.exports { devServer: { proxy: { /api: { target: http://localhost:8080, // ✅ 必须与后端实际端口一致 changeOrigin: true, pathRewrite: { ^/api: // ✅ 去掉 /api 前缀后端 Controller 才能匹配 RequestMapping(/login) } } } } }提示若后端端口是 8081此处target必须同步修改否则所有请求 404。3.2 Vue 打包嵌入 SpringBootdist目录的正确安放位置生产环境不跑npm run serve而是npm run build生成dist/再让 SpringBoot 托管静态资源。关键不是把dist放进resources/static而是放进src/main/resources/static注意路径层级。验证方法打包后启动 SpringBoot访问http://localhost:8080/应返回index.html且浏览器开发者工具 Network 标签页能看到js/app.xxx.js、css/app.xxx.css加载成功。若 404请检查src/main/resources/static下是否存在index.html由npm run build生成src/main/resources/static/js/下是否有app.xxx.jsxxx 为哈希值SpringBoot 是否禁用了默认静态资源路径检查application.yml中是否有spring.web.resources.static-locationsclasspath:/static/若被注释或修改需恢复3.3 登录流程穿透调试从 Vue 表单到 MyBatis-Plus 查询链路用户输入账号密码点击登录前端发请求// src/api/login.js export function login(data) { return request({ url: /auth/login, method: post, data // ✅ data 是 { username: admin, password: 123456 } }) }后端AuthController.java接收PostMapping(/login) public ResultVO login(RequestBody LoginDTO loginDTO) { // ✅ RequestBody 接收 JSON return authService.login(loginDTO); // ✅ 调用 service }AuthService.java中关键逻辑public ResultVO login(LoginDTO dto) { // 1. 根据 username 查询用户注意不是 email财务系统多用工号/账号 TUser user userMapper.selectOne(new QueryWrapperTUser().eq(username, dto.getUsername())); if (user null) { return ResultVO.fail(用户名不存在); } // 2. 密码校验源码常用 BCryptPasswordEncoder.matches(dto.getPassword(), user.getPassword()) if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { return ResultVO.fail(密码错误); } // 3. 生成 JWT Token财务系统敏感Token 有效期建议 ≤ 2h String token jwtUtil.generateToken(user); return ResultVO.success().data(token, token); }排查卡点若登录按钮无反应F12 查 Network →auth/login→ Preview看返回内容。若为空白说明后端没返回若返回{code:500,msg:Internal Server Error}查 SpringBoot 控制台日志重点看userMapper.selectOne是否抛出NullPointerExceptionuserMapper未注入或SQLSyntaxErrorException表名写错。4. 财务核心模块避坑凭证、科目、报表三大高频翻车点4.1 凭证录入借贷平衡校验的两种实现与致命漏洞财务系统最基础也最易崩的逻辑是“有借必有贷借贷必相等”。源码中常见两种校验方式方式一前端 JS 校验危险src/views/voucher/CreateVoucher.vue中checkBalance() { const debit this.details.reduce((sum, d) sum parseFloat(d.debitAmount || 0), 0); const credit this.details.reduce((sum, d) sum parseFloat(d.creditAmount || 0), 0); if (Math.abs(debit - credit) 0.01) { // ✅ 用 0.01 避免浮点误差 this.$message.error(借贷不平请检查); return false; } return true; }致命漏洞用户禁用 JS 后可绕过校验直接提交非法数据。必须后端二次校验。方式二后端 Service 层校验必须VoucherService.java中Transactional public void saveVoucher(TAccountVoucher voucher, ListTVoucherDetail details) { // ✅ 关键先计算总借方、总贷方 BigDecimal totalDebit details.stream() .map(d - d.getDebitAmount() ! null ? d.getDebitAmount() : BigDecimal.ZERO) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal totalCredit details.stream() .map(d - d.getCreditAmount() ! null ? d.getCreditAmount() : BigDecimal.ZERO) .reduce(BigDecimal.ZERO, BigDecimal::add); // ✅ 用 BigDecimal.compareTo() 避免浮点误差 if (totalDebit.compareTo(totalCredit) ! 0) { throw new BusinessException(凭证借贷不平 totalDebit ≠ totalCredit); } // 继续保存... }4.2 科目树加载递归查询的 N1 问题与 MyBatis-Plus 解法TAccountSubject表结构通常含id,parent_id,code,name,level,is_disabled。前端需要渲染多级科目树如1001 现金 → 1001.01 库存现金 → 1001.02 银行存款。常见翻车Controller 直接调用subjectService.list()返回全部科目前端用filterfind递归组装树 —— 数据量大时卡死。正确做法后端一次查出所有科目Java 层递归组装Service public class AccountSubjectServiceImpl extends ServiceImplAccountSubjectMapper, TAccountSubject implements AccountSubjectService { Override public ListTAccountSubject listSubjectTree() { // 1. 一次性查出所有启用的科目 ListTAccountSubject allSubjects this.list( new QueryWrapperTAccountSubject().eq(is_disabled, 0) ); // 2. 构建 parentId → 子科目列表 的 Map MapLong, ListTAccountSubject childrenMap allSubjects.stream() .filter(s - s.getParentId() ! null) // 过滤根节点parent_id 为 null .collect(Collectors.groupingBy(TAccountSubject::getParentId)); // 3. 找出根节点parent_id 为 null 或 0 ListTAccountSubject roots allSubjects.stream() .filter(s - s.getParentId() null || s.getParentId() 0) .sorted(Comparator.comparing(TAccountSubject::getCode)) .collect(Collectors.toList()); // 4. 递归设置 children roots.forEach(root - buildChildren(root, childrenMap)); return roots; } private void buildChildren(TAccountSubject parent, MapLong, ListTAccountSubject childrenMap) { ListTAccountSubject children childrenMap.get(parent.getId()); if (children ! null !children.isEmpty()) { children.sort(Comparator.comparing(TAccountSubject::getCode)); parent.setChildren(children); children.forEach(child - buildChildren(child, childrenMap)); } } }4.3 报表生成期初/期末/发生额的 SQL 聚合陷阱资产负债表、利润表依赖t_voucher_detail表按科目汇总。源码中常见错误 SQL-- ❌ 错误用 SUM(CASE WHEN ... THEN amount ELSE 0 END) 但未 GROUP BY 科目 SELECT subject_code, SUM(CASE WHEN debit_amount IS NOT NULL THEN debit_amount ELSE 0 END) AS debit_total, SUM(CASE WHEN credit_amount IS NOT NULL THEN credit_amount ELSE 0 END) AS credit_total FROM t_voucher_detail WHERE voucher_date BETWEEN 2024-01-01 AND 2024-12-31 GROUP BY subject_code; -- ✅ 必须有 GROUP BY更致命的是期初余额计算财务系统要求“期初余额 上期期末余额”。源码常写成-- ❌ 错误直接查上月最后一天凭证忽略未记账凭证 SELECT subject_code, SUM(debit_amount - credit_amount) as balance FROM t_voucher_detail WHERE voucher_date 2024-01-01 GROUP BY subject_code;正确逻辑期初余额应取t_account_balance余额表中balance_date 2023-12-31的数据。若无余额表则必须按会计期间闭账逻辑计算——即所有voucher_status posted已过账的凭证。血泪教训某次客户上线前测试发现利润表收入为 0查日志发现 SQL 中voucher_status draft草稿态而生产环境凭证默认是posted。根源是开发环境未模拟真实过账流程。5. 权限与安全加固从角色菜单到敏感操作二次验证5.1 RBAC 权限模型落地菜单与按钮级控制的双层校验财务系统权限必须细粒度。源码中sys_menu表通常含menu_id,parent_id,path,component,perms如voucher:add,report:export。前端控制src/router/index.js动态路由// 根据后端返回的 perms 列表过滤路由 const asyncRoutes routes.filter(route { if (route.meta route.meta.perms) { return store.getters.perms.some(perm route.meta.perms.includes(perm)) } return true })后端控制必须PreAuthorize(hasAuthority(voucher:delete))注解仅校验权限不校验数据归属。例如删除凭证时还需校验该凭证是否属于当前用户DeleteMapping(/vouchers/{id}) PreAuthorize(hasAuthority(voucher:delete)) public ResultVO deleteVoucher(PathVariable Long id) { TAccountVoucher voucher voucherMapper.selectById(id); if (voucher null) { return ResultVO.fail(凭证不存在); } // ✅ 二次校验凭证创建人是否为当前登录用户 if (!voucher.getCreateBy().equals(SecurityUtil.getCurrentUserId())) { return ResultVO.fail(无权删除他人凭证); } voucherMapper.deleteById(id); return ResultVO.success(); }5.2 敏感操作二次验证财务核心动作的防误触设计“删除凭证”、“结账”、“反结账”等操作必须二次确认。源码中常见简单弹窗this.$confirm(确认删除, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning }).then(() { // 执行删除 })但这是 UI 层防护后端必须做相同校验前端提交时带confirmCode如当前时间戳 MD5后端接口校验confirmCode是否在 60 秒内生成且未使用过记录操作日志谁、何时、删了哪条凭证PostMapping(/vouchers/{id}/delete-confirm) public ResultVO deleteConfirm(PathVariable Long id, RequestParam String confirmCode) { // 1. 校验 confirmCode 有效性Redis 缓存 时间戳 String cacheKey delete_confirm: id; String cachedCode redisTemplate.opsForValue().get(cacheKey); if (!Objects.equals(cachedCode, confirmCode)) { return ResultVO.fail(确认码无效); } redisTemplate.delete(cacheKey); // ✅ 用后即焚 // 2. 执行删除 voucherService.removeById(id); return ResultVO.success(); }5.3 数据脱敏与审计日志财务系统的合规底线财务数据严禁明文传输。源码中TUser实体类的password字段必须加TableField(select false)且toString()方法排除敏感字段。审计日志必备字段操作人 ID、操作时间、IP 地址、操作模块如“凭证管理”、操作类型新增/修改/删除、操作前数据快照、操作后数据快照。落地技巧用 Spring AOP 统一拦截Around(annotation(org.springframework.web.bind.annotation.PostMapping)) public Object logBeforeReturning(ProceedingJoinPoint joinPoint) throws Throwable { HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) .getRequest(); String ip getClientIp(request); // 获取被调用方法上的 LogAnnotation MethodSignature signature (MethodSignature) joinPoint.getSignature(); LogAnnotation annotation signature.getMethod().getAnnotation(LogAnnotation.class); if (annotation ! null) { AuditLog log new AuditLog(); log.setOperatorId(SecurityUtil.getCurrentUserId()); log.setIp(ip); log.setModule(annotation.module()); log.setOperation(annotation.operation()); log.setCreateTime(LocalDateTime.now()); // 记录参数JSON 序列化 log.setParams(JSON.toJSONString(joinPoint.getArgs())); auditLogMapper.insert(log); } return joinPoint.proceed(); }6. 进阶实战给现有系统增加“银行流水自动对账”功能财务人员每天要手工核对银行回单与凭证效率低易出错。客户临时提出需求“下周上线银行流水自动对账”。你只有 3 天时间不能重写系统必须基于现有代码扩展。以下是我在三个同类项目中验证过的最小可行路径。6.1 数据模型扩展新增 2 张表不动原有结构在finance_db.sql中追加-- 银行流水表客户上传 CSV 解析后存入 CREATE TABLE t_bank_statement ( id bigint NOT NULL AUTO_INCREMENT, bank_name varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 银行名称, account_no varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 银行账号, trade_date date DEFAULT NULL COMMENT 交易日期, amount decimal(18,2) DEFAULT NULL COMMENT 金额, remark varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 摘要, statement_no varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 流水号, status tinyint DEFAULT 0 COMMENT 0-未对账,1-已对账,2-已冲销, voucher_id bigint DEFAULT NULL COMMENT 关联凭证ID, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_account_date (account_no,trade_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; -- 对账明细表记录每笔流水与凭证的匹配关系 CREATE TABLE t_reconciliation_detail ( id bigint NOT NULL AUTO_INCREMENT, statement_id bigint NOT NULL, voucher_id bigint NOT NULL, match_type tinyint DEFAULT 1 COMMENT 1-金额日期,2-摘要关键词,3-人工指定, match_score decimal(5,2) DEFAULT 0.00 COMMENT 匹配得分0-100, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_statement_voucher (statement_id,voucher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;注意t_bank_statement.status字段复用现有系统状态码风格0/1/2避免新增枚举类t_reconciliation_detail.match_type为后续智能匹配留扩展空间。6.2 后端接口开发3 个核心接口复用现有技术栈接口 1上传银行流水 CSVPostMapping(/bank-statements/upload) public ResultVO uploadStatement(RequestParam(file) MultipartFile file) { try { // 1. 读取 CSV用 OpenCSV避免 Apache Commons CSV 的空行 bug ListBankStatementDTO statements CsvUtils.readBankStatement(file.getInputStream()); // 2. 批量插入MyBatis-Plus 的 saveBatch ListTBankStatement entities statements.stream() .map(dto - { TBankStatement bs new TBankStatement(); bs.setBankName(dto.getBankName()); bs.setAccountNo(dto.getAccountNo()); bs.setTradeDate(dto.getTradeDate()); bs.setAmount(dto.getAmount()); bs.setRemark(dto.getRemark()); bs.setStatementNo(dto.getStatementNo()); return bs; }).collect(Collectors.toList()); bankStatementService.saveBatch(entities); return ResultVO.success().data(count, entities.size()); } catch (Exception e) { log.error(上传银行流水失败, e); return ResultVO.fail(上传失败 e.getMessage()); } }接口 2执行自动对账核心算法对账逻辑分三层匹配匹配层级规则权重示例L1金额日期精确匹配amount相等且trade_date voucher_date100%流水 1000 元2024-05-20 → 凭证 1000 元2024-05-20L2金额模糊匹配摘要关键词ABS(amount - voucher_amount) 10且remark LIKE %工资%ANDvoucher_summary LIKE %工资%80%流水 9998.5 元摘要“工资”凭证摘要“员工工资发放”L3金额区间匹配amount在voucher_amount * 0.95 ~ voucher_amount * 1.05内60%流水 1050 元凭证 1000 元含手续费PostMapping(/bank-statements/reconcile) public ResultVO reconcile() { // 1. 查出所有未对账流水 ListTBankStatement unMatched bankStatementService.list( new QueryWrapperTBankStatement().eq(status, 0) ); // 2. 查出近30天未对账凭证借贷方含银行科目 ListTAccountVoucher vouchers voucherService.list( new QueryWrapperTAccountVoucher() .ge(voucher_date, LocalDate.now().minusDays(30)) .eq(status, posted) // 已过账凭证 .inSql(id, SELECT DISTINCT voucher_id FROM t_voucher_detail WHERE subject_code LIKE 1001% OR subject_code LIKE 1002%) ); // 3. 三层匹配伪代码实际用 Stream API 实现 for (TBankStatement bs : unMatched) { OptionalTAccountVoucher matched vouchers.stream() .filter(v - matchLevel1(bs, v)) // 金额日期 .findFirst(); if (matched.isPresent()) { // 创建对账明细更新流水状态 reconciliationDetailService.save(new TReconciliationDetail(...)); bankStatementService.update( new UpdateWrapperTBankStatement().eq(id, bs.getId()).set(status, 1).set(voucher_id, matched.get().getId()) ); } } return ResultVO.success(); }接口 3人工干预对账PostMapping(/reconciliation/manual-match) public ResultVO manualMatch(RequestBody ManualMatchDTO dto) { // dto 包含 statementId, voucherId, matchType // 1. 检查是否已存在对账记录 int count reconciliationDetailService.count( new QueryWrapperTReconciliationDetail() .eq(statement_id, dto.getStatementId()) .eq(voucher_id, dto.getVoucherId()) ); if (count 0) { return ResultVO.fail(已存在匹配关系); } // 2. 创建人工匹配记录 TReconciliationDetail detail new TReconciliationDetail(); detail.setStatementId(dto.getStatementId()); detail.setVoucherId(dto.getVoucherId()); detail.setMatchType(dto.getMatchType()); detail.setMatchScore(new BigDecimal(100)); // 人工指定为满分 reconciliationDetailService.save(detail); // 3. 更新流水状态 bankStatementService.update( new UpdateWrapperTBankStatement() .eq(id, dto.getStatementId()) .set(status, 1) .set(voucher_id, dto.getVoucherId()) ); return ResultVO.success(); }6.3 前端集成复用现有菜单与权限体系在src/router/index.js中新增路由{ path: /reconciliation, component: Layout, redirect: /reconciliation/list, name: Reconciliation, meta: { title: 银行对账, icon: money, perms: [reconciliation:list] }, children: [ { path: list, name: ReconciliationList, component: () import(/views/reconciliation/List), meta: { title: 对账列表, perms: [reconciliation:list] } }, { path: upload, name: ReconciliationUpload, component: () import(/views/reconciliation/Upload), meta: { title: 上传流水, perms: [reconciliation:upload] } } ] }对应sys_menu表插入INSERT INTO sys_menu (menu_name, parent_id, path, component, perms, order_num, status) VALUES (银行对账, 0, /reconciliation, Layout, reconciliation:list, 6, 0), (对账列表, 100, /reconciliation/list, reconciliation/List, reconciliation:list, 1, 0), (上传流水, 100, /reconciliation/upload, reconciliation/Upload, reconciliation:upload, 2, 0);关键技巧前端List.vue页面直接复用VoucherList.vue的表格组件只替换api调用地址和列定义statement_no,amount,trade_date,status节省 80% 开发时间。我做过最狠的一次客户周五下午提需求我周六上午拉出源码包下午完成数据库变更和后端接口周日早上交付前端页面周一上午培训财务人员使用。核心就三点不动原有架构、只增不改、用好 MyBatis-Plus 的 Wrapper 和 Stream API。那些说“老系统没法改”的人往往只是没找到杠杆支点。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站