交易
OrderModel
public class OrderModel {
//交易号不用Integer类型
//有明确的属性
private String id;
//购买用户id
private Integer userId;
//购买商品id
private Integer itemId;
//购买商品单价
private BigDecimal itemPrice;
//购买数量
private Integer amount;
//购买金额
private BigDecimal orderPrice;
...
}
OrderDO
myabtis-generator 中添加代码
<table tableName="order_info" domainObjectName="OrderDO"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="sequence_info" domainObjectName="SequenceDO"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
SequenceDOMapper
增加方法完成订单号中间6位自增
SequenceDO getSequenceByName(String name);
xml文件中for update 锁住
<select id="getSequenceByName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from sequence_info
where name = #{name,jdbcType=VARCHAR} for update
</select>
OrderService & OrderServiceImpl
public interface OrderService {
OrderModel createOrder(Integer userId, Integer itemId, Integer amount) throws BusinessException;
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ValidatorImpl validator;
@Autowired
private ItemDOMapper itemDOMapper;
@Autowired
private ItemStockDOMapper itemStockDOMapper;
@Autowired
private ItemServiceImpl itemService;
@Autowired
private UserServiceImpl userService;
@Autowired
private OrderDOMapper orderDOMapper;
@Autowired
private SequenceDOMapper sequenceDOMapper;
@Override
@Transactional
public OrderModel createOrder(Integer userId, Integer itemId, Integer amount) throws BusinessException {
//校验下单状态,下单的商品是否存在,用户是否合法,购买数量是否正确
ItemModel itemModel = itemService.getItemById(itemId);
if (itemId == null) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATTION_ERROR, "商品信息不存在");
}
UserModel userModel = userService.getUserById(userId);
if (userModel == null) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATTION_ERROR, "用户信息不存在");
}
if (amount <= 0 || amount > 99) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATTION_ERROR, "数量信息不正确");
}
//落单减库存
boolean result = itemService.decreaseStock(itemId, amount);
if (!result) {
throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
}
//订单入库
OrderModel orderModel = new OrderModel();
orderModel.setUserId(userId);
orderModel.setItemId(itemId);
orderModel.setAmount(amount);
//生成交易流水号,(订单号)
orderModel.setId(generateOrderNo());
OrderDO orderDO = convertFromOrderModel(orderModel);
orderDOMapper.insertSelective(orderDO);
//加上商品的销量
itemService.increaseSales(itemId, amount);
return orderModel;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)//不与创建订单在一个事务
private String generateOrderNo() {
//订单号为16位
StringBuilder stringBuilder = new StringBuilder();
// 前 8 位为时间信息,年月日 (方便管理 删除)
LocalDateTime now = LocalDateTime.now();
String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-", "");
stringBuilder.append(nowDate);
//中间 6 位为自增序列 (确保订单号唯一)
int sequence = 0;
SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
sequence = sequenceDO.getCurrentValue();
sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
//拼接构成6位
String sequenceStr = String.valueOf(sequence);
for (int i = 0; i < 6 - sequenceStr.length(); i++) {
stringBuilder.append(0);
}
stringBuilder.append(sequenceStr);
// 最后 2 位为分库分表位
stringBuilder.append("00");
return stringBuilder.toString();
}
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;
}
}
这里讲解了下单减库存和交易减库存的优缺点,根据情况选择以及item 和itenStock分开建表的好处。
下单完成后,库存 -1,销量+1;
ItemStockDOMapper添加decreaseStock 减库存,increaseSales加销量方法
int decreaseStock(@Param("itemId")Integer itemId, @Param("amount") Integer amount);
void increaseSales(Integer itemId, Integer amount) throws BusinessException;
减库存可以先查出库存再进行修改操作,视频里提出在ItemStockDOMapper.xml中添加sql语句一次操作完成,生成订单时即完成。
<update id="decreaseStock">
update item_stock
set stock = stock - #{amount}
where item_id = #{itemId} and stock >= #{amount}
</update>
OrderController
下单前先判断登录状态,这里使用的跟UserController中一样的方式通过session判断,但要注意两个controller中session设置的key值保持一致(“IS_LOGIN”)。
@Controller("order")
@RequestMapping("/order")
@CrossOrigin(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)
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(), itemId, amount);
return CommonReturnType.create(null);
}
}
秒杀
PromoModel
pom.xml 中引入joda
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.1</version>
</dependency>
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;
...
}
PromoDO
myabtis-generator 中添加代码
<table tableName="promo" domainObjectName="PromoDO"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
PromoDOMapper 增加 selectByItemId方法及实现
PromoDO selectByItemId(Integer itemId);
<select id="selectByItemId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from promo
where item_id = #{itemId,jdbcType=INTEGER}
</select>
ItemVO增加秒杀的属性
// 记录商品是否在秒杀活动中 0:没有秒杀活动 1:待开始 2:进行中
private Integer promoStatus;
private BigDecimal promoPrice;
private Integer promoId;
private String startDate;
PromoService & PromoServiceImpl
public interface PromoService {
//根据itemId 获取即将进行的或正在进行的秒杀活动
PromoModel getPromoByItemId(Integer itemId);
}
@Service
public class PromoServiceImpl implements PromoService {
@Autowired
private PromoDOMapper promoDOMapper;
@Override
public PromoModel getPromoByItemId(Integer itemId) {
//获取对应的活动商品信息
PromoDO promoDO = promoDOMapper.selectByItemId(itemId);
PromoModel promoModel = convertFromDataObject(promoDO);
if(promoDOMapper == null){
return null;
}
if(promoModel.getStartDate().isAfterNow()){
promoModel.setStatus(1);
}else if(promoModel.getEndDate().isBeforeNow()){
promoModel.setStatus(3);
}else{
promoModel.setStatus(2);
}
return promoModel;
}
//dataObject->model
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;
}
}
在ItemModel 里添加
private PromoModel promoModel;
OrderDO 增加字段以及修改对应的mapper
private Integer promoId;
修改 ItemController
private ItemVO convertFromModel(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("yyy-MM-dd HH:mm:ss")));
itemVO.setPromoPrice(itemModel.getPromoModel().getPromoItemPrice());
}else{
itemVO.setPromoStatus(0);
}
return itemVO;
}
修改OrderController
@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("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(), itemId, promoId, amount);
return CommonReturnType.create(null);
}