领域模型ItemModel
package com.miaoshaproject.service.model;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public class ItemModel {
private Integer id;
// 商品名称
@NotBlank(message = "商品名称不能为空")
private String title;
// 商品价格
@NotNull(message = "商品价格不能为空")
@Min(value = 0,message = "商品价格必须大于0")
private BigDecimal price;
// 商品库存
@NotNull(message = "库存不能不填")
private Integer stock;
//商品描述
@NotBlank(message = "商品描述信息不能为空")
private String description;
// 销量
private Integer sales;
// 商品描述图片的url
@NotBlank(message = "商品图片信息不能为空")
private String imgUrl;
// 使用聚合模型,若promoModel不为空,则表示其拥有还未结束的秒杀活动
private PromoModel promoModel;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public PromoModel getPromoModel() {
return promoModel;
}
public void setPromoModel(PromoModel promoModel) {
this.promoModel = promoModel;
}
}
根据领域模型设计表item和item_stock
根据数据库表生成对应的dao,dataObject以及sql.xml
编写service及其实现
- 包括创建商品,包括对item和item_stock表的操作,所以增加事务
- 获取商品
- 商品列表浏览
- 商品详情浏览
package com.miaoshaproject.service;
import com.miaoshaproject.error.BusinessException;
import com.miaoshaproject.service.model.ItemModel;
import java.util.List;
public interface ItemService {
// 创建商品
ItemModel createItem(ItemModel itemModel) throws BusinessException;
// 商品列表浏览
List<ItemModel> listItem();
// 商品详情浏览
ItemModel getItemById(Integer id);
// 库存扣减
boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException;
// 商品销量增加
void increaseSales(Integer itemId, Integer amount) throws BusinessException;
}
package com.miaoshaproject.service.impl;
import com.miaoshaproject.dao.ItemDOMapper;
import com.miaoshaproject.dao.ItemStockDOMapper;
import com.miaoshaproject.dao.PromoDOMapper;
import com.miaoshaproject.dataobject.ItemDO;
import com.miaoshaproject.dataobject.ItemStockDO;
import com.miaoshaproject.error.BusinessException;
import com.miaoshaproject.error.EmBussinessError;
import com.miaoshaproject.service.ItemService;
import com.miaoshaproject.service.PromoService;
import com.miaoshaproject.service.model.ItemModel;
import com.miaoshaproject.service.model.PromoModel;
import com.miaoshaproject.validator.ValidatorImpl;
import com.miaoshaproject.validator.ValidatorResult;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Service
public class ItemServiceImpl implements ItemService {
@Autowired
private ValidatorImpl validator;
@Autowired
private ItemDOMapper itemDOMapper;
@Autowired
private ItemStockDOMapper itemStockDOMapper;
@Autowired
private PromoService promoService;
@Override
@Transactional
public ItemModel createItem(ItemModel itemModel) throws BusinessException {
// 校验入参
ValidatorResult validate = validator.validate(itemModel);
if (validate.isHasErrors()) {
throw new BusinessException(EmBussinessError.PARAMETER_VALIDATION_ERROR,validate.getErrMsg());
}
// 转化item model -> dataobject
ItemDO itemDO = this.convertItemDOFromItemModel(itemModel);
// 写入数据库
itemDOMapper.insertSelective(itemDO);
itemModel.setId(itemDO.getId()); // id自增,插入数据库之后才有id
ItemStockDO itemStockDO = this.convertItemStockDoFromItemModel(itemModel);
itemStockDOMapper.insertSelective(itemStockDO);
// 返回创建完成的对象
return this.getItemById(itemModel.getId());
}
private ItemDO convertItemDOFromItemModel(ItemModel itemModel) {
if(itemModel == null) {
return null;
}
ItemDO itemDO = new ItemDO();
BeanUtils.copyProperties(itemModel, itemDO);
itemDO.setPrice(itemModel.getPrice().doubleValue());
return itemDO;
}
private ItemStockDO convertItemStockDoFromItemModel(ItemModel itemModel) {
if(itemModel == null) {
return null;
}
ItemStockDO itemStockDO = new ItemStockDO();
itemStockDO.setItemId(itemModel.getId());
itemStockDO.setStock(itemModel.getStock());
return itemStockDO;
}
@Override
public List<ItemModel> listItem() {
List<ItemModel> itemModels = new ArrayList<>();
List<ItemDO> itemDOS = itemDOMapper.listItem();
for (ItemDO itemDO : itemDOS) {
ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
ItemModel itemModel = convertModelFromDataObject(itemDO, itemStockDO);
itemModels.add(itemModel);
}
return itemModels;
}
@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() != 3) {
itemModel.setPromoModel(promoModel);
}
return itemModel;
}
@Override
@Transactional
public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
int affectedRow = itemStockDOMapper.decreaseStock(itemId, amount);
if(affectedRow > 0) {
// 更新库存成功
return true;
}
// 更新库存失败
return false;
}
@Override
@Transactional
public void increaseSales(Integer itemId, Integer amount) throws BusinessException {
itemDOMapper.increaseSales(itemId, amount);
}
private ItemModel convertModelFromDataObject(ItemDO itemDO, ItemStockDO itemStockDO) {
ItemModel itemModel = new ItemModel();
BeanUtils.copyProperties(itemDO, itemModel);
itemModel.setPrice(new BigDecimal(itemDO.getPrice()));
itemModel.setStock(itemStockDO.getStock());
return itemModel;
}
}
controller实现
package com.miaoshaproject.controller;
import com.miaoshaproject.controller.viewobject.ItemVO;
import com.miaoshaproject.dataobject.ItemDO;
import com.miaoshaproject.error.BusinessException;
import com.miaoshaproject.response.CommonReturnType;
import com.miaoshaproject.service.ItemService;
import com.miaoshaproject.service.model.ItemModel;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Controller("/item")
@RequestMapping("/item")
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*", originPatterns ="*")
public class ItemController extends BaseController {
@Autowired
private ItemService itemService;
// 创建商品的controller
@RequestMapping(value = "/create", method={RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createItem(@RequestParam(name = "title") String title,
@RequestParam(name = "description") String description,
@RequestParam(name = "price") BigDecimal price,
@RequestParam(name = "stock") Integer stock,
@RequestParam(name = "imgUrl") String imgUrl) throws BusinessException {
//封装service请求用来创建商品
ItemModel itemModel = new ItemModel();
itemModel.setTitle(title);
itemModel.setDescription(description);
itemModel.setPrice(price);
itemModel.setStock(stock);
itemModel.setImgUrl(imgUrl);
ItemModel itemModelForReturn = itemService.createItem(itemModel);
ItemVO itemVO = convertVOFromItemModel(itemModelForReturn);
return CommonReturnType.create(itemVO); // itemVo需要实现get/set方法,否则response是fail
}
// 商品详情页浏览
@RequestMapping(value = "/get", method={RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id) {
ItemModel itemById = itemService.getItemById(id);
ItemVO itemVO = convertVOFromItemModel(itemById);
return CommonReturnType.create(itemVO);
}
// 商品列表页面浏览
@RequestMapping(value = "/list", method={RequestMethod.GET})
@ResponseBody
public CommonReturnType getItemList() {
List<ItemModel> itemModels = itemService.listItem();
List<ItemVO> itemVOS = new ArrayList<>();
for (ItemModel itemModel : itemModels) {
ItemVO itemVO = convertVOFromItemModel(itemModel);
itemVOS.add(itemVO);
}
return CommonReturnType.create(itemVOS);
}
private ItemVO convertVOFromItemModel(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.setPromoStartDate(itemModel.getPromoModel().getStartTime().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
itemVO.setPromoPrice(itemModel.getPromoModel().getPromoItemPrice());
} else {
itemVO.setPromoStatus(0);
}
return itemVO;
}
}
为了前端可视化,创建ItemVO
package com.miaoshaproject.controller.viewobject;
import org.hibernate.validator.constraints.NotBlank;
import org.joda.time.DateTime;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public class ItemVO {
private Integer id;
// 商品名称
private String title;
// 商品价格
private BigDecimal price;
// 商品库存
private Integer stock;
//商品描述
private String description;
// 销量
private Integer sales;
// 商品描述图片的url
private String imgUrl;
// 记录商品是否在秒杀活动中,以及对应的状态0(没有秒杀活动),1(秒杀活动待开始),3(秒杀活动进行中)
private Integer promoStatus;
// 秒杀活动价格
private BigDecimal promoPrice;
// 秒杀活动id
private Integer promoId;
// 秒杀活动开始时间:倒计时展示用
private String promoStartDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public Integer getPromoStatus() {
return promoStatus;
}
public void setPromoStatus(Integer promoStatus) {
this.promoStatus = promoStatus;
}
public BigDecimal getPromoPrice() {
return promoPrice;
}
public void setPromoPrice(BigDecimal promoPrice) {
this.promoPrice = promoPrice;
}
public Integer getPromoId() {
return promoId;
}
public void setPromoId(Integer promoId) {
this.promoId = promoId;
}
public String getPromoStartDate() {
return promoStartDate;
}
public void setPromoStartDate(String promoStartDate) {
this.promoStartDate = promoStartDate;
}
}
前端页面使用ajax+jQuery实现
createitem.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>
</head>
<body class="login">
<div class="content">
<h3 class="form-title">创建商品</h3>
<div class="form-group">
<label class="control-label">商品名</label>
<div>
<input class="form-control" type="text" placeholder="商品名" name="title" id="title"/>
</div>
</div>
<div class="form-group">
<label class="control-label">商品描述</label>
<div>
<input class="form-control" type="text" placeholder="商品描述" name="description" id="description"/>
</div>
</div>
<div class="form-group">
<label class="control-label">价格</label>
<div>
<input class="form-control" type="text" placeholder="价格" name="price" id="price"/>
</div>
</div>
<div class="form-group">
<label class="control-label">图片</label>
<div>
<input class="form-control" type="text" placeholder="图片" name="imgUrl" id="imgUrl"/>
</div>
</div>
<div class="form-group">
<label class="control-label">库存</label>
<div>
<input class="form-control" type="text" placeholder="库存" name="stock" id="stock"/>
</div>
</div>
<div class="form-actions">
<button class="btn blue" id="create" type="submit">
提交创建
</button>
</div>
</div>
</body>
<script>
jQuery(document).ready(function() {
// 绑定otp的click事件,用于向后端发送获取手机验证码的请求
$("#create").on("click", function () {
var title = $("#title").val();
var description = $("#description").val();
var price = $("#price").val();
var imgUrl = $("#imgUrl").val();
var stock = $("#stock").val();
if(title == null || title == "") {
alert("商品名不能为空");
return false;
}
if(description == null || description == "") {
alert("商品描述不能为空");
return false;
}
if(price == null || price == "") {
alert("商品价格不能为空");
return false;
}
if(imgUrl == null || imgUrl == "") {
alert("商品图片不能为空");
return false;
}
if(stock == null || stock == "") {
alert("商品库存不能为空");
return false;
}
$.ajax({
type:"POST",
contentType:"application/x-www-form-urlencoded",
url:"http://localhost:8090/item/create",
data:{
"title":title,
"description":description,
"price":price,
"imgUrl":imgUrl,
"stock":stock,
},
xhrFields:{withCredentials:true},
success:function (data) {
if(data.status == "success") {
alert("商品添加成功");
} else {
alert("商品添加失败,原因为:" + data.data.errMsg);
}
},
error:function (data) {
alert("商品添加失败,原因为" + data.responseText);
}
});
return false;
});
});
</script>
</html>
listitem.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>
</head>
<body>
<div class="content">
<h3 class="form-title">商品列表浏览</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>商品名</th>
<th>商品图片</th>
<th>商品描述</th>
<th>商品价格</th>
<th>商品库存</th>
<th>商品销量</th>
</tr>
</thead>
<tbody id="container">
</tbody>
</table>
</div>
</div>
</body>
<script>
// 定义全局商品数组信息
var g_itemList = [];
jQuery(document).ready(function() {
$.ajax({
type:"GET",
contentType:"application/x-www-form-urlencoded",
url:"http://localhost:8090/item/list",
xhrFields:{withCredentials:true},
success:function (data) {
if(data.status == "success") {
g_itemList = data.data;
reloadDom();
} else {
alert("获取商品信息失败,原因为:" + data.data.errMsg);
}
},
error:function (data) {
alert("获取商品信息失败,原因为" + data.responseText);
}
});
});
function reloadDom() {
for(var i = 0; i < g_itemList.length; i++) {
var itemVO = g_itemList[i];
var dom = "<tr data-id='" +
itemVO.id
+"' id='itemDetail" +
itemVO.id
+"'><td>" +
itemVO.title
+"</td><td><img style='width: 100px; height: auto;' src=" +
itemVO.imgUrl
+"></td><td>" +
itemVO.description
+"</td><td>" +
itemVO.price
+"</td><td>" +
itemVO.stock
+"</td><td>" +
itemVO.sales
+"</td></tr>";
$("#container").append($(dom));
$("#itemDetail" + itemVO.id).on("click", function (e) {
window.location.href="getitem.html?id=" + $(this).data("id");
});
}
}
</script>
</html>
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>
</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">
<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">
<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>
<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({
type:"GET",
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);
$("#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.promoStartDate.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.promoStartDate + " 开始售卖 倒计时:" + 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();
} else {
$("#promoPriceContainer").hide();
}
}
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);
}
});
});
});
</script>
</html>