1.创建PromoService接口
public interface PromoService {
PromoModel getPromoByItemId(Integer itemId);
}
2.在PromoDOMapper中可定义方法
PromoDO selectByItemId(Integer itemId);
3.在PromoDOMapper.xml中添加sql语句
<select id="selectByItemId" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from promo
where item_id = #{itemId,jdbcType=INTEGER}
</select>
4.在数据库promo中添加字段end_date 相应的PromoModel和对应的xml文件中需要手动修改
还有PromoDO
在PromoModel中添加private Integer status;来判断秒杀状态 数据库中不用写入
5.创建实现接口的PromoServiceImpl
@Service
public class PromoServiceImpl implements PromoService {
@Autowired
private PromoDOMapper promoDOMapper;
@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.setPromoItemPrice(new BigDecimal(promoDO.getPromoItemPrice()));
promoModel.setStartDate(new DateTime(promoDO.getStartDate()));
promoModel.setEndDate(new DateTime(promoDO.getEndDate()));
return promoModel;
}
}
6.ItemModel
//使用聚合模型,如果promoModel不为空,则表示其拥有还未结束的秒杀活动
private PromoModel promoModel;
public PromoModel getPromoModel() {
return promoModel;
}
public void setPromoModel(PromoModel promoModel) {
this.promoModel = promoModel;
}
7.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());
//=null 没有秒杀活动 !=有秒杀活动
if(promoModel != null && promoModel.getStatus().intValue()!=3){
itemModel.setPromoModel(promoModel);
}
return itemModel;
}
8.ItemVO 前端展示信息添加
//记录商品是否在秒杀活动中 以及秒杀状态 0表示没有秒杀活动 1表示秒杀活动待开始 2表示秒杀活动进行中
private Integer promoStatus;
//秒杀活动价格
private BigDecimal promoPrice;
//秒杀活动ID
private Integer promoId;
//秒杀活动开始时间 用来做倒计时用
private DateTime startDate;
//get set 方法
9.ItemController
//商品详情页浏览
@RequestMapping(value = "/get", method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id") Integer id ){
ItemModel itemModel=itemService.getItemById(id);
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
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());
itemVO.setPromoPrice(itemModel.getPromoModel().getPromoItemPrice());
}else{
itemVO.setPromoStatus(0);
}
return itemVO;
}
10.修改ItemVO中的开始时间类型为String
//秒杀活动开始时间 用来做倒计时用
private String startDate;
11.修改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;
}
12.修改OrderModel
//订单号 有属性
private String id;
//购买用户的id
private Integer userId;
//购买的商品id
private Integer itemId;
//若非空,则表示是以秒杀商品的方式下单
private Integer promoId;
//购买商品的单价,若promoId非空,则表示秒杀商品价格
private BigDecimal itemPrice;
//购买数量
private Integer amount;
//购买金额若promoId非空,则表示秒杀商品价格
private BigDecimal orderPrice;
13.在order_info表中添加字段promo_id
同时修改OrderDO
private Integer promoId;
修改OrderDOMapper.xml
14.修改OrderService
public interface OrderService {
//使用了1.通过前端url上传过来秒杀活动id,然后下单接口内校验对应id是否属于对应商品且活动已开始
//2.直接再下单接口内判断对应的商品是否存在秒杀活动,若存在进行中的则以秒杀价格下单
OrderModel createOrder(Integer userId, Integer itemId,Integer promoId, Integer amount) throws BusinessException;
}
15.OrderServiceImpl
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private SequenceDOMapper sequenceDOMapper;
@Autowired
private ItemService itemService;
@Autowired
private UserService userService;
@Autowired
private OrderDOMapper orderDOMapper;
@Override
//保证创建订单在同一个事务当中
@Transactional
public OrderModel createOrder(Integer userId, Integer itemId,Integer promoId, Integer amount) throws BusinessException {
//1.校验下单状态,下单的商品是否存在,用户是否合法,购买数量是否正确
ItemModel itemModel = itemService.getItemById(itemId);
if(itemModel == null){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
}
UserModel userModel =userService.getUserById(userId);
if(userModel == null){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"用户信息不存在");
}
//下单数量假设为不能小于0 不能大于99
if(amount <=0 || amount >99){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"购买的数量信息不正确");
}
//校验活动信息
if(promoId !=1){
//(1)校验对应活动是否存在这个对应的商品
if(promoId.intValue()!=itemModel.getPromoModel().getId()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"活动信息不正确");
//(2)校验活动是否正在进行中
}else if(itemModel.getPromoModel().getStatus().intValue()!=2){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"活动信息不正确");
}
}
// 2.落单减库存:在调用订单的createorder下单之前,先把amount数量的库存锁定给这个用户使用,若库存不够就为下单失败。下单成功则锁定库存的操作必定成功
// 支付减库存(未使用)
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.setAmount(amount);
if(promoId != null){
orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
}else{
orderModel.setItemPrice(itemModel.getPrice());
}
orderModel.setPromoId(promoId);
orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));
//生成交易流水号--订单号
orderModel.setId(generateOrderNo());
OrderDO orderDO = convertFromOrderModel(orderModel);
orderDOMapper.insertSelective(orderDO);
//加上商品销量
itemService.increaseSales(itemId,amount);
//4.返回前端
return orderModel;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private String generateOrderNo(){
//订单号有16位
StringBuilder stringBuilder = new StringBuilder();
//前八位为时间信息 年月日
//获取今天日期
LocalDateTime now = LocalDateTime.now();
String nowDate= now.format(DateTimeFormatter.ISO_DATE).replace("-","");
stringBuilder.append(nowDate);
//中间六位为自增序列
//获取当前sequence
int sequence = 0;
SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
sequence =sequenceDO.getCurrentValue();
sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
//拼接对应的sequence 凑够六位
String sequenceStr = String.valueOf(sequence);
for(int i =0; i < 6-sequenceStr.length();i++){
stringBuilder.append(0);
}
stringBuilder.append(sequenceStr);
//最后两位为分库分表为1-99 暂时写死
stringBuilder.append("00");
return stringBuilder.toString();
}
//将Model转为为一个dataobject的方式
private OrderDO convertFromOrderModel(OrderModel orderModel){
if(orderModel == null){
return null;
}
OrderDO orderDO =new OrderDO();
BeanUtils.copyProperties(orderModel,orderDO);
orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
return orderDO;
}
}
16.OrderController
@Controller("order")
@RequestMapping("/order")
@CrossOrigin(origins = {"*"},allowCredentials = "true",allowedHeaders="*")
public class OrderController extends BaseController {
@Autowired
private OrderService orderService;
@Autowired
private HttpServletRequest httpServletRequest;
//封装下单请求
@RequestMapping(value = "/createorder", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createOrder(@RequestParam(name = "itemId") Integer itemId,
@RequestParam(name = "amount") Integer amount,
@RequestParam(name="promoId",required=false)Integer promoId) throws BusinessException {
//获取用户登录信息
Boolean isLogin = (Boolean) httpServletRequest.getSession().getAttribute("IS_LOGIN");
if (isLogin == null || !isLogin.booleanValue()) {
throw new BusinessException(EmBusinessError.USER_NOT_LOGIN, "用户还未登录,不能下单");
}
UserModel userModel = (UserModel) httpServletRequest.getSession().getAttribute("LOGIN_USER");
OrderModel orderModel = orderService.createOrder(userModel.getId(), promoId,itemId, amount);
return CommonReturnType.create(null);
}
}
17.修改getitem.html界面 商品详情页
<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<title>Title</title>
</head>
<body class="login">
<div class="content">
<h3 class="form-title">商品详情</h3>
<div id="promoStartDateContainer" class="form-group">
<label style="color: blue" id="promoStatus" class="control-label"></label>
<div>
<label style="color: red" class="control-label" id="promoStartDate" />
</div>
</div>
<div class="form-group">
<label class="control-label">商品名</label>
<div>
<label class="control-label" id="title" />
</div>
</div>
<div class="form-group">
<label class="control-label">商品描述</label>
<div>
<label class="control-label" id="description" />
</div>
</div>
<div id="normalPriceContainer" class="form-group">
<label class="control-label">商品价格</label>
<div>
<label class="control-label" id="price" />
</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" />
</div>
</div>
<div class="form-group">
<label class="control-label">图片</label>
<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" />
</div>
</div>
<div class="form-group">
<label class="control-label">商品销量</label>
<div>
<label class="control-label" id="sales" />
</div>
<div class="form-actions">
<button class="btn blue" id="createorder" type="submit">
下单
</button>
</div>
</div>
</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 () {
$("#createorder").on("click",function () {
$.ajax({
type:"POST",
contentType:"application/x-www-form-urlencoded",
url:"http://localhost:8090/order/createorder",
data:{
"itemId":g_itemVO.id,
"amount":1,
"promoId":g_itemVO.promoId
},
//允许跨域请求
xhrFields:{withCredentials:true},
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",
contentType:"application/x-www-form-urlencoded",
url:"http://localhost:8090/item/get",
data:{
"id":getParam("id"),
},
//允许跨域请求
xhrFields:{withCredentials:true},
success:function (data) {
if (data.status=="success") {
g_itemVO = data.data;
reloadDom();
//每隔一秒钟执行一次
setInterval(reloadDom,1000)
}else {
alert("获取信息失败,原因为" + data.data.errMsg);
}
},
error:function (data) {
alert("获取信息失败,原因为"+data.responseText);
}
});
});
function reloadDom() {
$("#title").text(g_itemVO.title);
$("#imgUrl").attr("src", g_itemVO.imgUrl);
$("#description").text(g_itemVO.description);
$("#price").text(g_itemVO.price);
$("#stock").text(g_itemVO.stock);
$("#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();
}
$("#promoStartDate").text("秒杀活动将于: "+g_itemVO.startDate+"开始售卖 倒计时:"+delta+"秒");
$("#promoPrice").text(g_itemVO.promoPrice);
//秒杀活动没开始时 不能下单
$("#createorder").attr("disabled",true);
}else if(g_itemVO.promoStatus == 2){
//秒杀活动正在进行
$("#promoStartDate").text("秒杀正在进行中");
$("#promoPrice").text(g_itemVO.promoPrice);
//秒杀活动开始时 可以下单
$("#createorder").attr("disabled",false);
$("#normalPriceContainer").hide();
}
}
</script>
</html>
运行效果图 成功!!!