简介这是一份软件工程课程设计报告书以停车场管理系统为实战项目完整呈现了从需求分析、概要设计、详细设计到编码测试的软件工程全过程涉及车辆信息记录、动态车位分配、停车费用结算、车位状态查看等核心功能。报告基于Windows XP平台使用Microsoft VC6.0开发并借助Visio 2007建模适合软件工程专业学生、课程设计者及对停车场管理系统感兴趣的学习者参考。压缩包内包含1个doc文档大小仅1.7MB内容为完整的课程设计说明书包含任务书、目录及引言、需求分析、概要结构设计、详细结构设计、测试分析、总结等章节。该资源已被4742人学习下载广受认可。读者可从中获取系统功能设计、数据结构选择、菜单函数与车辆进出管理流程、测试分析等具体实现思路还可借鉴报告排版与文档组织方式用于完成自己的课程设计或毕业设计。1. 停车场管理系统课程设计先搞清楚这份报告书到底要交什么拿到“停车场管理系统软件工程课程设计报告书”这个题目大多数人的第一反应是去找一份能跑的代码然后把代码贴进附录就算完事。但真正决定这份报告书分数的往往不是附录里那份源码而是正文里的需求分析、数据流图、数据库设计和测试用例——老师要验收的是你“有没有按软件工程的流程做完整套开发”而不是“代码能不能跑”。如果你正面临课程设计答辩或者想把一份报告书做成以后毕业设计里能直接复用的底子这篇笔记会把从需求拆解到最终文档的一整条路讲清楚包括哪里容易翻车、计费规则怎么设才算严谨以及答辩时最容易被追问的角落都在哪。2. 需求分析与功能拆解从“停车”到“一张能验收的用例清单”2.1 先把“停车场管理系统”拆成功能点10 个能写进验收标准的模块很多同学拿到题目后直接开始建表写代码写到一半发现不知道该不该做“月卡”、要不要“黑名单”回头再补需求文档结果图和实现对不上。我的习惯是第一步不碰数据库先拿一张 A4 纸把功能点全列出来每个功能点都写上“输入→处理→输出”作为后面所有文档的唯一依据。以课程设计最常见的规模我一般把系统拆成下面这 10 个功能点编号功能点输入处理核心输出优先级F01用户登录账号、密码校验账号密码与角色登录态、角色权限高F02车位管理车位编号、区域、类型车位信息的增删改查车位列表、车位状态高F03车辆入场登记车牌号、车位号校验车位状态并写入场记录入场记录、占用车位高F04车辆出场结算车牌号或入场记录编号计算停车时长与费用释放车位出场记录、应收金额高F05收费规则配置免费时长、单价、封顶金额保存计费规则参数计费规则列表中F06月卡管理车牌号、有效期、费用月卡开通、续费、过期判断月卡状态中F07黑名单管理车牌号、原因黑名单标记与查询黑名单列表入场时拦截中F08日报表统计日期范围汇总当日入场次数、收入日报表中F09停车记录查询车牌号、时间段按条件检索历史记录记录列表低F10超时/异常处理系统时间、订单状态长期占用提醒、异常订单标记异常订单列表低这里每一行都要能对应到后面的一张表、一个接口和至少一条测试用例。F01 到 F04 是最核心的报告书的需求分析部分必须覆盖 F01 到 F08如果你的篇幅有限F09 和 F10 可以用“扩展功能”带过但不能完全不提。功能点清单的最大作用是防止你画数据流图的时候“灵机一动”加一个数据库里并不存在的“优惠券模块”——这种图和实现对不上的情况是答辩老师翻车重灾区。2.2 一次画对三张图用例图、类图和 ER 图怎么保持口径一致软件工程课程设计的文档部分通常要求三张核心图用例图、类图和 ER 图。很多同学分开画结果用例图里有个“管理员充值”ER 图里却没有对应的实体——这就是最典型的“图面功能与实现脱节”。我的做法是先定功能清单 F01-F10然后按“用例图对应功能点、类图对应实体与操作、ER 图对应表与关系”三步走三张图共享同一份功能清单。用例图最基础的参与者有三个管理员管车位、配规则、看报表、收费员操作入场和出场、车主购买月卡、查询记录。用例直接挂到功能编号上收费员“登记入场”对应 F03管理员“配置计费规则”对应 F05。类图的核心类不要超过六个用户类User、车位类ParkingSpace、停车记录类ParkingRecord、月卡类MonthCard、收费规则类FeeRule、报表类DailyReport。类图里的方法与后面的 Service 层方法一一对应比如 ParkingService 里必须有 entry() 和 exit()这两个方法对应类图里 ParkingRecord 的创建和结算。ER 图画三个实体关系就够车位1——N停车记录用户1——N停车记录月卡1——1车辆。重点是在每个实体下标注主键和外键停车记录的 space_id 外键指向车位表的 space_id这个外键必须和建表 SQL 里的外键约束完全一致。画完三张图之后做一个动作拿功能清单逐条对照三张图凡是图里出现但清单没有的删掉清单里有但图里没有的补上。这一步看起来费时间却能避免答辩时“老师指着数据流图问你一个功能你却说这个还没做”的尴尬。2.3 软件工程需求文档直接照着填进报告书的一级目录需求文档放在报告书的前半部分是老师最先看的部分也是“软件工程”这门课最看重的内容。一份课程设计级别的需求文档按下面的结构写就足够引言写项目背景和范围明确“本系统面向中小型停车场不含车位引导和车牌自动识别”。用例清单直接用 2.1 的功能点表格把“处理核心”一列改成“用例描述”补充前置条件和后置条件。数据字典这是很多人漏写的评分点。对每个数据项写清楚字段名、数据类型、长度、取值范围和默认值。比如车牌号 plate_number 是 varchar(15)取值范围是“省份简称字母数字”默认值空。非功能需求不要写“系统运行流畅”这种不可验证的话要写“并发入场请求下车位状态错误率为 0”“月度报表生成时间不超过 3 秒”。数据字典是报告书中性价比最高的一节——写起来不需要动脑但老师一看就知道你做过认真的需求分析。这里有个血泪经验数据字典里的类型和长度务必和第四章的建表 SQL 完全一致比如字典里写 status tinyint表里就不要用 int。3. 数据库与计费规则设计核心表结构、时间状态机和三个边界坑3.1 四张核心表的建表 SQL 与字段设计参数说明数据库设计是报告书的主体章节也是实现代码的地基。课程设计级别的停车场系统四张表足够车位表、用户表、停车记录表、月卡表。下面是可直接复制的建表 SQLCREATE DATABASE parking_system DEFAULT CHARACTER SET utf8mb4; USE parking_system; CREATE TABLE parking_space ( space_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 车位编号, area VARCHAR(20) NOT NULL COMMENT 区域编号,如A区, status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1占用 2禁用, type TINYINT DEFAULT 0 COMMENT 0普通车位 1充电车位, UNIQUE KEY uk_area_space (area, space_id) ) COMMENT车位表; CREATE TABLE user_account ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(64) NOT NULL COMMENT 存储哈希值,不要存明文, role TINYINT DEFAULT 0 COMMENT 0管理员 1收费员 2车主, phone VARCHAR(20) ) COMMENT用户表; CREATE TABLE parking_record ( record_id BIGINT PRIMARY KEY AUTO_INCREMENT, plate_number VARCHAR(15) NOT NULL COMMENT 车牌号,统一大写, space_id INT NOT NULL, user_id INT COMMENT 入场操作人ID, entry_time DATETIME NOT NULL COMMENT 入场时间, exit_time DATETIME COMMENT 出场时间,为空表示在场内, fee DECIMAL(10,2) DEFAULT 0.00 COMMENT 实收金额, payment_type TINYINT COMMENT 0现金 1微信 2支付宝, INDEX idx_plate (plate_number), INDEX idx_entry (entry_time), CONSTRAINT fk_space FOREIGN KEY (space_id) REFERENCES parking_space(space_id) ) COMMENT停车记录表; CREATE TABLE month_card ( card_id INT PRIMARY KEY AUTO_INCREMENT, plate_number VARCHAR(15) NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, status TINYINT DEFAULT 1 COMMENT 1有效 0过期, amount DECIMAL(10,2) NOT NULL ) COMMENT月卡表;几个关键参数说明写完 SQL 后要在报告书里解释清楚record_id 用 BIGINT 而不是 INT。课程设计数据量小INT 看似够用但 INT 上限约 21 亿插入记录时一旦跨过边界就会报主键溢出。用 BIGINT 不丢人也显得你做设计时考虑过数据增长。plate_number 统一存大写。车牌录入时可能存在手输小写的情况程序入口统一toUpperCase()避免“京A12345”和“京a12345”在查重、月卡匹配时被当成两个车牌。建表时统一 utf8mb4。MySQL 默认字符集在部分老版本是 latin1插入中文车牌区域信息会直接变成问号这是最典型的乱翻车问题后面避坑章节会展开。UNIQUE KEY uk_area_space 加在 area 和 space_id 的联合上。这是防止程序 bug 导致同一车位号在不同区域重复出现。entry_time 和 exit_time 用 DATETIME 而不是 TIMESTAMP。TIMESTAMP 有 2038 年问题DATETIME 没有课程设计不需要对比 UTC 时区DATETIME 写入后就是“看着的样子”。3.2 停车计费的状态机与三个边界情况停车计费是整个系统的业务核心也是答辩必问的点。先明确计费规则入场 30 分钟内免费首小时收费 5 元不足 1 小时按 1 小时算超过首小时后每小时加收 2 元向上取整单日自然日封顶 20 元月卡车辆免费但必须产生停车记录计费本质是一个时间状态机入场时车位从“空闲”变为“占用”出场时从“占用”变为“空闲”。停车记录的状态迁移是创建记录入场→ 更新结算字段出场→ 不允许重复结算。边界情况在报告书里单独写一节写清楚这三种情况怎么处理第一种是跨天停车。假设 23:30 入场次日 01:30 出场总时长 2 小时。如果简单“2 小时 × 2 元 5 元”会算成 9 元但按自然日封顶23:30 到当天 24:00 是 30 分钟免费次日 00:00 到 01:30 是 1.5 小时应收 5 元 1 小时加时费 2 元总计 7 元。可见计费必须按自然日切分不能只按总分钟数算。第二种是免费时长的触发条件。免费 30 分钟只在一段连续停车中享受一次。如果车主出场后立刻重新入场需要生成新的记录重新计算免费时长不能因为“这车刚走又来了”就合并时段。第三种是月卡与计费的交互。月卡车辆出场时费用为 0但出场记录必须完整且车位释放逻辑不变。最容易漏的是月卡过期当天系统是免费放行还是按临时车计费我建议报告书里写明“月卡到期当日 24:00 前出场免费之后按临时车计费”这样规则明确测试也好写。3.3 接口与分层设计Service 层如何承载业务规则数据库表结构确定后接口设计要能对上表。课程设计不需要搞微服务一套经典的三层结构就够Controller接收请求、Service业务规则、MapperSQL 访问。我一般定义下面这些 REST 接口POST /api/parking/entry # 车辆入场 POST /api/parking/exit # 车辆出场 GET /api/parking/records # 停车记录查询 GET /api/parking/spaces # 车位状态列表 POST /api/month-card # 办理月卡 GET /api/report/daily # 日报表统计Controller 里不写计费逻辑只做参数校验和返回。计费规则全部收拢到 ParkingService 里这样收费规则调整时只需要改 Service不用动接口。这个分层的理由要写进报告书的设计说明里。4. 核心流程实现用 Spring Boot MyBatis 跑通入场到出场计费4.1 最小可启动项目的骨架与数据库连接配置课程设计的主流技术栈现在基本是 Spring Boot MyBatis MySQL。如果你的学校指定了 SSM 或 JSP原理相同把注解换成 XML 配置即可。先用一个最小可跑的工程# 项目结构Maven 标准结构 src/main/java/com/example/parking/ controller/ParkingController.java service/ParkingService.java mapper/ParkingSpaceMapper.java mapper/ParkingRecordMapper.java entity/ParkingSpace.java entity/ParkingRecord.java src/main/resources/ application.yml mapper/ParkingSpaceMapper.xml mapper/ParkingRecordMapper.xml这是最常见的 Maven Spring Boot 结构。Controller 暴露 HTTP 接口Service 写业务规则Mapper 接口配 XML 或注解 SQL。实体类 ParkingRecord 的字段与 3.1 的建表 SQL 一一对应。然后是最关键的数据库连接配置每个参数都决定你能否顺利连上并显示中文spring: datasource: url: jdbc:mysql://localhost:3306/parking_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true连接串里三个参数各有用途useUnicodetrue告诉驱动使用 Unicode 编码传输characterEncodingutf8指定传输编码为 UTF-8不写它中文大概率变问号serverTimezoneAsia/Shanghai解决 MySQL 8 时区报错。map-underscore-to-camel-case开启后数据库里的plate_number能自动映射到实体类的plateNumber少写一堆 ResultMap。启动项目就用标准命令mvn spring-boot:run如果你的本机没装 Maven用 IDEA 直接运行启动类也一样。启动成功后在浏览器输入http://localhost:8080/api/parking/spaces能看到车位列表就说明数据库连接和 MyBatis 映射都通了。4.2 入场与出场计费核心代码参数说明和事务隔离是关键入场逻辑是整个系统最需要严谨的部分涉及“占车位”和“写记录”两步操作必须放在同一个事务里Service public class ParkingService { Transactional public ParkingRecord entry(String plateNumber, Long spaceId) { // 1. 锁定车位行,防止并发下重复占位 ParkingSpace space parkingSpaceMapper.selectByIdForUpdate(spaceId); if (space null) { throw new BusinessException(车位不存在); } if (space.getStatus() ! 0) { throw new BusinessException(该车位已被占用或禁用); } // 2. 车位状态改为占用 space.setStatus(1); parkingSpaceMapper.update(space); // 3. 创建停车记录 ParkingRecord record new ParkingRecord(); record.setPlateNumber(plateNumber.toUpperCase()); record.setSpaceId(spaceId); record.setEntryTime(LocalDateTime.now()); parkingRecordMapper.insert(record); return record; } }代码里两个关键点必须看明白selectByIdForUpdate是SELECT ... FOR UPDATE的写法作用是对车位这行数据加行锁。两个入口同时扫同一车位时第二个事务会等第一个事务提交后才发现车位已被占用这是解决并发入场冲突的唯一可靠手段。Transactional保证“改状态”和“插记录”要么同时成功要么同时回滚不会出现“记录插入成功但车位状态没改”的中间态。出场计费的代码更考验规则的理解public ExitResult exit(Long recordId, Integer paymentType) { ParkingRecord record parkingRecordMapper.selectById(recordId); if (record null) { throw new BusinessException(停车记录不存在); } if (record.getExitTime() ! null) { throw new BusinessException(重复出场:该记录已结算); } LocalDateTime exitTime LocalDateTime.now(); record.setExitTime(exitTime); BigDecimal fee calcFee(record.getEntryTime(), exitTime, record.getPlateNumber()); record.setFee(fee); record.setPaymentType(paymentType); parkingRecordMapper.update(record); // 释放车位 ParkingSpace space parkingSpaceMapper.selectById(record.getSpaceId()); space.setStatus(0); parkingSpaceMapper.update(space); return new ExitResult(record.getPlateNumber(), record.getEntryTime(), exitTime, fee); } private BigDecimal calcFee(LocalDateTime entry, LocalDateTime exit, String plateNumber) { // 月卡车辆免费 if (monthCardMapper.isValid(plateNumber, LocalDate.now()) 0) { return BigDecimal.ZERO; } long parkMinutes Duration.between(entry, exit).toMinutes(); // 免费30分钟 if (parkMinutes 30) { return BigDecimal.ZERO; } // 跨天情况:按天数分段计算 LocalDate entryDay entry.toLocalDate(); LocalDate exitDay exit.toLocalDate(); if (!entryDay.equals(exitDay)) { long dayCount ChronoUnit.DAYS.between(entryDay, exitDay); BigDecimal completeDaysFee new BigDecimal(20.00).multiply(BigDecimal.valueOf(dayCount)); // 入口当天到24点按一天封顶算,出口当天从0点算 BigDecimal entryDayFee calcDayFee(entry, entryDay.atTime(23, 59, 59)); BigDecimal exitDayFee calcDayFee(exitDay.atStartOfDay(), exit); return completeDaysFee.add(entryDayFee).add(exitDayFee); } // 同一天内正常计费 return calcDayFee(entry, exit); } private BigDecimal calcDayFee(LocalDateTime entry, LocalDateTime exit) { long minutes Duration.between(entry, exit).toMinutes(); if (minutes 30) { return BigDecimal.ZERO; } if (minutes 60) { return new BigDecimal(5.00); } // 超过1小时后,按小时向上取整,封顶20元 long extraHours (minutes - 60 59) / 60; extraHours Math.min(extraHours, 7); BigDecimal fee new BigDecimal(5.00) .add(new BigDecimal(2.00).multiply(BigDecimal.valueOf(extraHours))); return fee.min(new BigDecimal(20.00)); }这段代码请你逐行读一遍两个地方需要特别说明(minutes - 60 59) / 60是整数除法做向上取整的技巧。首小时已经收了 5 元所以剩余分钟数减掉 60再加 59 后整除以 60任何不足一小时的部分都会进一。例如停车 61 分钟extraHours (61 - 60 59) / 60 1加收 2 元总计 7 元停车 119 分钟extraHours (119 - 60 59) / 60 1总计 7 元停车 121 分钟extraHours 2总计 9 元。Math.min(extraHours, 7)是为了满足封顶规则。首小时 5 元加 7 个加时小时 14 元等于 19 元逼近 20 元封顶超过后直接落回fee.min(20.00)。同一天内不管停多久最高 20 元。跨天部分的分段逻辑先按整天数乘 20 元封顶再把不完整的首日和末日拆出来单独按calcDayFee计算。这种写法的优点是规则清晰答辩时你能直接画出一条时间轴讲明白。4.3 用样例数据跑通全流程curl 验证入场与出场代码写完后用样例数据验证流程是必须写进报告书的步骤。先插入一条车位和一辆测试车INSERT INTO parking_space (area, status, type) VALUES (A区, 0, 0);然后启动项目控制台发起入场请求curl -X POST http://localhost:8080/api/parking/entry \ -H Content-Type: application/json \ -d {plateNumber:京A12345,spaceId:1}返回内容里应包含recordId、plateNumber和entryTime。再次查看车位状态确认 space_id1 的状态变为“占用”。等待 35 分钟后执行出场curl -X POST http://localhost:8080/api/parking/exit \ -H Content-Type: application/json \ -d {recordId:1,paymentType:0}返回的fee应为 0还在免费时长内。如果直接修改数据库把 entry_time 改成 3 小时前再出场fee 应该计算为 20 元封顶。用这两组数据验证既证明计费规则生效也证明数据库时间与代码逻辑一致。5. 停车场管理系统课程设计的 6 个常见翻车点与排查清单5.1 数据库中文乱码现象插入“京A12345”查询结果变成“???”或“å…«A12345”。原因三条链路上至少一处不是 UTF-8。建库时默认字符集是 latin1或 JDBC 连接串没有characterEncodingutf8或 MySQL 服务端配置文件character-set-server不是 utf8mb4。解决建库语句明确DEFAULT CHARACTER SET utf8mb4JDBC URL 加上useUnicodetruecharacterEncodingutf8修改/etc/my.cnf或my.ini的character-set-serverutf8mb4后重启 MySQL 服务。注意改完库后已存在的表要用ALTER TABLE转字符集否则新插入的数据还是乱。5.2 计费结果比预期多出几十分钟现象测试入场 10:00出场 10:45代码逻辑显示应免费实际却收了 5 元。原因最常见的两种一是数据库里 entry_time 插入时用了NOW()而代码里用LocalDateTime.now()两者来自不同时钟源测试时手工改过系统时间导致时间错位二是前端传入入场时间字符串代码没有按固定时区解析。解决统一时间来源入场时间一律由 Java 服务端生成LocalDateTime.now()数据库只负责存储不在 SQL 里写NOW()。测试时不要手工改系统时间要模拟长时间停车就 UPDATE entry_time改完确认时区一致。5.3 并发入场两个入口同时扫同一车位现象两个管理员几乎同时给同一车位入场系统里出现两条在场记录车位状态变成“占用”但记录有两条。原因入场逻辑没有行锁先查状态再写记录两步之间存在间隙。两个请求都读到“空闲”然后先后写入了记录。解决使用SELECT ... FOR UPDATE锁住车位行配合Transactional保证查锁和更新在同一事务里。报告书的“并发控制”一节要写清楚这一条很多项目不做并发控制答辩时被问“两个入口同时扫同一个车位怎么办”就卡住了。5.4 MySQL 连接报 Communications link failure现象项目启动直接报错java.sql.SQLException: Communications link failure。原因MySQL 服务没启动或端口被占用或 JDBC URL 里的 localhost 指向不对。解决先检查 MySQL 服务是否在运行Windows 下用netstat -ano | findstr 3306看端口是否被监听。如果端口被占用优先怀疑 MySQL 服务重复安装或被杀毒软件拦截。连接串里 localhost 和 127.0.0.1 在有 IPv6 的环境下有区别遇到连不上就换成 127.0.0.1 试试。5.5 报告书的数据流图与数据字典对不上现象数据流图里画了“充值”功能数据字典里却没有充值相关字段数据库里也没有充值表。原因先画图后建表或者画图时觉得多画一个功能更“完整”但实现时忘了补。这是答辩时被老师追问最尴尬的问题。解决以功能清单 F01-F10 为唯一基准画完图后逐项对照清单图上有而清单没有的功能直接删掉清单上有而图上没有的补上。数据库表结构也必须能覆盖所有功能点需要的字段宁可少画一个花哨功能不要多画一个实现不了的功能。5.6 报告书 Word 文档里图片模糊现象Visio 或 ProcessOn 画的用例图截图粘贴进 Word 后文字糊成一片打印出来更糊。原因直接截图粘贴时图片分辨率不够Word 自动压缩了图片。解决从绘图工具导出时选择 PNG 格式导出设置里 DPI 选 150 以上。插入 Word 后不要等比放大超过原始尺寸。表格用三线表风格去掉系统默认的网格线打印效果更好。6. 验证与进阶用 10 条测试用例钉死课程设计评分点6.1 覆盖功能与边界的测试用例表测试是报告书里最能体现“软件工程素养”的部分很多同学只写“系统测试通过”这是不够的。拿出一份可以执行的测试用例表老师会认为你真的做完了测试。下面这份覆盖了核心功能和边界用例编号功能点操作步骤预期结果TC01正常入场空闲车位执行入场入场成功车位状态变占用TC02重复入场对已占用的车位再次入场提示“该车位已被占用”TC03免费时长入场后 30 分钟内出场收费 0 元TC04首小时计费入场 45 分钟后出场收费 5 元TC05超时加价入场 2 小时 10 分钟后出场收费 9 元TC06单日封顶入场 10 小时后出场收费 20 元TC07跨天计费23:00 入场次日 01:00 出场收费 5 元TC08月卡免费有效月卡车辆入场后出场收费 0 元TC09重复出场对已结算记录再次执行出场提示“该记录已结算”TC10并发入场两线程同时入场同一车位一次成功一次失败仅一条在场记录每条用例报告书里要配实际运行结果截图截图里包含接口返回和数据库查询结果不要只贴一张“所有用例通过”的表格。6.2 加分项日报表统计与车位利用率怎么算课程设计想拿高分日报表是性价比最高的加分项。统计当天收入和车流量的 SQLSelect(SELECT DATE_FORMAT(entry_time, %Y-%m-%d) AS day, COUNT(*) AS total_count, SUM(CASE WHEN exit_time IS NOT NULL THEN fee ELSE 0 END) AS income FROM parking_record GROUP BY day ORDER BY day DESC LIMIT 7) ListDailyReport selectLast7DaysReport();DATE_FORMAT(entry_time, %Y-%m-%d)把 DATETIME 按天截断配合GROUP BY按天分组。这个查询在数据量小的时候没问题但做毕业设计时要考虑在 entry_time 上加索引否则数据上千条后报表会明显变慢。车位利用率可以用“已被占用的总时长 /总车位数×统计时长”来计算写一个 SQL 或者放在 Service 里算都行报告书里写明公式即可。6.3 一组验证完整性的检查清单代码和报告书都写完以后我会用一张检查清单逐项打勾这也是我一直保留的习惯功能清单 F01-F10 每一项都有对应的接口、代码、截图吗用例图和类图的类名与代码里的 Controller、Service、实体类名完全一致吗ER 图的外键与建表 SQL 的 FOREIGN KEY 约束完全一致吗数据字典的类型和长度与表结构完全一致吗测试用例表里的每条用例都在截图中有据可查吗报告书里的计费规则与calcFee方法里的三段分支免费、按小时、封顶逐行对应吗最后一条建议不要等到报告书快交了才写文档。正确节奏是功能清单先定画图建表写代码边写边截运行图最后一天只做排版。顺序反了就只能对着自己写的代码编文档那种感觉就像把黑匣子拆开硬讲原理翻车是大概率事件。这份流程是我做课程设计一直沿用的标准动作希望帮到你。本文还有配套的精品资源点击获取