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

SpringBoot+Vue财务管理系统:前后端分离实战与部署

SpringBoot+Vue财务管理系统:前后端分离实战与部署 ★ FEATURED ARTICLE
简介面向课程设计与毕业设计的SpringBootVue财务管理系统完整资源涵盖财务流水、采购、销售、资产等核心管理模块适合Java全栈学习者进行项目实战、二次开发或参考答辩。压缩包共450个文件约17.63MB主要包含123个Java后端源码、47个Vue前端组件、161个SVG图标及XML/JS/配置等文件按源码、文档、脚本分层组织目录辨识度高。已有780人学习下载反馈具参考价值。资源内除完整可运行的系统源码外还附专属部署说明、系统介绍与源码解释并含一键安装/运行/打包脚本及数据库初始化脚本可帮助快速搭建环境理解前后端分离架构下的业务实现与数据流转是一份理论与实践并重的课程设计或毕业设计参考资料。1. SpringBootVue财务管理系统课程设计和真实业务都能接住财务管理系统是Java Web开发里最经典的实战题材也是SpringBootVue前后端分离架构能拿得出手的完整练习样本。这套源码工程包含了用户登录、角色权限、会计科目、凭证录入、凭证审核、账簿查询和报表汇总的完整模块后端用SpringBoot 2.x MyBatis Plus MySQL前端用Vue 2 Element UI压缩包里还附带了部署说明、系统介绍和源码解释文档。适合两类人正在做课程设计或毕业设计、需要一套能跑通能讲清楚代码的学生以及想快速搭一个内部财务原型、需要参考前后端数据交互写法的小团队。这套项目不是点到即止的hello world而是把记账——凭证——账簿——报表这条财务主流程完整走了一遍。2. 前后端分离架构与数据权限先摸清模块边界再动手2.1 为什么选SpringBootVue而不是传统单体架构财务系统有个特点数据敏感、操作需要留痕但并发量并不高。用SpringBootVue前后端分离核心优势不在性能而在开发和维护时的边界清晰。后端只用RESTful接口暴露能力前端只负责页面渲染两边用JSON格式契约通信。这样课程设计答辩时你能清楚地说出「前端表单提交到哪个接口、后端在哪一层做校验、数据最终落到哪张表」而不是所有代码揉在JSP页面里。选这套方案的另一个实际理由是招聘市场和技术生态。SpringBoot是Java后端的事实标准Vue是国内中小团队使用率最高的前端框架之一。毕业设计用了这个组合简历上写「独立完成前后端分离的XX系统」是有说服力的面试官也更容易从项目切入提问。2.2 核心数据表设计科目、凭证、凭证明细的关联关系财务系统的数据模型不像电商那样有复杂的SKU关系它的核心是三张表科目表、凭证表、凭证明细表。这套资源里的数据库脚本位于压缩包的db目录下建表脚本可以直接导入MySQL。三张表的关系是这样的科目表存会计科目比如「1002 银行存款」是顶级科目下面挂「工商银行」「建设银行」这样的明细科目凭证表记录每一张凭证的编号、日期、附单据张数、制单人、审核状态凭证明细表是凭证的拆分内容一个凭证对应多条借/贷分录每条分录指定科目ID、摘要、借方金额、贷方金额。科目表的脚本大致长这样CREATE TABLE subject ( id bigint(20) NOT NULL AUTO_INCREMENT, subject_code varchar(20) NOT NULL COMMENT 科目编码如1002, subject_name varchar(50) NOT NULL COMMENT 科目名称, parent_id bigint(20) DEFAULT NULL COMMENT 父科目ID顶级科目为空, level tinyint(4) DEFAULT 1 COMMENT 科目层级 1/2/3, PRIMARY KEY (id), UNIQUE KEY uk_subject_code (subject_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表SQL里有几个关键设计parent_id自关联实现科目分级subject_code加唯一索引防止重复编码level字段方便前端按层级渲染树形控件。utf8mb4字符集是必须的因为科目名称和摘要里可能含生僻字或特殊符号utf8编码存不进去。凭证明细表的分录设计决定了财务借贷平衡的校验逻辑CREATE TABLE voucher_detail ( id bigint(20) NOT NULL AUTO_INCREMENT, voucher_id bigint(20) NOT NULL COMMENT 所属凭证ID, summary varchar(255) DEFAULT COMMENT 摘要, subject_id bigint(20) NOT NULL COMMENT 科目ID, debit_amount decimal(18,2) DEFAULT 0.00 COMMENT 借方金额, credit_amount decimal(18,2) DEFAULT 0.00 COMMENT 贷方金额, PRIMARY KEY (id), KEY idx_voucher_id (voucher_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;decimal(18,2)是财务金额的标准写法18位总长、2位小数最大支持到万亿级别课程设计完全够用。千万不要用float或double存金额浮点运算会产生精度误差这是财务系统的大忌。2.3 用户登录与JWT无状态认证的实现方式财务系统要求操作留痕所以权限模型比普通管理系统严格。这套系统的用户表里包含用户名、密码MD5加盐存储、真实姓名和角色字段角色分为制单员、审核员、记账员和管理员不同角色能看到的功能菜单不同。登录认证用的是JWT。前端登录成功后拿到token存到localStorageaxios拦截器在每个请求头自动带上Authorization: Bearer token字段。后端的SpringBoot过滤器统一解析token把当前用户信息放到ThreadLocal里后续Service层能随时拿到当前操作人这就实现了「谁做的凭证、谁审核的、什么时候操作」的审计追踪。前端axios封装是这套资源里很实用的一段代码拿过来改改就能用// api/http.js axios实例配置 import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 15000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use(response { const code response.data.code if (code 401) { localStorage.removeItem(token) router.push(/login) Message.error(登录状态已过期请重新登录) } return response })逻辑说明请求拦截器负责在发出请求前把token放入请求头响应拦截器统一处理401状态码token失效时自动清除本地存储并跳回登录页。baseURL从环境变量读取本地开发指向http://localhost:8080部署到服务器时直接改.env.production文件前端代码不用重新改。参数说明timeout设置15秒给慢SQL和网络波动留余量超过就报超时错误。process.env.VUE_APP_BASE_API是Vue CLI约定的环境变量命名规则必须以VUE_APP_开头才能被打包工具识别。2.4 项目目录结构拿到压缩包先看哪里压缩包解压后你会看到前端工程、后端工程、数据库脚本、部署说明文档和源码解释文档这几个部分。后端是标准的Maven工程结构controller层只做参数接收和结果返回service层写业务逻辑mapper层用MyBatis Plus操作数据库前端是Vue CLI标准工程api目录按模块封装请求方法views目录放页面组件router目录配置路由和菜单权限。第一次拿到源码我建议按这个顺序读先看部署说明文档把环境跑起来再用源码解释文档对照着看后端controller目录最后看前端的api和views目录。这样比从代码第一行开始看效率高很多也符合「先跑通再读码」的学习路径。3. 凭证到报表的核心链路接口设计、事务控制与余额计算3.1 凭证录入接口Controller接收、Service处理、Mapper落库凭证录入是财务系统最核心的操作。前端表单提交一张凭证包含凭证编号、日期、制单人、多条借/贷分录后端接口把这个JSON结构拆开先插凭证主表拿到自增ID再循环插入凭证明细表。后端Controller的写法// VoucherController.java RestController RequestMapping(/api/voucher) public class VoucherController { Autowired private VoucherService voucherService; PostMapping(/add) public Result addVoucher(RequestBody VoucherDTO voucher) { if (voucher.getVoucherNo() null || voucher.getDate() null) { return Result.error(凭证编号和日期不能为空); } if (voucher.getDetails() null || voucher.getDetails().isEmpty()) { return Result.error(凭证明细不能为空); } voucherService.saveVoucherWithDetails(voucher); return Result.success(凭证保存成功); } }逻辑说明RequestBody把前端传来的JSON自动绑定到VoucherDTO对象DTO里的details字段是一个ListVoucherDetailDTO对应前端表格里录入的多条分录。入参校验写在Controller层是合理的业务规则校验放Service层更合适。这里先做必填项校验再调用Service保存。代码里Result是统一返回体包含code、message、data三个字段前端axios响应拦截器也是按这个结构约定的。全项目所有接口都返回Result对象这是一个好习惯前端只需要处理一种返回格式。Service层的核心方法需要注意事务注解// VoucherServiceImpl.java Service public class VoucherServiceImpl implements VoucherService { Autowired private VoucherMapper voucherMapper; Autowired private VoucherDetailMapper voucherDetailMapper; Override Transactional(rollbackFor Exception.class) public void saveVoucherWithDetails(VoucherDTO dto) { // 1. 插入凭证主表 Voucher voucher new Voucher(); voucher.setVoucherNo(dto.getVoucherNo()); voucher.setVoucherDate(dto.getDate()); voucher.setStatus(0); // 0草稿 1已审核 2已记账 voucher.setCreator(dto.getCreator()); voucherMapper.insert(voucher); // 2. 循环插入明细分录 for (VoucherDetailDTO detail : dto.getDetails()) { VoucherDetail entity new VoucherDetail(); entity.setVoucherId(voucher.getId()); entity.setSubjectId(detail.getSubjectId()); entity.setSummary(detail.getSummary()); entity.setDebitAmount(detail.getDebitAmount()); entity.setCreditAmount(detail.getCreditAmount()); voucherDetailMapper.insert(entity); } // 3. 校验借贷平衡 BigDecimal totalDebit dto.getDetails().stream() .map(VoucherDetailDTO::getDebitAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal totalCredit dto.getDetails().stream() .map(VoucherDetailDTO::getCreditAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); if (totalDebit.compareTo(totalCredit) ! 0) { throw new RuntimeException(借贷不平衡保存失败); } } }逻辑说明Transactional(rollbackFor Exception.class)是事务控制的重点。第1步插主表、第2步插明细、第3步检查借贷平衡如果第3步抛出异常前两步的数据库操作都会回滚不会出现「只有凭证没有明细」的脏数据。参数说明status字段用整数枚举状态比字符串更省空间也更容易比较。compareTo比较两个BigDecimal是否相等为什么不用equals因为BigDecimal的equals会比较精度0.00和0会被判定为不相等而compareTo只比较数值大小。3.2 凭证审核与记账状态机防止跳过流程财务凭证有严格的流转状态草稿→已审核→已记账。审核通过后才能记账记账后凭证不能再修改。这套状态流转在Service层用条件判断来实现前端根据状态字段控制按钮的显示和禁用。审核接口的实现逻辑// VoucherServiceImpl.java 审核方法 Override Transactional(rollbackFor Exception.class) public void auditVoucher(Long voucherId, String auditor) { Voucher voucher voucherMapper.selectById(voucherId); if (voucher null) { throw new RuntimeException(凭证不存在); } if (voucher.getStatus() ! 0) { throw new RuntimeException(只有草稿状态的凭证才能审核); } voucher.setStatus(1); voucher.setAuditor(auditor); voucher.setAuditTime(new Date()); voucherMapper.updateById(voucher); }逻辑说明selectById先查凭证判断当前状态是否为草稿相当于一个简易状态机。审核人从JWT解析出来传入auditTime记录审核时间一张凭证从录入到审核的完整审计链就串起来了。跳状态的操作会被拒绝这就是财务系统「流程不可跳过」的实现方式。3.3 科目余额与报表汇总一条SQL搞定期间汇总账簿和报表是财务系统的输出端。教科书上有发生额余额表的概念指定会计期间内按科目汇总借方发生额、贷方发生额加上期初余额算出期末余额。这套系统用MyBatis Plus的注解SQL实现了这个查询核心是一条GROUP BY聚合语句-- VoucherDetailMapper.xml 按科目汇总期间发生额 SELECT vd.subject_id, s.subject_code, s.subject_name, SUM(vd.debit_amount) AS total_debit, SUM(vd.credit_amount) AS total_credit FROM voucher_detail vd INNER JOIN voucher v ON vd.voucher_id v.id INNER JOIN subject s ON vd.subject_id s.id WHERE v.voucher_date BETWEEN #{startDate} AND #{endDate} AND v.status 2 GROUP BY vd.subject_id, s.subject_code, s.subject_name ORDER BY s.subject_code;说明一下这条SQL的参数#{startDate}和#{endDate}是前端传入的查询区间status 2只统计已记账的凭证草稿和已审核未记账的数据不计入报表。GROUP BY按科目分组后SUM分别汇总各科目借/贷金额。期末余额的计算不在这条SQL里而是放在Java层期末余额 期初余额 借方发生额 - 贷方发生额资产类科目方向负债类则反过来。期初余额从科目表的opening_balance字段读取。前端展示这块的实现用的是Element UI的表格组件和日期范围选择器template div el-date-picker v-modeldateRange typedaterange value-formatyyyy-MM-dd range-separator至 start-placeholder开始日期 end-placeholder结束日期 /el-date-picker el-button typeprimary clickloadReport查询/el-button el-table :datareportList border el-table-column propsubjectCode label科目编码 width120 / el-table-column propsubjectName label科目名称 / el-table-column proptotalDebit label借方发生额 alignright / el-table-column proptotalCredit label贷方发生额 alignright / /el-table /div /templatevalue-formatyyyy-MM-dd保证传给后端的日期是字符串格式后端直接用String接收避免了日期序列化时区问题。el-table的alignright让金额列右对齐这是财务表格的显示习惯。3.4 前端路由与菜单权限菜单不是写死的这套系统前端的侧边栏菜单是根据用户角色动态生成的。router/index.js里先注册基础路由登录页、404页动态业务路由在登录成功后通过router.addRoutes()注入。实际代码处理逻辑后端登录接口返回用户信息和角色前端根据角色从路由配置表里过滤出有权限的路由再addRoutes动态添加。Vue Router 4.x和2.x的API略有不同这套工程用的是Vue 2 Vue Router 3写法是router.addRoutes()如果升级到Vue 3需要注意这个变化。菜单权限这块最容易翻车的地方是刷新页面后动态路由丢失解决方案是把路由信息存到Vuex里刷新时重新拉取用户信息和路由。4. 本地部署三步走环境配置、数据库初始化和前后端启动4.1 环境版本要求与推荐配置在动手部署前先确认本机环境版本匹配。这套SpringBootVue项目的版本组合比较常规推荐环境如下组件推荐版本备注JDK1.8 或 11SpringBoot 2.x 对这两个版本支持最好Maven3.6后端依赖管理MySQL5.7 或 8.0MySQL 8 需注意驱动类名变化Node.js14.x ~ 16.xVue 2 Vue CLI 4/5 的兼容区间npm6.x ~ 8.xNode自带版本需匹配Java版本这里多提一句如果你装的是JDK 17SpringBoot 2.6以下的版本跑起来会报错或出警告这是Spring框架对新JDK版本支持滞后导致的。如果你遇到「SpringBoot版本太高」的问题优先检查本机JDK版本而不是代码写错了。4.2 数据库初始化导入SQL脚本解压压缩包后在db目录下找到初始化脚本通常是finance.sql用命令行导入# 连接MySQL并创建数据库 mysql -u root -p -e CREATE DATABASE finance DEFAULT CHARACTER SET utf8mb4; # 导入表结构和初始数据 mysql -u root -p finance db/finance.sql逻辑说明第一步创建finance数据库并指定utf8mb4字符集第二步把SQL脚本导入。导入完成后可以用SHOW TABLES;验证表是否建成功。常见情况是SQL脚本里有外键或存储过程不同MySQL版本执行情况不太一样。MySQL 8执行时如果报Unknown collation或Invalid default value通常是脚本的字符集声明和你本机MySQL版本不兼容在导入前用文本编辑器打开脚本把CHARSETutf8mb4统一检查一遍把DEFAULT CHARSET相关的语法改成8.0兼容的写法或者直接在脚本顶部加一行SET NAMES utf8mb4;。4.3 后端配置修改与启动后端配置文件在src/main/resources/application.yml需要修改的关键项是数据库连接和端口# src/main/resources/application.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/finance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true注意driver-class-name这项MySQL 5.7用com.mysql.jdbc.DriverMySQL 8.0必须改成com.mysql.cj.jdbc.Driver写错启动直接报ClassNotFoundException。serverTimezoneAsia/Shanghai必须加上否则MySQL 8连接时区报错。修改完成后在项目根目录执行打包和启动命令# Maven打包跳过测试避免测试用例影响 mvn clean package -DskipTests # 启动JAR java -jar target/finance-0.0.1-SNAPSHOT.jar启动成功后控制台会出现Started FinanceApplication in xx seconds同时Tomcat默认端口8080开始监听。看到这句不代表系统完全正常还要用浏览器验证接口是否真的通了。4.4 前端依赖安装与启动前端是Vue CLI工程在frontend目录或解压后的web目录下执行# 安装依赖 npm install # 启动开发服务器 npm run servenpm install的执行时间取决于网络状况如果卡住不动换成国内镜像源再试npm config set registry https://registry.npmmirror.com npm install启动成功后终端会显示App running at: http://localhost:8081。这里要注意Vue CLI默认端口是8081和后端8080不同所以必须通过代理或跨域配置访问后端接口。这套工程的开发环境代理配置在vue.config.js里// vue.config.js module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }逻辑说明port: 8081指定前端开发服务器端口proxy把/api开头的请求转发到后端8080changeOrigin: true让后端收到的请求头Host变成localhost:8080。配置好代理后前端代码里的接口请求路径保持/api/xxx即可不需要写完整的http://localhost:8080/api/xxx。这个代理只对开发环境生效生产环境打包后需要nginx转发或后端统一部署。前端启动完成后浏览器访问http://localhost:8081系统默认的管理员账号密码在部署说明文档里有写通常是admin / admin123。登录后能看到凭证管理、账簿查询、报表分析等菜单说明前后端联调成功。4.5 生产环境打包与部署可选如果想打包部署到Linux服务器前端需要执行构建命令npm run build构建产物在dist目录里面是纯静态文件。常见做法是部署时把dist目录里的文件拷贝到SpringBoot的src/main/resources/static目录下再重新打包后端JAR这样前端页面由SpringBoot统一提供服务浏览器直接访问8080端口就能打开系统。「vue打包放进springboot中」这个操作我实际做过。注意一个细节打包前需要确认前端请求路径用的是相对路径还是绝对路径Vue CLI默认的publicPath是/如果前端资源路径写死部署后刷新页面会404。改成相对路径的方式是在.env.production文件里设置BASE_URL./或者在vue.config.js里配置publicPath: ./。另外刷新页面404的另一个原因是Vue Router用了history模式部署到服务器需要用nginx配置try_files回退到index.html否则刷新就白屏。开发时用hash模式就完全没这个问题。5. 运行排错实战五个最容易翻车的坑与处理5.1 端口占用导致后端启动失败现象执行java -jar启动命令后控制台报Port 8080 was already in useJAR包进程随即退出系统访问不了。原因本机已有其他程序占用了8080端口。常见占用的程序包括其他Java jar包、Tomcat服务、IDEA内置的嵌入式服务也可能是上一次启动没完全退出残留的进程。解决先用命令找出占用8080的进程再按需处理。# 查看占用8080的进程PID netstat -ano | findstr 8080 # Linux系统用lsof -i :8080 # 结束指定PIDWindows taskkill /PID 你的进程号 /F # Linux系统用kill -9 你的进程号杀掉占用进程后重新启动JAR即可。如果8080端口被系统保留端口占用或者你就是不想动已有的程序更快的办法是改SpringBoot的端口在application.yml里把server.port改成8082同时检查前端vue.config.js的代理target是否同步修改。5.2 前端页面能打开但接口全部报错跨域请求被拦截现象前端npm run serve启动正常登录页面能显示但点登录时报Access to XMLHttpRequest at http://localhost:8080/api/login from origin http://localhost:8081 has been blocked by CORS policy。原因浏览器同源策略拦截了跨域请求。前端8081的页面请求后端8080的接口属于跨域后端没有返回CORS响应头浏览器就把响应拦截了。解决后端加一个CORS配置类允许前端开发服务器的地址跨域访问。上面第2章提到的WebConfig实现的就是这个这里把完整配置贴一遍// config/WebConfig.java Configuration public class WebConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOrigins(http://localhost:8081) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }说明一下参数addMapping(/api/**)限定只允许/api路径跨域其他路径不受影响allowedOrigins填写前端的完整地址如果前端端口变了要同步改maxAge(3600)是预检请求OPTIONS的缓存时间设太大可能影响联调时的即时调试课程设计阶段3600秒足够。配置好后重新启动后端再刷新前端页面测试跨域报错就会消失。5.3 MySQL驱动类找不到或连接超时现象后端启动时控制台报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者Access denied for user rootlocalhost。原因分两种第一种是pom.xml里的MySQL依赖版本和配置文件的驱动类名不匹配。MySQL 8的驱动类名是com.mysql.cj.jdbc.DriverMySQL 5的驱动类名是com.mysql.jdbc.Driver两者写反必报错。第二种是连接信息错了用户名或密码不对、数据库名拼错、MySQL服务没启动。解决先检查pom.xml里的mysql-connector-java依赖版本dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency依赖版本是8.x时application.yml里的driver-class-name必须是com.mysql.cj.jdbc.Driver。然后确认MySQL服务已启动# 验证MySQL连接 mysql -u root -p -e SELECT 1;如果这条命令能正常返回1说明数据库服务正常问题出在SpringBoot配置文件。把url、username、password三项逐字检查一遍特别注意url里的database名称是否和CREATE DATABASE创建的名称一致。5.4 vue项目源码发给别人后npm install报错跑不起来现象把前端工程压缩发给同学后对方执行npm install报了一堆ERESOLVE或node-gyp错误甚至装完还是启动不了。原因前端工程里的package.json锁定的依赖版本和你发货时本机Node版本、npm版本不匹配。发送时没删掉node_modules目录对方也装了但版本差异导致依赖树解析失败。这是「vue项目源码怎么发给别人」最常见的坑。解决发送源码前删掉本机的node_modules目录和package-lock.json文件这两个文件不需要发。对方收到后先确认Node版本再执行安装# 删除本地依赖发送前执行 rm -rf node_modules package-lock.json # 对方收到后安装依赖 npm install如果对方Node版本过高比如Node 18配老工程npm install报ERESOLVE unable to resolve dependency tree加一个--legacy-peer-deps参数跳过依赖冲突检查npm install --legacy-peer-deps这个参数是npm 7之后处理历史遗留依赖冲突的通用解法。Vue 2 Element UI这类老工程的依赖树通常是为npm 6设计的用新npm装就会遇到这个问题。5.5 打包部署后前端刷新404静态资源找不到现象前端npm run build生成dist目录把项目运行起来后首页能打开但刷新页面或直接访问某个路由路径时报404控制台还有资源加载失败。原因这是Vue Router的history模式和静态资源路径共同导致的。history模式下路由路径是真实的URL比如/voucher/list服务器在收到这个路径的GET请求时找不到对应的物理文件就返回404如果publicPath配置不对CSS和JS会从错误路径加载。解决有两个处理办法。一是改路由模式为hash不需要任何服务器配置// router/index.js const router new VueRouter({ mode: hash, // 默认就是hash显式写出来 routes })hash模式下URL变成/#/voucher/list用#分隔路径服务器只认#前面的部分永远不会404。二是保持history模式在nginx配置里加try_fileslocation / { try_files $uri $uri/ /index.html; }这段配置的意思是请求路径匹配不到物理文件时回退到index.html由前端路由接管。两种方案里课程设计演示推荐hash模式省心、无环境依赖如果确实需要干净的URL再上history加nginx配置。6. 答辩验证技巧用一份API文档和一张演示清单收尾课程设计答辩或者向同事交付时最尴尬的情况不是代码有bug而是被问到「某个功能的实现逻辑」时不知道从哪个类开始讲。我拿到这套资源后的做法是花半小时把后端controller目录下的所有接口按模块整理一份接口清单包括请求路径、请求方法、入参字段、出参结构然后对照前端页面把每个按钮对应的接口标出来。这份接口清单的格式很简单用Markdown表格就能搞定模块请求路径方法功能对应页面登录/api/loginPOST用户登录登录页凭证/api/voucher/addPOST保存凭证凭证录入页凭证/api/voucher/auditPUT审核凭证凭证列表页凭证/api/voucher/listGET凭证分页查询凭证列表页报表/api/report/summaryGET发生额汇总报表页演示前用这张清单走一遍完整流程登录→录入一张借贷平衡的凭证→审核→在报表页查询到对应金额。这个闭环走通整个系统的主干功能就验证完了。录入凭证时特意录一张借贷不平衡的数据展示系统怎么拒绝比只演示正常流程更有说服力。验证接口时不需要打开前端页面用Postman或curl直接打接口更快# 登录获取token curl -X POST http://localhost:8080/api/login \ -H Content-Type: application/json \ -d {username:admin,password:admin123}拿到返回的token后调用业务接口时在请求头带上Authorization: Bearer token。通过接口文档加curl验证的方式能把「会写代码」和「能讲清楚系统」之间的差距补上。这套资源里的源码解释文档实际上就是告诉你每个核心方法做了什么我把它当作调试时的「后悔药」——改乱了代码对照文档就能回溯原始逻辑。从那以后我把每个接手的项目都强制整理一份接口清单遇见新项目不再一头扎进代码里先看接口再翻实现希望这个习惯帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站