一、sql

/*
 Navicat Premium Data Transfer

 Source Server         : testOne
 Source Server Type    : MySQL
 Source Server Version : 80028
 Source Host           : localhost:3306
 Source Schema         : miaosha

 Target Server Type    : MySQL
 Target Server Version : 80028
 File Encoding         : 65001

 Date: 20/04/2022 11:14:06
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sequence_info
-- ----------------------------
DROP TABLE IF EXISTS `sequence_info`;
CREATE TABLE `sequence_info`  (
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `current_value` int(0) NOT NULL DEFAULT 0,
  `step` int(0) NOT NULL DEFAULT 0,
  PRIMARY KEY (`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;


INSERT INTO `sequence_info`(`name`, `current_value`, `step`) VALUES ('order_info', 0, 1);
<!--sequence表-->
        <table tableName="sequence_info" domainObjectName="SequenceDO"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false" enableInsert="true"
               enableDeleteByPrimaryKey="false"></table>

二、OrderServiceImpl

  1 package com.miaoshaProject.service.impl;
  2 
  3 import com.miaoshaProject.dao.OrderDOMapper;
  4 import com.miaoshaProject.dao.SequenceDOMapper;
  5 import com.miaoshaProject.dataobject.OrderDO;
  6 import com.miaoshaProject.dataobject.SequenceDO;
  7 import com.miaoshaProject.error.BusinessException;
  8 import com.miaoshaProject.error.EnumBusinessError;
  9 import com.miaoshaProject.service.ItemService;
 10 import com.miaoshaProject.service.OrderService;
 11 import com.miaoshaProject.service.UesrService;
 12 import com.miaoshaProject.service.model.ItemModel;
 13 import com.miaoshaProject.service.model.OrderModel;
 14 import com.miaoshaProject.service.model.UserModel;
 15 import org.springframework.beans.BeanUtils;
 16 import org.springframework.beans.factory.annotation.Autowired;
 17 import org.springframework.stereotype.Service;
 18 import org.springframework.transaction.annotation.Propagation;
 19 import org.springframework.transaction.annotation.Transactional;
 20 
 21 import java.math.BigDecimal;
 22 import java.time.LocalDateTime;
 23 import java.time.format.DateTimeFormatter;
 24 import java.util.Date;
 25 
 26 /**
 27  * @Author wangshuo
 28  * @Date 2022/4/20, 9:35
 29  * Please add a comment
 30  */
 31 @Service
 32 public class OrderServiceImpl implements OrderService {
 33 
 34     @Autowired
 35     OrderDOMapper orderDOMapper;
 36 
 37     @Autowired
 38     ItemService itemService;
 39 
 40     @Autowired
 41     UesrService uesrService;
 42 
 43     @Autowired
 44     SequenceDOMapper sequenceDOMapper;
 45 
 46     @Override
 47     @Transactional
 48     public OrderModel createOrder(Integer userId, Integer itemId, Integer amount) throws BusinessException {
 49 
 50         //校验下单状态 : 下单的商品是否存在,用户是否合法,购买数量是否正确
 51         ItemModel itemModel = itemService.getItemById(itemId);
 52         if (itemModel == null)
 53             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
 54         UserModel byId = uesrService.getById(userId);
 55         if (byId == null)
 56             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR,"用户信息不存在");
 57         if (amount <= 0 || amount > 99)
 58             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR,"订单数量不正确");
 59         //落单减库存
 60         boolean b = itemService.decreaseStock(itemId, amount);
 61         if (!b)
 62             throw new BusinessException(EnumBusinessError.STOCK_NOT_ENOUGH);
 63         //订单入库
 64         OrderModel orderModel = new OrderModel();
 65         orderModel.setUserId(userId);
 66         orderModel.setItemId(itemId);
 67         orderModel.setAmount(amount);
 68         orderModel.setItemPrice(itemModel.getPrice());
 69             //相乘获得订单价格
 70         orderModel.setOrderPrice(itemModel.getPrice().multiply(new BigDecimal(amount)));
 71             //交易流水订单号
 72         orderModel.setId(generateOrderNO());
 73             //转化model
 74         OrderDO orderDO = convertFromOrderModel(orderModel);
 75             //新增订单
 76         orderDOMapper.insertSelective(orderDO);
 77         //增加销量
 78         itemService.increaseSales(orderModel);
 79         //返回前端
 80         return orderModel;
 81     }
 82 
 83     @Transactional(propagation = Propagation.REQUIRES_NEW)//不论外部事务成功与否 我这边都要提交事务,保证自增序列唯一性
 84     public String generateOrderNO(){
 85         //设计 : 订单号有十六位,
 86         StringBuilder stringBuilder = new StringBuilder();
 87         // 前八位为时间信息
 88         LocalDateTime date = LocalDateTime.now();
 89         stringBuilder.append(date.format(DateTimeFormatter.ISO_DATE).replace("-", ""));
 90         // 中间六位为自增序列
 91         Integer sequence = 0;
 92             //1.获取当前sequence
 93         SequenceDO sequence_info = sequenceDOMapper.getSequenceByName("order_info");
 94         sequence = sequence_info.getCurrentValue();
 95             //2.sequence增加步长并入库
 96         sequence_info.setCurrentValue(sequence_info.getCurrentValue() + sequence_info.getStep());
 97         sequenceDOMapper.updateByPrimaryKeySelective(sequence_info);
 98             //3.拼接sequence
 99         String sequenceStr = String.valueOf(sequence);
100         for (int i = 0; i < 6 - sequenceStr.length(); i++) {//当sequence被操作六位数以上后,中间自增序列就不止六位数了
101             stringBuilder.append(0);
102         }
103         stringBuilder.append(sequenceStr);
104         // 最后两位为分库分表位   暂时写死为00
105         stringBuilder.append("00");
106         return stringBuilder.toString();
107     }
108 
109     private OrderDO convertFromOrderModel(OrderModel orderModel){
110 
111         if (orderModel == null)
112             return null;
113         OrderDO orderDO = new OrderDO();
114         BeanUtils.copyProperties(orderModel,orderDO);
115         return orderDO;
116     }
117 }

三、ItemServiceImpl

  1 package com.miaoshaProject.service.impl;
  2 
  3 import com.miaoshaProject.dao.ItemDOMapper;
  4 import com.miaoshaProject.dao.ItemStockDOMapper;
  5 import com.miaoshaProject.dataobject.ItemDO;
  6 import com.miaoshaProject.dataobject.ItemStockDO;
  7 import com.miaoshaProject.error.BusinessException;
  8 import com.miaoshaProject.error.EnumBusinessError;
  9 import com.miaoshaProject.service.ItemService;
 10 import com.miaoshaProject.service.model.ItemModel;
 11 import com.miaoshaProject.service.model.OrderModel;
 12 import com.miaoshaProject.validator.ValidationResult;
 13 import com.miaoshaProject.validator.ValidatorImpl;
 14 import org.springframework.beans.BeanUtils;
 15 import org.springframework.beans.factory.annotation.Autowired;
 16 import org.springframework.stereotype.Service;
 17 import org.springframework.transaction.annotation.Transactional;
 18 
 19 import java.util.List;
 20 import java.util.stream.Collectors;
 21 
 22 /**
 23  * @Author wangshuo
 24  * @Date 2022/4/19, 10:17
 25  * Please add a comment
 26  */
 27 @Service
 28 public class ItemServiceImpl implements ItemService {
 29 
 30     @Autowired
 31     ValidatorImpl validator;
 32     @Autowired
 33     ItemDOMapper itemDOMapper;
 34     @Autowired
 35     ItemStockDOMapper itemStockDOMapper;
 36 
 37     @Override
 38     @Transactional//保证事务唯一性
 39     public ItemModel createItem(ItemModel itemModel) throws BusinessException {
 40 
 41         //校验入参
 42         ValidationResult validator = this.validator.validator(itemModel);
 43         if (validator.isHasErrors())
 44             throw new BusinessException(EnumBusinessError.PARAMETER_VALIDATION_ERROR, validator.getErrMsg());
 45         //转化model -》 dataObject
 46         ItemDO itemDO = convertItemDOFromItemModel(itemModel);
 47         itemDOMapper.insertSelective(itemDO);
 48         itemModel.setId(itemDO.getId());
 49         ItemStockDO itemStockDO = convertItemStockDOFromItemModel(itemModel);
 50         itemStockDOMapper.insertSelective(itemStockDO);
 51         //返回创建完成的对象
 52         return this.getItemById(itemDO.getId());
 53     }
 54 
 55     @Override
 56     public List<ItemModel> listItem() {
 57 
 58         List<ItemDO> list = itemDOMapper.listItem();
 59         //java8 stream  将itemDO map成为 itemModel
 60         List<ItemModel> modelList = list.stream().map(itemDO -> {
 61             ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
 62             ItemModel itemModel = convertModelFromDataObject(itemDO, itemStockDO);
 63             return itemModel;
 64         }).collect(Collectors.toList());
 65         return modelList;
 66     }
 67 
 68     @Override
 69     public ItemModel getItemById(Integer id) {
 70 
 71         ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
 72         if (itemDO == null)
 73             return null;
 74         //操作获得库存数量
 75         ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(id);
 76         //convert
 77         ItemModel itemModel = convertModelFromDataObject(itemDO, itemStockDO);
 78         return itemModel;
 79     }
 80 
 81     //减库存
 82     @Override
 83     @Transactional
 84     public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
 85 
 86         OrderModel orderModel = new OrderModel();
 87         orderModel.setItemId(itemId);
 88         orderModel.setAmount(amount);
 89         int affectedRow = itemStockDOMapper.decreaseStock(orderModel);
 90         //影响条数大于0即为成功
 91         return affectedRow > 0;
 92     }
 93 
 94     //加销量
 95     @Override
 96     @Transactional
 97     public void increaseSales(OrderModel orderModel) throws BusinessException {
 98         itemDOMapper.increaseSales(orderModel);
 99     }
100 
101     private ItemDO convertItemDOFromItemModel(ItemModel itemModel) {
102 
103         if (itemModel == null)
104             return null;
105         ItemDO itemDO = new ItemDO();
106         BeanUtils.copyProperties(itemModel, itemDO);
107         return itemDO;
108     }
109 
110     private ItemStockDO convertItemStockDOFromItemModel(ItemModel itemModel) {
111 
112         if (itemModel == null)
113             return null;
114         ItemStockDO itemStockDO = new ItemStockDO();
115         itemStockDO.setItemId(itemModel.getId());
116         itemStockDO.setStock(itemModel.getStock());
117         return itemStockDO;
118     }
119 
120     private ItemModel convertModelFromDataObject(ItemDO itemDO, ItemStockDO itemStockDO) {
121 
122         ItemModel itemModel = new ItemModel();
123         BeanUtils.copyProperties(itemDO, itemModel);
124         itemModel.setStock(itemStockDO.getStock());
125         return itemModel;
126     }
127 }

四、mapping

<update id="increaseSales" parameterType="com.miaoshaProject.service.model.OrderModel">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Mon Apr 18 09:20:26 CST 2022.
    -->
    update item
    set sales = sales + #{amount}
    where id = #{itemId}
  </update>
ItemDOMapper.xml
<update id="decreaseStock" parameterType="com.miaoshaProject.service.model.OrderModel">
        update item_stock
        set stock = stock - #{amount}
        where item_id = #{itemId}
          and stock >= #{amount}
    </update>
ItemStockDOMapper.xml
<select id="getSequenceByName" parameterType="java.lang.String" resultMap="BaseResultMap">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Wed Apr 20 11:11:22 CST 2022.
    -->
    select
    <include refid="Base_Column_List" />
    from sequence_info
    where name = #{name,jdbcType=VARCHAR} for update
  </select>
SequenceDOMapper.xml

五、OrderController

 1 package com.miaoshaProject.controller;
 2 
 3 import com.miaoshaProject.error.BusinessException;
 4 import com.miaoshaProject.error.EnumBusinessError;
 5 import com.miaoshaProject.response.CommonReturnType;
 6 import com.miaoshaProject.service.OrderService;
 7 import com.miaoshaProject.service.model.OrderModel;
 8 import com.miaoshaProject.service.model.UserModel;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.stereotype.Controller;
11 import org.springframework.web.bind.annotation.CrossOrigin;
12 import org.springframework.web.bind.annotation.RequestMapping;
13 import org.springframework.web.bind.annotation.RequestParam;
14 import org.springframework.web.bind.annotation.ResponseBody;
15 
16 import javax.servlet.http.HttpServletRequest;
17 
18 /**
19  * @Author wangshuo
20  * @Date 2022/4/20, 14:17
21  * Please add a comment
22  */
23 @Controller("order")
24 @RequestMapping("/order")
25 @CrossOrigin(origins = {"*"}, allowCredentials = "true")
26 public class OrderController extends BaseController {
27 
28     @Autowired
29     OrderService orderService;
30     @Autowired
31     HttpServletRequest request;
32 
33     //封装下单请求
34     @RequestMapping("/create")
35     @ResponseBody
36     public CommonReturnType create(@RequestParam(name = "itemId") Integer itemId,
37                                    @RequestParam(name = "amount") Integer amount) throws BusinessException {
38 
39         //获取user信息
40         Integer login_id = (Integer) this.request.getSession().getAttribute("LOGIN_USER");
41         OrderModel orderModel = new OrderModel();
42         if (login_id == null) {
43             /*throw new BusinessException(EnumBusinessError.USER_NOT_EXISTS,"用户未登录");*/
44             orderModel = orderService.createOrder(17, itemId, amount);
45         } else
46             orderModel = orderService.createOrder(login_id, itemId, amount);
47         return CommonReturnType.create(orderModel);
48     }
49 }

六、getItem.html

<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="assets/global/plugins/bootstrap/css/bootstrap.min.css" type="text/css"/>
    <link rel="stylesheet" href="assets/global/css/components.css" type="text/css"/>
    <link rel="stylesheet" href="assets/admin/pages/css/login.css" type="text/css"/>
    <script rel="stylesheet" src="assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
</head>
<body class="login" background="edge_bj_1.jpg">

<div class="content">
    <h3 class="form-title">商品详情</h3>
    <div class="form-group">
        <div>
            <label class="control-label" id="title"/>
        </div>
    </div>
    <div class="form-group">
        <label class="control-label" style="font-size: 18px">商品描述</label>
        <div>
            <label class="control-label" id="description"/>
        </div>
    </div>
    <div class="form-group">
        <div>
            <label class="control-label" id="price"/>
        </div>
    </div>
    <div class="form-group">
        <div>
            <img style="width: 200px;height: auto" id="imgUrl">
        </div>
    </div>
    <div class="form-group">
        <label class="control-label" style="font-size: 18px">商品库存</label>
        <div>
            <label class="control-label" id="stock"/>
        </div>
    </div>
    <div class="form-group">
        <label class="control-label" style="font-size: 18px">商品销量</label>
        <div>
            <label class="control-label" id="sales"/>
        </div>
    </div>
    <div class="from-actions">
        <button class="btn blue" id="createorder" type="submit">
            下单
        </button>
    </div>
</div>

</body>
<script>

    function getParam(paramName) {
        paramValue = "", isFound = !1;
        if (this.location.search.indexOf("?") == 0 && this.location.search.indexOf("=") > 1) {
            arrSource = unescape(this.location.search).substring(1, this.location.search.length).split("&"), i = 0;
            while (i < arrSource.length && !isFound) arrSource[i].indexOf("=") > 0 && arrSource[i].split("=")[0].toLowerCase() == paramName.toLowerCase() && (paramValue = arrSource[i].split("=")[1], isFound = !0), i++
        }
        return paramValue == "" && (paramValue = null), paramValue
    }

    function reloadDom() {

        $("#title").text(g_itemVO.title);
        $("#description").text(g_itemVO.description);
        $("#stock").text(g_itemVO.stock);
        $("#price").text(g_itemVO.price);
        $("#sales").text(g_itemVO.sales);
        $("#imgUrl").attr("src", g_itemVO.imgUrl);
    }

    var g_itemVO = {};

    jQuery(document).ready(function () {

        //下单
        $("#createorder").on("click", function () {
            $.ajax({
                type: "POST",
                contentType: "application/x-www-form-urlencoded",
                url: "http://localhost:8080/order/create",
                data: {
                    "itemId": g_itemVO.id,
                    "amount": 1,
                },
                dataType: "json",
                xhrFields: {
                    withCredentials: true
                },
                crossDomain: true,
                success: function (result) {

                    if (result.status == "success") {
                        alert("购买成功")
                        //刷新界面
                        window.location.reload();
                    } else {
                        alert("请求失败 原因为:" + result.data.errMsg)
                    }
                },
                error: function (result) {
                    alert("请求失败  原因为" + result.responseText)
                }
            })
            return false;
        })

        //获取商品详情
        $.ajax({
            type: "GET",
            contentType: "application/x-www-form-urlencoded",
            url: "http://localhost:8080/item/get",
            data: {
                "id": getParam("id")
            },
            dataType: "json",
            xhrFields: {
                withCredentials: true
            },
            crossDomain: true,
            success: function (result) {

                if (result.status == "success") {
                    g_itemVO = result.data;
                    reloadDom();
                } else {
                    alert("请求失败 原因为:" + result.data.errMsg)
                }
            },
            error: function (result) {
                alert("请求失败  原因为" + result.responseText)
            }
        })
    })
</script>
</html>

 七、EnumBusinessError

 1 package com.miaoshaProject.error;
 2 
 3 /**
 4  * @Author wangshuo
 5  * @Date 2022/4/14, 8:43
 6  * 自定义error
 7  */
 8 public enum EnumBusinessError implements CommonError{
 9     //10001 参数不合法
10     PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
11     //20000 未知错误
12     UNKNOWN_ERROR(20000,"未知错误"),
13     //200001 登录超时或未登录
14     UN_LOGIN_ERROR(20001,"登录超时或未登录"),
15     //以30000 开头的错误码代表用户信息错误
16     USER_NOT_EXISTS(30001,"用户不存在"),
17     REGISTER_OTP_ERROR(30002,"验证码错误"),
18     REGISTER_REPEAT(30003,"该手机号已注册账户,请勿重复注册"),
19     LOGIN_ERROR(30004,"用户手机号或密码不正确"),
20     //以40000 开头的错误码代表交易错误
21     STOCK_NOT_ENOUGH(40001,"库存不足")
22     ;
23 
24     private EnumBusinessError(Integer code,String msg){
25 
26         this.errorCode = code;
27         this.errorMsg = msg;
28     }
29 
30     private int errorCode;
31     private String errorMsg;
32 
33     @Override
34     public int getErrorCode() {
35         return this.errorCode;
36     }
37 
38     @Override
39     public String getErrorMsg() {
40         return this.errorMsg;
41     }
42 
43     //定制化的方法改动错误信息
44     @Override
45     public CommonError setErrorMsg(String msg) {
46         this.errorMsg = msg;
47         return this;
48     }
49 }