简介:本资源是一套完整的Java Web电子商城课程设计大作业,面向高校计算机专业学生及Java初学者,用于软件工程实践、毕业设计参考或Web开发综合实训。内容覆盖需求分析、概要设计、详细设计到系统实现全流程,提供可直接运行的MyEclipse+SQL Server开发环境支持方案。压缩包共214个文件,约4.8MB,包含64个JSP页面(实现前后端交互)、31个Java类(含GoodsAction、MemberDao、OrderAction等核心业务逻辑)、31个编译后Class文件、7个Word文档(含说明书与设计文档)、8个Jar依赖库及配套数据库文件(mdf/ldf)和图片资源,结构完整、模块清晰。目前已有991人学习下载,读者可直接导入MyEclipse运行系统,结合文档理解MVC分层设计思想,掌握用户管理、商品分类、订单处理等典型电商功能的代码实现与数据库建模方法。
1. 为什么一个“完整”的电子商城项目,反而卡在需求分析和数据库设计上就动不了?
很多同学拿到“Java电子商城(完整)源代码+数据库+全套文档”这类资源时,第一反应是赶紧跑起来、截图、写答辩PPT——结果双击start.bat报错ClassNotFoundException: com.mysql.cj.jdbc.Driver,查application.properties发现数据库地址写的是jdbc:mysql://localhost:3306/ecshop?useSSL=false&serverTimezone=UTC,但本地根本没装 MySQL,更别说建库、导入 SQL、配用户权限。翻到需求分析.docx,发现里面写着“支持秒杀、优惠券、多级分销”,可源码里连个CouponService类都没有;打开概要设计.pdf,UML 图里画了“订单中心”“支付网关”“库存服务”,实际模块只有OrderController.java和硬编码的if (status == 1) { paySuccess(); }。这不是资源不全,而是“完整”二字背后藏着典型的能力断层:需求没吃透,设计就成空中楼阁;数据库字段没对齐业务语义,代码再漂亮也是黑匣子。本方案专为需要交毕设/课设、又不想被导师一句“这个表结构怎么没加逻辑删除字段?”当场问住的同学准备——它不是教你从零写 Spring Boot,而是给你一套能讲清来龙去脉、改得动、验得出、答得稳的闭环交付物。覆盖 Java Web 主流技术栈(Spring Boot 2.7 + MyBatis-Plus + Thymeleaf + MySQL 8),含真实可运行的 6 大核心模块、12 张规范数据表、4 级粒度文档(需求→概设→详设→说明书),所有内容均按高校课程设计评审标准组织,重点标注答辩高频质疑点与自检清单。
2. 需求分析与数据库设计:从业务动作反推数据契约,而不是照着电商网站抄功能列表
做电子商城,最容易犯的错是把“淘宝有购物车,我们就加购物车”当需求。真实需求必须来自角色动作与约束条件。比如“用户登录后可查看历史订单”,背后隐含:
- 用户身份需唯一标识(
user_id主键 +phone唯一索引); - 订单状态需支持多值流转(
order_status TINYINT COMMENT '0-待支付,1-已支付,2-已发货,3-已完成,4-已取消'); - 历史订单需分页且不可删(
is_deleted TINYINT DEFAULT 0 COMMENT '逻辑删除标记',而非DELETE FROM orders)。
本方案的需求分析文档(docs/需求分析_v2.1.docx)采用“角色-场景-规则”三元组编写,共梳理出 23 条原子需求,每条附带验收示例。例如针对“管理员可批量导出近30天订单”:
角色:后台管理员
场景:在订单管理页点击“导出Excel”按钮,选择日期范围“2024-05-01 至 2024-05-31”
规则:导出文件名格式为orders_20240501_20240531.xlsx,包含字段:订单号、下单时间、用户昵称、商品名称、数量、实付金额、订单状态;导出过程需显示进度条,超时120秒自动中断并提示“导出失败,请重试”。
2.1 数据库表结构设计:用 12 张表支撑全部业务,拒绝“一张大宽表”式野蛮生长
本方案采用领域驱动拆分 + 读写分离预判原则设计数据库,共 12 张表,全部满足第三范式(3NF),关键表结构如下:
-- 用户表:强制邮箱+手机号双因子验证,密码字段加盐存储 CREATE TABLE `user` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', `username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', `phone` VARCHAR(11) NOT NULL UNIQUE COMMENT '手机号', `email` VARCHAR(100) COMMENT '邮箱', `password` VARCHAR(100) NOT NULL COMMENT 'BCRYPT加密密码', `salt` VARCHAR(32) NOT NULL COMMENT '随机盐值', `status` TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `is_deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户基本信息表'; -- 商品表:支持SPU/SKU分离,为后续规格扩展留接口 CREATE TABLE `product` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `spu_code` VARCHAR(32) NOT NULL COMMENT 'SPU编码,如 P202405001', `name` VARCHAR(100) NOT NULL COMMENT '商品名称', `category_id` BIGINT NOT NULL COMMENT '所属分类ID', `brand` VARCHAR(50) COMMENT '品牌', `description` TEXT COMMENT '商品描述', `status` TINYINT DEFAULT 1 COMMENT '上架状态:0-下架,1-上架', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `is_deleted` TINYINT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- SKU表:真正库存和价格的载体,与product一对多 CREATE TABLE `product_sku` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `product_id` BIGINT NOT NULL COMMENT '关联商品ID', `sku_code` VARCHAR(32) NOT NULL UNIQUE COMMENT 'SKU编码,如 S202405001-01', `specification` VARCHAR(200) COMMENT '规格描述,如 "颜色:红色,尺寸:M"', `price` DECIMAL(10,2) NOT NULL COMMENT '销售价格', `stock` INT NOT NULL DEFAULT 0 COMMENT '库存数量', `image_url` VARCHAR(255) COMMENT '主图URL', `status` TINYINT DEFAULT 1 COMMENT '状态:0-停售,1-在售', `is_deleted` TINYINT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;提示:
product_sku表中specification字段采用 JSON 字符串存储(如{"颜色":"红色","尺寸":"M"}),而非拆成多张属性表。这是权衡——课设阶段无需支撑千万级SKU的复杂检索,JSON 方便前端直接渲染,且 MyBatis-Plus 支持@TableField(typeHandler = JacksonTypeHandler.class)自动序列化。若需答辩时解释,可强调:“当前设计满足‘规格组合可配置’需求,且为未来升级至独立属性中心预留了spec_template_id扩展字段”。
2.2 关键关系与约束落地:外键不是摆设,而是业务一致性的铁闸
很多同学建完表就扔,结果出现“订单里user_id=999,但user表里根本没有这条记录”。本方案所有外键均显式声明,并在 MyBatis-Plus 的@TableName注解中开启autoResultMap = true,确保 DAO 层操作时自动校验。以订单主表为例:
CREATE TABLE `order_master` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `order_no` VARCHAR(64) NOT NULL UNIQUE COMMENT '订单号,全局唯一', `user_id` BIGINT NOT NULL COMMENT '下单用户ID', `total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单总金额', `pay_amount` DECIMAL(10,2) DEFAULT 0 COMMENT '实付金额', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `is_deleted` TINYINT DEFAULT 0, CONSTRAINT `fk_order_user` FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;此处ON DELETE CASCADE并非鼓励物理删除,而是防止user表因误操作被删导致订单孤儿数据。实际业务中,user.is_deleted=1后,order_master中对应订单仍保留(因逻辑删除不触发外键级联),但查询时通过WHERE u.is_deleted = 0 AND o.is_deleted = 0双重过滤,保证数据一致性。这种设计在答辩时极易被问到“为什么不用软删除级联?”,答案很实在:“MyBatis-Plus 的逻辑删除插件不支持跨表级联更新,硬编码UPDATE order_master SET is_deleted=1 WHERE user_id IN (...)易漏,双重过滤虽多写一行SQL,但可控、可测、无歧义”。
2.3 避坑:数据库设计阶段最常翻车的 4 个点
现象 → 原因 → 解决
1. 本地 MySQL 8.0 连接报错Public Key Retrieval is not allowed
- 原因:MySQL 8.0 默认关闭公钥检索,而部分 JDBC 驱动(尤其旧版)未显式配置
allowPublicKeyRetrieval=true。 - 解决:修改
application-dev.yml中数据库 URL:spring: datasource: url: jdbc:mysql://localhost:3306/ecshop?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8mb4注意:
allowPublicKeyRetrieval=true仅用于开发环境,生产环境应使用 SSL 证书认证,课设答辩时可说明“此配置已标注为开发专用,上线前将替换为 X.509 证书双向认证”。
2. 导入 SQL 文件后中文乱码,SELECT出来全是问号
- 原因:MySQL 服务端默认字符集为
latin1,而建表语句中虽写了CHARSET=utf8mb4,但连接层未同步。 - 解决:执行以下三步(缺一不可):
-- ① 修改 MySQL 配置文件 my.ini(Windows)或 my.cnf(Linux),在 [mysqld] 下添加: [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci -- ② 重启 MySQL 服务 -- ③ 重新创建数据库并指定字符集: CREATE DATABASE ecshop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
3.user.phone字段设为 UNIQUE,但插入时提示“Duplicate entry '' for key 'phone'”
- 原因:MySQL 对空字符串
''和NULL的 UNIQUE 约束处理不同——多个NULL值允许共存,但多个''视为重复。若前端传参未校验,空字符串被插入,第二次再插就报错。 - 解决:在 MyBatis-Plus 的
UserMapper接口方法上加@Select("SELECT * FROM user WHERE phone = #{phone} AND phone != ''"),并在 Service 层增加判空:if (StringUtils.isBlank(user.getPhone())) { throw new BusinessException("手机号不能为空"); }
4. 使用 MyBatis-Plus 分页插件,Page<OrderVO>查询返回 total=0,但数据库明明有数据
- 原因:未在 Spring Boot 启动类上添加
@MapperScan("com.ecshop.mapper"),或OrderVO实体类未加@TableName("order_master")注解,导致 MP 无法定位正确表名生成 SQL。 - 解决:检查
OrderVO.java是否有:
并确认启动类:@Data @TableName("order_master") // 必须显式指定,VO类不继承BaseEntity时尤其重要 public class OrderVO { private Long id; private String orderNo; // ... 其他字段 }@SpringBootApplication @MapperScan("com.ecshop.mapper") // 扫描路径必须精确到mapper包 public class EcShopApplication { public static void main(String[] args) { SpringApplication.run(EcShopApplication.class, args); } }
3. 总体架构与模块划分:三层结构 + 四个核心域,拒绝“Controller 里写 SQL”的课设通病
本方案采用经典分层架构(Layered Architecture),严格遵循 Controller → Service → Mapper 三层职责,杜绝业务逻辑泄露到 Controller 或 SQL 硬编码在 Service 中。整体划分为四个核心业务域:用户中心、商品中心、订单中心、后台管理,各域边界清晰,通过 Spring 的@Service注解和包路径隔离(com.ecshop.user,com.ecshop.product等)。这种结构在答辩时能清晰回答“模块如何解耦”“接口如何复用”等高频问题。
3.1 架构图与技术栈选型依据:为什么是 Spring Boot 2.7 而不是 3.x?
架构图(文字描述版)如下:
┌─────────────────┐ HTTP/HTTPS ┌───────────────────────┐ │ 前端页面 │◄─────────────────►│ Spring Boot 2.7 │ │ (Thymeleaf模板) │ │ ├─ Controller层 │ └────────┬────────┘ │ ├─ Service层(含事务)│ │ │ └─ Mapper层(MyBatis-Plus)│ │ JDBC │ │ ▼ └───────────────────────┘ ┌─────────────────┐ │ MySQL 8.0 │ │ (12张规范表) │ └─────────────────┘选择Spring Boot 2.7(而非 3.x)是经过权衡的务实决策:
- 兼容性:MyBatis-Plus 3.5.x(本方案所用版本)对 Spring Boot 3.x 的 Jakarta EE 9+ 命名空间支持不完善,
@MapperScan在某些场景下失效; - 教学友好:2.7 的
application.properties配置项更直观(如spring.http.encoding.enabled=true),而 3.x 强制spring.web.encoding.charset=UTF-8,初学者易混淆; - 生态成熟:2.7 的 Actuator、DevTools、Security 配置文档极其丰富,某高校课程设计指导书明确要求“使用 Spring Boot 2.x 版本”。
注意:若你的课程要求必须用 Spring Boot 3.x,只需升级
pom.xml中的spring-boot-starter-parent版本,并将mybatis-plus-boot-starter升至 3.5.3.1+,同时将所有javax.*包引用(如javax.validation.Valid)替换为jakarta.*(如jakarta.validation.Valid)。本方案文档docs/升级指南.md中已提供完整替换清单。
3.2 四大核心模块功能与代码结构映射
| 模块 | 核心功能 | 关键代码路径 | 设计亮点 |
|---|---|---|---|
| 用户中心 | 注册/登录(手机+短信验证码)、个人资料管理、收货地址增删改查 | com.ecshop.user.controller.UserControllercom.ecshop.user.service.impl.UserServiceImpl | 登录态使用HttpSession(非 JWT),符合课设轻量级要求;短信验证码存 Redis,过期自动清理 |
| 商品中心 | 分类浏览、商品搜索(LIKE 模糊匹配)、商品详情、加入购物车(内存实现) | com.ecshop.product.controller.ProductControllercom.ecshop.product.service.impl.ProductServiceImpl | 搜索支持name和description双字段OR查询,SQL 中用CONCAT防止全表扫描 |
| 订单中心 | 购物车结算、创建订单、订单支付模拟(状态变更)、订单查询(用户/管理员双视角) | com.ecshop.order.controller.OrderControllercom.ecshop.order.service.impl.OrderServiceImpl | 创建订单时加synchronized(this)锁库存(课设简化版),避免超卖;支付成功后发站内信通知用户 |
| 后台管理 | 用户管理、商品管理(SPU/SKU分离)、订单审核、数据统计(近7日订单量折线图) | com.ecshop.admin.controller.AdminControllercom.ecshop.admin.service.impl.AdminServiceImpl | 统计图表用 ECharts + Ajax,后端返回List<Map<String, Object>>,前端直接渲染,无额外框架 |
3.3 关键模块实现:以“创建订单”为例,看事务、锁、日志如何协同
OrderServiceImpl.createOrder()是最能体现工程能力的代码段。它不是简单INSERT INTO order_master,而是包含库存校验、订单生成、库存扣减、日志记录四步,且必须在同一个数据库事务中完成。以下是精简后的核心逻辑(含关键注释):
@Transactional(rollbackFor = Exception.class) @Override public Result createOrder(Long userId, List<OrderItemDTO> items) { // 1. 校验用户存在且启用 User user = userMapper.selectById(userId); if (user == null || user.getStatus() != 1) { return Result.fail("用户不存在或已被禁用"); } // 2. 校验商品库存(逐个SKU检查) for (OrderItemDTO item : items) { ProductSku sku = productSkuMapper.selectById(item.getSkuId()); if (sku == null || sku.getStock() < item.getQuantity()) { return Result.fail("商品【" + sku.getName() + "】库存不足"); } } // 3. 生成订单号(防并发:时间戳+用户ID哈希+随机数) String orderNo = OrderNoGenerator.generate(userId); // 4. 扣减库存(悲观锁:SELECT ... FOR UPDATE) // 注意:必须在事务内执行,且 WHERE 条件要走索引(sku_id为主键,肯定走) for (OrderItemDTO item : items) { int updated = productSkuMapper.decreaseStock(item.getSkuId(), item.getQuantity()); if (updated == 0) { // 库存被其他请求抢先扣完,回滚整个事务 throw new RuntimeException("库存扣减失败,可能已被抢购"); } } // 5. 插入订单主表 OrderMaster master = new OrderMaster(); master.setOrderNo(orderNo); master.setUserId(userId); master.setTotalAmount(calculateTotal(items)); master.setStatus(OrderStatus.WAIT_PAY.getCode()); orderMasterMapper.insert(master); // 6. 插入订单明细表(批量插入提升性能) List<OrderItem> orderItems = buildOrderItems(master.getId(), items); orderItemMapper.insertBatch(orderItems); // 7. 记录操作日志(异步,避免阻塞主流程) logService.asyncLog("用户" + userId + "创建订单:" + orderNo, LogType.ORDER_CREATE); return Result.success("订单创建成功", orderNo); }关键设计说明:
- 事务控制:
@Transactional保证四步原子性,任何一步失败(如库存不足、插入异常)都会回滚; - 库存锁:
decreaseStock方法对应的 XML SQL 为<update id="decreaseStock">UPDATE product_sku SET stock = stock - #{quantity} WHERE id = #{skuId} AND stock >= #{quantity}</update>,利用AND stock >= #{quantity}做乐观锁兜底,比纯FOR UPDATE更安全; - 日志异步:
logService.asyncLog()内部使用@Async注解,需在启动类加@EnableAsync,避免日志写入慢拖垮订单流程; - 订单号生成:
OrderNoGenerator.generate()返回类似20240520153022888123456789的字符串,前14位为yyyyMMddHHmmss,后10位为userId % 10000000000取模+随机数,保证全局唯一且可追溯。
3.4 避坑:模块开发中最容易被忽略的 3 个“隐形坑”
现象 → 原因 → 解决
1. Controller 返回Result.success(data),前端收到却是{"code":200,"msg":"成功","data":null}
- 原因:
Result类中data字段用了Object类型,但未加@JsonInclude(JsonInclude.Include.NON_NULL)注解,当data=null时 Jackson 仍会序列化"data":null;更常见的是data是一个未加@Data的 POJO,getter 方法未生成,Jackson 无法反射获取值。 - 解决:在
Result.java顶部加:
并确保所有 VO/DTO 类都用 Lombok 的@JsonInclude(JsonInclude.Include.NON_NULL) public class Result<T> { private Integer code; private String msg; private T data; // 泛型T,Jackson能自动处理 // ... 构造方法、getter/setter }@Data或手动写全 getter/setter。
2. 后台管理页点击“删除用户”,浏览器弹出 405 Method Not Allowed
- 原因:前端用
<a href="/admin/user/delete?id=123">发 GET 请求,但后端AdminController.deleteUser()方法用@PostMapping注解,要求 POST。 - 解决:统一 REST 风格,删除操作必须用 POST(防 CSRF):
后端:<!-- 前端:用 form 提交 --> <form action="/admin/user/delete" method="post" style="display:inline;"> <input type="hidden" name="id" value="123"> <button type="submit" onclick="return confirm('确定删除?')">删除</button> </form>@PostMapping("/delete") @ResponseBody public Result deleteUser(@RequestParam Long id) { // ... 逻辑 }
3. 商品搜索功能输入“手机”,返回一堆“手机壳”“手机膜”,但没返回“智能手机”
- 原因:SQL 中用了
WHERE name LIKE '%手机%',但description字段未参与搜索,且未对关键词做分词。 - 解决:改为
OR连接双字段,并用CONCAT拼接提升相关性:
此 SQL 优先展示“手机”开头的商品(如“智能手机”),其次才是中间包含的(如“手机壳”),最后是结尾包含的(极少),符合用户预期。@Select("<script>" + "SELECT * FROM product " + "WHERE (name LIKE CONCAT('%', #{keyword}, '%') OR description LIKE CONCAT('%', #{keyword}, '%')) " + "AND status = 1 AND is_deleted = 0 " + "ORDER BY CASE WHEN name LIKE CONCAT('#{keyword}', '%') THEN 1 " + " WHEN name LIKE CONCAT('%', '#{keyword}', '%') THEN 2 " + " ELSE 3 END " + "LIMIT #{page.size} OFFSET #{page.current}" + "</script>") Page<Product> search(@Param("keyword") String keyword, @Param("page") Page<Product> page);
4. 关键模块实现与文档配套:让代码、数据库、文档三者互相印证,形成答辩证据链
一份能过关的毕设/课设,绝不是代码能跑就行,而是代码里写的逻辑,能在数据库表结构里找到字段支撑,还能在需求文档里翻到对应条款,最后在说明书里看到操作截图。本方案的四大文档(需求分析、概要设计、详细设计、说明书)与代码完全对齐,形成闭环证据链。下面以“用户注册”功能为例,演示如何用这套结构应对答辩质询。
4.1 需求分析文档:用“可测试语言”写需求,拒绝模糊描述
docs/需求分析_v2.1.docx中“用户注册”章节原文节选:
ID-R001
标题:新用户可通过手机号+短信验证码完成注册
前置条件:用户未登录,且该手机号未被注册过
基本流程:
- 用户在注册页输入手机号,点击“获取验证码”;
- 系统向该手机号发送 6 位数字验证码(有效期 5 分钟),同一手机号 60 秒内不可重复发送;
- 用户输入验证码及密码(8-16 位,含大小写字母+数字),点击“注册”;
- 系统校验验证码有效性、密码强度、手机号唯一性;
- 校验通过,创建用户记录,状态为“启用”,返回成功页。
验收标准:
- 输入已注册手机号,点击“获取验证码”后提示“该手机号已被注册”;
- 输入错误验证码,点击“注册”后提示“验证码错误,请重试”;
- 密码为“12345678”,点击“注册”后提示“密码强度不足:需包含大小写字母及数字”。
提示:答辩时若被问“为什么验证码有效期设为5分钟?”,可答:“根据《电信网码号资源管理办法》及主流平台实践(如微信、支付宝),短信验证码有效期通常为 3-10 分钟,5 分钟是兼顾安全性与用户体验的平衡点;本方案在 Redis 中设置
EXPIRE sms:138****1234 300精确控制”。
4.2 概要设计文档:用 UML 类图+时序图说清“谁调谁”,不画大饼
docs/概要设计_v1.3.pdf中“用户注册”模块包含:
- 类图:标出
RegisterController(接收请求)、RegisterService(协调流程)、SmsService(发短信)、UserMapper(持久化)四类,箭头标明依赖方向; - 时序图(文字描述):
此图直接对应代码中RegisterController -> RegisterService: checkPhoneExists(phone) RegisterService -> UserMapper: selectOne(WRAPHONE=phone) UserMapper --> RegisterService: user or null RegisterService -> SmsService: sendSms(phone, code) SmsService -> Redis: setex sms:phone code 300 RegisterService -> RegisterController: success/failRegisterService.checkAndSendSms()方法的调用链,答辩时可指着图说:“您看,Controller 不直接操作 Redis,而是通过 Service 协调,这正是分层架构的价值”。
4.3 详细设计文档:给出每一行关键代码的“为什么”,不是贴代码
docs/详细设计_v1.0.docx中“短信验证码发送”章节:
方法名:
SmsService.sendSms(String phone, String code)
输入参数:phone(11位手机号,已通过正则^1[3-9]\\d{9}$校验)、code(6位随机数字)
处理逻辑:
- 构造 Redis Key:
sms:+ phone(如sms:13812345678);- 调用
redisTemplate.opsForValue().set(key, code, Duration.ofSeconds(300));- 调用第三方短信 SDK(本方案模拟为
System.out.println("发送短信至" + phone + ",验证码:" + code));
输出:true(发送成功),false(Redis 写入失败)
异常处理:捕获RedisConnectionFailureException,记录 ERROR 日志并返回false,前端提示“系统繁忙,请稍后再试”。
注意:文档中明确写出“本方案使用 Redis 存储验证码,而非数据库”,理由是“验证码为临时凭证,生命周期短,Redis 的高性能与过期特性更匹配;若用数据库,需额外建表、写定时任务清理,增加课设复杂度”。
4.4 说明书文档:图文并茂的操作指南,让导师 3 分钟上手验证
docs/说明书_v1.2.pdf中“用户注册”章节:
- 步骤1:访问
http://localhost:8080/register,截图注册页(含手机号输入框、获取验证码按钮、密码输入框); - 步骤2:输入
13812345678,点击“获取验证码”,截图控制台日志INFO c.e.s.SmsService - 发送短信至13812345678,验证码:123456; - 步骤3:输入验证码
123456和密码Abc12345,点击“注册”,截图跳转至http://localhost:8080/login?registerSuccess=true成功页; - 验证:登录 MySQL,执行
SELECT * FROM user WHERE phone = '13812345678';,截图返回结果(含id,username,status=1,create_time)。
提示:说明书中的所有截图,均来自本方案真实运行环境,路径、端口、数据均与代码一致。答辩时可现场打开 PDF,对照导师电脑上的浏览器和数据库,实时演示验证过程,可信度拉满。
4.5 避坑:文档与代码脱节的 3 个致命伤
现象 → 原因 → 解决
1. 文档里写“支持微信支付”,但代码里只有PayService.payByAlipay()方法
- 原因:文档编写早于代码实现,或多人协作时未同步更新。
- 解决:建立“文档-代码映射表”,在
docs/README.md中维护:文档章节 对应代码文件 状态 备注 3.2 微信支付接入 com.ecshop.pay.WechatPayService.java✅ 已实现 使用沙箱环境,APPID 为 wx1234567890abcdef4.1 订单导出Excel com.ecshop.admin.service.ExportService.java⚠️ 待测试 依赖 Apache POI 5.2.4,已加入 pom 此表每次提交代码前必须更新,答辩时可作为“工作量证明”直接展示。
2. 需求文档中“用户头像支持 JPG/PNG 格式”,但上传时 PNG 图片报错Unsupported image type
- 原因:
ImageIO.read()方法不支持某些 PNG 编码变种(如带 Alpha 通道的 PNG)。 - 解决:改用
BufferedImage+ImageIO.getImageReadersBySuffix()安全读取:
并在需求文档“附件要求”中补充:“头像文件需为标准 PNG/JPG,不支持 WebP、GIF 动图”。public static BufferedImage safeReadImage(InputStream in) throws IOException { Iterator<ImageReader> readers = ImageIO.getImageReadersBySuffix("png"); if (!readers.hasNext()) { throw new IOException("No PNG reader found"); } ImageReader reader = readers.next(); ImageInputStream iis = ImageIO.createImageInputStream(in); reader.setInput(iis, true); return reader.read(0); // 读取第一帧 }
3. 说明书截图里的 URL 是http://192.168.1.100:8080,但导师电脑 IP 不同,打不开
- 原因:截图时未使用
localhost或127.0.0.1。 - 解决:所有截图统一用
http://localhost:8080,并在说明书首页加说明:“本说明书所有截图基于本地开发环境,访问地址均为http://localhost:8080,请确保您的开发服务器已启动”。
5. 验收与答辩:用“三步自检法”和“五问预演表”,把答辩变成成果展示
答辩不是考试,而是你向导师展示“我不仅做了,还做得明白、改得动、验得出”。很多同学输在临场慌乱,其实只要提前走通三步自检、预演五问,就能把答辩变成单方面输出。我带过的某高校课程设计小组,A同学用这套方法,从被问住三次到最终获评“优秀”,核心就是把答辩拆解成可执行动作。
5.1 三步自检法:答辩前 2 小时必须完成的硬核检查
第一步:环境自检(30 分钟)
目标:确保导师电脑上 5 分钟内能跑起来。
- 下载 JDK 8/11、MySQL 8.0、IDEA 社区版(免安装绿色版),全部放在 U 盘根目录;
- 在导师电脑上解压
ecshop.zip,双击run_env_setup.bat(脚本内容:自动配置 JAVA_HOME、启动 MySQL 服务、导入ecshop.sql); - 浏览器访问
http://localhost:8080,注册一个账号,下一笔订单,进入后台查看该订单——全程不报错即通过。
血泪经验:曾有同学答辩时发现导师电脑没装 MySQL,现场下载安装耗时
本文还有配套的精品资源,点击获取