叮当书城

项目简介:基于Java开发的完整电商网站系统,实现了书籍浏览、搜索、购买及后台管理的全流程功能。

技术栈:Spring 5.1.6 + SpringMVC + MyBatis 3.2.2框架,MySQL 8.0.27数据库,JSP+JSTL前端技术,Maven构建管理。

核心功能

  • 用户模块:注册登录、个人信息管理、权限控制
  • 书籍模块:分类展示、搜索、详情查看、热门推荐
  • 购物车与订单:商品管理、订单提交、订单查询
  • 后台管理:用户管理、书籍管理、订单状态跟踪

架构特色:采用MVC三层架构设计,代码结构清晰,实现了前后台分离,具备良好的扩展性和维护性。通过该项目熟练掌握了Java Web开发全流程及主流框架应用。

pom.xml配置包

<?xml version="1.0" encoding="UTF-8"?>

<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.vilicode.group</groupId>
  <artifactId>BookShop</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>BookShop Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.2.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.1.6.RELEASE</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>5.1.6.RELEASE</version>
      </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp -->
    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.27</version>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.6.RELEASE</version>
    </dependency>
    <!--<dependency>-->
      <!--<groupId>com.vili.encryption</groupId>-->
      <!--<artifactId>encryption</artifactId>-->
      <!--<version>1.0.0</version>-->
    <!--</dependency>-->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>

  </dependencies>

  <build>
    <finalName>BookShop</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
      <plugins>
          <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <configuration>
                  <source>8</source>
                  <target>8</target>
              </configuration>
          </plugin>
      </plugins>
      <resources>
      <resource>
        <directory>src/main/java</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
    </resources>
  </build>

</project>

bean下实体类

Book – 书籍实体

  • 作用:表示系统中的书籍信息,包含书籍的基本属性和推荐状态
  • 核心属性:书籍ID、ISBN、名称、作者、出版社、价格、库存、分类、封面图片等
  • 特色功能:包含是否为轮播书籍、热门书籍、新书的标识属性
  • 在项目架构中的作用
  • 数据传输:作为数据传输对象(DTO)在不同层之间传递数据
  • 持久化映射:作为持久化对象(PO)与数据库表进行映射
  • 业务逻辑支持:封装业务数据和相关操作,如订单实体中包含购物车逻辑
  • 前端展示:将后端数据结构化后传递给前端JSP页面进行渲染展示
  • 这些实体类构成了整个项目的数据模型基础,通过它们实现了系统各层之间的数据流转和业务逻辑的实现。
package com.vilicode.bean;

public class Book {
    private Integer bid;
    private String bisbn;
    private String bname;
    private String bauthor;
    private String bpublisher;
    private String bcover;
    private String bimage1;
    private String bimage2;
    private Double bprice;
    private Integer btid;
    private String btname;
    private Integer bstock;
    private String bmark;

    private boolean isScroll;
    private boolean isHot;
    private boolean isNew;

    public boolean getIsScroll() {
        return isScroll;
    }
    public void setScroll(boolean isScroll) {
        this.isScroll = isScroll;
    }
    public boolean getIsHot() {
        return isHot;
    }
    public void setHot(boolean isHot) {
        this.isHot = isHot;
    }
    public boolean getIsNew() {
        return isNew;
    }
    public void setNew(boolean isNew) {
        this.isNew = isNew;
    }

    public String getBtname() {
        return btname;
    }

    public void setBtname(String btname) {
        this.btname = btname;
    }

    public Integer getBid() {
        return bid;
    }

    public void setBid(Integer bid) {
        this.bid = bid;
    }

    public String getBisbn() {
        return bisbn;
    }

    public void setBisbn(String bisbn) {
        this.bisbn = bisbn;
    }

    public String getBname() {
        return bname;
    }

    public void setBname(String bname) {
        this.bname = bname;
    }

    public String getBauthor() {
        return bauthor;
    }

    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }

    public String getBpublisher() {
        return bpublisher;
    }

    public void setBpublisher(String bpublisher) {
        this.bpublisher = bpublisher;
    }

    public String getBcover() {
        return bcover;
    }

    public void setBcover(String bcover) {
        this.bcover = bcover;
    }

    public String getBimage1() {
        return bimage1;
    }

    public void setBimage1(String bimage1) {
        this.bimage1 = bimage1;
    }

    public String getBimage2() {
        return bimage2;
    }

    public void setBimage2(String bimage2) {
        this.bimage2 = bimage2;
    }

    public Double getBprice() {
        return bprice;
    }

    public void setBprice(Double bprice) {
        this.bprice = bprice;
    }

    public Integer getBtid() {
        return btid;
    }

    public void setBtid(Integer btid) {
        this.btid = btid;
    }

    public Integer getBstock() {
        return bstock;
    }

    public void setBstock(Integer bstock) {
        this.bstock = bstock;
    }

    public String getBmark() {
        return bmark;
    }

    public void setBmark(String bmark) {
        this.bmark = bmark;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bid=" + bid +
                ", bisbn='" + bisbn + '\'' +
                ", bname='" + bname + '\'' +
                ", bauthor='" + bauthor + '\'' +
                ", bpublisher='" + bpublisher + '\'' +
                ", bcover='" + bcover + '\'' +
                ", bimage1='" + bimage1 + '\'' +
                ", bimage2='" + bimage2 + '\'' +
                ", bprice=" + bprice +
                ", btid=" + btid +
                ", btname='" + btname + '\'' +
                ", bstock=" + bstock +
                ", bmark='" + bmark + '\'' +
                '}';
    }
}

User – 用户实体

  • 作用:表示系统中的用户信息,区分普通用户和管理员
  • 核心属性:用户ID、用户名、密码、真实姓名、角色、手机号、地址等
  • 特色功能:内置权限判断逻辑,通过urole字段区分管理员和普通用户
package com.vilicode.bean;
public class User {  
	private Integer uid;
	private String uname;
	private String upwd;
	private String urealname;
	private Integer urole;
	private String umark;
	private String uphone;
	private String uaddress;
	private boolean isadmin=false;
	public Integer getUid() {
		return uid;
	}
	public void setUid(Integer uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public String getUpwd() {
		return upwd;
	}
	public void setUpwd(String upwd) {
		this.upwd = upwd;
	}
	public String getUrealname() {
		return urealname;
	}
	public void setUrealname(String urealname) {
		this.urealname = urealname;
	}

	public boolean isIsadmin() {
		return isadmin;
	}

	public void setIsadmin(boolean isadmin) {
		this.isadmin = isadmin;
	}

	public Integer getUrole() {
		return urole;
	}
	public void setUrole(Integer urole) {
		this.urole = urole;
		isadmin=urole==0?true:false;
	}
	public String getUmark() {
		return umark;
	}
	public void setUmark(String umark) {
		this.umark = umark;
	}
	public String getUphone() {
		return uphone;
	}

	public void setUphone(String uphone) {
		this.uphone = uphone;
	}

	public String getUaddress() {
		return uaddress;
	}

	public void setUaddress(String uaddress) {
		this.uaddress = uaddress;
	}

	public boolean isAdmin()
	{
		return urole==0;
	}

	@Override
	public String toString() {
		return "User{" +
				"uid=" + uid +
				", uname='" + uname + '\'' +
				", upwd='" + upwd + '\'' +
				", urealname='" + urealname + '\'' +
				", urole=" + urole +
				", umark='" + umark + '\'' +
				", uphone='" + uphone + '\'' +
				", uaddress='" + uaddress + '\'' +
				'}';
	}
}

Order – 订单实体

  • 作用:表示用户的订单信息,包含订单的整体信息
  • 核心属性:订单ID、总金额、商品数量、状态、支付方式、用户ID、收货信息等
  • 特色功能:内部集成购物车功能,包含添加、减少、删除商品的方法
package com.vilicode.bean;

import com.vilicode.Utils.PriceUtil;
import org.springframework.web.bind.annotation.RequestMapping;

import java.time.DateTimeException;
import java.util.*;

public class Order {
    private String oid;
    private Double ototal;
    private Integer oamount;
    private Integer ostatus;
    private Integer opaytype;
    private Integer uid;
    private String orealname;
    private String ophone;
    private String oaddress;
    private String odatetime;
    private Map<Integer,OrderItem> itemMap = new HashMap<Integer,OrderItem>();
    private List<OrderItem> itemList = new ArrayList<OrderItem>();
    public String getOid() {
        return oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public Double getOtotal() {
        return ototal;
    }

    public void setOtotal(Double ototal) {
        this.ototal = ototal;
    }

    public String getOdatetime() {
        return odatetime;
    }

    public void setOdatetime(String odatetime) {
        this.odatetime = odatetime;
    }
    public Integer getOamount() {
        return oamount;
    }

    public void setOamount(Integer oamount) {
        this.oamount = oamount;
    }

    public Integer getOstatus() {
        return ostatus;
    }

    public void setOstatus(Integer ostatus) {
        this.ostatus = ostatus;
    }

    public Integer getOpaytype() {
        return opaytype;
    }

    public void setOpaytype(Integer opaytype) {
        this.opaytype = opaytype;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getOrealname() {
        return orealname;
    }

    public void setOrealname(String orealname) {
        this.orealname = orealname;
    }

    public String getOphone() {
        return ophone;
    }

    public void setOphone(String ophone) {
        this.ophone = ophone;
    }

    public String getOaddress() {
        return oaddress;
    }

    public void setOaddress(String oaddress) {
        this.oaddress = oaddress;
    }

    public List<OrderItem> getItemList() {
        return itemList;
    }

    public void setItemList(List<OrderItem> itemList) {
        this.itemList = itemList;
    }

    public Map<Integer, OrderItem> getItemMap() {
        return itemMap;
    }

    public void setItemMap(Map<Integer, OrderItem> itemMap) {
        this.itemMap = itemMap;
    }

    public void addGoods(Book book) {
        if(itemMap.containsKey(book.getBid())) {
            OrderItem item = itemMap.get(book.getBid());
            item.setOiamount(item.getOiamount()+1);
        }else {
            OrderItem item = new OrderItem();
            item.setOiprice(book.getBprice());
            item.setOiamount(1);
            item.setBook(book);
            item.setOrder(this);
            item.setBid(book.getBid());
            itemMap.put(book.getBid(), item);
        }
        oamount++;

        ototal = PriceUtil.add(ototal,book.getBprice());
    }

    public void lessen(int bid) {
        if(itemMap.containsKey(bid)) {
            OrderItem item = itemMap.get(bid);
            item.setOiamount(item.getOiamount()-1);
            oamount--;
            ototal = PriceUtil.subtract(ototal,item.getOiprice());
            if(item.getOiamount()<=0) {
                itemMap.remove(bid);
            }
        }
    }
    public void delete(int bid)
    {
        if(itemMap.containsKey(bid)) {
            OrderItem item = itemMap.get(bid);
            ototal = PriceUtil.subtract(ototal,item.getOiamount()*item.getOiprice());
            oamount-=item.getOiamount();
            itemMap.remove(bid);
        }
    }
}

OrderItem – 订单项实体

  • 作用:表示订单中的具体商品项,连接订单和书籍
  • 核心属性:订单项ID、单价、数量、书籍ID、订单ID等
  • 关联关系:与Book和Order实体建立关联
package com.vilicode.bean;

public class OrderItem {
    private Integer oiid;
    private Double oiprice;
    private Integer oiamount;
    private Integer bid;
    private String oid;
    private Book book;
    private Order order;
    public Integer getOiid() {
        return oiid;
    }

    public void setOiid(Integer oiid) {
        this.oiid = oiid;
    }

    public Double getOiprice() {
        return oiprice;
    }

    public void setOiprice(Double oiprice) {
        this.oiprice = oiprice;
    }

    public Integer getOiamount() {
        return oiamount;
    }

    public void setOiamount(Integer oiamount) {
        this.oiamount = oiamount;
    }

    public Integer getBid() {
        return bid;
    }

    public void setBid(Integer bid) {
        this.bid = bid;
    }

    public String getOid() {
        return oid;
    }

    public void setOid(String oid) {
        this.oid = oid;
    }

    public Book getBook() {
        return book;
    }

    public void setBook(Book book) {
        this.book = book;
    }

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }
}

BookType – 书籍分类实体

  • 作用:表示书籍的分类信息
  • 核心属性:分类ID、分类名称
package com.vilicode.bean;

public class BookType {
    private Integer btid;
    private String btname;

    public Integer getBtid() {
        return btid;
    }

    public void setBtid(Integer btid) {
        this.btid = btid;
    }

    public String getBtname() {
        return btname;
    }

    public void setBtname(String btname) {
        this.btname = btname;
    }
}

Recommend – 推荐关系实体

  • 作用:表示书籍的推荐关系,管理书籍的推荐类型
  • 核心属性:推荐ID、推荐类型、书籍ID
package com.vilicode.bean;

public class Recommend {
    private Integer rid;
    private Integer rtype;
    private Integer bid;

    public Integer getRid() {
        return rid;
    }

    public void setRid(Integer rid) {
        this.rid = rid;
    }

    public Integer getRtype() {
        return rtype;
    }

    public void setRtype(Integer rtype) {
        this.rtype = rtype;
    }

    public Integer getBid() {
        return bid;
    }

    public void setBid(Integer bid) {
        this.bid = bid;
    }
}

Page – 分页实体

  • 作用:用于系统中所有列表数据的分页展示
  • 核心属性:当前页码、每页大小、总记录数、总页数、数据列表
  • 特色功能:提供计算总页数的方法
package com.vilicode.bean;

import java.util.List;

public class Page {
    private int pageNumber;
    private int pageSize;
    private int totalCount;
    private int totalPage;

    private List<Object> list;

    public void SetPageSizeAndTotalCount(int pageSize,int totalCount)
    {
        this.pageSize=pageSize;
        this.totalCount=totalCount;
        totalPage= (int)Math.ceil((double)totalCount/pageSize);
    }
    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public List<Object> getList() {
        return list;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }
}

Address类用于表示用户收货地址的数据模型

  1. 数据存储与映射:作为MyBatis ORM框架的映射对象,与数据库中存储用户地址的表结构对应
  2. 业务流程支持:在订单提交、用户中心地址管理等功能中,用于传递和处理地址信息
  3. 用户体验优化:支持用户管理多个收货地址,提高购物流程的便捷性
  4. 关联设计体现:通过uid属性与User实体类建立关联,体现了系统的关系型数据设计思想
  5. 分层架构支撑:通过配置文件明确划分了数据访问层(Mapper)、业务逻辑层(Service)和表现层(Controller)的边界和交互方式
  6. 框架集成:实现了Spring、SpringMVC和MyBatis三大框架的无缝集成
  7. 可配置性:将可变的配置信息(如数据库连接)与代码分离,提高了系统的可维护性和灵活性
  8. 自动化管理:大量使用包扫描和自动注入机制,减少了手动配置的工作量
  9. 该配置包是整个项目的骨架,确保了各个组件能够按照预定的方式协同工作,是项目正常运行的基础保障。

该类是一个典型的POJO(Plain Old Java Object),专注于数据的存储和访问,符合项目MVC架构中数据模型层的设计理念。

package com.vilicode.bean;

public class Address {
    private Integer aid;
    private Integer uid;
    private String aphone;
    private String aaddr;
    public Integer getAid() {
        return aid;
    }

    public void setAid(Integer aid) {
        this.aid = aid;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getAphone() {
        return aphone;
    }

    public void setAphone(String aphone) {
        this.aphone = aphone;
    }

    public String getAaddr() {
        return aaddr;
    }

    public void setAaddr(String aaddr) {
        this.aaddr = aaddr;
    }
}

com.vilicode.config核心配置包

负责配置整个项目的基础架构和运行环境。该包包含4个关键配置文件,共同实现了SSM(Spring+SpringMVC+MyBatis)框架的集成与配置。

jdbc.properties

  • 数据库连接配置文件,存储数据库访问的基础信息
  • 包含MySQL驱动类、连接URL(指定数据库名、编码、时区等参数)、用户名和密码
  • 采用外部化配置方式,便于环境切换和安全管理
className=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bookshop?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
user=root
password=123456

mybatis-config.xml

  • MyBatis框架的核心配置文件
  • 通过<typeAliases>配置实体类包扫描,简化映射文件中的类型引用
  • 通过<mappers>注册所有MyBatis映射文件,建立接口与SQL语句的关联
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <!-- 包名扫描 -->
        <package name="com.vilicode.bean"/>
    </typeAliases>
    <mappers>
        <mapper resource="com/vilicode/mapper/UserMapper.xml"/>
        <mapper resource="com/vilicode/mapper/BookMapper.xml"/>
        <mapper resource="com/vilicode/mapper/RecommendMapper.xml"/>
        <mapper resource="com/vilicode/mapper/BookTypeMapper.xml"/>
        <mapper resource="com/vilicode/mapper/OrderMapper.xml"/>
    </mappers>
</configuration>

spring.xml

  • Spring框架的核心配置文件
  • 导入数据库配置资源,配置DBCP数据库连接池
  • 配置SqlSessionFactoryBean,整合MyBatis与Spring
  • 通过MapperScannerConfigurer自动扫描并注册Mapper接口
  • 扫描service.impl包,管理业务层Bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 导入连接数据库的资源文件 -->
    <context:property-placeholder location="classpath:com/vilicode/config/jdbc.properties"/>
    <!-- 配置数据源 dbcp=database connection pool数据库连接池 -->
    <bean   id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${className}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    <!-- 配置SqlSessionFactoryBean,注入数据源和mybatis配置文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:com/vilicode/config/mybatis-config.xml"></property>
        <!-- 如果mybatis配置文件未扫描类的包,则在此处扫描,
        如果mybatis未指定mapper接口的映射实现文件,则在此处配置,为了增加程序的可读性通常
        在mybaits配置前两项 -->
    </bean>
    <!-- 通过MapperScannerConfigurer类自动完成基础包的扫描和Mapper接口的实现
    自动关联sqlSessionFactory -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.vilicode.mapper"></property>
    </bean>

    <!-- 扫描service -->
    <context:component-scan base-package="com.vilicode.service.impl"></context:component-scan>
</beans>

springMVC.xml

  • SpringMVC框架的配置文件
  • 扫描controller包,注册控制器Bean
  • 配置InternalResourceViewResolver视图解析器,设置JSP视图的前缀和后缀
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.vilicode.controller"></context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>

com.vilicode.controller包

com.vilicode.controller包是BookShop项目的控制器层,负责处理所有HTTP请求,实现前后端交互,是MVC架构中的控制器(Controller)组件。该包包含4个核心控制器类,共同构成了系统的请求处理中心。

求分发与处理:接收用户请求,调用相应的Service层方法处理业务逻辑

参数传递与校验:接收和验证请求参数,传递给业务层

视图渲染与响应:根据业务处理结果,选择合适的视图进行渲染或返回重定向

用户会话管理:管理用户登录状态、购物车信息等会话数据

权限控制:部分控制器方法实现了基于用户角色的权限控制

前后端交互桥梁:连接前端页面和后端业务逻辑,是系统与用户交互的主要入口

该包严格遵循了SpringMVC的设计理念,通过注解(如@RequestMapping、@Autowired)实现了请求映射和依赖注入,使代码结构清晰、职责分明。控制器层的设计确保了前端请求能够被正确路由和处理,同时也为系统提供了良好的扩展性

IndexController

  • 系统首页控制器,处理根路径和首页请求
  • 负责查询和展示推荐图书(滚动图书、热门图书、新书)
  • 加载图书分类信息到应用上下文,供全局使用
  • 实现首页数据的初始化和展示逻辑
package com.vilicode.controller;

import com.vilicode.bean.Book;
import com.vilicode.bean.BookType;
import com.vilicode.service.BookService;
import com.vilicode.service.BookTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Controller
public class IndexController {

    @Autowired
    public BookService bookService;

    @Autowired
    public BookTypeService bookTypeService;

    @RequestMapping("/index")
    public String Init(HttpServletRequest request)
    {
        List<Book> books=bookService.queryBookByRecommendType(1,1,6);
        if(books.size()>0)
            request.setAttribute("scrollBook",books.get(0));
        books=bookService.queryBookByRecommendType(2,1,6);
        request.setAttribute("hotList",books);
        books=bookService.queryBookByRecommendType(3,1,8);
        request.setAttribute("newList",books);
        List<BookType> bookTypes=bookTypeService.queryBookTypes();
        request.getServletContext().setAttribute("bookTypes",bookTypes);
        return "index";
    }
}

BookController

  • 图书管理控制器,功能最丰富的控制器之一
  • 处理图书浏览、搜索、详情查看等用户操作
  • 支持按推荐类型、分类、关键词等多种方式查询图书
  • 提供管理员功能,包括图书分类管理、图书推荐设置、图书信息CRUD
  • 实现图书数据的分页展示
package com.vilicode.controller;

import com.vilicode.bean.Book;
import com.vilicode.bean.Page;
import com.vilicode.bean.User;
import com.vilicode.mapper.RecommendMapper;
import com.vilicode.service.BookService;
import com.vilicode.service.BookTypeService;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;


@Controller
public class BookController {

    @Autowired
    public BookService bookService;
    @Autowired
    public BookTypeService bookTypeService;


    @RequestMapping("/recommend_books")
    public String showRecommendBook(int rtype, int pageNumber, HttpServletRequest request)
    {
        if(pageNumber<=0)
            pageNumber=1;
        Page p= bookService.queryBookByRecommendType(rtype,pageNumber);

        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p = bookService.queryBookByRecommendType(rtype,p.getTotalPage());
            }
        }
        request.setAttribute("p", p);
        request.setAttribute("t", rtype);
        return "recommend_list";
    }

    @RequestMapping("/booktypes_list")
    public String showBooksByBookTypeID(int pageNumber, int btid,HttpServletRequest request)
    {
        String btname="";
        if(btid!=0)
        {
            btname=bookTypeService.queryBookTypeNameByBookTypeID(btid);
        }
        request.setAttribute("t",btname);
        //List<Goods> list=gService.selectGoodsByTypeID(id,1,8);
        //request.setAttribute("goodsList",list);
        if(pageNumber<=0)
            pageNumber=1;
        Page p=bookTypeService.queryBookByBookTypeID(btid,pageNumber);

        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p=bookTypeService.queryBookByBookTypeID(btid,p.getTotalPage());
            }
        }

        request.setAttribute("p",p);
        request.setAttribute("btid",btid);
        return "booktypes_list";
    }

    @RequestMapping("/book_detail")
    public String showBookByID(int bid,HttpServletRequest request)
    {
        Book book= bookService.queryBookByID(bid);
        request.setAttribute("book",book);
        return "book_detail";
    }

    @RequestMapping("/search_books")
    public String SearchBooksByKeyword(int pageNumber,String keyword,HttpServletRequest request) throws UnsupportedEncodingException {

        if(pageNumber<=0)
        {
            pageNumber=1;
        }
        Page p =bookService.searchBooksByKeyword(keyword,pageNumber);

        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p =bookService.searchBooksByKeyword(keyword,p.getTotalPage());
            }
        }
        request.setAttribute("p", p);
        request.setAttribute("keyword", URLEncoder.encode(keyword,"utf-8"));
        return "book_search";
    }

    @RequestMapping("/admin/type_list")
    public String ShowBookTypes(HttpServletRequest request)
    {
        request.setAttribute("list",bookTypeService.queryBookTypes());
        return "admin/type_list";
    }
    @RequestMapping("/admin/type_add")
    public String CreateBookType(String btname,HttpServletRequest request)
    {
        boolean result= bookTypeService.addBookType(btname);
        UpdateBookType(request);
        return "redirect:type_list.action";
    }
    @RequestMapping("/admin/type_delete")
    public String RemoveBookType(int btid,HttpServletRequest request)
    {
        boolean result= bookTypeService.removeBookType(btid);
        UpdateBookType(request);
        return "redirect:type_list.action";
    }

    @RequestMapping("/admin/type_update")
    public String ModifyBookType(int btid,String btname,HttpServletRequest request)
    {
        boolean result= bookTypeService.modifyBookType(btid,btname);
        UpdateBookType(request);
        return "redirect:type_list.action";
    }

    public void UpdateBookType(HttpServletRequest request)
    {
        if(request.getServletContext().getAttribute("bookTypes")==null)
        {
            request.getServletContext().setAttribute("bookTypes",bookTypeService.queryBookTypes());
        }
        else
        {
            request.getServletContext().removeAttribute("bookTypes");
            request.getServletContext().setAttribute("bookTypes",bookTypeService.queryBookTypes());
        }
    }

    @RequestMapping("/admin/book_list")
    public String ShowBookByRecommend(int pageNumber,int rtype,HttpServletRequest request)
    {
        if(pageNumber<=0)
            pageNumber=1;
        Page p = bookService.queryBookByRecommendType(rtype, pageNumber);
        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p = bookService.queryBookByRecommendType(rtype, p.getTotalPage());
            }
        }
        request.setAttribute("p", p);
        request.setAttribute("rtype", rtype);
        return "/admin/book_list";
    }
    @RequestMapping("/admin/book_change")
    public String ChangeBookRecommend(int bid,int rtype,String method,int page)
    {
        boolean result=false;
        if(method.equals("add")) {
             result=bookService.addRecommend(bid,rtype);
        }else {
            result=bookService.remoteRecommend(bid,rtype);
        }
       return  "redirect:book_list.action?pageNumber=1&rtype="+page;
    }

    @RequestMapping("/admin/book_delete")
    public String DeleteBook(int bid,int rtype)
    {
        boolean result=bookService.removeBookByBid(bid);
        //
        return  "redirect:book_list.action?pageNumber=1&rtype="+rtype;
    }

    @RequestMapping("/admin/book_add")
    public String uploadimg(HttpServletRequest request) throws Exception{
        DiskFileItemFactory factory=new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            List<FileItem> list = upload.parseRequest(request);
            Book b = new Book();
            for(FileItem item:list) {
                if(item.isFormField()) {
                    switch(item.getFieldName()) {
                        case "bname":
                            b.setBname(item.getString("utf-8"));
                            break;
                        case "bprice":
                            b.setBprice(Double.parseDouble(item.getString("utf-8")));
                            break;
                        case "bmark":
                            b.setBmark(item.getString("utf-8"));
                            break;
                        case "bstock":
                            b.setBstock(Integer.parseInt(item.getString("utf-8")));
                            break;
                        case "btid":
                           b.setBtid(Integer.parseInt(item.getString("utf-8")));
                            break;
                        case "bisbn":
                            b.setBisbn(item.getString("utf-8"));
                            break;
                        case "bauthor":
                            b.setBauthor(item.getString("utf-8"));
                            break;
                        case "bpublisher":
                            b.setBpublisher(item.getString("utf-8"));
                            break;
                    }
                }else {
                    if(item.getInputStream().available()<=0)continue;
                    String fileName = item.getName();
                    fileName = fileName.substring(fileName.lastIndexOf("."));
                    fileName = "/"+new Date().getTime()+fileName;
                    String path = request.getServletContext().getRealPath("/images")+fileName;
                    InputStream in = item.getInputStream();
                    FileOutputStream out = new FileOutputStream(path);
                    byte[] buffer = new byte[1024];
                    int len=0;
                    while( (len=in.read(buffer))>0 ) {
                        out.write(buffer);
                    }
                    in.close();
                    out.close();
                    item.delete();
                    switch(item.getFieldName()) {
                        case "bcover":
                            b.setBcover("images"+fileName);
                            break;
                        case "bimage1":
                            b.setBimage1("images"+fileName);
                            break;
                        case "bimage2":
                            b.setBimage2("images"+fileName);
                            break;
                    }
                }
            }
            bookService.addBook(b);
        } catch (FileUploadException e) {
            System.out.println(e.getMessage());
        }

        return  "redirect:book_list.action?pageNumber=1&rtype=0";

    }

    @RequestMapping("/admin/book_edit_show")
    public String ShowBookByBid(int bid,HttpServletRequest request)
    {
        Book b = bookService.queryBookByID(bid);
        request.setAttribute("g", b);
        return "admin/book_edit";
    }

    @RequestMapping("/admin/book_update")
    public String ModifyBook(HttpServletRequest request) throws Exception {
        DiskFileItemFactory factory=new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        try {
            List<FileItem> list = upload.parseRequest(request);
            Book b=new Book();
            for(FileItem item:list) {
                if(item.isFormField()) {
                    switch(item.getFieldName()) {
                        case "bid":
                            b.setBid(Integer.parseInt(item.getString("utf-8")));
                            break;
                        case "bname":
                            b.setBname(item.getString("utf-8"));
                            break;
                        case "bprice":
                            b.setBprice(Double.parseDouble(item.getString("utf-8")));
                            break;
                        case "bmark":
                            b.setBmark(item.getString("utf-8"));
                            break;
                        case "bstock":
                            b.setBstock(Integer.parseInt(item.getString("utf-8")));
                            break;
                        case "btid":
                            b.setBtid(Integer.parseInt(item.getString("utf-8")));
                            break;
                        case "bisbn":
                            b.setBisbn(item.getString("utf-8"));
                            break;
                        case "bauthor":
                            b.setBauthor(item.getString("utf-8"));
                            break;
                        case "bpublisher":
                            b.setBpublisher(item.getString("utf-8"));
                            break;
                        case "bcover":
                            b.setBcover(item.getString("utf-8"));
                            break;
                        case "bimage1":
                            b.setBimage1(item.getString("utf-8"));
                            break;
                        case "bimage2":
                            b.setBimage2(item.getString("utf-8"));
                            break;
                    }
                }else {
                    if(item.getInputStream().available()<=0)continue;
                    String fileName = item.getName();
                    fileName = fileName.substring(fileName.lastIndexOf("."));
                    fileName = "/"+new Date().getTime()+fileName;
                    String path = request.getServletContext().getRealPath("/images")+fileName;
                    InputStream in = item.getInputStream();
                    FileOutputStream out = new FileOutputStream(path);
                    byte[] buffer = new byte[1024];
                    int len=0;
                    while( (len=in.read(buffer))>0 ) {
                        out.write(buffer);
                    }
                    in.close();
                    out.close();
                    item.delete();
                    switch(item.getFieldName()) {
                        case "bcover":
                            b.setBcover("images"+fileName);
                            break;
                        case "bimage1":
                            b.setBimage1("images"+fileName);
                            break;
                        case "bimage2":
                            b.setBimage2("images"+fileName);
                            break;
                    }
                }
            }
            bookService.modifyBook(b);
            //Service.update(g);
            //request.getRequestDispatcher("/admin/goods_list?pageNumber="+pageNumber+"&type="+type).forward(request, response);
        } catch (FileUploadException e) {
            System.out.println(e.getMessage());
        }

        return  "redirect:book_list.action?pageNumber=1&rtype=0";
    }
}

UserController

  • 用户管理控制器,处理所有用户相关操作
  • 实现用户注册、登录、登出功能
  • 管理用户个人信息(修改手机号、地址、密码等)
  • 提供管理员用户管理功能(添加、修改、删除、列表查询)
  • 负责用户会话管理和权限控制
package com.vilicode.controller;

import com.vilicode.Utils.MyCipher;
import com.vilicode.bean.Page;
import com.vilicode.bean.User;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.vilicode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/login")
    public String login(User tempUser, HttpServletRequest request, HttpSession session)
    {
        String path="";
        User user=userService.login(tempUser.getUname());
        if(user==null)
        {
            request.setAttribute("failMsg","用户名不存在!");
            return "user_login";
        }
        if(tempUser.getUpwd().equals(user.getUpwd()))
        {
            session.setAttribute("user", user);
            request.setAttribute("msg","登录成功!");
            return "redirect:/index.action";
        }else{
            request.setAttribute("failMsg","密码错误!");
            return "user_login";
        }
    }
    @RequestMapping("/register")
    public String register(User user, HttpServletRequest request)
    {
        String path="";
        user.setUrole(1);
        user.setUmark("普通用户");
        if(userService.register(user))
        {
            path="redirect:user_login.jsp";
        }else{
            request.setAttribute("msg","用户名重复!");
            path="user_register";
        }
        return path;
    }

    @RequestMapping("/admin/user_add")
    public String AddUser(User user, HttpServletRequest request)
    {
        user.setUrole(1);
        user.setUmark("普通用户");
        if(userService.register(user))
        {
            return "redirect:user_list.action?pageNumber=1";
        }else{
            request.setAttribute("msg","用户名重复!");
            return "admin/user_add";
        }
    }
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request)
    {
        request.getSession().removeAttribute("user");
        return "redirect:index.action";
    }
    @RequestMapping("/admin/logout")
    public String adminLogout(HttpServletRequest request)
    {
        request.getSession().removeAttribute("user");
        return "redirect:index.action";
    }

    @RequestMapping("/change_phone_and_address")
    public String updateUphoneAndUaddress(int uid,String uphone,String uaddress,HttpServletRequest request)
    {
        boolean result=userService.UpdatePhoneAndAddress(uid,uphone,uaddress);
        if(result)
        {
            request.setAttribute("msg","修改成功!");
            User u=(User)request.getSession().getAttribute("user");
            u.setUaddress(uaddress);
            u.setUphone(uphone);
            request.getSession().removeAttribute("user");
            request.getSession().setAttribute("user",u);
            return "user_center";
        }
        else
        {
            request.setAttribute("failMsg","修改密码时出现错误,请确认原密码是否正确或联系管理员!");
            return "user_center";
        }
    }

    @RequestMapping("/change_password")
    public String updatePassword(int uid,String oldupwd,String upwd,HttpServletRequest request)
    {
        boolean result=userService.UpdatePassword(uid,upwd,oldupwd);
        if(result)
        {
            request.setAttribute("msg","修改成功!");
            MyCipher myCipher=new MyCipher();
            User u=(User)request.getSession().getAttribute("user");
            u.setUpwd(myCipher.encrypt(upwd,"!"));
            request.getSession().removeAttribute("user");
            request.getSession().setAttribute("user",u);
            return "user_center";
        }
        else
        {
            request.setAttribute("failMsg","修改密码时出现错误,请确认原密码是否正确或联系管理员!");
            return "user_center";
        }
    }

    @RequestMapping("admin/change_password")
    public String updatePassword(int uid,String upwd,HttpServletRequest request)
    {
        boolean result=userService.UpdatePassword(uid,upwd);
        if(result)
        {
            MyCipher myCipher=new MyCipher();
            request.setAttribute("msg","修改成功!");
            User u=(User)request.getSession().getAttribute("user");
            u.setUpwd(myCipher.encrypt(upwd,"!"));
            request.getSession().removeAttribute("user");
            request.getSession().setAttribute("user",u);
            return "redirect:user_list.action?pageNumber=1";
        }
        else
        {
            request.setAttribute("failMsg","修改失败");
            return "redirect:user_list.action?pageNumber=1";
        }
    }

    @RequestMapping("admin/user_list")
    public String ShowUserList(int pageNumber,HttpServletRequest request)
    {
        if(pageNumber<=0)
            pageNumber=1;
        Page p = userService.queryUser(pageNumber);
        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p = userService.queryUser(p.getTotalPage());
            }
        }
        request.setAttribute("p", p);
        return "admin/user_list";
    }

    @RequestMapping("admin/user_delete")
    public String DeleteUser(int uid)
    {
        boolean result= userService.deleteUser(uid);
        return "redirect:user_list.action?pageNumber=1";
    }

    @RequestMapping("/admin/user_edit_show")
    public String ChangeUser(int uid,HttpServletRequest request)
    {
        User user=userService.queryUserByUid(uid);
        if(user==null)
            return "redirect:user_list.action?pageNumber=1";
        else
        {
            request.setAttribute("u",user);
            return "admin/user_edit";
        }
    }
    @RequestMapping("/admin/user_update")
    public String updateUser(int uid,String uphone,String uaddress,HttpServletRequest request)
    {
        boolean result=userService.UpdatePhoneAndAddress(uid,uphone,uaddress);
        if(result)
        {
            User user=(User)request.getSession().getAttribute("user");
            if(user.getUid()==uid)
            {
                user.setUaddress(uaddress);
                user.setUphone(uphone);
                request.getSession().removeAttribute("user");
                request.getSession().setAttribute("user",user);
            }
        }
        return "redirect:user_list.action?pageNumber=1";
    }
}

OrderController

  • 订单管理控制器,处理购物车和订单流程
  • 实现购物车商品的添加、修改、删除
  • 负责订单的确认和提交
  • 管理用户的订单列表和订单状态
  • 提供管理员订单管理功能(订单列表、状态修改、删除)
  • 处理订单支付相关逻辑
package com.vilicode.controller;

import com.vilicode.bean.Book;
import com.vilicode.bean.Order;
import com.vilicode.bean.Page;
import com.vilicode.bean.User;
import com.vilicode.service.BookService;
import com.vilicode.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

@Controller
public class OrderController {
    @Autowired
    public BookService bookService;
    @Autowired
    public OrderService orderService;

    @RequestMapping(value = "/books_buy")
    public void AddBookToCart(@RequestParam("bid") int bid, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Order o = null;
        if(request.getSession().getAttribute("order") != null) {
            o = (Order) request.getSession().getAttribute("order");
        }else {
            o = new Order();
            o.setOamount(0);
            o.setOtotal(0.0);
            request.getSession().setAttribute("order", o);
        }
        Book book = bookService.queryBookByID(bid);
        if(book.getBstock()>0) {
            o.addGoods(book);
            response.getWriter().print("ok");
        }else {
            response.getWriter().print("fail");
        }
    }
    @RequestMapping(value = "/books_lessen")
    public void LessenBookToCart(@RequestParam("bid") int bid, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Order o = (Order) request.getSession().getAttribute("order");
        o.lessen(bid);
        response.getWriter().print("ok");
    }

    @RequestMapping(value = "/books_delete")
    public void DeleteBookToCart(@RequestParam("bid") int bid, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Order o = (Order) request.getSession().getAttribute("order");
        o.delete(bid);
        response.getWriter().print("ok");
    }

    @RequestMapping(value = "/order_confirm")
    public String OrderConfirm( int opaytype,HttpServletRequest request)
    {
        Order o = (Order) request.getSession().getAttribute("order");
        o.setOstatus(2);
        o.setOpaytype(opaytype);
        User user=(User)request.getSession().getAttribute("user");
        o.setUid(user.getUid());
        o.setOrealname(user.getUrealname());
        o.setOphone(user.getUphone());
        o.setOaddress(user.getUaddress());
        boolean result=orderService.addOrder(o);
        if(result)
        {
            request.getSession().removeAttribute("order");
            request.setAttribute("msg", "订单支付成功!");
            return "order_result";
        }
        else
        {
            request.setAttribute("failmsg", "订单支付失败!");
            return "order_result";
        }

    }
    @RequestMapping("/order_submit")
    public String OrderSubmit(HttpServletRequest request)
    {
        if(request.getSession().getAttribute("user")!=null) {
            return "order_submit";
        }else {
            request.setAttribute("failMsg", "请登录后,再提交订单!");
            return "redirect:user_login.jsp";
        }
    }

    @RequestMapping("order_list")
    public String ShowOrderByUid(HttpServletRequest request)
    {
        User user=(User)request.getSession().getAttribute("user");
        if(user==null)
        {
            return "redirect:/index.action";
        }
        List<Order> orderList=orderService.queryOrderByUid(user.getUid());
        request.setAttribute("orderList", orderList);
        return "order_list";
    }

    @RequestMapping("/admin/order_list")
    public String ShowOrderList(int pageNumber,int ostatus,HttpServletRequest request)
    {
        request.setAttribute("ostatus", ostatus);
        if(pageNumber<=0)
            pageNumber=1;
        Page p = orderService.queryOrdersByOstatus(ostatus,pageNumber);
        if(p.getTotalPage()==0)
        {
            p.setTotalPage(1);
            p.setPageNumber(1);
        }
        else {
            if(pageNumber>=p.getTotalPage()+1)
            {
                p = orderService.queryOrdersByOstatus(ostatus,p.getTotalPage());
            }
        }

        request.setAttribute("p", p);
        return "admin/order_list";
    }

    @RequestMapping("/admin/order_status_change")
    public String ChangeOrderStatusByOid(String oid,int ostatus,HttpServletRequest request)
    {
        boolean result= orderService.updateOrderStatus(oid,ostatus);
        return "redirect:order_list.action?pageNumber=1&ostatus="+ostatus;
    }

    @RequestMapping("/admin/order_delete")
    public String DeleteOrderByOid(String oid,int ostatus,HttpServletRequest request)
    {
        boolean result= orderService.deleteOrderByOid(oid);
        return "redirect:order_list.action?pageNumber=1&ostatus="+ostatus;
    }

}

com.vilicode.filter;

com.vilicode.filter包是BookShop项目的过滤器包,负责实现Web应用中的请求过滤功能,用于在请求到达控制器之前进行预处理或后处理。目前该包仅包含一个核心过滤器类:AdminFilter

AdminFilter过滤器

  1. 拦截范围:通过@WebFilter注解配置,拦截所有访问/admin/*路径下的请求
  2. 权限验证逻辑
    • 从HTTP会话(HttpSession)中获取当前登录用户对象(user)
    • 检查用户对象是否存在(即用户是否已登录)
    • 检查用户是否具有管理员权限(通过isAdmin()方法判断)
    • 如果用户未登录不是管理员,则重定向到网站首页(../index.action)
    • 如果用户是管理员,则允许请求继续传递到后续处理组件(调用chain.doFilter()
    • 在项目安全架构中的作用
    • 权限控制:作为系统的安全屏障,防止未授权用户访问管理后台
    • 请求预处理:在请求到达控制器之前进行权限验证,是AOP(面向切面编程)思想的一种具体实现
    • 集中式安全管理:将管理后台的权限验证逻辑集中到一处,便于维护和扩展
    • 用户体验保护:对未授权访问提供友好的重定向处理
    • 这个过滤器是项目安全机制的重要组成部分,通过URL级别的拦截和会话级别的权限验证,确保了管理功能的安全性。虽然目前包中只有一个过滤器,但这种结构设计允许系统根据需要轻松添加更多过滤器,如字符编码过滤器、日志记录过滤器等。

技术细节

  • 实现了javax.servlet.Filter接口,重写了三个核心方法:
    • init():过滤器初始化方法(此处为空实现)
    • doFilter():核心过滤逻辑实现
    • destroy():过滤器销毁方法(此处为空实现)
  • 使用了ServletRequestServletResponse的HTTP特定实现类进行类型转换,以便访问HTTP特定功能
package com.vilicode.filter;

import com.vilicode.bean.User;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "AdminFilter",urlPatterns = "/admin/*")
public class AdminFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse requestp = (HttpServletResponse)resp;
        User u = (User) request.getSession().getAttribute("user");
        if(u==null || u.isAdmin()==false) {
            requestp.sendRedirect("../index.action");
        }else {
            // pass the request along the filter chain
            chain.doFilter(req, resp);
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

com.vilicode.mapper包

1. 包结构与组成
该包包含两类文件:

Java接口文件:定义数据操作方法(如BookMapper.java)
XML映射文件:实现具体SQL语句(如BookMapper.xml)
目前包含5组核心映射器:

BookMapper:图书数据操作
BookTypeMapper:图书分类数据操作
OrderMapper:订单数据操作
RecommendMapper:推荐关系数据操作
UserMapper:用户数据操作
3. 在项目架构中的作用
数据访问抽象:通过接口方式抽象数据库操作,实现业务逻辑与数据访问分离

SQL与代码分离:将SQL语句集中管理在XML文件中,便于维护和优化

对象关系映射:自动完成Java对象与数据库表之间的转换(ORM)

框架集成支持:通过Spring配置(MapperScannerConfigurer)实现自动扫描和装配

参数映射与结果映射:支持复杂参数传递和结果集映射,包括集合、分页等

4. 工作流程
服务层(Service)通过依赖注入调用Mapper接口方法
MyBatis框架根据接口方法名匹配XML中的SQL语句
执行SQL并将结果自动映射为Java对象返回给服务层
总结来说,com.vilicode.mapper包是项目的数据访问核心,它借助MyBatis框架实现了高效、灵活的数据库交互,是连接业务逻辑层与底层数据库的重要桥梁。
总结:Mapper接口与XML的协作模式
接口定义契约:每个Mapper接口定义了数据操作的契约(方法签名)
XML实现具体逻辑:对应的XML文件提供具体的SQL实现
自动映射:MyBatis框架自动将Java对象与数据库表进行映射转换
依赖注入:Spring框架通过自动扫描将Mapper接口实例注入到Service层
这种设计实现了业务逻辑与数据访问的解耦,使SQL语句集中管理便于维护,同时提供了灵活的参数传递和结果映射机制。

BookMapper接口

BookMapper接口是一个Java接口,主要负责定义图书数据操作的契约,它规定了可以对图书数据执行哪些操作,但不包含具体的实现逻辑。

  • 抽象数据操作:将图书的各种数据操作抽象为方法,形成统一的API
  • 约定方法签名:规定了方法名称、参数类型和返回值类型
  • 支持依赖注入:Spring框架可以通过接口自动注入实现类
  • 解耦业务逻辑与数据访问:Service层只需调用接口方法,无需关心具体实现
package com.vilicode.mapper;

import com.vilicode.bean.Book;

import java.util.List;

public interface BookMapper {
    public void addBook(Book book);             //添加一本书籍
    public void removeBook(int bid);            //删除一本书籍
    public void modifyBook(Book book);          //修改一本书籍
    public List<Book> queryBooks(int pageIndex,int pageSize);             //查询全部书籍
    public int queryCountOfBooks();
    public List<Book> queryBooksByKeyword(String keyword, int pageIndex,int pageSize);             //查询全部书籍
    public int queryCountOfBooksByKeyword(String keyword);
    public Book queryBookById(int bid);         //按bid查询一本书籍
    public Book queryBookByIsbn(String bisbn);  //按bisbn查询一本书籍
    public void deleteBookByBtid(int btid);
}

BookMapper.xml

BookMapper.xml是一个XML配置文件,主要负责为BookMapper接口中的方法提供具体的SQL实现,并定义数据映射规则。

数据映射功能

参数映射:通过#{参数名}语法将Java对象的属性映射到SQL语句中

结果映射:通过resultType="Book"将查询结果自动映射为Book对象

列名别名映射:使用bt_id btid这样的别名映射数据库列名到Java对象属性名

动态SQL支持

<set>标签:智能生成UPDATE语句中的SET部分,自动处理逗号

<where>标签:智能生成WHERE条件,自动处理AND/OR三、BookMapper接口与XML的协同工作机制

  1. 绑定关系:通过XML中的namespace="com.vilicode.mapper.BookMapper"将XML与接口绑定
  2. 方法匹配:XML中的SQL语句ID必须与接口中的方法名完全对应
  3. 参数传递:MyBatis自动将接口方法的参数传递给XML中对应的SQL语句
  4. 结果转换:MyBatis自动将SQL执行结果转换为接口方法指定的返回类型
四、在项目中的应用场景
  1. 图书管理:后台管理员添加、编辑、删除图书
  2. 图书展示:前台展示图书列表、图书详情
  3. 图书搜索:根据关键词搜索图书
  4. 图书分类:根据分类查询图书
  5. 分页功能:实现图书列表的分页展示
五、技术优势
  1. SQL集中管理:所有SQL语句集中在XML文件中,便于维护和优化
  2. 代码与SQL分离:Java代码专注于业务逻辑,SQL专注于数据操作
  3. 灵活的映射:支持复杂的参数映射和结果映射
  4. 动态SQL:支持条件判断、循环等动态SQL功能
  5. 性能优化:可以针对特定查询进行SQL优化

总结来说,BookMapper接口与BookMapper.xml共同构成了项目中图书数据访问的核心,它们通过MyBatis框架实现了高效、灵活的数据库交互,为整个系统提供了基础的数据支持。

<?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.vilicode.mapper.BookMapper">
<insert id="addBook" parameterType="Book">
insert into book(b_isbn,b_name,b_author,b_publisher,b_cover,b_image1,b_image2,b_price,bt_id,
b_stock,b_mark) values(#{bisbn},#{bname},#{bauthor},#{bpublisher},#{bcover},#{bimage1},#{bimage2},
#{bprice},#{btid},#{bstock},#{bmark})
</insert>

<update id="modifyBook" parameterType="Book">
update book
<set>
b_isbn=#{bisbn},b_name=#{bname},b_author=#{bauthor},b_publisher=#{bpublisher},
b_cover=#{bcover},b_image1=#{bimage1},b_image2=#{bimage2},b_price=#{bprice},bt_id=#{btid},
b_stock=#{bstock},b_mark=#{bmark},b_mark=#{bmark}
</set>
<where>
b_id=#{bid}
</where>
</update>

<delete id="removeBook" parameterType="int">
delete from book
<where>
b_id=#{value}
</where>
</delete>

<select id="queryBooks" resultType="Book">
select t.bt_id btid,bt_name btname,b_id bid,b_isbn bisbn,b_name bname,b_author bauthor,b_publisher bpublisher,
b_cover bcover,b_image1 bimage1,b_image2 bimage2,b_price bprice,b_stock bstock,b_mark bmark from booktype t,book b
where t.bt_id=b.bt_id order by b_id limit #{0},#{1}
</select>

<select id="queryBookById" resultType="Book" parameterType="int">
select t.bt_id btid,bt_name btname,b_id bid,b_isbn bisbn,b_name bname,b_author bauthor,b_publisher bpublisher,
b_cover bcover,b_image1 bimage1,b_image2 bimage2,b_price bprice,b_stock bstock,b_mark bmark from booktype t,book b
where t.bt_id=b.bt_id and b.b_id=#{value}
</select>

<select id="queryBookByIsbn" resultType="Book" parameterType="String">
select t.bt_id btid,bt_name btname,b_id bid,b_isbn bisbn,b_name bname,b_author bauthor,b_publisher bpublisher,
b_cover bcover,b_image1 bimage1,b_image2 bimage2,b_price bprice,b_stock bstock,b_mark bmark from booktype t,book b
where t.bt_id=b.bt_id and b.b_isbn=#{value}
</select>
<select id="queryCountOfBooks" resultType="int">
select count(*) from book;
</select>
<select id="queryCountOfBooksByKeyword" resultType="int" parameterType="String">
select count(*) from book where b_name like "%"#{value}"%"
</select>
<select id="queryBooksByKeyword" resultType="Book">
select t.bt_id btid,bt_name btname,b_id bid,b_isbn bisbn,b_name bname,b_author bauthor,b_publisher bpublisher,
b_cover bcover,b_image1 bimage1,b_image2 bimage2,b_price bprice,b_stock bstock,b_mark bmark from booktype t,book b
where t.bt_id=b.bt_id and b_name like "%"#{0}"%" limit #{1},#{2}
</select>
<delete id="deleteBookByBtid" parameterType="int">
delete from book where b_id=#{value}
</delete>
</mapper>

OrderMapper接口与OrderMapper.xml

作用:负责订单相关的数据操作,主要功能包括:

  • 订单管理:创建订单、更新订单状态、取消订单
  • 订单查询:查询用户订单列表、查询订单详情
  • 订单统计:可能包含订单金额统计、订单状态统计等功能
  • 订单项管理:处理订单与图书之间的多对多关系

应用场景:用户下单流程、订单历史查询、订单状态跟踪、后台订单管理等

package com.vilicode.mapper;

import com.vilicode.bean.Order;
import com.vilicode.bean.OrderItem;

import java.util.List;

public interface OrderMapper {
    public void addOrder(Order order);
    public void addOrderItem(OrderItem orderItem);
    public void deleteOrder(String oid);
    public void deleteOrderItem(String oid);
    public List<Order> queryOrderByUid(int uid);
    public List<OrderItem> queryOrderItemByUidAndOid(String oid);
    public List<Order> queryOrder(int pageIndex,int pageSize);
    public int queryCountOfOrder();
    public List<Order> queryOrderByOstatus(int ostatus,int pageIndex,int pageSize);
    public int queryCountOfOrderByOstatus(int ostatus);
    public void updateOrderStatus(String oid,int ostatus);
}
<?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.vilicode.mapper.OrderMapper">
    <insert id="addOrder" parameterType="Order">
      insert into `Order`(o_id,o_total,o_amount,o_status,o_paytype,u_id,o_realname,o_phone,o_address)
      values(#{oid},#{ototal},#{oamount},#{ostatus},#{opaytype},#{uid},#{orealname},#{ophone},#{oaddress})
    </insert>
    <insert id="addOrderItem" parameterType="OrderItem">
      insert into OrderItem(oi_price,oi_amount,b_id,o_id)
      values(#{oiprice},#{oiamount},#{bid},#{oid})
    </insert>
    <delete id="deleteOrder" parameterType="String">
      delete from `order` where o_id=#{value}
    </delete>
    <delete id="deleteOrderItem" parameterType="String">
      delete from orderitem where o_id=#{value}
    </delete>
    <select id="queryOrderByUid" parameterType="int" resultType="Order">
        select o_id oid,o_total ototal,o_amount oamount,o_status ostatus,o_paytype opaytype,u_id uid,
        o_realname orealname,o_phone ophone,o_address oaddress,o_datetime odatetime from `order`
        where u_id=#{value}
    </select>
    <select id="queryOrderItemByUidAndOid" resultType="OrderItem" parameterType="String">
        select oi_id oiid,oi_price oiprice,oi_amount oiamount,b_id bid,o_id oid
        from orderitem where o_id=#{0}
    </select>
    <select id="queryOrder" resultType="Order">
        select o_id oid,o_total ototal,o_amount oamount,o_status ostatus,o_paytype opaytype,u_id uid,
        o_realname orealname,o_phone ophone,o_address oaddress,o_datetime odatetime from `order`
        limit #{0},#{1}
    </select>
    <select id="queryCountOfOrder" resultType="int">
      select count(*) from `order`
    </select>
    <select id="queryOrderByOstatus" resultType="Order" parameterType="int">
        select o_id oid,o_total ototal,o_amount oamount,o_status ostatus,o_paytype opaytype,u_id uid,
        o_realname orealname,o_phone ophone,o_address oaddress,o_datetime odatetime from `order`
        where o_status=#{0} limit #{1},#{2}
    </select>
    <select id="queryCountOfOrderByOstatus" resultType="int" parameterType="int">
      select count(*) from `order` where o_status=#{value}
    </select>
    <update id="updateOrderStatus">
        update `order`
        <set>
            o_status=#{1}
        </set>
        <where>
           o_id=#{0}
        </where>
    </update>
</mapper>

RecommendMapper接口与RecommendMapper.xml

作用:负责图书推荐关系的数据操作,主要功能包括:

  • 推荐关系管理:添加、删除图书推荐关系
  • 推荐列表查询:根据用户ID或图书ID查询推荐内容
  • 推荐算法支持:为推荐系统提供数据基础

应用场景:首页推荐图书展示、图书详情页的相关推荐、个性化推荐功能等

package com.vilicode.mapper;

import com.vilicode.bean.Book;

import java.util.List;

public interface RecommendMapper {
    public List<Book> queryBookByRecommendType(int rtype,int pageIndex,int pageSize); //按推荐类型查询图书
    public int queryRecommendCountOfBooksByTypeID(int rtype);//根据推荐类型查询总数
    public int queryBookByRtypeAndBid(int rtype,int bid);
    public void addRecommendBook(int bid,int rtype);//添加推荐书籍
    public void removeRecommendBook(int bid,int rtype);//删除推荐书籍
    public void removeRecommend(int bid);
}
<?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.vilicode.mapper.RecommendMapper">
<select id="queryBookByRecommendType" resultType="Book">
select t.bt_id btid,bt_name btname,b.b_id bid,b_isbn bisbn,b_name bname,b_author bauthor,b_publisher bpublisher,
b_cover bcover,b_image1 bimage1,b_image2 bimage2,b_price bprice,b_stock bstock,b_mark bmark from booktype t,book b,recommend r
where t.bt_id=b.bt_id and b.b_id=r.b_id and r_type=#{0} order by b.b_id limit #{1},#{2}
</select>
<select id="queryRecommendCountOfBooksByTypeID" resultType="int" parameterType="int">
select count(*) from recommend where r_type=#{0};
</select>
<select id="queryBookByRtypeAndBid" resultType="int">
select count(*) from recommend where r_type=#{0} and b_id =#{1};
</select>
<insert id="addRecommendBook">
insert into recommend(r_type,b_id) values(#{1},#{0});
</insert>


<delete id="removeRecommendBook">
delete from recommend where r_type=#{1} and b_id=#{0};
</delete>
<delete id="removeRecommend" parameterType="int">
delete from recommend where b_id=#{0};
</delete>
</mapper>

UserMapper接口与UserMapper.xml

作用:负责用户相关的数据操作,主要功能包括:

  • 用户管理:注册新用户、更新用户信息、删除用户
  • 用户认证:验证用户登录信息
  • 用户查询:根据ID查询用户、查询所有用户(后台管理)
  • 权限管理:可能包含查询用户权限信息的功能

应用场景:用户注册、用户登录、个人中心信息管理、后台用户管理等

package com.vilicode.mapper;

import com.vilicode.bean.User;

import java.util.List;

public interface UserMapper {
    public void addUser(User user);
    public int identify(String uname);
    public void UpdatePhoneAndAddress(int uid,String uphone,String uaddress);
    public void UpdatePassword(int uid,String upwd);
    public int getUidByUname(String uname);
    public User login(String uname );
    public String queryUpwd(int uid);
    public List<User> queryUser(int pageIndex,int pageSize);
    public int queryCountOfUser();
    public void deleteUser(int uid);
    public User queryUserByUid(int uid);
}
<?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.vilicode.mapper.UserMapper">
    <select id="login" resultType="User" parameterType="String">
    select u_id uid,u_name uname,u_pwd upwd,u_realname urealname,u_role urole,u_mark umark,
    u_phone uphone,u_address uaddress
    from `user` where u_name=#{value}
    </select>
    <select id="identify" resultType="int" parameterType="String">
    select count(*) from `user` where u_name=#{value}
    </select>
    <select id="getUidByUname" resultType="int" parameterType="String">
    select u_id from `user` where u_name=#{value}
    </select>
    <select id="queryUpwd" resultType="String" parameterType="int">
    select u_pwd from `user` where u_id=#{value}
    </select>
    <insert id="addUser" parameterType="User">
    insert into `user`(u_name,u_pwd,u_realname,u_role,u_mark,u_phone,u_address)
    values(#{uname},#{upwd},#{urealname},#{urole},#{umark},#{uphone},#{uaddress})
    </insert>
    <update id="UpdatePhoneAndAddress">
        update `user`
        <set>
            u_phone=#{1},
            u_address=#{2}
        </set>
        <where>
            u_id=#{0}
        </where>
    </update>
    <update id="UpdatePassword" >
        update `user`
        <set>
            u_pwd=#{1}
        </set>
        <where>
            u_id=#{0}
        </where>
    </update>
    <select id="queryUser" resultType="User" >
    select u_id uid,u_name uname,u_pwd upwd,u_realname urealname,u_role urole,u_mark umark,
    u_phone uphone,u_address uaddress from `user` limit #{0},#{1}
    </select>
    <select id="queryCountOfUser" resultType="int">
    select count(*) from `user`
    </select>
    <delete id="deleteUser" parameterType="int">
        delete from `user` where u_id = #{value}
    </delete>
    <select id="queryUserByUid" resultType="User" parameterType="int">
    select u_id uid,u_name uname,u_pwd upwd,u_realname urealname,u_role urole,u_mark umark,
    u_phone uphone,u_address uaddress
    from `user` where u_id=#{value}
    </select>
</mapper>

com.vilicode.service

com.vilicode.service包是叮当书城项目中的业务逻辑层,负责实现系统的核心业务功能,是连接数据访问层(Mapper)和表示层(Controller/JSP)的重要桥梁。以下是其详细作用分析:

1. 包结构与组成
该包采用接口与实现分离的设计模式,包含:

接口文件:定义业务方法规范(如BookService.java)
impl子包:包含接口的具体实现类(如BookServiceImpl.java)
目前包含4组核心服务:

BookService:图书相关业务逻辑
BookTypeService:图书分类相关业务逻辑
OrderService:订单相关业务逻辑
UserService:用户相关业务逻辑
3. 在项目架构中的作用
业务逻辑封装:将复杂的业务逻辑封装在Service层,实现业务规则和流程控制

事务管理:作为事务的边界,确保业务操作的原子性、一致性、隔离性和持久性

数据访问协调:协调多个Mapper接口的调用,实现复杂业务场景下的数据操作

业务规则校验:实现数据校验、业务规则检查等逻辑

解耦表现层与数据层:使Controller层只需调用Service层方法,无需直接访问数据库

4. 服务层的核心功能
图书服务:图书查询、搜索、推荐管理、图书CRUD操作
分类服务:图书分类管理、分类下图书查询
订单服务:订单创建、查询、状态更新、订单统计
用户服务:用户注册、登录、信息管理、权限验证
5. 工作流程
表现层(Controller/JSP)调用Service层方法
Service层执行业务逻辑,可能涉及多个Mapper的调用
Service层将处理结果返回给表现层
整个过程中,Spring框架提供事务管理、依赖注入等支持
6. 技术特点
依赖注入:通过@Autowired注解自动注入Mapper接口和其他Service
AOP支持:支持面向切面编程,实现事务管理、日志记录等横切关注点
接口与实现分离:便于单元测试和实现切换
异常处理:捕获并处理业务异常,向上层提供友好的错误信息
总结来说,com.vilicode.service包是项目的业务逻辑核心,它通过封装复杂的业务规则和流程,协调数据访问操作,为整个系统提供了稳定、可靠的业务功能支持。

BookService接口与BookServiceImpl实现类

BookService接口 BookService.java 定义了图书管理的核心业务操作契约:

图书查询功能:按推荐类型查询、按ID查询、关键词搜索图书
推荐管理功能:添加/移除图书推荐
图书维护功能:添加/修改/删除图书
BookServiceImpl实现类 BookServiceImpl.java 是具体业务逻辑的实现:

使用@Service注解标记为Spring服务组件
通过@Autowired注入BookMapper和RecommendMapper实现数据访问
实现分页查询逻辑,处理推荐图书的特殊状态(滚动/热门/新书)
包含异常捕获和处理机制
package com.vilicode.service;

import com.vilicode.bean.Book;
import com.vilicode.bean.Page;

import java.util.ArrayList;
import java.util.List;

public interface BookService {
    public List<Book> queryBookByRecommendType(int rtype,int pageNumber,int pageSize);
    public Page queryBookByRecommendType(int rtype,int pageNumber);
    public Book queryBookByID(int bid);
    public Page searchBooksByKeyword(String keyword, int pageNumber);
    public boolean addRecommend(int bid,int rtype);
    public boolean remoteRecommend(int bid,int rtype);
    public boolean removeBookByBid(int bid);
    public boolean addBook(Book book);
    public boolean modifyBook(Book book);
}
package com.vilicode.service.impl;

import com.vilicode.bean.Book;
import com.vilicode.bean.Page;
import com.vilicode.mapper.BookMapper;
import com.vilicode.mapper.RecommendMapper;
import com.vilicode.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    public BookMapper bookMapper;
    @Autowired
    public RecommendMapper recommendMapper;
    @Override
    public List<Book> queryBookByRecommendType(int rtype,int pageNumber,int pageSize)
    {
        return recommendMapper.queryBookByRecommendType(rtype,(pageNumber-1)*pageSize,pageSize);
    }

    @Override
    public Page queryBookByRecommendType(int rtype, int pageNumber) {
        Page p = new Page();
        p.setPageNumber(pageNumber);
        int totalCount = 0;
        try {
            if(rtype==0)
                totalCount =bookMapper.queryCountOfBooks();
            else
                totalCount = recommendMapper.queryRecommendCountOfBooksByTypeID(rtype);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.SetPageSizeAndTotalCount(8, totalCount);
        List<Book> list=null;
        try {
            if(rtype==0)
                list=bookMapper.queryBooks((pageNumber-1)*8,8);
            else
                list = recommendMapper.queryBookByRecommendType(rtype,(pageNumber-1)*8,8);

            for(int i=0;i<list.size();i++)
            {
                Book book=list.get(i);
                book.setScroll(recommendMapper.queryBookByRtypeAndBid(1,book.getBid())>=1);
                book.setHot(recommendMapper.queryBookByRtypeAndBid(2,book.getBid())>=1);
                book.setNew(recommendMapper.queryBookByRtypeAndBid(3,book.getBid())>=1);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.setList((List) list);
        return p;
    }

    public Book queryBookByID(int bid)
    {
        return bookMapper.queryBookById(bid);

    }

    public Page searchBooksByKeyword(String keyword,int pageNumber) {
        Page p = new Page();
        p.setPageNumber(pageNumber);
        int totalCount = 0;
        try {
            totalCount = bookMapper.queryCountOfBooksByKeyword(keyword);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
        p.SetPageSizeAndTotalCount(8, totalCount);
        List list=null;
        try {
            list = bookMapper.queryBooksByKeyword(keyword,(pageNumber-1)*8,8);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.setList(list);
        return p;
    }

    @Override
    public boolean addRecommend(int bid, int rtype) {
        try {
            recommendMapper.addRecommendBook(bid,rtype);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public boolean remoteRecommend(int bid, int rtype) {
        try {
            recommendMapper.removeRecommendBook(bid,rtype);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public boolean removeBookByBid(int bid) {
        try {
            recommendMapper.removeRecommend(bid);
            bookMapper.deleteBookByBtid(bid);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public boolean addBook(Book book) {
        try {
            bookMapper.addBook(book);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }
    public boolean modifyBook(Book book) {
        try {
            bookMapper.modifyBook(book);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }
}

BookTypeService接口

BookTypeService接口 BookTypeService.java 定义了图书分类管理的业务操作:

  • 查询所有图书分类
  • 按分类ID查询图书(分页)
  • 查询分类名称
  • 添加/删除/修改图书分类
package com.vilicode.service;

import com.vilicode.bean.BookType;
import com.vilicode.bean.Page;

import java.util.List;

public interface BookTypeService {
    public List<BookType> queryBookTypes();
    public Page queryBookByBookTypeID(int btid, int pageNumber);
    public String queryBookTypeNameByBookTypeID(int btid);
    public boolean addBookType(String btname);
    public boolean removeBookType(int btid);
    public boolean modifyBookType(int btid,String btname);
 }

BookTypeServiceImpl实现类

BookTypeServiceImpl.java 实现了分类管理的具体逻辑:

  • 使用@Service注解标记
  • 注入BookMapperBookTypeMapper
  • 实现分类名称重复性检查
  • 在删除分类时级联删除相关图书
package com.vilicode.service.impl;

import com.vilicode.bean.BookType;
import com.vilicode.bean.Page;
import com.vilicode.mapper.BookMapper;
import com.vilicode.mapper.BookTypeMapper;
import com.vilicode.service.BookService;
import com.vilicode.service.BookTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookTypeServiceImpl implements BookTypeService {

    @Autowired
    public BookMapper bookMapper;
    @Autowired
    public BookTypeMapper bookTypeMapper;

    @Override
    public List<BookType> queryBookTypes() {
        return bookTypeMapper.queryBookTypes();
    }

    @Override
    public Page queryBookByBookTypeID(int btid, int pageNumber) {
        Page p = new Page();
        p.setPageNumber(pageNumber);
        int totalCount = 0;
        try {
            if(btid==-1)
                totalCount=bookMapper.queryCountOfBooks();
            else
                totalCount = bookTypeMapper.queryCountOfBooksByTypeID(btid);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.SetPageSizeAndTotalCount(8, totalCount);
        List list=null;
        try {
            if(btid==-1)
                list=bookMapper.queryBooks((pageNumber-1)*8,8);
            else
                list = bookTypeMapper.queryBooksByBtid(btid,(pageNumber-1)*8,8);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.setList(list);
        return p;
    }

    @Override
    public String queryBookTypeNameByBookTypeID(int btid) {
        return bookTypeMapper.queryBookTypeNameByBookTypeID(btid);
    }

    @Override
    public boolean addBookType(String btname) {
        try {
          int result=bookTypeMapper.identifyBtname(btname);
            if(result>=1)
                return false;
            bookTypeMapper.addBookType(btname);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }


    }

    @Override
    public boolean removeBookType(int btid) {
        try {
            bookMapper.deleteBookByBtid(btid);
            bookTypeMapper.deleteBookType(btid);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }

    }

    @Override
    public boolean modifyBookType(int btid, String btname) {
        try {
            bookTypeMapper.updateBookType(btid,btname);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

}

OrderService接口

OrderService接口 OrderService.java 定义了订单管理的业务操作:

  • 添加订单
  • 按用户ID查询订单
  • 按订单状态查询订单(分页)
  • 更新订单状态
  • 删除订单
package com.vilicode.service;

import com.vilicode.bean.Book;
import com.vilicode.bean.Order;
import com.vilicode.bean.OrderItem;
import com.vilicode.bean.Page;

import java.util.List;

public interface OrderService {
    public boolean addOrder(Order order);
    public List<Order> queryOrderByUid(int uid);
    public Page queryOrdersByOstatus(int status,int pageNumber);
    public boolean updateOrderStatus(String oid,int ostatus);
    public boolean deleteOrderByOid(String oid);
}

OrderServiceImpl实现类

OrderServiceImpl.java 实现了订单处理的具体逻辑:

  • 使用@Service注解标记
  • 注入OrderMapperBookMapper
  • 生成唯一订单ID(基于时间戳)
  • 实现订单与订单项的级联操作
  • 包含事务回滚机制(失败时删除已添加的订单和订单项)
  • 为查询结果中的订单项关联图书信息
package com.vilicode.service.impl;

import com.vilicode.bean.Book;
import com.vilicode.bean.Order;
import com.vilicode.bean.OrderItem;
import com.vilicode.bean.Page;
import com.vilicode.mapper.BookMapper;
import com.vilicode.mapper.OrderMapper;
import com.vilicode.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    public OrderMapper orderMapper;
    @Autowired
    public BookMapper bookMapper;
    @Override
    public boolean addOrder(Order order) {
        String oid=new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        order.setOid(oid);
        try {
            orderMapper.addOrder(order);
            order.getItemMap().forEach((key, value) -> {
                value.setOid(oid);
                orderMapper.addOrderItem(value);
            });
            return true;
        }catch (Exception e)
        {
            orderMapper.deleteOrder(oid);
            orderMapper.deleteOrderItem(oid);
            System.out.println(e.getMessage());
            return false;
        }


    }

    @Override
    public List<Order> queryOrderByUid(int uid) {
        List<Order> orders=new ArrayList<>();
        try {
            orders=orderMapper.queryOrderByUid(uid);
            for(int i=0;i<orders.size();i++)
            {
                orders.get(i).setItemList(orderMapper.queryOrderItemByUidAndOid(orders.get(i).getOid()));
                for(int j=0;j<orders.get(i).getItemList().size();j++)
                {
                    orders.get(i).getItemList().get(j).setBook(bookMapper.queryBookById(orders.get(i).getItemList().get(j).getBid()));
                }
            }
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        return orders;
    }

    @Override
    public Page queryOrdersByOstatus(int ostatus,int pageNumber) {
        Page p = new Page();
        p.setPageNumber(pageNumber);
        int pageSize = 4;
        int totalCount = 0;
        try {
            if(ostatus!=1)
                totalCount=orderMapper.queryCountOfOrderByOstatus(ostatus);
            else
                totalCount=orderMapper.queryCountOfOrder();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.SetPageSizeAndTotalCount(pageSize, totalCount);
        List<Order> list=null;
        try {
            if(ostatus!=1)
                list=orderMapper.queryOrderByOstatus(ostatus,(pageNumber-1)*pageSize,pageSize);
            else
                list=orderMapper.queryOrder((pageNumber-1)*pageSize,pageSize);
            for(int i=0;i<list.size();i++)
            {
                list.get(i).setItemList(orderMapper.queryOrderItemByUidAndOid(list.get(i).getOid()));
                for(int j=0;j<list.get(i).getItemList().size();j++)
                {
                    list.get(i).getItemList().get(j).setBook(bookMapper.queryBookById(list.get(i).getItemList().get(j).getBid()));
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        p.setList((List)list);
        return p;
    }

    @Override
    public boolean updateOrderStatus(String oid, int ostatus) {
        try {
            orderMapper.updateOrderStatus(oid,ostatus);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public boolean deleteOrderByOid(String oid) {
        try {
            orderMapper.deleteOrderItem(oid);
            orderMapper.deleteOrder(oid);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }

    }
}

UserService接口

UserService.java 定义了用户管理的业务操作:

  • 用户注册
  • 用户登录
  • 更新联系方式
  • 修改密码(带原密码验证和不带验证两种方式)
  • 查询用户(分页)
  • 删除用户
  • 按ID查询用户
package com.vilicode.service;

import com.vilicode.bean.Page;
import com.vilicode.bean.User;

import java.util.List;

public interface UserService {
    public Boolean register(User user);
    public User login(String uname);
    public boolean UpdatePhoneAndAddress(int uid,String uphone,String uaddress);
    public boolean UpdatePassword(int uid,String upwd,String oldupwd);
    public boolean UpdatePassword(int uid,String upwd);
    public Page queryUser(int pageNumber);
    public boolean deleteUser(int uid);
    public User queryUserByUid(int uid);
}

UserServiceImpl实现类

UserServiceImpl.java 实现了用户管理的具体逻辑:

  • 使用@Service注解标记
  • 注入UserMapper
  • 实现密码加密和解密功能(使用MyCipher工具类)
  • 进行用户名重复性检查
  • 实现密码修改时的原密码验证
package com.vilicode.service.impl;

import com.vilicode.Utils.MyCipher;
import com.vilicode.bean.Page;
import com.vilicode.bean.User;
import com.vilicode.mapper.UserMapper;
import com.vilicode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public Boolean register(User user) {
        boolean flag=false;
        //密码加密
        MyCipher mc=new MyCipher();
        user.setUpwd(mc.encrypt(user.getUpwd(), "!"));
        try {
            int result=userMapper.identify(user.getUname());
            if(result>=1)
                return false;
            userMapper.addUser(user);
            flag=true;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return flag;
    }

    @Override
    public User login(String uname) {
        String pwd="";
        //密码解密
        MyCipher  mc=new MyCipher();
        User user=userMapper.login(uname);
        if(user!=null)
            user.setUpwd(mc.decrypt(user.getUpwd(),"!"));
        return user;
    }

    public boolean UpdatePhoneAndAddress(int uid,String uphone,String uaddress)
    {
        try {
            userMapper.UpdatePhoneAndAddress(uid,uphone,uaddress);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }

    }

    @Override
    public boolean UpdatePassword(int uid, String upwd,String oldupwd) {
        MyCipher myCipher=new MyCipher();
        try {
            String temp=userMapper.queryUpwd(uid);
            if(temp.equals(myCipher.encrypt(oldupwd,"!")))
            {
                userMapper.UpdatePassword(uid,myCipher.encrypt(upwd,"!"));
                return true;
            }
            else
            {
                return false;
            }
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public boolean UpdatePassword(int uid, String upwd) {
        MyCipher myCipher=new MyCipher();
        try {
            userMapper.UpdatePassword(uid,myCipher.encrypt(upwd,"!"));
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public Page queryUser(int pageNumber) {
        Page p = new Page();
        p.setPageNumber(pageNumber);
        int pageSize = 7;
        int totalCount = 0;
        try {
            totalCount = userMapper.queryCountOfUser();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        p.SetPageSizeAndTotalCount(pageSize, totalCount);
        List list=null;
        try {
            list = userMapper.queryUser( (pageNumber-1)*pageSize, pageSize);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        p.setList(list);
        return p;
    }

    @Override
    public boolean deleteUser(int uid) {
        try {
            userMapper.deleteUser(uid);
            return true;
        }catch (Exception e)
        {
            System.out.println(e.getMessage());
            return false;
        }
    }

    @Override
    public User queryUserByUid(int uid) {
        return  userMapper.queryUserByUid(uid);
    }

}