diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDGenerator.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDGenerator.java new file mode 100644 index 0000000..133ce2e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDGenerator.java @@ -0,0 +1,104 @@ +package com.ruoyi.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Created by yangyincong on 15/8/16. + * ID生成器 workId (1~4) + */ +public class IDGenerator { + private final static Logger logger = LoggerFactory.getLogger(IDGenerator.class); + private final static long twepoch = 1361753741828L; + private final static long workerIdBits = 4L; + private final static long maxWorkerId = -1L ^ -1L << workerIdBits; + private final static long sequenceBits = 10L; + private long workerId; + private long sequence = 0L; + + private final static long workerIdShift = sequenceBits; + private final static long timestampLeftShift = sequenceBits + workerIdBits; + private final static long sequenceMask = -1L ^ -1L << sequenceBits; + + private long lastTimestamp = -1L; + + private IDGenerator(final long workerId) { + super(); + this.workerId = workerId; + } + + public static long generateMinId(int wid, long time) { + return (time - twepoch << timestampLeftShift) | (wid << workerIdShift); + } + + public synchronized long nextId() { + long timestamp = this.timeGen(); + if (this.lastTimestamp == timestamp) { + this.sequence = (this.sequence + 1) & sequenceMask; + if (this.sequence == 0) { + timestamp = this.tilNextMillis(this.lastTimestamp); + } + } else { + this.sequence = 0; + } + if (timestamp < this.lastTimestamp) { + try { + throw new Exception( + String.format( + "Clock moved backwards. Refusing to generate id for %d milliseconds", + this.lastTimestamp - timestamp)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + this.lastTimestamp = timestamp; + long nextId = ((timestamp - twepoch << timestampLeftShift)) + | (this.workerId << workerIdShift) | (this.sequence); + return nextId; + } + + public static long generateMaxId(long wid, long time) { + return (time - twepoch << timestampLeftShift) | (wid << workerIdShift) | sequenceMask; + } + + private long tilNextMillis(final long lastTimestamp) { + long timestamp = this.timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = this.timeGen(); + } + return timestamp; + } + + private long timeGen() { + return System.currentTimeMillis(); + } + + private static IDGenerator generator; + + public static synchronized void init(Long workerId) throws Exception { + workerId = workerId % maxWorkerId; + logger.info("程序中init的workid为:{}", workerId); + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format( + "worker Id can't be greater than %d or less than 0", + IDGenerator.maxWorkerId)); + } + generator = new IDGenerator(workerId); + } + + public static Long generateId() { + if (null == generator) { + synchronized (IDGenerator.class) { + if (null == generator) { + try { + init(2L); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return generator.nextId(); + } +} diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java b/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java index 40fe10a..ae8a9b4 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/controller/H5OrderController.java @@ -6,7 +6,12 @@ import com.cyl.h5.pojo.vo.form.OrderSubmitForm; import com.cyl.h5.pojo.vo.query.OrderH5Query; import com.cyl.oms.pojo.vo.OrderVO; import com.cyl.oms.service.OrderService; +import com.cyl.ums.domain.Member; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.redis.RedisService; +import com.ruoyi.framework.config.LocalDataUtil; import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -20,12 +25,32 @@ import java.util.List; @RestController @RequestMapping("/h5/order") +@Slf4j public class H5OrderController { @Autowired private OrderService orderService; + @Autowired + private RedisService redisService; + @PostMapping("/add") - public ResponseEntity submit(@RequestBody OrderSubmitForm form) { - return ResponseEntity.ok(orderService.submit(form)); + public ResponseEntity submit(@RequestBody OrderSubmitForm form) { + Member member = (Member) LocalDataUtil.getVar(Constants.MEMBER_INFO); + Long memberId = member.getId(); + String redisKey = "h5_order_add" + memberId; + String redisValue = memberId + "_" + System.currentTimeMillis(); + try{ + redisService.lock(redisKey, redisValue, 60); + return ResponseEntity.ok(orderService.submit(form)); + }catch (Exception e){ + log.info("创建订单方法异常", e); + return null; + }finally { + try { + redisService.unLock(redisKey, redisValue); + } catch (Exception e) { + e.printStackTrace(); + } + } } @PostMapping("orders") public ResponseEntity> queryOrderPage(@RequestBody OrderH5Query query, Pageable pageReq) { diff --git a/ruoyi-mall/src/main/java/com/cyl/h5/pojo/vo/form/OrderSubmitForm.java b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/vo/form/OrderSubmitForm.java index 7230cc1..2a7e21f 100644 --- a/ruoyi-mall/src/main/java/com/cyl/h5/pojo/vo/form/OrderSubmitForm.java +++ b/ruoyi-mall/src/main/java/com/cyl/h5/pojo/vo/form/OrderSubmitForm.java @@ -1,5 +1,6 @@ package com.cyl.h5.pojo.vo.form; +import com.cyl.h5.pojo.dto.OrderProductListDTO; import lombok.Data; import javax.validation.constraints.NotEmpty; @@ -11,8 +12,12 @@ public class OrderSubmitForm { @NotNull private Long addressId; private String note; + /** 支付方式 0:未支付 1:支付宝 2:微信 默认微信 */ + private Integer payType = 2; + /** 订单来源,购物车则为cart */ + private String from; @NotEmpty - private List skus; + private List skuList; @Data public static class SkuParam { private Long skuId; diff --git a/ruoyi-mall/src/main/java/com/cyl/oms/domain/Order.java b/ruoyi-mall/src/main/java/com/cyl/oms/domain/Order.java index 59745ee..0bc4cc9 100644 --- a/ruoyi-mall/src/main/java/com/cyl/oms/domain/Order.java +++ b/ruoyi-mall/src/main/java/com/cyl/oms/domain/Order.java @@ -2,6 +2,9 @@ package com.cyl.oms.domain; import java.math.BigDecimal; import java.time.LocalDateTime; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.ruoyi.common.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -20,6 +23,7 @@ public class Order extends BaseAudit { private static final long serialVersionUID = 1L; @ApiModelProperty("订单id") + @TableId(type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty("MEMBER_ID") diff --git a/ruoyi-mall/src/main/java/com/cyl/oms/domain/OrderItem.java b/ruoyi-mall/src/main/java/com/cyl/oms/domain/OrderItem.java index 73ae487..5fff192 100644 --- a/ruoyi-mall/src/main/java/com/cyl/oms/domain/OrderItem.java +++ b/ruoyi-mall/src/main/java/com/cyl/oms/domain/OrderItem.java @@ -1,6 +1,9 @@ package com.cyl.oms.domain; import java.math.BigDecimal; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; import com.ruoyi.common.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -19,6 +22,7 @@ public class OrderItem extends BaseAudit { private static final long serialVersionUID = 1L; @ApiModelProperty("ID") + @TableId(type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty("订单id") diff --git a/ruoyi-mall/src/main/java/com/cyl/oms/pojo/dto/SaveOrderItemDTO.java b/ruoyi-mall/src/main/java/com/cyl/oms/pojo/dto/SaveOrderItemDTO.java new file mode 100644 index 0000000..a6b25d9 --- /dev/null +++ b/ruoyi-mall/src/main/java/com/cyl/oms/pojo/dto/SaveOrderItemDTO.java @@ -0,0 +1,4 @@ +package com.cyl.oms.pojo.dto; + +public class SaveOrderItemDTO { +} diff --git a/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderItemService.java b/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderItemService.java index e23d18c..122f639 100644 --- a/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderItemService.java +++ b/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderItemService.java @@ -1,11 +1,16 @@ package com.cyl.oms.service; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.time.LocalDateTime; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.cyl.h5.pojo.dto.OrderProductListDTO; +import com.cyl.ums.domain.Member; import com.github.pagehelper.PageHelper; +import com.ruoyi.common.utils.IDGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.apache.commons.lang3.StringUtils; @@ -13,6 +18,7 @@ import org.springframework.stereotype.Service; import com.cyl.oms.mapper.OrderItemMapper; import com.cyl.oms.domain.OrderItem; import com.cyl.oms.pojo.query.OrderItemQuery; +import org.springframework.transaction.annotation.Transactional; /** * 订单中所包含的商品Service业务层处理 @@ -21,7 +27,7 @@ import com.cyl.oms.pojo.query.OrderItemQuery; * @author zcc */ @Service -public class OrderItemService { +public class OrderItemService extends ServiceImpl { @Autowired private OrderItemMapper orderItemMapper; @@ -136,4 +142,32 @@ public class OrderItemService { public int deleteById(Long id) { return orderItemMapper.deleteById(id); } + + @Transactional + public void saveOrderItem(Member member, LocalDateTime optTime, + Long orderId, List list){ + List addOrderItemList = new ArrayList<>(); + list.forEach(item -> { + OrderItem orderItem = new OrderItem(); + orderItem.setId(IDGenerator.generateId()); + orderItem.setOrderId(orderId); + orderItem.setProductId(item.getProduct().getId()); + orderItem.setOutProductId(item.getProduct().getOutProductId()); + orderItem.setSkuId(item.getSku().getId()); + orderItem.setOutSkuId(item.getSku().getOutSkuId()); + orderItem.setPic(item.getSku().getPic()); + orderItem.setProductName(item.getProduct().getName()); + orderItem.setSalePrice(item.getSku().getPrice()); + orderItem.setQuantity(item.getQuantity()); + orderItem.setProductCategoryId(item.getProduct().getCategoryId()); + orderItem.setSpData(item.getSku().getSpData()); + orderItem.setCreateBy(member.getId()); + orderItem.setCreateTime(optTime); + addOrderItemList.add(orderItem); + }); + boolean flag = saveBatch(addOrderItemList); + if (!flag){ + throw new RuntimeException("新增订单item失败"); + } + } } diff --git a/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderService.java b/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderService.java index d483fab..79d55c4 100644 --- a/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderService.java +++ b/ruoyi-mall/src/main/java/com/cyl/oms/service/OrderService.java @@ -12,7 +12,9 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyl.h5.pojo.dto.OrderCreateDTO; import com.cyl.h5.pojo.dto.OrderProductListDTO; import com.cyl.h5.pojo.vo.OrderCalcVO; @@ -21,7 +23,9 @@ import com.cyl.h5.pojo.vo.form.OrderSubmitForm; import com.cyl.h5.pojo.vo.query.OrderH5Query; import com.cyl.oms.convert.OrderConvert; import com.cyl.oms.domain.OrderItem; +import com.cyl.oms.domain.OrderOperateHistory; import com.cyl.oms.mapper.OrderItemMapper; +import com.cyl.oms.mapper.OrderOperateHistoryMapper; import com.cyl.oms.pojo.vo.OrderVO; import com.cyl.pms.convert.SkuConvert; import com.cyl.pms.domain.Product; @@ -31,12 +35,15 @@ import com.cyl.pms.mapper.SkuMapper; import com.cyl.pms.pojo.vo.SkuVO; import com.cyl.ums.domain.Member; import com.cyl.ums.domain.MemberAddress; +import com.cyl.ums.domain.MemberCart; import com.cyl.ums.mapper.MemberAddressMapper; +import com.cyl.ums.mapper.MemberCartMapper; import com.github.pagehelper.PageHelper; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.common.exception.base.BaseException; +import com.ruoyi.common.utils.IDGenerator; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.config.LocalDataUtil; import com.ruoyi.system.mapper.SysUserMapper; @@ -49,7 +56,10 @@ import org.springframework.stereotype.Service; import com.cyl.oms.mapper.OrderMapper; import com.cyl.oms.domain.Order; import com.cyl.oms.pojo.query.OrderQuery; +import org.springframework.transaction.annotation.Transactional; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; /** @@ -74,6 +84,12 @@ public class OrderService { private ProductMapper productMapper; @Autowired private SkuConvert skuConvert; + @Autowired + private OrderItemService orderItemService; + @Autowired + private OrderOperateHistoryMapper orderOperateHistoryMapper; + @Autowired + private MemberCartMapper memberCartMapper; /** * 查询订单表 @@ -243,73 +259,109 @@ public class OrderService { return orderMapper.deleteById(id); } - public OrderVO submit(OrderSubmitForm form) { - MemberAddress addr = memberAddressMapper.selectById(form.getAddressId()); - if (addr == null) { - throw new BaseException("参数错误"); + @Transactional + public Long submit(OrderSubmitForm form) { + Member member = (Member) LocalDataUtil.getVar(Constants.MEMBER_INFO); + //只支持快递 + Long addressId = form.getAddressId(); + if (addressId == null){ + throw new RuntimeException("收获地址不能为空"); + } + MemberAddress memberAddress = memberAddressMapper.selectById(addressId); + if (memberAddress == null){ + throw new RuntimeException("收货地址不能为空"); + } + //sku不能为空 + List skuList = form.getSkuList(); + if (CollectionUtil.isEmpty(skuList)){ + throw new RuntimeException("商品SKU信息不能为空"); } - List skuIds = form.getSkus().stream().map(OrderSubmitForm.SkuParam::getSkuId).distinct().collect(Collectors.toList()); - List skus = skuMapper.selectBatchIds(skuIds); - if (skuIds.size() != skus.size()) { - throw new BaseException("参数错误"); + //将sku信息转换为 key:skuId ,value:购买数量 + Map skuQuantityMap = skuList.stream().collect(Collectors.toMap(OrderProductListDTO::getSkuId, OrderProductListDTO::getQuantity)); + //查询所有sku信息 + Map querySkuMap = skuMapper + .selectBatchIds(skuList.stream().map(OrderProductListDTO::getSkuId).collect(Collectors.toList())) + .stream().collect(Collectors.toMap(Sku::getId, it -> it)); + //计算商品总额、订单总额(订单总金额=商品总金额,因为暂时没有运费等概念) + BigDecimal productTotalAmount = BigDecimal.ZERO; + BigDecimal orderTotalAmount = BigDecimal.ZERO; + for (OrderProductListDTO dto : skuList){ + if (!querySkuMap.containsKey(dto.getSkuId())){ + throw new RuntimeException("商品SKU不存在"); + } + Sku sku = querySkuMap.get(dto.getSkuId()); + Product product = productMapper.selectById(sku.getProductId()); + if (product == null){ + throw new RuntimeException("商品不存在"); + } + if (Constants.PublishStatus.UNDERCARRIAGE.equals(product.getPublishStatus())){ + throw new RuntimeException("商品" + product.getName() + "已下架"); + } + productTotalAmount = productTotalAmount.add(sku.getPrice().multiply(BigDecimal.valueOf(skuQuantityMap.get(sku.getId())))); + orderTotalAmount = orderTotalAmount.add(sku.getPrice().multiply(BigDecimal.valueOf(skuQuantityMap.get(sku.getId())))); + dto.setSku(sku); + dto.setProduct(product); } - Map id2sku = skus.stream().collect(Collectors.toMap(Sku::getId, it -> it)); - - Map id2Product = productMapper.selectBatchIds( - skus.stream().map(Sku::getProductId).distinct().collect(Collectors.toList()) - ).stream().collect(Collectors.toMap(Product::getId, it -> it)); + LocalDateTime optTime = LocalDateTime.now(); - // 1. 生成订单商品快照 + //生成一个统一的订单号 + Long orderId = IDGenerator.generateId(); + //创建订单 Order order = new Order(); - order.setTotalAmount(BigDecimal.ZERO); - List items = new ArrayList<>(); - form.getSkus().forEach(it -> { - Sku s = id2sku.get(it.getSkuId()); - Product p = id2Product.get(s.getProductId()); - - OrderItem item = new OrderItem(); - item.setProductId(s.getProductId()); - item.setOutProductId(p.getOutProductId()); - item.setSkuId(it.getSkuId()); - item.setOutSkuId(s.getOutSkuId()); - item.setProductSnapshotId(p.getId()); - item.setSkuSnapshotId(s.getId()); - item.setPic(StrUtil.isNotEmpty(s.getPic()) ? s.getPic() : p.getPic()); - item.setProductName(p.getName()); - item.setSalePrice(s.getPrice()); - item.setQuantity(it.getQuantity()); - item.setProductCategoryId(p.getCategoryId()); - item.setSpData(s.getSpData()); - items.add(item); - order.setTotalAmount(order.getTotalAmount().add(s.getPrice().multiply(BigDecimal.valueOf(it.getQuantity())))); - }); - LoginUser user = SecurityUtils.getLoginUser(); - - // 2. 生成订单 - order.setMemberId(user.getUserId()); - order.setMemberUsername(user.getUsername()); - order.setStatus(0); + order.setId(orderId); + order.setMemberId(member.getId()); + order.setMemberUsername(member.getNickname()); + order.setPayType(Constants.PayType.WECHAT); + order.setTotalAmount(orderTotalAmount); + order.setPurchasePrice(BigDecimal.ZERO); + order.setFreightAmount(BigDecimal.ZERO); + order.setPayAmount(orderTotalAmount); + //暂时为接入支付,直接设置为待发货 + order.setStatus(Constants.OrderStatus.SEND); order.setAftersaleStatus(1); - order.setAutoConfirmDay(7); - order.setReceiverName(addr.getName()); - order.setReceiverPhone(addr.getPhone()); - order.setReceiverProvince(addr.getProvince()); - order.setReceiverDistrict(addr.getDistrict()); - order.setReceiverDetailAddress(addr.getDetailAddress()); + order.setReceiverName(memberAddress.getName()); + order.setReceiverPhone(memberAddress.getPhone()); + order.setReceiverPostCode(memberAddress.getPostCode()); + order.setReceiverProvince(memberAddress.getProvince()); + order.setReceiverCity(memberAddress.getCity()); + order.setReceiverDistrict(memberAddress.getDistrict()); + order.setReceiverDetailAddress(memberAddress.getDetailAddress()); order.setNote(form.getNote()); order.setConfirmStatus(0); order.setDeleteStatus(0); - orderMapper.insert(order); - items.forEach(it -> it.setOrderId(order.getId())); - items.forEach(orderItemMapper::insert); - - // 3. 判断是否付费,生成支付订单 - if (BigDecimal.ZERO.compareTo(order.getTotalAmount()) > 0) { - // todo 生成支付订单 + order.setPaymentTime(optTime); + order.setCreateTime(optTime); + order.setCreateBy(member.getId()); + int rows = orderMapper.insert(order); + if (rows < 1){ + throw new RuntimeException("订单新增失败"); + } + // 保存orderItem + orderItemService.saveOrderItem(member, optTime, orderId, skuList); + // 保存订单操作记录 + OrderOperateHistory orderOperateHistory = new OrderOperateHistory(); + orderOperateHistory.setOrderId(orderId); + orderOperateHistory.setOperateMan(member.getId() + ""); + orderOperateHistory.setOrderStatus(Constants.OrderStatus.SEND); + orderOperateHistory.setCreateTime(optTime); + orderOperateHistory.setCreateBy(member.getId()); + rows = orderOperateHistoryMapper.insert(orderOperateHistory); + if (rows < 1){ + throw new RuntimeException("保存订单操作记录失败"); + } + //若来源为购物车,删除购物车 + if (Constants.OrderFrom.CART.equals(form.getFrom())){ + List skuIdList = skuList.stream().map(OrderProductListDTO::getSkuId).collect(Collectors.toList()); + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); + wrapper.eq(MemberCart::getMemberId, member.getId()); + wrapper.in(MemberCart::getSkuId, skuIdList); + rows = memberCartMapper.delete(wrapper); + if (rows < 1){ + throw new RuntimeException("删除购物车失败"); + } } - OrderVO vo = orderConvert.do2vo(order); - vo.setItems(items); - return vo; + //当前返回成功消息,接入支付后可返回payId + return orderId; } public Page queryOrderPage(OrderH5Query query, Pageable pageReq) { @@ -405,4 +457,5 @@ public class OrderService { res.setProductTotalAmount(productTotalAmount); return res; } + }