feat: 积分商城-基本实现拆单、确认收货

This commit is contained in:
2026-07-29 15:51:26 +08:00
parent 51a951d863
commit d613a41854
41 changed files with 2262 additions and 659 deletions
@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderPageParam;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderReceivedParam;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -43,6 +44,12 @@ public class PointsMallH5OrderController {
return Result.success(pointsMallOrderService.supplierOrderUrl(supplierId, orderId, orderType));
}
@At("/userOrderReceive")
@SaCheckPermission("h5.points.mall")
public Result userOrderReceive(PointsMallMobileOrderReceivedParam param) {
return Result.success(pointsMallOrderService.userOrderReceive(param, currentUserId()));
}
private String currentUserId() {
try {
return StpUtil.getLoginIdAsString();
@@ -9,6 +9,8 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.PointsMallBridgeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.adaptor.JsonAdaptor;
@@ -24,6 +26,7 @@ import org.nutz.mvc.annotation.POST;
@Ok("json:full")
@At("/open")
@AdaptBy(type = JsonAdaptor.class)
@Api(tags = "积分商城桥接接口", description = "供应商 token、订单、积分、发票、对账等桥接接口")
public class PointsMallBridgeController {
@Inject
@@ -31,6 +34,7 @@ public class PointsMallBridgeController {
@POST
@At("/connect/token")
@ApiOperation(value = "获取 token", notes = "供应商获取访问令牌")
public MallInboundResult getToken(@Param("..") MallTokenDTO dto) {
try {
return pointsMallBridgeService.getToken(dto);
@@ -41,6 +45,7 @@ public class PointsMallBridgeController {
@POST
@At("/connect/refreshToken")
@ApiOperation(value = "刷新 token", notes = "供应商刷新访问令牌")
public MallInboundResult refreshToken(@Param("..") MallTokenRefreshDTO dto) {
try {
return pointsMallBridgeService.refreshToken(dto);
@@ -53,6 +58,7 @@ public class PointsMallBridgeController {
@At("/pointOrder/queryPoints")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "积分查询", notes = "查询用户可用积分")
public MallInboundPointsResult queryPoints(@Param("..") MallPointQueryDTO dto) {
try {
return pointsMallBridgeService.queryPoints(dto);
@@ -65,6 +71,7 @@ public class PointsMallBridgeController {
@At("/pointOrder/pendingOrderInfo")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "接收待支付订单", notes = "供应商推送待支付主单")
public MallInboundResult receivePending(@Param("..") MallPendingOrderDTO dto) {
return inbound(() -> pointsMallBridgeService.receivePending(dto));
}
@@ -73,6 +80,7 @@ public class PointsMallBridgeController {
@At("/pointOrder/confirmPurchase")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "确认购买", notes = "支付完成后确认主单已购买")
public MallInboundResult confirmPurchase(@Param("..") MallPurchaseConfirmDTO dto) {
return inbound(() -> pointsMallBridgeService.confirmPurchase(dto));
}
@@ -81,6 +89,7 @@ public class PointsMallBridgeController {
@At("/pointOrder/purchaseResult")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "支付结果通知", notes = "接收供应商支付结果")
public MallInboundResult purchaseResult(@Param("..") MallPurchaseResultDTO dto) {
return inbound(() -> pointsMallBridgeService.purchaseResult(dto));
}
@@ -89,12 +98,14 @@ public class PointsMallBridgeController {
@At("/pointOrder/pushOrderInfoMsg")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "订单事件推送", notes = "接收取消、拆单、退换货、完成、发票等订单事件")
public MallInboundResult receiveEvent(@Param("..") MallOrderEventDTO dto) {
return inbound(() -> pointsMallBridgeService.receiveEvent(dto));
}
@POST
@At("/pointOrder/queryOrderInfo")
@ApiOperation(value = "查询订单详情", notes = "按主单或子单查询订单信息")
public MallOutboundResult queryOrderInfo(@Param("..") MallOrderQueryDTO dto) {
try {
return pointsMallBridgeService.queryOrderInfo(dto);
@@ -105,6 +116,7 @@ public class PointsMallBridgeController {
@POST
@At("/pointOrder/applyForInvoices")
@ApiOperation(value = "发票开具申请", notes = "接收供应商开票申请")
public MallOutboundResult applyInvoice(@Param("..") MallInvoiceApplyDTO dto) {
try {
return pointsMallBridgeService.applyInvoice(dto);
@@ -115,6 +127,7 @@ public class PointsMallBridgeController {
@POST
@At("/pointOrder/queryInvoicesInfo")
@ApiOperation(value = "查询发票信息", notes = "查询开票结果和发票明细")
public Result queryInvoicesInfo(@Param("..") QueryInvoiceInfoDTO dto) {
try {
return Result.success(pointsMallBridgeService.queryInvoicesInfo(dto));
@@ -125,6 +138,7 @@ public class PointsMallBridgeController {
@POST
@At("/pointOrder/queryFileBase64Info")
@ApiOperation(value = "查询图片 Base64", notes = "查询订单商品图片或发票图片")
public MallPictureOutboundResult queryFileBase64Info(@Param("..") PictureQueryDTO dto) {
try {
return pointsMallBridgeService.queryFileBase64Info(dto);
@@ -135,12 +149,14 @@ public class PointsMallBridgeController {
@POST
@At("/pointOrder/unReconciledMsg")
@ApiOperation(value = "对账异常通知", notes = "接收未对平订单通知")
public MallInboundResult unReconciledMsg(@Param("..") UnReconciledMsgNoticeDTO dto) {
return inbound(() -> pointsMallBridgeService.unReconciledMsg(dto));
}
@POST
@At("/pointOrder/payCompletion")
@ApiOperation(value = "订单支付完成通知", notes = "接收结算支付完成通知")
public MallInboundResult payCompletion(@Param("..") OrderPayFinishNoticeDTO dto) {
return inbound(() -> pointsMallBridgeService.payCompletion(dto));
}
@@ -0,0 +1,20 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
/**
* 订单事件处理器策略接口。
* 不同 orderType 的事件由不同实现处理,便于维护扩展。
*/
public interface MallOrderEventHandler {
/**
* 返回所支持的事件类型(orderType)。
*/
int getType();
/**
* 处理事件,返回统一结果。
*/
MallInboundResult handle(MallOrderEventDTO dto);
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallAbolishSubOrderEventHandler
* orderType=8: 废除子订单事件处理器。
*/
@IocBean
public class MallAbolishSubOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 8;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_ABOLISH);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallCancelPayedMainOrderEventHandler
* orderType=7: 已支付未拆单的整个父单取消。
*/
@IocBean
public class MallCancelPayedMainOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 7;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.cancelMainOrder(dto, true);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallCancelPendingOrderEventHandler
* orderType=1: 取消待支付主单事件处理器。
*/
@IocBean
public class MallCancelPendingOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 1;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.cancelMainOrder(dto, false);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallCancelSubOrderEventHandler
* orderType=3: 取消子订单事件处理器。
*/
@IocBean
public class MallCancelSubOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 3;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_CANCELLED);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallConfirmReceiptEventHandler
* orderType=6: 确认收货事件处理器。
*/
@IocBean
public class MallConfirmReceiptEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 6;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_COMPLETED);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallExchangeSubEventHandler
* orderType=5: 换货单事件处理器。
*/
@IocBean
public class MallExchangeSubEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 5;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_EXCHANGE);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallInvoiceInfoEventHandler
* orderType=11: 发票信息事件处理器。
*/
@IocBean
public class MallInvoiceInfoEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 11;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.invoiceInfo(dto);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallSplitSubOrderEventHandler
* orderType=2: 拆分子订单事件处理器。
*/
@IocBean
public class MallSplitSubOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 2;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.saveOrderSub(dto);
}
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallSubOrderReturnEventHandler
* orderType=4: 退货单事件处理器。
*/
@IocBean
public class MallSubOrderReturnEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 4;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_REFUNDED);
}
}
@@ -0,0 +1,85 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseConfirmDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
/**
* 商城桥接确认购买服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInboundConfirmPurchaseService extends AbstractMallBridgeSupport {
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
public MallInboundConfirmPurchaseService(Dao dao) {
super(dao);
}
/**
* 确认购买:校验积分是否充足,更新主单支付状态,并锁定主单积分。
*/
public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) {
log.info("积分商城确认购买开始,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderId());
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "订单不存在");
}
BigDecimal totalPrice = money(nvl(dto.getTotalPrice(), main.getTotalPrice()));
BigDecimal cash = money(nvl(dto.getWPayOrAPay()));
BigDecimal points = money(nvl(dto.getPointsPrice()));
if (totalPrice.compareTo(cash.add(points)) != 0 || money(nvl(main.getTotalPrice())).compareTo(totalPrice) != 0) {
return MallInboundResult.fail(400, "订单总金额不一致");
}
BigDecimal available = pointsMallUserPointsService.availablePoints(main.getUserId());
if (available.compareTo(points) < 0) {
return MallInboundResult.fail(400, "积分不足,无法确认购买");
}
Trans.exec((Atom) () -> {
updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cash.compareTo(BigDecimal.ZERO) == 0 ? 0 : 1, totalPrice, cash, points);
pointsMallPointsLockService.lockMain(main, points);
});
log.info("积分商城确认购买完成,supplierId={}orderId={}points={}", dto.getSupplierId(), dto.getOrderId(), points);
return MallInboundResult.success();
}
protected MallInboundResult updateMainOrderPay(String supplierId, String orderId, Integer cashPaid, BigDecimal totalPrice, BigDecimal cash, BigDecimal points) {
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", supplierId)
.and("orderId", "=", orderId).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "订单不存在");
}
main.setCashPaid(cashPaid);
main.setTotalPrice(money(nvl(totalPrice, main.getTotalPrice())));
main.setWPayOrAPay(money(nvl(cash, main.getWPayOrAPay())));
main.setPointsPrice(money(nvl(points, main.getPointsPrice())));
main.setOrderState(cashPaid == 1 || cashPaid == 0 ? ORDER_FINISH_PAY : main.getOrderState());
main.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(main);
log.info("积分商城主单支付状态更新完成,supplierId={}orderId={}cashPaid={}", supplierId, orderId, cashPaid);
return MallInboundResult.success();
}
private BigDecimal money(BigDecimal value) {
return nvl(value).setScale(2, RoundingMode.HALF_UP);
}
}
@@ -0,0 +1,313 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallAbolishSubOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelPayedMainOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelPendingOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelSubOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallConfirmReceiptEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallExchangeSubEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallInvoiceInfoEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSplitSubOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSubOrderReturnEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 商城桥接订单事件入站服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInboundEventService extends AbstractMallBridgeSupport {
public static final int ORDER_CANCELLED = 3;
public static final int ORDER_COMPLETED = 5;
public static final int ORDER_EXCHANGE = 6;
public static final int ORDER_REFUNDED = 7;
public static final int ORDER_ABOLISH = 8;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
@Inject
private MallCancelPendingOrderEventHandler mallCancelPendingOrderEventHandler;
@Inject
private MallSplitSubOrderEventHandler mallSplitSubOrderEventHandler;
@Inject
private MallCancelSubOrderEventHandler mallCancelSubOrderEventHandler;
@Inject
private MallSubOrderReturnEventHandler mallSubOrderReturnEventHandler;
@Inject
private MallExchangeSubEventHandler mallExchangeSubEventHandler;
@Inject
private MallConfirmReceiptEventHandler mallConfirmReceiptEventHandler;
@Inject
private MallCancelPayedMainOrderEventHandler mallCancelPayedMainOrderEventHandler;
@Inject
private MallAbolishSubOrderEventHandler mallAbolishSubOrderEventHandler;
@Inject
private MallInvoiceInfoEventHandler mallInvoiceInfoEventHandler;
public MallInboundEventService(Dao dao) {
super(dao);
}
/**
* 处理通用事件: 分发到具体处理器。
*/
public MallInboundResult processEvent(MallOrderEventDTO dto) {
Integer type = dto.getOrderType();
if (type == null) {
return MallInboundResult.fail(400, "缺少事件类型");
}
MallOrderEventHandler handler = handlerRegistry().get(type);
if (handler == null) {
return MallInboundResult.fail(400, "未知事件类型");
}
try {
return handler.handle(dto);
} catch (Exception e) {
log.error("事件处理失败:{}", e.getMessage(), e);
return MallInboundResult.fail(500, "服务器内部错误");
}
}
private Map<Integer, MallOrderEventHandler> handlerRegistry() {
Map<Integer, MallOrderEventHandler> handlerRegistry = new HashMap<>();
register(handlerRegistry, mallCancelPendingOrderEventHandler);
register(handlerRegistry, mallSplitSubOrderEventHandler);
register(handlerRegistry, mallCancelSubOrderEventHandler);
register(handlerRegistry, mallSubOrderReturnEventHandler);
register(handlerRegistry, mallExchangeSubEventHandler);
register(handlerRegistry, mallConfirmReceiptEventHandler);
register(handlerRegistry, mallCancelPayedMainOrderEventHandler);
register(handlerRegistry, mallAbolishSubOrderEventHandler);
register(handlerRegistry, mallInvoiceInfoEventHandler);
return handlerRegistry;
}
private void register(Map<Integer, MallOrderEventHandler> handlerRegistry, MallOrderEventHandler handler) {
if (handler != null) {
handlerRegistry.put(handler.getType(), handler);
}
}
public MallInboundResult cancelMainOrder(MallOrderEventDTO dto, boolean paidNotSplit) {
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
if (StrUtil.isBlank(mainOrderId)) {
return MallInboundResult.fail(400, "主订单信息不能为空");
}
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "主订单不存在");
}
if (!paidNotSplit && !Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) {
return MallInboundResult.fail(400, "只能取消未支付主单");
}
Date now = new Date();
Trans.exec((Atom) () -> {
main.setOrderState(ORDER_CANCELLED);
main.setOrderUpdateTime(now);
dao.updateIgnoreNull(main);
dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", now),
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId());
});
log.info("积分商城主单取消完成,supplierId={}mainOrderId={}paidNotSplit={}", dto.getSupplierId(), mainOrderId, paidNotSplit);
return MallInboundResult.success();
}
public MallInboundResult saveOrderSub(MallOrderEventDTO dto) {
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getMainOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "主订单不存在");
}
List<String> subOrderIds = splitIds(dto.getOrderId());
if (mallOrderQueryOutboundService != null) {
try {
return splitSubOrderByQuery(dto, main, subOrderIds);
} catch (Exception e) {
log.error("split sub order by query failed, fallback to local split logic, supplierId={}, mainOrderId={}, orderId={}",
dto.getSupplierId(), dto.getMainOrderId(), dto.getOrderId(), e);
}
}
if (subOrderIds.isEmpty()) {
return MallInboundResult.fail(400, "子订单信息不能为空");
}
List<OrderProInfoDTO> products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class);
List<PointsMallOrderSub> subLocks = new ArrayList<>();
Trans.exec((Atom) () -> {
for (String subOrderId : subOrderIds) {
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId)
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (sub == null) {
sub = toSub(main, subOrderId);
sub.setOrderState(ORDER_IN_PROGRESS);
dao.insert(sub);
insertProducts(subOrderId, products);
} else {
sub.setOrderState(ORDER_IN_PROGRESS);
sub.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(sub);
}
subLocks.add(sub);
}
pointsMallPointsLockService.createSubLocks(main, subLocks);
});
log.info("积分商城拆单完成,supplierId={}mainOrderId={}subOrderCount={}", dto.getSupplierId(), dto.getMainOrderId(), subOrderIds.size());
return MallInboundResult.success();
}
private MallInboundResult splitSubOrderByQuery(MallOrderEventDTO dto, PointsMallOrderMain main, List<String> subOrderIds) {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)开始执行:{}", dto);
if (subOrderIds.isEmpty()) {
return MallInboundResult.fail(400, "缺少拆单信息");
}
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)调用外部服务获取子单信息:{}", dto);
List<OrderInfoDTO> orderInfoList;
try {
orderInfoList = getOrderInfoList(dto);
} catch (Exception e) {
log.error("9.2.1 生成订单信息推送-订单状态为:2(拆单子单),调用9.2.2获取订单详情接口异常:{}", e.getMessage(), e);
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + subOrderIds);
}
if (orderInfoList == null || orderInfoList.isEmpty()) {
return MallInboundResult.fail(404, "子单不存在");
}
List<PointsMallOrderSub> subLocks = new ArrayList<>();
Trans.exec((Atom) () -> {
for (OrderInfoDTO info : orderInfoList) {
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", info.getOrderId())
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (sub == null) {
sub = toSub(main, info);
sub.setOrderState(ORDER_IN_PROGRESS);
dao.insert(sub);
insertProducts(info.getOrderId(), info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos());
} else {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)子单已存在:{}", info);
sub.setOrderState(ORDER_IN_PROGRESS);
sub.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(sub);
}
subLocks.add(sub);
}
pointsMallPointsLockService.createSubLocks(main, subLocks);
});
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)结束,supplierId={}mainOrderId={}subOrderCount={}",
dto.getSupplierId(), dto.getMainOrderId(), orderInfoList.size());
return MallInboundResult.success();
}
private List<OrderInfoDTO> getOrderInfoList(MallOrderEventDTO dto) {
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderId(dto.getOrderId());
queryDTO.setOrderType(dto.getOrderType());
MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO);
if (result == null || result.getResultCode() != 0) {
throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage());
}
return result.getOrderInfo();
}
private PointsMallOrderSub toSub(PointsMallOrderMain main, OrderInfoDTO info) {
PointsMallOrderSub sub = toSub(main, info.getOrderId());
sub.setOrderNumberId(info.getOrderNumberId());
sub.setAplName(StrUtil.blankToDefault(info.getAplName(), main.getAplName()));
sub.setOrderCreateTime(parseDate(info.getCreateTime()));
sub.setOrderUpdateTime(parseDate(info.getUpdateTime()));
sub.setDeliveryTime(parseDate(info.getDeliveryTime()));
sub.setOrderFinishTime(parseDate(info.getOrderFinishTime()));
sub.setOrderCompleteTime(parseDate(info.getCompleteTime()));
sub.setDeliveryStatus(parseInteger(info.getDeliveryStatus()));
sub.setFreightCost(nvl(info.getFreightCost()));
sub.setWPayOrAPayFreightCost(nvl(info.getWPayOrAPayFreightCost()));
sub.setRefund(nvl(info.getRefund()));
sub.setWPayOrAPayRefund(nvl(info.getWPayOrAPayRefund()));
sub.setLogisticsOrderId(info.getLogisticsOrderId());
sub.setCrrgBsnNm(info.getCrrgBsnNm());
sub.setTotalPrice(nvl(info.getTotalPrice()));
sub.setWPayOrAPay(nvl(info.getWPayOrAPay()));
sub.setPointsPrice(nvl(info.getPointsPrice()));
sub.setInvoiceCode(info.getInvoiceCode());
sub.setExtJson(JSONUtil.toJsonStr(info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos()));
sub.setReconciliationStatus(0);
sub.setInvoiceStatus(0);
return sub;
}
public MallInboundResult updateSubOrderStatus(MallOrderEventDTO dto, int state) {
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (sub == null) {
return MallInboundResult.fail(404, "订单不存在");
}
if (Integer.valueOf(state).equals(sub.getOrderState())) {
log.info("积分商城子单状态已是目标状态,跳过重复处理,supplierId={}orderId={}state={}", dto.getSupplierId(), dto.getOrderId(), state);
return MallInboundResult.success();
}
Trans.exec((Atom) () -> {
sub.setOrderState(state);
sub.setOrderUpdateTime(new Date());
if (state == ORDER_RECEIVED) {
sub.setOrderFinishTime(new Date());
}
if (state == ORDER_COMPLETED) {
sub.setOrderCompleteTime(new Date());
}
dao.updateIgnoreNull(sub);
if (state == ORDER_CANCELLED || state == ORDER_REFUNDED || state == ORDER_ABOLISH) {
pointsMallPointsLockService.releaseOrReturnSub(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO));
}
if (state == ORDER_COMPLETED) {
pointsMallPointsLockService.deductCompletedSub(sub);
}
});
log.info("积分商城子单状态更新完成,supplierId={}orderId={}state={}", dto.getSupplierId(), dto.getOrderId(), state);
return MallInboundResult.success();
}
public MallInboundResult invoiceInfo(MallOrderEventDTO dto) {
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
if (StrUtil.isBlank(mainOrderId)) {
return MallInboundResult.fail(400, "主订单信息不能为空");
}
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId())
.and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
log.info("积分商城发票通知状态更新完成,supplierId={}mainOrderId={}", dto.getSupplierId(), mainOrderId);
return MallInboundResult.success();
}
}
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPendingOrderDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
/**
* 商城桥接待支付主单入站服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInboundPendingOrderService extends AbstractMallBridgeSupport {
public MallInboundPendingOrderService(Dao dao) {
super(dao);
}
/**
* 接收供应商推送的待支付主单。
*/
public MallInboundResult receivePending(MallPendingOrderDTO dto) {
if (dto.getOrderInfo() == null || StrUtil.isBlank(dto.getOrderInfo().getOrderId())) {
return MallInboundResult.fail(400, "订单信息不能为空");
}
log.info("积分商城接收待支付订单开始,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId());
if (dao.count(PointsMallOrderMain.class, Cnd.where("orderId", "=", dto.getOrderInfo().getOrderId())
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) {
return MallInboundResult.fail(-1, "订单已存在");
}
Trans.exec((Atom) () -> dao.insert(toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY)));
log.info("积分商城接收待支付订单完成,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId());
return MallInboundResult.success();
}
}
@@ -0,0 +1,27 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPointQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* 商城桥接积分查询服务。
*/
@Slf4j
@IocBean
public class MallInboundPointsQueryService {
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
/**
* 查询用户可用积分。
*/
public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) {
log.info("积分商城查询用户积分,supplierId={}userId={}", dto == null ? null : dto.getSupplierId(), dto == null ? null : dto.getUserId());
return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(dto.getUserId()));
}
}
@@ -0,0 +1,69 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.Date;
/**
* 商城桥接购买结果通知服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInboundPurchaseResultService {
private final Dao dao;
@Inject
private MallInboundConfirmPurchaseService mallInboundConfirmPurchaseService;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
public MallInboundPurchaseResultService(Dao dao) {
this.dao = dao;
}
/**
* 接收购买结果。支付成功只同步现金支付状态;支付失败取消主单并释放锁定积分。
*/
public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) {
log.info("积分商城购买结果通知,supplierId={}orderId={}purchaseResult={}", dto.getSupplierId(), dto.getOrderId(), dto.getPurchaseResult());
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "订单不存在");
}
if (Boolean.TRUE.equals(dto.getPurchaseResult())) {
Integer cashPaid = main.getWPayOrAPay() != null && main.getWPayOrAPay().signum() > 0 ? 2 : 0;
return mallInboundConfirmPurchaseService.updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cashPaid,
dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice());
}
Trans.exec((Atom) () -> {
dao.update(PointsMallOrderMain.class,
Chain.make("orderState", 3)
.add("cashPaid", main.getWPayOrAPay() != null && main.getWPayOrAPay().signum() > 0 ? 3 : main.getCashPaid())
.add("totalPrice", dto.getTotalPrice())
.add("pointsPrice", dto.getPointsPrice())
.add("wPayOrAPay", dto.getWPayOrAPay())
.add("orderUpdateTime", new Date()),
Cnd.where("id", "=", main.getId()));
dao.update(PointsMallOrderSub.class,
Chain.make("orderState", 3).add("orderUpdateTime", new Date()),
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", dto.getOrderId()).and("delFlag", "=", false));
pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId());
});
log.info("积分商城购买失败处理完成,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderId());
return MallInboundResult.success();
}
}
@@ -0,0 +1,99 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
/**
* 商城桥接开票申请出站服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInvoiceApplyOutboundService extends AbstractMallBridgeSupport {
public MallInvoiceApplyOutboundService(Dao dao) {
super(dao);
}
/**
* 接收开票申请并保存开票记录。
*/
public MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId(), dto.getOrderIds())) {
return MallOutboundResult.fail(400, "开票申请参数不能为空");
}
List<String> orderIds = splitIds(dto.getOrderIds());
log.info("积分商城开票申请开始,supplierId={}settlementId={}orderCount={}", dto.getSupplierId(), dto.getSettlementId(), orderIds.size());
List<PointsMallOrderSub> orders = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "in", orderIds).and("delFlag", "=", false));
if (orders.isEmpty()) {
return MallOutboundResult.fail(404, "未找到可开票订单");
}
if (dto.getOrderNum() != null && dto.getOrderNum() != orders.size()) {
return MallOutboundResult.fail(400, "订单数量与开票申请不一致");
}
BigDecimal total = orders.stream().map(PointsMallOrderSub::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
if (dto.getOrderTotalPrice() != null && total.compareTo(dto.getOrderTotalPrice()) != 0) {
return MallOutboundResult.fail(400, "订单金额与开票申请不一致");
}
Trans.exec((Atom) () -> saveInvoiceApply(dto, orderIds, total));
log.info("积分商城开票申请完成,supplierId={}settlementId={}", dto.getSupplierId(), dto.getSettlementId());
return MallOutboundResult.success();
}
private void saveInvoiceApply(MallInvoiceApplyDTO dto, List<String> orderIds, BigDecimal total) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()).and("delFlag", "=", false));
if (main == null) {
main = new PointsMallInvoiceMain();
main.setSettlementId(dto.getSettlementId());
main.setBSuccess(0);
main.setSucOrderIds(String.join(",", orderIds));
main.setFailOrderIds("");
main.setFailMsg("");
dao.insert(main);
} else {
main.setBSuccess(0);
main.setSucOrderIds(String.join(",", orderIds));
main.setFailOrderIds("");
main.setFailMsg("");
dao.updateIgnoreNull(main);
}
PointsMallInvoiceInfo info = new PointsMallInvoiceInfo();
info.setMainId(main.getId());
info.setInvoiceId(dto.getSettlementId());
info.setInvoiceCode(dto.getInvoiceCode());
info.setInvoiceDate(parseDate(dto.getInvoiceDate()));
info.setInvoiceNakeAmount(nvl(dto.getInvoiceNakedPrice(), total));
info.setInvoiceTaxAmount(nvl(dto.getInvoiceTaxPrice(), BigDecimal.ZERO));
info.setInvoiceAmount(total);
info.setInvoiceType(Convert.toInt(dto.getInvoiceType(), null));
info.setInvoiceAddress(dto.getInvoiceAddress());
info.setInvoiceContact(dto.getInvoiceContact());
info.setInvoiceContent(dto.getInvoiceContent());
info.setInvoiceBank(dto.getBankName());
info.setInvoiceAccount(dto.getBankAccount());
info.setTaxIdNumber(dto.getInvoiceCode());
info.setRemark(dto.getRemark());
dao.insert(info);
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", dto.getSettlementId()),
Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds));
}
}
@@ -0,0 +1,53 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
import java.util.stream.Collectors;
/**
* 商城桥接发票信息查询服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport {
public MallInvoiceInfoQueryService(Dao dao) {
super(dao);
}
/**
* 查询指定结算单的发票信息。
*/
public GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId())) {
throw new IllegalArgumentException("发票查询参数不能为空");
}
log.info("积分商城查询发票信息,supplierId={}settlementId={}", dto.getSupplierId(), dto.getSettlementId());
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId())
.and("delFlag", "=", false));
if (main == null) {
throw new IllegalArgumentException("发票信息不存在");
}
List<InvoiceInfoDTO> invoiceInfos = dao.query(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false))
.stream().map(this::toInvoiceInfoDTO).collect(Collectors.toList());
GetInvoiceInfoDTO result = new GetInvoiceInfoDTO();
result.setBSuccess(main.getBSuccess());
result.setSettlementId(main.getSettlementId());
result.setSucOrderIds(main.getSucOrderIds());
result.setFailOrderIds(main.getFailOrderIds());
result.setFailMsg(main.getFailMsg());
result.setInvoiceInfos(invoiceInfos);
return result;
}
}
@@ -0,0 +1,90 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.annotation.Inject;
import java.util.HashMap;
import java.util.Map;
/**
* 9.2.2 获取订单详情外呼服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallOrderQueryOutboundService {
private final Dao dao;
@Inject
private PropertiesProxy conf;
public MallOrderQueryOutboundService(Dao dao) {
this.dao = dao;
}
public MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto) {
return orderQuery(dto);
}
/**
* 调用 BAB7/供应商开放接口获取订单详情。
*/
public MallOutboundResult orderQuery(MallOrderQueryDTO dto) {
log.info("9.2.2 获取订单详情调用开始:{}", dto);
String clientBAB7ServerUrl = conf.get("spdb.bab7.url", "");
String keyHex = conf.get("sm4cbc.keyHex", "");
String ivHex = conf.get("sm4cbc.ivHex", "");
if (StrUtil.isBlank(clientBAB7ServerUrl)) {
throw new RuntimeException("未配置 BAB7 服务地址");
}
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (supplier == null) {
log.error("9.2.2 获取订单详情接口 未找到对应供应商信息,supplierId={}", dto.getSupplierId());
throw new RuntimeException("未找到对应供应商信息");
}
long timestamp = System.currentTimeMillis();
dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp));
dto.setTimestamp(timestamp);
String address4 = StrUtil.blankToDefault(supplier.getSupplierApiUrl(), "") + "/supplierOpenApi/pointOrder/queryOrderInfo";
String params = JSONUtil.toJsonStr(dto);
log.info("9.2.2 获取订单详情接口-请求参数:{}", params);
String encrypt;
try {
encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(params, keyHex, ivHex);
} catch (Exception e) {
log.error("9.2.2 获取订单详情接口请求参数 SM4CBC 加密出错:{}", params, e);
throw new RuntimeException("9.2.2 获取订单详情接口请求参数 SM4CBC 加密出错");
}
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("Address4", address4);
bodyMap.put("BussDealMd", "P");
bodyMap.put("SroNo", supplier.getSroNo());
bodyMap.put("RsrvFld1", supplier.getClientId());
bodyMap.put("AplParmObjct", encrypt);
log.info("9.2.2 获取订单详情调用 BAB7 入参:{}", JSONUtil.toJsonStr(bodyMap));
String response = HttpUtil.createPost(clientBAB7ServerUrl)
.contentType("application/json;charset=UTF-8")
.body(JSONUtil.toJsonStr(bodyMap))
.execute()
.body();
log.info("9.2.2 获取订单详情调用结束,返回报文:{}", response);
return JSONUtil.toBean(response, MallOutboundResult.class);
}
}
@@ -0,0 +1,59 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.FinalStatementDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderPayFinishNoticeDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.List;
/**
* 商城桥接支付完成通知服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallPayFinishNoticeService extends AbstractMallBridgeSupport {
public MallPayFinishNoticeService(Dao dao) {
super(dao);
}
/**
* 接收结算支付完成通知并更新发票/订单开票状态。
*/
public MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto) {
if (dto == null || StrUtil.isBlank(dto.getSupplierId()) || dto.getExpFinalInfos() == null) {
return MallInboundResult.fail(400, "支付完成通知参数不能为空");
}
log.info("积分商城支付完成通知开始,supplierId={}statementCount={}", dto.getSupplierId(), dto.getExpFinalInfos().size());
Trans.exec((Atom) () -> {
for (FinalStatementDTO item : dto.getExpFinalInfos()) {
if (item == null || StrUtil.isBlank(item.getSettlementId())) {
continue;
}
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", item.getSettlementId())
.and("delFlag", "=", false));
if (main != null) {
main.setBSuccess(1);
dao.updateIgnoreNull(main);
}
List<String> orderIds = splitIds(item.getOrderIds());
if (!orderIds.isEmpty()) {
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds));
}
}
});
log.info("积分商城支付完成通知完成,supplierId={}", dto.getSupplierId());
return MallInboundResult.success();
}
}
@@ -0,0 +1,85 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.PictureQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 商城桥接图片文件查询服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallPictureApplyOutboundService extends AbstractMallBridgeSupport {
public MallPictureApplyOutboundService(Dao dao) {
super(dao);
}
/**
* 查询商品或发票图片 Base64 信息。
*/
public MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber())) {
return MallPictureOutboundResult.fail(400, "图片查询参数不能为空");
}
log.info("积分商城查询图片信息,supplierId={}queryType={}idNumber={}", dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber());
Map<String, String> map = "2".equals(dto.getQueryType()) ? queryInvoiceFiles(dto) : queryProductPictures(dto);
MallPictureOutboundResult result = MallPictureOutboundResult.success(map);
if ("2".equals(dto.getQueryType())) {
result.setFileType(firstInvoiceFileType(dto));
}
return result;
}
private Map<String, String> queryProductPictures(PictureQueryDTO dto) {
Cnd cnd = Cnd.where("orderSubId", "=", dto.getIdNumber()).and("delFlag", "=", false);
if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) {
cnd.and("sku", "in", dto.getFileIdList());
}
List<PointsMallOrderSubProduct> products = dao.query(PointsMallOrderSubProduct.class, cnd);
Map<String, String> map = new LinkedHashMap<>();
for (PointsMallOrderSubProduct product : products) {
map.put(product.getSku(), product.getPicInfo());
}
return map;
}
private Map<String, String> queryInvoiceFiles(PictureQueryDTO dto) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false));
if (main == null) {
return Collections.emptyMap();
}
Cnd cnd = Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false);
if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) {
cnd.and("invoiceId", "in", dto.getFileIdList());
}
List<PointsMallInvoiceInfo> invoices = dao.query(PointsMallInvoiceInfo.class, cnd);
Map<String, String> map = new LinkedHashMap<>();
for (PointsMallInvoiceInfo invoice : invoices) {
map.put(invoice.getInvoiceId(), invoice.getImageEncode());
}
return map;
}
private String firstInvoiceFileType(PictureQueryDTO dto) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false));
if (main == null) {
return null;
}
PointsMallInvoiceInfo info = dao.fetch(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false));
return info == null ? null : info.getFileType();
}
}
@@ -0,0 +1,96 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenRefreshDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundTokenResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.models.PointsMallSupplierTokenStore;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
/**
* 商城桥接授权服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallTokenService extends AbstractMallBridgeSupport {
public MallTokenService(Dao dao) {
super(dao);
}
/**
* 获取访问令牌。
*/
public MallInboundTokenResult getToken(MallTokenDTO dto) {
log.info("积分商城供应商获取Token开始,supplierId={}", dto == null ? null : dto.getSupplierId());
PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp());
invalidateOldToken(supplier.getSupplierId(), supplier.getClientId());
MallInboundTokenResult result = createToken(supplier);
log.info("积分商城供应商获取Token完成,supplierId={}", supplier.getSupplierId());
return result;
}
/**
* 刷新访问令牌。
*/
public MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto) {
log.info("积分商城供应商刷新Token开始,supplierId={}", dto == null ? null : dto.getSupplierId());
PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp());
PointsMallSupplierTokenStore store = dao.fetch(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplier.getSupplierId())
.and("refreshToken", "=", dto.getRefreshToken()).and("isValid", "=", true));
if (store == null || store.getRefreshExpireTime() == null || new Date().after(store.getRefreshExpireTime())) {
throw new IllegalArgumentException("刷新令牌已失效,请重新获取授权");
}
invalidateOldToken(supplier.getSupplierId(), supplier.getClientId());
MallInboundTokenResult result = createToken(supplier);
log.info("积分商城供应商刷新Token完成,supplierId={}", supplier.getSupplierId());
return result;
}
private PointsMallSupplier checkedSupplier(String supplierId, String clientId, String clientSecret, String sendSha256, String timestamp) {
if (StrUtil.hasBlank(supplierId, clientId, clientSecret, sendSha256, timestamp)) {
throw new IllegalArgumentException("供应商授权信息不能为空");
}
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId)
.and("clientId", "=", clientId).and("delFlag", "=", false));
if (supplier == null) {
throw new IllegalArgumentException("供应商不存在或已停用");
}
String serverSha256 = SecureUtil.sha256(supplier.getClientId() + supplier.getClientSecret() + timestamp);
if (!Objects.equals(sendSha256, serverSha256)) {
throw new IllegalArgumentException("签名验证信息错误");
}
return supplier;
}
private MallInboundTokenResult createToken(PointsMallSupplier supplier) {
String accessToken = UUID.randomUUID().toString().replace("-", "");
String refreshToken = UUID.randomUUID().toString().replace("-", "");
Date now = new Date();
PointsMallSupplierTokenStore store = new PointsMallSupplierTokenStore();
store.setSupplierId(supplier.getSupplierId());
store.setClientId(supplier.getClientId());
store.setAccessToken(accessToken);
store.setRefreshToken(refreshToken);
store.setAccessExpireTime(DateUtil.offsetSecond(now, (int) ACCESS_EXPIRE_SECONDS));
store.setRefreshExpireTime(DateUtil.offsetSecond(now, (int) REFRESH_EXPIRE_SECONDS));
store.setIsValid(true);
dao.insert(store);
return MallInboundTokenResult.success(DateUtil.format(now, "yyyyMMddHHmmss"), accessToken, refreshToken, ACCESS_EXPIRE_SECONDS, REFRESH_EXPIRE_SECONDS);
}
private void invalidateOldToken(String supplierId, String clientId) {
dao.clear(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplierId).and("clientId", "=", clientId));
}
}
@@ -0,0 +1,53 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.List;
/**
* 商城桥接对账异常通知服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallUnReconciledMsgNoticeService extends AbstractMallBridgeSupport {
public MallUnReconciledMsgNoticeService(Dao dao) {
super(dao);
}
/**
* 接收未对平订单通知并保存异常记录。
*/
public MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderIds())) {
return MallInboundResult.fail(400, "对账异常通知参数不能为空");
}
List<String> orderIds = splitIds(dto.getOrderIds());
log.info("积分商城对账异常通知开始,supplierId={}orderCount={}", dto.getSupplierId(), orderIds.size());
Trans.exec((Atom) () -> {
for (String orderId : orderIds) {
PointsMallReconciliationRecord record = new PointsMallReconciliationRecord();
record.setSupplierId(dto.getSupplierId());
record.setOrderId(orderId);
record.setDiffReason(dto.getUnReconciledMsg());
record.setDiffType(Convert.toInt(dto.getUnReconciledType(), null));
record.setBatchNo(StrUtil.blankToDefault(dto.getUniqueSeqNo(), dto.getStartDate() + "_" + dto.getEndDate()));
record.setNoticeStatus(1);
record.setPushTime(1);
dao.insert(record);
}
});
log.info("积分商城对账异常通知完成,supplierId={}orderCount={}", dto.getSupplierId(), orderIds.size());
return MallInboundResult.success();
}
}
@@ -1,15 +1,6 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.FinalStatementDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
@@ -19,9 +10,7 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseConfirmDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenRefreshDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderPayFinishNoticeDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.PictureQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO;
@@ -30,661 +19,117 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundTokenResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.models.PointsMallSupplierTokenStore;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundConfirmPurchaseService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPendingOrderService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPointsQueryService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPurchaseResultService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceApplyOutboundService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceInfoQueryService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallOrderQueryOutboundService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallPayFinishNoticeService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallPictureApplyOutboundService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallTokenService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallUnReconciledMsgNoticeService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.PointsMallBridgeService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
/**
* 积分商城桥接门面服务。
*
* <p>Controller 只依赖该门面,具体业务拆分到各个专职服务,避免桥接接口逻辑堆在一个实现类里。</p>
*/
@IocBean
public class PointsMallBridgeServiceImpl implements PointsMallBridgeService {
private static final long ACCESS_EXPIRE_SECONDS = 3600;
private static final long REFRESH_EXPIRE_SECONDS = 86400L * 7;
private static final int ORDER_PENDING_PAY = 0;
private static final int ORDER_FINISH_PAY = 1;
private static final int ORDER_IN_PROGRESS = 2;
private static final int ORDER_CANCELLED = 3;
private static final int ORDER_RECEIVED = 4;
private static final int ORDER_COMPLETED = 5;
private static final int ORDER_EXCHANGE = 6;
private static final int ORDER_REFUNDED = 7;
private static final int ORDER_ABOLISH = 8;
private final Dao dao;
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
public PointsMallBridgeServiceImpl(Dao dao) {
this.dao = dao;
}
private MallTokenService mallTokenService;
@Inject
private MallInboundPointsQueryService mallInboundPointsQueryService;
@Inject
private MallInboundPendingOrderService mallInboundPendingOrderService;
@Inject
private MallInboundConfirmPurchaseService mallInboundConfirmPurchaseService;
@Inject
private MallInboundPurchaseResultService mallInboundPurchaseResultService;
@Inject
private MallInboundEventService mallInboundEventService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
@Inject
private MallInvoiceApplyOutboundService mallInvoiceApplyOutboundService;
@Inject
private MallInvoiceInfoQueryService mallInvoiceInfoQueryService;
@Inject
private MallPictureApplyOutboundService mallPictureApplyOutboundService;
@Inject
private MallUnReconciledMsgNoticeService mallUnReconciledMsgNoticeService;
@Inject
private MallPayFinishNoticeService mallPayFinishNoticeService;
@Override
public MallInboundTokenResult getToken(MallTokenDTO dto) {
PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp());
invalidateOldToken(supplier.getSupplierId(), supplier.getClientId());
return createToken(supplier);
return mallTokenService.getToken(dto);
}
@Override
public MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto) {
PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp());
PointsMallSupplierTokenStore store = dao.fetch(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplier.getSupplierId())
.and("refreshToken", "=", dto.getRefreshToken()).and("isValid", "=", true));
if (store == null || store.getRefreshExpireTime() == null || new Date().after(store.getRefreshExpireTime())) {
throw new IllegalArgumentException("刷新令牌已失效,请重新获取授权");
}
invalidateOldToken(supplier.getSupplierId(), supplier.getClientId());
return createToken(supplier);
return mallTokenService.refreshToken(dto);
}
@Override
public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) {
return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(dto.getUserId()));
return mallInboundPointsQueryService.queryPoints(dto);
}
@Override
public MallInboundResult receivePending(MallPendingOrderDTO dto) {
if (dto.getOrderInfo() == null || StrUtil.isBlank(dto.getOrderInfo().getOrderId())) {
return MallInboundResult.fail(400, "订单信息不能为空");
}
if (dao.count(PointsMallOrderMain.class, Cnd.where("orderId", "=", dto.getOrderInfo().getOrderId())
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) {
return MallInboundResult.fail(-1, "订单已存在");
}
Trans.exec((Atom) () -> dao.insert(toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY)));
return MallInboundResult.success();
return mallInboundPendingOrderService.receivePending(dto);
}
@Override
public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) {
return updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), 1, dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice());
return mallInboundConfirmPurchaseService.confirmPurchase(dto);
}
@Override
public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) {
int cashPaid = Boolean.TRUE.equals(dto.getPurchaseResult()) ? 1 : 0;
return updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cashPaid, dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice());
return mallInboundPurchaseResultService.purchaseResult(dto);
}
@Override
public MallInboundResult receiveEvent(MallOrderEventDTO dto) {
Integer type = dto.getOrderType();
if (type == null) {
return MallInboundResult.fail(400, "订单事件类型不能为空");
}
if (type == 1) {
return cancelMainOrder(dto, false);
}
if (type == 2) {
return splitSubOrder(dto);
}
if (type == 3) {
return updateSubState(dto, ORDER_CANCELLED);
}
if (type == 4) {
return updateSubState(dto, ORDER_REFUNDED);
}
if (type == 5) {
return updateSubState(dto, ORDER_EXCHANGE);
}
if (type == 6) {
return updateSubState(dto, ORDER_COMPLETED);
}
if (type == 7) {
return cancelMainOrder(dto, true);
}
if (type == 8) {
return updateSubState(dto, ORDER_ABOLISH);
}
if (type == 11) {
return markInvoiceNotice(dto);
}
return MallInboundResult.success();
return mallInboundEventService.processEvent(dto);
}
@Override
public MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto) {
String table = Integer.valueOf(2).equals(dto.getOrderType()) ? "points_mall_order_sub" : "points_mall_order_main";
Sql sql = Sqls.create("select * from " + table + " where supplierId=@supplierId and orderId=@orderId and delFlag=0");
sql.params().set("supplierId", dto.getSupplierId());
sql.params().set("orderId", dto.getOrderId());
sql.setCallback(Sqls.callback.records());
dao.execute(sql);
List<OrderInfoDTO> list = sql.getList(Record.class).stream().map(this::toOrderInfo).collect(Collectors.toList());
return MallOutboundResult.success(list);
return mallOrderQueryOutboundService.queryOrderInfo(dto);
}
@Override
public MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId(), dto.getOrderIds())) {
return MallOutboundResult.fail(400, "开票申请参数不能为空");
}
List<String> orderIds = splitIds(dto.getOrderIds());
List<PointsMallOrderSub> orders = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "in", orderIds).and("delFlag", "=", false));
if (orders.isEmpty()) {
return MallOutboundResult.fail(404, "未找到可开票订单");
}
if (dto.getOrderNum() != null && dto.getOrderNum() != orders.size()) {
return MallOutboundResult.fail(400, "订单数量与开票申请不一致");
}
BigDecimal total = orders.stream().map(PointsMallOrderSub::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
if (dto.getOrderTotalPrice() != null && total.compareTo(dto.getOrderTotalPrice()) != 0) {
return MallOutboundResult.fail(400, "订单金额与开票申请不一致");
}
Trans.exec((Atom) () -> saveInvoiceApply(dto, orderIds, total));
return MallOutboundResult.success();
return mallInvoiceApplyOutboundService.applyInvoice(dto);
}
@Override
public GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId())) {
throw new IllegalArgumentException("发票查询参数不能为空");
}
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId())
.and("delFlag", "=", false));
if (main == null) {
throw new IllegalArgumentException("发票信息不存在");
}
List<InvoiceInfoDTO> invoiceInfos = dao.query(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false))
.stream().map(this::toInvoiceInfoDTO).collect(Collectors.toList());
GetInvoiceInfoDTO result = new GetInvoiceInfoDTO();
result.setBSuccess(main.getBSuccess());
result.setSettlementId(main.getSettlementId());
result.setSucOrderIds(main.getSucOrderIds());
result.setFailOrderIds(main.getFailOrderIds());
result.setFailMsg(main.getFailMsg());
result.setInvoiceInfos(invoiceInfos);
return result;
return mallInvoiceInfoQueryService.queryInvoicesInfo(dto);
}
@Override
public MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber())) {
return MallPictureOutboundResult.fail(400, "图片查询参数不能为空");
}
Map<String, String> map = "2".equals(dto.getQueryType()) ? queryInvoiceFiles(dto) : queryProductPictures(dto);
MallPictureOutboundResult result = MallPictureOutboundResult.success(map);
if ("2".equals(dto.getQueryType())) {
result.setFileType(firstInvoiceFileType(dto));
}
return result;
return mallPictureApplyOutboundService.queryFileBase64Info(dto);
}
@Override
public MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto) {
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderIds())) {
return MallInboundResult.fail(400, "对账异常通知参数不能为空");
}
List<String> orderIds = splitIds(dto.getOrderIds());
Trans.exec((Atom) () -> {
for (String orderId : orderIds) {
PointsMallReconciliationRecord record = new PointsMallReconciliationRecord();
record.setSupplierId(dto.getSupplierId());
record.setOrderId(orderId);
record.setDiffReason(dto.getUnReconciledMsg());
record.setDiffType(Convert.toInt(dto.getUnReconciledType(), null));
record.setBatchNo(StrUtil.blankToDefault(dto.getUniqueSeqNo(), dto.getStartDate() + "_" + dto.getEndDate()));
record.setNoticeStatus(1);
record.setPushTime(1);
dao.insert(record);
}
});
return MallInboundResult.success();
return mallUnReconciledMsgNoticeService.unReconciledMsg(dto);
}
@Override
public MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto) {
if (dto == null || StrUtil.isBlank(dto.getSupplierId()) || dto.getExpFinalInfos() == null) {
return MallInboundResult.fail(400, "支付完成通知参数不能为空");
}
Trans.exec((Atom) () -> {
for (FinalStatementDTO item : dto.getExpFinalInfos()) {
if (item == null || StrUtil.isBlank(item.getSettlementId())) {
continue;
}
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", item.getSettlementId())
.and("delFlag", "=", false));
if (main != null) {
main.setBSuccess(1);
dao.updateIgnoreNull(main);
}
List<String> orderIds = splitIds(item.getOrderIds());
if (!orderIds.isEmpty()) {
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds));
}
}
});
return MallInboundResult.success();
}
private PointsMallSupplier checkedSupplier(String supplierId, String clientId, String clientSecret, String sendSha256, String timestamp) {
if (StrUtil.hasBlank(supplierId, clientId, clientSecret, sendSha256, timestamp)) {
throw new IllegalArgumentException("供应商授权信息不能为空");
}
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId)
.and("clientId", "=", clientId).and("delFlag", "=", false));
if (supplier == null) {
throw new IllegalArgumentException("供应商不存在或已停用");
}
String serverSha256 = SecureUtil.sha256(supplier.getClientId() + supplier.getClientSecret() + timestamp);
if (!Objects.equals(sendSha256, serverSha256)) {
throw new IllegalArgumentException("签名验证信息错误");
}
return supplier;
}
private MallInboundTokenResult createToken(PointsMallSupplier supplier) {
String accessToken = UUID.randomUUID().toString().replace("-", "");
String refreshToken = UUID.randomUUID().toString().replace("-", "");
Date now = new Date();
PointsMallSupplierTokenStore store = new PointsMallSupplierTokenStore();
store.setSupplierId(supplier.getSupplierId());
store.setClientId(supplier.getClientId());
store.setAccessToken(accessToken);
store.setRefreshToken(refreshToken);
store.setAccessExpireTime(DateUtil.offsetSecond(now, (int) ACCESS_EXPIRE_SECONDS));
store.setRefreshExpireTime(DateUtil.offsetSecond(now, (int) REFRESH_EXPIRE_SECONDS));
store.setIsValid(true);
dao.insert(store);
return MallInboundTokenResult.success(DateUtil.format(now, "yyyyMMddHHmmss"), accessToken, refreshToken, ACCESS_EXPIRE_SECONDS, REFRESH_EXPIRE_SECONDS);
}
private void invalidateOldToken(String supplierId, String clientId) {
dao.clear(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplierId).and("clientId", "=", clientId));
}
private MallInboundResult updateMainOrderPay(String supplierId, String orderId, Integer cashPaid, BigDecimal totalPrice, BigDecimal cash, BigDecimal points) {
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", supplierId)
.and("orderId", "=", orderId).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "订单不存在");
}
main.setCashPaid(cashPaid);
main.setTotalPrice(nvl(totalPrice, main.getTotalPrice()));
main.setWPayOrAPay(nvl(cash, main.getWPayOrAPay()));
main.setPointsPrice(nvl(points, main.getPointsPrice()));
main.setOrderState(cashPaid == 1 ? ORDER_FINISH_PAY : main.getOrderState());
main.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(main);
return MallInboundResult.success();
}
private MallInboundResult cancelMainOrder(MallOrderEventDTO dto, boolean paidNotSplit) {
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
if (StrUtil.isBlank(mainOrderId)) {
return MallInboundResult.fail(400, "主订单信息不能为空");
}
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "主订单不存在");
}
if (!paidNotSplit && !Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) {
return MallInboundResult.fail(400, "只能取消未支付主单");
}
main.setOrderState(ORDER_CANCELLED);
main.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(main);
dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", new Date()),
Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
return MallInboundResult.success();
}
private MallInboundResult splitSubOrder(MallOrderEventDTO dto) {
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getMainOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(404, "主订单不存在");
}
List<String> subOrderIds = splitIds(dto.getOrderId());
if (subOrderIds.isEmpty()) {
return MallInboundResult.fail(400, "子订单信息不能为空");
}
List<OrderProInfoDTO> products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class);
Trans.exec((Atom) () -> {
for (String subOrderId : subOrderIds) {
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId)
.and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (sub == null) {
sub = toSub(main, subOrderId);
sub.setOrderState(ORDER_IN_PROGRESS);
dao.insert(sub);
insertProducts(subOrderId, products);
} else {
sub.setOrderState(ORDER_IN_PROGRESS);
sub.setOrderUpdateTime(new Date());
dao.updateIgnoreNull(sub);
}
}
});
return MallInboundResult.success();
}
private MallInboundResult updateSubState(MallOrderEventDTO dto, int state) {
PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (sub == null) {
return MallInboundResult.fail(404, "订单不存在");
}
sub.setOrderState(state);
sub.setOrderUpdateTime(new Date());
if (state == ORDER_RECEIVED) {
sub.setOrderFinishTime(new Date());
}
if (state == ORDER_COMPLETED) {
sub.setOrderCompleteTime(new Date());
}
dao.updateIgnoreNull(sub);
return MallInboundResult.success();
}
private MallInboundResult markInvoiceNotice(MallOrderEventDTO dto) {
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId());
if (StrUtil.isBlank(mainOrderId)) {
return MallInboundResult.fail(400, "主订单信息不能为空");
}
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId())
.and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false));
return MallInboundResult.success();
}
private void saveInvoiceApply(MallInvoiceApplyDTO dto, List<String> orderIds, BigDecimal total) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()).and("delFlag", "=", false));
if (main == null) {
main = new PointsMallInvoiceMain();
main.setSettlementId(dto.getSettlementId());
main.setBSuccess(0);
main.setSucOrderIds(String.join(",", orderIds));
main.setFailOrderIds("");
main.setFailMsg("");
dao.insert(main);
} else {
main.setBSuccess(0);
main.setSucOrderIds(String.join(",", orderIds));
main.setFailOrderIds("");
main.setFailMsg("");
dao.updateIgnoreNull(main);
}
PointsMallInvoiceInfo info = new PointsMallInvoiceInfo();
info.setMainId(main.getId());
info.setInvoiceId(dto.getSettlementId());
info.setInvoiceCode(dto.getInvoiceCode());
info.setInvoiceDate(parseDate(dto.getInvoiceDate()));
info.setInvoiceNakeAmount(nvl(dto.getInvoiceNakedPrice(), total));
info.setInvoiceTaxAmount(nvl(dto.getInvoiceTaxPrice(), BigDecimal.ZERO));
info.setInvoiceAmount(total);
info.setInvoiceType(Convert.toInt(dto.getInvoiceType(), null));
info.setInvoiceAddress(dto.getInvoiceAddress());
info.setInvoiceContact(dto.getInvoiceContact());
info.setInvoiceContent(dto.getInvoiceContent());
info.setInvoiceBank(dto.getBankName());
info.setInvoiceAccount(dto.getBankAccount());
info.setTaxIdNumber(dto.getInvoiceCode());
info.setRemark(dto.getRemark());
dao.insert(info);
dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", dto.getSettlementId()),
Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds));
}
private Map<String, String> queryProductPictures(PictureQueryDTO dto) {
Cnd cnd = Cnd.where("orderSubId", "=", dto.getIdNumber()).and("delFlag", "=", false);
if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) {
cnd.and("sku", "in", dto.getFileIdList());
}
List<PointsMallOrderSubProduct> products = dao.query(PointsMallOrderSubProduct.class, cnd);
Map<String, String> map = new LinkedHashMap<>();
for (PointsMallOrderSubProduct product : products) {
map.put(product.getSku(), product.getPicInfo());
}
return map;
}
private Map<String, String> queryInvoiceFiles(PictureQueryDTO dto) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false));
if (main == null) {
return Collections.emptyMap();
}
Cnd cnd = Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false);
if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) {
cnd.and("invoiceId", "in", dto.getFileIdList());
}
List<PointsMallInvoiceInfo> invoices = dao.query(PointsMallInvoiceInfo.class, cnd);
Map<String, String> map = new LinkedHashMap<>();
for (PointsMallInvoiceInfo invoice : invoices) {
map.put(invoice.getInvoiceId(), invoice.getImageEncode());
}
return map;
}
private String firstInvoiceFileType(PictureQueryDTO dto) {
PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false));
if (main == null) {
return null;
}
PointsMallInvoiceInfo info = dao.fetch(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false));
return info == null ? null : info.getFileType();
}
private PointsMallOrderMain toMain(String supplierId, String userId, OrderInfoDTO orderInfo, Integer state) {
PointsMallOrderMain main = new PointsMallOrderMain();
main.setOrderId(orderInfo.getOrderId());
main.setSupplierId(supplierId);
main.setOrderNumberId(orderInfo.getOrderNumberId());
main.setUserId(userId);
main.setAplName(orderInfo.getAplName());
main.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
main.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
main.setDeliveryTime(parseDate(orderInfo.getDeliveryTime()));
main.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime()));
main.setOrderCompleteTime(parseDate(orderInfo.getCompleteTime()));
main.setDeliveryStatus(orderInfo.getDeliveryStatus());
main.setFreightCost(nvl(orderInfo.getFreightCost()));
main.setRefund(nvl(orderInfo.getRefund()));
main.setLogisticsOrderId(orderInfo.getLogisticsOrderId());
main.setCrrgBsnNm(orderInfo.getCrrgBsnNm());
main.setOrderState(state);
main.setTotalPrice(nvl(orderInfo.getTotalPrice()));
main.setWPayOrAPay(nvl(orderInfo.getWPayOrAPay()));
main.setPointsPrice(nvl(orderInfo.getPointsPrice()));
main.setInvoiceCode(orderInfo.getInvoiceCode());
main.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos() == null ? Collections.emptyList() : orderInfo.getOrderProInfos()));
main.setCashPaid(0);
return main;
}
private PointsMallOrderSub toSub(PointsMallOrderMain main, String subOrderId) {
PointsMallOrderSub sub = new PointsMallOrderSub();
sub.setOrderId(StrUtil.blankToDefault(subOrderId, main.getOrderId()));
sub.setOrderNumberId(main.getOrderNumberId());
sub.setMainOrderId(main.getOrderId());
sub.setSupplierId(main.getSupplierId());
sub.setUserId(main.getUserId());
sub.setAplName(main.getAplName());
sub.setOrderCreateTime(main.getOrderCreateTime());
sub.setOrderUpdateTime(new Date());
sub.setDeliveryTime(main.getDeliveryTime());
sub.setOrderFinishTime(main.getOrderFinishTime());
sub.setOrderCompleteTime(main.getOrderCompleteTime());
sub.setDeliveryStatus(parseInteger(main.getDeliveryStatus()));
sub.setFreightCost(main.getFreightCost());
sub.setRefund(main.getRefund());
sub.setLogisticsOrderId(main.getLogisticsOrderId());
sub.setCrrgBsnNm(main.getCrrgBsnNm());
sub.setOrderState(main.getOrderState());
sub.setTotalPrice(main.getTotalPrice());
sub.setWPayOrAPay(main.getWPayOrAPay());
sub.setPointsPrice(main.getPointsPrice());
sub.setInvoiceCode(main.getInvoiceCode());
sub.setExtJson(main.getExtJson());
sub.setReconciliationStatus(0);
sub.setInvoiceStatus(0);
return sub;
}
private void insertProducts(String orderSubId, List<OrderProInfoDTO> products) {
if (products == null) {
return;
}
for (OrderProInfoDTO item : products) {
PointsMallOrderSubProduct product = new PointsMallOrderSubProduct();
product.setOrderSubId(orderSubId);
product.setSku(item.getSku());
product.setNumber(item.getNumber());
product.setPrice(item.getPrice());
product.setName(item.getName());
product.setTaxRate(item.getTaxRate());
product.setPicInfo(item.getPicInfo() == null || item.getPicInfo().isEmpty() ? null : item.getPicInfo().get(0));
dao.insert(product);
}
}
private OrderInfoDTO toOrderInfo(Record record) {
OrderInfoDTO dto = new OrderInfoDTO();
dto.setOrderId(record.getString("orderId"));
dto.setOrderNumberId(record.getString("orderNumberId"));
dto.setSupplierId(record.getString("supplierId"));
dto.setAplName(record.getString("aplName"));
dto.setCreateTime(formatDate(record.get("orderCreateTime")));
dto.setUpdateTime(formatDate(record.get("orderUpdateTime")));
dto.setDeliveryTime(formatDate(record.get("deliveryTime")));
dto.setOrderFinishTime(formatDate(record.get("orderFinishTime")));
dto.setCompleteTime(formatDate(record.get("orderCompleteTime")));
dto.setDeliveryStatus(record.getString("deliveryStatus"));
dto.setFreightCost(decimal(record.get("freightCost")));
dto.setWPayOrAPayFreightCost(decimal(record.get("wPayOrAPayFreightCost")));
dto.setRefund(decimal(record.get("refund")));
dto.setWPayOrAPayRefund(decimal(record.get("wPayOrAPayRefund")));
dto.setLogisticsOrderId(record.getString("logisticsOrderId"));
dto.setCrrgBsnNm(record.getString("crrgBsnNm"));
dto.setOrderState(record.getInt("orderState"));
dto.setTotalPrice(decimal(record.get("totalPrice")));
dto.setWPayOrAPay(decimal(record.get("wPayOrAPay")));
dto.setPointsPrice(decimal(record.get("pointsPrice")));
dto.setInvoiceCode(record.getString("invoiceCode"));
dto.setOrderProInfos(readProducts(dto.getOrderId(), record.getString("extJson")));
return dto;
}
private List<OrderProInfoDTO> readProducts(String orderId, String extJson) {
List<PointsMallOrderSubProduct> products = dao.query(PointsMallOrderSubProduct.class, Cnd.where("orderSubId", "=", orderId).and("delFlag", "=", false));
if (!products.isEmpty()) {
return products.stream().map(this::toProductDTO).collect(Collectors.toList());
}
if (StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson)) {
return Collections.emptyList();
}
return JSONUtil.toList(extJson, OrderProInfoDTO.class);
}
private OrderProInfoDTO toProductDTO(PointsMallOrderSubProduct product) {
OrderProInfoDTO dto = new OrderProInfoDTO();
dto.setSku(product.getSku());
dto.setNumber(product.getNumber());
dto.setPrice(product.getPrice());
dto.setName(product.getName());
dto.setTaxRate(product.getTaxRate());
dto.setPicInfo(StrUtil.isBlank(product.getPicInfo()) ? Collections.emptyList() : Collections.singletonList(product.getPicInfo()));
dto.setFileId(product.getSku());
dto.setFilePath(product.getPicInfo());
return dto;
}
private InvoiceInfoDTO toInvoiceInfoDTO(PointsMallInvoiceInfo info) {
InvoiceInfoDTO dto = new InvoiceInfoDTO();
dto.setInvoiceId(info.getInvoiceId());
dto.setInvoiceCode(info.getInvoiceCode());
dto.setInvoiceDate(toLocalDate(info.getInvoiceDate()));
dto.setInvoiceNakeAmount(info.getInvoiceNakeAmount());
dto.setInvoiceTaxRate(info.getInvoiceTaxRate());
dto.setInvoiceTaxAmount(info.getInvoiceTaxAmount());
dto.setInvoiceAmount(info.getInvoiceAmount());
dto.setInvoiceType(info.getInvoiceType());
dto.setUrl(info.getUrl());
dto.setImageEncode(info.getImageEncode());
dto.setFileType(info.getFileType());
dto.setCheckCode(info.getCheckCode());
dto.setInvoiceAddress(info.getInvoiceAddress());
return dto;
}
private Date parseDate(String value) {
if (StrUtil.isBlank(value)) {
return null;
}
return DateUtil.parse(value.replace("T", " "));
}
private String formatDate(Object value) {
if (value == null) {
return null;
}
return DateUtil.format(Convert.toDate(value), "yyyy-MM-dd HH:mm:ss");
}
private LocalDate toLocalDate(Date date) {
return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
private List<String> splitIds(String value) {
if (StrUtil.isBlank(value)) {
return Collections.emptyList();
}
return Arrays.stream(value.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.toList());
}
private BigDecimal nvl(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
private BigDecimal nvl(BigDecimal value, BigDecimal defaultValue) {
return value == null ? defaultValue : value;
}
private BigDecimal decimal(Object value) {
return Convert.toBigDecimal(value, BigDecimal.ZERO);
}
private Integer parseInteger(String value) {
if (StrUtil.isBlank(value)) {
return null;
}
return Convert.toInt(value, null);
return mallPayFinishNoticeService.payCompletion(dto);
}
}
@@ -0,0 +1,241 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service.support;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.entity.Record;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 商城桥接公共支撑能力。
*
* <p>只放通用转换和常量,具体接口业务放在各自服务中。</p>
*/
public abstract class AbstractMallBridgeSupport {
protected static final long ACCESS_EXPIRE_SECONDS = 3600;
protected static final long REFRESH_EXPIRE_SECONDS = 86400L * 7;
protected static final int ORDER_PENDING_PAY = 0;
protected static final int ORDER_FINISH_PAY = 1;
protected static final int ORDER_IN_PROGRESS = 2;
protected static final int ORDER_CANCELLED = 3;
protected static final int ORDER_RECEIVED = 4;
protected static final int ORDER_COMPLETED = 5;
protected static final int ORDER_EXCHANGE = 6;
protected static final int ORDER_REFUNDED = 7;
protected static final int ORDER_ABOLISH = 8;
protected final Dao dao;
protected AbstractMallBridgeSupport(Dao dao) {
this.dao = dao;
}
protected PointsMallOrderMain toMain(String supplierId, String userId, OrderInfoDTO orderInfo, Integer state) {
PointsMallOrderMain main = new PointsMallOrderMain();
main.setOrderId(orderInfo.getOrderId());
main.setSupplierId(supplierId);
main.setOrderNumberId(orderInfo.getOrderNumberId());
main.setUserId(userId);
main.setAplName(orderInfo.getAplName());
main.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
main.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
main.setDeliveryTime(parseDate(orderInfo.getDeliveryTime()));
main.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime()));
main.setOrderCompleteTime(parseDate(orderInfo.getCompleteTime()));
main.setDeliveryStatus(orderInfo.getDeliveryStatus());
main.setFreightCost(nvl(orderInfo.getFreightCost()));
main.setRefund(nvl(orderInfo.getRefund()));
main.setLogisticsOrderId(orderInfo.getLogisticsOrderId());
main.setCrrgBsnNm(orderInfo.getCrrgBsnNm());
main.setOrderState(state);
main.setTotalPrice(nvl(orderInfo.getTotalPrice()));
main.setWPayOrAPay(nvl(orderInfo.getWPayOrAPay()));
main.setPointsPrice(nvl(orderInfo.getPointsPrice()));
main.setInvoiceCode(orderInfo.getInvoiceCode());
main.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos() == null ? Collections.emptyList() : orderInfo.getOrderProInfos()));
main.setCashPaid(0);
return main;
}
protected PointsMallOrderSub toSub(PointsMallOrderMain main, String subOrderId) {
PointsMallOrderSub sub = new PointsMallOrderSub();
sub.setOrderId(StrUtil.blankToDefault(subOrderId, main.getOrderId()));
sub.setOrderNumberId(main.getOrderNumberId());
sub.setMainOrderId(main.getOrderId());
sub.setSupplierId(main.getSupplierId());
sub.setUserId(main.getUserId());
sub.setAplName(main.getAplName());
sub.setOrderCreateTime(main.getOrderCreateTime());
sub.setOrderUpdateTime(new Date());
sub.setDeliveryTime(main.getDeliveryTime());
sub.setOrderFinishTime(main.getOrderFinishTime());
sub.setOrderCompleteTime(main.getOrderCompleteTime());
sub.setDeliveryStatus(parseInteger(main.getDeliveryStatus()));
sub.setFreightCost(main.getFreightCost());
sub.setRefund(main.getRefund());
sub.setLogisticsOrderId(main.getLogisticsOrderId());
sub.setCrrgBsnNm(main.getCrrgBsnNm());
sub.setOrderState(main.getOrderState());
sub.setTotalPrice(main.getTotalPrice());
sub.setWPayOrAPay(main.getWPayOrAPay());
sub.setPointsPrice(main.getPointsPrice());
sub.setInvoiceCode(main.getInvoiceCode());
sub.setExtJson(main.getExtJson());
sub.setReconciliationStatus(0);
sub.setInvoiceStatus(0);
return sub;
}
protected void insertProducts(String orderSubId, List<OrderProInfoDTO> products) {
if (products == null) {
return;
}
for (OrderProInfoDTO item : products) {
PointsMallOrderSubProduct product = new PointsMallOrderSubProduct();
product.setOrderSubId(orderSubId);
product.setSku(item.getSku());
product.setNumber(item.getNumber());
product.setPrice(item.getPrice());
product.setName(item.getName());
product.setTaxRate(item.getTaxRate());
product.setPicInfo(item.getPicInfo() == null || item.getPicInfo().isEmpty() ? null : item.getPicInfo().get(0));
dao.insert(product);
}
}
protected OrderInfoDTO toOrderInfo(Record record) {
OrderInfoDTO dto = new OrderInfoDTO();
dto.setOrderId(record.getString("orderId"));
dto.setOrderNumberId(record.getString("orderNumberId"));
dto.setSupplierId(record.getString("supplierId"));
dto.setAplName(record.getString("aplName"));
dto.setCreateTime(formatDate(record.get("orderCreateTime")));
dto.setUpdateTime(formatDate(record.get("orderUpdateTime")));
dto.setDeliveryTime(formatDate(record.get("deliveryTime")));
dto.setOrderFinishTime(formatDate(record.get("orderFinishTime")));
dto.setCompleteTime(formatDate(record.get("orderCompleteTime")));
dto.setDeliveryStatus(record.getString("deliveryStatus"));
dto.setFreightCost(decimal(record.get("freightCost")));
dto.setWPayOrAPayFreightCost(decimal(record.get("wPayOrAPayFreightCost")));
dto.setRefund(decimal(record.get("refund")));
dto.setWPayOrAPayRefund(decimal(record.get("wPayOrAPayRefund")));
dto.setLogisticsOrderId(record.getString("logisticsOrderId"));
dto.setCrrgBsnNm(record.getString("crrgBsnNm"));
dto.setOrderState(record.getInt("orderState"));
dto.setTotalPrice(decimal(record.get("totalPrice")));
dto.setWPayOrAPay(decimal(record.get("wPayOrAPay")));
dto.setPointsPrice(decimal(record.get("pointsPrice")));
dto.setInvoiceCode(record.getString("invoiceCode"));
dto.setOrderProInfos(readProducts(dto.getOrderId(), record.getString("extJson")));
return dto;
}
protected List<OrderProInfoDTO> readProducts(String orderId, String extJson) {
List<PointsMallOrderSubProduct> products = dao.query(PointsMallOrderSubProduct.class, Cnd.where("orderSubId", "=", orderId).and("delFlag", "=", false));
if (!products.isEmpty()) {
return products.stream().map(this::toProductDTO).collect(Collectors.toList());
}
if (StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson)) {
return Collections.emptyList();
}
return JSONUtil.toList(extJson, OrderProInfoDTO.class);
}
protected OrderProInfoDTO toProductDTO(PointsMallOrderSubProduct product) {
OrderProInfoDTO dto = new OrderProInfoDTO();
dto.setSku(product.getSku());
dto.setNumber(product.getNumber());
dto.setPrice(product.getPrice());
dto.setName(product.getName());
dto.setTaxRate(product.getTaxRate());
dto.setPicInfo(StrUtil.isBlank(product.getPicInfo()) ? Collections.emptyList() : Collections.singletonList(product.getPicInfo()));
dto.setFileId(product.getSku());
dto.setFilePath(product.getPicInfo());
return dto;
}
protected InvoiceInfoDTO toInvoiceInfoDTO(PointsMallInvoiceInfo info) {
InvoiceInfoDTO dto = new InvoiceInfoDTO();
dto.setInvoiceId(info.getInvoiceId());
dto.setInvoiceCode(info.getInvoiceCode());
dto.setInvoiceDate(toLocalDate(info.getInvoiceDate()));
dto.setInvoiceNakeAmount(info.getInvoiceNakeAmount());
dto.setInvoiceTaxRate(info.getInvoiceTaxRate());
dto.setInvoiceTaxAmount(info.getInvoiceTaxAmount());
dto.setInvoiceAmount(info.getInvoiceAmount());
dto.setInvoiceType(info.getInvoiceType());
dto.setUrl(info.getUrl());
dto.setImageEncode(info.getImageEncode());
dto.setFileType(info.getFileType());
dto.setCheckCode(info.getCheckCode());
dto.setInvoiceAddress(info.getInvoiceAddress());
return dto;
}
protected Date parseDate(String value) {
if (StrUtil.isBlank(value)) {
return null;
}
return DateUtil.parse(value.replace("T", " "));
}
protected String formatDate(Object value) {
if (value == null) {
return null;
}
return DateUtil.format(Convert.toDate(value), "yyyy-MM-dd HH:mm:ss");
}
protected LocalDate toLocalDate(Date date) {
return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
protected List<String> splitIds(String value) {
if (StrUtil.isBlank(value)) {
return Collections.emptyList();
}
return Arrays.stream(value.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.toList());
}
protected BigDecimal nvl(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
protected BigDecimal nvl(BigDecimal value, BigDecimal defaultValue) {
return value == null ? defaultValue : value;
}
protected BigDecimal decimal(Object value) {
return Convert.toBigDecimal(value, BigDecimal.ZERO);
}
protected Integer parseInteger(String value) {
if (StrUtil.isBlank(value)) {
return null;
}
return Convert.toInt(value, null);
}
}
@@ -0,0 +1,31 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.util;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
/**
* 积分商城桥接国密工具。
*/
public class MallBridgeCryptoUtil {
private MallBridgeCryptoUtil() {
}
/**
* 生成供应商开放接口签名。
*/
public static String generateSign(String supplierId, String clientId, long timestamp) {
return SmUtil.sm3(supplierId + clientId + timestamp);
}
/**
* SM4-CBC 加密,返回 Base64 密文。
*/
public static String sm4CbcEncrypt(String data, String keyHex, String ivHex) {
SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(keyHex), HexUtil.decodeHex(ivHex));
return sm4.encryptBase64(data);
}
}
@@ -72,6 +72,6 @@ public class PointsMallMessage extends BaseModel implements Serializable {
private Date planPushTime;
@Column
@Comment("状态")
@Comment("状态:0待推送,1已推送,2待重推,3推送失败")
private Integer status;
}
@@ -6,7 +6,6 @@ import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate;
import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessagePageParam;
import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessageTemplatePageParam;
import java.util.List;
public interface PointsMallMessageService extends BaseService<PointsMallMessageTemplate> {
@@ -5,6 +5,8 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate;
import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessagePageParam;
@@ -13,14 +15,19 @@ import com.budwk.app.zhgh.pointsmall.message.service.PointsMallMessageService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class PointsMallMessageServiceImpl extends BaseServiceImpl<PointsMallMessageTemplate> implements PointsMallMessageService {
@Inject
private GlobalMessageSendService globalMessageSendService;
public PointsMallMessageServiceImpl(Dao dao) {
super(dao);
}
@@ -64,7 +71,7 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl<PointsMallMess
PointsMallMessageTemplate template = fetch(id);
return template != null && !Integer.valueOf(5).equals(template.getType());
})
.toList();
.collect(Collectors.toList());
if (canDelete.isEmpty()) {
throw new IllegalArgumentException("模板正在使用中或为业务消息,无法删除");
}
@@ -74,6 +81,77 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl<PointsMallMess
@Override
public Pagination<PointsMallMessage> messagePage(PointsMallMessagePageParam param) {
Cnd cnd = messageCnd(param);
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessage.class, cnd);
}
@Override
public void resend(List<String> ids) {
if (CollUtil.isEmpty(ids)) {
throw new IllegalArgumentException("请选择消息记录");
}
List<PointsMallMessage> messages = dao().query(PointsMallMessage.class, Cnd.where("id", "in", ids)
.and("status", "in", Arrays.asList(0, 2, 3)).and("delFlag", "=", false));
if (CollUtil.isEmpty(messages)) {
throw new IllegalArgumentException("没有可推送的消息记录");
}
for (PointsMallMessage message : messages) {
pushToGlobalMessage(message);
}
}
@Override
public void resendAll(PointsMallMessagePageParam param) {
Cnd cnd = messageCnd(param);
cnd.and("status", "in", Arrays.asList(0, 2, 3));
List<PointsMallMessage> messages = dao().query(PointsMallMessage.class, cnd);
for (PointsMallMessage message : messages) {
pushToGlobalMessage(message);
}
}
private void pushToGlobalMessage(PointsMallMessage message) {
try {
if (StrUtil.isBlank(message.getTouser())) {
throw new IllegalArgumentException("目标用户不能为空");
}
List<String> loginNames = StrUtil.splitTrim(message.getTouser(), ",");
List<Sys_user> users = dao().query(Sys_user.class, Cnd.where("loginname", "in", loginNames)
.and("disabled", "=", false).and("delFlag", "=", false));
if (CollUtil.isEmpty(users)) {
throw new IllegalArgumentException("未找到目标用户");
}
List<String> receiverIds = users.stream().map(Sys_user::getId).collect(Collectors.toList());
PointsMallMessageTemplate template = StrUtil.isBlank(message.getTemplateId()) ? null : fetch(message.getTemplateId());
String title = template == null || StrUtil.isBlank(template.getName()) ? "积分商城消息" : template.getName();
String content = StrUtil.blankToDefault(message.getContent(), template == null ? "" : template.getContent());
if (StrUtil.isBlank(content)) {
throw new IllegalArgumentException("消息内容不能为空");
}
globalMessageSendService.sendMessage(title, content, 2, receiverIds, null);
dao().update(PointsMallMessage.class, Chain.make("status", 1), Cnd.where("id", "=", message.getId()));
} catch (Exception e) {
dao().update(PointsMallMessage.class, Chain.make("status", 3), Cnd.where("id", "=", message.getId()));
throw new IllegalArgumentException("消息推送失败:" + e.getMessage(), e);
}
}
private Cnd templateCnd(PointsMallMessageTemplatePageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getName())) {
cnd.and("name", "like", "%" + param.getName() + "%");
}
if (param.getType() != null) {
cnd.and("type", "=", param.getType());
}
if (param.getStatus() != null) {
cnd.and("status", "=", param.getStatus());
}
return cnd;
}
private Cnd messageCnd(PointsMallMessagePageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getTemplateId())) {
cnd.and("templateId", "=", param.getTemplateId());
@@ -91,41 +169,6 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl<PointsMallMess
if (StrUtil.isNotBlank(param.getCreateTimeEnd())) {
cnd.and("createdAt", "<=", DateUtil.parse(param.getCreateTimeEnd()).getTime());
}
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessage.class, cnd);
}
@Override
public void resend(List<String> ids) {
if (CollUtil.isEmpty(ids)) {
throw new IllegalArgumentException("请选择消息记录");
}
dao().update(PointsMallMessage.class, Chain.make("status", 2), Cnd.where("id", "in", ids).and("status", "in", Arrays.asList(0, 3)));
}
@Override
public void resendAll(PointsMallMessagePageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false).and("status", "in", Arrays.asList(0, 3));
if (StrUtil.isNotBlank(param.getTemplateId())) {
cnd.and("templateId", "=", param.getTemplateId());
}
if (StrUtil.isNotBlank(param.getTouser())) {
cnd.and("touser", "like", "%" + param.getTouser() + "%");
}
dao().update(PointsMallMessage.class, Chain.make("status", 2), cnd);
}
private Cnd templateCnd(PointsMallMessageTemplatePageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getName())) {
cnd.and("name", "like", "%" + param.getName() + "%");
}
if (param.getType() != null) {
cnd.and("type", "=", param.getType());
}
if (param.getStatus() != null) {
cnd.and("status", "=", param.getStatus());
}
return cnd;
}
@@ -152,6 +195,6 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl<PointsMallMess
.map(String::trim)
.filter(StrUtil::isNotBlank)
.map(Integer::valueOf)
.toList();
.collect(Collectors.toList());
}
}
@@ -0,0 +1,30 @@
package com.budwk.app.zhgh.pointsmall.order.param;
import lombok.Data;
/**
* 订单确认收货入参。
*/
@Data
public class PointsMallMobileOrderReceivedParam {
/**
* 用户工号。
*/
private String emplid;
/**
* 供应商ID。
*/
private String supplierId;
/**
* 订单ID。
*/
private String orderId;
/**
* 订单类型。
*/
private String orderType;
}
@@ -5,6 +5,7 @@ import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderExchange;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderReceivedParam;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderPageParam;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallOrderPageParam;
@@ -24,6 +25,8 @@ public interface PointsMallOrderService extends BaseService<PointsMallOrderMain>
Record supplierOrderUrl(String supplierId, String orderId, Integer orderType);
Boolean userOrderReceive(PointsMallMobileOrderReceivedParam param, String defaultUserId);
List<Record> listExchange(String mainOrderId);
List<Record> listSubProducts(String orderId);
@@ -7,8 +7,10 @@ import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderExchange;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderPageParam;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderReceivedParam;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallOrderPageParam;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -16,7 +18,10 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.Arrays;
import java.util.Date;
@@ -25,6 +30,9 @@ import java.util.List;
@IocBean(args = {"refer:dao"})
public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderMain> implements PointsMallOrderService {
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
public PointsMallOrderServiceImpl(Dao dao) {
super(dao);
}
@@ -39,7 +47,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
cnd.and("m.orderId", "like", "%" + param.getOrderId() + "%");
}
if (StrUtil.isNotBlank(param.getUserId())) {
cnd.and("m.userId", "like", "%" + param.getUserId() + "%");
cnd.and("u.loginname", "like", "%" + param.getUserId() + "%");
}
if (StrUtil.isNotBlank(param.getOrderState())) {
cnd.and("m.orderState", "=", param.getOrderState());
@@ -48,7 +56,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
cnd.and("m.logisticsOrderId", "like", "%" + param.getLogisticsOrderId() + "%");
}
cnd.desc("m.orderCreateTime");
Sql sql = Sqls.create("select m.*, s.supplierName from points_mall_order_main m left join points_mall_supplier s on m.supplierId = s.supplierId and s.delFlag = 0 $condition");
Sql sql = Sqls.create("select m.*, u.loginname as userAccount, s.supplierName from points_mall_order_main m left join sys_user u on m.userId = u.id and u.delFlag = 0 left join points_mall_supplier s on m.supplierId = s.supplierId and s.delFlag = 0 $condition");
sql.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@@ -63,7 +71,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
cnd.and("o.orderState", "=", param.getOrderState());
}
cnd.desc("o.orderCreateTime");
Sql sql = Sqls.create("select o.*, s.supplierName from points_mall_order_sub o left join points_mall_supplier s on o.supplierId = s.supplierId and s.delFlag = 0 $condition");
Sql sql = Sqls.create("select o.*, u.loginname as userAccount, s.supplierName from points_mall_order_sub o left join sys_user u on o.userId = u.id and u.delFlag = 0 left join points_mall_supplier s on o.supplierId = s.supplierId and s.delFlag = 0 $condition");
sql.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@@ -87,7 +95,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
cnd.desc("o.orderCreateTime");
Sql sql = Sqls.create("select o.*, s.supplierName, s.supplierOrderUrl, s.supplierReturnOrderUrl from points_mall_order_sub o left join points_mall_supplier s on o.supplierId = s.supplierId and s.delFlag = 0 $condition");
sql.setCondition(cnd);
Pagination<Record> page = listPageMap(param.getPageNumber(), param.getPageSize(), sql);
Pagination<Record> page = listPage(param.getPageNumber(), param.getPageSize(), sql);
for (Record item : page.getList()) {
item.put("productImgList", productImages(item.getString("orderId")));
}
@@ -122,6 +130,29 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
return record;
}
@Override
public Boolean userOrderReceive(PointsMallMobileOrderReceivedParam param, String defaultUserId) {
String userId = StrUtil.blankToDefault(param.getEmplid(), defaultUserId);
PointsMallOrderSub sub = dao().fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", param.getOrderId())
.and("userId", "=", userId)
.and("supplierId", "=", param.getSupplierId())
.and("delFlag", "=", false));
if (sub == null) {
throw new RuntimeException("order not exists");
}
if (!Integer.valueOf(2).equals(sub.getOrderState())) {
throw new RuntimeException("order does not support receive");
}
Trans.exec((Atom) () -> {
sub.setOrderState(5);
sub.setOrderCompleteTime(new Date());
sub.setOrderUpdateTime(new Date());
dao().updateIgnoreNull(sub);
pointsMallPointsLockService.deductCompletedSub(sub);
});
return true;
}
@Override
public List<Record> listExchange(String mainOrderId) {
Cnd cnd = Cnd.where("e.delFlag", "=", false);
@@ -129,7 +160,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderM
cnd.and("e.orderMainId", "=", mainOrderId);
}
cnd.desc("e.orderCreateTime");
Sql sql = Sqls.create("select e.*, s.supplierName from points_mall_order_exchange e left join points_mall_supplier s on e.supplierId = s.supplierId and s.delFlag = 0 $condition");
Sql sql = Sqls.create("select e.*, u.loginname as userAccount, s.supplierName from points_mall_order_exchange e left join sys_user u on e.userId = u.id and u.delFlag = 0 left join points_mall_supplier s on e.supplierId = s.supplierId and s.delFlag = 0 $condition");
sql.setCondition(cnd);
return list(sql);
}
@@ -0,0 +1,20 @@
package com.budwk.app.zhgh.pointsmall.points.enums;
import lombok.Getter;
/**
* 积分锁定状态。
*/
@Getter
public enum PointsMallPointLockStatusEnum {
LOCK(0, "锁定"),
UNLOCK(1, "释放");
private final Integer code;
private final String description;
PointsMallPointLockStatusEnum(Integer code, String description) {
this.code = code;
this.description = description;
}
}
@@ -0,0 +1,62 @@
package com.budwk.app.zhgh.pointsmall.points.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 主单积分锁定表。
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_main_points_lock")
@Comment("积分商城主单积分锁定")
public class PointsMallMainPointsLock extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("主订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderId;
@Column
@Comment("供应商ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("锁定积分")
@ColDefine(customType = "decimal(18,2)")
private BigDecimal lockAmount;
@Column
@Comment("积分状态:0锁定,1释放")
private Integer status;
@Column
@Comment("锁定时间")
private Date lockTime;
}
@@ -0,0 +1,66 @@
package com.budwk.app.zhgh.pointsmall.points.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 子单积分变动锁定表。
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_sub_points_lock")
@Comment("积分商城子单积分变动锁定")
public class PointsMallSubPointsLock extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("主订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String mainOrderId;
@Column
@Comment("子订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String subOrderId;
@Column
@Comment("供应商ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("变动积分,扣减为负数,返还为正数")
@ColDefine(customType = "decimal(18,2)")
private BigDecimal changePoints;
@Column
@Comment("子单状态")
private Integer orderSubStatus;
@Column
@Comment("锁定时间")
private Date lockTime;
}
@@ -0,0 +1,22 @@
package com.budwk.app.zhgh.pointsmall.points.service;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import java.math.BigDecimal;
import java.util.List;
public interface PointsMallPointsLockService {
BigDecimal sumLocked(String userId);
void lockMain(PointsMallOrderMain mainOrder, BigDecimal lockAmount);
void releaseMain(String userId, String supplierId, String orderId);
void createSubLocks(PointsMallOrderMain mainOrder, List<PointsMallOrderSub> subOrders);
void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund);
void deductCompletedSub(PointsMallOrderSub subOrder);
}
@@ -0,0 +1,213 @@
package com.budwk.app.zhgh.pointsmall.points.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.points.enums.PointsMallPointLockStatusEnum;
import com.budwk.app.zhgh.pointsmall.points.models.PointsMallMainPointsLock;
import com.budwk.app.zhgh.pointsmall.points.models.PointsMallSubPointsLock;
import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPoints;
import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPointsLog;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* 积分锁定、释放和实际扣减服务。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class PointsMallPointsLockServiceImpl implements PointsMallPointsLockService {
private final Dao dao;
public PointsMallPointsLockServiceImpl(Dao dao) {
this.dao = dao;
}
@Override
public BigDecimal sumLocked(String userId) {
if (StrUtil.isBlank(userId)) {
return BigDecimal.ZERO;
}
List<PointsMallMainPointsLock> list = dao.query(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId)
.and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()).and("delFlag", "=", false));
return list.stream().map(PointsMallMainPointsLock::getLockAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
}
@Override
public void lockMain(PointsMallOrderMain mainOrder, BigDecimal lockAmount) {
if (mainOrder == null || StrUtil.hasBlank(mainOrder.getUserId(), mainOrder.getOrderId(), mainOrder.getSupplierId())) {
throw new IllegalArgumentException("主单积分锁定参数不能为空");
}
BigDecimal amount = nvl(lockAmount);
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
PointsMallMainPointsLock exists = dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", mainOrder.getUserId())
.and("supplierId", "=", mainOrder.getSupplierId())
.and("orderId", "=", mainOrder.getOrderId())
.and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode())
.and("delFlag", "=", false));
if (exists != null) {
throw new IllegalArgumentException("该订单已经确认购买,请不要重复确认购买");
}
PointsMallMainPointsLock lock = new PointsMallMainPointsLock();
lock.setUserId(mainOrder.getUserId());
lock.setSupplierId(mainOrder.getSupplierId());
lock.setOrderId(mainOrder.getOrderId());
lock.setLockAmount(amount);
lock.setStatus(PointsMallPointLockStatusEnum.LOCK.getCode());
lock.setLockTime(new Date());
dao.insert(lock);
log.info("积分商城主单积分锁定完成,userId={}supplierId={}orderId={}lockAmount={}",
mainOrder.getUserId(), mainOrder.getSupplierId(), mainOrder.getOrderId(), amount);
}
@Override
public void releaseMain(String userId, String supplierId, String orderId) {
if (StrUtil.hasBlank(userId, supplierId, orderId)) {
return;
}
dao.update(PointsMallMainPointsLock.class,
Chain.make("status", PointsMallPointLockStatusEnum.UNLOCK.getCode()).add("delFlag", true),
Cnd.where("userId", "=", userId)
.and("supplierId", "=", supplierId)
.and("orderId", "=", orderId)
.and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode())
.and("delFlag", "=", false));
log.info("积分商城主单积分释放完成,userId={}supplierId={}orderId={}", userId, supplierId, orderId);
}
@Override
public void createSubLocks(PointsMallOrderMain mainOrder, List<PointsMallOrderSub> subOrders) {
if (mainOrder == null || subOrders == null || subOrders.isEmpty()) {
return;
}
for (PointsMallOrderSub sub : subOrders) {
if (sub == null || StrUtil.isBlank(sub.getOrderId())) {
continue;
}
PointsMallSubPointsLock exists = dao.fetch(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", sub.getSupplierId())
.and("subOrderId", "=", sub.getOrderId()).and("delFlag", "=", false));
if (exists != null) {
continue;
}
PointsMallSubPointsLock lock = new PointsMallSubPointsLock();
lock.setUserId(mainOrder.getUserId());
lock.setMainOrderId(sub.getMainOrderId());
lock.setSubOrderId(sub.getOrderId());
lock.setSupplierId(sub.getSupplierId());
lock.setChangePoints(nvl(sub.getPointsPrice()).negate());
lock.setOrderSubStatus(sub.getOrderState());
lock.setLockTime(new Date());
dao.insert(lock);
}
log.info("积分商城子单积分锁定记录生成完成,supplierId={}mainOrderId={}subOrderCount={}",
mainOrder.getSupplierId(), mainOrder.getOrderId(), subOrders.size());
}
@Override
public void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund) {
if (subOrder == null || StrUtil.hasBlank(subOrder.getSupplierId(), subOrder.getOrderId(), subOrder.getMainOrderId())) {
return;
}
PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId());
if (mainLock == null) {
return;
}
PointsMallSubPointsLock lastSubLock = latestSubLock(subOrder.getSupplierId(), subOrder.getOrderId());
if (lastSubLock == null) {
return;
}
BigDecimal returnPoint = Integer.valueOf(7).equals(orderState) ? nvl(refund) : nvl(lastSubLock.getChangePoints()).negate();
if (returnPoint.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
PointsMallSubPointsLock insert = new PointsMallSubPointsLock();
insert.setUserId(subOrder.getUserId());
insert.setMainOrderId(subOrder.getMainOrderId());
insert.setSubOrderId(subOrder.getOrderId());
insert.setSupplierId(subOrder.getSupplierId());
insert.setChangePoints(returnPoint);
insert.setOrderSubStatus(orderState);
insert.setLockTime(new Date());
dao.insert(insert);
BigDecimal after = nvl(mainLock.getLockAmount()).subtract(returnPoint);
if (after.compareTo(BigDecimal.ZERO) < 0) {
after = BigDecimal.ZERO;
}
dao.update(PointsMallMainPointsLock.class, Chain.make("lockAmount", after), Cnd.where("id", "=", mainLock.getId()));
log.info("积分商城子单积分释放完成,supplierId={}mainOrderId={}subOrderId={}returnPoint={}",
subOrder.getSupplierId(), subOrder.getMainOrderId(), subOrder.getOrderId(), returnPoint);
}
@Override
public void deductCompletedSub(PointsMallOrderSub subOrder) {
if (subOrder == null || StrUtil.hasBlank(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId(), subOrder.getOrderId())) {
return;
}
PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId());
if (mainLock == null) {
return;
}
BigDecimal subtract = nvl(subOrder.getPointsPrice()).subtract(nvl(subOrder.getRefund()));
if (subtract.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
PointsMallUserPoints userPoints = dao.fetch(PointsMallUserPoints.class, Cnd.where("userId", "=", subOrder.getUserId()).and("delFlag", "=", false));
if (userPoints == null) {
throw new IllegalArgumentException("用户积分不存在");
}
BigDecimal availableAfter = nvl(userPoints.getAvailablePoints()).subtract(subtract);
if (availableAfter.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("积分不足,无法扣减");
}
dao.update(PointsMallUserPoints.class,
Chain.make("availablePoints", availableAfter).add("usedPoints", nvl(userPoints.getUsedPoints()).add(subtract)),
Cnd.where("id", "=", userPoints.getId()));
PointsMallUserPointsLog logRecord = new PointsMallUserPointsLog();
logRecord.setUserId(subOrder.getUserId());
logRecord.setChangeAmount(subtract);
logRecord.setChangeType("DEDUCTION");
logRecord.setSubOrderId(subOrder.getOrderId());
logRecord.setRemark("积分商城订单确认完成扣减");
dao.insert(logRecord);
BigDecimal lockAfter = nvl(mainLock.getLockAmount()).subtract(subtract);
if (lockAfter.compareTo(BigDecimal.ZERO) < 0) {
lockAfter = BigDecimal.ZERO;
}
dao.update(PointsMallMainPointsLock.class, Chain.make("lockAmount", lockAfter), Cnd.where("id", "=", mainLock.getId()));
log.info("积分商城子单确认完成扣减积分,userId={}supplierId={}subOrderId={}points={}",
subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getOrderId(), subtract);
}
private PointsMallMainPointsLock activeMainLock(String userId, String supplierId, String orderId) {
return dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId)
.and("supplierId", "=", supplierId)
.and("orderId", "=", orderId)
.and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode())
.and("delFlag", "=", false));
}
private PointsMallSubPointsLock latestSubLock(String supplierId, String subOrderId) {
List<PointsMallSubPointsLock> list = dao.query(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", supplierId)
.and("subOrderId", "=", subOrderId).and("delFlag", "=", false).desc("createdAt"));
return list.isEmpty() ? null : list.get(0);
}
private BigDecimal nvl(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
}
@@ -10,6 +10,7 @@ import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsBatchParam
import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsChangeParam;
import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsLogPageParam;
import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsPageParam;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -17,6 +18,7 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
@@ -28,6 +30,9 @@ import java.util.List;
@IocBean(args = {"refer:dao"})
public class PointsMallUserPointsServiceImpl extends BaseServiceImpl<PointsMallUserPoints> implements PointsMallUserPointsService {
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
public PointsMallUserPointsServiceImpl(Dao dao) {
super(dao);
}
@@ -47,10 +52,16 @@ public class PointsMallUserPointsServiceImpl extends BaseServiceImpl<PointsMallU
n.name as unitName,
ifnull(p.totalPoints, 0) as totalPoints,
ifnull(p.usedPoints, 0) as usedPoints,
ifnull(p.availablePoints, 0) as availablePoints,
ifnull(p.lockPoints, 0) as lockPoints
greatest(ifnull(p.availablePoints, 0) - ifnull(l.lockPoints, 0), 0) as availablePoints,
ifnull(l.lockPoints, 0) as lockPoints
from sys_user u
left join points_mall_user_points p on p.userId = u.id and p.delFlag = false
left join (
select userId, ifnull(sum(lockAmount), 0) as lockPoints
from points_mall_main_points_lock
where status = 0 and delFlag = false
group by userId
) l on l.userId = u.id
left join sys_unit n on n.id = u.unitid
$condition
""");
@@ -94,7 +105,10 @@ public class PointsMallUserPointsServiceImpl extends BaseServiceImpl<PointsMallU
return BigDecimal.ZERO;
}
PointsMallUserPoints points = fetch(Cnd.where("userId", "=", userId).and("delFlag", "=", false));
return points == null || points.getAvailablePoints() == null ? BigDecimal.ZERO : points.getAvailablePoints();
BigDecimal total = points == null || points.getAvailablePoints() == null ? BigDecimal.ZERO : points.getAvailablePoints();
BigDecimal locked = pointsMallPointsLockService.sumLocked(userId);
BigDecimal available = total.subtract(locked);
return available.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : available;
}
@Override
+8
View File
@@ -8,6 +8,14 @@ server:
port: 80
host: 0.0.0.0
spdb:
bab7:
url: http://localhost:8888/supplierOpenApi/pointOrder/queryOrderInfo
sm4cbc:
keyHex: 00112233445566778899aabbccddeeff
ivHex: 0102030405060708090a0b0c0d0e0f10
jetty:
contextPath: /
threadpool:
@@ -33,7 +33,7 @@ layout("/layouts/platform.html"){
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单号" min-width="160" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单编码" min-width="150" prop="orderNumberId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="120" prop="userAccount" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="140" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="应用名称" min-width="120" prop="aplName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="创建时间" min-width="160" prop="orderCreateTime" show-overflow-tooltip></el-table-column>
@@ -73,7 +73,7 @@ layout("/layouts/platform.html"){
<el-table-column align="center" label="子订单号" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单编码" min-width="150" prop="orderNumberId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="130" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="110" prop="userId"></el-table-column>
<el-table-column align="center" label="工号" min-width="110" prop="userAccount"></el-table-column>
<el-table-column align="center" label="创建时间" min-width="160" prop="orderCreateTime"></el-table-column>
<el-table-column align="center" label="订单状态" min-width="100">
<template slot-scope="{row}">
@@ -121,7 +121,7 @@ layout("/layouts/platform.html"){
<el-table :data="exchangeList" border size="small" v-loading="exchangeLoading">
<el-table-column align="center" label="售后订单号" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="130" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="工号" min-width="110" prop="userId"></el-table-column>
<el-table-column align="center" label="工号" min-width="110" prop="userAccount"></el-table-column>
<el-table-column align="center" label="创建时间" min-width="160" prop="orderCreateTime"></el-table-column>
<el-table-column align="center" label="状态" min-width="100">
<template slot-scope="{row}">