news 2026/10/2 6:35:12

SSM停车计费系统毕业设计:从数据库建模到事务处理的完整实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSM停车计费系统毕业设计:从数据库建模到事务处理的完整实战解析

简介:基于Java+SSM的校内车辆停车计费收费系统是一份完整毕业设计项目资源,面向计算机相关专业在校学生、教师及企业技术人员,适用于毕业设计、课程设计、项目演示或学习进阶。资源包内含项目源码、数据库脚本及使用文档,共1148个文件,涵盖前端HTML/CSS/JS页面、后端Java/JSP逻辑、SQL数据库文件、配置文档及依赖JAR包,整体大小约18.84MB,目录结构清晰,便于按需查阅。目前已有172人学习下载。项目答辩评审分达95分,已获导师认可并通过Windows/macOS环境运行测试,功能可靠。可直接部署参考,也可基于源码二次开发,深入理解SSM框架整合、数据库设计与停车计费业务实现,全流程代码逻辑清晰,是高质量的学习与实践范本。

1. 校内车辆停车计费收费系统:毕业设计里最该做透的SSM实战题

校内车辆停车计费收费系统,基于 Java + SSM(Spring + SpringMVC + MyBatis)实现,属于毕业设计题库里"看着不惊艳,做完很扎实"的那类项目。它要解决的是学校门岗车辆进出登记混乱、出场时靠翻本子计时收费的问题,核心就两件事:车进出时记录时间点,出场时套用费率算出钱、记好账。这个标题附带的是可以直接运行并二次开发的完整源码、数据库脚本和使用文档,适合需要交 Java 毕设、或者想用一套最小可跑的 SSM 工程把三层架构彻底看明白的从业者和学生。项目不碰分布式,但 MVC 分层、ORM 映射、声明式事务、权限拦截这些 Java 后端就业高频考点全部能对上号,拿它做底子扩展简历项目也顺手。

2. 先把业务理清:停车计费系统的模块划分与数据库建模

做这套系统最忌讳的是拿到源码就开跑,跑通后却答不上来"表之间什么关系"。答辩老师第一句话往往是:"你这个系统有哪些功能?数据库几张表?"这题答不顺,后面再好的功能都打折。我的习惯是先按业务用例把边界划清楚,再落表结构,最后才写代码。

2.1 三大功能域:出入场管理、计费引擎、收费结算

停车计费系统看着功能多,剥开就三个域:车辆从进场到出场的状态流转,叫出入场管理;根据费率和停车时长算钱,叫计费引擎;把收到的钱记下来并能汇总统计,叫收费结算。毕设版本常加的车位管理,本质是附属模块,挂在停车记录上做状态联动即可,不应该让它喧宾夺主。

功能域职责核心表
出入场管理车辆进场登记、出场确认,记录时间点与在场状态parking_record
计费引擎按费率表计算停车费用,支持免费时长、首小时价、封顶金额fee_rate, parking_record
收费结算收费流水记录、按日/月汇总、系统用户与权限维护payment_record, sys_user

三个域对应三条独立的业务链路:入场时写一条在场记录,出场时更新这条记录并生成费用,同时插入一条收费流水。我在代码里严格按这个链路划分 Service 方法,避免出现"出场只更新停车记录但没生成收费记录"这种漏一半的情况。

2.2 五张核心表:字段设计与选型理由

表不在多,覆盖完整流程即可。我一般用五张表:sys_user 管登录用户,fee_rate 管计费参数,parking_record 管车辆每一次进出的完整生命周期,payment_record 管缴费流水,再加一张 vehicle_info 做车辆档案,区分教职工车、学生车和临时车。如果不想做月租车,vehicle_info 可以不要,但毕设里保留它能在演示时多讲一层"车辆分类管理"。

几个关键字段的选择要提前定死:

  • 金额一律用 DECIMAL(10,2),绝不用 FLOAT/DOUBLE。浮点算钱会出 0.1+0.2 != 0.3 的翻车现场,这在计费系统里是致命的。
  • 停车记录表必须有 status 字段,0 表示在场,1 表示已出场。这个字段是防重复入场和防重复出场的唯一依据,后续并发控制全靠它。
  • 费率表的 free_minutes(免费分钟数)、unit_price(超时后每小时单价)、daily_cap(24小时封顶金额)三件套必须齐全,缺了任何一个,计费逻辑就写不完整。
  • parking_record 里存 rate_id 而不是存费率快照,这样改费率不影响历史记录的计算口径。我见过把单价直接存进记录表的做法,短看是方便,长看历史报表口径全乱。

2.3 初始化脚本:建库建表与演示数据

数据库脚本是这个压缩包里最该先看的东西。拿到手先执行建库脚本,再谈跑项目。以下是我常用的 MySQL 初始化脚本核心部分:

CREATE DATABASE IF NOT EXISTS parking_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE parking_db; -- 系统用户表:角色 1=管理员 2=收费员 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(32) DEFAULT NULL, role TINYINT NOT NULL DEFAULT 2, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; -- 费率表:免费30分钟,首小时5元,之后每小时2元,24h封顶30元 CREATE TABLE fee_rate ( id INT PRIMARY KEY AUTO_INCREMENT, rate_name VARCHAR(32) NOT NULL, free_minutes INT NOT NULL DEFAULT 30, first_hour_price DECIMAL(10,2) NOT NULL DEFAULT 5.00, unit_price DECIMAL(10,2) NOT NULL DEFAULT 2.00, daily_cap DECIMAL(10,2) DEFAULT 30.00, status TINYINT DEFAULT 1 ) ENGINE=InnoDB; -- 停车记录表:status 0=在场 1=已出场 CREATE TABLE parking_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, plate_no VARCHAR(16) NOT NULL, in_time DATETIME NOT NULL, out_time DATETIME DEFAULT NULL, fee DECIMAL(10,2) DEFAULT 0.00, rate_id INT NOT NULL, status TINYINT DEFAULT 0, create_by INT DEFAULT NULL, INDEX idx_plate_status (plate_no, status), INDEX idx_in_time (in_time) ) ENGINE=InnoDB; -- 收费记录表:一条停车记录对应一条缴费流水 CREATE TABLE payment_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, record_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, pay_method TINYINT DEFAULT 1 COMMENT '1=现金 2=扫码 3=月票', operator_id INT NOT NULL, pay_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_payment_record FOREIGN KEY (record_id) REFERENCES parking_record(id) ) ENGINE=InnoDB; -- 初始化一个管理员账号,密码做了MD5 INSERT INTO sys_user (username, password, real_name, role) VALUES ('admin', MD5('123456'), '系统管理员', 1); -- 初始化一份默认临时车费率 INSERT INTO fee_rate (rate_name, free_minutes, first_hour_price, unit_price, daily_cap) VALUES ('临时车标准费率', 30, 5.00, 2.00, 30.00);

这段脚本里有两个细节值得说明。一是 ENGINE=InnoDB 必须写,MyISAM 不支持外键和行级锁,计费系统写入频繁,行锁能减少并发冲突;二是联合索引 idx_plate_status 直接服务于"查某车牌是否在场"这条高频查询,没有这个索引,表数据过万后每次入场都全表扫描,答辩演示时现场卡顿很难看。

密码存储用了 MD5,这是毕业设计最常见的妥协方案。我建议不要在这里纠结安全性,演示项目用 MD5 足够,但在答辩时要有意识地说一句"生产环境会用 BCrypt 加盐",这句话能体现你知道这里的水深。

3. SSM 三层整合:从 web.xml 到 MyBatis 映射的完整搭建

SSM 项目最劝退新手的不是业务代码,而是那一堆 XML 配置。Spring Boot 用自动配置把这些藏起来了,SSM 则把每一步都摆在明面上。换个角度看,这正是用 SSM 做毕设的优势:配置配明白了,框架原理也就懂了七八成。

3.1 三个配置文件的分工与必填项

SSM 项目的配置集中在三个文件:web.xml 管启动流程和全局过滤器,spring-dao.xml 管数据源和事务,spring-mvc.xml 管 Controller 和视图。以下是我整理的分工表:

配置文件管什么必配项
web.xmlServlet 容器启动、过滤、转发CharacterEncodingFilter、ContextLoaderListener、DispatcherServlet
spring-dao.xml数据源、SQL 会话工厂、事务DruidDataSource、SqlSessionFactoryBean、MapperScannerConfigurer、事务管理器
spring-mvc.xmlController 扫描、视图解析注解驱动、组件扫描、InternalResourceViewResolver

web.xml 是最容易被忽略编码问题的地方。POST 请求乱码十有八九是过滤器没配或配置位置不对:

<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</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>

CharacterEncodingFilter 这个过滤器必须放在所有过滤器的最前面,因为后续的请求解析全都依赖编码格式。放在后面的话,参数读取可能已经按 ISO-8859-1 解析完了,再转码也无力回天,这是血泪经验。

spring-dao.xml 里数据源配置是坑最多的,先看核心段:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/parking_db?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.parking.mapper"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/>

URL 里的 serverTimezone=Asia/Shanghai 是给 MySQL 8 用的,不加这个,连接时会报 CST 时区异常。driverClassName 必须写 com.mysql.cj.jdbc.Driver 而不是老版本的 com.mysql.jdbc.Driver。这两个点配错,项目一启动就报错,且报错信息极具误导性,像是数据库地址不通,其实是驱动问题。

MapperScannerConfigurer 省去了写 MapperFactoryBean 的繁琐,只要指定 basePackage,MyBatis 会自动扫描接口并把代理对象注入 Spring 容器。

3.2 Controller / Service / Mapper 的分层边界

拿到源码后先看包结构,包结构就是架构。我一般约定三层规则:Controller 只做参数接收和结果封装,不写任何业务判断;Service 写完整业务逻辑,事务注解加在 Service 实现类上;Mapper 只做单表/关联查询,不在 XML 里写复杂业务计算。

层职责常见反例
Controller参数校验、视图/JSON 返回在 Controller 里写 SQL 拼接或事务
Service业务编排、事务控制、计费计算Service 里塞一堆 System.out.println
Mapper数据库增删改查、SQL 映射在 Mapper XML 里写 if/else 复杂计算

3.3 从 Controller 到 Mapper:入场功能完整走查

以入场功能为例,走一遍完整调用链。Controller 层接收车牌号:

@Controller @RequestMapping("/parking") public class ParkingController { @Autowired private ParkingService parkingService; @PostMapping("/in") @ResponseBody public Result vehicleIn(@RequestParam String plateNo) { if (plateNo == null || plateNo.trim().isEmpty()) { return Result.error("车牌号不能为空"); } return parkingService.in(plateNo.trim().toUpperCase()); } }

这里车牌转大写是关键前置处理。用户输入 abc123 和 ABC123 如果当作不同车牌,系统就会出现同一辆车重复入场的假象。这种问题在答辩演示时特别容易翻车,因为演示用的测试牌号往往随手输入,大小写一不一致就露馅。

Service 实现类承担了业务主逻辑:

@Service public class ParkingServiceImpl implements ParkingService { @Autowired private ParkingRecordMapper recordMapper; @Autowired private FeeRateMapper feeRateMapper; @Override @Transactional public Result in(String plateNo) { int count = recordMapper.countByPlateAndStatus(plateNo, 0); if (count > 0) { return Result.error("该车辆已入场,请勿重复登记"); } FeeRate rate = feeRateMapper.selectActiveRate(); ParkingRecord record = new ParkingRecord(); record.setPlateNo(plateNo); record.setInTime(new Date()); record.setRateId(rate.getId()); record.setStatus(0); recordMapper.insert(record); return Result.success("入场成功,入场时间:" + record.getInTime()); } }

这个入场的逻辑顺序很重要:先查重,再取费率,最后插入记录。查重必须放在 insert 之前,否则并发请求下同一辆车可能插入两条在场记录。事务注解 @Transactional 必须加在 public 方法上,加在 private 方法上不生效,加在 Controller 上虽然也能跑但有性能损耗,且职责错位。

Mapper 接口和 XML 配合:

public interface ParkingRecordMapper { int countByPlateAndStatus(String plateNo, int status); int insert(ParkingRecord record); }
<select id="countByPlateAndStatus" resultType="int"> SELECT COUNT(*) FROM parking_record WHERE plate_no = #{plateNo} AND status = #{status} </select> <insert id="insert" parameterType="com.parking.entity.ParkingRecord" useGeneratedKeys="true" keyProperty="id"> INSERT INTO parking_record (plate_no, in_time, rate_id, status) VALUES (#{plateNo}, #{inTime}, #{rateId}, #{status}) </insert>

useGeneratedKeys 和 keyProperty 必须成对出现,这样 MyBatis 会把数据库自增主键回填到对象的 id 属性上。后续出场更新、生成收费流水都依赖这个 id,不配的话 id 永远是 null,后面逻辑直接断掉。

4. 计费规则怎么设计:按小时、免费时长、封顶金额的取舍

停车计费是整个项目的灵魂。入场出场做得再顺,计费公式错了,系统就是废的。我见过太多版本把计费写死在代码里,改个单价要改代码重新部署,这是最让答辩老师皱眉头的设计。

4.1 先定规则再写代码:一套可演示的计费规则

毕设里我推荐的计费规则是:入场前 30 分钟免费;超过 30 分钟后,首小时 5 元;首小时之后每小时加收 2 元;24 小时内封顶 30 元。这套规则覆盖了免费时长、阶梯计价、封顶三个维度,逻辑上完整,演示时又好解释。

规则必须放在 fee_rate 表里配置化,而不是写死在 Java 里。配置化的价值在答辩时可以现场演示:把 daily_cap 改成 20 元,重启项目(或者热加载),出场费用立刻按新规则计算。老师看到的是可维护性,这是评分点。

4.2 计费计算:用 Java 还是 SQL

计费可以用一条 SQL 算,也可以拉到 Java 里算。对比一下持仓,我只说我的选择:用 Java 算。原因有三:一是逻辑可读性强,答辩时能对着代码逐行讲;二是方便单元测试,拿几个边界时间点一测就知道对错;三是复杂规则演进空间大,夜间费率、分段累进都能在 Java 里优雅实现。SQL 方案短小精悍,但一旦封顶和免费时长叠加,SQL 就变得像天书,别说同学看不懂,三个月后的自己也看不懂。

计费核心代码我按下面的方式写:

public BigDecimal calcFee(Date inTime, Date outTime, FeeRate rate) { long diffMillis = outTime.getTime() - inTime.getTime(); double minutes = diffMillis / (60.0 * 1000); if (minutes <= 0) { return new BigDecimal("0"); } // 免费时长内不收费 if (minutes <= rate.getFreeMinutes()) { return new BigDecimal("0"); } // 超过免费时长后,不足1小时按1小时计算 double overMinutes = minutes - rate.getFreeMinutes(); int hours = (int) Math.ceil(overMinutes / 60.0); BigDecimal fee = rate.getFirstHourPrice() .add(rate.getUnitPrice().multiply(new BigDecimal(Math.max(0, hours - 1)))); // 封顶:超过dailyCap则按dailyCap收 if (rate.getDailyCap() != null && fee.compareTo(rate.getDailyCap()) > 0) { fee = rate.getDailyCap(); } return fee; }

这个方法的几个参数值得细说。Math.ceil 向上取整保证了"不足1小时按1小时"的规则,停车 31 分钟会按 1 小时算。Math.max(0, hours - 1) 是因为第一个小时已经收了 first_hour_price,所以超出的部分从第二小时开始按 unit_price 累加。封顶判断用的是 compareTo 而不是>,因为 BigDecimal 是对象,>比较的是内存地址而不是数值。我见过有人在这里写成fee > rate.getDailyCap(),编译能过,运行结果全错,属于典型的隐蔽 bug。

4.3 出场结算与防重复出场

出场是计费和状态更新的联动操作,最容易出并发问题。两辆车同时扫同一个车牌出场(比如前后车车牌一样,或者误操作点了两次),如果代码不做保护,会产生两次收费记录,把一笔停车费收成两笔。

我的出场实现:

@Override @Transactional public Result out(Long recordId) { ParkingRecord record = recordMapper.selectById(recordId); if (record == null) { return Result.error("停车记录不存在"); } if (record.getStatus() == 1) { return Result.error("该车辆已出场,请勿重复操作"); } Date outTime = new Date(); FeeRate rate = feeRateMapper.selectById(record.getRateId()); BigDecimal fee = calcFee(record.getInTime(), outTime, rate); // 关键:更新时带 status=0 条件,防止并发重复出场 int rows = recordMapper.updateOut(recordId, outTime, fee, 1, 0); if (rows == 0) { return Result.error("出场操作失败,请刷新后重试"); } PaymentRecord payment = new PaymentRecord(); payment.setRecordId(recordId); payment.setAmount(fee); payment.setPayMethod(1); payment.setOperatorId(getLoginUserId()); paymentRecordMapper.insert(payment); return Result.success("出场成功,应收金额:" + fee + "元"); }

对应 Mapper 里的更新 SQL:

<update id="updateOut"> UPDATE parking_record SET out_time = #{outTime}, fee = #{fee}, status = #{newStatus} WHERE id = #{id} AND status = #{oldStatus} </update>

这里 status = oldStatus(即 0)作为更新的前提条件,是整个防重复出场的关键。两个并发请求同时执行这条 SQL,数据库行锁会让第二个请求等待,等它拿到锁时,第一条已经把 status 改成 1 了,第二条的 WHERE 条件不再满足,更新行数为 0。Service 通过判断 rows == 0 感知到并发冲突并返回错误提示。这套机制比在 Java 里加 synchronized 靠谱得多,因为多个 Tomcat 实例部署时,synchronized 只对单实例内的线程有效。

5. 停车计费系统避坑指南:从连不上数据库到乱扣费

这套系统我前前后后调试的坑都集中在配置、时间和并发三条线上。每条都是现象、原因、解决三步走,照着排查能省半天功夫。

5.1 MySQL 8 连接失败:驱动与时区的组合拳

现象:Tomcat 启动时报 Communications link failure 或者 Access denied for user 'root'@'localhost',数据库密码明明没错。

原因:数据库是 MySQL 8.0,但用了旧的 mysql-connector-java 5.x,驱动类名还是 com.mysql.jdbc.Driver;同时 JDBC URL 里没有带 serverTimezone 参数,驱动无法解析本地时区。这个报错信息极具迷惑性,表面看是网络不通,实际是驱动版本和时区双重问题。

解决:驱动包换成 mysql-connector-java 8.0.x,driverClassName 改为 com.mysql.cj.jdbc.Driver,URL 追加?serverTimezone=Asia/Shanghai&”useUnicode=true&characterEncoding=utf8。注意 XML 里 & 符号必须写成&amp;,直接写 & 会 XML 解析报错。

5.2 时长算出负数:时间精度与单位换算

现象:车辆进场后立即出场,费用算出来是负数或者报错;或者停车 5 分钟被收了 5 块钱。

原因:时长计算用了(outTime.getTime() - inTime.getTime()) / 60000,两个时间差不足一分钟时整除结果为 0,再参与计费逻辑就乱了;如果 inTime 和 outTime 从数据库读取时带了时分秒精度丢失,差值也可能为负。另一种情况是没判断"时长是否在免费时长内"就急着算钱,直接把免费时段也按每小时收费。

解决:时长计算改成diffMillis / (60.0 * 1000)保留小数,然后在进入计费前先判断minutes <= 0直接返回 0,再判断是否落在 free_minutes 内。单价计算用 Math.ceil 向上取整,确保超过免费时长哪怕 1 分钟也按 1 小时起算。

5.3 MyBatis 动态 SQL 里的小于号被吞了

现象:Mapper XML 里写了WHERE status < 1,启动时报错,错误信息里有一串 XML 解析异常。

原因:XML 里<是标签开头字符,MyBatis 解析 XML 时把它当成标签处理,直接解析失败。这是 MyBatis 上手期最高频的报错之一。

解决:在 XML 中不能用裸的小于号,改用&lt;转义或者用<![CDATA[ 条件 ]]>包起来。比如查未出场车辆(status=0),写成status &lt; 1。这个坑不难跳,难在明明知道却每次写 SQL 时都忘记,尤其从 SQLyog 里拷 SQL 出来的时候最容易踩。

5.4 事务没生效:停车记录更新了,收费流水没写

现象:出场后查看停车记录,状态已经变成已出场,但 payment_record 表里没有对应的收费记录,金额对不上。

原因:@Transactional 注解加在了 Controller 上,但 Spring AOP 默认用 JDK 动态代理,代理对象对 Controller 的方法调用不经过事务增强;或者注解加在了 Service 私有方法上,私有方法不走代理。还有一种情况是自调用,同一个类里的方法互相调用,事务同样失效。

解决:事务注解统一加在 Service 实现类的 public 方法上。出场和收费这两步写操作必须在一个事务里,要么全成功要么全回滚,否则数据永远对不上账。

5.5 中文乱码:车牌的省字变问号

现象:输入"京A12345"保存后,页面显示"??A12345",数据库里存成了乱码。

原因:多级编码不一致。JSP 页面请求没带 UTF-8,Tomcat 默认用 ISO-8859-1 解码,数据库连接串又没有 characterEncoding=utf8,三层各乱一层,最终入库就是问号。

解决:web.xml 里 CharacterEncodingFilter 的 url-pattern 写成/*并放在过滤器链第一位;JSP 页面顶部加pageEncoding="UTF-8";JDBC URL 加characterEncoding=utf8。三层统一后乱码基本绝迹。另外车牌过滤规则也要提前想好,只允许中文、字母和数字,其他字符直接拦截,免得特殊字符入库引发后续查询问题。

6. 把毕业设计做出区分度:验证流程与加分项

系统跑通只是及格,做出区分度才拿得到高分。我建议在交付前按下面这套流程走一遍:先初始化数据库,用 admin 账号登录后台,配置一份费率(比如免费 30 分钟、首小时 5 元、封顶 30 元);然后录入一辆临时车入场,记下入场时间;等超出免费时长后再操作出场,核对费用是否按预期计算;最后查看收费记录和统计报表,确认数据闭环。这张自检清单我每次答辩前都跑一遍,比临时翻代码管用。

加分项里性价比最高的是统计报表。给收费结算加一张按日汇总的查询,用一条 SQL 撑起来:

SELECT DATE_FORMAT(in_time, '%Y-%m-%d') AS day, COUNT(*) AS total_count, SUM(fee) AS total_fee FROM parking_record WHERE status = 1 AND in_time >= #{startDate} AND in_time < #{endDate} GROUP BY day ORDER BY day;

这条 SQL 按天分组统计出场车次和总收费金额,返回的数据直接喂给 ECharts 画柱状图,折线图也行。答辩时展示这张趋势图,配合讲解"数据库层用 GROUP BY 做聚合,避免在 Java 里循环计算",技术深度一下就出来了。统计功能工作量不大,但对项目的观感提升非常明显。

我自己的习惯是每写完一个模块,先用边界数据把它打一遍:免费时长边界(29 分钟、30 分钟、31 分钟)、整点边界(1 小时整、1 小时 1 分)、封顶边界(23 小时 vs 25 小时)这些用例跑完,心里才有底。计费系统是跟钱打交道的代码,算错一分钱都是 bug,早测早安心。这套项目做完,SSM 配置、MyBatis 动态 SQL、声明式事务、BigDecimal 精度处理,这几个后端基本功基本就焊死在手上了,希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 4:38:53

U-Net心脏MRI分割实战:从环境配置到临床可用结果

简介&#xff1a;本资源是一套基于U-Net架构实现心脏医学图像分割的完整Python项目&#xff0c;面向计算机、人工智能、生物医学工程等专业的本科生与研究生&#xff0c;适用于毕业设计、课程设计及深度学习入门实践。项目代码已通过实测验证&#xff0c;支持端到端训练与推理&…

作者头像 李华
网站建设 2026/10/1 4:38:20

Playwright追踪查看器:端到端测试与动态页面调试的现场还原指南

绝大多数自动化项目的问题只有两类&#xff1a;跑不通的&#xff0c;和跑通了但结果是错的。而最让人崩溃的&#xff0c;是CI环境里跑不通&#xff0c;本地怎么复现都是绿的。以前遇到这种情况&#xff0c;我能做的就是翻日志、翻截图&#xff0c;运气好了能从screenshot里看出…

作者头像 李华
网站建设 2026/10/1 4:37:31

AI一键生成专业报告:从大模型原理到RAG实战应用全解析

1. 为什么“AI一键生成专业报告”能成为决策的关键环节我做内容和技术相关的工作有年头了&#xff0c;这几年最明显的一个变化是&#xff1a;一个能打的人&#xff0c;往往是“会问问题会看报告”的人&#xff0c;而不是“会写报告”的人。但你反过来看&#xff0c;大部分人的时…

作者头像 李华
网站建设 2026/10/1 4:37:28

GPT-6 Luna降价背后:从API选型到微调部署的成本重构

最近不少开发者群里都在传同一张截图&#xff1a;最新更新的模型价格表里&#xff0c;GPT-6 Luna 的输入价格已经比 DeepSeek V4.1 Flash 低了将近三分之一&#xff0c;输出价格也低了一截。第一反应是“又降价了”&#xff0c;第二反应是“那我之前花大半年做好的选型是不是白…

作者头像 李华
网站建设 2026/10/1 4:37:09

从密钥泄露到成本失控:自建API管理系统的完整复盘与设计实践

先说个真实事故。上个月我们团队一位同事图省事&#xff0c;把一条 DeepSeek 的 API key 直接塞进了前端项目的构建变量里&#xff0c;结果前端打包产物被人扒走&#xff0c;当天下午线上就开始疯狂报unexpected status 401 unauthorized: incorrect api key provided&#xff…

作者头像 李华
网站建设 2026/10/1 4:37:08

设备禁用功能自动化测试:链路验证、断言设计与稳定落地

做设备管理平台测试的时候&#xff0c;我遇到过最典型的“假通过”问题&#xff1a;后台页面上把设备状态改成“已禁用”&#xff0c;UI提示禁用成功&#xff0c;数据库里状态也变了&#xff0c;所有人都以为功能上线了。结果设备端呢&#xff1f;照样登录、照样拉数据&#xf…

作者头像 李华