|
|
@@ -0,0 +1,418 @@
|
|
|
+package com.haha.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.haha.common.enums.ReplenishmentOrderStatusEnum;
|
|
|
+import com.haha.common.exception.BusinessException;
|
|
|
+import com.haha.entity.ReplenishmentOrder;
|
|
|
+import com.haha.entity.ReplenishmentOrderItem;
|
|
|
+import com.haha.entity.dto.ReplenishmentOrderCreateDTO;
|
|
|
+import com.haha.entity.dto.ReplenishmentOrderQueryDTO;
|
|
|
+import com.haha.entity.dto.ReplenishmentOrderUpdateDTO;
|
|
|
+import com.haha.mapper.ReplenishmentOrderItemMapper;
|
|
|
+import com.haha.mapper.ReplenishmentOrderMapper;
|
|
|
+import com.haha.service.ReplenishmentOrderService;
|
|
|
+import com.qdb.sdk.QdbClient;
|
|
|
+import com.qdb.sdk.model.request.OrderGoods;
|
|
|
+import com.qdb.sdk.model.request.OrderSaveRequest;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 补货单服务实现
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class ReplenishmentOrderServiceImpl extends ServiceImpl<ReplenishmentOrderMapper, ReplenishmentOrder>
|
|
|
+ implements ReplenishmentOrderService {
|
|
|
+
|
|
|
+ private final ReplenishmentOrderMapper orderMapper;
|
|
|
+ private final ReplenishmentOrderItemMapper orderItemMapper;
|
|
|
+
|
|
|
+ @Autowired(required = false)
|
|
|
+ private QdbClient qdbClient;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IPage<ReplenishmentOrder> getPage(ReplenishmentOrderQueryDTO queryDTO) {
|
|
|
+ queryDTO.validate();
|
|
|
+
|
|
|
+ LambdaQueryWrapper<ReplenishmentOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
+
|
|
|
+ if (StringUtils.hasText(queryDTO.getOrderNo())) {
|
|
|
+ wrapper.like(ReplenishmentOrder::getOrderNo, queryDTO.getOrderNo());
|
|
|
+ }
|
|
|
+ if (StringUtils.hasText(queryDTO.getDeviceId())) {
|
|
|
+ wrapper.eq(ReplenishmentOrder::getDeviceId, queryDTO.getDeviceId());
|
|
|
+ }
|
|
|
+ if (queryDTO.getShopId() != null) {
|
|
|
+ wrapper.eq(ReplenishmentOrder::getShopId, queryDTO.getShopId());
|
|
|
+ }
|
|
|
+ if (queryDTO.getStatus() != null) {
|
|
|
+ wrapper.eq(ReplenishmentOrder::getStatus, queryDTO.getStatus());
|
|
|
+ }
|
|
|
+ if (queryDTO.getCreatorId() != null) {
|
|
|
+ wrapper.eq(ReplenishmentOrder::getCreatorId, queryDTO.getCreatorId());
|
|
|
+ }
|
|
|
+ if (StringUtils.hasText(queryDTO.getStartTime())) {
|
|
|
+ wrapper.ge(ReplenishmentOrder::getCreateTime,
|
|
|
+ LocalDate.parse(queryDTO.getStartTime()).atStartOfDay());
|
|
|
+ }
|
|
|
+ if (StringUtils.hasText(queryDTO.getEndTime())) {
|
|
|
+ wrapper.le(ReplenishmentOrder::getCreateTime,
|
|
|
+ LocalDate.parse(queryDTO.getEndTime()).atTime(23, 59, 59));
|
|
|
+ }
|
|
|
+
|
|
|
+ wrapper.orderByDesc(ReplenishmentOrder::getCreateTime);
|
|
|
+
|
|
|
+ Page<ReplenishmentOrder> page = new Page<>(queryDTO.getPage(), queryDTO.getPageSize());
|
|
|
+ IPage<ReplenishmentOrder> result = page(page, wrapper);
|
|
|
+
|
|
|
+ for (ReplenishmentOrder order : result.getRecords()) {
|
|
|
+ fillStatusLabel(order);
|
|
|
+ int count = orderItemMapper.selectByOrderId(order.getId()).size();
|
|
|
+ order.setItemCount(count);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ReplenishmentOrder getDetailWithItems(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ReplenishmentOrderItem> getItems(Long orderId) {
|
|
|
+ return orderItemMapper.selectByOrderId(orderId);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder createOrder(ReplenishmentOrderCreateDTO dto, Long creatorId, String creatorName) {
|
|
|
+ ReplenishmentOrder order = new ReplenishmentOrder();
|
|
|
+ order.setOrderNo(generateOrderNo());
|
|
|
+ order.setDeviceId(dto.getDeviceId());
|
|
|
+ order.setShopId(dto.getShopId());
|
|
|
+ order.setStatus(ReplenishmentOrderStatusEnum.DRAFT.getCode());
|
|
|
+ order.setSupplierName(dto.getSupplierName());
|
|
|
+ order.setWarehouseName(dto.getWarehouseName());
|
|
|
+ order.setExpectedArrivalTime(dto.getExpectedArrivalTime());
|
|
|
+ order.setRemark(dto.getRemark());
|
|
|
+ order.setCreatorId(creatorId);
|
|
|
+ order.setCreatorName(creatorName);
|
|
|
+ order.setCreateTime(LocalDateTime.now());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ int totalQuantity = 0;
|
|
|
+ BigDecimal totalAmount = BigDecimal.ZERO;
|
|
|
+ List<ReplenishmentOrderItem> items = new ArrayList<>();
|
|
|
+
|
|
|
+ for (ReplenishmentOrderCreateDTO.ReplenishmentOrderItemDTO itemDto : dto.getItems()) {
|
|
|
+ ReplenishmentOrderItem item = new ReplenishmentOrderItem();
|
|
|
+ item.setDeviceId(dto.getDeviceId());
|
|
|
+ item.setProductId(itemDto.getProductId());
|
|
|
+ item.setProductCode(itemDto.getProductCode());
|
|
|
+ item.setProductName(itemDto.getProductName());
|
|
|
+ item.setPlannedQuantity(itemDto.getPlannedQuantity());
|
|
|
+ item.setUnitPrice(itemDto.getUnitPrice());
|
|
|
+ item.setShelfNum(itemDto.getShelfNum());
|
|
|
+ item.setPosition(itemDto.getPosition());
|
|
|
+
|
|
|
+ if (itemDto.getUnitPrice() != null) {
|
|
|
+ BigDecimal itemTotal = itemDto.getUnitPrice()
|
|
|
+ .multiply(BigDecimal.valueOf(itemDto.getPlannedQuantity()));
|
|
|
+ item.setTotalPrice(itemTotal);
|
|
|
+ totalAmount = totalAmount.add(itemTotal);
|
|
|
+ }
|
|
|
+ totalQuantity += itemDto.getPlannedQuantity();
|
|
|
+ items.add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setTotalQuantity(totalQuantity);
|
|
|
+ order.setTotalAmount(totalAmount);
|
|
|
+
|
|
|
+ save(order);
|
|
|
+
|
|
|
+ for (ReplenishmentOrderItem item : items) {
|
|
|
+ item.setOrderId(order.getId());
|
|
|
+ item.setCreateTime(LocalDateTime.now());
|
|
|
+ orderItemMapper.insert(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("创建补货单: orderNo={}, deviceId={}, items={}", order.getOrderNo(), dto.getDeviceId(), items.size());
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ order.setItemCount(items.size());
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder updateOrder(ReplenishmentOrderUpdateDTO dto) {
|
|
|
+ ReplenishmentOrder order = getById(dto.getId());
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ if (order.getStatus() != ReplenishmentOrderStatusEnum.DRAFT.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅草稿状态的补货单可编辑");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dto.getSupplierName() != null) {
|
|
|
+ order.setSupplierName(dto.getSupplierName());
|
|
|
+ }
|
|
|
+ if (dto.getWarehouseName() != null) {
|
|
|
+ order.setWarehouseName(dto.getWarehouseName());
|
|
|
+ }
|
|
|
+ if (dto.getExpectedArrivalTime() != null) {
|
|
|
+ order.setExpectedArrivalTime(dto.getExpectedArrivalTime());
|
|
|
+ }
|
|
|
+ if (dto.getRemark() != null) {
|
|
|
+ order.setRemark(dto.getRemark());
|
|
|
+ }
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ if (dto.getItems() != null && !dto.getItems().isEmpty()) {
|
|
|
+ orderItemMapper.deleteByOrderId(dto.getId());
|
|
|
+
|
|
|
+ int totalQuantity = 0;
|
|
|
+ BigDecimal totalAmount = BigDecimal.ZERO;
|
|
|
+
|
|
|
+ for (ReplenishmentOrderCreateDTO.ReplenishmentOrderItemDTO itemDto : dto.getItems()) {
|
|
|
+ ReplenishmentOrderItem item = new ReplenishmentOrderItem();
|
|
|
+ item.setOrderId(dto.getId());
|
|
|
+ item.setDeviceId(order.getDeviceId());
|
|
|
+ item.setProductId(itemDto.getProductId());
|
|
|
+ item.setProductCode(itemDto.getProductCode());
|
|
|
+ item.setProductName(itemDto.getProductName());
|
|
|
+ item.setPlannedQuantity(itemDto.getPlannedQuantity());
|
|
|
+ item.setUnitPrice(itemDto.getUnitPrice());
|
|
|
+ item.setShelfNum(itemDto.getShelfNum());
|
|
|
+ item.setPosition(itemDto.getPosition());
|
|
|
+
|
|
|
+ if (itemDto.getUnitPrice() != null) {
|
|
|
+ BigDecimal itemTotal = itemDto.getUnitPrice()
|
|
|
+ .multiply(BigDecimal.valueOf(itemDto.getPlannedQuantity()));
|
|
|
+ item.setTotalPrice(itemTotal);
|
|
|
+ totalAmount = totalAmount.add(itemTotal);
|
|
|
+ }
|
|
|
+ totalQuantity += itemDto.getPlannedQuantity();
|
|
|
+ item.setCreateTime(LocalDateTime.now());
|
|
|
+ orderItemMapper.insert(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setTotalQuantity(totalQuantity);
|
|
|
+ order.setTotalAmount(totalAmount);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateById(order);
|
|
|
+
|
|
|
+ log.info("更新补货单: id={}, orderNo={}", dto.getId(), order.getOrderNo());
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void deleteOrder(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ if (order.getStatus() != ReplenishmentOrderStatusEnum.DRAFT.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅草稿状态的补货单可删除");
|
|
|
+ }
|
|
|
+
|
|
|
+ orderItemMapper.deleteByOrderId(id);
|
|
|
+ removeById(id);
|
|
|
+
|
|
|
+ log.info("删除补货单: id={}, orderNo={}", id, order.getOrderNo());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder submitOrder(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ if (order.getStatus() != ReplenishmentOrderStatusEnum.DRAFT.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅草稿状态的补货单可提交");
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setStatus(ReplenishmentOrderStatusEnum.SUBMITTED.getCode());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+ updateById(order);
|
|
|
+
|
|
|
+ log.info("提交补货单: id={}, orderNo={}", id, order.getOrderNo());
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder syncToErp(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ if (order.getStatus() != ReplenishmentOrderStatusEnum.SUBMITTED.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅已提交状态的补货单可同步到ERP");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (qdbClient == null) {
|
|
|
+ throw new BusinessException(500, "ERP客户端未配置,无法同步");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ReplenishmentOrderItem> items = orderItemMapper.selectByOrderId(id);
|
|
|
+ if (items.isEmpty()) {
|
|
|
+ throw new BusinessException(400, "补货单明细为空,无法同步");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建订单商品列表
|
|
|
+ List<OrderGoods> goodsList = items.stream().map(item -> {
|
|
|
+ BigDecimal price = item.getUnitPrice() != null ? item.getUnitPrice() : BigDecimal.ZERO;
|
|
|
+ BigDecimal qty = BigDecimal.valueOf(item.getPlannedQuantity());
|
|
|
+ return OrderGoods.builder()
|
|
|
+ .platformGoodsCode(item.getProductCode() != null ? item.getProductCode() : "")
|
|
|
+ .platformGoodsName(item.getProductName() != null ? item.getProductName() : "")
|
|
|
+ .quantity(qty)
|
|
|
+ .price(price)
|
|
|
+ .realPrice(price)
|
|
|
+ .build();
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 构建订单上传请求
|
|
|
+ // TODO: shopId 需要映射为 ERP 系统中的店铺ID,当前暂用设备关联的门店ID
|
|
|
+ // TODO: receiver* 收件人信息需从设备/门店地址中获取,当前暂不填充
|
|
|
+ // TODO: warehouseName 需确认与 ERP 仓库名称一致
|
|
|
+ OrderSaveRequest request = OrderSaveRequest.builder()
|
|
|
+ .shopId(order.getShopId())
|
|
|
+ .tradeNo(order.getOrderNo())
|
|
|
+ .platformTradeStatus("02")
|
|
|
+ .platformRefundStatus("0")
|
|
|
+ .orderTypeCode("3")
|
|
|
+ .postAmount(BigDecimal.ZERO)
|
|
|
+ .payAmount(order.getTotalAmount() != null ? order.getTotalAmount() : BigDecimal.ZERO)
|
|
|
+ // TODO: 从设备/门店信息中获取收件人地址
|
|
|
+ // .receiverProvince(null)
|
|
|
+ // .receiverCity(null)
|
|
|
+ // .receiverDistrict(null)
|
|
|
+ // .receiverAddress(null)
|
|
|
+ // .receiverName(null)
|
|
|
+ // .receiverMobile(null)
|
|
|
+ .ordersGoods(goodsList)
|
|
|
+ .buyerMessage(order.getRemark())
|
|
|
+ .sellerMessage("设备补货单: " + order.getDeviceId())
|
|
|
+ // TODO: 确认仓库名称与 ERP 一致
|
|
|
+ .warehouseName(order.getWarehouseName())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ try {
|
|
|
+ qdbClient.getOrderApi().saveOrder(request);
|
|
|
+
|
|
|
+ order.setStatus(ReplenishmentOrderStatusEnum.SYNCED_TO_ERP.getCode());
|
|
|
+ order.setErpSyncTime(LocalDateTime.now());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+ updateById(order);
|
|
|
+
|
|
|
+ log.info("补货单同步ERP成功: id={}, orderNo={}", id, order.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("补货单同步ERP失败: id={}, orderNo={}, error={}", id, order.getOrderNo(), e.getMessage());
|
|
|
+ throw new BusinessException(500, "ERP同步失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder completeOrder(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ if (order.getStatus() != ReplenishmentOrderStatusEnum.SYNCED_TO_ERP.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅已同步ERP的补货单可完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setStatus(ReplenishmentOrderStatusEnum.COMPLETED.getCode());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+ updateById(order);
|
|
|
+
|
|
|
+ log.info("完成补货单: id={}, orderNo={}", id, order.getOrderNo());
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public ReplenishmentOrder cancelOrder(Long id) {
|
|
|
+ ReplenishmentOrder order = getById(id);
|
|
|
+ if (order == null) {
|
|
|
+ throw new BusinessException(404, "补货单不存在");
|
|
|
+ }
|
|
|
+ int status = order.getStatus();
|
|
|
+ if (status != ReplenishmentOrderStatusEnum.DRAFT.getCode()
|
|
|
+ && status != ReplenishmentOrderStatusEnum.SUBMITTED.getCode()) {
|
|
|
+ throw new BusinessException(400, "仅草稿或已提交状态的补货单可取消");
|
|
|
+ }
|
|
|
+
|
|
|
+ order.setStatus(ReplenishmentOrderStatusEnum.CANCELLED.getCode());
|
|
|
+ order.setUpdateTime(LocalDateTime.now());
|
|
|
+ updateById(order);
|
|
|
+
|
|
|
+ log.info("取消补货单: id={}, orderNo={}", id, order.getOrderNo());
|
|
|
+
|
|
|
+ fillStatusLabel(order);
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成补货单号:RO + yyyyMMdd + 4位序号
|
|
|
+ */
|
|
|
+ private String generateOrderNo() {
|
|
|
+ String datePrefix = "RO" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
|
|
|
+ String maxNo = orderMapper.selectMaxOrderNoToday();
|
|
|
+ int seq = 1;
|
|
|
+ if (maxNo != null && maxNo.startsWith(datePrefix)) {
|
|
|
+ try {
|
|
|
+ seq = Integer.parseInt(maxNo.substring(maxNo.length() - 4)) + 1;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("解析补货单号失败: {}", maxNo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return datePrefix + String.format("%04d", seq);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void fillStatusLabel(ReplenishmentOrder order) {
|
|
|
+ if (order.getStatus() != null) {
|
|
|
+ var statusEnum = ReplenishmentOrderStatusEnum.getLabelByCode(order.getStatus());
|
|
|
+ order.setStatusLabel(statusEnum.getLabel());
|
|
|
+ order.setStatusColor(statusEnum.getColor());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|