SpringBoot+Mybatis完成商品秒杀项目之秒杀模块开发(九)

本文介绍了如何在SpringBoot+Mybatis环境下开发商品秒杀模块,包括创建秒杀活动模型,整合商品模型,设计数据库,修改Mybatis配置,以及实现服务层和前端界面的调整,确保秒杀价格能在订单中正确体现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第六章 秒杀模块开发

6.1 秒杀模型管理——活动模型创建
1.使用添加依赖

<dependency>
  <groupId>joda-time</groupId>
  <artifactId>joda-time</artifactId>
  <version>2.9.1</version>
</dependency>

2.创建活动模型

public class PromoModel {
    private Integer id;

    //秒杀活动状态:1表示还未开始,2表示正在进行,3表示已结束
    private Integer status;

    
    //秒杀活动名称
    private String promoName;

    //秒杀活动的开始时间
    private DateTime startDate;

    //秒杀活动的结束时间
    private DateTime endDate;

    //秒杀活动的适用商品
    private Integer itemId;

    //秒杀活动的商品价格
    private BigDecimal promoItemPrice;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}

	public String getPromoName() {
		return promoName;
	}

	public void setPromoName(String promoName) {
		this.promoName = promoName;
	}

	public DateTime getStartDate() {
		return startDate;
	}

	public void setStartDate(DateTime startDate) {
		this.startDate = startDate;
	}

	public DateTime getEndDate() {
		return endDate;
	}

	public void setEndDate(DateTime endDate) {
		this.endDate = endDate;
	}

	public Integer getItemId() {
		return itemId;
	}

	public void setItemId(Integer itemId) {
		this.itemId = itemId;
	}

	public BigDecimal getPromoItemPrice() {
		return promoItemPrice;
	}

	public void setPromoItemPrice(BigDecimal promoItemPrice) {
		this.promoItemPrice = promoItemPrice;
	}
    

}

3.设计数据库

CREATE TABLE `promo`  (
  `id` int(100) NOT NULL AUTO_INCREMENT,
  `promo_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '',
  `start_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `end_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `item_id` int(11) NOT NULL DEFAULT 0,
  `promo_item_price` decimal(10, 2) NOT NULL DEFAULT 0.00,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Compact;

4.修改mybatis:

<table tableName="promo" domainObjectName="PromoDO"
       enableCountByExample="false"
       enableUpdateByExample="false"
       enableDeleteByExample="false"
       enableSelectByExample="false"
       selectByExampleQueryId="false" ></table>

秒杀模型管理——活动模型与商品模型结合

1.service

秒杀服务根据商品id,查询得到当前的活动以及其价格

PromoService

PromoModel getPromoByItemId(Integer itemId);

PromoServiceImpl

@Service
public class PromoServiceImpl implements PromoService {

    @Autowired
    private PromoDOMapper promoDOMapper;



    //根据iremId获取即将开始的或者正在进行的活动
    @Override
    public PromoModel getPromoByItemId(Integer itemId) {

        //获取商品对应的秒杀信息
        PromoDO promoDO = promoDOMapper.selectByItemId(itemId);

        //dataobject->model
        PromoModel promoModel = convertFromDataObject(promoDO);
        if (promoModel == null) {
            return null;
        }

        //判断当前时间是否秒杀活动即将开始或正在进行
        DateTime now = new DateTime();
        if (promoModel.getStartDate().isAfterNow()) {
            promoModel.setStatus(1);
        } else if (promoModel.getEndDate().isBeforeNow()) {
            promoModel.setStatus(3);
        } else {
            promoModel.setStatus(2);
        }

        return promoModel;
    }

    private PromoModel convertFromDataObject(PromoDO promoDO) {
        if (promoDO == null) {
            return null;
        }
        PromoModel promoModel = new PromoModel();
        BeanUtils.copyProperties(promoDO, promoModel);
        promoModel.setStartDate(new DateTime(promoDO.getStartDate()));
        promoModel.setEndDate(new DateTime(promoDO.getEndDate()));

        return promoModel;
    }
}

2.使用聚合模型,在ItemModel上添加属性

//使用聚合模型,如果promoModel不为空,则表示其拥有还未结束的秒杀活动
private PromoModel promoModel;

更改ItemServiceImpl

@Override
public ItemModel getItemById(Integer id) {
    ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
    if (itemDO == null) {
        return null;
    }
    //操作获得库存数量
    ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());

    //将dataobject-> Model
    ItemModel itemModel = convertModelFromDataObject(itemDO, itemStockDO);

    //获取活动商品信息
    PromoModel promoModel = promoService.getPromoByItemId(itemModel.getId());
    if (promoModel != null && promoModel.getStatus().intValue() != 3) {
        itemModel.setPromoModel(promoModel);
    }
    return itemModel;
}

同时修改ItemVO

//商品是否在秒杀活动中,以及对应的状态:0表示没有秒杀活动,1表示秒杀活动等待开始,2表示进行中
private Integer promoStatus;

//秒杀活动价格
private BigDecimal promoPrice;

//秒杀活动id
private Integer promoId;

//秒杀活动开始时间
private String startDate;
修改ItemController

private ItemVO convertVOFromModel(ItemModel itemModel) {
    if (itemModel == null) {
        return null;
    }
    ItemVO itemVO = new ItemVO();
    BeanUtils.copyProperties(itemModel, itemVO);
    if (itemModel.getPromoModel() != null) {
        itemVO.setPromoStatus(itemModel.getPromoModel().getStatus());
        itemVO.setPromoId(itemModel.getPromoModel().getId());
                   itemVO.setStartDate(itemModel.getPromoModel().getStartDate().
                    toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
        itemVO.setPromoPrice(itemModel.getPromoModel().getPromoItemPrice());
    } else {
        itemVO.setPromoStatus(0);
    }
    return itemVO;
}

3.修改前端界面

</head>
<body class="login">
<div class="content">
    <h3 class="form-title">商品详情</h3>
    <!-- 仅在有秒杀活动时显示 -->
    <div id="promoStartTimeContainer" class="form-group">
        <label style="color: blue" id="promoStatus" class="control-label"></label>
        <div>
            <label style="color: red" class="control-label" id="promoStartTime"/></label>
        </div>
    </div>
    <div class="form-group"> 

        <div>
            <label class="control-lable" id="title"/></label>
        </div>
    </div>
    <div class="form-group">
        <label class="control-label">商品描述</label>
        <div>
            <label class="control-lable" id="description"/></label>
        </div>
    </div>
    <div id="normalprice" class="form-group">
        <label class="control-label">价格</label>
        <div>
            <label class="control-label" id="price"/></label>
        </div>
    </div>
    <!-- 仅在有秒杀活动时显示 -->
    <div id="promoPriceContainer" class="form-group">
        <label style="color: red" class="control-label">秒杀价格</label>
        <div>
            <label style="color: red" class="control-label" id="promoPrice"/></label>
        </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">库存</label>
        <div>
            <label class="control-label" id="stock" /></label>
        </div>

    </div>
    <div class="form-group">
        <label class="control-label">销量</label>
        <div>
            <label class="control-label" id="sales" /></label>
        </div>

    </div>
     <div class="form-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
    }
    var g_itemVO = {}
    jQuery(document).ready(function(){

      //下单ajax请求
        $("#createorder").on("click",function(){
            $.ajax({
                type:"POST",
                contentType:"application/x-www-form-urlencoded",
                url:"http://localhost:8080/order/createorder",
                xhrFields:{withCredentials:true},
                data:{
                    "itemId":g_itemVO.id,
                    "amount":1,
                    "promoId":g_itemVO.promoId
                },
                success:function(data){
                    if(data.status =="success"){
                        alert("下单成功!");
                        window.location.reload();
                    }else{
                        alert("下单失败!原因为"+data.data.errMsg);
                        if(data.data.errCode=="20003")
                            window.location.href="login.html";
                    }
                },
                error:function(data){
                    alert("下单失败!原因为"+data.responseText);
                }
            });
        });
        //获取商品详情
        $.ajax({
            type:"GET",

            url:"http://localhost:8080/item/get",
            xhrFields:{withCredentials:true},
            data:{
                "id":getParam("id"),
            },
            success:function(data){
                if(data.status =="success"){
                    g_itemVO = data.data;
                    reloadDom();
                    /*设置一个定时器,到秒杀时间后,自动放开下单按钮*/
                    /*每隔1秒钟,执行一次*/
                    setInterval(reloadDom,1000);
                }else{
                    alert("获取商品详情失败!原因为"+data.data.errMsg);
                }
            },
            error:function(data){
                alert("获取商品详情失败!原因为"+data.responseText);
            }
        });

    });
    function reloadDom(){
        $("#title").text(g_itemVO.title);
        $("#description").text(g_itemVO.description);
        $("#stock").text(g_itemVO.stock);
        $("#price").text(g_itemVO.price);
        $("#imgUrl").attr("src",g_itemVO.imgUrl);
        $("#sales").text(g_itemVO.sales);
       //秒杀还未开始,展示开始时间
        if(g_itemVO.promoStatus == 1){

            var startTime = g_itemVO.startDate.replace(new RegExp("-","gm"),"/");
            //转为毫秒
            startTime = (new Date(startTime)).getTime();
            //当前时间转为毫秒
            var nowTime = Date.parse(new Date());
            var delta = (startTime-nowTime)/1000;
            //活动开始了
            if(delta<=0){
                g_itemVO.promoStatus = 2;
                reloadDom();
            }
            $("#promoStartTime").text("该商品将于"+g_itemVO.startDate+"开始秒杀 倒计时:"+delta+"秒");
            //展示秒杀价格
            $("#promoPrice").text(g_itemVO.promoPrice);
            //临近秒杀,将正常下单按钮禁用
            $("#createorder").attr("disabled",true);
            //秒杀正在进行
        }else if(g_itemVO.promoStatus == 2){
            $("#promoStartTime").text("秒杀正在进行...");
            //展示秒杀价格
            $("#promoPrice").text(g_itemVO.promoPrice);
            //秒杀正在进行,放开下单按钮
            $("#createorder").attr("disabled",false);
            //将原价隐藏掉
            $("#normalprice").hide();
        }
        else{
        	$("#promoPriceContainer").hide();
        }
    }

</script>
</html>

4.修改OrderModel

增加秒杀价格字段

//若非空,则表示是以秒杀商品方式下单
private Integer promoId;

//购买时商品的单价,若promoId非空,则表示是以秒杀商品方式下单
private BigDecimal itemPrice;

然后在数据库中,DO中,DOMapper中增加此字段
5.改造下单接口

//1.通过url上传过来秒杀活动id,然后下单接口内校验对应id是否属于对应商品且活动已开始
//2.直接在下单接口内判断对应的商品是否存在秒杀活动,若存在进行中的则以秒杀价格下单
//倾向于使用第一种形式,因为对同一个商品可能存在不同的秒杀活动,而且第二种方案普通销售的商品也需要校验秒杀
OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount) throws BusinessException;

实现

//校验活动信息
        if (promoId != null) {
            //(1)校验对应活动是否存在这个适用商品
            if (promoId.intValue() != itemModel.getPromoModel().getId()) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动信息不正确");
                //(2)校验活动是否正在进行中
            } else if (itemModel.getPromoModel().getStatus() != 2) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动信息不正确");
            }
        }

        //2.落单减库存
        boolean result = itemService.decreaseStock(itemId, amount);
        if (!result) {
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }

        //3.订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setPromoId(promoId);
        orderModel.setAmount(amount);

        if (promoId != null) {
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        } else {
            orderModel.setItemPrice(itemModel.getPrice());
        }

        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(BigDecimal.valueOf(amount)));

在controller层添加参数

@RequestParam(name = "promoId",required = false) Integer promoId,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值