当前位置:   article > 正文

超市收银系统_超市收银系统开发

超市收银系统开发

目录

项目大纲

逻辑建模

界面设计

注册账号

登录/切换账号

上架商品

浏览商品

更新商品

浏览订单

订单详情

购买商品

场景梳理

场景1:用户管理

准备

用户密码保存的问题

用户注册

注册Controller

合法性检验

错误捕获

合法的注册数据处理

用户信息存储

用户登录

登录Controller

登录数据验证

查找登录对象

场景2:商品管理

商品上架

上架DoController

商品上架工作

商品记录存储

浏览商品

获取浏览数据记录

商品浏览工作

商品记录读取

动态添加商品记录

更新商品

更新

商品更新操作

商品记录更新

场景3:订单管理

订单状态梳理

OrderService梳理

生成订单事务

确认订单事务

取消订单事务

购买商品

购买

正在处理的订单详情

功能1:订单信息展示

功能2:确认订单

功能2:取消订单

浏览所有订单记录

获取订单表数据记录

查询订单记录列表

项目大纲

项目简介为超市实现一个信息管理平台,使超市的工作人员可以进行商品的管理和订单的管理

主要模块:◇ 用户管理 :用户注册、用户登录

商品管理 :商品上架,浏览商品,更新商品

订单管理:购买商品、浏览订单

项目环境:Windows,IDEA,Maven,Java8,MySQL

项目技术:SpringSpringMVCMybatis密码加密存储事务管理

逻辑建模

用户表(主键user_id、用户名(唯一username)、密码password)

商品表(主键product_id、关系字段user_id、名称name、介绍introduce、库存stock、单位unit、价格price、折扣discount)

订单表(主键order_id、关系字段user_id、订单编号(uuid)、下单时间created_at、完成时间finished_at、应付payable、实付actual、状态status)

订单项表(主键id、两个关系字段order_id和product_id、为了防止商品下架后信息丢失,将商品信息备份一份(product_name、product_introduce、product_number、product_unit、product_price、product_discount)):订单和商品的关系表,记录本次订单的每一份商品信息

界面设计

注册账号

登录/切换账号

上架商品

浏览商品

更新商品

<img src="https://gitee.com/xial1007/img-up-load/raw/master/img/202209090622538.png" alt="image-20220909062217684" style="zoom:25%;" />

浏览订单

订单详情

购买商品

场景梳理

用户管理 : 用户注册,用户登录

商品管理 :商品上架,浏览商品,更新商品

订单管理 : 购买商品(生成订单、确认订单、取消订单),浏览订单

场景1:用户管理

准备

在用户注册、用户登录中使用一个加密库(实际上是在做hash,不是真正的加密),不再保存明文密码

使用BCrypt算法:专门做密码hash的。例:BCrypt算法用于加密,及验证密码是否匹配

  1. package org.mindrot.jbcrypt;
  2. public class BCryptDemo {
  3.    public static void main(String[] args) {
  4.        String password = "123";
  5.        String salt = BCrypt.gensalt(); // 每次随机出来的盐值是不同的
  6.        System.out.println("salt = " + salt);
  7.        String hashpw = BCrypt.hashpw(password, salt);
  8.        System.out.println("hashpw = " + hashpw);
  9.        System.out.println("长度 = " + hashpw.length());
  10.        // 用做密码加密的 hash 算法都是精心设计过的,碰撞概率很低。
  11.        // 使用同样的 hash 算法去 hash "123",如果 hash 值一样,大概率用户的密码是正确的
  12.        boolean checkpw = BCrypt.checkpw("123", hashpw);
  13.        System.out.println(checkpw);
  14.   }
  15. }

用户密码保存的问题

不能明文、不能直接MD5、使用加盐后的Hash算法->B Crypt成熟的算法

引入第三方类:orgmindrotjbcrypt.BCrypt

基本用法: 1.存储过程:(注册) 1)生成一个随机的盐值:String salt=BCrypt.gensalt(); 2)将明文密码+盐值一同做hash,得到要储存的密码:Stringp=BCrypt. hashpw(password,salt); 2.验证过程: (登录) 1)从存储中取出密文密码:String hashpw=从表中获取 2)BCryptcheckpw(password,hashpw):对比hash值(理论上,确实 存在不同密码,但验证成功的情况,但概率很小

用户注册

资源名作用类型
/reqister.html提供一个form表单静态资源 GET
/reqister.do验证注册逻辑动态资源 POST

注册逻辑

1)Controller(读取form表单提交上来的用户信息,做参数合法性校验)

2)Service(负责密码加密的工作)

3)Mapper(数据库的插入)一mybatis

注册Controller

DoController下 /register.do

1. 进行参数的合法性校验

        1)用户名的要求:不是 null && 不是 "" && 长度不能超过 50 && 不能重复,一旦出错,重定向到register.html

        2)密码的要求:不是 null && 不是 ""

        3)优化:

        a. 将用户名合法性验证、密码合法性验证部分代码封装成两个类,减少代码长度,增加代码可读性

        b. 为防止过抛出异常、重定向语句出现错误,不好修改,将其封装成类完成错误捕获(ErrorRedirectException和CashControllerAdvice)

2. 进行注册工作UserService的register方法(密码加密,并保存至数据库)

        1)用户名重复:抛出异常,重定向到register.html

        2)用户名不重复:执行4.完成注册步骤

3. 完成注册:

        1)合法的注册数据处理:进行密码加密处理,并将合法的注册信息添加到数据库:UserService的register方法

        2)直接完成登录操作:把刚注册的用户信息放入 session 中,视为登录了

        3)最终注册成功之后,跳转到首页(/)

  1. // 这个方法中没有 try-catch,catch 在 CashControllerAdvice 中
  2. @PostMapping("/register.do")
  3. // 由于参数名称和 form 表单里的 input 的 name 是对应上的,所以省略了 @RequestParam 注解了
  4. public String register(String username, String password, HttpServletRequest request) {
  5.    String module = "用户注册";  // 用户注册里抛出的异常
  6.    String redirectUrl = "/register.html"; // 抛出异常后要重定向的页面
  7.    // 规则,无论哪个动态资源,先打印功能 + 参数
  8.    log.debug("用户注册: username = {}, password = {}", username, password);
  9.    // 1. 进行参数的合法性校验 validate
  10.    // 1.1)用户名合法性校验
  11.    username = usernameValidator.validate(module, redirectUrl, username);    // 如果有错,直接异常穿透 register 方法就出去了
  12.    // 1.2)密码合法性校验
  13.    password = passwordValidator.validate(module, redirectUrl, password);     // 同理
  14.    // 2. 完成注册的工作
  15.    try {
  16.        // 2.1)用户名不重复 ——> 执行 3.注册成功
  17.        // 3.1) 合法的注册数据处理:进行密码加密处理,并将合法的注册信息添加到数据库
  18.        User user = userService.register(username, password);
  19.        // 3.2). 直接完成登录操作(把刚注册的用户信息放入 session 中,视为登录了)
  20.        HttpSession session = request.getSession();
  21.        session.setAttribute("currentUser", user);
  22.        // 3.3). 最终注册成功之后,跳转到首页(/)
  23.        log.debug("用户注册: 注册成功, user = {}", user);
  24.        return "redirect:/";
  25.   } catch (DuplicateKeyException exc) { // 重复的key异常
  26.        // 2.2)用户名重复——注册失败
  27.        throw new ErrorRedirectException("username 重复", module, redirectUrl, exc);
  28.   }
  29. }

合法性检验

父类AbsValidator

由于多个属性都需要进行判空操作,因此我们将它写成一个抽象类,其他属性进行合法性检验时,不需要重复判空,只需要继承该类即可

  1. // 这个类不准备实例化对象,所以定义成抽象类
  2. public abstract class AbsValidator {
  3.    public String validate(String module, String redirectUrl, String value) {
  4.        if (value == null) {
  5.            throw new ErrorRedirectException("value 是 null", module, redirectUrl);
  6.       }
  7.        value = value.trim();
  8.        if (value.isEmpty()) {
  9.            throw new ErrorRedirectException("value 是 \"\"", module, redirectUrl);
  10.       }
  11.        return value;
  12.   }
  13. }

子类 1)用户名合法性检验

不用再重复判空,直接继承父类方法

  1. @Slf4j
  2. @Component  // 交给 Spring 管理
  3. public class UsernameValidator {
  4.    public String validate(String module, String redirectUrl, String username) {
  5.        // 用户名的要求:不是 null && 不是 "" && 长度不能超过 50 && 不能重复
  6.        if (username == null) {
  7.            throw new ErrorRedirectException("username 是 null", module, redirectUrl);
  8.       }
  9.        username = username.trim(); // 去掉两边的空格
  10.        if (username.isEmpty()) {
  11.            throw new ErrorRedirectException("username 是 \"\"", module, redirectUrl);
  12.       }
  13.        if (username.length() > 50) {
  14.            throw new ErrorRedirectException("username 的长度超过 50", module, redirectUrl);
  15.       }
  16.        return username;
  17.   }
  18. }

子类 2)密码合法性检验

  1. @Slf4j
  2. @Component  // 交给 Spring 管理
  3. public class UsernameValidator {
  4.    public String validate(String module, String redirectUrl, String username) {
  5.        // 用户名的要求:不是 null && 不是 "" && 长度不能超过 50 && 不能重复
  6.        if (username == null) {
  7.            throw new ErrorRedirectException("username 是 null", module, redirectUrl);
  8.       }
  9.        username = username.trim(); // 去掉两边的空格
  10.        if (username.isEmpty()) {
  11.            throw new ErrorRedirectException("username 是 \"\"", module, redirectUrl);
  12.       }
  13.        if (username.length() > 50) {
  14.            throw new ErrorRedirectException("username 的长度超过 50", module, redirectUrl);
  15.       }
  16.        return username;
  17.   }
  18. }

错误捕获

1)抛出异常ErrorRedirectException

捕获错误然后需要重定向的异常,希望是一个非受查异常(继承在 RuntimeException)

由于项目中有多种错误,因此对不同的错误,应该有不同的处理方式,跳转到不同的页面,故我们在此定义三个属性以区分不同错误和重定向结果

  1. /* 错误然后重定向的异常,希望是一个非受查异常(继承在 RuntimeException)*/
  2. @Getter
  3. public class ErrorRedirectException extends RuntimeException {
  4.    private final String error;         // 错误类型
  5.    private final String module;        // 哪个功能抛出的异常
  6.    private final String redirectUrl;   // 重定向到哪
  7.    // 构造错误信息的对象
  8.    public ErrorRedirectException(String error, String module, String redirectUrl) {
  9.        // 调用父类的构造方法
  10.        super();    // 这句可以省略
  11.        this.error = error;
  12.        this.module = module;
  13.        this.redirectUrl = redirectUrl;
  14.   }
  15.    // 构造错误信息和捕获到的异常对象
  16.    public ErrorRedirectException(String error, String module, String redirectUrl, Throwable cause) {
  17.        // 调用父类的构造方法
  18.        super(cause);   // 这句就不能省略了
  19.        this.error = error;
  20.        this.module = module;
  21.        this.redirectUrl = redirectUrl;
  22.   }
  23. }

2)重定向CashControllerAdvice

添加@ControllerAdvice,作为异常统一处理的类 AOP思想的体现,在某个事务完全执行完毕后调用(AfterResourceHandler),AOP里Advice是通知的意思

关于捕获到的错误对象处理的方法:根据不同的逻辑,打印不同,跳转的页面也不同

  1.    @ExceptionHandler(ErrorRedirectException.class)
  2.    public String logAndRedirect(ErrorRedirectException exc) {
  3.        log.debug("{}: {}", exc.getModule(), exc.getError());
  4.        return "redirect:" + exc.getRedirectUrl();
  5.   }

合法的注册数据处理

UserService下register方法

  1. 对密码进行加密处理

  2. 保存至数据库:插入User数据库(通过UserMapper处理)

    1. @Slf4j
    2. @Service
    3. public class UserService {
    4.    // 注入 UserMapper
    5.    private final UserMapper userMapper;
    6.    @Autowired
    7.    public UserService(UserMapper userMapper) {
    8.        this.userMapper = userMapper;
    9.   }
    10.    public User register(String username, String password) {
    11.        // 对密码进行加密处理
    12.        String salt = BCrypt.gensalt();
    13.        String hashPassword = BCrypt.hashpw(password, salt);
    14.        // 进行插入操作
    15.        User user = new User(username, hashPassword);
    16.        userMapper.insert(user);
    17.        return user;
    18.   }
    19. }

用户信息存储

UserMapper接口下的insert方法

用Mybatis里XML形式直接将User对象映射到sql语句里插入数据表User

  1. @Repository
  2. @Mapper
  3. public interface UserMapper {
  4.    void insert(User user);
  5. }

UserMapper.xml文件

  1. <mapper namespace="com.xlinzhang.cash.mapper.UserMapper">
  2.    <insert id="insert" useGeneratedKeys="true" keyProperty="userId" keyColumn="user_id">
  3.        insert into users (username, password) values (#{username}, #{password})
  4.    </insert>
  5. </mapper>

用户登录

资源名作用类型
/login.html提供一个form表单静态资源
/login.do验证登录逻辑动态资源

登录Controller

DoController下 /login.do

  1. 进行参数的合法性校验:用户名、密码合法性校验

  2. 进行登录工作UserService的login方法(查询用户名并验证密码)

    1)查询到的用户对象User为空:登陆不成功

    2)查询到的用户对象User不为空:完成登录

  3. 完成登录:

    1)登录信息记录:把刚注册的用户信息放入 session 中,视为登录了

    2)登录成功之后,跳转到首页

    1. @PostMapping("/login.do")
    2. public String login(String username, String password, HttpServletRequest request) {
    3.    String module = "用户登录"; // 用户登录里抛出的异常
    4.    String redirectUrl = "/login.html"; // 抛出异常后要重定向的页面
    5.    log.debug("{}: username = {}, password = {}", module, username, password);
    6.    // 进行参数的合法性校验:用户名、密码合法性校验
    7.    username = usernameValidator.validate(module, redirectUrl, username);
    8.    password = passwordValidator.validate(module, redirectUrl, password);
    9.    // 进行登录工作UserService的login方法
    10.    User user = userService.login(username, password);
    11.    if (user == null) {
    12.        throw new ErrorRedirectException("username or password 错误", module, redirectUrl);
    13.   }
    14.    HttpSession session = request.getSession();
    15.    session.setAttribute("currentUser", user);
    16.    log.debug("{}: 登录成功, user = {}", module, user);
    17.    return "redirect:/";
    18. }

登录数据验证

UserService下login方法

  1. 在数据库中查询用户名和密码(User对象)

    用户名为空:返回空值

    用户名不空:进行密码验证

  1. 密码验证:密码错误,返回null;密码正确,返回获取到的User对象

  1. public User login(String username, String password) {
  2.    User user = userMapper.selectByUsername(username);
  3.    log.debug("通过 mybatis 查询得到的用户 = {}", user);
  4.    if (user == null) {
  5.        return null;
  6.   }
  7.    // 进行密码验证
  8.    if (!BCrypt.checkpw(password, user.getPassword())) {
  9.        return null;
  10.   }
  11.    return user;
  12. }

查找登录对象

UserMapper接口下的insert方法

  1. // 由于只有一个参数,所以省略了 @Param 注解
  2. User selectByUsername(String username);

UserMapper.xml文件

  1. <select id="selectByUsername" resultType="com.xlinzhang.cash.model.user.User">
  2.    <!-- User对象中保存的是 userId,而表的列名是 user_id,所以需要做个映射 -->
  3.    <!-- 因为仅仅只是转一下 user_id 到 userId 就些 resultMapper 有点麻烦,mybatis 已经提供了一个选项,开启即可 -->
  4.    select user_id, username, password from users where username = #{username}
  5. </select>

场景2:商品管理

商品上架

资源名作用类型
/product/create.html提供一个form表单静态资源
/product/create.do保存的逻辑,对应的表是products动态资源

上架DoController

DoController下/product/create.do

由于product参数列表比较长,所以不采用一个个列出来的方式,而是使用一个ProductParam对象的方式,通过对象接收请求参数的方式

  1. 判断用户是否登录:通过session中的“currentUser”

    1)否:无权限进行上架操作,回到登录页面

    2)是:获取当前登录用户信息,即User对象,继续进行上架操作

  2. 进行参数的合法性校验

    name:不空,长度不超过100

    introduce:不空,长度不超过200

    unit:不空,长度不超过10(范围是<=10)

    stock:不空,确认下 stock 是不是数字(int类型),并且得是 > 0 的数字

    price:不空,确认下 price 是不是数字(double类型),并且得是 > 0 的数字(如果传入的价格参数有小数点后二位之后,则直接丢弃)

    discount:不空,得确认下 discount 是不是数字,并且得是 [1,100] 的数字

  1. 进行上架工作:操作上架的是哪个用户,在插入商品记录时就用哪个用户的id(即当前登录的用户)

    ProductService的create方法(将商品记录插入数据库)

  2. 完成上架:跳转到商品列表页

  1.  @PostMapping("/product/create.do")
  2.    // 由于参数列表比较长,所以我们不采用一个个列出来的方式,而是使用一个对象的方式
  3.    // 通过对象接收请求参数的方式
  4.    public String productCreate(ProductParam productParam, HttpServletRequest request) {
  5.        String module = "商品上架";
  6.        String redirectUrl = "/product/create.html";
  7.        log.debug("{}: 请求参数 = {}", module, productParam);
  8.        User currentUser = null;
  9.        HttpSession session = request.getSession(false);
  10.        if (session != null) {
  11.            // 用户已登录
  12.            currentUser = (User) session.getAttribute("currentUser");
  13.       }
  14.        if (currentUser == null) {
  15.            // 说明用户未登录
  16.            log.debug("{}: 用户未登录,无权进行该操作", module);
  17.            return "redirect:/login.html";  // 重定向到登录页,让用户登录
  18.       }
  19.        // 验证合法性【整体业务写完再回来写这个,但要求自己输入的时候,一定要输入合法的参数,比如价格如果不是数字,就会 500
  20. //       productParam.validate(module, redirectUrl);
  21.        // 使用 service -> mapper 保存
  22.        Product product = productService.create(currentUser, productParam);
  23.        log.debug("{}: 成功,上架商品为: {}", module, product);
  24.        return "redirect:/product/list.html";   // 商品上架成功后跳转到商品列表页
  25.   }

商品上架工作

ProductService的create方法

  1. 构造Product对象:根据填入的数据(ProductParam)和 当前用户(User)构造

  2. 调用 mapper 的 insert,完成数据库插入

  1. @Slf4j
  2. @Service
  3. public class ProductService {
  4.    private final ProductMapper productMapper;
  5.    @Autowired
  6.    public ProductService(ProductMapper productMapper) {
  7.        this.productMapper = productMapper;
  8.   }
  9.    public Product create(User user, ProductParam param) {
  10.        // 1. 构造一个 Product 对象(从 ProductParam 对象)
  11.        Product product = new Product(user, param);
  12.        // 2. 调用 mapper 的 insert,完成数据库插入
  13.        productMapper.insert(product);
  14.        // 3. 返回构造好的对象
  15.        return product;
  16.   }
  17. }

商品记录存储

ProductMapper接口下的insert方法

  1. @Repository
  2. @Mapper
  3. public interface ProductMapper {
  4.    void insert(Product product);
  5. }

ProductMapper.xml文件

  1. <insert id="insert" useGeneratedKeys="true" keyProperty="productId" keyColumn="product_id">
  2.    insert into products
  3.   (user_id, name, introduce, stock, unit, price, discount)
  4.    values
  5.   (#{userId}, #{name}, #{introduce}, #{stock}, #{unit}, #{price}, #{discount})
  6. </insert>

浏览商品

资源名作用类型
/product/list.html提供HTML框架静态资源 GET
/product/list.js发起Ajax请求,读取JSON格式的商品列表,修改DOM树(动态的添加数据记录)静态资源 GET
/product/list.json返回所有的商品,以JSON形式返回动态资源 GET

获取浏览数据记录

JsonController下/product/list.json

  1. 判断用户是否登录:通过session中的“currentUser”

    1)否:无权限进行浏览操作,回到登录页面

    2)是:获取当前登录用户信息,即User对象,继续进行浏览操作

  2. 以列表形式返回Products表中数据:List<Product>

  3. 将后端列表数据 转换成 前端用的ProductListView对象:由于对外展示的对象格式和内部使用的对象略有区别,因此我们定义一个ProductView类。从数据表中,我们获取到的是一组view对象,因此再定义一个ProductListView类。这两个类只会在当前Controller下使用,所以就定义成当前Controller下的静态内部类。如,对于price属性:不关心后端如何存储,前端看到的价格都是10.07这种形式

  1.        private int productId;
  2.        private String name;
  3.        private String introduce;
  4.        private int stock;
  5.        private String unit;
  6.        private double price;
  7.        private int discount;
  8.        public ProductView(Product product) {
  9.            // 将后端Product对象 转换成 前端显示用的view对象
  10.            log.debug("ProductView(product = {})", product);
  11.            this.productId = product.getProductId();
  12.            this.name = product.getName();
  13.            this.introduce = product.getIntroduce();
  14.            this.stock = product.getStock();
  15.            this.unit = product.getUnit();
  16.            // price在前端要保留两位小数保存:
  17.            // product.getPrice()返回int类型,100也是int类型,若用 int/int 结果也是 int,向下取整,丢失小数信息,故必须是 100.0 而不能是 100
  18.            this.price = product.getPrice() / 100.0;
  19.            this.discount = product.getDiscount();
  20.       }
  21.   }
  22.    // 存放从数据表Products中获取的一组数据记录转换成的一组ProductView对象
  23.    @Slf4j
  24.    @Data
  25.    private static class ProductListView {
  26.        // JsonInclude 这个注解是属于 jackson 的(负责 JSON 序列化/反序列化 的一个第三方库,是 Spring 的默认库)
  27.        // JsonInclude.Include.NON_NULL 只有在 不是 null 的时候,才会写到响应中,如:若未登录,无权查看商品表,故data为null,不显示
  28.        @JsonInclude(JsonInclude.Include.NON_NULL)
  29.        private String redirectUrl;
  30.        @JsonInclude(JsonInclude.Include.NON_NULL)
  31.        private List<ProductView> data;
  32.        public static ProductListView success(List<Product> productList) {
  33.            ProductListView view = new ProductListView();
  34.            // Product 的线性表 -> ProductView 的线性表
  35.            // 普通写法
  36.            view.data = new ArrayList<>();
  37.            for (Product product : productList) {
  38.                ProductView productView = new ProductView(product);
  39.                view.data.add(productView);
  40.           }
  41.            return view;
  42.       }
  43.        public static ProductListView failure(String redirectUrl) {
  44.            // 注意,这里不是 HTTP 协议层面的重定向,我们一会儿会让前端进行跳转
  45.            ProductListView view = new ProductListView();
  46.            view.redirectUrl = redirectUrl;
  47.            return view;
  48.       }
  49.   }
  50.    @GetMapping("/product/list.json")
  51.    public ProductListView getProductList(HttpServletRequest request) {
  52.        User currentUser = null;
  53.        HttpSession session = request.getSession(false);
  54.        if (session != null) {
  55.            currentUser = (User) session.getAttribute("currentUser");
  56.       }
  57.        if (currentUser == null) {
  58.            // 说明没有登录
  59.            return ProductListView.failure("/login.html");
  60.       }
  61.        List<Product> productList = productService.getList();
  62.        return ProductListView.success(productList);
  63.   }

商品浏览工作

ProductService的getList方法

从Products表中获取所有商品数据记录

  1. public List<Product> getList() {
  2.    return productMapper.selectAll();
  3. }

商品记录读取

ProductMapper接口下的selectAll方法

List<Product> selectAll();

ProductMapper.xml文件

  1. <select id="selectAll" resultType="com.xlinzhang.cash.model.product.Product">
  2.    select product_id, user_id, name, introduce, stock, unit, price, discount
  3.    from products order by product_id desc
  4. </select>

动态添加商品记录

list.js文件

将后端数据库数据通过“/product/list.json”转换成前端ProductListView对象

  1. 通过get请求,获取'/product/list.json'里的资源(Json形式)

  1. 将资源转换成js对象后,判断是否登陆了

    1)有 redirectUrl:用户未登录,跳转到 redirectUrl地址中

    2)无 redirectUrl:用户登录了,将获取到的ProductListView对象里的列表data里的记录逐一添加到list.html页面中

  1. var xhr = new XMLHttpRequest()
  2. xhr.open('get', '/product/list.json')
  3. xhr.onload = function () {
  4.  console.log(this.status)
  5.  console.log(this.responseText)
  6.  // 进行 JSON 字符串 -> JS 对象
  7.  var ret = JSON.parse(this.responseText)
  8.  // 根据是否有 redirectUrl 来判断是正确还是错误情况
  9.  if (ret.redirectUrl) {
  10.    // 返回里有 redirectUrl,说明错了,由 JS 进行页面的跳转
  11.    // JS 提供了一个方法,让页面跳转到新的地方
  12.    location.assign(ret.redirectUrl)  // 到时候的跳转是我们通过 JS 自己完成的,不是 HTTP 层面上的重定向(浏览器自动完成)
  13.    return;   // 这个 return 其实可以不加
  14. }
  15.  var productList = ret.data;
  16.  var oTbody = document.querySelector('tbody')
  17.  // 通过遍历 productList,得到每一个 商品,根据商品中的值,修改 DOM 结构(为 <tbody> 添加一个 <tr> 一行
  18.  for (var product of productList) {
  19.    var html = "<tr>" +
  20.      `<td>${product.name}</td>` +  // 反引号,模板字符串
  21.      `<td>${product.introduce}</td>` +  // 反引号,模板字符串
  22.      `<td>${product.stock}</td>` +  // 反引号,模板字符串
  23.      `<td>${product.unit}</td>` +  // 反引号,模板字符串
  24.      `<td>${product.price}</td>` +  // 反引号,模板字符串
  25.      `<td>${product.discount}</td>` +  // 反引号,模板字符串
  26.      "</tr>";
  27.    // 添加到 tbody 的内部
  28.    oTbody.innerHTML += html
  29. }
  30. }
  31. // 真正发起请求
  32. xhr.send()

更新商品

资源名作用类型
/product/update.html提供一个form表单静态资源
/product/update.do保存的逻辑,对应的表是products动态资源

更新

DoController下 /product/update.do

  1. 判断用户是否登录:通过session中的“currentUser”

    1)否:无权限进行浏览操作,回到登录页面

    2)是:获取当前登录用户信息,即User对象,继续进行更新操作

  1. 根据表单中的数据更新Products表

  1. @PostMapping("/product/update.do")
  2. public String productUpdate(ProductParam productParam, HttpServletRequest request) {
  3.    log.debug("商品更新: 请求参数 = {}", productParam);
  4.    User currentUser = null;
  5.    HttpSession session = request.getSession(false);
  6.    if (session != null) {
  7.        currentUser = (User) session.getAttribute("currentUser");
  8.   }
  9.    if (currentUser == null) {
  10.        // 说明用户未登录
  11.        log.debug("商品更新: 用户未登录,无权进行该操作");
  12.        return "redirect:/login.html";  // 重定向到登录页,让用户登录
  13.   }
  14.    Product product = productService.update(currentUser, productParam);
  15.    log.debug("商品更新: 成功,product = {}", product);
  16.    return "redirect:/product/list.html";
  17. }

商品更新操作

ProductService下update方法

  1. 构造Product对象:根据填入的数据(ProductParam)和 当前用户(User)构造

  2. 调用 mapper 的 updateByProductId,完成数据库插入

  1. public Product update(User user, ProductParam productParam) {
  2.    Product product = new Product(user, productParam);
  3.    productMapper.updateByProductId(product);
  4.    return product;
  5. }

商品记录更新

ProductMapper接口下的updateByProductId方法

void updateByProductId(Product product);

ProductMapper.xml文件

  1.    <update id="updateByProductId">
  2.        update products set
  3.            user_id = #{userId},
  4.            name = #{name},
  5.            introduce = #{introduce},
  6.            stock = #{stock},
  7.            unit = #{unit},
  8.            price = #{price},
  9.            discount = #{discount}
  10.        where product_id = #{productId}
  11.    </update>

场景3:订单管理

订单状态梳理

1. 订单状态:开始 -> 未支付 -> 已支付

2. 购买商品(开始 -> 未支付)

        对应很多条SQL:整体上应该当作一个事务来对待

        1)查询商品库存是否够

         2)每个商品进行减库存操作 —— 更新

        3)创建订单信息(状态是末支付)—— 插入

        4)创建订单项信息

3. 确认(未支付->己支付)

        更新订单状态:未支付更新 为已支付

update orders set status ='已支付’ where order_id = ... ;

4. 取消(未支付->开始)

        进行数据的回滚操作:当时【购买商品】时所做的数据变更全部反向再来一遍

        1)订单项删除【不考虑期间商品被下架的情况】

        2)订单删除

        3)商品的库存增加

OrderService梳理

事务: Order create(Map<商品id,购买数量>productMap):创建订单项 Order confirm(int orderld):确认订单 Order cancel(int orderld):取消订单 有了订单 id,我们就可以查询出该订单关联的所有商品id,从订单项表中可以查出

生成订单事务

OrderService下create

1. 确认商品库存是否足够

        1)执行

selectfrom products where product id in (?, ?, .., ?);

        2)List<product>转成 Map<商品id, Product>

        3)遍历,检查每个商品的库存是否够

2. 商品减库存:遍历每个商品(entry<商品 id,购买数量>),依次执行

update products set stock = stock- 购买数量 where product id = 商品 id

3. 创建订单记录

        1)准备订单里的各种数据(uuid、创建订单时的时间(现在)、应付总额、实付总额,订单状态(未支付))

                a. uuid 来自 UUID 算法计算出来后,把其中的"-"全部去掉

                b. 当前时间:Timestamp.from(nstant.now0)

                c. 应付金额:求和(商品的价格 商品的购买数量1针对每个商品)

                d. 实付金额:求和(商品的价格* 商品的购买数量*商品折扣1 针对每个商品)

        2)执行:插入订单

insert into orders (user id, uuid, created at, payable, actual, status) values (...)

4. 创建订单项记录

        执行:插入订单

 insert into order_items ( order_id,product_id,product_name,product_introduce,product_number,product_unit,product_price,product_discount ) values (...)

代码:

  1.    @Transactional  // 整体上应该作为事务存储
  2.    // toBoughtProductMap<商品 id, 要购买的数量>
  3.    public Order create(User user, Map<Integer, Integer> toBoughtProductMap) {
  4.        log.debug("toBoughtProductMap = {}", toBoughtProductMap);
  5.        // 1. 确认商品的库存足够
  6.        Map<Integer, Product> productMap = 确认商品库存是否足够(toBoughtProductMap);
  7.        log.debug("productMap = {}", productMap);
  8.        // 2. 商品减库存
  9.        商品减库存(toBoughtProductMap);
  10. //       // 3. 创建订单记录
  11.        Order order = 创建订单记录(user, productMap, toBoughtProductMap);
  12.        // 4. 创建订单项记录
  13.        插入订单项(order, productMap, toBoughtProductMap);
  14.        return order;
  15.   }
  16.    private Order 创建订单记录(User user, Map<Integer, Product> productMap, Map<Integer, Integer> toBoughtProductMap) {
  17.        String uuid = generateUUID();
  18.        Timestamp createdAt = Timestamp.from(Instant.now());
  19.        // 计算应付总额
  20.        int payable = 计算应付总额(productMap, toBoughtProductMap);
  21.        // 计算实付总额
  22.        int actual = 计算实付总额(productMap, toBoughtProductMap);
  23.        // 初识状态
  24.        OrderStatus status = OrderStatus.未支付;
  25.        Order order = new Order(user.getUserId(), uuid, createdAt, payable, actual, status);
  26.        // OrderMapper 完成订单插入
  27.        orderMapper.insert(order);
  28.        log.debug("order = {}", order);
  29.        return order;
  30.   }
  31.    private void 插入订单项(Order order, Map<Integer, Product> productMap, Map<Integer, Integer> toBoughtProductMap) {
  32.        // 先遍历每个商品,构造 OrderItem 对象
  33.        Set<Integer> productIdSet = productMap.keySet();
  34.        Set<OrderItem> orderItemSet = new HashSet<>();
  35.        for (Integer productId : productIdSet) {
  36.            Product product = productMap.get(productId);
  37.            int number = toBoughtProductMap.get(productId);
  38.            OrderItem orderItem = new OrderItem(order, product, number);
  39.            orderItemSet.add(orderItem);
  40.       }
  41.        orderItemMapper.insertBatch(orderItemSet);
  42.   }
  43.    // 实付总额就是每个商品的价格 * 商品购买数量 * 折扣,然后求和
  44.    private int 计算实付总额(Map<Integer, Product> productMap, Map<Integer, Integer> toBoughtProductMap) {
  45.        Set<Integer> productIdSet = productMap.keySet();
  46.        int sum = 0;
  47.        for (Integer productId : productIdSet) {
  48.            int number = toBoughtProductMap.get(productId);
  49.            Product product = productMap.get(productId);
  50.            int price = product.getPrice();
  51.            double discount = product.getDiscount() / 100.0;
  52.            int productActual = (int)(price * number * discount);
  53.            sum += productActual;
  54.       }
  55.        return sum;
  56.   }
  57.    // 实付总额就是每个商品的价格 * 商品购买数量,然后求和
  58.    private int 计算应付总额(Map<Integer, Product> productMap, Map<Integer, Integer> toBoughtProductMap) {
  59.        Set<Integer> productIdSet = productMap.keySet();
  60.        int sum = 0;
  61.        for (Integer productId : productIdSet) {
  62.            int number = toBoughtProductMap.get(productId);
  63.            Product product = productMap.get(productId);
  64.            int price = product.getPrice();
  65.            int productPayable = price * number;
  66.            sum += productPayable;
  67.       }
  68.        return sum;
  69.   }
  70.    private String generateUUID() {
  71.        String s = UUID.randomUUID().toString();
  72.        return s.replace("-", "");
  73.   }
  74.    private void 商品减库存(Map<Integer, Integer> toBoughtProductMap) {
  75.        for (Map.Entry<Integer, Integer> entry : toBoughtProductMap.entrySet()) {
  76.            int productId = entry.getKey();
  77.            int number = entry.getValue();
  78.            productMapper.decrementStockByProductId(productId, number);
  79.       }
  80.   }
  81.    private Map<Integer, Product> 确认商品库存是否足够(Map<Integer, Integer> toBoughtProductMap) {
  82.        // 把所有的商品 id 收集起来
  83.        Set<Integer> productIdSet = toBoughtProductMap.keySet();
  84.        List<Product> productList = productMapper.selectProductListByProductIdSet(productIdSet);
  85.        // 把 List<Product> -> Map<商品id, Product>
  86.        Map<Integer, Product> productMap = new HashMap<>();
  87.        for (Product product : productList) {
  88.            productMap.put(product.getProductId(), product);
  89.       }
  90.        // TODO: 这里暂时不考虑传入的商品 id在表中不存在的情况了
  91.        // 比较库存是否够
  92.        for (Integer productId : productIdSet) {
  93.            int number = toBoughtProductMap.get(productId);
  94.            Product product = productMap.get(productId);
  95.            if (number > product.getStock()) {
  96.                throw new RuntimeException(product.getProductId().toString());   // 说明我们这个商品的库存是不够的
  97.           }
  98.       }
  99.        // 说明所有商品的库存都是够的
  100.        return productMap;
  101.   }

查询库存和修改库存

ProductMapper下selectProductListByProductIdSet,decrementStockByProductId

  1. List<Product> selectProductListByProductIdSet(@Param("list") Set<Integer> productIdSet);
  2. void decrementStockByProductId(@Param("productId") int productId, @Param("number") int number);

ProductMapper.xml

  1. <select id="selectProductListByProductIdSet" resultType="com.xlinzhang.cash.model.product.Product">
  2.    select product_id, user_id, name, introduce, stock, unit, price, discount
  3.    from products where product_id in (
  4.    <foreach collection="list" item="product_id" separator=", ">
  5.        #{product_id}
  6.    </foreach>
  7.   )
  8. </select>
  9. <update id="decrementStockByProductId">
  10.    update products set stock = stock - #{number} where product_id = #{productId}
  11. </update>

插入订单记录

OrderMapper下insert

void insert(Order order);

OrderMapper.xml

  1. <insert id="insert" useGeneratedKeys="true" keyProperty="orderId" keyColumn="order_id">
  2.    insert into orders
  3.   (user_id, uuid, created_at, payable, actual, status)
  4.    values
  5.   (#{userId}, #{uuid}, #{createdAt}, #{payable}, #{actual}, #{status.value})
  6. </insert>

插入订单项

OrderItemMapper下insertBatch

void insertBatch(@Param("set") Set<OrderItem> orderItemSet);

OrderItemMapper.xml

  1. <insert id="insertBatch" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
  2.    insert into order_items
  3.   (
  4.    order_id,
  5.    product_id,
  6.    product_name,
  7.    product_introduce,
  8.    product_number,
  9.    product_unit,
  10.    product_price,
  11.    product_discount
  12.   )
  13.    values
  14.    <foreach collection="set" item="item" separator=", ">
  15.       (
  16.        #{item.orderId},
  17.        #{item.productId},
  18.        #{item.productName},
  19.        #{item.productIntroduce},
  20.        #{item.productNumber},
  21.        #{item.productUnit},
  22.        #{item.productPrice},
  23.        #{item.productDiscount}
  24.       )
  25.    </foreach>
  26. </insert>

确认订单事务

OrderService下confirm:更新当前订单状态为已支付

  1. @Transactional  // 虽然只有一条 SQL,但加上也没错,本来一条 SQL 就是隐式事务
  2. public void confirm(int orderId) {
  3.    // 更新订单记录(状态变成已支付,设置订单完成时间是当前时间)
  4.    Timestamp finishedAt = Timestamp.from(Instant.now());
  5.    OrderStatus status = OrderStatus.已支付;
  6.    orderMapper.updateConfirm(orderId, finishedAt, status);
  7. }

修改订单记录状态

OrderMapper下updateConfirm

  1. void updateConfirm(
  2.        @Param("orderId") int orderId,
  3.        @Param("finishedAt") Timestamp finishedAt,
  4.        @Param("status") OrderStatus status);

OrderMapper.xml文件

  1. <update id="updateConfirm">
  2.    update orders set finished_at = #{finishedAt}, status = #{status.value} where order_id = #{orderId}
  3. </update>

取消订单事务

OrderService下cancel

  1. 根据当前订单id(orderId) 查询出这个订单涉及的所有商品 id 和 它对应的购买数量

  2. 删除所有该订单(orderId) 关联的商品项记录(删除订单项)

  3. 删除这个订单

  4. 遍历每个订单中涉及到的商品,增加商品库存

  1. @Transactional
  2. public void cancel(int orderId) {
  3.    // 1. 根据 orderId 查询出这个订单涉及的所有商品 id 和 它对应的购买数量
  4.    List<OrderItem> orderItemList = orderItemMapper.selectByOrderId(orderId);
  5.    Map<Integer, Integer> toBoughtProductMap = new HashMap<>();
  6.    for (OrderItem orderItem : orderItemList) {
  7.        int productId = orderItem.getProductId();
  8.        int number = orderItem.getProductNumber();
  9.        toBoughtProductMap.put(productId, number);
  10.   }
  11.    // 2. 删除所有该 orderId 关联的 order_items 记录(删除订单项)
  12.    orderItemMapper.deleteByOrderId(orderId);
  13.    // 3. 删除该 orderId 对应的订单记录
  14.    orderMapper.deleteByOrderId(orderId);
  15.    // 4. 遍历每个商品,为每个商品增加库存
  16.    for (Map.Entry<Integer, Integer> entry : toBoughtProductMap.entrySet()) {
  17.        int productId = entry.getKey();
  18.        int number = entry.getValue();
  19.        productMapper.incrementStockByProductId(productId, number);
  20.   }
  21. }

查询订单中商品 和 删除订单项

OrerItemMapper下selectByOrderId,deleteByOrderId

  1. List<OrderItem> selectByOrderId(int orderId);
  2. void deleteByOrderId(int orderId);

OrerItemMapper.xml

  1. <select id="selectByOrderId" resultType="com.xlinzhang.cash.model.order.OrderItem">
  2.    select product_id, product_number from order_items where order_id = #{orderId}
  3. </select>
  4. <delete id="deleteByOrderId">
  5.    delete from order_items where order_id = #{orderId}
  6. </delete>

删除订单记录

OrderMapper下deleteByOrderId

void deleteByOrderId(int orderId);

OrderMapper.xml

  1. <delete id="deleteByOrderId">
  2.    delete from orders where order_id = #{orderId}
  3. </delete>

恢复商品库存

ProductMapper下incrementStockByProductId

void incrementStockByProductId(@Param("productId") int productId, @Param("number") int number);

ProductMapper.xml文件

  1. <update id="incrementStockByProductId">
  2.    update products set stock = stock + #{number} where product_id = #{productId}
  3. </update>

购买商品

资源名作用类型
/order/create.html提供一个form表单静态资源
/order/create.do保存订单动态资源

购买

DoController下/order/create.do

1. 将订单参数createParam转成Map<商品id,购买数量>:

2. 判断用户是否登录

        1)未登录:login.html

        2)登录:session记录,继续执行

3. 生成订单:orderService下create事务

4. 跳转至用户正在处理的订单(刚刚生成的订单)详情页面

  1. @PostMapping("/order/create.do")
  2. public String orderCreate(@RequestParam("create-param") String createParam, HttpServletRequest request) {
  3.    log.debug("购买商品: createParam = {}", createParam);
  4.    // 1. 将订单createParam转成Map<商品id,购买数量>
  5.    Map<Integer, Integer> toBoughtProductMap = new HashMap<>();
  6.    // TODO: 暂时不考虑参数合法性的问题
  7.    String[] split = createParam.split(",");
  8.    for (String s : split) {
  9.        String[] group = s.split("-");
  10.        String productIdStr = group[0];     // 参数不对,就会抛异常
  11.        String numberStr = group[1];     // 参数不对,就会抛异常
  12.        int productId = Integer.parseInt(productIdStr);     // 参数不对,就会抛异常
  13.        int number = Integer.parseInt(numberStr);           // 参数不对,就会抛异常
  14.        toBoughtProductMap.put(productId, number);
  15.   }
  16.    log.debug("toBoughtProductMap = {}", toBoughtProductMap);
  17.    // 2. 判断用户是否登录
  18.    User currentUser = null;
  19.    HttpSession session = request.getSession(false);
  20.    if (session != null) {
  21.        currentUser = (User) session.getAttribute("currentUser");
  22.   }
  23.    if (currentUser == null) {
  24.        // 说明用户未登录
  25.        log.debug("商品更新: 用户未登录,无权进行该操作");
  26.        return "redirect:/login.html";  // 重定向到登录页,让用户登录
  27.   }
  28.    log.debug("当前用户: user = {}", currentUser);
  29.    Order order = orderService.create(currentUser, toBoughtProductMap);
  30.    log.debug("创建订单: {}", order);
  31.    return "redirect:/order/detail/" + order.getUuid();
  32. }

正在处理的订单详情

功能1:订单信息展示

DoController下/order/detail/{uuid}

1. 判断用户是否登录

        1)未登录:login.html

        2)登录:session记录,继续执行

2. 查询当前正在处理的订单内容并展示

  1. @GetMapping("/order/detail/{uuid}")
  2. public String orderDetail(@PathVariable("uuid") String uuid, HttpServletRequest request, Model model) {
  3.    log.debug("订单详情: uuid = {}", uuid);
  4.    User currentUser = null;
  5.    HttpSession session = request.getSession(false);
  6.    if (session != null) {
  7.        currentUser = (User) session.getAttribute("currentUser");
  8.   }
  9.    if (currentUser == null) {
  10.        // 说明用户未登录
  11.        log.debug("商品更新: 用户未登录,无权进行该操作");
  12.        return "redirect:/login.html";  // 重定向到登录页,让用户登录
  13.   }
  14.    log.debug("当前用户: user = {}", currentUser);
  15.    OrderDetail orderDetail = orderService.query(uuid);
  16.    log.debug("order = {}", orderDetail);
  17.    model.addAttribute("order", orderDetail);
  18.    return "order-detail";
  19. }

展示用户正在处理的订单详情

OrderService下query

  1. public OrderDetail query(String uuid) {
  2.    OrderDetail order = orderMapper.selectByUUID(uuid);
  3.    order.setItemList(orderItemMapper.selectAllByOrderId(order.getOrderId()));
  4.    return order;
  5. }

查询用户处理的订单

OrderMapper下selectByUUID

OrderDetail selectByUUID(String uuid);

OrderMapper.xml

  1. <select id="selectByUUID" resultType="com.xlinzhang.cash.model.order.OrderDetail">
  2.    select
  3.    order_id, orders.user_id, username, uuid, created_at, status, payable, actual
  4.    from orders
  5.    join users on users.user_id = orders.user_id
  6.    where uuid = #{uuid}
  7. </select>

查询当前订单的具体订单项

OrderItemMapper下selectAllByOrderId

List<OrderItemDetail> selectAllByOrderId(int orderId);

OrderItemMapper.xml

  1. <select id="selectAllByOrderId" resultType="com.xlinzhang.cash.model.order.OrderItemDetail">
  2.    select product_id, product_name, product_price, product_number, product_unit, product_discount
  3.    from order_items where order_id = #{orderId}
  4.    order by id
  5. </select>

功能2:确认订单

DoController下/order/confirm/{orderId}

  1. 判断用户是否登录,未登录:login.html;登录:session记录,继续执行

  2. 完成确认订单事务:OrderService下confirm事务

  3. 转到浏览订单页

  1. @GetMapping("/order/confirm/{orderId}")
  2. public String orderConfirm(@PathVariable int orderId, HttpServletRequest request) {
  3.    log.debug("确认订单: orderId = {}", orderId);
  4.    User currentUser = null;
  5.    HttpSession session = request.getSession(false);
  6.    if (session != null) {
  7.        currentUser = (User) session.getAttribute("currentUser");
  8.   }
  9.    if (currentUser == null) {
  10.        // 说明用户未登录
  11.        log.debug("商品更新: 用户未登录,无权进行该操作");
  12.        return "redirect:/login.html";  // 重定向到登录页,让用户登录
  13.   }
  14.    log.debug("当前用户: user = {}", currentUser);
  15.    orderService.confirm(orderId);
  16.    return "redirect:/order/list.html";
  17. }

功能2:取消订单

DoController下/order/cancel/{orderId}

  1. 判断用户是否登录,未登录:login.html;登录:session记录,继续执行

  2. 完成取消订单事务:OrderService下cancel事务

  3. 转到浏览订单页

  1. @GetMapping("/order/cancel/{orderId}")
  2. public String orderCancel(@PathVariable int orderId, HttpServletRequest request) {
  3.    log.debug("确认订单: orderId = {}", orderId);
  4.    User currentUser = null;
  5.    HttpSession session = request.getSession(false);
  6.    if (session != null) {
  7.        currentUser = (User) session.getAttribute("currentUser");
  8.   }
  9.    if (currentUser == null) {
  10.        // 说明用户未登录
  11.        log.debug("商品更新: 用户未登录,无权进行该操作");
  12.        return "redirect:/login.html";  // 重定向到登录页,让用户登录
  13.   }
  14.    log.debug("当前用户: user = {}", currentUser);
  15.    orderService.cancel(orderId);
  16.    return "redirect:/order/list.html";
  17. }

浏览所有订单记录

资源名作用类型
/order/list.html提供HTML框架静态资源
/order/list.js发起Ajax请求,读取JSON格式的order列表,修改DOM树(动态的添加数据记录)静态资源
/order/list.json返回所有的order,以JSON形式返回动态资源

获取订单表数据记录

JsonController下/order/list.json

  1. 判断用户是否登录:通过session中的“currentUser”

    1)否:无权限进行浏览操作,回到登录页面

    2)是:获取当前登录用户信息,即User对象,继续进行浏览操作

  2. 以列表形式返回Order表中数据:List<Order>

  3. 将后端列表数据 转换成 前端用的OrderListView对象:由于对外展示的对象格式和内部使用的对象略有区别,因此我们定义一个OrderView类。从数据表中,我们获取到的是一组view对象,因此再定义一个OrderListView类。这两个类只会在当前Controller下使用,所以就定义成当前Controller下的静态内部类

  1. @Data
  2. private static class OrderView {
  3.    private String uuid;
  4.    private String status;
  5.    private Timestamp createdAt;
  6.    private Timestamp finishedAt;
  7.    OrderView(Order order) {
  8.        this.uuid = order.getUuid();
  9.        this.status = order.getStatus().toString();
  10.        this.createdAt = order.getCreatedAt();
  11.        this.finishedAt = order.getFinishedAt();
  12.   }
  13. }
  14. @Data
  15. private static class OrderListView {
  16.    @JsonInclude(JsonInclude.Include.NON_NULL)
  17.    private String redirectUrl;
  18.    @JsonInclude(JsonInclude.Include.NON_NULL)
  19.    private List<OrderView> data;
  20.    public static OrderListView success(List<Order> orderList) {
  21.        OrderListView view = new OrderListView();
  22.        view.data = orderList.stream()
  23.               .map(OrderView::new)
  24.               .collect(Collectors.toList());
  25.        return view;
  26.   }
  27.    public static OrderListView failure(String redirectUrl) {
  28.        OrderListView view = new OrderListView();
  29.        view.redirectUrl = redirectUrl;
  30.        return view;
  31.   }
  32. }
  33. @GetMapping("/order/list.json")
  34. public OrderListView orderList(HttpServletRequest request) {
  35.    User currentUser = null;
  36.    HttpSession session = request.getSession(false);
  37.    if (session != null) {
  38.        currentUser = (User) session.getAttribute("currentUser");
  39.   }
  40.    if (currentUser == null) {
  41.        // 说明没有登录
  42.        return OrderListView.failure("/login.html");
  43.   }
  44.    List<Order> orderList = orderService.getList();
  45.    return OrderListView.success(orderList);
  46. }

查询订单记录列表

OrserSerive下getList

  1. public List<Order> getList() {
  2.    return orderMapper.selectAll();
  3. }

OrderMapper下selectAll

List<Order> selectAll();

OrderMapper.xml

  1. <select id="selectAll" resultType="com.xlinzhang.cash.model.order.Order">
  2.    select
  3.    uuid, status, created_at, finished_at
  4.    from orders
  5.    order by order_id desc
  6. </select>

项目总结

项目实现了超市收银系统的基本功能,为用户提供了登陆、注册、上架商品、购买商品等基础功能。未来项目功能还可以进行改进:

1功能方面:增加统计功能(为超市老板开发).增加权限控制 (不同角色的功能不同)

2易用性方面:参数合法性校验、更新商品不需要再全部输入、购买商品可以使用对接扭描墙

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号