一、项目背景与意义
在互联网飞速发展的今天,电子商务行业蓬勃兴起,在线商城成为人们购物的重要途径。开发一个功能完备的在线商城系统,不仅能满足用户便捷购物的需求,还能为商家提供高效的商品管理和订单处理平台。本项目将基于 Java 语言,结合先进的开发框架和技术,构建一个具有高可用性、可扩展性和安全性的在线商城系统。
二、项目架构设计
(一)整体架构概述
采用经典的三层架构模式,即表现层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer),同时引入 Spring 和 Spring MVC 框架来管理对象和处理请求,使用 MyBatis 作为持久化框架与数据库进行交互。这种架构模式使得系统结构清晰,各层职责明确,便于开发、维护和扩展。
(二)分层详细设计
- 表现层(Presentation Layer)
- 负责与用户进行交互,接收用户的请求并将处理结果展示给用户。使用 Spring MVC 框架来处理请求和响应的映射关系,通过控制器(Controller)接收用户请求,调用业务逻辑层的服务进行处理,最后将处理结果返回给视图(View)进行展示。
- 视图部分可以使用 JSP、Thymeleaf 等模板引擎来生成动态页面,为用户提供友好的界面。
- 业务逻辑层(Business Logic Layer)
- 处理具体的业务规则和流程,是整个系统的核心。该层包含各种服务类(Service),负责处理与业务相关的逻辑,如用户注册、登录验证、商品库存管理、订单生成等。
- 业务逻辑层调用数据访问层的接口来获取或更新数据,并将处理后的结果返回给表现层。同时,该层还负责处理事务管理,确保数据的一致性和完整性。
- 数据访问层(Data Access Layer)
- 负责与数据库进行交互,执行如插入、查询、更新、删除等操作。使用 MyBatis 框架来实现数据访问层,通过 Mapper 接口和 XML 映射文件来定义 SQL 语句,将 Java 对象与数据库表进行映射。
- 数据访问层提供了统一的接口,业务逻辑层通过调用这些接口来访问数据库,而不需要关心具体的数据库操作细节。
(三)数据库设计
选择 MySQL 作为数据库管理系统,设计以下主要数据表:
-
用户表(user)
| 字段名 | 类型 | 描述 |
|-------------|--------------|----------------|
| id | int | 用户 ID,主键 |
| username | varchar (50) | 用户名,唯一 |
| password | varchar (255) | 用户密码 |
| email | varchar (100) | 用户邮箱 |
| phone | varchar (20) | 用户手机号码 |
| address | varchar (255) | 用户收货地址 |
| create_time | datetime | 用户创建时间 | -
商品表(product)
| 字段名 | 类型 | 描述 |
|-------------|--------------|----------------|
| id | int | 商品 ID,主键 |
| name | varchar (100) | 商品名称 |
| description | text | 商品描述 |
| price | decimal (10, 2) | 商品价格 |
| stock | int | 商品库存数量 |
| image_url | varchar (255) | 商品图片链接 |
| create_time | datetime | 商品创建时间 | -
订单表(order)
| 字段名 | 类型 | 描述 |
|-------------|--------------|----------------|
| id | int | 订单 ID,主键 |
| user_id | int | 用户 ID,外键 |
| order_number| varchar (50) | 订单编号,唯一 |
| total_price | decimal (10, 2) | 订单总价 |
| status | varchar (20) | 订单状态(待支付、已支付、已发货等) |
| create_time | datetime | 订单创建时间 | -
订单详情表(order_detail)
| 字段名 | 类型 | 描述 |
|-------------|--------------|----------------|
| id | int | 订单详情 ID,主键 |
| order_id | int | 订单 ID,外键 |
| product_id | int | 商品 ID,外键 |
| quantity | int | 商品数量 |
| price | decimal (10, 2) | 商品单价 | -
购物车表(cart)
| 字段名 | 类型 | 描述 |
|-------------|--------------|----------------|
| id | int | 购物车 ID,主键 |
| user_id | int | 用户 ID,外键 |
| product_id | int | 商品 ID,外键 |
| quantity | int | 商品数量 |
| create_time | datetime | 加入购物车时间 |
三、环境搭建
(一)开发工具与环境
- 集成开发环境(IDE):选择 IntelliJ IDEA,它具有强大的代码编辑、自动补全、调试等功能,能够提高开发效率。
- Java 开发工具包(JDK):安装 JDK 1.8 或以上版本,确保项目能够基于合适的 Java 版本进行编译和运行。
- 数据库管理系统:安装 MySQL 数据库,并创建相应的数据库和数据表。
- 服务器:选择 Tomcat 作为 Java Web 应用的服务器,用于部署项目。
(二)项目依赖管理
使用 Maven 作为项目依赖管理工具,在 pom.xml
文件中配置项目的基本信息以及所需的各种依赖库,示例如下:
xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>online-mall</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.18</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
四、核心功能代码实现
(一)用户注册与登录功能
- 实体类(Entity)
java
// User.java
public class User {
private Integer id;
private String username;
private String password;
private String email;
private String phone;
private String address;
private Date createTime;
// 省略 getter 和 setter 方法
}
- 数据访问层(DAO)
java
// UserMapper.java
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
void insertUser(User user);
User findUserByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
}
xml
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemall.dao.UserMapper">
<insert id="insertUser" parameterType="com.example.onlinemall.entity.User">
INSERT INTO user (username, password, email, phone, address, create_time)
VALUES (#{username}, #{password}, #{email}, #{phone}, #{address}, #{createTime})
</insert>
<select id="findUserByUsernameAndPassword" resultType="com.example.onlinemall.entity.User">
SELECT * FROM user WHERE username = #{username} AND password = #{password}
</select>
</mapper>
- 业务逻辑层(Service)
java
// UserService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void register(User user) {
userMapper.insertUser(user);
}
public User login(String username, String password) {
return userMapper.findUserByUsernameAndPassword(username, password);
}
}
- 表现层(Controller)
java
// UserController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
@Controller
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public String register(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("email") String email,
@RequestParam("phone") String phone,
@RequestParam("address") String address,
RedirectAttributes redirectAttributes) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
user.setPhone(phone);
user.setAddress(address);
user.setCreateTime(new Date());
try {
userService.register(user);
redirectAttributes.addFlashAttribute("message", "注册成功,请登录!");
return "redirect:/login";
} catch (Exception e) {
redirectAttributes.addFlashAttribute("error", "注册失败,请重试!");
return "redirect:/register";
}
}
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session,
RedirectAttributes redirectAttributes) {
User user = userService.login(username, password);
if (user != null) {
session.setAttribute("user", user);
return "redirect:/index";
} else {
redirectAttributes.addFlashAttribute("error", "用户名或密码错误,请重试!");
return "redirect:/login";
}
}
}
(二)商品展示与查询功能
- 实体类(Entity)
java
// Product.java
public class Product {
private Integer id;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
private String imageUrl;
private Date createTime;
// 省略 getter 和 setter 方法
}
- 数据访问层(DAO)
java
// ProductMapper.java
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ProductMapper {
List<Product> findAllProducts();
List<Product> findProductsByName(String name);
}
xml
<!-- ProductMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemall.dao.ProductMapper">
<select id="findAllProducts" resultType="com.example.onlinemall.entity.Product">
SELECT * FROM product
</select>
<select id="findProductsByName" resultType="com.example.onlinemall.entity.Product">
SELECT * FROM product WHERE name LIKE CONCAT('%', #{name}, '%')
</select>
</mapper>
- 业务逻辑层(Service)
java
// ProductService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
public List<Product> getAllProducts() {
return productMapper.findAllProducts();
}
public List<Product> searchProductsByName(String name) {
return productMapper.findProductsByName(name);
}
}
- 表现层(Controller)
java
// ProductController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/products")
public String showAllProducts(Model model) {
List<Product> productList = productService.getAllProducts();
model.addAttribute("productList", productList);
return "productList";
}
@GetMapping("/searchProducts")
public String searchProducts(@RequestParam("name") String name, Model model) {
List<Product> productList = productService.searchProductsByName(name);
model.addAttribute("productList", productList);
return "productList";
}
}
(三)购物车功能
- 实体类(Entity)
java
// Cart.java
public class Cart {
private Integer id;
private Integer userId;
private Integer productId;
private Integer quantity;
private Date createTime;
// 省略 getter 和 setter 方法
}
- 数据访问层(DAO)
java
// CartMapper.java
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CartMapper {
void addProductToCart(Cart cart);
void updateProductQuantityInCart(@Param("id") int id, @Param("quantity") int quantity);
Cart findCartByUserIdAndProductId(@Param("userId") int userId, @Param("productId") int productId);
List<Cart> getCartByUserId(int userId);
void deleteCartById(int id);
}
xml
<!-- CartMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemall.dao.CartMapper">
<insert id="addProductToCart" parameterType="com.example.onlinemall.entity.Cart">
INSERT INTO cart (user_id, product_id, quantity, create_time)
VALUES (#{userId}, #{productId}, #{quantity}, #{createTime})
</insert>
<update id="updateProductQuantityInCart">
UPDATE cart SET quantity = #{quantity} WHERE id = #{id}
</update>
<select id="findCartByUserIdAndProductId" resultType="com.example.onlinemall.entity.Cart">
SELECT * FROM cart WHERE user_id = #{userId} AND product_id = #{productId}
</select>
<select id="getCartByUserId" resultType="com.example.onlinemall.entity.Cart">
SELECT * FROM cart WHERE user_id = #{userId}
</select>
<delete id="deleteCartById">
DELETE FROM cart WHERE id = #{id}
</delete>
</mapper>
- 业务逻辑层(Service)
java
// CartService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@Service
public class CartService {
@Autowired
private CartMapper cartMapper;
@Transactional
public void addProductToCart(int userId, int productId, int quantity) {
Cart cart = cartMapper.findCartByUserIdAndProductId(userId, productId);
if (cart != null) {
int newQuantity = cart.getQuantity() + quantity;
cartMapper.updateProductQuantityInCart(cart.getId(), newQuantity);
} else {
cart = new Cart();
cart.setUserId(userId);
cart.setProductId(productId);
cart.setQuantity(quantity);
cart.setCreateTime(new Date());
cartMapper.addProductToCart(cart);
}
}
public List<Cart> getCartByUserId(int userId) {
return cartMapper.getCartByUserId(userId);
}
@Transactional
public void deleteCartById(int id) {
cartMapper.deleteCartById(id);
}
}
- 表现层(Controller)
java
// CartController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
public class CartController {
@Autowired
private CartService cartService;
@PostMapping("/addToCart")
public String addToCart(@RequestParam("productId") int productId,
@RequestParam("quantity") int quantity,
HttpSession session,
RedirectAttributes redirectAttributes) {
User user = (User) session.getAttribute("user");
if (user == null) {
redirectAttributes.addFlashAttribute("error", "请先登录!");
return "redirect:/login";
}
cartService.addProductToCart(user.getId(), productId, quantity);
redirectAttributes.addFlashAttribute("message", "商品已成功加入购物车!");
return "redirect:/products";
}
@GetMapping("/cart")
public String showCart(HttpSession session, Model model) {
User user = (User) session.getAttribute("user");
if (user == null) {
return "redirect:/login";
}
List<Cart> cartList = cartService.getCartByUserId(user.getId());
model.addAttribute("cartList", cartList);
return "cart";
}
@GetMapping("/deleteCart/{id}")
public String deleteCart(@PathVariable("id") int id, RedirectAttributes redirectAttributes) {
cartService.deleteCartById(id);
redirectAttributes.addFlashAttribute("message", "商品已从购物车中移除!");
return "redirect:/cart";
}
}
(四)订单功能
- 实体类(Entity)
java
// Order.java
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Order {
private Integer id;
private Integer userId;
private String orderNumber;
private BigDecimal totalPrice;
private String status;
private Date createTime;
private List<OrderDetail> orderDetails;
public Order() {
this.orderDetails = new ArrayList<>();
}
public void addOrderDetail(Product product, int quantity) {
OrderDetail orderDetail = new OrderDetail();
orderDetail.setProductId(product.getId());
orderDetail.setQuantity(quantity);
orderDetail.setPrice(product.getPrice());
this.orderDetails.add(orderDetail);
}
// 省略 getter 和 setter 方法
}
// OrderDetail.java
public class OrderDetail {
private Integer id;
private Integer orderId;
private Integer productId;
private Integer quantity;
private BigDecimal price;
// 省略 getter 和 setter 方法
}
- 数据访问层(DAO)
java
// OrderMapper.java
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface OrderMapper {
void insertOrder(Order order);
void insertOrderDetail(@Param("orderId") int orderId, @Param("orderDetail") OrderDetail orderDetail);
List<Order> getOrdersByUserId(int userId);
}
xml
<!-- OrderMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemall.dao.OrderMapper">
<insert id="insertOrder" parameterType="com.example.onlinemall.entity.Order">
INSERT INTO order (user_id, order_number, total_price, status, create_time)
VALUES (#{userId}, #{orderNumber}, #{totalPrice}, #{status}, #{createTime})
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
<insert id="insertOrderDetail" parameterType="com.example.onlinemall.entity.OrderDetail">
INSERT INTO order_detail (order_id, product_id, quantity, price)
VALUES (#{orderId}, #{orderDetail.productId}, #{orderDetail.quantity}, #{orderDetail.price})
</insert>
<select id="getOrdersByUserId" resultType="com.example.onlinemall.entity.Order">
SELECT * FROM order WHERE user_id = #{userId}
</select>
</mapper>
- 业务逻辑层(Service)
java
// OrderService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Service
public class OrderService {
@Autowired
private CartService cartService;
@Autowired
private ProductService productService;
@Autowired
private OrderMapper orderMapper;
@Transactional
public void placeOrder(int userId) {
List<Cart> cartList = cartService.getCartByUserId(userId);
Order order = new Order();
order.setUserId(userId);
order.setOrderNumber(UUID.randomUUID().toString());
order.setStatus("待支付");
order.setCreateTime(new Date());
BigDecimal totalPrice = BigDecimal.ZERO;
for (Cart cart : cartList) {
Product product = productService.getProductById(cart.getProductId());
if (product.getStock() < cart.getQuantity()) {
throw new RuntimeException("商品库存不足,无法下单!");
}
order.addOrderDetail(product, cart.getQuantity());
totalPrice = totalPrice.add(product.getPrice().multiply(BigDecimal.valueOf(cart.getQuantity())));
// 更新商品库存
product.setStock(product.getStock() - cart.getQuantity());
productService.updateProductStock(product);
}
order.setTotalPrice(totalPrice);
orderMapper.insertOrder(order);
for (OrderDetail orderDetail : order.getOrderDetails()) {
orderMapper.insertOrderDetail(order.getId(), orderDetail);
}
// 清空购物车
for (Cart cart : cartList) {
cartService.deleteCartById(cart.getId());
}
}
public List<Order> getOrdersByUserId(int userId) {
return orderMapper.getOrdersByUserId(userId);
}
}
- 表现层(Controller)
java
// OrderController.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
import java.util.List;
@Controller
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/placeOrder")
public String placeOrder(HttpSession session, RedirectAttributes redirectAttributes) {
User user = (User) session.getAttribute("user");
if (user == null) {
redirectAttributes.addFlashAttribute("error", "请先登录!");
return "redirect:/login";
}
try {
orderService.placeOrder(user.getId());
redirectAttributes.addFlashAttribute("message", "订单创建成功,请及时支付!");
return "redirect:/orders";
} catch (Exception e) {
redirectAttributes.addFlashAttribute("error", e.getMessage());
return "redirect:/cart";
}
}
@GetMapping("/orders")
public String showOrders(HttpSession session, Model model) {
User user = (User) session.getAttribute("user");
if (user == null) {
return "redirect:/login";
}
List<Order> orderList = orderService.getOrdersByUserId(user.getId());
model.addAttribute("orderList", orderList);
return "orderList";
}
}
五、项目测试与部署
(一)单元测试
使用 JUnit 和 Mockito 框架对各个功能模块进行单元测试,确保每个方法的正确性和稳定性。例如,对 UserService
类的 register
和 login
方法进行测试:
java
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.*;
public class UserServiceTest {
@Test
public void testRegister() {
UserMapper userMapper = Mockito.mock(UserMapper.class);
UserService userService = new UserService();
userService.setUserMapper(userMapper);
User user = new User();
user.setUsername("testuser");
user.setPassword("testpassword");
userService.register(user);
Mockito.verify(userMapper, Mockito.times(1)).insertUser(user);
}
@Test
public void testLogin() {
UserMapper userMapper = Mockito.mock(UserMapper.class);
UserService userService = new UserService();
userService.setUserMapper(userMapper);
User user = new User();
user.setUsername("testuser");
user.setPassword("testpassword");
Mockito.when(userMapper.findUserByUsernameAndPassword("testuser", "testpassword")).thenReturn(user);
User result = userService.login("testuser", "testpassword");
assertEquals(user, result);
}
}
(二)集成测试
在完成单元测试后,进行集成测试,模拟用户实际的操作流程,从用户注册登录、浏览商品、添加购物车、下单到查看订单等一系列操作进行整体测试,检查各个模块之间的交互是否正常,数据传递是否准确无误,确保整个系统的功能完整性和连贯性。
(三)部署上线
将项目打包成 WAR 文件,部署到 Tomcat 服务器的 webapps
目录下,配置好数据库连接等相关环境参数,启动 Tomcat 服务器,使项目能够对外提供服务,供用户访问和使用。
六、总结与展望
通过本项目的开发,我们成功构建了一个功能完备的在线商城系统,涵盖了用户注册登录、商品展示查询、购物车管理、订单处理等核心功能。在开发过程中,我们充分运用了 Java 语言的特性和各种开发框架,提高了开发效率和系统的可维护性。
未来,我们可以进一步优化系统,增加更多的功能,如支付接口集成、商品评论与评分、营销活动管理等,以提升用户体验和系统的竞争力。同时,还可以考虑对系统进行性能优化,提高系统的响应速度和并发处理能力,以应对大规模用户的访问。
希望通过本文的详细介绍,能让你对基于 Java 开发复杂项目有更深入的了解和认识