Merge remote-tracking branch 'refs/remotes/origin/dev-mall' into release_20260829

# Conflicts:
#	src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java
#	src/main/resources/views/platform/zhghh5/sys/home/msg.js
This commit is contained in:
2026-08-29 09:12:07 +08:00
157 changed files with 13694 additions and 545 deletions
@@ -45,4 +45,24 @@ public class GlobalMessageSendRequest {
*/
private List<String> receiverIds;
/**
* 是否系统发送
*/
private Boolean systemSend;
/**
* 发送人ID
*/
private String senderId;
/**
* 发送人名称
*/
private String senderName;
/**
* 发送人工号
*/
private String senderLoginName;
}
@@ -97,6 +97,66 @@ public class GlobalMessageSendService {
sendMessage(request);
}
/**
* 发送系统消息(不依赖当前登录token)
*/
public void sendSystemMessage(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
GlobalMessageSendRequest request = new GlobalMessageSendRequest();
request.setTitle(title);
request.setContent(content);
request.setType(type);
request.setReceiverIds(receiverIds);
request.setConfig(config);
request.setSystemSend(true);
request.setSenderId("system");
request.setSenderName("系统");
request.setSenderLoginName("system");
sendMessage(request);
}
/**
* 发送本地系统消息(不依赖当前登录token)
*/
public void sendLocalSystemMessage(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
GlobalMessageSendRequest request = new GlobalMessageSendRequest();
request.setTitle(title);
request.setContent(content);
request.setType(type);
request.setReceiverIds(receiverIds);
request.setConfig(config);
request.setSystemSend(true);
request.setSenderId("system");
request.setSenderName("系统");
request.setSenderLoginName("system");
sendLocalMessage(request);
}
/**
* 发送本地消息(使用请求对象)
*/
public void sendLocalMessage(GlobalMessageSendRequest request) {
try {
log.info("开始发送本地全局消息,标题:{},接收人数量:{}", request.getTitle(),
request.getReceiverIds() != null ? request.getReceiverIds().size() : 0);
validateRequest(request);
applySystemSenderConfig(request);
if (localStrategy == null) {
throw new RuntimeException("本地消息发送策略未找到,系统无法正常工作");
}
localStrategy.send(request.getTitle(), request.getContent(), request.getType(),
request.getReceiverIds(), request.getConfig());
log.info("本地全局消息发送完成,标题:{}", request.getTitle());
} catch (Exception e) {
log.error("本地全局消息发送失败,标题:{},错误:{}", request.getTitle(), e.getMessage(), e);
throw new RuntimeException("本地消息发送失败:" + e.getMessage(), e);
}
}
/**
* 发送消息(使用请求对象)
*/
@@ -107,6 +167,7 @@ public class GlobalMessageSendService {
// 参数校验
validateRequest(request);
applySystemSenderConfig(request);
// 获取启用的策略
List<GlobalMessageSendStrategy> enabledStrategies = getEnabledStrategies();
@@ -161,6 +222,21 @@ public class GlobalMessageSendService {
}
}
private void applySystemSenderConfig(GlobalMessageSendRequest request) {
if (!Boolean.TRUE.equals(request.getSystemSend())) {
return;
}
JSONObject config = request.getConfig();
if (config == null) {
config = new JSONObject();
request.setConfig(config);
}
config.set("systemSend", true);
config.set("senderId", request.getSenderId());
config.set("senderName", request.getSenderName());
config.set("senderLoginName", request.getSenderLoginName());
}
/**
* 参数校验
*/
@@ -13,6 +13,7 @@ import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Date;
import java.util.List;
/**
@@ -56,9 +57,15 @@ public class LocalMessageSendStrategy implements GlobalMessageSendStrategy {
globalMessage.setTitle(title);
globalMessage.setContent(content);
globalMessage.setType(type);
globalMessage.setSenderId(SecurityUtil.getUserId());
globalMessage.setSenderName(SecurityUtil.getUserUsername());
globalMessage.setSenderLoginName(SecurityUtil.getUserLoginname());
if (config != null && Boolean.TRUE.equals(config.getBool("systemSend"))) {
globalMessage.setSenderId(config.getStr("senderId"));
globalMessage.setSenderName(config.getStr("senderName"));
globalMessage.setSenderLoginName(config.getStr("senderLoginName"));
} else {
globalMessage.setSenderId(SecurityUtil.getUserId());
globalMessage.setSenderName(SecurityUtil.getUserUsername());
globalMessage.setSenderLoginName(SecurityUtil.getUserLoginname());
}
globalMessage.setStatus(1); // 草稿状态
globalMessage.setSendSuccess(false);
dao.insert(globalMessage);
@@ -77,6 +84,7 @@ public class LocalMessageSendStrategy implements GlobalMessageSendStrategy {
// 更新消息状态为已发送
globalMessage.setStatus(2);
globalMessage.setSendTime(new Date());
globalMessage.setSendSuccess(true);
globalMessage.setSendResult("本地消息发送成功");
dao.update(globalMessage);
@@ -0,0 +1,49 @@
package com.budwk.app.zhgh.pointsmall.h5.index;
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.points.service.PointsMallUserPointsService;
import com.budwk.app.zhgh.pointsmall.supplier.service.PointsMallSupplierService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/h5/points-mall")
public class PointsMallH5IndexController {
@Inject
private PointsMallSupplierService pointsMallSupplierService;
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
@At("")
@Ok("beetl:/platform/zhghh5/points-mall/index.html")
@SaCheckPermission("h5.points.mall")
public void index() {
}
@At("/supplierList")
@SaCheckPermission("h5.points.mall")
public Result supplierList(@Param("mallType") Integer mallType) {
return Result.success(pointsMallSupplierService.listByMallType(mallType));
}
@At("/me/points")
@SaCheckPermission("h5.points.mall")
public Result points() {
return Result.success(pointsMallUserPointsService.availablePoints(currentUserId()));
}
private String currentUserId() {
try {
return StpUtil.getLoginIdAsString();
} catch (Exception e) {
return "";
}
}
}
@@ -0,0 +1,54 @@
package com.budwk.app.zhgh.pointsmall.h5.order;
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;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/h5/points-mall/order")
public class PointsMallH5OrderController {
@Inject
private PointsMallOrderService pointsMallOrderService;
@At("")
@Ok("beetl:/platform/zhghh5/points-mall/order/index.html")
@SaCheckPermission("h5.points.mall")
public void index() {
}
@At("/pageData")
@SaCheckPermission("h5.points.mall")
public Result pageData(PointsMallMobileOrderPageParam param) {
return Result.success(pointsMallOrderService.mobileOrderPage(param, currentUserId()));
}
@At("/supplierOrderUrl")
@SaCheckPermission("h5.points.mall")
public Result supplierOrderUrl(@Param("supplierId") String supplierId, @Param("orderId") String orderId, @Param("orderType") Integer orderType) {
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();
} catch (Exception e) {
return "";
}
}
}
@@ -0,0 +1,77 @@
package com.budwk.app.zhgh.pointsmall.invoice.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/invoice")
public class PointsMallInvoiceController {
@Inject
private PointsMallInvoiceService pointsMallInvoiceService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/invoice/index.html")
@SaCheckPermission("points.mall.invoice")
public void index() {
}
@At
@SaCheckPermission("points.mall.invoice")
public Result pageData(PointsMallInvoiceMainPageParam param) {
return Result.success(pointsMallInvoiceService.mainPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result infoPage(PointsMallInvoiceInfoPageParam param) {
return Result.success(pointsMallInvoiceService.infoPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result infoDetail(@Param("id") String id) {
return Result.success(pointsMallInvoiceService.infoDetail(id));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result orderPage(PointsMallInvoiceOrderPageParam param) {
return Result.success(pointsMallInvoiceService.orderPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result orderTotalPoints(PointsMallInvoiceOrderPageParam param) {
return Result.success(pointsMallInvoiceService.orderTotalPoints(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result supplierList() {
return Result.success(pointsMallInvoiceService.listSuppliers());
}
@At
@SLog(tag = "积分商城发票管理", msg = "发起开票申请")
@SaCheckPermission("points.mall.invoice")
public Result applyInvoice(PointsMallInvoiceApplyParam param) {
try {
return Result.success(pointsMallInvoiceService.applyInvoice(param));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
}
@@ -0,0 +1,128 @@
package com.budwk.app.zhgh.pointsmall.invoice.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
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_invoice_info")
@Comment("积分商城发票信息")
public class PointsMallInvoiceInfo 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 = 32)
private String mainId;
@Column
@Comment("发票号码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceId;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("发票日期")
private Date invoiceDate;
@Column
@Comment("发票裸价")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceNakeAmount;
@Column
@Comment("发票税率")
@ColDefine(type = ColType.FLOAT, width = 10, precision = 4)
private BigDecimal invoiceTaxRate;
@Column
@Comment("发票税额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceTaxAmount;
@Column
@Comment("价税合计")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceAmount;
@Column
@Comment("发票类型")
private Integer invoiceType;
@Column
@Comment("电子发票地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String url;
@Column
@Comment("发票Base64")
@ColDefine(type = ColType.TEXT)
private String imageEncode;
@Column
@Comment("文件类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String fileType;
@Column
@Comment("校验码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String checkCode;
@Column
@Comment("发票地址")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String invoiceAddress;
@Column
@Comment("开户银行")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String invoiceBank;
@Column
@Comment("银行账号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceAccount;
@Column
@Comment("联系电话")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceContact;
@Column
@Comment("开票内容")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String invoiceContent;
@Column
@Comment("纳税人识别号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String taxIdNumber;
@Column
@Comment("备注")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String remark;
@Column
@Comment("开票人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String biller;
}
@@ -0,0 +1,47 @@
package com.budwk.app.zhgh.pointsmall.invoice.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_invoice_main")
@Comment("积分商城发票主表")
public class PointsMallInvoiceMain extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("结算单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String settlementId;
@Column
@Comment("开票结果")
private Integer bSuccess;
@Column
@Comment("可开票订单列表")
@ColDefine(type = ColType.TEXT)
private String sucOrderIds;
@Column
@Comment("无法开票订单列表")
@ColDefine(type = ColType.TEXT)
private String failOrderIds;
@Column
@Comment("开票失败原因")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String failMsg;
}
@@ -0,0 +1,23 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import lombok.Data;
@Data
public class PointsMallInvoiceApplyParam {
private String supplierId;
private String orderIds;
private String startDate;
private String endDate;
private String invoiceDate;
private String invoiceTitle;
private String invoiceCode;
private String invoiceAddress;
private String invoiceContact;
private Integer invoiceType;
private String invoiceContent;
private String remark;
private String bankAccount;
private String registeredAddress;
private String bankName;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceInfoPageParam extends PageForm {
private String mainId;
private String invoiceId;
private String invoiceCode;
private String invoiceDate;
private Integer invoiceType;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceMainPageParam extends PageForm {
private String settlementId;
private Integer bSuccess;
private String sucOrderIds;
private String failOrderIds;
private String failMsg;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceOrderPageParam extends PageForm {
private String supplierId;
private String orderId;
private String startDate;
private String endDate;
private Integer invoiceStatus;
}
@@ -0,0 +1,31 @@
package com.budwk.app.zhgh.pointsmall.invoice.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import org.nutz.dao.entity.Record;
import java.math.BigDecimal;
import java.util.List;
public interface PointsMallInvoiceService extends BaseService<PointsMallInvoiceMain> {
Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param);
Pagination<Record> infoPage(PointsMallInvoiceInfoPageParam param);
Record infoDetail(String id);
Pagination<Record> orderPage(PointsMallInvoiceOrderPageParam param);
BigDecimal orderTotalPoints(PointsMallInvoiceOrderPageParam param);
String applyInvoice(PointsMallInvoiceApplyParam param);
List<PointsMallSupplier> listSuppliers();
}
@@ -0,0 +1,218 @@
package com.budwk.app.zhgh.pointsmall.invoice.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
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.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
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.IocBean;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvoiceMain> implements PointsMallInvoiceService {
public PointsMallInvoiceServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getSettlementId())) {
cnd.and("settlementId", "like", "%" + param.getSettlementId() + "%");
}
if (param.getBSuccess() != null) {
cnd.and("bSuccess", "=", param.getBSuccess());
}
if (StrUtil.isNotBlank(param.getSucOrderIds())) {
cnd.and("sucOrderIds", "like", "%" + param.getSucOrderIds() + "%");
}
if (StrUtil.isNotBlank(param.getFailOrderIds())) {
cnd.and("failOrderIds", "like", "%" + param.getFailOrderIds() + "%");
}
if (StrUtil.isNotBlank(param.getFailMsg())) {
cnd.and("failMsg", "like", "%" + param.getFailMsg() + "%");
}
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallInvoiceMain.class, cnd);
}
@Override
public Pagination<Record> infoPage(PointsMallInvoiceInfoPageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getMainId())) {
cnd.and("mainId", "=", param.getMainId());
}
if (StrUtil.isNotBlank(param.getInvoiceId())) {
cnd.and("invoiceId", "like", "%" + param.getInvoiceId() + "%");
}
if (StrUtil.isNotBlank(param.getInvoiceCode())) {
cnd.and("invoiceCode", "like", "%" + param.getInvoiceCode() + "%");
}
if (StrUtil.isNotBlank(param.getInvoiceDate())) {
cnd.and("invoiceDate", "=", DateUtil.parse(param.getInvoiceDate()));
}
if (param.getInvoiceType() != null) {
cnd.and("invoiceType", "=", param.getInvoiceType());
}
cnd.desc("createdAt");
Sql sql = Sqls.create("select * from points_mall_invoice_info $condition");
sql.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@Override
public Record infoDetail(String id) {
if (StrUtil.isBlank(id)) {
return null;
}
Sql sql = Sqls.create("select * from points_mall_invoice_info where id=@id and delFlag=0");
sql.params().set("id", id);
return (Record) dao().execute(sql.setCallback(Sqls.callback.record())).getResult();
}
@Override
public Pagination<Record> orderPage(PointsMallInvoiceOrderPageParam param) {
Cnd cnd = invoiceOrderCnd(param, "o");
cnd.desc("o.orderCompleteTime").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.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@Override
public BigDecimal orderTotalPoints(PointsMallInvoiceOrderPageParam param) {
Sql sql = Sqls.create("select ifnull(sum(ifnull(pointsPrice,0) - ifnull(refund,0)), 0) as totalPoints from points_mall_order_sub $condition");
sql.setCondition(invoiceOrderCnd(param, ""));
Record record = (Record) dao().execute(sql.setCallback(Sqls.callback.record())).getResult();
return record == null || record.get("totalPoints") == null ? BigDecimal.ZERO : new BigDecimal(record.get("totalPoints").toString());
}
@Override
public String applyInvoice(PointsMallInvoiceApplyParam param) {
checkApplyParam(param);
PointsMallInvoiceOrderPageParam query = new PointsMallInvoiceOrderPageParam();
query.setSupplierId(param.getSupplierId());
query.setOrderId(param.getOrderIds());
query.setStartDate(param.getStartDate());
query.setEndDate(param.getEndDate());
query.setInvoiceStatus(0);
List<PointsMallOrderSub> orders = dao().query(PointsMallOrderSub.class, invoiceOrderCnd(query, ""));
if (CollUtil.isEmpty(orders)) {
throw new IllegalArgumentException("无符合条件的订单数据");
}
List<String> orderIds = orders.stream().map(PointsMallOrderSub::getOrderId).collect(Collectors.toList());
String settlementId = System.currentTimeMillis() + RandomUtil.randomNumbers(6);
PointsMallInvoiceMain invoice = new PointsMallInvoiceMain();
invoice.setSettlementId(settlementId);
invoice.setBSuccess(0);
invoice.setSucOrderIds(String.join(",", orderIds));
invoice.setFailOrderIds("");
invoice.setFailMsg("");
insert(invoice);
PointsMallInvoiceInfo info = new PointsMallInvoiceInfo();
info.setMainId(invoice.getId());
info.setInvoiceId(settlementId);
info.setInvoiceCode(param.getInvoiceCode());
info.setInvoiceDate(DateUtil.parse(param.getInvoiceDate()));
info.setInvoiceType(param.getInvoiceType());
info.setInvoiceAmount(orderTotalPoints(query));
info.setInvoiceNakeAmount(info.getInvoiceAmount());
info.setInvoiceAddress(param.getInvoiceAddress());
info.setInvoiceContact(param.getInvoiceContact());
info.setInvoiceContent(param.getInvoiceContent());
info.setTaxIdNumber(param.getInvoiceCode());
info.setRemark(param.getRemark());
info.setInvoiceBank(param.getBankName());
info.setInvoiceAccount(param.getBankAccount());
dao().insert(info);
dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2), Cnd.where("supplierId", "=", param.getSupplierId()).and("orderId", "in", orderIds));
return "开票申请已生成,结算单号:" + settlementId;
}
@Override
public List<PointsMallSupplier> listSuppliers() {
return dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false).asc("sortCode"));
}
private Cnd invoiceOrderCnd(PointsMallInvoiceOrderPageParam param, String alias) {
Cnd cnd = Cnd.where(field(alias, "delFlag"), "=", false)
.and(field(alias, "orderState"), "=", 5)
.and(field(alias, "reconciliationStatus"), "=", 1);
if (StrUtil.isNotBlank(param.getSupplierId())) {
cnd.and(field(alias, "supplierId"), "=", param.getSupplierId());
}
if (param.getInvoiceStatus() != null) {
cnd.and(field(alias, "invoiceStatus"), "=", param.getInvoiceStatus());
}
if (StrUtil.isNotBlank(param.getOrderId())) {
List<String> orderIds = StrUtil.splitTrim(param.getOrderId(), ",");
if (orderIds.size() > 1) {
cnd.and(field(alias, "orderId"), "in", orderIds);
} else {
cnd.and(field(alias, "orderId"), "like", "%" + param.getOrderId() + "%");
}
}
if (StrUtil.isNotBlank(param.getStartDate())) {
cnd.and(field(alias, "orderCompleteTime"), ">=", DateUtil.beginOfDay(DateUtil.parse(param.getStartDate())));
}
if (StrUtil.isNotBlank(param.getEndDate())) {
cnd.and(field(alias, "orderCompleteTime"), "<=", DateUtil.endOfDay(DateUtil.parse(param.getEndDate())));
}
return cnd;
}
private String field(String alias, String field) {
return StrUtil.isBlank(alias) ? field : alias + "." + field;
}
private void checkApplyParam(PointsMallInvoiceApplyParam param) {
if (param == null) {
throw new IllegalArgumentException("开票参数不能为空");
}
if (StrUtil.isBlank(param.getSupplierId())) {
throw new IllegalArgumentException("供应商不能为空");
}
if (StrUtil.isBlank(param.getOrderIds())) {
throw new IllegalArgumentException("订单不能为空");
}
if (StrUtil.isBlank(param.getStartDate()) || StrUtil.isBlank(param.getEndDate())) {
throw new IllegalArgumentException("订单完成日期不能为空");
}
if (StrUtil.isBlank(param.getInvoiceDate())) {
throw new IllegalArgumentException("开票日期不能为空");
}
if (StrUtil.isBlank(param.getInvoiceCode())) {
throw new IllegalArgumentException("纳税人识别号不能为空");
}
if (StrUtil.isBlank(param.getInvoiceAddress())) {
throw new IllegalArgumentException("发票地址不能为空");
}
if (StrUtil.isBlank(param.getInvoiceContact())) {
throw new IllegalArgumentException("联系电话不能为空");
}
}
}
@@ -0,0 +1,11 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MallTokenRequired {
}
@@ -0,0 +1,175 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.controller;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.mallbridge.annotation.MallTokenRequired;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.*;
import com.budwk.app.zhgh.pointsmall.mallbridge.interceptor.MallTokenInterceptor;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult;
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;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.By;
import org.nutz.mvc.annotation.Filters;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.annotation.POST;
@IocBean
@Ok("json:full")
@At("/open")
@AdaptBy(type = JsonAdaptor.class)
@Api(tags = "积分商城桥接接口", description = "供应商 token、订单、积分、发票、对账等桥接接口")
public class PointsMallBridgeController {
@Inject
private PointsMallBridgeService pointsMallBridgeService;
@POST
@At("/connect/token")
@ApiOperation(value = "获取 token", notes = "供应商获取访问令牌")
public MallInboundResult getToken(@Param("..") MallTokenDTO dto) {
try {
return pointsMallBridgeService.getToken(dto);
} catch (Exception e) {
return MallInboundResult.fail(401, e.getMessage());
}
}
@POST
@At("/connect/refreshToken")
@ApiOperation(value = "刷新 token", notes = "供应商刷新访问令牌")
public MallInboundResult refreshToken(@Param("..") MallTokenRefreshDTO dto) {
try {
return pointsMallBridgeService.refreshToken(dto);
} catch (Exception e) {
return MallInboundResult.fail(401, e.getMessage());
}
}
@POST
@At("/pointOrder/queryPoints")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "积分查询", notes = "查询用户可用积分")
public MallInboundPointsResult queryPoints(@Param("..") MallPointQueryDTO dto) {
try {
return pointsMallBridgeService.queryPoints(dto);
} catch (Exception e) {
return MallInboundPointsResult.fail(401, e.getMessage());
}
}
@POST
@At("/pointOrder/pendingOrderInfo")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "接收待支付订单", notes = "供应商推送待支付主单")
public MallInboundResult receivePending(@Param("..") MallPendingOrderDTO dto) {
return inbound(() -> pointsMallBridgeService.receivePending(dto));
}
@POST
@At("/pointOrder/confirmPurchase")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "确认购买", notes = "支付完成后确认主单已购买")
public MallInboundResult confirmPurchase(@Param("..") MallPurchaseConfirmDTO dto) {
return inbound(() -> pointsMallBridgeService.confirmPurchase(dto));
}
@POST
@At("/pointOrder/purchaseResult")
@MallTokenRequired
@Filters(@By(type = MallTokenInterceptor.class))
@ApiOperation(value = "支付结果通知", notes = "接收供应商支付结果")
public MallInboundResult purchaseResult(@Param("..") MallPurchaseResultDTO dto) {
return inbound(() -> pointsMallBridgeService.purchaseResult(dto));
}
@POST
@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);
} catch (Exception e) {
return MallOutboundResult.fail(401, e.getMessage());
}
}
@POST
@At("/pointOrder/applyForInvoices")
@ApiOperation(value = "发票开具申请", notes = "接收供应商开票申请")
public MallOutboundResult applyInvoice(@Param("..") MallInvoiceApplyDTO dto) {
try {
return pointsMallBridgeService.applyInvoice(dto);
} catch (Exception e) {
return MallOutboundResult.fail(401, e.getMessage());
}
}
@POST
@At("/pointOrder/queryInvoicesInfo")
@ApiOperation(value = "查询发票信息", notes = "查询开票结果和发票明细")
public Result queryInvoicesInfo(@Param("..") QueryInvoiceInfoDTO dto) {
try {
return Result.success(pointsMallBridgeService.queryInvoicesInfo(dto));
} catch (Exception e) {
return Result.error(401, e.getMessage());
}
}
@POST
@At("/pointOrder/queryFileBase64Info")
@ApiOperation(value = "查询图片 Base64", notes = "查询订单商品图片或发票图片")
public MallPictureOutboundResult queryFileBase64Info(@Param("..") PictureQueryDTO dto) {
try {
return pointsMallBridgeService.queryFileBase64Info(dto);
} catch (Exception e) {
return MallPictureOutboundResult.fail(401, e.getMessage());
}
}
@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));
}
private MallInboundResult inbound(InboundCall call) {
try {
return call.get();
} catch (Exception e) {
return MallInboundResult.fail(401, e.getMessage());
}
}
private interface InboundCall {
MallInboundResult get() throws Exception;
}
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class FinalStatementDTO {
private String settlementId;
private String invoiceIds;
private String orderIds;
private String payDate;
private BigDecimal totalFee;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.util.List;
@Data
public class GetInvoiceInfoDTO {
private Integer bSuccess;
private String settlementId;
private String sucOrderIds;
private String failOrderIds;
private String failMsg;
private List<InvoiceInfoDTO> invoiceInfos;
}
@@ -0,0 +1,23 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class InvoiceInfoDTO {
private String invoiceId;
private String invoiceCode;
private LocalDate invoiceDate;
private BigDecimal invoiceNakeAmount;
private BigDecimal invoiceTaxRate;
private BigDecimal invoiceTaxAmount;
private BigDecimal invoiceAmount;
private Integer invoiceType;
private String url;
private String imageEncode;
private String fileType;
private String checkCode;
private String invoiceAddress;
}
@@ -0,0 +1,29 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class MallInvoiceApplyDTO {
private String supplierId;
private String token;
private String settlementId;
private String orderIds;
private Integer orderNum;
private BigDecimal orderTotalPrice;
private String invoiceDate;
private String invoiceTitle;
private String invoiceCode;
private String invoiceAddress;
private String invoiceContact;
private BigDecimal invoiceNakedPrice;
private BigDecimal invoiceTaxPrice;
private String invoiceType;
private String invoiceContent;
private String remark;
private String bankAccount;
private String registeredAddress;
private String bankName;
private Long timestamp;
}
@@ -0,0 +1,18 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallOrderEventDTO {
private String supplierId;
private String token;
private Integer orderType;
private String orderId;
private String mainOrderId;
private String userId;
private Integer deliveryStatus;
private String deliveryTime;
private String logisticsOrderId;
private String crrgBsnNm;
private Long time;
}
@@ -0,0 +1,12 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallOrderQueryDTO {
private String supplierId;
private String token;
private Integer orderType;
private String orderId;
private Long timestamp;
}
@@ -0,0 +1,11 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallPendingOrderDTO {
private String supplierId;
private String token;
private String userId;
private OrderInfoDTO orderInfo;
}
@@ -0,0 +1,10 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallPointQueryDTO {
private String supplierId;
private String token;
private String userId;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class MallPurchaseConfirmDTO {
private String supplierId;
private String token;
private String orderId;
private BigDecimal totalPrice;
private BigDecimal wPayOrAPay;
private BigDecimal pointsPrice;
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class MallPurchaseResultDTO {
private String supplierId;
private String token;
private String orderId;
private BigDecimal totalPrice;
private BigDecimal wPayOrAPay;
private BigDecimal pointsPrice;
private Boolean purchaseResult;
private String message;
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallTokenDTO {
private String supplierId;
private String clientId;
private String clientSecret;
private String sendSha256;
private String code;
private String timestamp;
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class MallTokenRefreshDTO {
private String supplierId;
private String refreshToken;
private String clientId;
private String clientSecret;
private String sendSha256;
private String timestamp;
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class MallTokenResDTO {
private String time;
private String accessToken;
private String refreshToken;
private long expire;
private long refreshExpire;
}
@@ -0,0 +1,33 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class OrderInfoDTO {
private String orderId;
private String orderNumberId;
private String supplierId;
private String token;
private String aplName;
private String createTime;
private String updateTime;
private String deliveryTime;
private String orderFinishTime;
private String completeTime;
private String deliveryStatus;
private BigDecimal freightCost;
private BigDecimal wPayOrAPayFreightCost;
private BigDecimal refund;
private BigDecimal wPayOrAPayRefund;
private String logisticsOrderId;
private String crrgBsnNm;
private Integer orderState;
private BigDecimal totalPrice;
private BigDecimal wPayOrAPay;
private BigDecimal pointsPrice;
private String invoiceCode;
private List<OrderProInfoDTO> orderProInfos;
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.util.List;
@Data
public class OrderPayFinishNoticeDTO {
private String supplierId;
private String token;
private String expReportNumber;
private List<FinalStatementDTO> expFinalInfos;
private Long timestamp;
}
@@ -0,0 +1,18 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class OrderProInfoDTO {
private String sku;
private Integer number;
private BigDecimal price;
private String name;
private BigDecimal taxRate;
private List<String> picInfo;
private String fileId;
private String filePath;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
import java.util.List;
@Data
public class PictureQueryDTO {
private String supplierId;
private String token;
private String queryType;
private String idNumber;
private List<String> fileIdList;
private Long timestamp;
}
@@ -0,0 +1,11 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class QueryInvoiceInfoDTO {
private String supplierId;
private String token;
private String settlementId;
private Long timestamp;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.dto;
import lombok.Data;
@Data
public class UnReconciledMsgNoticeDTO {
private String uniqueSeqNo;
private String supplierId;
private String token;
private String orderIds;
private String unReconciledMsg;
private String unReconciledType;
private String startDate;
private String endDate;
}
@@ -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,30 @@
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 com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
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, MallOrderStatusEnum.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,123 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.sys.models.Sys_user;
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.MallCallPMessagePushService;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
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.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.Date;
/**
* orderType=3:取消子订单事件处理器。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallCancelSubOrderEventHandler implements MallOrderEventHandler {
private final Dao dao;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private PointsMallOrderService pointsMallOrderService;
@Inject
private PointsMallOrderSubService pointsMallOrderSubService;
@Inject
private MallCallPMessagePushService mallCallPMessagePushService;
public MallCancelSubOrderEventHandler(Dao dao) {
this.dao = dao;
}
@Override
public int getType() {
return 3;
}
/**
* 取消子订单:校验用户和子单,更新子单状态,写入子单历史,释放积分,联动主单状态并推送本地消息。
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
// 供应商推送取消子单时必须携带子单号和用户标识。
if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getUserId())) {
return MallInboundResult.fail(400, "供应商、订单、用户不能为空");
}
// userId 兼容系统用户ID和工号,后续消息推送需要使用用户信息。
Sys_user user = fetchUser(dto.getUserId());
if (user == null) {
return MallInboundResult.fail(-1, "用户不存在");
}
// 按供应商和外部子单号定位当前子订单。
PointsMallOrderSub sub = pointsMallOrderSubService.findBySubOrderExtId(dto.getOrderId(), dto.getSupplierId());
if (sub == null) {
log.info("9.2.1生成订单信息推送,订单不存在:{}", dto);
return MallInboundResult.fail(-1, "订单不存在");
}
Integer orderStatus = MallOrderStatusEnum.CANCELLED.getCode();
log.info("推送的订单类型比较:推送过来的订单状态:{},库中订单状态:{}", orderStatus, sub.getOrderState());
if (orderStatus.equals(sub.getOrderState())) {
return MallInboundResult.fail(-1, "请不要重复推送");
}
Date now = new Date();
Trans.exec((Atom) () -> {
// 只更新子单状态相关字段,避免完整对象回写影响其它订单字段。
PointsMallOrderSub modifySub = new PointsMallOrderSub();
modifySub.setId(sub.getId());
modifySub.setOrderState(orderStatus);
modifySub.setOrderUpdateTime(now);
pointsMallOrderSubService.updateSubOrderStatus(modifySub);
sub.setOrderState(orderStatus);
sub.setOrderUpdateTime(now);
// 记录子单状态变更历史,便于后续追踪供应商推送流水。
PointsMallOrderSubHistory history = BeanUtil.copyProperties(sub, PointsMallOrderSubHistory.class);
history.setId(null);
history.setOrderState(orderStatus);
dao.insert(history);
log.info("9.2.1生成订单信息推送-订单状态为 已取消 释放积分");
// 释放积分/返回用户积分
pointsMallPointsLockService.releaseOrReturnPoints(sub, orderStatus, BigDecimal.ZERO);
// 更新主订单状态,所有子单退货/取消/完成后,修改主单状态为已完成。
pointsMallOrderService.updateMainOrderStatus(sub.getMainOrderId(), sub.getSupplierId());
});
try {
mallCallPMessagePushService.pushMessageToGlobal(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()), sub);
} catch (Exception e) {
log.error("9.2.1 子单取消,callP消息推送:{},{}", dto, e.getMessage());
}
log.info("9.2.1生成订单信息推送,取消子订单处理完成,supplierId={}orderId={}", dto.getSupplierId(), dto.getOrderId());
return MallInboundResult.success();
}
/**
* 入参 userId 可能是系统用户ID或工号,按当前项目用户表兼容查询。
*/
private Sys_user fetchUser(String userId) {
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
if (user != null) {
return user;
}
return dao.fetch(Sys_user.class, Cnd.where("loginname", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
}
}
@@ -0,0 +1,34 @@
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 com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* MallConfirmReceiptEventHandler
* orderType=6: 确认收货事件处理器。
* 行为: 根据子单外部ID更新子单状态为已妥投(电商处已收货待用户callP侧确认收货)。
*
* <p>注意:供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”,
* 不等同于 zhgh/callP 用户确认收货后的“已完成(5)”。</p>
*/
@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, MallOrderStatusEnum.RECEIVED);
}
}
@@ -0,0 +1,124 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.bean.BeanUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
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.MallOrderQueryOutboundService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
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.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
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;
/**
* MallExchangeSubEventHandler
* orderType=5: 换货单事件处理器。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallExchangeSubEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
@Inject
private PointsMallOrderSubService pointsMallOrderSubService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
public MallExchangeSubEventHandler(Dao dao) {
super(dao);
}
@Override
public int getType() {
return 5;
}
/**
* 9.2.1 生成订单信息推送
* 订单类型为:5(换货单)时,mainOrderId 代表原子单IDorderId 代表换货单ID。
* 1. 重新调用9.2.2获取换货单详情。
* 2. 记录换货单流水记录。
* 3. 换货单状态只保存在退换货表中,不更新原子单退款积分。
*
* @param dto 请求参数
* @return 操作结果
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
if (!hasRequiredOrderEventFields(dto)) {
return MallInboundResult.fail(MallBridgeError.PARAM_ERROR.code(), "参数缺少");
}
Sys_user user = fetchUser(dto.getUserId());
if (user == null) {
return MallInboundResult.fail(-1, "用户不存在");
}
Integer orderStatus = MallOrderStatusEnum.EXCHANGE.getCode();
log.info("9.2.1生成订单信息推送开始:{},{}", orderStatus, dto);
// 订单类型为:5(换货单),mainOrderId 代表子单IDorderId 代表换货单ID
String subOrderId = dto.getMainOrderId();
String exchangeOrderId = dto.getOrderId();
PointsMallOrderSub sub = pointsMallOrderSubService.findBySubOrderExtId(subOrderId, dto.getSupplierId());
if (sub == null) {
log.info("9.2.1生成订单信息推送,订单不存在:{}", dto);
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), MallBridgeError.ORDER_NOT_EXISTS.message());
}
// 订单类型为:5(换货单),需限制同一个换货单只能换一次
PointsMallOrderExchange existed = dao.fetch(PointsMallOrderExchange.class, Cnd.where("orderMainId", "=", subOrderId)
.and("orderId", "=", exchangeOrderId).and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (existed != null) {
log.info("9.2.1生成订单信息推送,该订单 已退货/已换货,请不要重复操作:{}", dto);
return MallInboundResult.fail(-1, "该订单 已退货/已换货,请不要重复操作");
}
log.info("订单类型为:4(退货单),5(换货单)时,调用 9.2.2获取订单详情接口:{}", dto);
OrderInfoDTO orderInfo;
try {
// 订单类型为:5(换货单)时,需重新调用 9.2.2获取订单详情接口
orderInfo = queryExchangeOrderInfo(dto);
} catch (Exception e) {
log.error("订单类型为:4(退货单),5(换货单)时,调用9.2.2 获取订单详情接口异常:{}", dto);
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + dto.getOrderId());
}
PointsMallOrderExchange orderExchange = toExchange(dto, orderInfo, sub, orderStatus);
Trans.exec((Atom) () -> {
// 把推送过来的换货单存入退换货表
dao.insert(orderExchange);
log.info("9.2.1生成订单信息推送-记录退换货的订单流水记录");
// 记录推送的子订单流水记录
PointsMallOrderSubHistory history = BeanUtil.copyProperties(orderExchange, PointsMallOrderSubHistory.class);
history.setId(null);
history.setMainOrderId(orderExchange.getOrderMainId());
history.setOrderState(orderStatus);
dao.insert(history);
});
log.info("9.2.1生成订单信息推送结束!");
return MallInboundResult.success();
}
private OrderInfoDTO queryExchangeOrderInfo(MallOrderEventDTO dto) {
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderType(dto.getOrderType());
queryDTO.setOrderId(dto.getOrderId());
MallOutboundResult result = mallOrderQueryOutboundService.queryOrderInfo(queryDTO);
if (result == null || result.getResultCode() != 0 || result.getOrderInfo() == null || result.getOrderInfo().isEmpty()) {
log.error("订单类型为:4(退货单),5(换货单)时,调用9.2.2 获取订单详情接口异常:{},{}", dto, result);
throw new RuntimeException("调用9.2.2获取订单详情接口异常");
}
return result.getOrderInfo().get(0);
}
}
@@ -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;
/**
* MallShipOrderEventHandler
* orderType=99: 供应商已发货 / 更新发货状态。
*/
@IocBean
public class MallShipOrderEventHandler implements MallOrderEventHandler {
@Inject
private MallInboundEventService inboundEventService;
@Override
public int getType() {
return 99;
}
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
return inboundEventService.updateDeliveryStatus(dto);
}
}
@@ -0,0 +1,144 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
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.MallOrderQueryOutboundService;
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.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubHistoryService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* MallSplitSubOrderEventHandler
* orderType=2: 拆分子订单事件处理器。
* 行为: 根据主单外部ID与子单外部ID新增子单映射(status=0)。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallSplitSubOrderEventHandler implements MallOrderEventHandler {
private final Dao dao;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
@Inject
private PointsMallOrderSubHistoryService pointsMallOrderSubHistoryService;
public MallSplitSubOrderEventHandler(Dao dao) {
this.dao = dao;
}
@Override
public int getType() {
return 2;
}
/**
* 第一个接口,orderType是2的时候,获取子单信息,存储到数据库
*
* @param dto 入参
* @return 成功
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 开始执行:{}", dto);
// 校验 orderId 必须存在且有效
if (dto == null || StrUtil.isBlank(dto.getOrderId())) {
return MallInboundResult.fail(400, "缺少主订单信息");
}
// 分割子订单ID列表
List<String> subOrderIds = Arrays.stream(dto.getOrderId().split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.distinct()
.collect(Collectors.toList());
if (subOrderIds.isEmpty()) {
return MallInboundResult.fail(400, "缺少拆单信息");
}
// 查询主单是否存在
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, "主单不存在");
}
log.info("9.2.1生成订单信息推送-调用外部服务获取子单信息:{}", dto);
List<OrderInfoDTO> orderInfoList;
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderId(dto.getOrderId());
queryDTO.setOrderType(dto.getOrderType());
try {
// 调用外部服务获取子订单信息
MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO);
if (result == null || result.getResultCode() != 0) {
throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage());
}
orderInfoList = result.getOrderInfo();
} catch (Exception e) {
log.error("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),调用9.2.2获取订单详情接口异常{}", e.getMessage());
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + subOrderIds);
}
if (orderInfoList == null || orderInfoList.isEmpty()) {
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), MallBridgeError.ORDER_NOT_EXISTS.message());
}
// 1. 批量查询已存在的子单
Set<String> existingOrderIds = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "in", orderInfoList.stream().map(OrderInfoDTO::getOrderId).collect(Collectors.toList()))
.and("delFlag", "=", false))
.stream().map(PointsMallOrderSub::getOrderId).collect(Collectors.toSet());
// 子订单单表
List<PointsMallOrderSub> mallOrderSubList = new ArrayList<>();
// 子订单历史记录表
List<PointsMallOrderSubHistory> mallOrderSubHistoryList = new ArrayList<>();
log.info("9.2.1 生成订单信息推送-调用外部服务获取子单信息:{}", orderInfoList);
for (OrderInfoDTO info : orderInfoList) {
// 2. 检查子单是否已存在
if (existingOrderIds.contains(info.getOrderId())) {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 子单已存在:{}", info);
return MallInboundResult.fail(MallBridgeError.ORDER_EXISTS.code(), MallBridgeError.ORDER_EXISTS.message());
}
try {
// 构造对象
SubOrderBuildResult result = pointsMallOrderSubHistoryService.buildSubOrder(info, dto, main.getUserId());
mallOrderSubList.add(result.getSub());
mallOrderSubHistoryList.add(result.getHistory());
} catch (IllegalArgumentException e) {
return MallInboundResult.fail(-1, e.getMessage());
}
}
Trans.exec((Atom) () -> {
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单");
dao.insert(mallOrderSubList);
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单流水记录");
dao.insert(mallOrderSubHistoryList);
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹),保存子订单积分锁定记录");
pointsMallPointsLockService.createSubLocks(main, mallOrderSubList);
});
log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子弹) 执行结束");
return MallInboundResult.success();
}
}
@@ -0,0 +1,178 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
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.MallCallPMessagePushService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallOrderQueryOutboundService;
import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
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.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
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.util.Date;
/**
* MallSubOrderReturnEventHandler
* orderType=4: 退货单事件处理器。
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallSubOrderReturnEventHandler extends AbstractMallBridgeSupport implements MallOrderEventHandler {
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private PointsMallOrderService pointsMallOrderService;
@Inject
private PointsMallOrderSubService pointsMallOrderSubService;
@Inject
private MallCallPMessagePushService mallCallPMessagePushService;
@Inject
private MallOrderQueryOutboundService mallOrderQueryOutboundService;
public MallSubOrderReturnEventHandler(Dao dao) {
super(dao);
}
@Override
public int getType() {
return 4;
}
/**
* 9.2.1 生成订单信息推送
* 订单类型为:4(退货单)时,mainOrderId 代表原子单IDorderId 代表退货单ID。
* 1. 重新调用9.2.2获取退货单详情。
* 2. 记录退货单流水记录。
* 3. 退货单退回用户积分。
* 4. 判断子单是否都完成了,修改主单状态。
*
* @param dto 请求参数
* @return 操作结果
*/
@Override
public MallInboundResult handle(MallOrderEventDTO dto) {
if (!hasRequiredOrderEventFields(dto)) {
return MallInboundResult.fail(MallBridgeError.PARAM_ERROR.code(), "参数缺少");
}
Sys_user user = fetchUser(dto.getUserId());
if (user == null) {
return MallInboundResult.fail(-1, "用户不存在");
}
Integer orderStatus = MallOrderStatusEnum.REFUNDED.getCode();
log.info("9.2.1生成订单信息推送开始:{},{}", orderStatus, dto);
// 订单类型为:4(退货单),mainOrderId 代表子单IDorderId 代表退货单ID
String subOrderId = dto.getMainOrderId();
String exchangeOrderId = dto.getOrderId();
PointsMallOrderSub sub = pointsMallOrderSubService.findBySubOrderExtId(subOrderId, dto.getSupplierId());
if (sub == null) {
log.info("9.2.1生成订单信息推送,订单不存在:{}", dto);
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), MallBridgeError.ORDER_NOT_EXISTS.message());
}
// 订单类型为:4(退货单),需限制同一个退货单只能退一次
PointsMallOrderExchange existed = dao.fetch(PointsMallOrderExchange.class, Cnd.where("orderMainId", "=", subOrderId)
.and("orderId", "=", exchangeOrderId).and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false));
if (existed != null) {
log.info("9.2.1生成订单信息推送,该订单 已退货/已换货,请不要重复操作:{}", dto);
return MallInboundResult.fail(-1, "该订单 已退货/已换货,请不要重复操作");
}
log.info("订单类型为:4(退货单),5(换货单)时,调用 9.2.2获取订单详情接口:{}", dto);
OrderInfoDTO orderInfo;
try {
// 订单类型为:4(退货单)时,需重新调用 9.2.2获取订单详情接口
orderInfo = queryReturnOrderInfo(dto);
} catch (Exception e) {
log.error("订单类型为:4(退货单),5(换货单)时,调用9.2.2 获取订单详情接口异常:{}", dto);
return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + dto.getOrderId());
}
// 订单退款积分【订单为退货单时有值】
BigDecimal refund = nvl(orderInfo.getRefund());
BigDecimal refunded = nvl(sub.getRefund());
BigDecimal pointsPrice = nvl(sub.getPointsPrice());
// 订单退货
if (pointsPrice.compareTo(refunded.add(refund)) < 0) {
// 订单退款积分 如果超过 子订单锁定积分,则校验不通过
log.info("当前子单:{},订单退款积分:{},超过子表消耗积分:{}", sub.getOrderId(), refund, pointsPrice);
return MallInboundResult.fail(-1, "该订单 已退积分超过下单积分!");
}
Date now = new Date();
PointsMallOrderExchange orderExchange = toExchange(dto, orderInfo, sub, orderStatus);
// 退货单需要更新订单子表的退款金额,对账使用;退满时更新子单状态
BigDecimal refundSum = refunded.add(refund);
PointsMallOrderSub modifySub = new PointsMallOrderSub();
modifySub.setId(sub.getId());
modifySub.setRefund(refundSum);
modifySub.setOrderUpdateTime(now);
if (modifySub.getRefund().compareTo(pointsPrice) == 0) {
modifySub.setOrderState(orderStatus);
}
// 记录推送的子订单流水记录
PointsMallOrderSubHistory history = BeanUtil.copyProperties(orderExchange, PointsMallOrderSubHistory.class);
history.setId(null);
history.setMainOrderId(orderExchange.getOrderMainId());
history.setOrderState(orderStatus);
log.info("9.2.1生成订单信息推送-记录退换货的订单流水记录");
log.info("9.2.1生成订单信息推送-订单状态为 已取消 释放积分 为 已退货 退回用户积分");
Trans.exec((Atom) () -> {
pointsMallOrderSubService.updateSubOrder(modifySub);
// 把推送过来的退货单存入退换货表
dao.insert(orderExchange);
dao.insert(history);
// 释放积分/返回用户积分
pointsMallPointsLockService.releaseOrReturnPoints(sub, orderStatus, refund);
// 更新主订单状态,所有子单退货/取消/完成后,修改主单状态
pointsMallOrderService.updateMainOrderStatus(sub.getMainOrderId(), sub.getSupplierId());
});
try {
// callP消息推送
PointsMallOrderSub notifySub = BeanUtil.copyProperties(sub, PointsMallOrderSub.class);
notifySub.setOrderId(dto.getOrderId());
notifySub.setPointsPrice(refund);
notifySub.setTotalPrice(orderInfo.getTotalPrice());
notifySub.setWPayOrAPay(orderInfo.getWPayOrAPay());
notifySub.setExtJson(orderExchange.getExtJson());
mallCallPMessagePushService.pushMessageToGlobal(StrUtil.blankToDefault(user.getUsername(), user.getLoginname()), notifySub);
} catch (Exception e) {
log.error("9.2.1 子单取消 或 退货,callP消息推送: {}, {}", dto, e.getMessage());
}
log.info("9.2.1生成订单信息推送结束!");
return MallInboundResult.success();
}
private OrderInfoDTO queryReturnOrderInfo(MallOrderEventDTO dto) {
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderType(dto.getOrderType());
queryDTO.setOrderId(dto.getOrderId());
MallOutboundResult result = mallOrderQueryOutboundService.queryOrderInfo(queryDTO);
if (result == null || result.getResultCode() != 0 || result.getOrderInfo() == null || result.getOrderInfo().isEmpty()) {
log.error("订单类型为:4(退货单),5(换货单)时,调用9.2.2 获取订单详情接口异常:{},{}", dto, result);
throw new RuntimeException("调用9.2.2获取订单详情接口异常");
}
return result.getOrderInfo().get(0);
}
}
@@ -0,0 +1,32 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.exception;
/**
* MallBridgeError
* 说明: 商城桥接的统一错误码枚举, 包含HTTP风格的resultCode与默认提示文案。
* 用于抛出MallBridgeException时传递标准化的错误信息。
*/
public enum MallBridgeError {
ERROR(-1, "校验失败"),
AUTH_FAIL(401, "鉴权失败"),
PARAM_ERROR(400, "参数错误"),
ORDER_EXISTS(409, "订单已存在"),
ORDER_NOT_EXISTS(410, "订单不存在"),
NOT_FOUND(404, "资源不存在"),
SERVER_ERROR(500, "服务器内部错误");
private final int code;
private final String message;
MallBridgeError(int code, String message) {
this.code = code;
this.message = message;
}
public int code() {
return code;
}
public String message() {
return message;
}
}
@@ -0,0 +1,52 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.interceptor;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.annotation.MallTokenRequired;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.mallbridge.models.PointsMallSupplierTokenStore;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.json.JsonFormat;
import org.nutz.mvc.ActionContext;
import org.nutz.mvc.ActionFilter;
import org.nutz.mvc.Mvcs;
import org.nutz.mvc.View;
import org.nutz.mvc.view.UTF8JsonView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
public class MallTokenInterceptor implements ActionFilter {
@Override
public View match(ActionContext actionContext) {
if (actionContext.getMethod() == null || !actionContext.getMethod().isAnnotationPresent(MallTokenRequired.class)) {
return null;
}
String accessToken = actionContext.getRequest().getHeader("token");
if (StrUtil.isBlank(accessToken)) {
return fail(401, "缺少accessToken参数");
}
Dao dao = Mvcs.getIoc().get(Dao.class);
PointsMallSupplierTokenStore tokenStore = dao.fetch(PointsMallSupplierTokenStore.class,
Cnd.where("accessToken", "=", accessToken).and("isValid", "=", true));
if (tokenStore == null || tokenStore.getAccessExpireTime() == null || tokenStore.getAccessExpireTime().before(new Date())) {
return fail(401, "无效的accessToken");
}
return null;
}
private View fail(int code, String message) {
return new View() {
@Override
public void render(HttpServletRequest req, HttpServletResponse resp, Object obj) throws Throwable {
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
new UTF8JsonView(JsonFormat.compact()).render(req, resp, MallInboundResult.fail(code, message));
}
};
}
}
@@ -0,0 +1,27 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.model;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class MallInboundPointsResult {
private int resultCode;
private String message;
private BigDecimal userPoints;
public static MallInboundPointsResult success(BigDecimal points) {
MallInboundPointsResult result = new MallInboundPointsResult();
result.setResultCode(0);
result.setMessage("成功");
result.setUserPoints(points);
return result;
}
public static MallInboundPointsResult fail(int code, String message) {
MallInboundPointsResult result = new MallInboundPointsResult();
result.setResultCode(code);
result.setMessage(message);
return result;
}
}
@@ -0,0 +1,23 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.model;
import lombok.Data;
@Data
public class MallInboundResult {
private int resultCode;
private String message;
public static MallInboundResult success() {
MallInboundResult result = new MallInboundResult();
result.setResultCode(0);
result.setMessage("成功");
return result;
}
public static MallInboundResult fail(int code, String message) {
MallInboundResult result = new MallInboundResult();
result.setResultCode(code);
result.setMessage(message);
return result;
}
}
@@ -0,0 +1,26 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class MallInboundTokenResult extends MallInboundResult {
private String time;
private String accessToken;
private String refreshToken;
private Long expire;
private Long refreshExpire;
public static MallInboundTokenResult success(String time, String accessToken, String refreshToken, long expire, long refreshExpire) {
MallInboundTokenResult result = new MallInboundTokenResult();
result.setResultCode(0);
result.setMessage("成功");
result.setTime(time);
result.setAccessToken(accessToken);
result.setRefreshToken(refreshToken);
result.setExpire(expire);
result.setRefreshExpire(refreshExpire);
return result;
}
}
@@ -0,0 +1,32 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.model;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO;
import lombok.Data;
import java.util.List;
@Data
public class MallOutboundResult {
private int resultCode;
private String message;
private List<OrderInfoDTO> orderInfo;
public static MallOutboundResult success() {
return success(null);
}
public static MallOutboundResult success(List<OrderInfoDTO> orderInfo) {
MallOutboundResult result = new MallOutboundResult();
result.setResultCode(0);
result.setMessage("成功");
result.setOrderInfo(orderInfo);
return result;
}
public static MallOutboundResult fail(int code, String message) {
MallOutboundResult result = new MallOutboundResult();
result.setResultCode(code);
result.setMessage(message);
return result;
}
}
@@ -0,0 +1,57 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.model;
import java.util.Map;
public class MallPictureOutboundResult {
private int resultCode;
private String message;
private String fileType;
private Map<String, String> imageEncodeMap;
public static MallPictureOutboundResult success(Map<String, String> imageEncodeMap) {
MallPictureOutboundResult result = new MallPictureOutboundResult();
result.resultCode = 0;
result.message = "成功";
result.imageEncodeMap = imageEncodeMap;
return result;
}
public static MallPictureOutboundResult fail(int code, String message) {
MallPictureOutboundResult result = new MallPictureOutboundResult();
result.resultCode = code;
result.message = message;
return result;
}
public int getResultCode() {
return resultCode;
}
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public Map<String, String> getImageEncodeMap() {
return imageEncodeMap;
}
public void setImageEncodeMap(Map<String, String> imageEncodeMap) {
this.imageEncodeMap = imageEncodeMap;
}
}
@@ -0,0 +1,49 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_supplier_token_store")
@Comment("积分商城供应商Token")
public class PointsMallSupplierTokenStore extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 128)
private String clientId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String accessToken;
@Column
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String refreshToken;
@Column
private Date accessExpireTime;
@Column
private Date refreshExpireTime;
@Column
private Boolean isValid;
}
@@ -0,0 +1,168 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage;
import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
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.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.math.RoundingMode;
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 MallCallPMessagePushService {
private final Dao dao;
@Inject
private GlobalMessageSendService globalMessageSendService;
public MallCallPMessagePushService(Dao dao) {
this.dao = dao;
}
/**
* 支付成功callP消息推送。
*
* @param mainOrder 订单信息
*/
public void pushMessageToGlobal(PointsMallOrderMain mainOrder) {
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class, Cnd.where("id", "=", "11111111").and("delFlag", "=", false));
if (template == null) {
log.info("支付成功推送消息失败,未配置消息模板");
return;
}
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", mainOrder.getUserId()).or("loginname", "=", mainOrder.getUserId()));
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", mainOrder.getSupplierId()).and("delFlag", "=", false));
String supplierName = supplier == null || StrUtil.isBlank(supplier.getSupplierName()) ? mainOrder.getSupplierId() : supplier.getSupplierName();
// 消息唯一标识 模板id+供应商+订单号
String uniqueNo = template.getId().concat("-").concat(mainOrder.getSupplierId()).concat("-").concat(mainOrder.getOrderId());
int count = dao.count(PointsMallMessage.class, Cnd.where("uniqueNo", "=", uniqueNo).and("delFlag", "=", false));
if (count > 0) {
log.info("9.2.14支付结果通知接口-支付成功2,消息重复推送:{}", uniqueNo);
return;
}
StringBuilder sb = new StringBuilder();
sb.append(" 商品列表:").append(System.lineSeparator());
List<OrderProInfoDTO> productList = StrUtil.isBlank(mainOrder.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(mainOrder.getExtJson(), OrderProInfoDTO.class);
productList.forEach(product ->
sb.append(" 商品名称:").append(product.getName()).append(",商品价格:").append(formatMoney(product.getPrice())).append("").append(System.lineSeparator())
);
// 模板变量替换数据
Map<String, String> paramMap = new HashMap<>();
paramMap.put("#{userName}", user == null || StrUtil.isBlank(user.getUsername()) ? mainOrder.getUserId() : user.getUsername());
paramMap.put("#{orderFinishTime}", DateUtil.formatDateTime(mainOrder.getOrderCreateTime()));
paramMap.put("#{pointPrice}", nvl(mainOrder.getPointsPrice()).toPlainString());
paramMap.put("#{supplierName}", supplierName);
paramMap.put("#{totalPrice}", nvl(mainOrder.getTotalPrice()).toPlainString());
paramMap.put("#{payPrice}", nvl(mainOrder.getWPayOrAPay()).toPlainString());
paramMap.put("#{productList}", sb.toString());
// 模板转换成具体的消息
String content = template.getContent();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
}
// 生成记录
PointsMallMessage message = new PointsMallMessage();
message.setUniqueNo(uniqueNo);
message.setPushType("CALLP");
message.setTemplateId(template.getId());
message.setTouser(mainOrder.getUserId());
message.setContent(content);
message.setContentLink(template.getContentLink());
message.setPlanPushTime(new Date());
// 状态设置为已推送
message.setStatus(1);
dao.insert(message);
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(mainOrder.getUserId()), null);
}
/**
* callP消息推送。
*
* @param userName 用户姓名
* @param subOrder 订单信息
*/
public void pushMessageToGlobal(String userName, PointsMallOrderSub subOrder) {
PointsMallMessageTemplate template = dao.fetch(PointsMallMessageTemplate.class, Cnd.where("id", "=", "22222222").and("delFlag", "=", false));
if (template == null) {
log.info("子单取消后推送消息失败,未配置消息模板");
return;
}
PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", subOrder.getSupplierId()).and("delFlag", "=", false));
String supplierName = supplier == null || StrUtil.isBlank(supplier.getSupplierName()) ? subOrder.getSupplierId() : supplier.getSupplierName();
Date now = new Date();
Date finishTime = subOrder.getOrderFinishTime() == null ? now : subOrder.getOrderFinishTime();
long day = DateUtil.betweenDay(DateUtil.beginOfDay(finishTime), DateUtil.beginOfDay(now), false);
String uniqueNo = template.getId().concat("-").concat(subOrder.getSupplierId()).concat("-")
.concat(subOrder.getOrderId()).concat("-").concat(String.valueOf(day));
StringBuilder sb = new StringBuilder();
sb.append("商品列表: ").append(System.lineSeparator());
List<OrderProInfoDTO> productList = StrUtil.isBlank(subOrder.getExtJson())
? Collections.emptyList()
: JSONUtil.toList(subOrder.getExtJson(), OrderProInfoDTO.class);
productList.forEach(product ->
sb.append(" 商品名称: ").append(product.getName()).append(",商品价格:").append(formatMoney(product.getPrice())).append("").append(System.lineSeparator())
);
Map<String, String> paramMap = new HashMap<>();
paramMap.put("#{userName}", userName);
paramMap.put("#{orderFinishTime}", DateUtil.formatDateTime(finishTime));
paramMap.put("#{pointPrice}", nvl(subOrder.getPointsPrice()).toPlainString());
paramMap.put("#{supplierName}", supplierName);
paramMap.put("#{totalPrice}", nvl(subOrder.getTotalPrice()).toPlainString());
paramMap.put("#{payPrice}", nvl(subOrder.getWPayOrAPay()).toPlainString());
paramMap.put("#{productList}", sb.toString());
String content = template.getContent();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
content = content.replace(entry.getKey(), entry.getValue());
}
Date planPushTime = DateUtil.offsetDay(finishTime, (int) day);
PointsMallMessage message = new PointsMallMessage();
message.setUniqueNo(uniqueNo);
message.setPushType("CALLP");
message.setTemplateId(template.getId());
message.setTouser(subOrder.getUserId());
message.setContent(content);
message.setContentLink(template.getContentLink());
message.setPlanPushTime(planPushTime);
message.setStatus(0);
dao.insert(message);
globalMessageSendService.sendLocalSystemMessage(template.getName(), content, 1, Collections.singletonList(subOrder.getUserId()), null);
}
private String formatMoney(BigDecimal value) {
return nvl(value).setScale(2, RoundingMode.HALF_UP).toPlainString();
}
private BigDecimal nvl(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
}
@@ -0,0 +1,92 @@
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 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 PointsMallPointsLockService pointsMallPointsLockService;
public MallInboundConfirmPurchaseService(Dao dao) {
super(dao);
}
/**
* 确认购买:校验积分是否充足,更新主单支付状态,并锁定主单积分。
*/
public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) {
log.info("9.2.9 确认购买调用开始: {}", dto);
PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (main == null) {
return MallInboundResult.fail(-1, "订单不存在");
}
if (dto.getTotalPrice() == null || dto.getPointsPrice() == null || dto.getWPayOrAPay() == null) {
return MallInboundResult.fail(-1, "参数缺少");
}
BigDecimal totalPrice = money(dto.getTotalPrice());
BigDecimal cash = money(dto.getWPayOrAPay());
BigDecimal points = money(dto.getPointsPrice());
if (totalPrice.compareTo(cash.add(points)) != 0 || money(nvl(main.getTotalPrice())).compareTo(totalPrice) != 0) {
return MallInboundResult.fail(-1, "订单总金额不一致");
}
try {
Trans.exec((Atom) () -> {
// 支付成功,修改主订单支付状态为已支付
updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cash.compareTo(BigDecimal.ZERO) == 0 ? 0 : 1, totalPrice, cash, points);
// 锁定积分,重复确认和并发超用由积分锁服务处理
log.info("9.2.9确认购买,锁定积分:supplierId={}orderId={}points={}", dto.getSupplierId(), dto.getOrderId(), points);
pointsMallPointsLockService.lockMain(main, points);
});
} catch (IllegalArgumentException e) {
log.error(">>> 积分锁定失败:", e);
return MallInboundResult.fail(-1, e.getMessage());
} catch (Exception e) {
log.error(">>> 积分锁定失败:", e);
return MallInboundResult.fail(-1, "服务器内部错误");
}
log.info("9.2.9 确认购买调用结束。");
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,423 @@
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.MallShipOrderEventHandler;
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.enums.MallOrderStatusEnum;
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 {
@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;
@Inject
private MallShipOrderEventHandler mallShipOrderEventHandler;
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);
register(handlerRegistry, mallShipOrderEventHandler);
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;
}
/**
* 更新子单状态。
*
* <p>订单类型为:6(确认收货)时,供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”,
* 需重新调用 9.2.2 获取订单详情接口,补充确认收货时间、发货时间、商品信息等字段。</p>
*
* @param dto 请求参数
* @param statusEnum zhgh/callP 侧订单状态枚举
* @return 操作结果
*/
public MallInboundResult updateSubOrderStatus(MallOrderEventDTO dto, MallOrderStatusEnum statusEnum) {
Integer state = statusEnum.getCode();
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) () -> {
if (statusEnum == MallOrderStatusEnum.RECEIVED) {
fillReceivedSubOrder(dto, sub);
}
sub.setOrderState(state);
if (statusEnum != MallOrderStatusEnum.RECEIVED || sub.getOrderUpdateTime() == null) {
sub.setOrderUpdateTime(new Date());
}
if (statusEnum == MallOrderStatusEnum.RECEIVED && sub.getOrderFinishTime() == null) {
sub.setOrderFinishTime(new Date());
}
if (statusEnum == MallOrderStatusEnum.COMPLETED) {
sub.setOrderCompleteTime(new Date());
}
dao.updateIgnoreNull(sub);
if (statusEnum == MallOrderStatusEnum.CANCELLED || statusEnum == MallOrderStatusEnum.REFUNDED || statusEnum == MallOrderStatusEnum.ABOLISH) {
pointsMallPointsLockService.releaseOrReturnPoints(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO));
}
if (statusEnum == MallOrderStatusEnum.COMPLETED) {
pointsMallPointsLockService.deductCompletedSub(sub);
}
});
log.info("积分商城子单状态更新完成,supplierId={}orderId={}state={}", dto.getSupplierId(), dto.getOrderId(), state);
return MallInboundResult.success();
}
/**
* 订单类型为:6(确认收货)时,调用 9.2.2 获取订单详情接口,补充子单妥投相关信息。
*
* <p>供应商侧确认收货只代表电商处已收货,对 zhgh/callP 侧应更新为已妥投(4)
* 等待 callP 用户侧确认收货后才进入已完成(5)。</p>
*/
private void fillReceivedSubOrder(MallOrderEventDTO dto, PointsMallOrderSub sub) {
if (mallOrderQueryOutboundService == null) {
return;
}
MallOrderQueryDTO queryDTO = new MallOrderQueryDTO();
queryDTO.setSupplierId(dto.getSupplierId());
queryDTO.setOrderId(dto.getOrderId());
queryDTO.setOrderType(2);
log.info("订单类型为:6(确认收货)时,调用9.2.2获取订单详情接口:{}", queryDTO);
try {
MallOutboundResult result = mallOrderQueryOutboundService.queryOrderInfo(queryDTO);
if (result == null || result.getResultCode() != 0 || result.getOrderInfo() == null || result.getOrderInfo().isEmpty()) {
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情失败:{},{}", queryDTO, result == null ? null : result.getMessage());
return;
}
OrderInfoDTO orderInfo = result.getOrderInfo().get(0);
sub.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
sub.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
sub.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime()));
sub.setDeliveryTime(parseDate(orderInfo.getDeliveryTime()));
sub.setDeliveryStatus(parseInteger(orderInfo.getDeliveryStatus()));
if (orderInfo.getOrderProInfos() != null) {
sub.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos()));
}
} catch (Exception e) {
log.error("订单类型为:6(确认收货)时,调用9.2.2 获取订单详情异常:{},{}", queryDTO, e.getMessage());
}
}
public MallInboundResult updateDeliveryStatus(MallOrderEventDTO dto) {
if (StrUtil.isBlank(dto.getSupplierId()) || StrUtil.isBlank(dto.getOrderId())) {
return MallInboundResult.fail(400, "供应商或订单不能为空");
}
List<String> orderIds = splitIds(dto.getOrderId());
if (orderIds.isEmpty()) {
return MallInboundResult.fail(400, "订单信息不能为空");
}
Date deliveryTime = parseDate(dto.getDeliveryTime());
if (deliveryTime == null) {
deliveryTime = new Date();
}
Cnd subCnd = Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "in", orderIds)
.and("delFlag", "=", false);
if (StrUtil.isNotBlank(dto.getUserId())) {
subCnd.and("userId", "=", dto.getUserId());
}
List<PointsMallOrderSub> subs = dao.query(PointsMallOrderSub.class, subCnd);
if (subs.isEmpty()) {
return MallInboundResult.fail(404, "订单不存在");
}
Date finalDeliveryTime = deliveryTime;
Integer deliveredStatus = 1;
Trans.exec((Atom) () -> {
Chain subChain = Chain.make("deliveryStatus", deliveredStatus)
.add("deliveryTime", finalDeliveryTime);
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
subChain.add("logisticsOrderId", dto.getLogisticsOrderId());
}
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
subChain.add("crrgBsnNm", dto.getCrrgBsnNm());
}
dao.update(PointsMallOrderSub.class, subChain, subCnd);
String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), subs.get(0).getMainOrderId());
if (StrUtil.isNotBlank(mainOrderId)) {
Chain mainChain = Chain.make("deliveryStatus", String.valueOf(deliveredStatus))
.add("deliveryTime", finalDeliveryTime);
if (StrUtil.isNotBlank(dto.getLogisticsOrderId())) {
mainChain.add("logisticsOrderId", dto.getLogisticsOrderId());
}
if (StrUtil.isNotBlank(dto.getCrrgBsnNm())) {
mainChain.add("crrgBsnNm", dto.getCrrgBsnNm());
}
dao.update(PointsMallOrderMain.class, mainChain, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", mainOrderId).and("delFlag", "=", false));
}
});
log.info("供应商已发货事件处理完成,supplierId={}mainOrderId={}orderIds={}deliveryStatus={}",
dto.getSupplierId(), dto.getMainOrderId(), orderIds, deliveredStatus);
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,52 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.bean.BeanUtil;
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 com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMainHistory;
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) () -> {
PointsMallOrderMain main = toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY);
dao.insert(main);
// 新增主单历史记录
PointsMallOrderMainHistory orderMainHistory = BeanUtil.copyProperties(main, PointsMallOrderMainHistory.class);
orderMainHistory.setId(null);
dao.insert(orderMainHistory);
});
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,95 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import cn.hutool.core.bean.BeanUtil;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.exception.MallBridgeError;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult;
import com.budwk.app.zhgh.pointsmall.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMainHistory;
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.Objects;
/**
* 9.2.14 支付结果通知接口
*/
@Slf4j
@IocBean(args = {"refer:dao"})
public class MallInboundPurchaseResultService {
private final Dao dao;
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
@Inject
private MallCallPMessagePushService mallCallPMessagePushService;
public MallInboundPurchaseResultService(Dao dao) {
this.dao = dao;
}
public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) {
log.info("9.2.14 支付结果通知接口 调用开始:{}", dto);
// 查询订单是否存在
PointsMallOrderMain orderMain = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId())
.and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false));
if (null == orderMain) {
return MallInboundResult.fail(MallBridgeError.ORDER_NOT_EXISTS.code(), "订单不存在");
}
if (dto.getTotalPrice() == null || dto.getPointsPrice() == null || dto.getWPayOrAPay() == null || dto.getPurchaseResult() == null) {
return MallInboundResult.fail(MallBridgeError.PARAM_ERROR.code(), "参数缺少");
}
// 支付结果
if (dto.getPurchaseResult()) {
log.info("9.2.14 支付结果通知接口,支付成功");
if (BigDecimal.ZERO.compareTo(orderMain.getWPayOrAPay()) != 0) {
// 已现金支付
dao.update(PointsMallOrderMain.class, Chain.make("cashPaid", 2), Cnd.where("id", "=", orderMain.getId()));
}
try {
// callP消息推送
mallCallPMessagePushService.pushMessageToGlobal(orderMain);
} catch (Exception e) {
log.error("9.2.14 支付结果通知接口,callP消息推送:{},{}", dto, e.getMessage());
}
return MallInboundResult.success();
}
if (Objects.equals(orderMain.getOrderState(), MallOrderStatusEnum.CANCELLED.getCode())) {
return MallInboundResult.fail(MallBridgeError.PARAM_ERROR.code(), "订单已取消,请不要重复推送");
}
log.info("9.2.14 支付结果通知接口,支付失败,1.修改主订单支付状态为已取消 2.释放积分");
// 支付失败,修改主订单支付状态为已取消
Trans.exec((Atom) () -> {
Chain mainOrderUpdate = Chain.make("totalPrice", dto.getTotalPrice())
.add("pointsPrice", dto.getPointsPrice())
.add("wPayOrAPay", dto.getWPayOrAPay())
.add("orderState", MallOrderStatusEnum.CANCELLED.getCode());
if (BigDecimal.ZERO.compareTo(orderMain.getWPayOrAPay()) != 0) {
// 取消现金支付
mainOrderUpdate.add("cashPaid", 3);
}
dao.update(PointsMallOrderMain.class, mainOrderUpdate, Cnd.where("id", "=", orderMain.getId()));
// 支付失败,释放积分
pointsMallPointsLockService.releaseMain(orderMain.getUserId(), orderMain.getSupplierId(), orderMain.getOrderId());
// 添加历史记录
PointsMallOrderMainHistory orderMainHistory = BeanUtil.copyProperties(orderMain, PointsMallOrderMainHistory.class);
// id 主键
orderMainHistory.setId(null);
log.info("9.2.14 支付结果通知接口,保存订单主表流水记录");
dao.insert(orderMainHistory);
});
log.info("9.2.14 支付结果通知接口 调用结束");
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();
}
}
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.*;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult;
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;
public interface PointsMallBridgeService {
MallInboundTokenResult getToken(MallTokenDTO dto);
MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto);
MallInboundPointsResult queryPoints(MallPointQueryDTO dto);
MallInboundResult receivePending(MallPendingOrderDTO dto);
MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto);
MallInboundResult purchaseResult(MallPurchaseResultDTO dto);
MallInboundResult receiveEvent(MallOrderEventDTO dto);
MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto);
MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto);
GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto);
MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto);
MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto);
MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto);
}
@@ -0,0 +1,135 @@
package com.budwk.app.zhgh.pointsmall.mallbridge.service.impl;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO;
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;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPendingOrderDTO;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPointQueryDTO;
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.OrderPayFinishNoticeDTO;
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;
import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult;
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.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 org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* 积分商城桥接门面服务。
*
* <p>Controller 只依赖该门面,具体业务拆分到各个专职服务,避免桥接接口逻辑堆在一个实现类里。</p>
*/
@IocBean
public class PointsMallBridgeServiceImpl implements PointsMallBridgeService {
@Inject
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) {
return mallTokenService.getToken(dto);
}
@Override
public MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto) {
return mallTokenService.refreshToken(dto);
}
@Override
public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) {
return mallInboundPointsQueryService.queryPoints(dto);
}
@Override
public MallInboundResult receivePending(MallPendingOrderDTO dto) {
return mallInboundPendingOrderService.receivePending(dto);
}
@Override
public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) {
return mallInboundConfirmPurchaseService.confirmPurchase(dto);
}
@Override
public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) {
return mallInboundPurchaseResultService.purchaseResult(dto);
}
@Override
public MallInboundResult receiveEvent(MallOrderEventDTO dto) {
return mallInboundEventService.processEvent(dto);
}
@Override
public MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto) {
return mallOrderQueryOutboundService.queryOrderInfo(dto);
}
@Override
public MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto) {
return mallInvoiceApplyOutboundService.applyInvoice(dto);
}
@Override
public GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto) {
return mallInvoiceInfoQueryService.queryInvoicesInfo(dto);
}
@Override
public MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto) {
return mallPictureApplyOutboundService.queryFileBase64Info(dto);
}
@Override
public MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto) {
return mallUnReconciledMsgNoticeService.unReconciledMsg(dto);
}
@Override
public MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto) {
return mallPayFinishNoticeService.payCompletion(dto);
}
}
@@ -0,0 +1,303 @@
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.sys.models.Sys_user;
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.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.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.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.Map;
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 boolean hasRequiredOrderEventFields(MallOrderEventDTO dto) {
return dto != null && !StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderId(), dto.getMainOrderId(), dto.getUserId());
}
protected Sys_user fetchUser(String userId) {
Sys_user user = dao.fetch(Sys_user.class, Cnd.where("id", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
if (user != null) {
return user;
}
return dao.fetch(Sys_user.class, Cnd.where("loginname", "=", userId).and("disabled", "=", false).and("delFlag", "=", false));
}
protected PointsMallOrderExchange toExchange(MallOrderEventDTO dto, OrderInfoDTO orderInfo, PointsMallOrderSub sub, Integer orderStatus) {
PointsMallOrderExchange exchange = new PointsMallOrderExchange();
exchange.setOrderMainId(dto.getMainOrderId());
exchange.setOrderId(dto.getOrderId());
exchange.setSupplierId(dto.getSupplierId());
exchange.setUserId(dto.getUserId());
exchange.setAplName(orderInfo.getAplName());
exchange.setOrderCreateTime(parseDate(orderInfo.getCreateTime()));
exchange.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime()));
exchange.setOrderCompleteTime(parseDate(StrUtil.blankToDefault(orderInfo.getCompleteTime(), orderInfo.getOrderFinishTime())));
exchange.setDeliveryStatus(parseInteger(orderInfo.getDeliveryStatus()));
exchange.setFreightCost(orderInfo.getFreightCost());
exchange.setRefund(orderInfo.getRefund());
exchange.setLogisticsOrderId(orderInfo.getLogisticsOrderId());
exchange.setCrrgBsnNm(orderInfo.getCrrgBsnNm());
exchange.setOrderState(orderStatus);
exchange.setTotalPrice(orderInfo.getTotalPrice());
exchange.setWPayOrAPay(orderInfo.getWPayOrAPay());
exchange.setPointsPrice(orderInfo.getPointsPrice());
exchange.setInvoiceCode(orderInfo.getInvoiceCode());
// 商品信息:退换货单详情接口返回的商品信息补齐原订单中的图片和文件信息
exchange.setExtJson(JSONUtil.toJsonStr(mergeProductInfo(orderInfo.getOrderProInfos(), sub.getExtJson())));
return exchange;
}
protected List<OrderProInfoDTO> mergeProductInfo(List<OrderProInfoDTO> products, String subExtJson) {
if (products == null || products.isEmpty()) {
return Collections.emptyList();
}
List<OrderProInfoDTO> subProducts = StrUtil.isBlank(subExtJson) ? Collections.emptyList() : JSONUtil.toList(subExtJson, OrderProInfoDTO.class);
// 将原子单商品列表转换为 Map,key 为 sku;若 sku 重复,保留第一个
Map<String, OrderProInfoDTO> subProductMap = subProducts.stream()
.filter(item -> StrUtil.isNotBlank(item.getSku()))
.collect(Collectors.toMap(OrderProInfoDTO::getSku, item -> item, (left, right) -> left));
// 遍历退换货单商品,从原子单商品 Map 中获取对应图片和文件信息并赋值
products.forEach(product -> {
OrderProInfoDTO origin = subProductMap.get(product.getSku());
if (origin != null) {
product.setPicInfo(origin.getPicInfo());
product.setFileId(origin.getFileId());
product.setFilePath(origin.getFilePath());
}
});
return products;
}
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);
}
}
@@ -0,0 +1,104 @@
package com.budwk.app.zhgh.pointsmall.message.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
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 com.budwk.app.zhgh.pointsmall.message.service.PointsMallMessageService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.List;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/message")
public class PointsMallMessageController {
@Inject
private PointsMallMessageService pointsMallMessageService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/message/index.html")
@SaCheckPermission("points.mall.message")
public void index() {
}
@At
@SaCheckPermission("points.mall.message")
public Result pageData(PointsMallMessageTemplatePageParam param) {
return Result.success(pointsMallMessageService.templatePage(param));
}
@At
@SaCheckPermission("points.mall.message")
public Result list() {
return Result.success(pointsMallMessageService.templateList());
}
@At
@SaCheckPermission("points.mall.message")
public Result get(@Param("id") String id) {
return Result.success(pointsMallMessageService.fetch(id));
}
@At
@SLog(tag = "消息推送", msg = "保存消息模板")
@SaCheckPermission("points.mall.message")
public Result doSave(PointsMallMessageTemplate template) {
try {
pointsMallMessageService.saveTemplate(template);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "消息推送", msg = "删除消息模板")
@SaCheckPermission("points.mall.message")
public Result doDelete(@Param("ids") String ids) {
List<String> idList = StrUtil.split(ids, ',');
if (idList.isEmpty()) {
return Result.error("请选择需要删除的数据");
}
try {
return Result.success(pointsMallMessageService.deleteTemplate(idList));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SaCheckPermission("points.mall.message")
public Result recordPage(PointsMallMessagePageParam param) {
return Result.success(pointsMallMessageService.messagePage(param));
}
@At
@SLog(tag = "消息推送", msg = "重新推送消息")
@SaCheckPermission("points.mall.message")
public Result resend(@Param("ids") String ids) {
List<String> idList = StrUtil.split(ids, ',');
try {
pointsMallMessageService.resend(idList);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "消息推送", msg = "一键推送消息")
@SaCheckPermission("points.mall.message")
public Result resendAll(PointsMallMessagePageParam param) {
pointsMallMessageService.resendAll(param);
return Result.success();
}
}
@@ -0,0 +1,77 @@
package com.budwk.app.zhgh.pointsmall.message.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.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_message")
@Comment("积分商城消息记录")
public class PointsMallMessage extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("业务唯一号")
@ColDefine(type = ColType.VARCHAR, width = 128)
private String uniqueNo;
@Column
@Comment("推送类型")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String pushType;
@Column
@Comment("模板ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String templateId;
@Column
@Comment("目标用户")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String touser;
@Column
@Comment("目标部门")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String toparty;
@Column
@Comment("目标标签")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String totag;
@Column
@Comment("推送内容")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String content;
@Column
@Comment("内容链接")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String contentLink;
@Column
@Comment("计划推送时间")
private Date planPushTime;
@Column
@Comment("状态:0待推送,1已推送,2待重推,3推送失败")
private Integer status;
}
@@ -0,0 +1,85 @@
package com.budwk.app.zhgh.pointsmall.message.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.Default;
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;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_message_template")
@Comment("积分商城消息模板")
public class PointsMallMessageTemplate extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("模板名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String name;
@Column
@Comment("模板类型")
private Integer type;
@Column
@Comment("内容模板")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String content;
@Column
@Comment("内容链接")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String contentLink;
@Column
@Comment("状态:0禁用,1启用")
@Default("1")
private Integer status;
@Column
@Comment("扩展参数")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String extParam;
@Column
@Comment("是否发放积分")
@Default("0")
private Integer isSendPoints;
@Column
@Comment("发放积分")
@Default("0")
@ColDefine(customType = "decimal(18,2)")
private BigDecimal sendPoints;
@Column
@Comment("目标机构列表")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String deptIdList;
@Column
@Comment("目标工会列表")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String unionIdList;
@Column
@Comment("节日日期")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String festivalDate;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.message.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallMessagePageParam extends PageForm {
private String templateId;
private String touser;
private String statuses;
private String createTimeStart;
private String createTimeEnd;
}
@@ -0,0 +1,14 @@
package com.budwk.app.zhgh.pointsmall.message.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallMessageTemplatePageParam extends PageForm {
private String name;
private Integer type;
private Integer status;
}
@@ -0,0 +1,26 @@
package com.budwk.app.zhgh.pointsmall.message.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
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> {
Pagination<PointsMallMessageTemplate> templatePage(PointsMallMessageTemplatePageParam param);
List<PointsMallMessageTemplate> templateList();
void saveTemplate(PointsMallMessageTemplate template);
int deleteTemplate(List<String> ids);
Pagination<PointsMallMessage> messagePage(PointsMallMessagePageParam param);
void resend(List<String> ids);
void resendAll(PointsMallMessagePageParam param);
}
@@ -0,0 +1,200 @@
package com.budwk.app.zhgh.pointsmall.message.service.impl;
import cn.hutool.core.collection.CollUtil;
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;
import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessageTemplatePageParam;
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);
}
@Override
public Pagination<PointsMallMessageTemplate> templatePage(PointsMallMessageTemplatePageParam param) {
Cnd cnd = templateCnd(param);
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessageTemplate.class, cnd);
}
@Override
public List<PointsMallMessageTemplate> templateList() {
return query(Cnd.where("delFlag", "=", false).and("status", "=", 1).desc("createdAt"));
}
@Override
public void saveTemplate(PointsMallMessageTemplate template) {
checkTemplate(template);
if (template.getStatus() == null) {
template.setStatus(1);
}
if (template.getIsSendPoints() == null) {
template.setIsSendPoints(0);
}
if (StrUtil.isBlank(template.getId())) {
insert(template);
} else {
updateIgnoreNull(template);
}
}
@Override
public int deleteTemplate(List<String> ids) {
if (CollUtil.isEmpty(ids)) {
return 0;
}
List<String> canDelete = ids.stream()
.filter(id -> dao().count("points_mall_message_schedule", Cnd.where("templateId", "=", id).and("delFlag", "=", false)) == 0)
.filter(id -> {
PointsMallMessageTemplate template = fetch(id);
return template != null && !Integer.valueOf(5).equals(template.getType());
})
.collect(Collectors.toList());
if (canDelete.isEmpty()) {
throw new IllegalArgumentException("模板正在使用中或为业务消息,无法删除");
}
dao().update(PointsMallMessageTemplate.class, Chain.make("delFlag", true), Cnd.where("id", "in", canDelete));
return canDelete.size();
}
@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());
}
if (StrUtil.isNotBlank(param.getTouser())) {
cnd.and("touser", "like", "%" + param.getTouser() + "%");
}
List<Integer> statuses = splitStatuses(param.getStatuses());
if (CollUtil.isNotEmpty(statuses)) {
cnd.and("status", "in", statuses);
}
if (StrUtil.isNotBlank(param.getCreateTimeStart())) {
cnd.and("createdAt", ">=", DateUtil.parse(param.getCreateTimeStart()).getTime());
}
if (StrUtil.isNotBlank(param.getCreateTimeEnd())) {
cnd.and("createdAt", "<=", DateUtil.parse(param.getCreateTimeEnd()).getTime());
}
return cnd;
}
private void checkTemplate(PointsMallMessageTemplate template) {
if (template == null) {
throw new IllegalArgumentException("模板信息不能为空");
}
if (StrUtil.isBlank(template.getName())) {
throw new IllegalArgumentException("模板名称不能为空");
}
if (template.getType() == null) {
throw new IllegalArgumentException("模板类型不能为空");
}
if (StrUtil.isBlank(template.getContent())) {
throw new IllegalArgumentException("模板内容不能为空");
}
}
private List<Integer> splitStatuses(String statuses) {
if (StrUtil.isBlank(statuses)) {
return List.of();
}
return Arrays.stream(statuses.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.map(Integer::valueOf)
.collect(Collectors.toList());
}
}
@@ -0,0 +1,62 @@
package com.budwk.app.zhgh.pointsmall.order.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.order.param.PointsMallOrderPageParam;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/order")
public class PointsMallOrderController {
@Inject
private PointsMallOrderService pointsMallOrderService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/order/index.html")
@SaCheckPermission("points.mall.order")
public void index() {
}
@At
@SaCheckPermission("points.mall.order")
public Result pageData(PointsMallOrderPageParam param) {
return Result.success(pointsMallOrderService.pageMain(param));
}
@At
@SaCheckPermission("points.mall.order")
public Result supplierList() {
return Result.success(pointsMallOrderService.listSuppliers());
}
@At
@SaCheckPermission("points.mall.order")
public Result subPageData(PointsMallOrderPageParam param) {
return Result.success(pointsMallOrderService.pageSub(param));
}
@At
@SaCheckPermission("points.mall.order")
public Result exchangeList(@Param("mainOrderId") String mainOrderId) {
return Result.success(pointsMallOrderService.listExchange(mainOrderId));
}
@At
@SaCheckPermission("points.mall.order")
public Result productList(@Param("orderId") String orderId) {
return Result.success(pointsMallOrderService.listSubProducts(orderId));
}
@At
@SaCheckPermission("points.mall.order")
public Result exchangeProductList(@Param("orderId") String orderId) {
return Result.success(pointsMallOrderService.listExchangeProducts(orderId));
}
}
@@ -0,0 +1,89 @@
package com.budwk.app.zhgh.pointsmall.order.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
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.PointsMallOrderPageParam;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.adaptor.JsonAdaptor;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.annotation.POST;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/order/sub")
@AdaptBy(type = JsonAdaptor.class)
public class PointsMallOrderSubController {
@Inject
private PointsMallOrderService pointsMallOrderService;
@Inject
private PointsMallOrderSubService pointsMallOrderSubService;
@POST
@At("/page")
@SaCheckPermission("points.mall.order")
public Result pageData(@Param("..") PointsMallOrderPageParam param) {
return Result.success(pointsMallOrderService.pageSub(param));
}
@POST
@At("/product")
@SaCheckPermission("points.mall.order")
public Result productList(@Param("orderId") String orderId) {
return Result.success(pointsMallOrderService.listSubProducts(orderId));
}
@POST
@At("/exchange/list")
@SaCheckPermission("points.mall.order")
public Result exchangeList(@Param("mainOrderId") String mainOrderId) {
return Result.success(pointsMallOrderService.listExchange(mainOrderId));
}
@POST
@At("/exchange/product")
@SaCheckPermission("points.mall.order")
public Result exchangeProductList(@Param("orderId") String orderId) {
return Result.success(pointsMallOrderService.listExchangeProducts(orderId));
}
@POST
@At("/update")
@SaCheckPermission("points.mall.order")
public Result update(@Param("..") PointsMallOrderSub orderSub) {
return result(pointsMallOrderSubService.updateSubOrder(orderSub));
}
@POST
@At("/delete")
@SaCheckPermission("points.mall.order")
public Result delete(@Param("id") String id) {
return result(pointsMallOrderService.deleteSub(id));
}
@POST
@At("/exchange/update")
@SaCheckPermission("points.mall.order")
public Result updateExchange(@Param("..") PointsMallOrderExchange exchange) {
return result(pointsMallOrderService.updateExchange(exchange));
}
@POST
@At("/exchange/delete")
@SaCheckPermission("points.mall.order")
public Result deleteExchange(@Param("id") String id) {
return result(pointsMallOrderService.deleteExchange(id));
}
private Result result(boolean success) {
return success ? Result.success("操作成功") : Result.error("操作失败");
}
}
@@ -0,0 +1,48 @@
package com.budwk.app.zhgh.pointsmall.order.enums;
import lombok.Getter;
/**
* 积分商城订单状态枚举。
*
* <p>zhgh/callP 侧订单状态与供应商商城订单状态是两套规则,不能混用。</p>
*/
@Getter
public enum MallOrderStatusEnum {
// 待付款 - 主单状态
PENDING_PAY(0, "待付款", false),
// 已支付 - 主单状态
FINISH_PAY(1, "已支付", false),
// 进行中 - 子单状态
IN_PROGRESS(2, "进行中", true),
// 已取消 - 主单子单状态
CANCELLED(3, "已取消", true),
// 已妥投(电商处已收货待用户callP侧确认收货)
RECEIVED(4, "已妥投", true),
// 已完成(callP侧用户确认收货)
COMPLETED(5, "已完成", true),
// 已换货(退货换货表的订单状态)
EXCHANGE(6, "换货", true),
// 已退货(退货换货表的订单状态)
REFUNDED(7, "退货", true),
// 已废除
ABOLISH(8, "废除", false);
private final Integer code;
private final String description;
private final Boolean isSaveHis;
MallOrderStatusEnum(Integer code, String description, Boolean isSaveHis) {
this.code = code;
this.description = description;
this.isSaveHis = isSaveHis;
}
}
@@ -0,0 +1,115 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
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_order_exchange")
@Comment("积分商城退换货订单")
public class PointsMallOrderExchange 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 orderMainId;
@Column
@Comment("售后订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
private Integer deliveryStatus;
@Column
@Comment("运费")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("退款")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("商品扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
}
@@ -0,0 +1,128 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
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_order_main")
@Comment("积分商城主订单")
public class PointsMallOrderMain 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 orderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("订单编号")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNumberId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("发货时间")
private Date deliveryTime;
@Column
@Comment("确认收货时间")
private Date orderFinishTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String deliveryStatus;
@Column
@Comment("运费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("退款积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("商品扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
@Column
@Comment("现金支付状态")
private Integer cashPaid;
}
@@ -0,0 +1,136 @@
package com.budwk.app.zhgh.pointsmall.order.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_order_main_history")
@Comment("积分商城主订单历史记录")
public class PointsMallOrderMainHistory 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 orderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("订单编号")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNumberId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("发货时间")
private Date deliveryTime;
@Column
@Comment("确认收货时间")
private Date orderFinishTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String deliveryStatus;
@Column
@Comment("运费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("退款积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("商品扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
@Column
@Comment("现金支付状态")
private Integer cashPaid;
}
@@ -0,0 +1,146 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
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_order_sub")
@Comment("积分商城子订单")
public class PointsMallOrderSub 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 orderId;
@Column
@Comment("订单编号")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNumberId;
@Column
@Comment("主订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String mainOrderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("发货时间")
private Date deliveryTime;
@Column
@Comment("确认收货时间")
private Date orderFinishTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
private Integer deliveryStatus;
@Column
@Comment("运费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("运费现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayFreightCost;
@Column
@Comment("退款积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("退款现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayRefund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
@Column
@Comment("对账状态")
private Integer reconciliationStatus;
@Column
@Comment("开票状态")
private Integer invoiceStatus;
}
@@ -0,0 +1,151 @@
package com.budwk.app.zhgh.pointsmall.order.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_order_sub_history")
@Comment("积分商城子订单历史记录")
public class PointsMallOrderSubHistory 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 orderId;
@Column
@Comment("订单编号")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String orderNumberId;
@Column
@Comment("主订单ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String mainOrderId;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String userId;
@Column
@Comment("应用名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String aplName;
@Column
@Comment("订单创建时间")
private Date orderCreateTime;
@Column
@Comment("订单更新时间")
private Date orderUpdateTime;
@Column
@Comment("发货时间")
private Date deliveryTime;
@Column
@Comment("确认收货时间")
private Date orderFinishTime;
@Column
@Comment("完成时间")
private Date orderCompleteTime;
@Column
@Comment("发货状态")
private Integer deliveryStatus;
@Column
@Comment("运费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal freightCost;
@Column
@Comment("运费现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayFreightCost;
@Column
@Comment("退款积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal refund;
@Column
@Comment("退款现金")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPayRefund;
@Column
@Comment("物流单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String logisticsOrderId;
@Column
@Comment("物流承运商")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String crrgBsnNm;
@Column
@Comment("订单状态")
private Integer orderState;
@Column
@Comment("订单总金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal totalPrice;
@Column
@Comment("现金金额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal wPayOrAPay;
@Column
@Comment("消费积分")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal pointsPrice;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("扩展信息JSON")
@ColDefine(type = ColType.TEXT)
private String extJson;
@Column
@Comment("对账状态")
private Integer reconciliationStatus;
@Column
@Comment("开票状态")
private Integer invoiceStatus;
}
@@ -0,0 +1,58 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_order_sub_product")
@Comment("积分商城子订单商品")
public class PointsMallOrderSubProduct 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 orderSubId;
@Column
@Comment("商品SKU")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String sku;
@Column
@Comment("购买数量")
private Integer number;
@Column
@Comment("商品单价")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal price;
@Column
@Comment("商品名称")
@ColDefine(type = ColType.VARCHAR, width = 300)
private String name;
@Column
@Comment("税率")
@ColDefine(type = ColType.FLOAT, width = 10, precision = 4)
private BigDecimal taxRate;
@Column
@Comment("商品图片信息")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String picInfo;
}
@@ -0,0 +1,98 @@
package com.budwk.app.zhgh.pointsmall.order.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_supplier")
@Comment("积分商城供应商")
public class PointsMallSupplier extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("供应商编码")
@ColDefine(type = ColType.VARCHAR, width = 64)
private String supplierId;
@Column
@Comment("供应商名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String supplierName;
@Column
@Comment("供应商首页跳转地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierUrl;
@Column
@Comment("供应商订单详情地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierOrderUrl;
@Column
@Comment("供应商售后订单地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierReturnOrderUrl;
@Column
@Comment("供应商接口地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierApiUrl;
@Column
@Comment("供应商图片访问地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierPic;
@Column
@Comment("供应商图片文件路径")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String supplierPicPath;
@Column
@Comment("联系人信息")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String contactInfo;
@Column
@Comment("服务商编号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String sroNo;
@Column
@Comment("合作到期时间")
private Date cooperationExpTime;
@Column
@Comment("客户端ID")
@ColDefine(type = ColType.VARCHAR, width = 128)
private String clientId;
@Column
@Comment("客户端密钥")
@ColDefine(type = ColType.VARCHAR, width = 256)
private String clientSecret;
@Column
@Comment("排序号")
@Default("0")
private Integer sortCode;
@Column
@Comment("商城类型")
@Default("1")
private Integer mallType;
}
@@ -0,0 +1,15 @@
package com.budwk.app.zhgh.pointsmall.order.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallMobileOrderPageParam extends PageForm {
private String supplierId;
private String orderId;
private String userId;
private Integer orderState;
}
@@ -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;
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.pointsmall.order.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallOrderPageParam extends PageForm {
private String supplierId;
private String orderId;
private String userId;
private String orderState;
private String logisticsOrderId;
private String mainOrderId;
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.pointsmall.order.result;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SubOrderBuildResult {
private PointsMallOrderSub sub;
private PointsMallOrderSubHistory history;
}
@@ -0,0 +1,45 @@
package com.budwk.app.zhgh.pointsmall.order.service;
import com.budwk.app.base.page.Pagination;
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;
import com.budwk.app.zhgh.pointsmall.order.vo.PointsMallMobileOrderVO;
import org.nutz.dao.entity.Record;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface PointsMallOrderService extends BaseService<PointsMallOrderMain> {
Pagination<Record> pageMain(PointsMallOrderPageParam param);
Pagination<Record> pageSub(PointsMallOrderPageParam param);
Pagination<PointsMallMobileOrderVO> mobileOrderPage(PointsMallMobileOrderPageParam param, String defaultUserId);
Record supplierOrderUrl(String supplierId, String orderId, Integer orderType);
Boolean userOrderReceive(PointsMallMobileOrderReceivedParam param, String defaultUserId);
List<Record> listExchange(String mainOrderId);
List<NutMap> listSubProducts(String orderId);
List<NutMap> listExchangeProducts(String orderId);
boolean deleteSub(String id);
boolean updateExchange(PointsMallOrderExchange exchange);
boolean deleteExchange(String id);
void updateMainOrderStatus(String mainOrderId, String supplierId);
List<PointsMallSupplier> listSuppliers();
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.pointsmall.order.service;
import com.budwk.app.base.service.BaseService;
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.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import java.util.List;
public interface PointsMallOrderSubHistoryService extends BaseService<PointsMallOrderSubHistory> {
List<OrderProInfoDTO> getPictureBase64(String supplierId, String orderId, String queryType, List<OrderProInfoDTO> orderProInfo);
SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId);
}
@@ -0,0 +1,22 @@
package com.budwk.app.zhgh.pointsmall.order.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
public interface PointsMallOrderSubService extends BaseService<PointsMallOrderSub> {
/**
* 根据供应商编码和子订单外部ID查询子订单。
*/
PointsMallOrderSub findBySubOrderExtId(String subOrderId, String supplierId);
/**
* 更新子订单状态和更新时间。
*/
boolean updateSubOrderStatus(PointsMallOrderSub orderSub);
/**
* 更新子订单非空字段和更新时间。
*/
boolean updateSubOrder(PointsMallOrderSub orderSub);
}
@@ -0,0 +1,505 @@
package com.budwk.app.zhgh.pointsmall.order.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO;
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.enums.MallOrderStatusEnum;
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.order.vo.PointsMallMobileOrderVO;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.trans.Atom;
import org.nutz.trans.Trans;
import java.util.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@IocBean(args = {"refer:dao"})
public class PointsMallOrderServiceImpl extends BaseServiceImpl<PointsMallOrderMain> implements PointsMallOrderService {
@Inject
private PointsMallPointsLockService pointsMallPointsLockService;
public PointsMallOrderServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination<Record> pageMain(PointsMallOrderPageParam param) {
Cnd cnd = Cnd.where("m.delFlag", "=", false);
if (StrUtil.isNotBlank(param.getSupplierId())) {
cnd.and("m.supplierId", "=", param.getSupplierId());
}
if (StrUtil.isNotBlank(param.getOrderId())) {
cnd.and("m.orderId", "like", "%" + param.getOrderId() + "%");
}
if (StrUtil.isNotBlank(param.getUserId())) {
cnd.and("u.loginname", "like", "%" + param.getUserId() + "%");
}
if (StrUtil.isNotBlank(param.getOrderState())) {
cnd.and("m.orderState", "=", param.getOrderState());
}
if (StrUtil.isNotBlank(param.getLogisticsOrderId())) {
cnd.and("m.logisticsOrderId", "like", "%" + param.getLogisticsOrderId() + "%");
}
cnd.desc("m.orderCreateTime");
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);
}
@Override
public Pagination<Record> pageSub(PointsMallOrderPageParam param) {
Cnd cnd = Cnd.where("o.delFlag", "=", false);
if (StrUtil.isNotBlank(param.getMainOrderId())) {
cnd.and("o.mainOrderId", "=", param.getMainOrderId());
}
if (StrUtil.isNotBlank(param.getOrderState())) {
cnd.and("o.orderState", "=", param.getOrderState());
}
cnd.desc("o.orderCreateTime");
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);
}
@Override
public Pagination<PointsMallMobileOrderVO> mobileOrderPage(PointsMallMobileOrderPageParam param, String defaultUserId) {
String userId = StrUtil.blankToDefault(param.getUserId(), defaultUserId);
if (StrUtil.isBlank(userId)) {
throw new RuntimeException("用户不存在");
}
String orderState = param.getOrderState() == null ? "-1" : String.valueOf(param.getOrderState());
List<String> orderStateMain = Collections.singletonList("0");
List<String> orderStateSub = Arrays.asList("2", "4", "5", "3");
List<String> orderStateExchange = Arrays.asList("7", "6");
// 获取供应商列表
List<PointsMallSupplier> supplierList = dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false));
// 构建供应商键值对Mapkey为supplierIdvalue为supplierName
Map<String, String> supplierNameMap = supplierList.stream()
.collect(Collectors.toMap(PointsMallSupplier::getSupplierId, PointsMallSupplier::getSupplierName));
Sql sql;
if (StrUtil.equals(orderState, "-1")) { // 全部订单,走主订单查询,查询所有主订单表
sql = getMobileOrderPageSql(userId, orderState);
} else if (orderStateMain.contains(orderState)) { // 主订单状态,走主订单查询,查询主订单表
sql = getMobileOrderMainPageSql(userId, orderState);
} else if (orderStateSub.contains(orderState)) { // 子订单状态,走子订单查询,查询子订单表
sql = getMobileOrderSubPageSql(userId, orderState);
} else if (orderStateExchange.contains(orderState)) { // 退换货订单状态,走退换货查询,查询退换货订单表
sql = getMobileOrderExchangePageSql(userId, orderState);
} else {
throw new RuntimeException("订单状态不正确");
}
Pagination<PointsMallMobileOrderVO> page = listMobileOrderPage(param.getPageNumber(), param.getPageSize(), sql);
for (PointsMallMobileOrderVO item : page.getList()) {
// 塞入供应商名称
item.setSupplierName(supplierNameMap.get(item.getSupplierId()));
// 处理商品信息
fillProductInfo(item);
}
return page;
}
@Override
public Record supplierOrderUrl(String supplierId, String orderId, Integer orderType) {
PointsMallSupplier supplier = dao().fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId).and("delFlag", "=", false));
Record record = new Record();
record.put("supplierOrderUrl", "");
if (supplier != null) {
String url = orderType != null && orderType == 2 ? supplier.getSupplierReturnOrderUrl() : supplier.getSupplierOrderUrl();
record.put("supplierOrderUrl", appendOrderId(url, orderId));
}
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("订单不存在");
}
if (!MallOrderStatusEnum.RECEIVED.getCode().equals(sub.getOrderState())) {
throw new RuntimeException("订单不支持收货");
}
log.info("积分商城H5用户确认收货开始,supplierId={}userId={}orderId={}", sub.getSupplierId(), sub.getUserId(), sub.getOrderId());
try {
Trans.exec((Atom) () -> {
Date now = new Date();
// 使用状态条件更新,避免重复确认收货导致积分重复扣减
int update = dao().update(PointsMallOrderSub.class,
Chain.make("orderState", MallOrderStatusEnum.COMPLETED.getCode())
.add("orderCompleteTime", now)
.add("orderUpdateTime", now),
Cnd.where("id", "=", sub.getId())
.and("orderState", "=", MallOrderStatusEnum.RECEIVED.getCode())
.and("delFlag", "=", false));
if (update <= 0) {
throw new RuntimeException("更新订单失败,请重试");
}
sub.setOrderState(MallOrderStatusEnum.COMPLETED.getCode());
sub.setOrderCompleteTime(now);
sub.setOrderUpdateTime(now);
// 扣除用户积分、增加记录,并同步修改主单锁定积分
pointsMallPointsLockService.deductCompletedSub(sub);
/// 更新主订单状态,所有子单callP端确认收货后,修改主单的状态为已完成
updateMainOrderStatus(sub.getMainOrderId(), sub.getSupplierId());
});
} catch (Exception e) {
log.error(">>> 积分锁定失败:", e);
throw new RuntimeException("系统繁忙,请稍后重试");
}
log.info("积分商城H5用户确认收货完成,supplierId={}userId={}orderId={}", sub.getSupplierId(), sub.getUserId(), sub.getOrderId());
return true;
}
@Override
public List<Record> listExchange(String mainOrderId) {
Cnd cnd = Cnd.where("e.delFlag", "=", false);
if (StrUtil.isNotBlank(mainOrderId)) {
cnd.and("e.orderMainId", "=", mainOrderId);
}
cnd.desc("e.orderCreateTime");
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);
}
@Override
public List<NutMap> listSubProducts(String orderId) {
PointsMallOrderSub orderSub = dao().fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", orderId).and("delFlag", "=", false));
List<NutMap> extProducts = parseProductExtJson(orderId, orderSub == null ? null : orderSub.getExtJson());
if (!extProducts.isEmpty()) {
return extProducts;
}
return listProductTable(orderId);
}
@Override
public List<NutMap> listExchangeProducts(String orderId) {
PointsMallOrderExchange orderExchange = dao().fetch(PointsMallOrderExchange.class, Cnd.where("orderId", "=", orderId).and("delFlag", "=", false));
List<NutMap> extProducts = parseProductExtJson(orderId, orderExchange == null ? null : orderExchange.getExtJson());
if (!extProducts.isEmpty()) {
return extProducts;
}
return listProductTable(orderId);
}
private List<NutMap> listProductTable(String orderId) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(orderId)) {
cnd.and("orderSubId", "=", orderId);
}
cnd.asc("createdAt");
Sql sql = Sqls.create("select * from points_mall_order_sub_product $condition");
sql.setCondition(cnd);
return list(sql).stream().map(this::toProductRecord).collect(Collectors.toList());
}
private List<NutMap> parseProductExtJson(String orderId, String extJson) {
if (StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson)) {
return Collections.emptyList();
}
try {
return JSONUtil.toList(extJson, OrderProInfoDTO.class).stream().map(product -> {
return NutMap.NEW()
.addv("orderId", orderId)
.addv("sku", product.getSku())
.addv("number", product.getNumber())
.addv("price", product.getPrice())
.addv("name", product.getName())
.addv("taxRate", product.getTaxRate())
.addv("picInfo", product.getPicInfo())
.addv("fileId", product.getFileId())
.addv("filePath", product.getFilePath());
}).collect(Collectors.toList());
} catch (Exception e) {
log.warn("解析订单商品扩展信息失败,orderId={}extJson={}", orderId, extJson, e);
return Collections.emptyList();
}
}
private NutMap toProductRecord(Record item) {
return NutMap.NEW()
.addv("orderId", item.getString("orderSubId"))
.addv("sku", item.getString("sku"))
.addv("number", item.get("number"))
.addv("price", item.get("price"))
.addv("name", item.getString("name"))
.addv("taxRate", item.get("taxRate"))
.addv("picInfo", item.getString("picInfo"))
.addv("fileId", item.getString("fileId"))
.addv("filePath", item.getString("filePath"));
}
@Override
public boolean deleteSub(String id) {
if (StrUtil.isBlank(id)) {
return false;
}
return dao().update(PointsMallOrderSub.class, Chain.make("delFlag", true), Cnd.where("id", "=", id)) > 0;
}
@Override
public boolean updateExchange(PointsMallOrderExchange exchange) {
if (exchange == null || StrUtil.isBlank(exchange.getId())) {
return false;
}
exchange.setOrderUpdateTime(new Date());
return dao().updateIgnoreNull(exchange) > 0;
}
@Override
public boolean deleteExchange(String id) {
if (StrUtil.isBlank(id)) {
return false;
}
return dao().update(PointsMallOrderExchange.class, Chain.make("delFlag", true), Cnd.where("id", "=", id)) > 0;
}
@Override
public List<PointsMallSupplier> listSuppliers() {
return dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false).asc("sortCode"));
}
/**
* 移动端订单分页(合并主单、子单、退换货单)
*/
private Sql getMobileOrderPageSql(String userId, String orderState) {
boolean filterOrderState = StrUtil.isNotBlank(orderState) && !StrUtil.equals(orderState, "-1");
Sql sql = Sqls.create("select " +
"id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, cashPaid, " +
"orderState, orderCreateTime, createTime, orderType, extJson " +
"from (" +
"select id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, cashPaid, " +
"orderState, orderCreateTime, createdAt as createTime, 1 as orderType, extJson " +
"from points_mall_order_main " +
"where userId=@userId and orderId not in (" +
"select distinct mainOrderId from points_mall_order_sub where userId=@userId and delFlag=0 and mainOrderId is not null" +
") and delFlag=0 " +
"union all " +
"select id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, null as cashPaid, " +
"orderState, orderCreateTime, createdAt as createTime, 2 as orderType, extJson " +
"from points_mall_order_sub " +
"where userId=@userId and delFlag=0 and orderState != 8" +
") as mall_order " +
"where userId=@userId " +
(filterOrderState ? "and orderState=@orderState " : "") +
"order by createTime desc, orderType");
sql.params().set("userId", userId);
if (filterOrderState) {
sql.params().set("orderState", orderState);
}
return sql;
}
/**
* 移动端主单分页
*/
private Sql getMobileOrderMainPageSql(String userId, String orderState) {
boolean filterOrderState = StrUtil.isNotBlank(orderState) && !StrUtil.equals(orderState, "-1");
Sql sql = Sqls.create("select " +
"id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, cashPaid, " +
"orderState, orderCreateTime, createdAt as createTime, 1 as orderType, extJson " +
"from points_mall_order_main " +
"where userId=@userId and orderId not in (" +
"select distinct mainOrderId from points_mall_order_sub where userId=@userId and delFlag=0 and mainOrderId is not null" +
") " +
(filterOrderState ? "and orderState=@orderState " : "") +
"and delFlag=0 order by createTime desc, orderType asc");
sql.params().set("userId", userId);
if (filterOrderState) {
sql.params().set("orderState", orderState);
}
return sql;
}
/**
* 移动端子单分页(合并主单+子单)
*/
private Sql getMobileOrderSubPageSql(String userId, String orderState) {
boolean filterOrderState = StrUtil.isNotBlank(orderState) && !StrUtil.equals(orderState, "-1");
Sql sql = Sqls.create("select " +
"id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, " +
"orderState, orderCreateTime, createTime, orderType, extJson " +
"from (" +
"select id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, " +
"orderState, orderCreateTime, createdAt as createTime, 1 as orderType, extJson " +
"from points_mall_order_main " +
"where userId=@userId and orderId not in (" +
"select distinct mainOrderId from points_mall_order_sub where userId=@userId and delFlag=0 and mainOrderId is not null" +
") and delFlag=0 " +
"union all " +
"select id, orderId, orderNumberId, supplierId, userId, totalPrice, wPayOrAPay, pointsPrice, " +
"orderState, orderCreateTime, createdAt as createTime, 2 as orderType, extJson " +
"from points_mall_order_sub " +
"where userId=@userId and delFlag=0 and orderState != 8" +
") as mall_order " +
"where userId=@userId " +
(filterOrderState ? "and orderState=@orderState " : "") +
"order by createTime desc, orderType");
sql.params().set("userId", userId);
if (filterOrderState) {
sql.params().set("orderState", orderState);
}
return sql;
}
/**
* 移动端退换货单分页
*/
private Sql getMobileOrderExchangePageSql(String userId, String orderState) {
boolean filterOrderState = StrUtil.isNotBlank(orderState) && !StrUtil.equals(orderState, "-1");
Sql sql = Sqls.create("select " +
"moe.id as id, " +
"moe.orderId as orderId, " +
"null as orderNumberId, " +
"moe.supplierId as supplierId, " +
"moe.userId as userId, " +
"moe.totalPrice as totalPrice, " +
"moe.wPayOrAPay as wPayOrAPay, " +
"moe.pointsPrice as pointsPrice, " +
"moe.refund as refund, " +
"moe.orderState as orderState, " +
"moe.orderCreateTime as orderCreateTime, " +
"moe.createdAt as createTime, " +
"3 as orderType, " +
"moe.extJson as extJson, " +
"moe.orderMainId as mainOrderId, " +
"mos.pointsPrice as relPointsPrice " +
"from points_mall_order_exchange moe " +
"left join points_mall_order_sub mos on moe.orderMainId = mos.orderId " +
"where moe.userId=@userId " +
(filterOrderState ? "and moe.orderState=@orderState " : "") +
"and moe.delFlag=0 and moe.orderState != 8 order by moe.createdAt desc");
sql.params().set("userId", userId);
if (filterOrderState) {
sql.params().set("orderState", orderState);
}
return sql;
}
private void fillProductInfo(PointsMallMobileOrderVO record) {
if (StrUtil.isBlank(record.getExtJson())) {
return;
}
JSONArray array = null;
try {
array = JSONUtil.parseArray(record.getExtJson());
} catch (Exception e) {
log.error("订单类型:{},订单号:{},商品信息:{}", record.getOrderType(), record.getOrderId(), record.getExtJson());
}
if (array == null || array.isEmpty()) return;
int number = 0;
List<String> imgUrlList = new ArrayList<>();
for (int i = 0; i < array.size(); i++) {
JSONObject obj = array.getJSONObject(i);
Integer productNumber = obj.getInt("number");
number = number + (productNumber == null ? 0 : productNumber);
if (i < 3) {
String filePath = obj.getStr("filePath");
if (StrUtil.isNotEmpty(filePath)) {
imgUrlList.add(filePath);
}
}
}
record.setProductNum(number);
record.setProductImgList(imgUrlList);
}
/**
* 更新主订单状态,所有子单取消、退货或完成后,修改主单状态为已完成。
*/
@Override
public void updateMainOrderStatus(String mainOrderId, String supplierId) {
if (StrUtil.hasBlank(mainOrderId, supplierId)) {
return;
}
int count = dao().count(PointsMallOrderSub.class, Cnd.where("mainOrderId", "=", mainOrderId)
.and("supplierId", "=", supplierId).and("delFlag", "=", false));
if (count == 0) {
return;
}
// 只有一个子单主订单随子单变化
if (count == 1) {
// 修改主订单状态为完成
dao().update(PointsMallOrderMain.class,
Chain.make("orderState", MallOrderStatusEnum.COMPLETED.getCode()).add("orderUpdateTime", new Date()),
Cnd.where("orderId", "=", mainOrderId).and("supplierId", "=", supplierId).and("delFlag", "=", false));
log.info("主订单只有一个子单,则更新主订单状态为已完成");
return;
}
log.info("查询所有子订单的状态是否都为已取消、退款或已完成收货");
List<Integer> finishedStatus = Arrays.asList(
MallOrderStatusEnum.CANCELLED.getCode(),
MallOrderStatusEnum.REFUNDED.getCode(),
MallOrderStatusEnum.COMPLETED.getCode()
);
int finishedCount = dao().count(PointsMallOrderSub.class, Cnd.where("mainOrderId", "=", mainOrderId)
.and("supplierId", "=", supplierId)
.and("orderState", "in", finishedStatus)
.and("delFlag", "=", false));
// 如果所有子订单都已取消、已退货或已完成收货,则更新主订单状态为已完成
if (count == finishedCount) {
// 修改主订单状态为完成
dao().update(PointsMallOrderMain.class,
Chain.make("orderState", MallOrderStatusEnum.COMPLETED.getCode()).add("orderUpdateTime", new Date()),
Cnd.where("orderId", "=", mainOrderId).and("supplierId", "=", supplierId).and("delFlag", "=", false));
log.info("所有子订单都已取消、已退货或已完成收货,则更新主订单状态为已完成");
}
}
private Pagination<PointsMallMobileOrderVO> listMobileOrderPage(Integer pageNumber, int pageSize, Sql sql) {
pageNumber = getPageNumber(pageNumber);
pageSize = getPageSize(pageSize);
Pager pager = dao().createPager(pageNumber, pageSize);
pager.setRecordCount((int) Daos.queryCount(dao(), sql));
sql.setPager(pager);
sql.setEntity(dao().getEntity(PointsMallMobileOrderVO.class));
sql.setCallback(Sqls.callback.entities());
dao().execute(sql);
return new Pagination<>(pageNumber, pageSize, pager.getRecordCount(), sql.getList(PointsMallMobileOrderVO.class));
}
private String appendOrderId(String url, String orderId) {
if (StrUtil.isBlank(url) || StrUtil.isBlank(orderId)) {
return url;
}
return url + (url.contains("?") ? "&" : "?") + "orderId=" + orderId;
}
}
@@ -0,0 +1,70 @@
package com.budwk.app.zhgh.pointsmall.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
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.order.enums.MallOrderStatusEnum;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubHistory;
import com.budwk.app.zhgh.pointsmall.order.result.SubOrderBuildResult;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubHistoryService;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Collections;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class PointsMallOrderSubHistoryServiceImpl extends BaseServiceImpl<PointsMallOrderSubHistory> implements PointsMallOrderSubHistoryService {
public PointsMallOrderSubHistoryServiceImpl(Dao dao) {
super(dao);
}
@Override
public List<OrderProInfoDTO> getPictureBase64(String supplierId, String orderId, String queryType, List<OrderProInfoDTO> orderProInfo) {
return CollUtil.isEmpty(orderProInfo) ? Collections.emptyList() : orderProInfo;
}
@Override
public SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId) {
// 3. 检查商品信息
List<OrderProInfoDTO> orderProInfo = info.getOrderProInfos();
if (orderProInfo == null || orderProInfo.isEmpty()) {
// 必须要存在商品信息,若无商品信息返回错误
throw new IllegalArgumentException("缺少商品信息");
}
// 4. 获取图片base64编码,1:商品图片(正常订单)2:发票 3:退货单图片,4:换货单图片
List<OrderProInfoDTO> pictureBase64 = this.getPictureBase64(dto.getSupplierId(), info.getOrderId(), "1", orderProInfo);
// 5. 构建子单对象
PointsMallOrderSub sub = new PointsMallOrderSub();
BeanUtil.copyProperties(info, sub);
sub.setSupplierId(dto.getSupplierId());
sub.setMainOrderId(dto.getMainOrderId());
sub.setUserId(userId);
sub.setOrderState(MallOrderStatusEnum.IN_PROGRESS.getCode());
sub.setExtJson(JSONUtil.toJsonStr(pictureBase64));
sub.setReconciliationStatus(0);
sub.setInvoiceStatus(0);
sub.setOrderCreateTime(StrUtil.isBlank(info.getCreateTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getCreateTime().replace("T", " ")));
sub.setOrderUpdateTime(StrUtil.isBlank(info.getUpdateTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getUpdateTime().replace("T", " ")));
sub.setDeliveryTime(StrUtil.isBlank(info.getDeliveryTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getDeliveryTime().replace("T", " ")));
sub.setOrderFinishTime(StrUtil.isBlank(info.getOrderFinishTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getOrderFinishTime().replace("T", " ")));
sub.setOrderCompleteTime(StrUtil.isBlank(info.getCompleteTime()) ? null : cn.hutool.core.date.DateUtil.parse(info.getCompleteTime().replace("T", " ")));
sub.setDeliveryStatus(StrUtil.isBlank(info.getDeliveryStatus()) ? null : Integer.valueOf(info.getDeliveryStatus()));
// 6. 构建子单历史记录
PointsMallOrderSubHistory subHis = new PointsMallOrderSubHistory();
BeanUtil.copyProperties(sub, subHis);
subHis.setId(null);
return new SubOrderBuildResult(sub, subHis);
}
}
@@ -0,0 +1,48 @@
package com.budwk.app.zhgh.pointsmall.order.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderSubService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.Date;
@IocBean(args = {"refer:dao"})
public class PointsMallOrderSubServiceImpl extends BaseServiceImpl<PointsMallOrderSub> implements PointsMallOrderSubService {
public PointsMallOrderSubServiceImpl(Dao dao) {
super(dao);
}
@Override
public PointsMallOrderSub findBySubOrderExtId(String subOrderId, String supplierId) {
return dao().fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId)
.and("supplierId", "=", supplierId).and("delFlag", "=", false));
}
/**
* 只更新状态相关字段,避免完整子单对象回写影响其它字段。
*/
@Override
public boolean updateSubOrderStatus(PointsMallOrderSub orderSub) {
if (orderSub == null || StrUtil.isBlank(orderSub.getId())) {
return false;
}
Chain chain = Chain.make("orderState", orderSub.getOrderState())
.add("orderUpdateTime", orderSub.getOrderUpdateTime() == null ? new Date() : orderSub.getOrderUpdateTime());
return dao().update(PointsMallOrderSub.class, chain, Cnd.where("id", "=", orderSub.getId()).and("delFlag", "=", false)) > 0;
}
@Override
public boolean updateSubOrder(PointsMallOrderSub orderSub) {
if (orderSub == null || StrUtil.isBlank(orderSub.getId())) {
return false;
}
orderSub.setOrderUpdateTime(new Date());
return dao().updateIgnoreNull(orderSub) > 0;
}
}
@@ -0,0 +1,87 @@
package com.budwk.app.zhgh.pointsmall.order.vo;
import lombok.Data;
import org.nutz.dao.entity.annotation.Column;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 移动端订单列表展示对象。
*/
@Data
public class PointsMallMobileOrderVO {
@Column
private String id;
@Column
private String orderId;
@Column
private String orderNumberId;
@Column
private String mainOrderId;
@Column
private BigDecimal relPointsPrice;
@Column
private String supplierId;
@Column
private String supplierName;
@Column
private String userId;
@Column
private String aplName;
@Column
private Date orderCreateTime;
@Column
private Long createTime;
@Column
private Date orderUpdateTime;
@Column
private Date deliveryTime;
@Column
private Date orderFinishTime;
@Column
private Date orderCompleteTime;
@Column
private Integer deliveryStatus;
@Column
private BigDecimal freightCost;
@Column
private BigDecimal wPayOrAPayFreightCost;
@Column
private BigDecimal refund;
@Column
private BigDecimal wPayOrAPayRefund;
@Column
private String logisticsOrderId;
@Column
private String crrgBsnNm;
@Column
private Integer orderState;
@Column
private BigDecimal totalPrice;
@Column
private BigDecimal wPayOrAPay;
@Column
private BigDecimal pointsPrice;
@Column
private String invoiceCode;
@Column
private String extJson;
@Column
private Integer reconciliationStatus;
@Column
private Integer invoiceStatus;
@Column
private String supplierOrderUrl;
@Column
private String supplierReturnOrderUrl;
@Column
private Integer productNum;
@Column
private Integer orderType;
@Column
private Integer cashPaid;
private List<String> productImgList;
}
@@ -0,0 +1,114 @@
package com.budwk.app.zhgh.pointsmall.points.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
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.PointsMallUserPointsService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.List;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/points")
public class PointsMallUserPointsController {
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/points/index.html")
@SaCheckPermission("points.mall.points")
public void index() {
}
@At
@SaCheckPermission("points.mall.points")
public Result pageData(PointsMallUserPointsPageParam param) {
return Result.success(pointsMallUserPointsService.page(param));
}
@At
@SaCheckPermission("points.mall.points")
public Result logPage(PointsMallUserPointsLogPageParam param) {
if (StrUtil.isBlank(param.getUserId())) {
return Result.error("用户ID不能为空");
}
return Result.success(pointsMallUserPointsService.logPage(param));
}
@At
@SaCheckPermission("points.mall.points")
public Result userIds(PointsMallUserPointsPageParam param) {
return Result.success(pointsMallUserPointsService.userIds(param));
}
@At
@SLog(tag = "用户积分管理", msg = "增加积分")
@SaCheckPermission("points.mall.points")
public Result doGrant(PointsMallUserPointsChangeParam param) {
try {
pointsMallUserPointsService.grant(param);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "用户积分管理", msg = "扣减积分")
@SaCheckPermission("points.mall.points")
public Result doDeduction(PointsMallUserPointsChangeParam param) {
try {
pointsMallUserPointsService.deduction(param);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "用户积分管理", msg = "批量增加积分")
@SaCheckPermission("points.mall.points")
public Result batchGrant(PointsMallUserPointsBatchParam param) {
try {
pointsMallUserPointsService.batchGrant(param);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "用户积分管理", msg = "批量扣减积分")
@SaCheckPermission("points.mall.points")
public Result batchDeduction(PointsMallUserPointsBatchParam param) {
try {
pointsMallUserPointsService.batchDeduction(param);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "用户积分管理", msg = "删除积分记录")
@SaCheckPermission("points.mall.points")
public Result doDelete(@Param("ids") String ids) {
List<String> idList = StrUtil.split(ids, ',');
if (idList.isEmpty()) {
return Result.error("请选择需要删除的数据");
}
pointsMallUserPointsService.deleteByIds(idList);
return Result.success();
}
}

Some files were not shown because too many files have changed in this diff Show More