☰
JavaWeb金融借贷系统毕业设计:Servlet+JSP+MySQL实现等额本息还款
2026/10/8 19:34:47 网站建设 项目流程

简介:一套基于JavaWeb实现的金融借贷系统(P2P金融管理/小额贷款系统)完整毕业设计资源,面向计算机相关专业毕设学生及需要项目实战的Java学习者。资源包含可运行的项目源码、项目文档、数据库脚本及辅助软件工具,系统采用Servlet、JDBC、FileUpload作为后台框架,结合Bootstrap、jQuery、Ajax构建界面,数据库使用MySQL,覆盖融资产品查询与申请、每日新闻、后台贷款申请管理、产品类型管理、贷款周期管理、新闻管理、企业管理等模块,功能完整、界面简洁,适合用于毕设展示或项目练习。压缩包约14.67MB,共15个文件,主要包含源码压缩包(p2p.zip)、MySQL数据库脚本(.sql)、项目文档(.pdf/.md)、运行截图(.png)以及软件工具下载说明(.txt),从源码到部署说明一应俱全。目前已有2536人学习/下载,资源经过严格调试,确保可直接运行。附带文档和截图便于快速了解系统结构,按目录整理清晰,可节省配置与查阅时间。

1. 金融借贷系统不是“借钱”那么简单:这门毕设到底在做什么

金融借贷系统是最经典的 JavaWeb 毕设题之一,但很多人把它的难点想反了——以为页面多、表格漂亮就能拿高分,真正卡住答辩的往往是那套“还钱”的数据逻辑。借一笔钱出去只需要一条 INSERT,把钱按等额本息分 12 期收回来,中间涉及审核状态流转、还款计划生成、逾期判定,这些才是整套系统的核心。这篇笔记从技术选型、数据库设计、核心流程代码到避坑清单,按我实际做这类项目的顺序讲清楚。适合正在做 JavaWeb 毕设、想用最短时间拿到一套能跑通全流程并讲明白源码的同学,也适合想复习 Servlet + JSP + MySQL 全链路开发的从业者。

2. 技术选型与项目骨架:为什么 JavaWeb 仍是毕设最稳的答案

2.1 技术栈分工:Servlet 管流程、JSP 管页面、MySQL 管账目

“JavaWeb”这个词在不同学校有不同界定。有的要求纯 Servlet + JSP + JDBC,有的允许用 SSM(Spring + SpringMVC + MyBatis),少数直接默认 SpringBoot。我在做这类毕设时一般先问清楚题目出处:标题写的是“基于 JavaWeb”,没有带框架名,那最稳妥的形态就是 Servlet 处理请求、JSP 渲染页面、MySQL 存账目数据,JDBC 做持久化。

这套组合的优点是每个环节都是答辩现场能说清原理的:请求怎么进来、Servlet 怎么分发、数据库连接怎么管理。用 SpringBoot 虽然开发快,但很多答辩老师会追问“那你讲讲 DispatcherServlet 和普通 Servlet 的区别”,反而容易露馅。如果你有富余时间,可以在核心用 Servlet 的前提下,把连接池换成 Druid、把 DAO 层做个简单封装,这些属于加分项,不影响主架构。

2.2 从零搭建项目骨架:IDEA 建工程与包结构划分

我习惯在 IDEA 里用普通 Java Enterprise 工程而不是 Maven 骨架,原因是纯 Servlet 项目不需要拉一堆依赖,手写 lib 目录放一个 mysql-connector-java 和一个 druid 就够。新建工程后选择 Web Application,勾选 Create web.xml,然后把包结构按职责切好,这样后面写代码时不用回头重构:

src/main/java ├─ com.loan.servlet # Servlet 层,接收请求、转发视图 ├─ com.loan.service # 业务层,审核、还款计划生成等核心逻辑 ├─ com.loan.dao # DAO 层,封装 JDBC 操作 ├─ com.loan.entity # 实体类,对应数据库表 └─ com.loan.util # DBUtil、DateUtil、BigDecimalUtil 等工具 webapp ├─ admin/ # 管理端 JSP:借款审核、放款管理 ├─ user/ # 用户端 JSP:借款申请、还款列表 └─ WEB-INF/web.xml

这个分层照着做就行,Servlet 只做参数接收和页面跳转,金额计算、状态判断必须放到 service 里,别写在 Servlet 中。我用过一个血泪教训:把等额本息算法直接写在审核 Servlet 里,后来要支持两种还款方式时不得不重写整个方法。分层不是应付检查用的,是给自己留后路。

2.3 连接数据库:JDBC 工具类与连接池参数调优

JavaWeb 连 MySQL 的常规做法是写一个 DBUtil,把驱动加载、连接获取、资源关闭统一收口。下面这个版本没有依赖框架,直接复制到 util 包下改数据库名和密码就能用:

public class DBUtil { private static DruidDataSource dataSource; static { try { dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/loan_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); dataSource.setMaxActive(20); dataSource.setMaxWait(60000); dataSource.setValidationQuery("SELECT 1"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(AutoCloseable... resources) { for (AutoCloseable r : resources) { if (r != null) { try { r.close(); } catch (Exception ignored) {} } } } }

连接池参数里,initialSize设为 5 是启动时预热连接,maxActive20 对应测试环境够用,如果并发再大一些可以调到 50,但要注意 MySQL 默认的max_connections是 151,超了会报 Too many connections。maxWait单位是毫秒,60000 表示 60 秒内拿不到连接就抛异常,避免系统在数据库挂了时无限卡死。validationQuery会在取连接时做探活,MySQL 用SELECT 1开销最小,别写SELECT NOW()这种多余查询。

注意 URL 里我用了serverTimezone=Asia/Shanghai,这是新版驱动必须带的参数,否则 8.0 驱动会直接报The server time zone value的运行时错误。characterEncoding=utf8是为了让插入的中文不乱码,这个我在第 5 章避坑清单里还会展开讲。

3. 数据库设计是这套系统的“命根子”:五张表把账算明白

3.1 表结构拆解:从用户到还款计划,一条借款的生命周期

金融借贷系统听名字像是贷款产品做的事,落到数据库层面其实是一条借款申请从提交到结清的生命周期。我见过不少同学上来就设计十几张表,权限表、日志表、配置表全堆上,最后写代码时自己都连不清外键关系。毕设题目没必要这么做,把下面 5 张核心表设计清楚,业务逻辑就撑得住:

用户表存借款人和管理员,用角色字段区分;借款表存每一笔申请的金额、期限、利率、状态;还款计划表在审核通过时按期数批量生成,每期一条记录,记录本金、利息、到期日和状态;还款记录表用户每还一笔就写一条流水。外加一张简单的管理员操作表可选,不做也不影响流程。关键是把“借款”和“还款计划”分开——它们是 1 对 N 的关系,很多人混在一张表里,导致后续做部分提前还款时完全没法处理。

状态字段建议用字符串存代码而不是数字,比如WAIT_AUDIT、AUDIT_PASS、REPAYING、FINISH,这样写代码和调试时一眼能看懂,SQL 排查也方便。数字枚举看起来省空间,但对毕设项目来说可读性远比那一个字节重要。

3.2 建表 SQL 落地:金额字段为什么必须用 DECIMAL

下面是核心表的建表脚本,直接在 Navicat 或命令行里执行即可:

CREATE TABLE `user` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `username` VARCHAR(32) NOT NULL UNIQUE, `password` VARCHAR(64) NOT NULL, `salt` VARCHAR(16) NOT NULL, `role` VARCHAR(10) DEFAULT 'USER', `credit_limit` DECIMAL(12,2) DEFAULT 10000.00, `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `loan` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `user_id` BIGINT NOT NULL, `amount` DECIMAL(12,2) NOT NULL, `annual_rate` DECIMAL(5,2) NOT NULL, `term_months` INT NOT NULL, `purpose` VARCHAR(255) DEFAULT '', `status` VARCHAR(20) DEFAULT 'WAIT_AUDIT', `apply_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `audit_time` DATETIME DEFAULT NULL, `audit_user_id` BIGINT DEFAULT NULL, `reject_reason` VARCHAR(255) DEFAULT NULL, KEY `idx_user_id` (`user_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `repay_plan` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `loan_id` BIGINT NOT NULL, `period_num` INT NOT NULL, `due_date` DATE NOT NULL, `principal` DECIMAL(12,2) NOT NULL, `interest` DECIMAL(12,2) NOT NULL, `total` DECIMAL(12,2) NOT NULL, `status` VARCHAR(20) DEFAULT 'UNPAID', `pay_time` DATETIME DEFAULT NULL, KEY `idx_loan_id` (`loan_id`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `repay_record` ( `id` BIGINT AUTO_INCREMENT PRIMARY KEY, `loan_id` BIGINT NOT NULL, `plan_id` BIGINT NOT NULL, `pay_amount` DECIMAL(12,2) NOT NULL, `pay_time` DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

所有金额字段都用DECIMAL(12,2),这是金融项目里必须养成的习惯。DECIMAL(12,2)表示整数部分最多 10 位、小数 2 位,最大支持到 99 亿的金额,对毕设和大多数业务场景都够。如果用 double 或者 float,算等额本息时会出现 0.1 + 0.2 不等于 0.3 的精度问题,这在第 5 章会专门讲。

利率字段我用annual_rate存年利率,比如年化 12% 存12.00,计算月利率时再除以 12。不要直接存月利率,因为还款计划生成、逾期利息计算都依赖年利率这个原始值,存派生数据会让自己后面绕坑。索引上给 loan 表和 repay_plan 表都建了状态索引,因为管理端查询“待审核列表”“未还款列表”是最高频的 SQL。

3.3 状态字段设计:让审核和还款可追溯

借款表的status字段完整取值建议为:WAIT_AUDIT待审核、AUDIT_PASS审核通过、AUDIT_REJECT审核拒绝、REPAYING还款中、FINISH已结清、OVERDUE已逾期。还款计划表的状态则简化成UNPAID未还、PAID已还、OVERDUE逾期。两个表的状态需要联动:所有计划都还完,借款表状态从REPAYING改成FINISH;某一期计划超期未还,借款表状态改为OVERDUE。

这种设计能让业务逻辑非常直白:管理端审核列表查status = 'WAIT_AUDIT',用户端还款列表查plan.status = 'UNPAID',不需要任何复杂的 join 条件。设计状态字段时唯一要注意的是别用1、2、3这样的数字代码,因为当你写loan.getStatus() == 3时,三个月后自己都想不起来 3 代表什么。字符串状态虽然存储多几个字节,换来的是代码可读性和排错效率,值了。

4. 核心流程代码实现:借款、审核、还款计划生成

4.1 注册与登录:Session 管理和密码处理

注册登录是每套系统的入口,但借贷系统的密码处理不能省。明文存密码在答辩现场被老师打开数据库看一眼就崩了,所以必须加盐哈希。下面的代码用在注册时生成密码和盐值:

public class PasswordUtil { public static String generateSalt() { return UUID.randomUUID().toString().replace("-", "").substring(0, 8); } public static String encrypt(String password, String salt) { String input = salt + password; for (int i = 0; i < 10; i++) { input = DigestUtils.md5Hex(input + salt); } return input; } }

逻辑说明:先随机生成 8 位盐值,然后把盐值和密码拼接做 10 轮 MD5。每轮迭代都把上一轮的输出再次拼盐,这样即使两个用户密码相同,生成的密文也不同。DigestUtils.md5Hex来自 commons-codec,如果没有这个依赖,可以用MessageDigest手动实现,逻辑是一样的。登录时流程是:按用户名查出 salt → 用输入的密码和库里的 salt 调用 encrypt → 比对结果。Session 里只存userId和role,不要存密码,页面展示用户信息时单独查库。

4.2 借款申请提交:金额、期限、利率的前置校验

借款申请这一步看着只是插入一条 loan 记录,实际要做的校验远比想象多。典型的校验链是:用户是否存在、借款金额是否大于 0、是否超过该用户的授信额度、期限是否在允许范围内。校验失败时返回错误信息到申请页面,成功后才插入草稿状态。下面是 Service 层的一段代码:

public Result applyLoan(Long userId, BigDecimal amount, Integer termMonths) { User user = userDao.findById(userId); if (user == null) return Result.error("用户不存在"); // 校验金额正数 if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { return Result.error("借款金额必须大于0"); } // 校验授信额度 BigDecimal remainingLimit = user.getCreditLimit() .subtract(loanDao.sumActiveAmountByUserId(userId)); if (amount.compareTo(remainingLimit) > 0) { return Result.error("超出授信额度,当前剩余可借:" + remainingLimit); } Loan loan = new Loan(); loan.setUserId(userId); loan.setAmount(amount); loan.setAnnualRate(new BigDecimal("12.00")); // 由系统配置 loan.setTermMonths(termMonths); loan.setStatus("WAIT_AUDIT"); loanDao.insert(loan); return Result.ok("申请成功,请等待审核"); }

关键点有两个。第一,金额比较用的是compareTo而不是>,因为 BigDecimal 的equals会比较精度,new BigDecimal("10.0")和new BigDecimal("10.00")用equals不等,而compareTo只看数值,这是金融计算里必须记住的细节。第二,sumActiveAmountByUserId统计的是该用户当前处于待审核和还款中状态的借款总额,防止一个人提交多笔申请把额度套完,如果忽略这点,额度校验就形同虚设了。

4.3 还款计划生成:等额本息的算法与实现

等额本息是毕设中最常被要求实现的还款方式,答辩老师必问,所以这段代码要能背也能写。公式是:每期还款额 = 本金 × 月利率 × (1 + 月利率)^期数 / ((1 + 月利率)^期数 - 1),每期利息 = 剩余本金 × 月利率,每期本金 = 每期还款额 - 当期利息。

public List<RepayPlan> generatePlan(Loan loan) { BigDecimal amount = loan.getAmount(); int months = loan.getTermMonths(); BigDecimal monthlyRate = loan.getAnnualRate() .divide(new BigDecimal("1200"), 8, RoundingMode.HALF_UP); // 计算每期还款额:用 double 算幂,再转 BigDecimal BigDecimal factor = BigDecimal.valueOf( Math.pow(1 + monthlyRate.doubleValue(), months)); BigDecimal monthPay = amount.multiply(monthlyRate).multiply(factor) .divide(factor.subtract(BigDecimal.ONE), 2, RoundingMode.HALF_UP); BigDecimal remaining = amount; List<RepayPlan> list = new ArrayList<>(); LocalDate dueDate = LocalDate.now().plusMonths(1); for (int i = 1; i <= months; i++) { BigDecimal interest = remaining.multiply(monthlyRate) .setScale(2, RoundingMode.HALF_UP); BigDecimal principal = monthPay.subtract(interest); if (i == months) { // 最后一期做平差,避免累计误差 principal = remaining; monthPay = principal.add(interest); } RepayPlan plan = new RepayPlan(); plan.setPeriodNum(i); plan.setDueDate(dueDate); plan.setPrincipal(principal); plan.setInterest(interest); plan.setTotal(monthPay); list.add(plan); remaining = remaining.subtract(principal); dueDate = dueDate.plusMonths(1); } return list; }

这段代码里有两个必须讲清楚的细节。第一,monthlyRate的除法用divide时指定了 8 位小数和HALF_UP舍入,因为 BigDecimal 除法不指定精度会直接抛ArithmeticException,这是初学者最常见的翻车点。第二,最后一期做了“平差”处理:由于每期利息按剩余本金重新计算,最后一期的本金直接取剩余金额。如果不做这一步,前面每期舍入误差累计起来,最后一期会多出几分钱,别人对账时会发现计划和实际还款差一分,非常尴尬。

日期计算用的是java.time.LocalDate,它的plusMonths能正确处理跨年和大小月。比如还款日是每月 15 号,1 月 15 日申请的借款,第一期 2 月 15 日、第二期 3 月 15 日,依次推下去,不需要手工处理闰年。

4.4 管理端审核:事务保证数据一致性

审核是整个系统里最容易出数据一致性问题的环节,因为它要做两件事:把 loan 状态从待审核改成已通过,同时批量生成还款计划。这两步必须在一个事务里完成,否则会出现贷款已经是“通过”状态但还款计划表是空的,用户压根不知道怎么还钱。

public void auditLoan(Long loanId, Long adminId, boolean pass) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); Loan loan = loanDao.findById(conn, loanId); if (loan == null) throw new RuntimeException("借款记录不存在"); if (!"WAIT_AUDIT".equals(loan.getStatus())) { throw new RuntimeException("该借款已处理,请勿重复审核"); } if (pass) { loanDao.updateStatus(conn, loanId, "REPAYING", adminId); List<RepayPlan> plans = repayPlanService.generatePlan(loan); repayPlanDao.batchInsert(conn, plans); } else { loanDao.updateStatus(conn, loanId, "AUDIT_REJECT", adminId); } conn.commit(); } catch (Exception e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ignored) {} } throw new RuntimeException("审核失败:" + e.getMessage(), e); } finally { DBUtil.close(conn); } }

注意这里 DAO 层的每个方法都传了Connection参数,这是让多个 SQL 共享同一个事务的关键。很多初学者会写conn.setAutoCommit(false)之后又去调用 DAO,但 DAO 内部自己getConnection()拿到了另一条连接,导致事务控制完全失效。代码里还做了一次状态校验,防止管理端在页面里双击“通过”按钮时被并发请求重复处理——虽然 Servlet 单线程处理每个请求,但在真实项目里这是很常见的隐患。审核拒绝时不生成计划,只改状态,简单直接。

5. 毕设级避坑清单:运行、调试与演示中的五个血泪教训

5.1 金额精度翻车:double 算账差一分钱的正确解法

现象:还款计划每期金额单独看都对,手动加一遍总额比借款本金多出 0.01 元;或者用 double 算出19.999999这种奇葩数字。

原因:计算机里的 double 是二进制浮点数,0.1 在二进制里是无限循环小数,参与运算必然产生误差。金融计算不能用 double,这是硬性规则。

解决:所有金额字段用BigDecimal,数据库用DECIMAL(12,2),除法必须显式指定小数位和舍入模式。我一般统一用RoundingMode.HALF_UP,也就是四舍五入,符合还款场景的直觉。注意从 double 转 BigDecimal 时用BigDecimal.valueOf(19.99)而不是new BigDecimal(19.99),后者的构造方式会把 double 的二进制误差也带进来。

5.2 事务没提交:审核通过但还款计划没生成

现象:管理端看到借款状态已是“还款中”,但用户端还款列表为空;直接在数据库里查 repay_plan 表,一条记录都没有。

原因:最常见的是 DAO 方法内部自己通过DBUtil.getConnection()获取了新的连接,业务层的conn.setAutoCommit(false)作用在另外一条连接上,commit 时链路上没有任何 SQL 参与事务。另一种情况是生成还款计划的方法抛了异常,但 catch 块里没有 rollback,异常之后连接被返回连接池,状态已改的部分被连接池误提交。

解决:统一让 Service 层创建事务连接,并作为参数传入 DAO 方法。每条连接在 finally 里关闭并归还连接池。异常分支一定要rollback(),哪怕你觉得“SHA,这个异常不会发生”,也要写,事务代码没有后悔药。

5.3 日期计算踩坑:跨月还款日与闰年

现象:借款申请日是 1 月 31 日,第一期还款日成了 2 月 29 日或 3 月 3 日,而不是 2 月 28 日;用Calendar.add(Calendar.MONTH, 1)时还遇到过年份不跳、直接出现 13 月的情况。

原因:老版Calendar对月末日期的处理逻辑是“如果目标月份没有这一天,则顺延到下个月的对应日”,所以 1 月 31 日加一个月会变成 2 月 28 日或 3 月 3 日,不同 JDK 版本行为还不一致。

解决:直接用java.time.LocalDate,plusMonths(1)的规则虽然是“映射到目标月最后一天”,但配合每月固定还款日(建议申请日+天然月份)可以避免歧义。最省心做法是还款日统一取“下一个月的同一天”,如果申请日在月末则明确显示为“当月最后一天”。毕设里一定要把日期工具类抽出来,别散落在业务代码里。

5.4 IDEA 运行 JavaWeb 项目配置:Tomcat 版本不匹配导致项目起不来

现象:启动 Tomcat 时报UnsupportedClassVersionError,或者项目部署后所有请求都 404,控制台也没有明显报错;有时是 JSP 页面打开乱码或报 500。

原因:IDEA 的 Web 项目跑不起来,八成问题不在代码而在运行环境。常见三种情况:Tomcat 版本和 JDK 版本不匹配(比如 Tomcat 10 默认要求 JDK 11,但学校机器装的是 JDK8);项目的 Artifacts 没有配置 lib 依赖,mysql 驱动和 druid jar 没被打进 WEB-INF/lib;或者部署时选了 war 包模式而不是 war exploded,导致改动 JSP 还要重新打包。

解决:毕设别追新版本,Tomcat 8.5 + JDK8 是最稳的组合。IDEA 里打开 Project Structure → Artifacts,确认 Output Layout 里有WEB-INF/lib且包含项目的所有 jar 包;Run Configuration 里 Deployment 选择war exploded,这样改 JSP 刷新即生效。启动后先访问最简单的 index.jsp,通了再试登录,缩小排查范围。

5.5 中文乱码:编码问题在三个环节逐个排查

现象:JSP 页面中文显示正常,但插入数据库后变成???;或者表单提交的中文到 Servlet 里读出来是乱码。

原因:编码问题会在三个环节分别断掉——页面渲染、HTTP 传输、数据库存储。页面用的是 GBK,Tomcat 解析 POST 请求用的是 ISO-8859-1,数据库表是 utf8,任何一环不一致都会出乱码。

解决:第一个环节,JSP 文件头部统一用<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;第二个环节,在 web.xml 里配置编码过滤器,让所有请求都经过 UTF-8 解码;第三个环节,建表统一utf8mb4,连接 URL 加characterEncoding=utf8。三个环节都统一了之后,乱码基本绝迹。下面是最常用的过滤器配置:

<filter> <filter-name>encodingFilter</filter-name> <filter-class>com.loan.util.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

过滤器类里只需要在doFilter中先设置request.setCharacterEncoding(encoding),再调用chain.doFilter(request, response)。注意 GET 请求的编码由 Tomcat 的URIEncoding控制,可以在 server.xml 的 Connector 上加上URIEncoding="UTF-8",否则 URL 上的中文参数仍是乱码。

6. 从能跑到能答辩:给系统加分的三个进阶功能

6.1 还款记录与逾期标记

一个只支持“到期还款”的系统做出来很容易,但答辩时没有亮点。我建议加一个逾期判定逻辑:用定时任务或启动时扫描的方式,把所有due_date < CURDATE()且status = 'UNPAID'的还款计划标记为OVERDUE,同步更新借款表状态。这个功能用一条 SQL 就能演示效果,但能体现你对业务状态机的理解:

UPDATE repay_plan SET status = 'OVERDUE' WHERE status = 'UNPAID' AND due_date < CURDATE();

演示时可以先把系统时间改到还款日后一天,或直接在测试库里把某条计划的 due_date 改成昨天,然后执行这条 SQL,再刷新页面给老师看状态变化。再加一个逾期费用字段,还款时自动累加,整个系统的完整度立刻不一样。

6.2 数据可视化:用 ECharts 把账目画出来

管理端页面如果只有表格,视觉上太干巴。常规做法是写一个统计 Servlet,查询每月的放款总额和还款总额,返回 JSON,前端用 ECharts 画折线图。后端只需要一个最简单的查询:

SELECT DATE_FORMAT(apply_time, '%Y-%m') AS month, SUM(amount) AS total FROM loan WHERE status IN ('REPAYING', 'FINISH') GROUP BY DATE_FORMAT(apply_time, '%Y-%m') ORDER BY month;

前端在 admin/dashboard.jsp 里引入 ECharts 的 CDN,初始化折线图,把后端返回的 month 和 total 填进 xAxis 和 series。这个功能代码量不大,但视觉冲击力很强,答辩演示时放在最后展示,老师会觉得你考虑了“管理视角”。

6.3 答辩演示脚本:三分钟讲清系统亮点

我复盘过很多次毕设答辩,发现真正加分的是讲解顺序。别从登录页逐页点起,最好按下面这条链路走:先讲数据库五张表的关联关系,让老师知道你设计了状态机;再注册一个新用户,提交一笔 12000 元分 12 期、年化 12% 的借款;切到管理端审核通过,立刻打开还款计划列表,指着数字验证第一期利息是 120 元;最后演示还款、逾期标记和图表。每个环节都提前准备好测试数据,千万别现场随便输金额,一旦金额没设计好,算出来的还款计划数字不整,讲起来就乱了。这套流程走下来,老师基本不会刁难细节。

做这种带源码的毕设项目,最忌讳的是拿到工程就跑,跑通了就交。我做这类题时养成一个习惯:拿到任何一套源码,先自己删掉核心模块的几行代码,逼着自己补回去,顺便把每个方法的入参和返回理一遍。这个过程能让你从“能跑”到“能讲”,答辩时被问到“这里为什么这样写”才能接得住。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询