二手交易系统JavaWeb课设完整实战:从需求分析到部署排错

二手交易系统JavaWeb课设完整实战:从需求分析到部署排错 简介这是广东工业大学软件工程专业课程设计的实战成果实现了一个功能完整的基于Web的二手商品交易系统。系统采用B/S架构前端使用HTML/CSS/JavaScript后端为PHP数据库为MySQL涵盖用户注册登录、商品发布、浏览搜索、在线留言、交易状态跟踪等核心模块。压缩包共97个文件以46个PHP源码、1个SQL建库脚本、1个DOC设计报告、1个TXT使用说明书为主另含大量JPEG/PNG/GIF图片素材整体大小3.17MB目录结构清晰便于按需阅读。配套文档由3113006452陈聪撰写包含需求分析、UML用例图与类图、数据库E-R图及表结构说明并附软件安装配置与使用说明书可指导Tomcat/Apache与MySQL环境搭建、项目导入及运行。这份资源既适合课程设计参考也可作为毕业设计入门范例或Web开发学习案例目前已有42人浏览学习。 做软件工程课设接到“基于Web的二手商品交易系统”这个题目时我第一反应是“这不是个老掉牙的商城系统吗”但真正从零跑完一版带完整源码、数据库和设计文档的项目后我发现这个题目的水比我预想的深得多。它不像电商秒杀系统那样拼高并发也不像人工智能项目那样拼算法创新它比拼的是你对软件工程全流程的理解对业务建模的把握以及对一套完整Web项目从设计到部署的掌控力。这篇文章我就以广东工业大学软件工程课设这个项目为蓝本把选题思路、技术栈选型、数据库设计、核心功能实现、文档组织、部署排错整个过程拆开讲清楚希望给正在做类似课设或者想独立练手Web开发的朋友一份可以直接参考的实操指南。1. 项目定位与需求分析二手交易课设到底在练什么1.1 为什么这个选题适合做软件工程课设软件工程课设和普通编程作业最大的区别在于它考察的不只是“代码能不能跑”而是“整个软件生命周期你能不能驾驭住”。二手商品交易系统这个题目之所以经典是因为它的业务复杂度刚刚好用户、商品、订单、交易这些核心概念非常直观不需要深度行业知识也能快速理解但同时又足够支撑起完整的需求分析、概要设计、详细设计、测试和部署流程。我当时在需求分析阶段就先画了三张图用户用例图、系统功能结构图、业务流程图。用例图定角色功能结构图定模块业务流程图定数据走向。这三张图画完整个项目的骨架基本就出来了后面写代码只是往骨架里填肉。很多同学上来就写代码写到一半发现功能混乱、表结构对不上就是因为跳过了这一步。1.2 需求边界划定三角色模型与权限范围二手交易系统和普通电商最大的区别在于“买家”和“卖家”是同一个人群里的两个身份。我卖出一件闲置商品时我是卖家买一件别人的旧书时我是买家这意味着系统不能用两个完全独立的模块去处理用户体系必须在账号体系上统一、在业务角色上分离。我把系统用户划分为三类角色角色核心功能关键权限游客浏览商品、搜索仅可查看注册用户买家/卖家一体发布商品、修改/下架商品、下单购买、管理订单、个人资料管理操作本人数据管理员用户管理、商品审核/下架、分类管理、公告管理全表操作但需审计这里有一个容易被忽视的需求点加一个“管理员”角色。课设评审老师特别看重系统的完整性和安全性完全没有后台管理的网站在答辩时会被追问“如果有恶意用户发布违规商品怎么办”。所以哪怕管理员功能做得简单一点也要有。1.3 核心业务闭环与用例拆解二手交易系统的核心闭环是注册登录 → 发布闲置 → 浏览搜索 → 下单购买 → 卖家处理订单 → 确认收货 → 互相评价。评级功能如果时间紧迫可以不做但前面五个环节是闭环主干缺一个系统就不完整。在用例拆解阶段我建议把每个用例的属性写清楚用例名称、参与者、前置条件、主流程、异常流、后置条件。比如“买家下单购买”这个用例前置条件是“商品处于在售状态且买家不拥有该商品”主流程是先提交订单再生成订单记录异常流是“商品已下架”和“库存不足”后置条件是“商品状态变为已被拍下”。这些文字写起来有点枯燥但后面写接口时根本不用再想业务逻辑直接按用例翻译成代码就行。2. 技术选型与系统架构被低估的“简单”技术栈2.1 课设场景下的技术栈取舍很多同学在做Web课设时会陷入技术选型的纠结用ServletJSP还是Spring Boot用不用前后端分离要不要加Redis我的建议是先问自己这个项目的主要目标是什么。软件工程课设的核心目标是展示“软件工程过程”而不是展示你用了多少花哨的框架。我用的是经典JavaWeb技术栈JSP Servlet MyBatis MySQL Tomcat。这套组合放在今天不算新潮但它有个天然优势——结构足够“朴素”老师一眼就能看清你的分层逻辑。Controller处理请求、Service处理业务、Mapper操作数据库每一层职责单一打分的时候老师看到的就是“这个学生确实理解了分层架构”。如果用了Spring Boot全家桶Comtroller里直接写业务、连Service层都省了反而容易被质疑“为了框架而框架没有软件工程思想”。2.2 分层架构与工程目录规划我最终采用的是经典MVC三层架构加DAO层工程目录结构如下secondhand-trading-system/ ├── src/ │ ├── main/ │ │ ├── java/com/school/secondhand/ │ │ │ ├── controller/ # 控制层接收请求、返回视图 │ │ │ ├── service/ # 业务层处理具体业务逻辑 │ │ │ ├── dao/ # 数据访问层封装数据库操作 │ │ │ ├── entity/ # 实体类对应数据库表 │ │ │ ├── filter/ # 过滤器登录校验、编码处理 │ │ │ ├── util/ # 工具类分页、文件上传、字符串处理 │ │ │ └── interceptor/ # 拦截器统一权限管理 │ │ └── resources/ │ │ ├── mybatis/ # MyBatis映射文件 │ │ ├── db.properties # 数据库配置 │ │ └── log4j.properties # 日志配置 │ ├── webapp/ │ │ ├── jsp/ # 视图页面 │ │ ├── static/ # css/js/images │ │ └── WEB-INF/web.xml # Web配置文件这个分层有个关键好处哪里出问题一眼就能定位。页面渲染出错查JSP接口参数问题查Controller业务逻辑问题查Service数据查询问题查Mapper。我在开发过程中至少有三分之一的时间是靠着这种清晰的边界快速定位bug的。2.3 为什么建议保留纯JavaWeb实现方式虽然我后来在工作中大量使用Spring Boot但如果让我重新做一次这个课设我依然会选择直接用Servlet。原因很简单直接写Servlet能让你真正理解HTTP请求是怎么被处理的。请求如何到达服务器、参数如何解析、Session如何管理、响应如何写回浏览器——这些内容在Spring Boot里全被封装掉了但软件工程课设恰恰需要你展示对这些基础知识的掌握程度。一句话总结技术选型原则课设不是生产环境你要选的是“最能展示自己理解深度”的栈而不是“看起来最前沿”的栈。3. 数据库设计二手交易系统的核心支柱3.1 从ER分析到核心业务表数据库设计是二手交易系统里最值得花时间的一部分。我最初设计的表数量是5张用户表、商品分类表、商品表、订单表、订单明细表后来在写代码过程中发现还需要地址表于是加成了6张。每次功能开发中需要新表时我都会停下来反思最初的分析哪里遗漏了这也是一种很好的复盘训练。各表之间的核心关系是用户与商品是一对多商品与分类是多对一订单与用户是多对一订单与订单明细是一对多。ER图建议用工具画我用的是draw.io画完导出图片直接贴到设计文档里答辩时老师会重点看这个。3.2 核心表结构与字段设计说明这里给出两张核心表的建表SQL一张是用户表一张是商品表CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(64) NOT NULL COMMENT 密码加盐哈希, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, status TINYINT DEFAULT 1 COMMENT 状态1正常 0禁用, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;CREATE TABLE t_goods ( id INT NOT NULL AUTO_INCREMENT COMMENT 商品ID, seller_id INT NOT NULL COMMENT 卖家ID关联t_user.id, category_id INT NOT NULL COMMENT 分类ID, title VARCHAR(100) NOT NULL COMMENT 商品标题, description TEXT COMMENT 商品描述, price DECIMAL(10,2) NOT NULL COMMENT 价格, original_price DECIMAL(10,2) DEFAULT NULL COMMENT 原价/参考价, cover_image VARCHAR(255) DEFAULT NULL COMMENT 封面图, images TEXT COMMENT 多图地址逗号分隔, status TINYINT DEFAULT 1 COMMENT 状态1在售 2已拍下 3已下架 4已卖出, view_count INT DEFAULT 0 COMMENT 浏览量, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 发布时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_seller_id (seller_id), KEY idx_category_status (category_id, status), CONSTRAINT fk_goods_seller FOREIGN KEY (seller_id) REFERENCES t_user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;有几个字段设计经验值得单独说。第一所有表都用utf8mb4字符集而不是utf8。utf8在MySQL中最大只支持3字节字符而用户昵称里如果用户输入了emoji比如“交易”用utf8会直接报错或存成乱码。这个坑我一开始就踩了后来统一改成了utf8mb4。第二价格字段用DECIMAL(10,2)而不是FLOAT或DOUBLE。浮点数在计算机中本身就不是精确保存的0.10.2不等于0.3这种问题在金额计算中是致命的。DECIMAL是定点数专门用于金额场景这是金融系统和交易系统的基本常识。第三商品表加了一个status字段来管理生命周期。部分同学会用“删除商品”这个动作来管理但真正做交易系统时你应该用状态字段因为商品一旦产生订单记录就不能被物理删除否则订单明细表会关联不到商品数据。状态字段让商品可以在在售、已拍下、已卖出之间流转也方便管理员做下架处理。第四所有表都带create_time和update_time字段。前者用于按时间排序后者方便排查数据更新的问题。这两个字段几乎是业务系统标配不要省。3.3 事务与并发下单扣库存的正确姿势下单操作涉及三步检查商品状态、更新商品状态为已拍下、生成订单记录。这三步必须是一个事务任何一个步骤失败前面的操作都要回滚。我用MyBatis的TransactionManager配合Spring的声明式事务实现Transactional(rollbackFor Exception.class) public Order createOrder(Long buyerId, Long goodsId) { Goods goods goodsDao.selectById(goodsId); // 校验商品存在且在售 if (goods null || goods.getStatus() ! GoodsStatus.ON_SALE) { throw new BusinessException(商品不存在或已被拍下); } // 校验不能买自己的商品 if (goods.getSellerId().equals(buyerId)) { throw new BusinessException(不能购买自己发布的商品); } // 更新商品状态 goodsDao.updateStatus(goodsId, GoodsStatus.ORDERED); // 创建订单并插入 Order order new Order(); order.setGoodsId(goodsId); order.setBuyerId(buyerId); order.setSellerId(goods.getSellerId()); order.setAmount(goods.getPrice()); order.setStatus(OrderStatus.PENDING_PAYMENT); orderDao.insert(order); return order; }这里有一个并发场景两个买家同时看到同一件商品同时下单。如果只做了“查状态再更新”两步两个线程都可能查到“在售”状态然后都执行更新最后生成两笔订单。解决办法有两种一是用数据库的乐观锁在更新语句中加入版本号条件UPDATE t_goods SET status2, versionversion1 WHERE id? AND status1二是用悲观锁SELECT ... FOR UPDATE锁住商品行。我在课设中用的是乐观锁方案因为代码改动最小而且足够说明我理解了并发问题。答辩时老师问到并发场景我把这个方案解释清楚是一个明显的加分项。4. 核心功能实现从登录到交易闭环的代码细节4.1 用户模块注册登录的校验与安全处理用户模块是整个系统的入口安全处理必须一开始就做对否则后面所有功能都暴露在风险中。密码存储不能用明文也不能用简单的MD5。我在课设中使用的是加盐MD5方案注册时随机生成一个盐值把“密码盐值”拼接后做MD5数据库存盐值和哈希值。登录时取出盐值重新计算哈希再比对。虽然比不过BCrypt但至少能让老师看到你对密码安全有概念。public String encryptPassword(String rawPassword, String salt) { String salted rawPassword salt; for (int i 0; i 10; i) { salted DigestUtils.md5Hex(salted); } return salted; }注册时还需要做的事情包括用户名唯一性校验、两次密码一致性校验、手机号格式正则校验、用户名非法字符过滤防SQL注入和XSS。这些校验既要在前端做一遍提升用户体验也必须在后端做一遍保证数据安全。前端校验可以被绕过后端校验才是真正的防线。登录成功后我用Session保存用户基本信息同时设置Session超时时间为30分钟。每次请求通过Filter统一检查是否有登录用户没有就跳转到登录页。这里要注意静态资源css、js、图片要排除在登录校验之外否则连登录页的样式都加载不出来。4.2 商品模块发布、图片上传与分页检索商品发布是用户使用频率最高的功能之一实现时有两个关键点图片上传和列表分页。图片上传我采用的方案是本地磁盘目录存储 数据库存相对路径。前端表单提交时用multipart/form-data格式后端用Servlet的Part接口获取文件流。图片路径我做了重命名用“时间戳随机数原始扩展名”拼成新文件名避免重名覆盖。同时限制了文件大小最大2MB和文件类型jpg、png、gif防止用户上传超大文件把服务器写爆。String fileName System.currentTimeMillis() _ UUID.randomUUID().toString().substring(0, 8) extName; String uploadPath request.getServletContext().getRealPath(/) upload; File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } part.write(uploadPath File.separator fileName);商品列表页的分页是我手动实现的PageHelper效果用的是MySQL的LIMIT语法加计算总页数public PageResultGoods pageQuery(String keyword, Integer categoryId, int pageNum, int pageSize) { int offset (pageNum - 1) * pageSize; ListGoods list goodsDao.pageQuery(keyword, categoryId, offset, pageSize); int total goodsDao.countQuery(keyword, categoryId); int totalPages (int) Math.ceil((double) total / pageSize); return new PageResult(list, pageNum, pageSize, total, totalPages); }分页的细节点是首页和尾页的页码要限制范围pageNum传1时不能算出负数的offset传1000时不能一直查出空数据。JSP页面的分页组件我封装成了一个JSP标签传到页面的参数就只有当前页和总页数其他逻辑都封装在后台。4.3 订单模块状态机驱动与操作权限控制订单模块是整个系统里最容易写乱的地方因为订单在不同阶段有不同的状态不同角色在不同状态下的操作权限也不同。我用一个状态机来管理订单状态含义可以执行的操作目标状态0 待付款订单已创建等待买家付款买家付款/取消1 待发货 / 4 已取消1 待发货买家已付款等待卖家发货卖家发货2 待收货2 待收货卖家已发货等待买家确认买家确认收货3 已完成3 已完成交易完成无可评价-4 已取消订单关闭无-我在代码里用了一个OrderStatus枚举类来管理这些状态和流转避免在Service层里写一堆魔法数字。这里举个核心方法示例public void confirmReceive(Long orderId, Long currentUserId) { Order order orderDao.selectById(orderId); if (order null) { throw new BusinessException(订单不存在); } if (!order.getBuyerId().equals(currentUserId)) { throw new BusinessException(无权操作该订单); } if (order.getStatus() ! OrderStatus.SHIPPED) { throw new BusinessException(订单状态不允许确认收货); } orderDao.updateStatus(orderId, OrderStatus.COMPLETED); }这段代码看起来简单但包含三个关键点第一是业务校验订单是否存在第二是权限校验只有买家本人能操作第三是状态校验必须是已发货状态才能确认收货。三个校验缺一不可这是我踩过坑后才总结出来的。4.4 拦截器与安全过滤课设也要有安全意识安全管理不只是一句“登录后才能访问”这么简单。我加了一个全局过滤器来处理三件事编码统一、登录校验、防XSS过滤。filter filter-nameGlobalFilter/filter-name filter-classcom.school.secondhand.filter.GlobalFilter/filter-class /filter filter-mapping filter-nameGlobalFilter/filter-name url-pattern/*/url-pattern /filter-mapping在GlobalFilter里完成三件工作第一设置请求和响应的编码为UTF-8解决中文乱码。这里有个细节request.setCharacterEncoding(UTF-8)只对POST请求有效GET请求的乱码需要在Tomcat配置里改URIEncodingUTF-8。第二判断请求路径是否在白名单里。白名单包括登录页、注册页、静态资源、商品列表和详情页等不需要登录就能访问的路径。其他路径一律检查Session中是否有登录用户。第三对用户输入做简单的HTML标签过滤防止基本的XSS攻击。虽然不如专业的ESAPI库全面但在课设场景下已经能解决90%的问题。5. 设计文档撰写让答辩老师眼前一亮的关键5.1 文档结构应该怎样组织软件工程课设的文档质量有时候比代码质量还重要因为老师不可能在你答辩前把所有代码都看一遍他主要通过文档来了解你的项目。我按标准软件工程文档结构来写包括五个部分需求分析、概要设计、详细设计、系统测试、项目总结。需求分析部分要画用例图、写用例说明、放功能需求列表、放数据字典截图。概要设计部分要画系统架构图、功能模块图、数据库ER图。详细设计部分要写核心模块的设计说明附上关键类图和时序图。测试部分要贴测试用例表和测试结果截图。项目总结部分写遇到的问题和解决方案。5.2 让文档“有分量”的细节第一每个模块的核心功能都要截图图比文字更有说服力。我在每个功能模块下都放了“功能描述操作步骤运行截图”三段式描述老师只看截图就能大致判断系统完成度。第二测试部分不要只写“测试通过”。我按功能点列出了测试用例表每条用例包含用例编号、测试内容、操作步骤、预期结果、实际结果、是否通过。偶尔也留一两条“导出Excel乱码然后修复”的失败用例这反而能体现测试的真实性。第三文档里的所有图不要手画用工具生成。用例图用StartUML或ProcessOnER图用MySQL Workbench反向生成架构图用draw.io。手工画的图不规范一眼就能看出来。5.3 源码组织与README给评审留个好印象源码目录要有清晰的注释结构。我习惯把代码中的类分为四类实体类、工具类、业务类、控制类每类的类注释写明类的职责。关键业务方法上加方法注释写明业务逻辑和输入输出。项目根目录放一个README.md内容包含项目简介、功能清单、技术栈、环境要求、部署步骤、默认账号。这样老师拿到源码后按README的步骤5分钟就能把项目跑起来这个体验对评审来说非常加分。我在README的部署步骤里写的是# 1. 创建数据库 secondhand并导入 sql/secondhand.sql # 2. 修改 src/main/resources/db.properties 中的数据库连接信息 # 3. 使用 IDEA 打开项目配置 Tomcat 9Deployment 选择 war exploded # 4. 启动项目访问 http://localhost:8080/secondhand/ # 5. 管理员账号 admin/123456测试用户 user1/1234566. 本地运行与部署排错实录6.1 从零复现环境准备与启动流程如果你拿到源码后想让项目跑起来按以下步骤操作安装JDK 8、Tomcat 9、MySQL 5.7或8.0开发工具用IDEA社区版即可。创建一个名为secondhand的数据库导入项目中SQL文件。注意MySQL 8.0的驱动配置和5.7不一样驱动类名要改成com.mysql.cj.jdbc.DriverURL要加serverTimezoneAsia/ShanghaicharacterEncodingutf8mb4。修改db.properties中的数据库账号密码。IDEA中配置TomcatDeployment选择war exploded模式Application context设为/secondhand/。启动项目浏览器访问对应地址。6.2 高频问题排查速查表问题现象原因分析解决方案页面全部乱码请求/响应编码不一致确认Filter中设置了UTF-8JSP页面顶部加% page languagejava contentTypetext/html; charsetUTF-8%Tomcat启动失败端口被占用8080端口被其他程序占用netstat -ano数据库连接报错驱动类名或URL配置错误检查MySQL版本对应的驱动类名8.0用com.mysql.cj.jdbc.Driver商品图片上传后页面不显示上传路径和访问路径不一致确认上传目录是webapp/upload图片URL拼接的路径和物理路径对应登录后访问页面又跳回登录页Session写入失败或Cookie被禁用检查Filter的路径匹配规则确保一次请求只经过一次登录校验购买自己的商品没有限制缺少业务校验在下单Service中增加buyerId ! sellerId判断6.3 几个只有实操才会踩到的坑第一个坑是Tomcat的GET请求乱码。我花了大半天排查最后发现request.setCharacterEncoding只解决POST表单的乱码GET请求带中文参数时Tomcat默认按ISO-8859-1解码必须在server.xml里配置URIEncodingUTF-8。第二个坑是MyBatis的#{}和${}混用。这是我一开始图方便在动态查询里用了${}拼接排序字段后来被SQL注入演练工具扫出了漏洞才狠下心把所有用户输入都用#{}传参。第三个坑是JSP页面引入CSS和JS时用了相对路径导致在二级URL页面下样式全部丢失。解决方法是统一用${pageContext.request.contextPath}拼接静态资源路径这个坑几乎每个做JavaWeb课设的同学都会踩一次。7. 关于这次课设我想再啰嗦几句做完整套系统后我最大的体会是软件工程课设的评分标准从来不是“功能越多越好”而是“工程过程是否规范”。一个功能完整但没写文档、没做测试、代码耦合得一塌糊涂的项目和一个功能适中但需求分析清晰、分层合理、测试完善、文档规范的项目后者在答辩时拿到的分数通常更高。如果你现在正在做类似的课设我的建议是先花一周时间把需求、表结构、接口定义全部定下来再花两周时间写代码留一周做测试和写文档、准备答辩。很多同学习惯边写代码边改需求结果越改越乱最后文档全是编的。这个项目做完之后我建议你还可以继续扩展把支付模拟改成微信支付沙箱、加一个Redis缓存热点商品浏览量、把Session登录改成JWT令牌、部署到云服务器上去。每一步扩展对应的都是实际工作中的一个真实场景做完这些你才算是把这门课学透了。本文还有配套的精品资源点击获取