Java 商品交易实验(第二版)

Java 商品交易实验(第二版)

一、项目概述

本次实验完成的是一个商品交易类后端项目,项目名为 new_shop_finall。项目整体采用 Spring Boot 开发,主要提供用户注册登录、商品发布与查询、商品点赞、订单创建、订单查询、管理员管理等功能。项目没有做复杂的前端页面,重点放在后端接口、数据库操作、登录鉴权、事务处理和缓存使用上。

从代码结构看,这个项目属于比较典型的 Java Web 单体后端项目。它按照控制层、业务层、数据访问层进行划分,接口通过 REST 风格暴露给前端或接口测试工具使用。项目使用 MySQL 保存用户、商品、订单和点赞数据,使用 Redis 缓存商品详情,登录状态则通过 Session 保存。

项目入口类是:

text 复制代码
src/main/java/com/key/new_shop_finall/NewShopFinallApplication.java

启动后默认端口为 8081,接口文档地址为:

text 复制代码
http://localhost:8081/swagger-ui.html

二、开发环境与技术栈

本项目最后使用的开发环境如下:

类型 版本或说明
操作系统 Windows 11
JDK JDK 17
构建工具 Maven 3.9.12
后端框架 Spring Boot 2.7.18
数据库 MySQL
缓存 Redis
接口文档 Springdoc OpenAPI
运行端口 8081

项目主要依赖在 pom.xml 中配置。比较关键的依赖有:

  • spring-boot-starter-web:用于编写 Web 接口,处理 HTTP 请求。
  • spring-boot-starter-jdbc:用于通过 JdbcTemplate 操作数据库。
  • spring-boot-starter-validation:用于请求参数校验。
  • spring-security-crypto:用于 BCrypt 密码加密。
  • springdoc-openapi-ui:用于生成 Swagger 接口文档。
  • mysql-connector-j:用于连接 MySQL。
  • lombok:减少实体类和 DTO 中重复的 getter、setter 代码。

项目中已经把 Java 版本统一配置为 17:

xml 复制代码
<java.version>17</java.version>

这也是后面解决 JDK 25 不兼容问题时最关键的一步。

三、项目制作步骤

1. 创建 Spring Boot 项目

制作项目时,首先创建 Maven 结构的 Spring Boot 项目,并确定包名和项目坐标:

xml 复制代码
<groupId>com.key</groupId>
<artifactId>new_shop_finall</artifactId>
<version>0.0.1-SNAPSHOT</version>

项目父工程使用的是 Spring Boot 2.7.18:

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.18</version>
</parent>

之后创建启动类 NewShopFinallApplication,使用 @SpringBootApplication 标记,让 Spring Boot 自动扫描项目中的 Controller、Service、Repository 等组件。

2. 配置运行参数

项目运行参数写在 application.yml 中,主要包括端口、数据库、Redis 和 Swagger 地址。

核心配置如下:

yaml 复制代码
server:
  port: 8081
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop_date
    username: root
  redis:
    host: localhost
    port: 6379
springdoc:
  swagger-ui:
    path: /swagger-ui.html

这里说明项目默认连接本机的 shop_date 数据库和本机 Redis。如果在其他电脑运行,需要根据自己电脑上的 MySQL 用户名、密码和 Redis 地址修改配置。

3. 搭建分层目录

项目主要目录如下:

text 复制代码
com.key.new_shop_finall
├── common        通用响应对象和 Session 常量
├── config        Web、JDBC、Swagger 等配置
├── controller    接口控制层
├── dao           数据访问接口
├── dao.impl      数据访问实现
├── dto           请求参数和返回对象
├── entity        实体类
├── exception     异常类和全局异常处理
├── intercepetor  登录拦截器
├── service       业务接口
├── service.impl  业务实现
└── util          工具类

这种分层方式比较清楚。Controller 只负责接收请求和返回结果,Service 处理业务规则,DAO 负责 SQL,DTO 负责参数传递和校验,Entity 对应数据库中的主要字段。

4. 统一接口返回格式

项目中定义了 ApiResponse<T>,接口统一返回 codemessagedata 三个字段。这样前端或接口测试工具拿到的数据格式比较固定,不需要每个接口单独判断返回结构。

成功时使用:

java 复制代码
ApiResponse.success(data)

失败时使用:

java 复制代码
ApiResponse.fail(code, message)

这个设计虽然简单,但在项目接口比较多时很有用。

5. 编写用户模块

用户模块主要在 UserControllerUserServiceImpl 中实现,包含注册、登录、退出登录、获取当前登录用户等功能。

主要接口有:

text 复制代码
POST /api/users/register
POST /api/users/login
POST /api/users/logout
GET  /api/users/me

注册时先检查用户名是否存在,如果不存在就把密码加密后保存到数据库。登录时先根据用户名查询用户,再校验密码是否正确。登录成功后,系统会把用户信息保存到 Session 中:

java 复制代码
session.setAttribute(SessionKeys.LOGIN_USER, loginUser);

后面的接口就可以根据 Session 判断用户是否已经登录。

6. 添加登录拦截器

为了避免每个接口都重复写登录判断,项目中写了 LoginInterceptor。它会在请求进入 Controller 前检查 Session 中是否存在 LOGIN_USER

如果用户没有登录,接口会返回 401;如果已经登录,请求继续执行。

WebConfig 中配置了拦截器,同时放行登录、注册、健康检查和 Swagger 文档相关路径:

text 复制代码
/api/users/login
/api/users/register
/api/health
/swagger-ui.html
/swagger-ui/**
/v3/api-docs/**
/webjars/**
/error

这样登录和注册可以直接访问,其他业务接口则需要先登录。

7. 编写商品模块

商品模块是项目中内容最多的部分,主要代码在 ProductControllerProductServiceImplProductDaoImpl 中。

主要功能包括:

  • 查询商品详情;
  • 分页查询商品;
  • 发布商品;
  • 修改商品;
  • 点赞商品;
  • 取消点赞;
  • 查询销量排行榜;
  • 查询点赞排行榜。

对应接口包括:

text 复制代码
GET    /api/products/{id}
GET    /api/products
POST   /api/products
PUT    /api/products/{id}
POST   /api/products/{id}/like
DELETE /api/products/{id}/like
GET    /api/products/rank/sales
GET    /api/products/rank/likes

商品详情查询时没有每次都直接查数据库,而是先查 Redis。缓存中有数据就直接返回,缓存没有才查 MySQL。查到商品后再写入 Redis。对于不存在的商品,项目也写入了一个短时间的空值缓存,避免一直查询数据库。

8. 编写订单模块

订单模块主要在 OrderControllerOrderServiceImplOrderDaoImpl 中实现。

主要接口有:

text 复制代码
POST   /api/orders
GET    /api/orders/my
GET    /api/orders/sold
DELETE /api/orders/{id}

创建订单时,业务流程大致是:

  1. 查询商品是否存在;
  2. 判断商品是否处于上架状态;
  3. 判断库存是否足够;
  4. 扣减库存;
  5. 创建订单;
  6. 增加商品销量;
  7. 清除商品详情缓存。

这一段业务使用了 @Transactional,因为扣库存和创建订单必须保持一致。如果中间步骤失败,事务会回滚,避免库存和订单数据不一致。

9. 编写管理员模块

管理员接口集中在 AdminController 中,主要用于管理普通用户和商品。

管理员相关接口包括:

text 复制代码
GET    /api/admin/users
PUT    /api/admin/users/{id}
PUT    /api/admin/users/{id}/status
PUT    /api/admin/users/{id}/disable
DELETE /api/admin/users/{id}
PUT    /api/admin/products/{id}/status
DELETE /api/admin/products/{id}

管理员权限通过 AuthUtils.requireAdmin(...) 判断。项目中约定 userType1 的用户是管理员,普通用户不能操作管理员接口。

10. 添加异常处理

项目中使用 GlobalExceptionHandler 统一处理异常。它主要处理业务异常、参数校验异常、数据库异常和其他未知异常。

这样做的好处是接口不会直接把 Java 错误堆栈返回给前端,而是返回统一格式的数据。例如参数错误时返回 400,未登录时返回 401,没有管理员权限时返回 403。

四、技术要点分析

1. Spring Boot 自动配置

项目使用 Spring Boot 后,很多配置不需要手动写 XML。只要在类上添加对应注解,Spring 就可以自动创建对象并完成依赖注入。

例如:

  • @RestController 用于接口类;
  • @Service 用于业务类;
  • @Repository 用于数据访问类;
  • @Configuration 用于配置类;
  • @Bean 用于手动注册对象。

这让项目的搭建过程比较快,也减少了配置文件数量。

2. REST 接口设计

项目接口基本按照 REST 风格设计。查询用 GET,新增用 POST,修改用 PUT,删除用 DELETE。接口路径也比较直观,比如商品详情是 /api/products/{id},订单列表是 /api/orders/my

这种设计对接口测试比较友好,也便于后期接前端页面。

3. Session 登录鉴权

本项目没有使用 Token 或 JWT,而是使用 Session。用户登录成功后,服务端保存登录用户信息,浏览器或接口工具通过 Cookie 维持会话。

这种方式实现起来更直接,适合本项目这种课程设计规模的系统。不过如果后期要做分布式部署,就需要考虑 Session 共享或改成 Token 鉴权。

4. BCrypt 密码加密

项目没有直接保存明文密码,而是使用 BCryptPasswordEncoder 加密。注册时加密,登录时匹配。

相关工具类是:

text 复制代码
src/main/java/com/key/new_shop_finall/util/PasswordUtils.java

这一点比较重要,因为如果直接保存明文密码,一旦数据库泄露,用户密码就会完全暴露。使用 BCrypt 后,即使拿到数据库中的密码字段,也不能直接还原出原密码。

5. 参数校验

项目中的请求对象使用了很多校验注解,例如:

java 复制代码
@NotBlank
@NotNull
@Size
@Min
@Max
@Email
@Pattern
@DecimalMin

例如注册用户时,用户名不能为空,密码长度需要符合要求,邮箱要符合邮箱格式,手机号也使用正则表达式限制。

这些校验放在 DTO 中,比在业务代码里写大量 if 判断更清晰。

6. JdbcTemplate 操作数据库

项目没有使用 MyBatis,而是直接使用 JdbcTemplateNamedParameterJdbcTemplate 写 SQL。

这样做的优点是 SQL 很直观,可以清楚看到每个接口到底执行了什么数据库语句。缺点是代码量比 MyBatis 多一些,尤其是 RowMapper 需要自己写。

分页查询中使用了动态 SQL,比如商品查询可以按照关键字、分类、状态、价格范围和时间范围筛选。

7. 事务控制

订单创建和点赞等操作使用了 @Transactional(rollbackFor = Exception.class)。这些操作不只是改一张表,例如创建订单会同时影响订单表、商品库存、销量和缓存。

如果不加事务,就可能出现扣了库存但订单没有创建成功的情况。加上事务后,只要业务中出现异常,数据库操作就会回滚。

8. Redis 缓存

项目中商品详情使用 Redis 缓存,key 的格式是:

text 复制代码
shop:product:detail:{id}

正常商品缓存 30 分钟,不存在的商品缓存 3 分钟。商品被修改、删除、下单或点赞变化时,会主动删除缓存。

比较有特点的是,项目没有直接引入 Spring Data Redis,而是自己写了 RedisCacheUtil,通过 Socket 按 Redis RESP 协议发送 GETSETEXDEL 命令。这个实现比直接使用现成库更底层,也能帮助理解 Redis 客户端和服务端之间是怎么通信的。

9. 逻辑删除

项目中很多删除操作不是直接删除数据库记录,而是把 is_deleted 设置为 1

例如商品删除:

sql 复制代码
update product set is_deleted = 1 where id = ? and is_deleted = 0

查询时再统一加上 is_deleted = 0。这种方式可以保留历史数据,也方便以后做数据恢复或审计。

10. Swagger 接口文档

项目集成了 Springdoc OpenAPI,启动后可以访问:

text 复制代码
http://localhost:8081/swagger-ui.html

在没有前端页面的情况下,Swagger 对测试接口很方便。可以直接查看接口路径、请求方式、参数格式和返回结构。

五、数据库设计分析

从 DAO 层 SQL 可以看出,项目主要使用了四张表:userproductorderlike

user 表保存用户信息,包括用户名、密码、邮箱、手机号、头像、用户类型、状态和逻辑删除标记。

product 表保存商品信息,包括商品名、描述、分类、价格、库存、图片、发布者、状态、点赞数、销量和浏览量。

order 表保存订单信息,包括订单号、买家、商品、购买数量、总价、订单状态和支付时间。

like 表保存用户和商品之间的点赞关系,用来判断某个用户是否已经点赞过某个商品。

这几个表之间的关系比较直接。用户可以发布商品,也可以买商品生成订单;商品可以被多个用户点赞;订单关联买家和商品。

六、遇到的问题与解决方法

1. JDK 25 与项目不兼容

实验开始时,我在 IDE 中使用的是 JDK 25。刚开始以为 JDK 越新越好,但实际运行和配置项目时发现并不合适。

项目使用的是 Spring Boot 2.7.18,这个版本更适合 JDK 8、JDK 11、JDK 17 这类长期支持版本。使用 JDK 25 时,可能会遇到下面这些问题:

  • IDE 项目 SDK 和 Maven 使用的 JDK 不一致;
  • Maven 编译插件对过新的 JDK 支持不稳定;
  • Lombok 注解处理可能出现兼容问题;
  • Spring Boot 2.7.x 的部分依赖没有针对 JDK 25 做完整适配;
  • 编译版本、运行版本和语言级别容易混乱。

后来我把环境统一改成 JDK 17,具体做法是:

  1. 安装 JDK 17;
  2. 在 IDE 的 Project Structure 中把 Project SDK 改成 JDK 17;
  3. 把模块的 Language Level 改成 17;
  4. 在 Maven Runner 中选择 JDK 17;
  5. 修改 pom.xml,设置 <java.version>17</java.version>
  6. 重新刷新 Maven 依赖;
  7. 执行 mvn test 验证项目是否能正常编译。

改完之后,项目能够正常编译,Spring Boot 测试也可以通过。这个问题说明,做 Java 项目时并不是 JDK 越新越合适,还是要看框架版本和依赖生态。

2. IDE 和命令行 JDK 不一致

调试时还发现一个容易忽略的问题:IDE 中看到的 JDK 版本和命令行实际使用的 JDK 版本可能不是同一个。

所以后面我用下面两个命令确认环境:

bash 复制代码
java -version
mvn -v

当前验证时使用的是 Java 17.0.18 和 Maven 3.9.12。确认这两个命令显示的 JDK 是 17 后,再运行项目会稳很多。

3. 中文乱码问题

查看源码时,部分中文注解和提示信息在 Windows 终端里显示成乱码。这个问题主要和编码有关。项目文件一般按 UTF-8 保存,但 Windows 控制台默认编码可能是 GBK,直接输出文件内容时就容易显示不正常。

解决方法是:

  • IDE 文件编码统一设置为 UTF-8;
  • Maven 资源编码保持 UTF-8;
  • 尽量在 IDE 中查看源码中的中文;
  • 如果必须在终端查看,可以切换终端编码后再看。

这个问题不一定影响编译,但会影响阅读和后期维护。

4. MySQL 和 Redis 服务依赖

项目运行时依赖 MySQL。数据库没有启动、库名不对、账号密码不对,都会导致接口访问失败。

商品详情缓存还依赖 Redis。如果 Redis 没有启动,项目不会完全不能运行,但商品详情缓存就不能正常使用,只能退回数据库查询。

所以运行项目之前,需要先检查:

  • MySQL 是否启动;
  • 是否已经创建 shop_date 数据库;
  • application.yml 中账号密码是否正确;
  • Redis 是否启动;
  • Redis 端口是否为 6379

5. Maven settings 文件问题

一开始尝试使用自定义 settings 文件运行:

bash 复制代码
mvn -s .mvn/local-settings.xml test

但是项目中没有 .mvn/local-settings.xml,所以 Maven 会报错。后来直接使用:

bash 复制代码
mvn test

项目可以正常测试通过。这个问题说明,命令中的配置文件路径必须真实存在,否则 Maven 不会继续执行。

七、项目使用方法

1. 准备环境

先确认本机安装 JDK 17:

bash 复制代码
java -version

再确认 Maven 可以使用:

bash 复制代码
mvn -v

建议两个命令中显示的 Java 版本都为 17。

2. 准备数据库

启动 MySQL 后,创建数据库:

sql 复制代码
create database shop_date default character set utf8mb4 collate utf8mb4_general_ci;

然后根据项目实体和 DAO 中的 SQL 创建 userproductorderlike 表。

如果本机 MySQL 密码和项目配置不同,需要修改 application.yml

yaml 复制代码
spring:
  datasource:
    username: root
    password: 你的数据库密码

3. 启动 Redis

启动 Redis,确认地址为:

text 复制代码
localhost:6379

如果 Redis 地址或端口不同,也需要修改 application.yml

4. 构建项目

在项目根目录执行:

bash 复制代码
mvn clean package

或者只做测试:

bash 复制代码
mvn test

本次实验中,我执行 mvn test 后结果为:

text 复制代码
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

5. 启动项目

可以使用 Maven 启动:

bash 复制代码
mvn spring-boot:run

也可以直接运行主类:

text 复制代码
com.key.new_shop_finall.NewShopFinallApplication

启动成功后,访问地址为:

text 复制代码
http://localhost:8081

接口文档地址为:

text 复制代码
http://localhost:8081/swagger-ui.html

6. 基本测试流程

实际测试时可以按下面顺序来:

  1. 调用 /api/users/register 注册用户;
  2. 调用 /api/users/login 登录,并保留 Cookie;
  3. 调用 /api/products 发布商品;
  4. 调用 /api/products 查询商品列表;
  5. 调用 /api/products/{id} 查询商品详情;
  6. 调用 /api/products/{id}/like 点赞;
  7. 调用 /api/orders 创建订单;
  8. 调用 /api/orders/my 查询自己的订单;
  9. 使用管理员账号登录后,测试 /api/admin 下的管理接口。

注册请求示例:

http 复制代码
POST /api/users/register
Content-Type: application/json

{
  "username": "testuser",
  "password": "123456",
  "email": "test@example.com",
  "phone": "13800138000"
}

登录请求示例:

http 复制代码
POST /api/users/login
Content-Type: application/json

{
  "username": "testuser",
  "password": "123456"
}

发布商品示例:

http 复制代码
POST /api/products
Content-Type: application/json

{
  "name": "测试商品",
  "description": "这是一个测试商品",
  "categoryId": 1,
  "categoryName": "数码",
  "price": 99.99,
  "stock": 20,
  "imageUrl": "https://example.com/product.png"
}

创建订单示例:

http 复制代码
POST /api/orders
Content-Type: application/json

{
  "productId": 1,
  "quantity": 2
}

八、测试结果

本次在项目根目录执行:

bash 复制代码
mvn test

测试结果为:

text 复制代码
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

说明项目在 JDK 17 环境下可以正常编译,Spring Boot 上下文也能正常加载。虽然当前测试用例比较基础,只验证了项目能启动,但至少可以确认环境配置和依赖没有明显问题。

九、总结

通过这次实验,我对 Spring Boot 后端项目的基本开发流程有了更完整的理解。项目从创建工程、配置依赖、连接数据库,到编写 Controller、Service、DAO,再到加入登录拦截、异常处理、事务和 Redis 缓存,整体流程比较完整。

本项目比较有价值的地方是功能虽然不算特别复杂,但覆盖了后端开发中很常见的内容,例如 REST 接口、Session 登录、BCrypt 密码加密、参数校验、JdbcTemplate SQL 操作、事务控制、逻辑删除、Redis 缓存和 Swagger 文档。

开发过程中印象最深的问题是 JDK 25 与项目环境不兼容。最后把 IDE、Maven 和 pom.xml 都统一到 JDK 17 后,项目才稳定通过测试。这也说明做项目时环境版本要和框架生态匹配,不能只追求最新版本。

总体来说,本项目达到了商品交易后端系统的基本要求,也为后续继续扩展前端页面、支付功能、商品分类管理、订单状态流转和更完善的测试打下了基础。成,具有较完整的实验和学习价值。

相关推荐
小瓦码J码2 小时前
轻量化线程池实战:忙时并发、闲时归零,搞定周期批量任务
java·后端
百珏2 小时前
[灰度发布]:灰度流量如何匹配与识别:从特征补全到网关命中引擎
java·后端·架构
Misnearch2 小时前
1345. 跳跃游戏 IV
java·leetcode·bfs
阳光九叶草LXGZXJ2 小时前
自制数据库迁移工具-C版-07-HappySunshineV1.6-(支持PG、达梦、Gbase8a)
linux·c语言·开发语言·数据库·学习·postgresql
Bechamz2 小时前
大数据开发学习Day34
java·大数据·学习
不吃土豆的马铃薯2 小时前
5.SGI STL 二级空间配置器 _S_chunk_alloc核心函数解析
开发语言·c++·vscode·c·内存池
学掌门2 小时前
JavaScript:为什么命名参数比位置参数更好
开发语言·javascript·ecmascript
JoneBB2 小时前
ABAP上传EXCEL模板并将内表内容存到两个sheet中
java·前端·数据库
码界筑梦坊2 小时前
124-基于Python的航空旅客满意度数据可视化分析系统
开发语言·python·信息可视化·数据分析·flask·毕业设计