feat:增加换货单的逻辑,已发货短信模板更改

This commit is contained in:
一休哥哥6666
2026-06-15 13:42:52 +08:00
parent dfef98814b
commit 2991dd88bb
28 changed files with 575 additions and 628 deletions

View File

@@ -16,7 +16,7 @@ public class SmsProperties
/** 配片订单分配至镜片厂(模板变量 code=订单号) */
private SceneConfig assignOrder = new SceneConfig();
/** 配片订单发货通知(模板变量 code、expressCompany、expressNo */
/** 配片订单发货通知(模板变量 code、expressCompany */
private SceneConfig shipOrder = new SceneConfig();
public String getSignName()

View File

@@ -199,32 +199,6 @@ public class JySysOrderController extends BaseController
rejectDTO.getOrderId(), rejectDTO.getReason().trim(), getUsername()));
}
@PreAuthorize("@ss.hasPermi('jysystem:order:edit')")
@Log(title = "拒绝换货申请订单完成", businessType = BusinessType.UPDATE)
@PutMapping("/exchangeRejectedComplete")
public AjaxResult exchangeRejectedComplete(@RequestBody OrderCloseDTO completeDTO)
{
if (completeDTO == null || completeDTO.getOrderId() == null)
{
return error("订单ID不能为空");
}
return toAjax(jySysOrderService.adminCompleteExchangeRejectedOrder(
completeDTO.getOrderId(), getUsername()));
}
@PreAuthorize("@ss.hasPermi('jysystem:order:edit')")
@Log(title = "批量完成拒绝换货申请订单", businessType = BusinessType.UPDATE)
@PutMapping("/exchangeRejectedCompleteAll")
public AjaxResult exchangeRejectedCompleteAll()
{
int rows = jySysOrderService.adminCompleteAllExchangeRejectedOrders(getUsername());
if (rows <= 0)
{
return success("没有需要更新的订单");
}
return success("已成功更新 " + rows + " 条订单");
}
@PreAuthorize("@ss.hasPermi('jysystem:order:edit')")
@Log(title = "订单状态变更", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")

View File

@@ -210,7 +210,7 @@ public class JySysOrderPortalController extends BaseController
{
return error("换货申请原因不能为空");
}
return toAjax(jySysOrderPortalService.exchangeApplyPortalOrder(
return success(jySysOrderPortalService.exchangeApplyPortalOrder(
exchangeDTO.getOrderId(), exchangeDTO.getReason().trim()));
}

View File

@@ -25,12 +25,6 @@ public class JySysOrderStatusLog
private BigDecimal logisticsFee;
/** 应收结算状态0未结算 1已结算 */
private String saleSettleStatus;
/** 应付结算状态0未结算 1已结算 */
private String purchaseSettleStatus;
private String remark;
private String operator;
@@ -98,26 +92,6 @@ public class JySysOrderStatusLog
this.logisticsFee = logisticsFee;
}
public String getSaleSettleStatus()
{
return saleSettleStatus;
}
public void setSaleSettleStatus(String saleSettleStatus)
{
this.saleSettleStatus = saleSettleStatus;
}
public String getPurchaseSettleStatus()
{
return purchaseSettleStatus;
}
public void setPurchaseSettleStatus(String purchaseSettleStatus)
{
this.purchaseSettleStatus = purchaseSettleStatus;
}
public String getRemark()
{
return remark;
@@ -158,8 +132,6 @@ public class JySysOrderStatusLog
.append("expressCompany", getExpressCompany())
.append("expressNo", getExpressNo())
.append("logisticsFee", getLogisticsFee())
.append("saleSettleStatus", getSaleSettleStatus())
.append("purchaseSettleStatus", getPurchaseSettleStatus())
.append("remark", getRemark())
.append("operator", getOperator())
.append("operateTime", getOperateTime())

View File

@@ -28,6 +28,8 @@ public interface JySysOrderMapper extends BaseMapper<JySysOrder>
int countByTenantId(@Param("tenantId") Long tenantId);
int countByOrderNo(@Param("orderNo") String orderNo);
List<JySysOrder> selectCompletedOrdersByYearMonth(@Param("settleYearMonth") String settleYearMonth);
int updatePurchaseSettleStatusByOrderIds(@Param("orderIds") List<Long> orderIds,

View File

@@ -17,10 +17,4 @@ public interface JySysOrderStatusLogMapper
List<JySysOrderStatusLog> selectUnsettledShipLogsByOrderIdAndType(@Param("orderId") Long orderId,
@Param("settlementType") String settlementType);
int updateSaleSettleStatusByIds(@Param("ids") List<Long> ids, @Param("settleStatus") String settleStatus);
int updatePurchaseSettleStatusByIds(@Param("ids") List<Long> ids, @Param("settleStatus") String settleStatus);
int resetShipLogSettleStatusByOrderIds(@Param("orderIds") List<Long> orderIds);
}

View File

@@ -46,7 +46,7 @@ public interface IJySysOrderPortalService
int completePortalOrder(Long orderId);
int exchangeApplyPortalOrder(Long orderId, String reason);
JySysOrder exchangeApplyPortalOrder(Long orderId, String reason);
int receivePortalOrder(Long orderId);

View File

@@ -52,7 +52,7 @@ public interface IJySysOrderService extends IService<JySysOrder>
int brandReceiveExchangeRejectedOrder(Long orderId, Long brandTenantId, String operator);
int brandExchangeApplyOrder(Long orderId, Long brandTenantId, String reason, String operator);
JySysOrder brandExchangeApplyOrder(Long orderId, Long brandTenantId, String reason, String operator);
int adminRejectOrder(Long orderId, String reason, String operator);
@@ -64,10 +64,6 @@ public interface IJySysOrderService extends IService<JySysOrder>
int adminRejectExchangeOrder(Long orderId, String reason, String operator);
int adminCompleteExchangeRejectedOrder(Long orderId, String operator);
int adminCompleteAllExchangeRejectedOrders(String operator);
int brandResubmitOrder(Long orderId, Long brandTenantId, String reason, String operator);
int changeOrderStatus(Long orderId, String orderStatus, String operator);

View File

@@ -19,7 +19,6 @@ public interface SmsNotificationService
* @param phone 手机号(品牌方租户手机号,或收货人手机号)
* @param orderNo 订单号,对应模板变量 code
* @param expressCompany 快递公司,对应模板变量 expressCompany
* @param expressNo 快递单号,对应模板变量 expressNo
*/
void trySendOrderShippedNotify(String phone, String orderNo, String expressCompany, String expressNo);
void trySendOrderShippedNotify(String phone, String orderNo, String expressCompany);
}

View File

@@ -37,6 +37,14 @@ import com.ruoyi.jysystem.vo.FeeSettlementDetailVO;
@Service
public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
{
private static final String ORDER_STATUS_EXCHANGE_APPLY = "12";
private static final String ORDER_STATUS_EXCHANGE_AGREED = "13";
private static final String ORDER_STATUS_EXCHANGE_LOGISTICS = "15";
private static final String ORDER_STATUS_EXCHANGE_COMPLETED = "17";
@Resource
private JySysFeeSettlementMapper feeSettlementMapper;
@@ -130,10 +138,6 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
{
throw new ServiceException("该年月没有已完成状态的订单,无法生成结算单");
}
if (forceRegenerate && exists)
{
resetShipLogSettleStatusForOrders(orders);
}
Map<Long, List<JySysOrder>> brandGroups = new LinkedHashMap<>();
Map<Long, List<JySysOrder>> factoryGroups = new LinkedHashMap<>();
@@ -210,27 +214,9 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
update.setUpdateTime(DateUtils.getNowDate());
int rows = feeSettlementMapper.updateSettleStatus(update);
updateOrderSettleStatusBySettlement(settlement, operator);
updateStatusLogSettleStatusBySettlement(settlement);
return rows;
}
private void updateStatusLogSettleStatusBySettlement(JySysFeeSettlement settlement)
{
List<Long> statusLogIds = collectStatusLogIdsBySettlement(settlement.getId());
if (statusLogIds.isEmpty())
{
return;
}
if (JySysFeeSettlement.TYPE_AP.equals(settlement.getSettlementType()))
{
jySysOrderStatusLogMapper.updatePurchaseSettleStatusByIds(statusLogIds, "1");
}
else if (JySysFeeSettlement.TYPE_AR.equals(settlement.getSettlementType()))
{
jySysOrderStatusLogMapper.updateSaleSettleStatusByIds(statusLogIds, "1");
}
}
private void updateOrderSettleStatusBySettlement(JySysFeeSettlement settlement, String operator)
{
List<JySysFeeSettlementItem> items = feeSettlementItemMapper.selectBySettlementId(settlement.getId());
@@ -314,10 +300,13 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
private JySysFeeSettlementItem buildSettlementItem(Long settlementId, String type, JySysOrder order)
{
BigDecimal lensPrice = JySysFeeSettlement.TYPE_AR.equals(type)
? defaultAmount(order.getPriceC()) : defaultAmount(order.getPriceB());
BigDecimal progressivePrice = Integer.valueOf(1).equals(order.getProgressiveMultifocal())
? defaultAmount(order.getProgressiveMultifocalPrice()) : BigDecimal.ZERO;
boolean exchangeOrder = isExchangeOrder(order);
BigDecimal lensPrice = exchangeOrder ? BigDecimal.ZERO
: (JySysFeeSettlement.TYPE_AR.equals(type)
? defaultAmount(order.getPriceC()) : defaultAmount(order.getPriceB()));
BigDecimal progressivePrice = exchangeOrder ? BigDecimal.ZERO
: (Integer.valueOf(1).equals(order.getProgressiveMultifocal())
? defaultAmount(order.getProgressiveMultifocalPrice()) : BigDecimal.ZERO);
List<JySysOrderStatusLog> shipLogs = jySysOrderStatusLogMapper
.selectUnsettledShipLogsByOrderIdAndType(order.getId(), type);
@@ -374,7 +363,6 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
{
continue;
}
revertLogSettleStatusBySettlement(settlement);
if ("1".equals(settlement.getSettleStatus()))
{
revertOrderSettleStatusBySettlement(settlement);
@@ -392,24 +380,6 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
}
}
/** 删除结算单时回滚关联发货日志的结算状态,便于重新生成时再次纳入物流费用 */
private void revertLogSettleStatusBySettlement(JySysFeeSettlement settlement)
{
List<Long> statusLogIds = collectStatusLogIdsBySettlement(settlement.getId());
if (statusLogIds.isEmpty())
{
return;
}
if (JySysFeeSettlement.TYPE_AP.equals(settlement.getSettlementType()))
{
jySysOrderStatusLogMapper.updatePurchaseSettleStatusByIds(statusLogIds, "0");
}
else if (JySysFeeSettlement.TYPE_AR.equals(settlement.getSettlementType()))
{
jySysOrderStatusLogMapper.updateSaleSettleStatusByIds(statusLogIds, "0");
}
}
private void revertOrderSettleStatusBySettlement(JySysFeeSettlement settlement)
{
List<JySysFeeSettlementItem> items = feeSettlementItemMapper.selectBySettlementId(settlement.getId());
@@ -433,38 +403,6 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
}
}
private void resetShipLogSettleStatusForOrders(List<JySysOrder> orders)
{
if (orders == null || orders.isEmpty())
{
return;
}
List<Long> orderIds = orders.stream()
.map(JySysOrder::getId)
.filter(orderId -> orderId != null)
.distinct()
.collect(Collectors.toList());
if (!orderIds.isEmpty())
{
jySysOrderStatusLogMapper.resetShipLogSettleStatusByOrderIds(orderIds);
}
}
private List<Long> collectStatusLogIdsBySettlement(Long settlementId)
{
List<JySysFeeSettlementItem> items = feeSettlementItemMapper.selectBySettlementId(settlementId);
if (items == null || items.isEmpty())
{
return new ArrayList<>();
}
List<Long> itemIds = items.stream().map(JySysFeeSettlementItem::getId).collect(Collectors.toList());
return feeSettlementLogisticsMapper.selectBySettlementItemIds(itemIds).stream()
.map(JySysFeeSettlementLogistics::getStatusLogId)
.filter(statusLogId -> statusLogId != null)
.distinct()
.collect(Collectors.toList());
}
private String nextSettlementNo(String type)
{
String datePart = new SimpleDateFormat("yyyyMMdd").format(new Date());
@@ -496,6 +434,23 @@ public class JySysFeeSettlementServiceImpl implements IJySysFeeSettlementService
return value != null ? value : BigDecimal.ZERO;
}
private boolean isExchangeOrder(JySysOrder order)
{
if (order == null)
{
return false;
}
if (StringUtils.isNotEmpty(order.getOrderNo()) && order.getOrderNo().startsWith("HH"))
{
return true;
}
String status = order.getOrderStatus();
return ORDER_STATUS_EXCHANGE_APPLY.equals(status)
|| ORDER_STATUS_EXCHANGE_AGREED.equals(status)
|| ORDER_STATUS_EXCHANGE_LOGISTICS.equals(status)
|| ORDER_STATUS_EXCHANGE_COMPLETED.equals(status);
}
@Override
public byte[] exportPdf(Long id)
{

View File

@@ -229,7 +229,7 @@ public class JySysOrderPortalServiceImpl implements IJySysOrderPortalService
}
@Override
public int exchangeApplyPortalOrder(Long orderId, String reason)
public JySysOrder exchangeApplyPortalOrder(Long orderId, String reason)
{
JySysTenant tenant = requireBrandTenant();
return jySysOrderService.brandExchangeApplyOrder(

View File

@@ -59,6 +59,10 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
private static final String ORDER_STATUS_EXCHANGE_AGREED = "13";
private static final String ORDER_STATUS_EXCHANGE_LOGISTICS = "15";
private static final String ORDER_STATUS_EXCHANGE_COMPLETED = "17";
private static final String ORDER_STATUS_EXCHANGE_REJECTED = "21";
private static final String ORDER_STATUS_CANCELLED = "18";
@@ -347,7 +351,7 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
throw new ServiceException("物流费用不能小于0");
}
saveStatusLog(orderId, ORDER_STATUS_SHIPPED, operator, expressCompany, expressNo, null, logisticsFee);
saveStatusLog(orderId, resolveShipStatus(order), operator, expressCompany, expressNo, null, logisticsFee);
BigDecimal totalLogisticsFee = jySysOrderStatusLogMapper.sumLogisticsFeeByOrderId(orderId);
if (totalLogisticsFee == null)
{
@@ -362,7 +366,7 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(totalLogisticsFee);
update.setOrderStatus(ORDER_STATUS_SHIPPED);
update.setOrderStatus(resolveShipStatus(order));
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
@@ -383,12 +387,13 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(order.getLogisticsFee());
update.setOrderStatus(ORDER_STATUS_COMPLETED);
String completeStatus = resolveCompleteStatus(order);
update.setOrderStatus(completeStatus);
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
int rows = jySysOrderMapper.updateJySysOrder(update);
saveStatusLog(orderId, ORDER_STATUS_COMPLETED, operator, null, null, null);
saveStatusLog(orderId, completeStatus, operator, null, null, null);
return rows;
}
@@ -406,12 +411,13 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(order.getLogisticsFee());
update.setOrderStatus(ORDER_STATUS_COMPLETED);
String completeStatus = resolveCompleteStatus(order);
update.setOrderStatus(completeStatus);
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
int rows = jySysOrderMapper.updateJySysOrder(update);
saveStatusLog(orderId, ORDER_STATUS_COMPLETED, operator, null, null, null);
saveStatusLog(orderId, completeStatus, operator, null, null, null);
return rows;
}
@@ -440,29 +446,19 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
@Override
@Transactional(rollbackFor = Exception.class)
public int brandExchangeApplyOrder(Long orderId, Long brandTenantId, String reason, String operator)
public JySysOrder brandExchangeApplyOrder(Long orderId, Long brandTenantId, String reason, String operator)
{
JySysOrder order = requireBrandExchangeAppliableOrder(orderId, brandTenantId);
requireBrandExchangeAppliableOrder(orderId, brandTenantId);
if (StringUtils.isEmpty(reason))
{
throw new ServiceException("换货申请原因不能为空");
}
JySysOrder update = new JySysOrder();
update.setId(orderId);
update.setBrandTenantId(order.getBrandTenantId());
update.setLensId(order.getLensId());
update.setProductId(order.getProductId());
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(order.getLogisticsFee());
update.setOrderStatus(ORDER_STATUS_EXCHANGE_APPLY);
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
int rows = jySysOrderMapper.updateJySysOrder(update);
saveStatusLog(orderId, ORDER_STATUS_EXCHANGE_APPLY, operator, null, null, reason);
return rows;
JySysOrder source = selectJySysOrderById(orderId);
if (source == null)
{
throw new ServiceException("订单不存在");
}
return copyOrderForExchangeApply(source, reason, operator);
}
@Override
@@ -557,11 +553,13 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
{
throw new ServiceException("订单不存在");
}
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus()))
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus())
&& !ORDER_STATUS_EXCHANGE_LOGISTICS.equals(order.getOrderStatus()))
{
throw new ServiceException("仅已发货状态的订单可完成");
throw new ServiceException("仅已发货或换货物流状态的订单可完成");
}
String completeStatus = resolveCompleteStatus(order);
JySysOrder update = new JySysOrder();
update.setId(orderId);
update.setBrandTenantId(order.getBrandTenantId());
@@ -570,12 +568,12 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(order.getLogisticsFee());
update.setOrderStatus(ORDER_STATUS_COMPLETED);
update.setOrderStatus(completeStatus);
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
int rows = jySysOrderMapper.updateJySysOrder(update);
saveStatusLog(orderId, ORDER_STATUS_COMPLETED, operator, null, null, null);
saveStatusLog(orderId, completeStatus, operator, null, null, null);
return rows;
}
@@ -629,33 +627,6 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
return rows;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int adminCompleteExchangeRejectedOrder(Long orderId, String operator)
{
return completeExchangeRejectedToCompleted(orderId, operator);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int adminCompleteAllExchangeRejectedOrders(String operator)
{
JySysOrder query = new JySysOrder();
query.setOrderStatus(ORDER_STATUS_EXCHANGE_REJECTED);
List<JySysOrder> orderList = jySysOrderMapper.selectJySysOrderList(query);
if (orderList == null || orderList.isEmpty())
{
return 0;
}
int count = 0;
for (JySysOrder order : orderList)
{
completeExchangeRejectedToCompleted(order.getId(), operator);
count++;
}
return count;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int brandResubmitOrder(Long orderId, Long brandTenantId, String reason, String operator)
@@ -801,9 +772,10 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
private JySysOrder requireFactoryCompletableOrder(Long orderId, Long lensFactoryTenantId)
{
JySysOrder order = requireFactoryOwnedOrder(orderId, lensFactoryTenantId);
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus()))
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus())
&& !ORDER_STATUS_EXCHANGE_LOGISTICS.equals(order.getOrderStatus()))
{
throw new ServiceException("仅已发货状态的订单可完成");
throw new ServiceException("仅已发货或换货物流状态的订单可完成");
}
return order;
}
@@ -827,9 +799,10 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
{
throw new ServiceException("无权操作该订单");
}
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus()))
if (!ORDER_STATUS_SHIPPED.equals(order.getOrderStatus())
&& !ORDER_STATUS_EXCHANGE_LOGISTICS.equals(order.getOrderStatus()))
{
throw new ServiceException("仅已发货状态的订单可完成");
throw new ServiceException("仅已发货或换货物流状态的订单可完成");
}
return order;
}
@@ -880,46 +853,14 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
throw new ServiceException("无权操作该订单");
}
if (!ORDER_STATUS_COMPLETED.equals(order.getOrderStatus())
&& !ORDER_STATUS_EXCHANGE_COMPLETED.equals(order.getOrderStatus())
&& !ORDER_STATUS_EXCHANGE_REJECTED.equals(order.getOrderStatus()))
{
throw new ServiceException("仅已完成或拒绝换货申请状态的订单可申请换货");
throw new ServiceException("仅已完成、换货完成或拒绝换货申请状态的订单可申请换货");
}
return order;
}
private int completeExchangeRejectedToCompleted(Long orderId, String operator)
{
if (orderId == null)
{
throw new ServiceException("订单ID不能为空");
}
JySysOrder order = jySysOrderMapper.selectJySysOrderById(orderId);
if (order == null)
{
throw new ServiceException("订单不存在");
}
if (!ORDER_STATUS_EXCHANGE_REJECTED.equals(order.getOrderStatus()))
{
throw new ServiceException("仅拒绝换货申请状态的订单可完成");
}
JySysOrder update = new JySysOrder();
update.setId(orderId);
update.setBrandTenantId(order.getBrandTenantId());
update.setLensId(order.getLensId());
update.setProductId(order.getProductId());
update.setAttachment(order.getAttachment());
update.setLensFactoryTenantId(order.getLensFactoryTenantId());
update.setLogisticsFee(order.getLogisticsFee());
update.setOrderStatus(ORDER_STATUS_COMPLETED);
update.setUpdateBy(operator);
update.setUpdateTime(DateUtils.getNowDate());
int rows = jySysOrderMapper.updateJySysOrder(update);
saveStatusLog(orderId, ORDER_STATUS_COMPLETED, operator, null, null, null);
return rows;
}
private JySysOrder requireAdminExchangeAppliableOrder(Long orderId)
{
if (orderId == null)
@@ -1126,6 +1067,45 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
}
}
private String resolveShipStatus(JySysOrder order)
{
if (order != null && isExchangeOrder(order.getId()))
{
return ORDER_STATUS_EXCHANGE_LOGISTICS;
}
return ORDER_STATUS_SHIPPED;
}
private String resolveCompleteStatus(JySysOrder order)
{
if (order != null && ORDER_STATUS_EXCHANGE_LOGISTICS.equals(order.getOrderStatus()))
{
return ORDER_STATUS_EXCHANGE_COMPLETED;
}
return ORDER_STATUS_COMPLETED;
}
private boolean isExchangeOrder(Long orderId)
{
if (orderId == null)
{
return false;
}
List<JySysOrderStatusLog> logs = jySysOrderStatusLogMapper.selectJySysOrderStatusLogByOrderId(orderId);
if (logs == null || logs.isEmpty())
{
return false;
}
for (JySysOrderStatusLog log : logs)
{
if (log != null && ORDER_STATUS_EXCHANGE_APPLY.equals(log.getOrderStatus()))
{
return true;
}
}
return false;
}
private void saveOptometryList(JySysOrder jySysOrder)
{
if (jySysOrder.getId() == null)
@@ -1200,12 +1180,6 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
statusLog.setExpressCompany(expressCompany);
statusLog.setExpressNo(expressNo);
statusLog.setLogisticsFee(logisticsFee);
if (ORDER_STATUS_SHIPPED.equals(orderStatus)
&& StringUtils.isNotEmpty(expressNo))
{
statusLog.setSaleSettleStatus("0");
statusLog.setPurchaseSettleStatus("0");
}
statusLog.setRemark(remark);
statusLog.setOperator(operator);
statusLog.setOperateTime(DateUtils.getNowDate());
@@ -1219,4 +1193,94 @@ public class JySysOrderServiceImpl extends ServiceImpl<JySysOrderMapper, JySysOr
int randomPart = ThreadLocalRandom.current().nextInt(1000, 10000);
return "JY" + timePart + randomPart;
}
private JySysOrder copyOrderForExchangeApply(JySysOrder source, String reason, String operator)
{
Date now = DateUtils.getNowDate();
JySysOrder newOrder = new JySysOrder();
newOrder.setOrderNo(buildExchangeOrderNo(source.getOrderNo()));
newOrder.setBrandTenantId(source.getBrandTenantId());
newOrder.setLensFactoryTenantId(source.getLensFactoryTenantId());
newOrder.setProductId(source.getProductId());
newOrder.setLensId(source.getLensId());
newOrder.setPriceB(source.getPriceB());
newOrder.setPriceC(source.getPriceC());
newOrder.setLogisticsFee(BigDecimal.ZERO);
newOrder.setProgressiveMultifocal(source.getProgressiveMultifocal());
newOrder.setProgressiveMultifocalPrice(source.getProgressiveMultifocalPrice());
newOrder.setOrderStatus(ORDER_STATUS_EXCHANGE_APPLY);
newOrder.setPurchaseSettleStatus("0");
newOrder.setSaleSettleStatus("0");
newOrder.setReceiverName(source.getReceiverName());
newOrder.setReceiverPhone(source.getReceiverPhone());
newOrder.setReceiverAddress(source.getReceiverAddress());
newOrder.setAttachment(source.getAttachment());
newOrder.setRemark(source.getRemark());
newOrder.setDelFlag(0);
newOrder.setCreateBy(operator);
newOrder.setUpdateBy(operator);
newOrder.setCreateTime(now);
newOrder.setUpdateTime(now);
newOrder.setOptometryList(copyOptometryList(source.getOptometryList()));
fillDefaultValue(newOrder);
jySysOrderMapper.insertJySysOrder(newOrder);
saveOptometryList(newOrder);
saveStatusLog(newOrder.getId(), ORDER_STATUS_EXCHANGE_APPLY, operator, null, null, reason);
return selectJySysOrderById(newOrder.getId());
}
private List<JySysOrderOptometry> copyOptometryList(List<JySysOrderOptometry> sourceList)
{
List<JySysOrderOptometry> copiedList = new ArrayList<>();
if (sourceList == null || sourceList.isEmpty())
{
return copiedList;
}
for (JySysOrderOptometry source : sourceList)
{
if (source == null || StringUtils.isEmpty(source.getEyeSide()))
{
continue;
}
JySysOrderOptometry copy = new JySysOrderOptometry();
copy.setEyeSide(source.getEyeSide());
copy.setSphereS(source.getSphereS());
copy.setCylinderC(source.getCylinderC());
copy.setAxisA(source.getAxisA());
copy.setPd(source.getPd());
copy.setDualPd(source.getDualPd());
copy.setPdType(source.getPdType());
copy.setPupilHeight(source.getPupilHeight());
copy.setDistanceVa(source.getDistanceVa());
copy.setNearAdd(source.getNearAdd());
copiedList.add(copy);
}
return copiedList;
}
private String buildExchangeOrderNo(String sourceOrderNo)
{
String numericPart = StringUtils.isEmpty(sourceOrderNo)
? new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
+ ThreadLocalRandom.current().nextInt(1000, 10000)
: sourceOrderNo.replaceAll("\\D", "");
if (StringUtils.isEmpty(numericPart))
{
numericPart = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())
+ ThreadLocalRandom.current().nextInt(1000, 10000);
}
String orderNo = "HH" + numericPart;
int retry = 0;
while (jySysOrderMapper.countByOrderNo(orderNo) > 0 && retry < 10)
{
orderNo = "HH" + numericPart + ThreadLocalRandom.current().nextInt(1000, 10000);
retry++;
}
if (jySysOrderMapper.countByOrderNo(orderNo) > 0)
{
throw new ServiceException("生成换货订单号失败,请稍后重试");
}
return orderNo;
}
}

View File

@@ -2,6 +2,8 @@ package com.ruoyi.jysystem.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
@@ -23,12 +25,16 @@ import com.ruoyi.jysystem.utils.TenantTypeHelper;
@Service
public class JySysTenantMessageServiceImpl implements IJySysTenantMessageService
{
private static final Logger log = LoggerFactory.getLogger(JySysTenantMessageServiceImpl.class);
private static final String ORDER_STATUS_DRAFT = "1";
private static final String ORDER_STATUS_ASSIGNED = "3";
private static final String ORDER_STATUS_SHIPPED = "8";
private static final String ORDER_STATUS_EXCHANGE_LOGISTICS = "15";
@Resource
private JySysTenantMessageMapper jySysTenantMessageMapper;
@@ -102,18 +108,30 @@ public class JySysTenantMessageServiceImpl implements IJySysTenantMessageService
}
insertMessage(order.getBrandTenantId(), order.getId(), orderNo,
JySysTenantMessage.TYPE_STATUS_CHANGE, "订单状态变更", content, orderStatus);
if (ORDER_STATUS_SHIPPED.equals(orderStatus))
if (ORDER_STATUS_SHIPPED.equals(orderStatus)
|| ORDER_STATUS_EXCHANGE_LOGISTICS.equals(orderStatus))
{
// 模板「您的配片单…」通知配片单所属品牌方;无租户手机号时回退收货人手机号
String smsPhone = StringUtils.isNotEmpty(brandTenant.getPhone())
? brandTenant.getPhone()
: order.getReceiverPhone();
log.info("[发货短信] 触发检查: orderId={}, orderNo={}, orderStatus={}, expressCompany={}, brandTenantId={}, brandPhone={}, receiverPhone={}",
orderId, orderNo, orderStatus, expressCompany, brandTenant.getId(), brandTenant.getPhone(), order.getReceiverPhone());
String smsPhone = resolveShipNotifyPhone(brandTenant.getPhone(), order.getReceiverPhone());
if (StringUtils.isNotEmpty(smsPhone))
{
smsNotificationService.trySendOrderShippedNotify(smsPhone, orderNo, expressCompany, expressNo);
log.info("[发货短信] 准备发送: orderNo={}, smsPhone={}, expressCompany={}", orderNo, smsPhone, expressCompany);
smsNotificationService.trySendOrderShippedNotify(
smsPhone, orderNo, expressCompany);
}
else
{
log.warn("[发货短信] 跳过: 无有效手机号, orderNo={}, brandPhone={}, receiverPhone={}",
orderNo, brandTenant.getPhone(), order.getReceiverPhone());
}
}
}
else
{
log.warn("[发货短信] 跳过: 品牌方租户无效, orderId={}, brandTenantId={}, tenantType={}",
orderId, order.getBrandTenantId(), brandTenant != null ? brandTenant.getTenantType() : null);
}
}
if (order.getLensFactoryTenantId() != null)
@@ -171,4 +189,41 @@ public class JySysTenantMessageServiceImpl implements IJySysTenantMessageService
}
return tenant;
}
private String resolveShipNotifyPhone(String brandPhone, String receiverPhone)
{
if (isValidMobile(brandPhone))
{
return normalizeMobile(brandPhone);
}
if (isValidMobile(receiverPhone))
{
return normalizeMobile(receiverPhone);
}
return null;
}
private boolean isValidMobile(String phone)
{
return normalizeMobile(phone) != null;
}
private String normalizeMobile(String phone)
{
if (StringUtils.isEmpty(phone))
{
return null;
}
String digits = phone.replaceAll("\\D", "");
if (digits.length() >= 11)
{
String mobile = digits.substring(digits.length() - 11);
if (mobile.matches("^1[3-9]\\d{9}$"))
{
return mobile;
}
}
String trimmed = phone.trim();
return trimmed.matches("^1[3-9]\\d{9}$") ? trimmed : null;
}
}

View File

@@ -35,56 +35,67 @@ public class SmsNotificationServiceImpl implements SmsNotificationService
}
@Override
public void trySendOrderShippedNotify(String phone, String orderNo, String expressCompany, String expressNo)
public void trySendOrderShippedNotify(String phone, String orderNo, String expressCompany)
{
Map<String, String> params = new HashMap<>(3);
Map<String, String> params = new HashMap<>(2);
params.put("code", nullToEmpty(orderNo));
params.put("expressCompany", nullToEmpty(expressCompany));
params.put("expressNo", nullToEmpty(expressNo));
trySendScene("配片订单发货", smsProperties.getShipOrder(), phone, params);
}
private void trySendScene(String sceneLabel, SmsProperties.SceneConfig scene, String phone, Map<String, String> templateParams)
{
if (scene == null || !scene.isEnabled())
if (scene == null)
{
log.warn("[发货短信] 跳过: {}场景配置为空", sceneLabel);
return;
}
if (!scene.isEnabled())
{
log.warn("[发货短信] 跳过: {}短信未启用, enabled=false", sceneLabel);
return;
}
if (StringUtils.isEmpty(phone))
{
log.warn("[发货短信] 跳过: {}手机号为空", sceneLabel);
return;
}
String templateCode = StringUtils.isNotEmpty(scene.getTemplate()) ? scene.getTemplate().trim() : null;
if (StringUtils.isEmpty(templateCode))
{
log.warn("未配置{}短信模板请在 application.yml 的 sms.{} 中填写 template",
log.warn("[发货短信] 跳过: 未配置{}短信模板, 请在 application.yml 的 sms.{} 中填写 template",
sceneLabel, sceneLabel.contains("分配") ? "assign-order" : "ship-order");
return;
}
String mobile = phone.trim();
if (!MOBILE_PATTERN.matcher(mobile).matches())
String mobile = normalizeMobile(phone);
if (mobile == null)
{
log.warn("{}短信手机号格式无效,跳过通知: {}", sceneLabel, phone);
log.warn("[发货短信] 跳过: {}手机号格式无效, rawPhone={}", sceneLabel, phone);
return;
}
String signName = smsProperties.getSignName();
String templateParam = JSON.toJSONString(templateParams);
log.info("[发货短信] 调用阿里云: scene={}, phone={}, signName={}, template={}, params={}",
sceneLabel, mobile, signName, templateCode, templateParam);
try
{
String templateParam = JSON.toJSONString(templateParams);
Map<String, Object> result = SmsUtils.sendNotifyMsg(mobile, smsProperties.getSignName(),
templateCode, templateParam);
Map<String, Object> result = SmsUtils.sendNotifyMsg(mobile, signName, templateCode, templateParam);
Integer statusCode = (Integer) result.get("statusCode");
String bodyCode = (String) result.get("bodyCode");
String bodyMessage = (String) result.get("bodyMessage");
String bizId = (String) result.get("bizId");
String requestId = (String) result.get("requestId");
if (statusCode == null || statusCode != 200 || !"OK".equalsIgnoreCase(bodyCode))
{
log.warn("{}短信发送失败: phone={}, template={}, params={}, statusCode={}, bodyCode={}",
sceneLabel, mobile, templateCode, templateParam, statusCode, bodyCode);
log.warn("[发货短信] 发送失败: scene={}, phone={}, template={}, params={}, statusCode={}, bodyCode={}, bodyMessage={}, bizId={}, requestId={}",
sceneLabel, mobile, templateCode, templateParam, statusCode, bodyCode, bodyMessage, bizId, requestId);
return;
}
log.info("{}短信已发送: {}", sceneLabel, mobile);
log.info("[发货短信] 发送成功: scene={}, phone={}, bizId={}, requestId={}", sceneLabel, mobile, bizId, requestId);
}
catch (Exception e)
{
log.warn("{}短信发送异常: phone={}, err={}", sceneLabel, mobile, e.getMessage());
log.warn("[发货短信] 发送异常: scene={}, phone={}, err={}", sceneLabel, mobile, e.getMessage(), e);
}
}
@@ -92,4 +103,22 @@ public class SmsNotificationServiceImpl implements SmsNotificationService
{
return value != null ? value : "";
}
private String normalizeMobile(String phone)
{
if (StringUtils.isEmpty(phone))
{
return null;
}
String digits = phone.replaceAll("\\D", "");
if (digits.length() >= 11)
{
String mobile = digits.substring(digits.length() - 11);
if (MOBILE_PATTERN.matcher(mobile).matches())
{
return mobile;
}
}
return MOBILE_PATTERN.matcher(phone.trim()).matches() ? phone.trim() : null;
}
}

View File

@@ -185,6 +185,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and (brand_tenant_id = #{tenantId} or lens_factory_tenant_id = #{tenantId})
</select>
<select id="countByOrderNo" resultType="int">
select count(1)
from jy_sys_order
where del_flag = '0'
and order_no = #{orderNo}
</select>
<select id="selectCompletedOrdersByYearMonth" resultMap="JySysOrderResult">
select o.id, o.order_no, o.brand_tenant_id, bt.name as brand_tenant_name,
o.lens_factory_tenant_id, lf.name as lens_factory_tenant_name,
@@ -197,11 +204,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join jy_sys_tenant bt on o.brand_tenant_id = bt.id
left join jy_sys_tenant lf on o.lens_factory_tenant_id = lf.id
where o.del_flag = '0'
and o.order_status = '9'
and o.order_status in ('9', '17')
and exists (
select 1 from jy_sys_order_status_log sl
where sl.order_id = o.id
and sl.order_status = '9'
and sl.order_status in ('9', '17')
and DATE_FORMAT(sl.operate_time, '%Y-%m') = #{settleYearMonth}
)
order by o.id asc

View File

@@ -11,8 +11,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="expressCompany" column="express_company" />
<result property="expressNo" column="express_no" />
<result property="logisticsFee" column="logistics_fee" />
<result property="saleSettleStatus" column="sale_settle_status" />
<result property="purchaseSettleStatus" column="purchase_settle_status" />
<result property="remark" column="remark" />
<result property="operator" column="operator" />
<result property="operateTime" column="operate_time" />
@@ -20,7 +18,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectJySysOrderStatusLogByOrderId" parameterType="Long" resultMap="JySysOrderStatusLogResult">
select id, order_id, order_status, express_company, express_no, logistics_fee,
sale_settle_status, purchase_settle_status, remark, operator, operate_time
remark, operator, operate_time
from jy_sys_order_status_log
where order_id = #{orderId}
order by operate_time desc, id desc
@@ -43,8 +41,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="expressCompany != null">express_company,</if>
<if test="expressNo != null">express_no,</if>
<if test="logisticsFee != null">logistics_fee,</if>
<if test="saleSettleStatus != null">sale_settle_status,</if>
<if test="purchaseSettleStatus != null">purchase_settle_status,</if>
<if test="remark != null">remark,</if>
<if test="operator != null">operator,</if>
<if test="operateTime != null">operate_time,</if>
@@ -55,8 +51,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="expressCompany != null">#{expressCompany},</if>
<if test="expressNo != null">#{expressNo},</if>
<if test="logisticsFee != null">#{logisticsFee},</if>
<if test="saleSettleStatus != null">#{saleSettleStatus},</if>
<if test="purchaseSettleStatus != null">#{purchaseSettleStatus},</if>
<if test="remark != null">#{remark},</if>
<if test="operator != null">#{operator},</if>
<if test="operateTime != null">#{operateTime},</if>
@@ -64,52 +58,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</insert>
<select id="selectUnsettledShipLogsByOrderIdAndType" resultMap="JySysOrderStatusLogResult">
select id, order_id, order_status, express_company, express_no, logistics_fee,
sale_settle_status, purchase_settle_status, remark, operator, operate_time
from jy_sys_order_status_log
where order_id = #{orderId}
and order_status = '8'
and express_no is not null
and TRIM(express_no) != ''
<choose>
<when test="settlementType == 'AR'">
and (sale_settle_status is null or sale_settle_status = '0')
</when>
<when test="settlementType == 'AP'">
and (purchase_settle_status is null or purchase_settle_status = '0')
</when>
</choose>
order by operate_time asc, id asc
select l.id, l.order_id, l.order_status, l.express_company, l.express_no, l.logistics_fee,
l.remark, l.operator, l.operate_time
from jy_sys_order_status_log l
where l.order_id = #{orderId}
and l.order_status in ('8', '15')
and l.express_no is not null
and TRIM(l.express_no) != ''
and l.id not in (
select fl.status_log_id
from jy_sys_fee_settlement_logistics fl
inner join jy_sys_fee_settlement_item fi on fl.settlement_item_id = fi.id
inner join jy_sys_fee_settlement fs on fi.settlement_id = fs.id
where fl.status_log_id is not null
and fs.settlement_type = #{settlementType}
)
order by l.operate_time asc, l.id asc
</select>
<update id="updateSaleSettleStatusByIds">
update jy_sys_order_status_log
set sale_settle_status = #{settleStatus}
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<update id="updatePurchaseSettleStatusByIds">
update jy_sys_order_status_log
set purchase_settle_status = #{settleStatus}
where id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<update id="resetShipLogSettleStatusByOrderIds">
update jy_sys_order_status_log
set sale_settle_status = '0',
purchase_settle_status = '0'
where order_id in
<foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
#{orderId}
</foreach>
and order_status = '8'
and express_no is not null
and TRIM(express_no) != ''
</update>
</mapper>

View File

@@ -51,7 +51,7 @@ INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value
SELECT 166, 14, '退货物流', '14', 'jy_order_status', NULL, 'primary', 'N', '0', 'admin', NOW(), NULL
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_dict_data` WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '14');
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`)
SELECT 167, 15, '换货已发货', '15', 'jy_order_status', NULL, 'primary', 'N', '0', 'admin', NOW(), NULL
SELECT 167, 15, '换货物流', '15', 'jy_order_status', NULL, 'primary', 'N', '0', 'admin', NOW(), NULL
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_dict_data` WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '15');
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`)
SELECT 168, 16, '退货完成', '16', 'jy_order_status', NULL, 'success', 'N', '0', 'admin', NOW(), NULL

View File

@@ -0,0 +1,16 @@
-- 订单状态字典:恢复/确保换货物流、换货完成标签
UPDATE `sys_dict_data`
SET `dict_label` = '换货物流'
WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '15';
UPDATE `sys_dict_data`
SET `dict_label` = '换货完成'
WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '17';
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`)
SELECT 167, 15, '换货物流', '15', 'jy_order_status', NULL, 'primary', 'N', '0', 'admin', NOW(), NULL
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_dict_data` WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '15');
INSERT INTO `sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`)
SELECT 169, 17, '换货完成', '17', 'jy_order_status', NULL, 'success', 'N', '0', 'admin', NOW(), NULL
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_dict_data` WHERE `dict_type` = 'jy_order_status' AND `dict_value` = '17');

View File

@@ -0,0 +1,4 @@
-- 订单状态日志:移除物流应收/应付结算状态字段(改由费用结算单物流明细关联发货日志)
ALTER TABLE `jy_sys_order_status_log`
DROP COLUMN `sale_settle_status`,
DROP COLUMN `purchase_settle_status`;