diff --git a/src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java b/src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java index 6b764010..829997e6 100644 --- a/src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java +++ b/src/main/java/com/budwk/app/sys/controller/v4/SysV4MsgController.java @@ -1,7 +1,6 @@ package com.budwk.app.sys.controller.v4; import cn.dev33.satoken.annotation.SaCheckLogin; -import cn.hutool.core.util.StrUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; @@ -53,7 +52,8 @@ public class SysV4MsgController { m.type, m.sendTime, r.isRead, - r.readTime + r.readTime, + r.createdAt FROM global_message m INNER JOIN global_message_receiver r ON m.id = r.messageId @@ -71,11 +71,7 @@ public class SysV4MsgController { cnd.andEX("m.type", "=", type); - if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - String keyword = "%" + pageForm.getSearchKeyword() + "%"; - cnd.and(Cnd.exps("m.title", "like", keyword).or("m.content", "like", keyword)); - } - cnd.desc("m.sendTime"); + cnd.desc("m.sendTime").desc("r.createdAt"); sql.setCondition(cnd); String title = "审批待办"; diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/message/mode/GlobalMessageSendRequest.java b/src/main/java/com/budwk/app/zhgh/dayofficework/message/mode/GlobalMessageSendRequest.java index 328d03f6..a45c22f1 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/message/mode/GlobalMessageSendRequest.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/message/mode/GlobalMessageSendRequest.java @@ -45,4 +45,24 @@ public class GlobalMessageSendRequest { */ private List receiverIds; + /** + * 是否系统发送 + */ + private Boolean systemSend; + + /** + * 发送人ID + */ + private String senderId; + + /** + * 发送人名称 + */ + private String senderName; + + /** + * 发送人工号 + */ + private String senderLoginName; + } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/message/service/GlobalMessageSendService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/message/service/GlobalMessageSendService.java index eb846e79..6441cafd 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/message/service/GlobalMessageSendService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/message/service/GlobalMessageSendService.java @@ -97,6 +97,66 @@ public class GlobalMessageSendService { sendMessage(request); } + /** + * 发送系统消息(不依赖当前登录token) + */ + public void sendSystemMessage(String title, String content, Integer type, List 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 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 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()); + } + /** * 参数校验 */ diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/message/strategy/impl/LocalMessageSendStrategy.java b/src/main/java/com/budwk/app/zhgh/dayofficework/message/strategy/impl/LocalMessageSendStrategy.java index 895bc2b6..922c22a6 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/message/strategy/impl/LocalMessageSendStrategy.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/message/strategy/impl/LocalMessageSendStrategy.java @@ -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); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/h5/index/PointsMallH5IndexController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/index/PointsMallH5IndexController.java new file mode 100644 index 00000000..ccc0aabf --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/index/PointsMallH5IndexController.java @@ -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 ""; + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java new file mode 100644 index 00000000..b2392a7a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java @@ -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 ""; + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java new file mode 100644 index 00000000..f32cee59 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/controller/PointsMallInvoiceController.java @@ -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()); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java new file mode 100644 index 00000000..45922f11 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceInfo.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceMain.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceMain.java new file mode 100644 index 00000000..d975c29f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/models/PointsMallInvoiceMain.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java new file mode 100644 index 00000000..62c63f96 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceApplyParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceInfoPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceInfoPageParam.java new file mode 100644 index 00000000..b8c9fec8 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceInfoPageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceMainPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceMainPageParam.java new file mode 100644 index 00000000..c53c9cf6 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceMainPageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceOrderPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceOrderPageParam.java new file mode 100644 index 00000000..3b777c73 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/param/PointsMallInvoiceOrderPageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java new file mode 100644 index 00000000..49d8e144 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/PointsMallInvoiceService.java @@ -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 { + + Pagination mainPage(PointsMallInvoiceMainPageParam param); + + Pagination infoPage(PointsMallInvoiceInfoPageParam param); + + Record infoDetail(String id); + + Pagination orderPage(PointsMallInvoiceOrderPageParam param); + + BigDecimal orderTotalPoints(PointsMallInvoiceOrderPageParam param); + + String applyInvoice(PointsMallInvoiceApplyParam param); + + List listSuppliers(); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java new file mode 100644 index 00000000..2ea00da8 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/invoice/service/impl/PointsMallInvoiceServiceImpl.java @@ -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 implements PointsMallInvoiceService { + + public PointsMallInvoiceServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination 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 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 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 orders = dao().query(PointsMallOrderSub.class, invoiceOrderCnd(query, "")); + if (CollUtil.isEmpty(orders)) { + throw new IllegalArgumentException("无符合条件的订单数据"); + } + + List 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 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 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("联系电话不能为空"); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/annotation/MallTokenRequired.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/annotation/MallTokenRequired.java new file mode 100644 index 00000000..ad3ae293 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/annotation/MallTokenRequired.java @@ -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 { +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java new file mode 100644 index 00000000..c669eaf9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java @@ -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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/FinalStatementDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/FinalStatementDTO.java new file mode 100644 index 00000000..0c0738db --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/FinalStatementDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/GetInvoiceInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/GetInvoiceInfoDTO.java new file mode 100644 index 00000000..96285515 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/GetInvoiceInfoDTO.java @@ -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 invoiceInfos; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java new file mode 100644 index 00000000..657ea471 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/InvoiceInfoDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallInvoiceApplyDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallInvoiceApplyDTO.java new file mode 100644 index 00000000..55edcda9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallInvoiceApplyDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderEventDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderEventDTO.java new file mode 100644 index 00000000..941548c1 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderEventDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderQueryDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderQueryDTO.java new file mode 100644 index 00000000..f9a1f976 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallOrderQueryDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPendingOrderDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPendingOrderDTO.java new file mode 100644 index 00000000..7e82d0f0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPendingOrderDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPointQueryDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPointQueryDTO.java new file mode 100644 index 00000000..35d89485 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPointQueryDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseConfirmDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseConfirmDTO.java new file mode 100644 index 00000000..0fa062b9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseConfirmDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseResultDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseResultDTO.java new file mode 100644 index 00000000..4336d829 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallPurchaseResultDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenDTO.java new file mode 100644 index 00000000..5fde506d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenRefreshDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenRefreshDTO.java new file mode 100644 index 00000000..6bf0fc03 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenRefreshDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenResDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenResDTO.java new file mode 100644 index 00000000..a5a2aefc --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/MallTokenResDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoDTO.java new file mode 100644 index 00000000..d9293b57 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderInfoDTO.java @@ -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 orderProInfos; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderPayFinishNoticeDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderPayFinishNoticeDTO.java new file mode 100644 index 00000000..25f9a522 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderPayFinishNoticeDTO.java @@ -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 expFinalInfos; + private Long timestamp; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderProInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderProInfoDTO.java new file mode 100644 index 00000000..5396c5dd --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/OrderProInfoDTO.java @@ -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 picInfo; + private String fileId; + private String filePath; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/PictureQueryDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/PictureQueryDTO.java new file mode 100644 index 00000000..8c0c2440 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/PictureQueryDTO.java @@ -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 fileIdList; + private Long timestamp; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java new file mode 100644 index 00000000..293d8696 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/QueryInvoiceInfoDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java new file mode 100644 index 00000000..d2e26696 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/dto/UnReconciledMsgNoticeDTO.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java new file mode 100644 index 00000000..5014a5ae --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java @@ -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); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java new file mode 100644 index 00000000..b4b8854c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java new file mode 100644 index 00000000..3bd3653a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java new file mode 100644 index 00000000..8d38ae87 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java new file mode 100644 index 00000000..a30bbc09 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java @@ -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)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java new file mode 100644 index 00000000..246f65fd --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java @@ -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侧确认收货)。 + * + *

注意:供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”, + * 不等同于 zhgh/callP 用户确认收货后的“已完成(5)”。

+ */ +@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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java new file mode 100644 index 00000000..8ad093f6 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java @@ -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 代表原子单ID,orderId 代表换货单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 代表子单ID,orderId 代表换货单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); + } + +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java new file mode 100644 index 00000000..4a51cf59 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallShipOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallShipOrderEventHandler.java new file mode 100644 index 00000000..f89becef --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallShipOrderEventHandler.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java new file mode 100644 index 00000000..159a30df --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java @@ -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 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 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 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 mallOrderSubList = new ArrayList<>(); + // 子订单历史记录表 + List 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java new file mode 100644 index 00000000..2410de4d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java @@ -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 代表原子单ID,orderId 代表退货单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 代表子单ID,orderId 代表退货单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); + } + +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/exception/MallBridgeError.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/exception/MallBridgeError.java new file mode 100644 index 00000000..6cd0864a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/exception/MallBridgeError.java @@ -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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/interceptor/MallTokenInterceptor.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/interceptor/MallTokenInterceptor.java new file mode 100644 index 00000000..5d250743 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/interceptor/MallTokenInterceptor.java @@ -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)); + } + }; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundPointsResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundPointsResult.java new file mode 100644 index 00000000..6df11f82 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundPointsResult.java @@ -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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundResult.java new file mode 100644 index 00000000..0376cdd2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundResult.java @@ -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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundTokenResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundTokenResult.java new file mode 100644 index 00000000..271a6993 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallInboundTokenResult.java @@ -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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundResult.java new file mode 100644 index 00000000..4b8aa182 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallOutboundResult.java @@ -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 orderInfo; + + public static MallOutboundResult success() { + return success(null); + } + + public static MallOutboundResult success(List 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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallPictureOutboundResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallPictureOutboundResult.java new file mode 100644 index 00000000..c5f5bdc0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/model/MallPictureOutboundResult.java @@ -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 imageEncodeMap; + + public static MallPictureOutboundResult success(Map 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 getImageEncodeMap() { + return imageEncodeMap; + } + + public void setImageEncodeMap(Map imageEncodeMap) { + this.imageEncodeMap = imageEncodeMap; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/models/PointsMallSupplierTokenStore.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/models/PointsMallSupplierTokenStore.java new file mode 100644 index 00000000..d3126065 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/models/PointsMallSupplierTokenStore.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallCallPMessagePushService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallCallPMessagePushService.java new file mode 100644 index 00000000..41716465 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallCallPMessagePushService.java @@ -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 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 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 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 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 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 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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java new file mode 100644 index 00000000..4e3ee8b2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java new file mode 100644 index 00000000..71b23123 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java @@ -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 handlerRegistry() { + Map 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 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 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 products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson()) + ? Collections.emptyList() + : JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class); + List 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 subOrderIds) { + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)开始执行:{}", dto); + if (subOrderIds.isEmpty()) { + return MallInboundResult.fail(400, "缺少拆单信息"); + } + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)调用外部服务获取子单信息:{}", dto); + List 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 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 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; + } + + /** + * 更新子单状态。 + * + *

订单类型为:6(确认收货)时,供应商侧“确认收货”对应 zhgh/callP 侧“已妥投(4)”, + * 需重新调用 9.2.2 获取订单详情接口,补充确认收货时间、发货时间、商品信息等字段。

+ * + * @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 获取订单详情接口,补充子单妥投相关信息。 + * + *

供应商侧确认收货只代表电商处已收货,对 zhgh/callP 侧应更新为已妥投(4), + * 等待 callP 用户侧确认收货后才进入已完成(5)。

+ */ + 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 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 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java new file mode 100644 index 00000000..28b0053d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java @@ -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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java new file mode 100644 index 00000000..822f1eef --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java @@ -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())); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java new file mode 100644 index 00000000..e0121300 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java @@ -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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java new file mode 100644 index 00000000..84b0c0e9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java @@ -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 orderIds = splitIds(dto.getOrderIds()); + log.info("积分商城开票申请开始,supplierId={},settlementId={},orderCount={}", dto.getSupplierId(), dto.getSettlementId(), orderIds.size()); + List 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 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)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java new file mode 100644 index 00000000..656571f0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java @@ -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 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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java new file mode 100644 index 00000000..d6a079a2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java @@ -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 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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java new file mode 100644 index 00000000..66ea6666 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java @@ -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 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java new file mode 100644 index 00000000..98cf33b7 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java @@ -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 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 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 products = dao.query(PointsMallOrderSubProduct.class, cnd); + Map map = new LinkedHashMap<>(); + for (PointsMallOrderSubProduct product : products) { + map.put(product.getSku(), product.getPicInfo()); + } + return map; + } + + private Map 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 invoices = dao.query(PointsMallInvoiceInfo.class, cnd); + Map 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java new file mode 100644 index 00000000..52588599 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java @@ -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)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java new file mode 100644 index 00000000..7bee8b24 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java @@ -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 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/PointsMallBridgeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/PointsMallBridgeService.java new file mode 100644 index 00000000..334da01a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/PointsMallBridgeService.java @@ -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); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java new file mode 100644 index 00000000..deb5eb11 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java @@ -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; + +/** + * 积分商城桥接门面服务。 + * + *

Controller 只依赖该门面,具体业务拆分到各个专职服务,避免桥接接口逻辑堆在一个实现类里。

+ */ +@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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java new file mode 100644 index 00000000..c1f928bb --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java @@ -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; + +/** + * 商城桥接公共支撑能力。 + * + *

只放通用转换和常量,具体接口业务放在各自服务中。

+ */ +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 mergeProductInfo(List products, String subExtJson) { + if (products == null || products.isEmpty()) { + return Collections.emptyList(); + } + List subProducts = StrUtil.isBlank(subExtJson) ? Collections.emptyList() : JSONUtil.toList(subExtJson, OrderProInfoDTO.class); + // 将原子单商品列表转换为 Map,key 为 sku;若 sku 重复,保留第一个 + Map 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 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 readProducts(String orderId, String extJson) { + List 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 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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java new file mode 100644 index 00000000..26016b03 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java @@ -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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/controller/PointsMallMessageController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/controller/PointsMallMessageController.java new file mode 100644 index 00000000..c21b6530 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/controller/PointsMallMessageController.java @@ -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 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 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(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java new file mode 100644 index 00000000..f5cdc940 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessageTemplate.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessageTemplate.java new file mode 100644 index 00000000..910ad482 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessageTemplate.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessagePageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessagePageParam.java new file mode 100644 index 00000000..e2831b9a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessagePageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessageTemplatePageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessageTemplatePageParam.java new file mode 100644 index 00000000..a451ef44 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/param/PointsMallMessageTemplatePageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java new file mode 100644 index 00000000..798765ec --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java @@ -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 { + + Pagination templatePage(PointsMallMessageTemplatePageParam param); + + List templateList(); + + void saveTemplate(PointsMallMessageTemplate template); + + int deleteTemplate(List ids); + + Pagination messagePage(PointsMallMessagePageParam param); + + void resend(List ids); + + void resendAll(PointsMallMessagePageParam param); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java new file mode 100644 index 00000000..0b379f37 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java @@ -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 implements PointsMallMessageService { + + @Inject + private GlobalMessageSendService globalMessageSendService; + + public PointsMallMessageServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination templatePage(PointsMallMessageTemplatePageParam param) { + Cnd cnd = templateCnd(param); + cnd.desc("createdAt"); + return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessageTemplate.class, cnd); + } + + @Override + public List 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 ids) { + if (CollUtil.isEmpty(ids)) { + return 0; + } + List 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 messagePage(PointsMallMessagePageParam param) { + Cnd cnd = messageCnd(param); + cnd.desc("createdAt"); + return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessage.class, cnd); + } + + @Override + public void resend(List ids) { + if (CollUtil.isEmpty(ids)) { + throw new IllegalArgumentException("请选择消息记录"); + } + List 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 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 loginNames = StrUtil.splitTrim(message.getTouser(), ","); + List 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 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 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 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()); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderController.java new file mode 100644 index 00000000..d0db8a71 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderController.java @@ -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)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderSubController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderSubController.java new file mode 100644 index 00000000..67426b00 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/controller/PointsMallOrderSubController.java @@ -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("操作失败"); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/enums/MallOrderStatusEnum.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/enums/MallOrderStatusEnum.java new file mode 100644 index 00000000..372b7473 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/enums/MallOrderStatusEnum.java @@ -0,0 +1,48 @@ +package com.budwk.app.zhgh.pointsmall.order.enums; + +import lombok.Getter; + +/** + * 积分商城订单状态枚举。 + * + *

zhgh/callP 侧订单状态与供应商商城订单状态是两套规则,不能混用。

+ */ +@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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderExchange.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderExchange.java new file mode 100644 index 00000000..7af2404f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderExchange.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMain.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMain.java new file mode 100644 index 00000000..0a2df4c0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMain.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMainHistory.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMainHistory.java new file mode 100644 index 00000000..07f2461c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderMainHistory.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSub.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSub.java new file mode 100644 index 00000000..ae68a4c2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSub.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubHistory.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubHistory.java new file mode 100644 index 00000000..c6e3f41d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubHistory.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubProduct.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubProduct.java new file mode 100644 index 00000000..8c72ff9f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallOrderSubProduct.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallSupplier.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallSupplier.java new file mode 100644 index 00000000..550261a2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/models/PointsMallSupplier.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderPageParam.java new file mode 100644 index 00000000..d77b17b5 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderPageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderReceivedParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderReceivedParam.java new file mode 100644 index 00000000..1666c93f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderReceivedParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallOrderPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallOrderPageParam.java new file mode 100644 index 00000000..99b72d18 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallOrderPageParam.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/result/SubOrderBuildResult.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/result/SubOrderBuildResult.java new file mode 100644 index 00000000..51c67255 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/result/SubOrderBuildResult.java @@ -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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderService.java new file mode 100644 index 00000000..c48b0d51 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderService.java @@ -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 { + + Pagination pageMain(PointsMallOrderPageParam param); + + Pagination pageSub(PointsMallOrderPageParam param); + + Pagination mobileOrderPage(PointsMallMobileOrderPageParam param, String defaultUserId); + + Record supplierOrderUrl(String supplierId, String orderId, Integer orderType); + + Boolean userOrderReceive(PointsMallMobileOrderReceivedParam param, String defaultUserId); + + List listExchange(String mainOrderId); + + List listSubProducts(String orderId); + + List listExchangeProducts(String orderId); + + boolean deleteSub(String id); + + boolean updateExchange(PointsMallOrderExchange exchange); + + boolean deleteExchange(String id); + + void updateMainOrderStatus(String mainOrderId, String supplierId); + + List listSuppliers(); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubHistoryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubHistoryService.java new file mode 100644 index 00000000..5fe10385 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubHistoryService.java @@ -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 { + + List getPictureBase64(String supplierId, String orderId, String queryType, List orderProInfo); + + SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubService.java new file mode 100644 index 00000000..bf80a3d3 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/PointsMallOrderSubService.java @@ -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 { + + /** + * 根据供应商编码和子订单外部ID查询子订单。 + */ + PointsMallOrderSub findBySubOrderExtId(String subOrderId, String supplierId); + + /** + * 更新子订单状态和更新时间。 + */ + boolean updateSubOrderStatus(PointsMallOrderSub orderSub); + + /** + * 更新子订单非空字段和更新时间。 + */ + boolean updateSubOrder(PointsMallOrderSub orderSub); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java new file mode 100644 index 00000000..46d0be4a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java @@ -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 implements PointsMallOrderService { + + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + + public PointsMallOrderServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination 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 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 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 orderStateMain = Collections.singletonList("0"); + List orderStateSub = Arrays.asList("2", "4", "5", "3"); + List orderStateExchange = Arrays.asList("7", "6"); + + // 获取供应商列表 + List supplierList = dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false)); + // 构建供应商键值对Map,key为supplierId,value为supplierName + Map 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 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 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 listSubProducts(String orderId) { + PointsMallOrderSub orderSub = dao().fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", orderId).and("delFlag", "=", false)); + List extProducts = parseProductExtJson(orderId, orderSub == null ? null : orderSub.getExtJson()); + if (!extProducts.isEmpty()) { + return extProducts; + } + return listProductTable(orderId); + } + + @Override + public List listExchangeProducts(String orderId) { + PointsMallOrderExchange orderExchange = dao().fetch(PointsMallOrderExchange.class, Cnd.where("orderId", "=", orderId).and("delFlag", "=", false)); + List extProducts = parseProductExtJson(orderId, orderExchange == null ? null : orderExchange.getExtJson()); + if (!extProducts.isEmpty()) { + return extProducts; + } + return listProductTable(orderId); + } + + private List 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 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 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 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 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 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; + } + +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubHistoryServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubHistoryServiceImpl.java new file mode 100644 index 00000000..a2e9eef2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubHistoryServiceImpl.java @@ -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 implements PointsMallOrderSubHistoryService { + + public PointsMallOrderSubHistoryServiceImpl(Dao dao) { + super(dao); + } + + @Override + public List getPictureBase64(String supplierId, String orderId, String queryType, List orderProInfo) { + return CollUtil.isEmpty(orderProInfo) ? Collections.emptyList() : orderProInfo; + } + + @Override + public SubOrderBuildResult buildSubOrder(OrderInfoDTO info, MallOrderEventDTO dto, String userId) { + // 3. 检查商品信息 + List orderProInfo = info.getOrderProInfos(); + if (orderProInfo == null || orderProInfo.isEmpty()) { + // 必须要存在商品信息,若无商品信息返回错误 + throw new IllegalArgumentException("缺少商品信息"); + } + + // 4. 获取图片base64编码,1:商品图片(正常订单)2:发票 3:退货单图片,4:换货单图片 + List 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); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubServiceImpl.java new file mode 100644 index 00000000..8750b857 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderSubServiceImpl.java @@ -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 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; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/vo/PointsMallMobileOrderVO.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/vo/PointsMallMobileOrderVO.java new file mode 100644 index 00000000..fc20402e --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/vo/PointsMallMobileOrderVO.java @@ -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 productImgList; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/controller/PointsMallUserPointsController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/controller/PointsMallUserPointsController.java new file mode 100644 index 00000000..6067ec3b --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/controller/PointsMallUserPointsController.java @@ -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 idList = StrUtil.split(ids, ','); + if (idList.isEmpty()) { + return Result.error("请选择需要删除的数据"); + } + pointsMallUserPointsService.deleteByIds(idList); + return Result.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/enums/PointsMallPointLockStatusEnum.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/enums/PointsMallPointLockStatusEnum.java new file mode 100644 index 00000000..1ea78632 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/enums/PointsMallPointLockStatusEnum.java @@ -0,0 +1,20 @@ +package com.budwk.app.zhgh.pointsmall.points.enums; + +import lombok.Getter; + +/** + * 积分锁定状态。 + */ +@Getter +public enum PointsMallPointLockStatusEnum { + LOCK(0, "锁定"), + UNLOCK(1, "释放"); + + private final Integer code; + private final String description; + + PointsMallPointLockStatusEnum(Integer code, String description) { + this.code = code; + this.description = description; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallMainPointsLock.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallMainPointsLock.java new file mode 100644 index 00000000..267854d3 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallMainPointsLock.java @@ -0,0 +1,62 @@ +package com.budwk.app.zhgh.pointsmall.points.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 主单积分锁定表。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("points_mall_main_points_lock") +@Comment("积分商城主单积分锁定") +public class PointsMallMainPointsLock extends BaseModel implements Serializable { + + @Name + @Column + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("主订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String orderId; + + @Column + @Comment("供应商ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String supplierId; + + @Column + @Comment("锁定积分") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal lockAmount; + + @Column + @Comment("积分状态:0锁定,1释放") + private Integer status; + + @Column + @Comment("锁定时间") + private Date lockTime; + +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallSubPointsLock.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallSubPointsLock.java new file mode 100644 index 00000000..b6dc2600 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallSubPointsLock.java @@ -0,0 +1,66 @@ +package com.budwk.app.zhgh.pointsmall.points.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 子单积分变动锁定表。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("points_mall_sub_points_lock") +@Comment("积分商城子单积分变动锁定") +public class PointsMallSubPointsLock extends BaseModel implements Serializable { + + @Name + @Column + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("主订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String mainOrderId; + + @Column + @Comment("子订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String subOrderId; + + @Column + @Comment("供应商ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String supplierId; + + @Column + @Comment("变动积分,扣减为负数,返还为正数") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal changePoints; + + @Column + @Comment("子单状态") + private Integer orderSubStatus; + + @Column + @Comment("锁定时间") + private Date lockTime; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPoints.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPoints.java new file mode 100644 index 00000000..458572bb --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPoints.java @@ -0,0 +1,59 @@ +package com.budwk.app.zhgh.pointsmall.points.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.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_user_points") +@Comment("积分商城用户积分") +public class PointsMallUserPoints extends BaseModel implements Serializable { + + @Name + @Column + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("总积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal totalPoints; + + @Column + @Comment("已用积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal usedPoints; + + @Column + @Comment("可用积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal availablePoints; + + @Column + @Comment("锁定积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal lockPoints; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPointsLog.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPointsLog.java new file mode 100644 index 00000000..c634c30b --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallUserPointsLog.java @@ -0,0 +1,61 @@ +package com.budwk.app.zhgh.pointsmall.points.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.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_user_points_log") +@Comment("积分商城用户积分流水") +public class PointsMallUserPointsLog extends BaseModel implements Serializable { + + @Name + @Column + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("变动积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal changeAmount; + + @Column + @Comment("变动类型") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String changeType; + + @Column + @Comment("子订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String subOrderId; + + @Column + @Comment("消息模板ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String templateId; + + @Column + @Comment("备注") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String remark; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsBatchParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsBatchParam.java new file mode 100644 index 00000000..8f45872c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsBatchParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.points.param; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PointsMallUserPointsBatchParam { + + private String userIds; + private BigDecimal points; + private String remark; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsChangeParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsChangeParam.java new file mode 100644 index 00000000..16103f0c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsChangeParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.points.param; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PointsMallUserPointsChangeParam { + + private String userId; + private BigDecimal points; + private String remark; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsLogPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsLogPageParam.java new file mode 100644 index 00000000..3eee042f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsLogPageParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.points.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallUserPointsLogPageParam extends PageForm { + + private String userId; + private String changeType; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsPageParam.java new file mode 100644 index 00000000..5f973c27 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/param/PointsMallUserPointsPageParam.java @@ -0,0 +1,16 @@ +package com.budwk.app.zhgh.pointsmall.points.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallUserPointsPageParam extends PageForm { + + private String account; + private String realName; + private String mobile; + private String unitId; + private Boolean member; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallPointsLockService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallPointsLockService.java new file mode 100644 index 00000000..b4c3f754 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallPointsLockService.java @@ -0,0 +1,25 @@ +package com.budwk.app.zhgh.pointsmall.points.service; + +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; + +import java.math.BigDecimal; +import java.util.List; + +public interface PointsMallPointsLockService { + + BigDecimal sumLocked(String userId); + + void lockMain(PointsMallOrderMain mainOrder, BigDecimal lockAmount); + + void releaseMain(String userId, String supplierId, String orderId); + + void createSubLocks(PointsMallOrderMain mainOrder, List subOrders); + + /** + * 子单取消时释放锁定积分,退货时退回用户积分。 + */ + void releaseOrReturnPoints(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund); + + void deductCompletedSub(PointsMallOrderSub subOrder); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallUserPointsService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallUserPointsService.java new file mode 100644 index 00000000..a3f49743 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallUserPointsService.java @@ -0,0 +1,36 @@ +package com.budwk.app.zhgh.pointsmall.points.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPoints; +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 org.nutz.dao.entity.Record; + +import java.math.BigDecimal; +import java.util.List; + +public interface PointsMallUserPointsService extends BaseService { + + Pagination page(PointsMallUserPointsPageParam param); + + Pagination logPage(PointsMallUserPointsLogPageParam param); + + List userIds(PointsMallUserPointsPageParam param); + + BigDecimal availablePoints(String userId); + + Record userInfo(String userId); + + void grant(PointsMallUserPointsChangeParam param); + + void deduction(PointsMallUserPointsChangeParam param); + + void batchGrant(PointsMallUserPointsBatchParam param); + + void batchDeduction(PointsMallUserPointsBatchParam param); + + void deleteByIds(List ids); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java new file mode 100644 index 00000000..703a2af5 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java @@ -0,0 +1,240 @@ +package com.budwk.app.zhgh.pointsmall.points.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +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.enums.PointsMallPointLockStatusEnum; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallMainPointsLock; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallSubPointsLock; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPoints; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPointsLog; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * 积分锁定、释放和实际扣减服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class PointsMallPointsLockServiceImpl implements PointsMallPointsLockService { + + private final Dao dao; + + public PointsMallPointsLockServiceImpl(Dao dao) { + this.dao = dao; + } + + @Override + public BigDecimal sumLocked(String userId) { + if (StrUtil.isBlank(userId)) { + return BigDecimal.ZERO; + } + List list = dao.query(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()).and("delFlag", "=", false)); + return list.stream().map(PointsMallMainPointsLock::getLockAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); + } + + @Override + public void lockMain(PointsMallOrderMain mainOrder, BigDecimal lockAmount) { + if (mainOrder == null || StrUtil.hasBlank(mainOrder.getUserId(), mainOrder.getOrderId(), mainOrder.getSupplierId())) { + throw new IllegalArgumentException("主单积分锁定参数不能为空"); + } + BigDecimal amount = nvl(lockAmount); + if (amount.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + PointsMallMainPointsLock exists = dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", mainOrder.getUserId()) + .and("supplierId", "=", mainOrder.getSupplierId()) + .and("orderId", "=", mainOrder.getOrderId()) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + if (exists != null) { + throw new IllegalArgumentException("该订单已经确认购买,请不要重复确认购买"); + } + PointsMallMainPointsLock lock = new PointsMallMainPointsLock(); + lock.setUserId(mainOrder.getUserId()); + lock.setSupplierId(mainOrder.getSupplierId()); + lock.setOrderId(mainOrder.getOrderId()); + lock.setLockAmount(amount); + lock.setStatus(PointsMallPointLockStatusEnum.LOCK.getCode()); + lock.setLockTime(new Date()); + dao.insert(lock); + log.info("积分商城主单积分锁定完成,userId={},supplierId={},orderId={},lockAmount={}", + mainOrder.getUserId(), mainOrder.getSupplierId(), mainOrder.getOrderId(), amount); + } + + @Override + public void releaseMain(String userId, String supplierId, String orderId) { + if (StrUtil.hasBlank(userId, supplierId, orderId)) { + return; + } + dao.update(PointsMallMainPointsLock.class, + Chain.make("status", PointsMallPointLockStatusEnum.UNLOCK.getCode()).add("delFlag", true), + Cnd.where("userId", "=", userId) + .and("supplierId", "=", supplierId) + .and("orderId", "=", orderId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + log.info("积分商城主单积分释放完成,userId={},supplierId={},orderId={}", userId, supplierId, orderId); + } + + @Override + public void createSubLocks(PointsMallOrderMain mainOrder, List subOrders) { + if (mainOrder == null || subOrders == null || subOrders.isEmpty()) { + return; + } + for (PointsMallOrderSub sub : subOrders) { + if (sub == null || StrUtil.isBlank(sub.getOrderId())) { + continue; + } + PointsMallSubPointsLock exists = dao.fetch(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", sub.getSupplierId()) + .and("subOrderId", "=", sub.getOrderId()).and("delFlag", "=", false)); + if (exists != null) { + continue; + } + PointsMallSubPointsLock lock = new PointsMallSubPointsLock(); + lock.setUserId(mainOrder.getUserId()); + lock.setMainOrderId(sub.getMainOrderId()); + lock.setSubOrderId(sub.getOrderId()); + lock.setSupplierId(sub.getSupplierId()); + lock.setChangePoints(nvl(sub.getPointsPrice()).negate()); + lock.setOrderSubStatus(sub.getOrderState()); + lock.setLockTime(new Date()); + dao.insert(lock); + } + log.info("积分商城子单积分锁定记录生成完成,supplierId={},mainOrderId={},subOrderCount={}", + mainOrder.getSupplierId(), mainOrder.getOrderId(), subOrders.size()); + } + + @Override + public void releaseOrReturnPoints(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund) { + if (subOrder == null || StrUtil.hasBlank(subOrder.getSupplierId(), subOrder.getOrderId(), subOrder.getMainOrderId())) { + return; + } + log.info("9.2.1 生成订单信息推送-订单状态为已取消或者已退货,释放积分开始执行"); + PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId()); + if (mainLock == null) { + return; + } + PointsMallSubPointsLock lastSubLock = latestSubLock(subOrder.getSupplierId(), subOrder.getOrderId()); + if (lastSubLock == null) { + return; + } + BigDecimal subLockPoint = nvl(lastSubLock.getChangePoints()).abs(); + // 订单取消,判断积分有没有超过主单锁定的积分 + if (Objects.equals(MallOrderStatusEnum.CANCELLED.getCode(), orderState) && nvl(mainLock.getLockAmount()).compareTo(subLockPoint) < 0) { + log.info("当前子单{}:释放的积分{},超过主表表已锁定积分{}", subOrder.getOrderId(), subLockPoint, mainLock.getLockAmount()); + throw new IllegalArgumentException("积分不足,超过主表表已锁定积分"); + } + // 新增一条积分记录,积分就是原订单的取反积分 + PointsMallSubPointsLock insert = new PointsMallSubPointsLock(); + BeanUtil.copyProperties(lastSubLock, insert); + // 确保是新记录 + insert.setId(null); + insert.setOrderSubStatus(orderState); + // 如果是退货场景,则取值订单退款积分 + BigDecimal returnPoint = Objects.equals(MallOrderStatusEnum.REFUNDED.getCode(), orderState) ? nvl(refund) : nvl(lastSubLock.getChangePoints()).negate(); + insert.setChangePoints(returnPoint); + // 子单的积分没有释放或锁定状态,记录是怎么加减分的(订单状态) + insert.setOrderSubStatus(orderState); + insert.setLockTime(new Date()); + dao.insert(insert); + + // 如果订单是已妥投/取消订单,就需要把主单的锁定积分给释放掉 + if (Objects.equals(subOrder.getOrderState(), MallOrderStatusEnum.RECEIVED.getCode()) + || Objects.equals(orderState, MallOrderStatusEnum.CANCELLED.getCode())) { + // 同步修改主单锁定的积分,把扣减的子单积分恢复到主单的锁定积分里面 + org.nutz.dao.sql.Sql updateSql = org.nutz.dao.Sqls.create("update points_mall_main_points_lock set lockAmount = case when lockAmount - @returnPoint < 0 then 0 else lockAmount - @returnPoint end where id = @id"); + updateSql.params().set("returnPoint", returnPoint); + updateSql.params().set("id", mainLock.getId()); + dao.execute(updateSql); + log.info("9.2.1 生成订单信息推送-订单状态为已取消或者已退货,释放积分结束"); + return; + } + + if (!Objects.equals(subOrder.getOrderState(), MallOrderStatusEnum.COMPLETED.getCode())) { + return; + } + // 如果CallP确认收货了,直接给用户加积分 + /// 退货还需要给用户积分加return + PointsMallUserPoints userPoints = dao.fetch(PointsMallUserPoints.class, Cnd.where("userId", "=", subOrder.getUserId()).and("delFlag", "=", false)); + if (userPoints == null) { + throw new IllegalArgumentException("用户积分不存在"); + } + org.nutz.dao.sql.Sql userPointsSql = org.nutz.dao.Sqls.create("update points_mall_user_points set availablePoints = availablePoints + @returnPoint where userId = @userId"); + userPointsSql.params().set("returnPoint", returnPoint); + userPointsSql.params().set("userId", subOrder.getUserId()); + dao.execute(userPointsSql); + } + + @Override + public void deductCompletedSub(PointsMallOrderSub subOrder) { + if (subOrder == null || StrUtil.hasBlank(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId(), subOrder.getOrderId())) { + return; + } + PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId()); + if (mainLock == null) { + return; + } + BigDecimal subtract = nvl(subOrder.getPointsPrice()).subtract(nvl(subOrder.getRefund())); + if (subtract.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + PointsMallUserPoints userPoints = dao.fetch(PointsMallUserPoints.class, Cnd.where("userId", "=", subOrder.getUserId()).and("delFlag", "=", false)); + if (userPoints == null) { + throw new IllegalArgumentException("用户积分不存在"); + } + BigDecimal availableAfter = nvl(userPoints.getAvailablePoints()).subtract(subtract); + if (availableAfter.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("积分不足,无法扣减"); + } + dao.update(PointsMallUserPoints.class, + Chain.make("availablePoints", availableAfter).add("usedPoints", nvl(userPoints.getUsedPoints()).add(subtract)), + Cnd.where("id", "=", userPoints.getId())); + + PointsMallUserPointsLog logRecord = new PointsMallUserPointsLog(); + logRecord.setUserId(subOrder.getUserId()); + logRecord.setChangeAmount(subtract); + logRecord.setChangeType("DEDUCTION"); + logRecord.setSubOrderId(subOrder.getOrderId()); + logRecord.setRemark("积分商城订单确认完成扣减"); + dao.insert(logRecord); + + BigDecimal lockAfter = nvl(mainLock.getLockAmount()).subtract(subtract); + if (lockAfter.compareTo(BigDecimal.ZERO) < 0) { + lockAfter = BigDecimal.ZERO; + } + dao.update(PointsMallMainPointsLock.class, Chain.make("lockAmount", lockAfter), Cnd.where("id", "=", mainLock.getId())); + log.info("积分商城子单确认完成扣减积分,userId={},supplierId={},subOrderId={},points={}", + subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getOrderId(), subtract); + } + + private PointsMallMainPointsLock activeMainLock(String userId, String supplierId, String orderId) { + return dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId) + .and("supplierId", "=", supplierId) + .and("orderId", "=", orderId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + } + + private PointsMallSubPointsLock latestSubLock(String supplierId, String subOrderId) { + List list = dao.query(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", supplierId) + .and("subOrderId", "=", subOrderId).and("delFlag", "=", false).desc("createdAt")); + return list.isEmpty() ? null : list.get(0); + } + + private BigDecimal nvl(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java new file mode 100644 index 00000000..79017ca9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java @@ -0,0 +1,237 @@ +package com.budwk.app.zhgh.pointsmall.points.service.impl; + +import cn.hutool.core.collection.CollUtil; +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.points.models.PointsMallUserPoints; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPointsLog; +import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsBatchParam; +import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsChangeParam; +import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsLogPageParam; +import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsPageParam; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.entity.Record; +import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class PointsMallUserPointsServiceImpl extends BaseServiceImpl implements PointsMallUserPointsService { + + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + + public PointsMallUserPointsServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination page(PointsMallUserPointsPageParam param) { + Sql sql = Sqls.create(""" + select + p.id, + u.id as userId, + u.loginname as account, + u.username as realName, + u.sex as gender, + u.mobile, + u.member, + u.unitid as unitId, + n.name as unitName, + ifnull(p.totalPoints, 0) as totalPoints, + ifnull(p.usedPoints, 0) as usedPoints, + greatest(ifnull(p.availablePoints, 0) - ifnull(l.lockPoints, 0), 0) as availablePoints, + ifnull(l.lockPoints, 0) as lockPoints + from sys_user u + left join points_mall_user_points p on p.userId = u.id and p.delFlag = false + left join ( + select userId, ifnull(sum(lockAmount), 0) as lockPoints + from points_mall_main_points_lock + where status = 0 and delFlag = false + group by userId + ) l on l.userId = u.id + left join sys_unit n on n.id = u.unitid + $condition + """); + sql.setCondition(userCondition(param).asc("u.loginname")); + return listPageMap(param.getPageNumber(), param.getPageSize(), sql); + } + + @Override + public Pagination logPage(PointsMallUserPointsLogPageParam param) { + Cnd cnd = Cnd.where("l.delFlag", "=", false); + cnd.and("l.userId", "=", param.getUserId()); + if (StrUtil.isNotBlank(param.getChangeType())) { + cnd.and("l.changeType", "=", param.getChangeType()); + } + cnd.desc("l.createdAt"); + Sql sql = Sqls.create(""" + select + l.*, + u.loginname as account, + u.username as realName + from points_mall_user_points_log l + left join sys_user u on u.id = l.userId + $condition + """); + sql.setCondition(cnd); + return listPageMap(param.getPageNumber(), param.getPageSize(), sql); + } + + @Override + public List userIds(PointsMallUserPointsPageParam param) { + Sql sql = Sqls.create("select u.id from sys_user u $condition"); + sql.setCallback(Sqls.callback.strList()); + sql.setCondition(userCondition(param).asc("u.loginname")); + dao().execute(sql); + return sql.getList(String.class); + } + + @Override + public BigDecimal availablePoints(String userId) { + if (StrUtil.isBlank(userId)) { + return BigDecimal.ZERO; + } + PointsMallUserPoints points = fetch(Cnd.where("userId", "=", userId).and("delFlag", "=", false)); + BigDecimal total = points == null || points.getAvailablePoints() == null ? BigDecimal.ZERO : points.getAvailablePoints(); + BigDecimal locked = pointsMallPointsLockService.sumLocked(userId); + BigDecimal available = total.subtract(locked); + return available.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : available; + } + + @Override + public Record userInfo(String userId) { + Record record = new Record(); + record.put("userId", userId); + if (StrUtil.isBlank(userId)) { + return record; + } + Sql sql = Sqls.create("select id, loginname, username, mobile from sys_user where id=@id or loginname=@id or username=@id limit 1"); + sql.params().set("id", userId); + Record user = (Record) dao().execute(sql.setCallback(Sqls.callback.record())).getResult(); + return user == null ? record : user; + } + + @Override + public void grant(PointsMallUserPointsChangeParam param) { + BigDecimal points = checkedPoints(param.getPoints()); + Trans.exec((Atom) () -> change(param.getUserId(), points, "GRANT", param.getRemark())); + } + + @Override + public void deduction(PointsMallUserPointsChangeParam param) { + BigDecimal points = checkedPoints(param.getPoints()).negate(); + Trans.exec((Atom) () -> change(param.getUserId(), points, "DEDUCTION", param.getRemark())); + } + + @Override + public void batchGrant(PointsMallUserPointsBatchParam param) { + List userIds = splitUserIds(param.getUserIds()); + BigDecimal points = checkedPoints(param.getPoints()); + Trans.exec((Atom) () -> userIds.forEach(userId -> change(userId, points, "GRANT", param.getRemark()))); + } + + @Override + public void batchDeduction(PointsMallUserPointsBatchParam param) { + List userIds = splitUserIds(param.getUserIds()); + BigDecimal points = checkedPoints(param.getPoints()).negate(); + Trans.exec((Atom) () -> userIds.forEach(userId -> change(userId, points, "DEDUCTION", param.getRemark()))); + } + + @Override + public void deleteByIds(List ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + dao().update(PointsMallUserPoints.class, Chain.make("delFlag", true), Cnd.where("id", "in", ids)); + } + + private Cnd userCondition(PointsMallUserPointsPageParam param) { + Cnd cnd = Cnd.where("u.delFlag", "=", false).and("u.disabled", "=", false); + if (StrUtil.isNotBlank(param.getAccount())) { + cnd.and("u.loginname", "like", "%" + param.getAccount() + "%"); + } + if (StrUtil.isNotBlank(param.getRealName())) { + cnd.and("u.username", "like", "%" + param.getRealName() + "%"); + } + if (StrUtil.isNotBlank(param.getMobile())) { + cnd.and("u.mobile", "=", param.getMobile()); + } + if (StrUtil.isNotBlank(param.getUnitId())) { + cnd.and("u.unitid", "=", param.getUnitId()); + } + if (param.getMember() != null) { + cnd.and("u.member", "=", param.getMember()); + } + return cnd; + } + + private void change(String userId, BigDecimal amount, String changeType, String remark) { + if (StrUtil.isBlank(userId)) { + throw new IllegalArgumentException("用户ID不能为空"); + } + PointsMallUserPoints points = fetch(Cnd.where("userId", "=", userId).and("delFlag", "=", false)); + if (points == null && amount.signum() < 0) { + throw new IllegalArgumentException("积分记录不存在"); + } + if (points == null) { + points = new PointsMallUserPoints(); + points.setUserId(userId); + points.setTotalPoints(amount); + points.setUsedPoints(BigDecimal.ZERO); + points.setAvailablePoints(amount); + points.setLockPoints(BigDecimal.ZERO); + insert(points); + } else { + BigDecimal total = nvl(points.getTotalPoints()).add(amount); + BigDecimal available = nvl(points.getAvailablePoints()).add(amount); + if (amount.signum() < 0 && available.signum() < 0) { + throw new IllegalArgumentException("积分不足,无法扣减"); + } + Chain chain = Chain.make("totalPoints", total).add("availablePoints", available); + update(chain, Cnd.where("id", "=", points.getId())); + } + + PointsMallUserPointsLog log = new PointsMallUserPointsLog(); + log.setUserId(userId); + log.setChangeAmount(amount.abs()); + log.setChangeType(changeType); + log.setRemark(remark); + dao().insert(log); + } + + private BigDecimal checkedPoints(BigDecimal points) { + if (points == null || points.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("请输入有效的积分数量"); + } + return points; + } + + private BigDecimal nvl(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } + + private List splitUserIds(String userIds) { + List list = Arrays.stream(StrUtil.blankToDefault(userIds, "").split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (list.isEmpty()) { + throw new IllegalArgumentException("请选择用户"); + } + return list; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/controller/PointsMallUserPointsGrantPlanController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/controller/PointsMallUserPointsGrantPlanController.java new file mode 100644 index 00000000..a991179f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/controller/PointsMallUserPointsGrantPlanController.java @@ -0,0 +1,68 @@ +package com.budwk.app.zhgh.pointsmall.pointsgrant.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.pointsgrant.models.PointsMallUserPointsGrantPlan; +import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam; +import com.budwk.app.zhgh.pointsmall.pointsgrant.service.PointsMallUserPointsGrantPlanService; +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/pointsgrant") +public class PointsMallUserPointsGrantPlanController { + + @Inject + private PointsMallUserPointsGrantPlanService grantPlanService; + + @At("") + @Ok("beetl:/platform/zhgh/points-mall/pointsgrant/index.html") + @SaCheckPermission("points.mall.pointsgrant") + public void index() { + } + + @At + @SaCheckPermission("points.mall.pointsgrant") + public Result pageData(PointsMallUserPointsGrantPlanPageParam param) { + return Result.success(grantPlanService.page(param)); + } + + @At + @SLog(tag = "积分发放", msg = "创建发放计划") + @SaCheckPermission("points.mall.pointsgrant") + public Result doAdd(PointsMallUserPointsGrantPlan plan) { + try { + grantPlanService.createPlan(plan); + return Result.success(); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + @At + @SLog(tag = "积分发放", msg = "执行发放计划") + @SaCheckPermission("points.mall.pointsgrant") + public Result startPlan(@Param("id") String id) { + try { + return Result.success(grantPlanService.startPlan(id)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + @At + @SLog(tag = "积分发放", msg = "取消发放计划") + @SaCheckPermission("points.mall.pointsgrant") + public Result cancelPlan(@Param("id") String id) { + try { + return Result.success(grantPlanService.cancelPlan(id)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/models/PointsMallUserPointsGrantPlan.java b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/models/PointsMallUserPointsGrantPlan.java new file mode 100644 index 00000000..28888051 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/models/PointsMallUserPointsGrantPlan.java @@ -0,0 +1,71 @@ +package com.budwk.app.zhgh.pointsmall.pointsgrant.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_user_points_grant_plan") +@Comment("积分商城积分发放计划") +public class PointsMallUserPointsGrantPlan 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 planName; + + @Column + @Comment("计划描述") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String planDescription; + + @Column + @Comment("发放模式:1手动发放,2定时发放") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String grantMode; + + @Column + @Comment("发放时间") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String grantTime; + + @Column + @Comment("Cron表达式") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String cronExp; + + @Column + @Comment("计划状态") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String status; + + @Column + @Comment("发放用户ID列表") + @ColDefine(type = ColType.VARCHAR, customType = "mediumtext") + private String userIdList; + + @Column + @Comment("发放积分") + @Default("0") + @ColDefine(customType = "decimal(18,2)") + private BigDecimal grantPoints; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/param/PointsMallUserPointsGrantPlanPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/param/PointsMallUserPointsGrantPlanPageParam.java new file mode 100644 index 00000000..5449e685 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/param/PointsMallUserPointsGrantPlanPageParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.pointsgrant.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallUserPointsGrantPlanPageParam extends PageForm { + + private String planName; + private String status; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/PointsMallUserPointsGrantPlanService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/PointsMallUserPointsGrantPlanService.java new file mode 100644 index 00000000..010200ab --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/PointsMallUserPointsGrantPlanService.java @@ -0,0 +1,25 @@ +package com.budwk.app.zhgh.pointsmall.pointsgrant.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.pointsmall.pointsgrant.models.PointsMallUserPointsGrantPlan; +import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam; + +public interface PointsMallUserPointsGrantPlanService extends BaseService { + + String STATUS_PENDING = "待执行"; + String STATUS_IN_PROGRESS = "执行中"; + String STATUS_COMPLETED = "已完成"; + String STATUS_CANCELLED = "已取消"; + String STATUS_FAILED = "已失败"; + + Pagination page(PointsMallUserPointsGrantPlanPageParam param); + + void createPlan(PointsMallUserPointsGrantPlan plan); + + boolean startPlan(String id); + + boolean cancelPlan(String id); + + int scheduledGrantPlan(); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/impl/PointsMallUserPointsGrantPlanServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/impl/PointsMallUserPointsGrantPlanServiceImpl.java new file mode 100644 index 00000000..6cf94fce --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/pointsgrant/service/impl/PointsMallUserPointsGrantPlanServiceImpl.java @@ -0,0 +1,128 @@ +package com.budwk.app.zhgh.pointsmall.pointsgrant.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.date.DateUtil; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsBatchParam; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; +import com.budwk.app.zhgh.pointsmall.pointsgrant.models.PointsMallUserPointsGrantPlan; +import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam; +import com.budwk.app.zhgh.pointsmall.pointsgrant.service.PointsMallUserPointsGrantPlanService; +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.math.BigDecimal; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class PointsMallUserPointsGrantPlanServiceImpl extends BaseServiceImpl implements PointsMallUserPointsGrantPlanService { + + @Inject + private PointsMallUserPointsService pointsMallUserPointsService; + + public PointsMallUserPointsGrantPlanServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination page(PointsMallUserPointsGrantPlanPageParam param) { + Cnd cnd = Cnd.where("delFlag", "=", false); + if (StrUtil.isNotBlank(param.getPlanName())) { + cnd.and("planName", "like", "%" + param.getPlanName() + "%"); + } + if (StrUtil.isNotBlank(param.getStatus())) { + cnd.and("status", "=", param.getStatus()); + } + cnd.desc("createdAt"); + return listPage(param.getPageNumber(), param.getPageSize(), PointsMallUserPointsGrantPlan.class, cnd); + } + + @Override + public void createPlan(PointsMallUserPointsGrantPlan plan) { + checkPlan(plan); + plan.setStatus(STATUS_PENDING); + insert(plan); + } + + @Override + public boolean startPlan(String id) { + if (StrUtil.isBlank(id)) { + throw new IllegalArgumentException("计划ID不能为空"); + } + PointsMallUserPointsGrantPlan plan = fetch(id); + if (plan == null || Boolean.TRUE.equals(plan.getDelFlag())) { + throw new IllegalArgumentException("计划不存在"); + } + if (!STATUS_PENDING.equals(plan.getStatus())) { + throw new IllegalArgumentException("计划未处于待执行状态,无法启动"); + } + update(Chain.make("status", STATUS_IN_PROGRESS), Cnd.where("id", "=", id)); + PointsMallUserPointsBatchParam batchParam = new PointsMallUserPointsBatchParam(); + batchParam.setUserIds(plan.getUserIdList()); + batchParam.setPoints(plan.getGrantPoints()); + batchParam.setRemark("积分发放计划:" + plan.getPlanName()); + try { + pointsMallUserPointsService.batchGrant(batchParam); + update(Chain.make("status", STATUS_COMPLETED), Cnd.where("id", "=", id)); + } catch (RuntimeException e) { + update(Chain.make("status", STATUS_FAILED), Cnd.where("id", "=", id)); + throw e; + } + return true; + } + + @Override + public boolean cancelPlan(String id) { + if (StrUtil.isBlank(id)) { + throw new IllegalArgumentException("计划ID不能为空"); + } + PointsMallUserPointsGrantPlan plan = fetch(id); + if (plan == null || Boolean.TRUE.equals(plan.getDelFlag())) { + throw new IllegalArgumentException("计划不存在"); + } + if (!STATUS_PENDING.equals(plan.getStatus())) { + throw new IllegalArgumentException("计划未处于待执行状态,无法取消"); + } + update(Chain.make("status", STATUS_CANCELLED), Cnd.where("id", "=", id)); + return true; + } + + @Override + public int scheduledGrantPlan() { + List plans = query(Cnd.where("delFlag", "=", false) + .and("status", "=", STATUS_PENDING) + .and("grantMode", "=", "2") + .and("grantTime", "<=", DateUtil.now())); + plans.forEach(plan -> startPlan(plan.getId())); + return plans.size(); + } + + private void checkPlan(PointsMallUserPointsGrantPlan plan) { + if (plan == null) { + throw new IllegalArgumentException("计划信息不能为空"); + } + if (StrUtil.isBlank(plan.getPlanName())) { + throw new IllegalArgumentException("计划名称不能为空"); + } + if (StrUtil.isBlank(plan.getGrantMode())) { + throw new IllegalArgumentException("发放模式不能为空"); + } + if (!"1".equals(plan.getGrantMode()) && !"2".equals(plan.getGrantMode())) { + throw new IllegalArgumentException("发放模式不正确"); + } + if ("2".equals(plan.getGrantMode()) && StrUtil.isBlank(plan.getGrantTime())) { + throw new IllegalArgumentException("定时发放时间不能为空"); + } + if (StrUtil.isBlank(plan.getUserIdList())) { + throw new IllegalArgumentException("发放用户不能为空"); + } + BigDecimal grantPoints = plan.getGrantPoints(); + if (grantPoints == null || grantPoints.compareTo(BigDecimal.ZERO) <= 0) { + throw new IllegalArgumentException("发放积分必须大于0"); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java new file mode 100644 index 00000000..6a1bb191 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/controller/PointsMallReconciliationController.java @@ -0,0 +1,67 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.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.reconciliation.param.PointsMallReconciliationOrderPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationRecordPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.service.PointsMallReconciliationService; +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; + +@IocBean +@Ok("json:full") +@At("/platform/zhgh/points-mall/reconciliation") +public class PointsMallReconciliationController { + + @Inject + private PointsMallReconciliationService pointsMallReconciliationService; + + @At("") + @Ok("beetl:/platform/zhgh/points-mall/reconciliation/index.html") + @SaCheckPermission("points.mall.reconciliation") + public void index() { + } + + @At + @SaCheckPermission("points.mall.reconciliation") + public Result pageData(PointsMallReconciliationOrderPageParam param) { + return Result.success(pointsMallReconciliationService.orderPage(param)); + } + + @At + @SaCheckPermission("points.mall.reconciliation") + public Result supplierList() { + return Result.success(pointsMallReconciliationService.listSuppliers()); + } + + @At + @SaCheckPermission("points.mall.reconciliation") + public Result pointsTotal(PointsMallReconciliationParam param) { + try { + return Result.success(pointsMallReconciliationService.pointsTotal(param)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + @At + @SLog(tag = "积分商城对账管理", msg = "执行订单对账") + @SaCheckPermission("points.mall.reconciliation") + public Result execute(PointsMallReconciliationParam param) { + try { + return Result.success(pointsMallReconciliationService.execute(param)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + @At + @SaCheckPermission("points.mall.reconciliation") + public Result recordPage(PointsMallReconciliationRecordPageParam param) { + return Result.success(pointsMallReconciliationService.recordPage(param)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationOrder.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationOrder.java new file mode 100644 index 00000000..dae826dd --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationOrder.java @@ -0,0 +1,59 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.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_reconciliation_order") +@Comment("积分商城供应商对账订单") +public class PointsMallReconciliationOrder 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("订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String orderId; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @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 invoiceNakedPrice; + + @Column + @Comment("商品信息") + @ColDefine(type = ColType.TEXT) + private String productInfo; + + @Column + @Comment("查询范围") + @ColDefine(type = ColType.VARCHAR, width = 200) + private String queryScope; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationRecord.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationRecord.java new file mode 100644 index 00000000..a91dc582 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/models/PointsMallReconciliationRecord.java @@ -0,0 +1,101 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.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_reconciliation_record") +@Comment("积分商城对账记录") +public class PointsMallReconciliationRecord 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("订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String orderId; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("平台退款") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pRefund; + + @Column + @Comment("平台订单总金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pTotalPrice; + + @Column + @Comment("平台现金金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pCashAmount; + + @Column + @Comment("平台消费积分") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pPointsPrice; + + @Column + @Comment("供应商退款") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sRefund; + + @Column + @Comment("供应商订单总金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sTotalPrice; + + @Column + @Comment("供应商现金金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sCashAmount; + + @Column + @Comment("供应商消费积分") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sPointsPrice; + + @Column + @Comment("差异原因") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String diffReason; + + @Column + @Comment("差异类型") + private Integer diffType; + + @Column + @Comment("对账批次号") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String batchNo; + + @Column + @Comment("通知状态") + private Integer noticeStatus; + + @Column + @Comment("推送次数") + private Integer pushTime; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationOrderPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationOrderPageParam.java new file mode 100644 index 00000000..c69dfceb --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationOrderPageParam.java @@ -0,0 +1,19 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallReconciliationOrderPageParam extends PageForm { + + private String supplierId; + private String orderId; + private String userId; + private String startDate; + private String endDate; + private Integer reconciliationType; + private Integer reconciliationStatus; + private Integer invoiceStatus; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java new file mode 100644 index 00000000..04c3164a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.param; + +import lombok.Data; + +@Data +public class PointsMallReconciliationParam { + + private String supplierId; + private Integer reconciliationType; + private String orderId; + private String startDate; + private String endDate; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationRecordPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationRecordPageParam.java new file mode 100644 index 00000000..6659aa0f --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/param/PointsMallReconciliationRecordPageParam.java @@ -0,0 +1,17 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallReconciliationRecordPageParam extends PageForm { + + private String supplierId; + private String orderId; + private String userId; + private Integer diffType; + private String batchNo; + private Integer noticeStatus; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java new file mode 100644 index 00000000..a1817522 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/PointsMallReconciliationService.java @@ -0,0 +1,26 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationOrderPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationRecordPageParam; +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 PointsMallReconciliationService extends BaseService { + + Pagination orderPage(PointsMallReconciliationOrderPageParam param); + + BigDecimal pointsTotal(PointsMallReconciliationParam param); + + String execute(PointsMallReconciliationParam param); + + Pagination recordPage(PointsMallReconciliationRecordPageParam param); + + List listSuppliers(); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java new file mode 100644 index 00000000..8ba61444 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliation/service/impl/PointsMallReconciliationServiceImpl.java @@ -0,0 +1,245 @@ +package com.budwk.app.zhgh.pointsmall.reconciliation.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +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.order.models.PointsMallOrderSub; +import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationOrder; +import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationOrderPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.param.PointsMallReconciliationRecordPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliation.service.PointsMallReconciliationService; +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.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@IocBean(args = {"refer:dao"}) +public class PointsMallReconciliationServiceImpl extends BaseServiceImpl implements PointsMallReconciliationService { + + public PointsMallReconciliationServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination orderPage(PointsMallReconciliationOrderPageParam param) { + Cnd cnd = orderCnd(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 pointsTotal(PointsMallReconciliationParam param) { + checkReconciliationParam(param); + Sql sql = Sqls.create("select ifnull(sum(pointsPrice), 0) as totalPoints from points_mall_order_sub $condition"); + sql.setCondition(reconciliationCnd(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 execute(PointsMallReconciliationParam param) { + checkReconciliationParam(param); + List orders = dao().query(PointsMallOrderSub.class, reconciliationCnd(param, "")); + if (CollUtil.isEmpty(orders)) { + return "未查询到待对账订单"; + } + + String batchNo = DateUtil.format(new Date(), "yyyyMMddHHmmss") + IdUtil.fastSimpleUUID().substring(0, 6); + int success = 0; + int exception = 0; + List records = new ArrayList<>(); + + for (PointsMallOrderSub order : orders) { + PointsMallReconciliationOrder supplierOrder = dao().fetch(PointsMallReconciliationOrder.class, + Cnd.where("orderId", "=", order.getOrderId()) + .and("supplierId", "=", order.getSupplierId()) + .and("delFlag", "=", false)); + PointsMallReconciliationRecord record = buildRecord(order, supplierOrder, batchNo); + if (record.getDiffType() == null || record.getDiffType() == 0) { + success++; + dao().update(PointsMallOrderSub.class, Chain.make("reconciliationStatus", 1), Cnd.where("id", "=", order.getId())); + } else { + exception++; + dao().update(PointsMallOrderSub.class, Chain.make("reconciliationStatus", 2), Cnd.where("id", "=", order.getId())); + } + records.add(record); + } + + dao().insert(records); + return "对账完成,批次号:" + batchNo + ",成功:" + success + ",异常:" + exception; + } + + @Override + public Pagination recordPage(PointsMallReconciliationRecordPageParam param) { + Cnd cnd = Cnd.where("r.delFlag", "=", false); + if (StrUtil.isNotBlank(param.getSupplierId())) { + cnd.and("r.supplierId", "=", param.getSupplierId()); + } + if (StrUtil.isNotBlank(param.getOrderId())) { + cnd.and("r.orderId", "like", "%" + param.getOrderId() + "%"); + } + if (StrUtil.isNotBlank(param.getUserId())) { + cnd.and("r.userId", "like", "%" + param.getUserId() + "%"); + } + if (param.getDiffType() != null) { + cnd.and("r.diffType", "=", param.getDiffType()); + } + if (StrUtil.isNotBlank(param.getBatchNo())) { + cnd.and("r.batchNo", "like", "%" + param.getBatchNo() + "%"); + } + if (param.getNoticeStatus() != null) { + cnd.and("r.noticeStatus", "=", param.getNoticeStatus()); + } + cnd.desc("r.createdAt"); + Sql sql = Sqls.create("select r.*, s.supplierName from points_mall_reconciliation_record r left join points_mall_supplier s on r.supplierId = s.supplierId and s.delFlag = 0 $condition"); + sql.setCondition(cnd); + return listPageMap(param.getPageNumber(), param.getPageSize(), sql); + } + + @Override + public List listSuppliers() { + return dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false).asc("sortCode")); + } + + private Cnd orderCnd(PointsMallReconciliationOrderPageParam param, String alias) { + Cnd cnd = Cnd.where(field(alias, "delFlag"), "=", false); + if (StrUtil.isNotBlank(param.getSupplierId())) { + cnd.and(field(alias, "supplierId"), "=", param.getSupplierId()); + } + if (StrUtil.isNotBlank(param.getOrderId())) { + List 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.getUserId())) { + cnd.and(field(alias, "userId"), "like", "%" + param.getUserId() + "%"); + } + if (param.getReconciliationStatus() != null) { + cnd.and(field(alias, "reconciliationStatus"), "=", param.getReconciliationStatus()); + } + if (param.getInvoiceStatus() != null) { + cnd.and(field(alias, "invoiceStatus"), "=", param.getInvoiceStatus()); + } + addDateRange(cnd, alias, param.getStartDate(), param.getEndDate()); + return cnd; + } + + private Cnd reconciliationCnd(PointsMallReconciliationParam param, String alias) { + Cnd cnd = Cnd.where(field(alias, "delFlag"), "=", false); + cnd.and(field(alias, "supplierId"), "=", param.getSupplierId()); + if (param.getReconciliationType() != null && param.getReconciliationType() == 2) { + cnd.and(field(alias, "reconciliationStatus"), "=", 2); + } else { + cnd.and(field(alias, "reconciliationStatus"), "=", 0); + } + if (StrUtil.isNotBlank(param.getOrderId())) { + List 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() + "%"); + } + } + addDateRange(cnd, alias, param.getStartDate(), param.getEndDate()); + return cnd; + } + + private void addDateRange(Cnd cnd, String alias, String startDate, String endDate) { + if (StrUtil.isNotBlank(startDate)) { + cnd.and(field(alias, "orderCompleteTime"), ">=", DateUtil.beginOfDay(DateUtil.parse(startDate))); + } + if (StrUtil.isNotBlank(endDate)) { + cnd.and(field(alias, "orderCompleteTime"), "<=", DateUtil.endOfDay(DateUtil.parse(endDate))); + } + } + + private String field(String alias, String field) { + return StrUtil.isBlank(alias) ? field : alias + "." + field; + } + + private void checkReconciliationParam(PointsMallReconciliationParam param) { + if (param == null) { + throw new IllegalArgumentException("对账参数不能为空"); + } + if (StrUtil.isBlank(param.getSupplierId())) { + throw new IllegalArgumentException("供应商不能为空"); + } + if (param.getReconciliationType() == null) { + throw new IllegalArgumentException("对账类型不能为空"); + } + if (StrUtil.isBlank(param.getStartDate()) || StrUtil.isBlank(param.getEndDate())) { + throw new IllegalArgumentException("对账日期不能为空"); + } + } + + private PointsMallReconciliationRecord buildRecord(PointsMallOrderSub order, PointsMallReconciliationOrder supplierOrder, String batchNo) { + PointsMallReconciliationRecord record = new PointsMallReconciliationRecord(); + record.setSupplierId(order.getSupplierId()); + record.setOrderId(order.getOrderId()); + record.setUserId(order.getUserId()); + record.setPRefund(nullToZero(order.getRefund())); + record.setPTotalPrice(nullToZero(order.getTotalPrice())); + record.setPCashAmount(nullToZero(order.getWPayOrAPay())); + record.setPPointsPrice(nullToZero(order.getPointsPrice())); + record.setBatchNo(batchNo); + record.setNoticeStatus(0); + record.setPushTime(0); + + if (supplierOrder == null) { + record.setSRefund(BigDecimal.ZERO); + record.setSTotalPrice(record.getPTotalPrice()); + record.setSCashAmount(record.getPCashAmount()); + record.setSPointsPrice(record.getPPointsPrice()); + record.setDiffType(0); + record.setDiffReason("未获取到供应商侧对账订单,按平台订单确认"); + return record; + } + + record.setSRefund(BigDecimal.ZERO); + record.setSTotalPrice(nullToZero(supplierOrder.getTotalPrice())); + record.setSCashAmount(BigDecimal.ZERO); + record.setSPointsPrice(nullToZero(supplierOrder.getTotalPrice())); + + List reasons = new ArrayList<>(); + if (!amountEquals(record.getPTotalPrice(), record.getSTotalPrice())) { + reasons.add("订单总金额不一致"); + } + if (!amountEquals(record.getPPointsPrice(), record.getSPointsPrice())) { + reasons.add("积分金额不一致"); + } + if (!Objects.equals(order.getUserId(), supplierOrder.getUserId())) { + reasons.add("用户ID不一致"); + } + record.setDiffType(reasons.isEmpty() ? 0 : 1); + record.setDiffReason(reasons.isEmpty() ? "对账一致" : String.join(";", reasons)); + return record; + } + + private BigDecimal nullToZero(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } + + private boolean amountEquals(BigDecimal left, BigDecimal right) { + return nullToZero(left).compareTo(nullToZero(right)) == 0; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/controller/PointsMallReconciliationRecordController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/controller/PointsMallReconciliationRecordController.java new file mode 100644 index 00000000..c5ee3fc2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/controller/PointsMallReconciliationRecordController.java @@ -0,0 +1,37 @@ +package com.budwk.app.zhgh.pointsmall.reconciliationrecord.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.result.Result; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.param.PointsMallReconciliationRecordPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.service.PointsMallReconciliationRecordService; +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; + +@IocBean +@Ok("json:full") +@At("/platform/zhgh/points-mall/reconciliationRecord") +public class PointsMallReconciliationRecordController { + + @Inject + private PointsMallReconciliationRecordService pointsMallReconciliationRecordService; + + @At("") + @Ok("beetl:/platform/zhgh/points-mall/reconciliationRecord/index.html") + @SaCheckPermission("points.mall.reconciliation.record") + public void index() { + } + + @At + @SaCheckPermission("points.mall.reconciliation.record") + public Result pageData(PointsMallReconciliationRecordPageParam param) { + return Result.success(pointsMallReconciliationRecordService.page(param)); + } + + @At + @SaCheckPermission("points.mall.reconciliation.record") + public Result supplierList() { + return Result.success(pointsMallReconciliationRecordService.listSuppliers()); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/models/PointsMallReconciliationRecord.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/models/PointsMallReconciliationRecord.java new file mode 100644 index 00000000..b6a5bf5d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/models/PointsMallReconciliationRecord.java @@ -0,0 +1,101 @@ +package com.budwk.app.zhgh.pointsmall.reconciliationrecord.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_reconciliation_record") +@Comment("积分商城对账记录") +public class PointsMallReconciliationRecord 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("订单ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String orderId; + + @Column + @Comment("用户ID") + @ColDefine(type = ColType.VARCHAR, width = 64) + private String userId; + + @Column + @Comment("平台退款") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pRefund; + + @Column + @Comment("平台订单总金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pTotalPrice; + + @Column + @Comment("平台现金金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pCashAmount; + + @Column + @Comment("平台消费积分") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal pPointsPrice; + + @Column + @Comment("供应商退款") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sRefund; + + @Column + @Comment("供应商订单总金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sTotalPrice; + + @Column + @Comment("供应商现金金额") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sCashAmount; + + @Column + @Comment("供应商消费积分") + @ColDefine(type = ColType.FLOAT, width = 18, precision = 2) + private BigDecimal sPointsPrice; + + @Column + @Comment("差异原因") + @ColDefine(type = ColType.VARCHAR, width = 1000) + private String diffReason; + + @Column + @Comment("差异类型") + private Integer diffType; + + @Column + @Comment("对账批次号") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String batchNo; + + @Column + @Comment("通知状态") + private Integer noticeStatus; + + @Column + @Comment("推送次数") + private Integer pushTime; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/param/PointsMallReconciliationRecordPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/param/PointsMallReconciliationRecordPageParam.java new file mode 100644 index 00000000..3560e973 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/param/PointsMallReconciliationRecordPageParam.java @@ -0,0 +1,17 @@ +package com.budwk.app.zhgh.pointsmall.reconciliationrecord.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallReconciliationRecordPageParam extends PageForm { + + private String supplierId; + private String orderId; + private String userId; + private Integer diffType; + private String batchNo; + private Integer noticeStatus; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/PointsMallReconciliationRecordService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/PointsMallReconciliationRecordService.java new file mode 100644 index 00000000..41d80b63 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/PointsMallReconciliationRecordService.java @@ -0,0 +1,17 @@ +package com.budwk.app.zhgh.pointsmall.reconciliationrecord.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.models.PointsMallReconciliationRecord; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.param.PointsMallReconciliationRecordPageParam; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import org.nutz.dao.entity.Record; + +import java.util.List; + +public interface PointsMallReconciliationRecordService extends BaseService { + + Pagination page(PointsMallReconciliationRecordPageParam param); + + List listSuppliers(); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java new file mode 100644 index 00000000..405b3a43 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/reconciliationrecord/service/impl/PointsMallReconciliationRecordServiceImpl.java @@ -0,0 +1,57 @@ +package com.budwk.app.zhgh.pointsmall.reconciliationrecord.service.impl; + +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.reconciliationrecord.models.PointsMallReconciliationRecord; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.param.PointsMallReconciliationRecordPageParam; +import com.budwk.app.zhgh.pointsmall.reconciliationrecord.service.PointsMallReconciliationRecordService; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +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.util.List; + +@IocBean(args = {"refer:dao"}) +public class PointsMallReconciliationRecordServiceImpl extends BaseServiceImpl implements PointsMallReconciliationRecordService { + + public PointsMallReconciliationRecordServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination page(PointsMallReconciliationRecordPageParam param) { + Cnd cnd = Cnd.where("r.delFlag", "=", false); + if (StrUtil.isNotBlank(param.getSupplierId())) { + cnd.and("r.supplierId", "=", param.getSupplierId()); + } + if (StrUtil.isNotBlank(param.getOrderId())) { + cnd.and("r.orderId", "like", "%" + param.getOrderId() + "%"); + } + if (StrUtil.isNotBlank(param.getUserId())) { + cnd.and("r.userId", "like", "%" + param.getUserId() + "%"); + } + if (param.getDiffType() != null) { + cnd.and("r.diffType", "=", param.getDiffType()); + } + if (StrUtil.isNotBlank(param.getBatchNo())) { + cnd.and("r.batchNo", "like", "%" + param.getBatchNo() + "%"); + } + if (param.getNoticeStatus() != null) { + cnd.and("r.noticeStatus", "=", param.getNoticeStatus()); + } + cnd.desc("r.createdAt"); + Sql sql = Sqls.create("select r.*, s.supplierName from points_mall_reconciliation_record r left join points_mall_supplier s on r.supplierId = s.supplierId and s.delFlag = 0 $condition"); + sql.setCondition(cnd); + return listPageMap(param.getPageNumber(), param.getPageSize(), sql); + } + + @Override + public List listSuppliers() { + return dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false).asc("sortCode")); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/controller/PointsMallSupplierController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/controller/PointsMallSupplierController.java new file mode 100644 index 00000000..df3a3d7c --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/controller/PointsMallSupplierController.java @@ -0,0 +1,104 @@ +package com.budwk.app.zhgh.pointsmall.supplier.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.supplier.models.PointsMallSupplier; +import com.budwk.app.zhgh.pointsmall.supplier.param.PointsMallSupplierPageParam; +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; + +import java.util.List; + +@IocBean +@Ok("json:full") +@At("/platform/zhgh/points-mall/supplier") +public class PointsMallSupplierController { + + @Inject + private PointsMallSupplierService pointsMallSupplierService; + + @At("") + @Ok("beetl:/platform/zhgh/points-mall/supplier/index.html") + @SaCheckPermission("points.mall.supplier") + public void index() { + } + + @At + @SaCheckPermission("points.mall.supplier") + public Result pageData(PointsMallSupplierPageParam param) { + return Result.success(pointsMallSupplierService.page(param)); + } + + @At + @SLog(tag = "积分商城供应商管理", msg = "新增供应商") + @SaCheckPermission("points.mall.supplier") + public Result doAdd(PointsMallSupplier supplier) { + Result check = checkSupplier(supplier, false); + if (!check.isSuccess()) { + return check; + } + if (pointsMallSupplierService.existsSupplierId(supplier.getSupplierId(), null)) { + return Result.error("供应商编码已存在"); + } + pointsMallSupplierService.insert(supplier); + return Result.success(); + } + + @At + @SLog(tag = "积分商城供应商管理", msg = "编辑供应商") + @SaCheckPermission("points.mall.supplier") + public Result doEdit(PointsMallSupplier supplier) { + if (StrUtil.isBlank(supplier.getId())) { + return Result.error("ID不能为空"); + } + Result check = checkSupplier(supplier, true); + if (!check.isSuccess()) { + return check; + } + if (pointsMallSupplierService.existsSupplierId(supplier.getSupplierId(), supplier.getId())) { + return Result.error("供应商编码已存在"); + } + pointsMallSupplierService.updateIgnoreNull(supplier); + return Result.success(); + } + + @At + @SLog(tag = "积分商城供应商管理", msg = "删除供应商") + @SaCheckPermission("points.mall.supplier") + public Result doDelete(@Param("ids") String ids) { + List idList = StrUtil.split(ids, ','); + if (idList.isEmpty()) { + return Result.error("请选择需要删除的数据"); + } + pointsMallSupplierService.deleteByIds(idList); + return Result.success(); + } + + private Result checkSupplier(PointsMallSupplier supplier, boolean update) { + if (supplier == null) { + return Result.error("供应商信息不能为空"); + } + if (update && StrUtil.isBlank(supplier.getId())) { + return Result.error("ID不能为空"); + } + if (StrUtil.isBlank(supplier.getSupplierId())) { + return Result.error("供应商编码不能为空"); + } + if (StrUtil.isBlank(supplier.getSupplierName())) { + return Result.error("供应商名称不能为空"); + } + if (supplier.getMallType() == null) { + return Result.error("商城类型不能为空"); + } + if (supplier.getSortCode() == null) { + supplier.setSortCode(99); + } + return Result.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/models/PointsMallSupplier.java b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/models/PointsMallSupplier.java new file mode 100644 index 00000000..df0aee50 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/models/PointsMallSupplier.java @@ -0,0 +1,98 @@ +package com.budwk.app.zhgh.pointsmall.supplier.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; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/param/PointsMallSupplierPageParam.java b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/param/PointsMallSupplierPageParam.java new file mode 100644 index 00000000..d70bde29 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/param/PointsMallSupplierPageParam.java @@ -0,0 +1,13 @@ +package com.budwk.app.zhgh.pointsmall.supplier.param; + +import com.budwk.app.base.param.PageForm; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PointsMallSupplierPageParam extends PageForm { + + private String supplierName; + private Integer mallType; +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/PointsMallSupplierService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/PointsMallSupplierService.java new file mode 100644 index 00000000..26c8ed8d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/PointsMallSupplierService.java @@ -0,0 +1,19 @@ +package com.budwk.app.zhgh.pointsmall.supplier.service; + +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.service.BaseService; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import com.budwk.app.zhgh.pointsmall.supplier.param.PointsMallSupplierPageParam; + +import java.util.List; + +public interface PointsMallSupplierService extends BaseService { + + Pagination page(PointsMallSupplierPageParam param); + + boolean existsSupplierId(String supplierId, String excludeId); + + List listByMallType(Integer mallType); + + void deleteByIds(List ids); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/impl/PointsMallSupplierServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/impl/PointsMallSupplierServiceImpl.java new file mode 100644 index 00000000..01371ac7 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/supplier/service/impl/PointsMallSupplierServiceImpl.java @@ -0,0 +1,65 @@ +package com.budwk.app.zhgh.pointsmall.supplier.service.impl; + +import cn.hutool.core.collection.CollUtil; +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.supplier.models.PointsMallSupplier; +import com.budwk.app.zhgh.pointsmall.supplier.param.PointsMallSupplierPageParam; +import com.budwk.app.zhgh.pointsmall.supplier.service.PointsMallSupplierService; +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; +import java.util.List; + +@IocBean(args = {"refer:dao"}) +public class PointsMallSupplierServiceImpl extends BaseServiceImpl implements PointsMallSupplierService { + + public PointsMallSupplierServiceImpl(Dao dao) { + super(dao); + } + + @Override + public Pagination page(PointsMallSupplierPageParam param) { + Cnd cnd = Cnd.where("delFlag", "=", false); + if (StrUtil.isNotBlank(param.getSupplierName())) { + cnd.and("supplierName", "like", "%" + param.getSupplierName() + "%"); + } + if (param.getMallType() != null) { + cnd.and("mallType", "=", param.getMallType()); + } + cnd.asc("sortCode"); + return listPage(param.getPageNumber(), param.getPageSize(), PointsMallSupplier.class, cnd); + } + + @Override + public boolean existsSupplierId(String supplierId, String excludeId) { + Cnd cnd = Cnd.where("supplierId", "=", supplierId).and("delFlag", "=", false); + if (StrUtil.isNotBlank(excludeId)) { + cnd.and("id", "!=", excludeId); + } + return dao().count(PointsMallSupplier.class, cnd) > 0; + } + + @Override + public List listByMallType(Integer mallType) { + Cnd cnd = Cnd.where("delFlag", "=", false); + cnd.and(Cnd.exps("cooperationExpTime", "is", null).or("cooperationExpTime", ">=", new Date())); + if (mallType != null) { + cnd.and("mallType", "=", mallType); + } + cnd.asc("sortCode"); + return dao().query(PointsMallSupplier.class, cnd); + } + + @Override + public void deleteByIds(List ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + dao().update(PointsMallSupplier.class, Chain.make("delFlag", true), Cnd.where("id", "in", ids)); + } +} diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml index 52398357..e7dc563c 100644 --- a/src/main/resources/application-dev.yaml +++ b/src/main/resources/application-dev.yaml @@ -8,6 +8,14 @@ server: port: 8080 host: 0.0.0.0 +spdb: + bab7: + url: http://localhost:8888/supplierOpenApi/pointOrder/queryOrderInfo + +sm4cbc: + keyHex: 00112233445566778899aabbccddeeff + ivHex: 0102030405060708090a0b0c0d0e0f10 + jetty: contextPath: / threadpool: @@ -21,7 +29,7 @@ jetty: #结合ftp使用,或用nginx代理ftp路径 #staticPath: /Users/wizzer/temp/files #开发模式静态资源 - staticPathLocal: E:/projects/zhgh_cug_v4/src/main/resources/static + staticPathLocal: E:/DevOpsCode/zhgh_cug_v4/src/main/resources/static security: tokenName: saToken @@ -71,9 +79,9 @@ redis: #nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378 jdbc: - url: jdbc:mysql://192.168.21.215:3306/zhgh_cug_v4?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://127.0.0.1:3306/zhgh_cug_v4?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root - password: root + password: Root@123456 validationQuery: select 1 maxActive: 1000 testWhileIdle: true @@ -87,7 +95,7 @@ xsssql: beetl: RESOURCE: #本地路径,开发时设置本地路径,便于调试 - rootLocal: E:/projects/zhgh_cug_v4/src/main/resources/views/ + rootLocal: E:/DevOpsCode/zhgh_cug_v4/src/main/resources/views/ root: views/ DELIMITER_STATEMENT_START: "" @@ -160,9 +168,9 @@ quartz: class: com.budwk.app.task.conn.NutConnectionProvider minio: - accessKey: minioadmin - secretKey: minioadmin - endPoint: http://192.168.21.214:9000 + accessKey: o1MwuzixNqAXh5zqEfMH + secretKey: RVnZum91JmGO79qtzWy2gKMBsF6OxAaibGrr7ets + endPoint: http://localhost:9000 bucket: njupt data-center: diff --git a/src/main/resources/db/points-mall.sql b/src/main/resources/db/points-mall.sql new file mode 100644 index 00000000..d1982fff --- /dev/null +++ b/src/main/resources/db/points-mall.sql @@ -0,0 +1,512 @@ +-- Points mall tables for zhgh_cug_v4. +-- MySQL 8.x / InnoDB / utf8mb4. +-- Naming follows the current zhgh_cug_v4 style: varchar(32) id, camelCase columns, +-- createdBy/createdAt/updatedBy/updatedAt/delFlag audit fields. + +CREATE TABLE IF NOT EXISTS `points_mall_supplier` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `supplierName` varchar(100) NOT NULL COMMENT '供应商名称', + `supplierUrl` varchar(1000) DEFAULT NULL COMMENT '供应商首页跳转地址', + `supplierOrderUrl` varchar(1000) DEFAULT NULL COMMENT '供应商订单详情地址', + `supplierReturnOrderUrl` varchar(1000) DEFAULT NULL COMMENT '供应商售后订单地址', + `supplierApiUrl` varchar(1000) DEFAULT NULL COMMENT '供应商接口地址', + `supplierPic` varchar(1000) DEFAULT NULL COMMENT '供应商图片访问地址', + `supplierPicPath` varchar(1000) DEFAULT NULL COMMENT '供应商图片文件路径', + `contactInfo` varchar(500) DEFAULT NULL COMMENT '联系人信息', + `sroNo` varchar(100) DEFAULT NULL COMMENT '服务商编号', + `cooperationExpTime` datetime DEFAULT NULL COMMENT '合作到期时间', + `clientId` varchar(128) DEFAULT NULL COMMENT '客户端ID', + `clientSecret` varchar(256) DEFAULT NULL COMMENT '客户端密钥', + `sortCode` int DEFAULT 0 COMMENT '排序号', + `mallType` int DEFAULT 1 COMMENT '商城类型:1积分商城,2电影商城', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_supplier_supplier` (`supplierId`), + KEY `idx_points_mall_supplier_type_sort` (`mallType`, `sortCode`), + KEY `idx_points_mall_supplier_exp_time` (`cooperationExpTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城供应商'; + +CREATE TABLE IF NOT EXISTS `points_mall_supplier_auth_client` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `clientId` varchar(128) NOT NULL COMMENT '客户端ID', + `clientSecret` varchar(256) DEFAULT NULL COMMENT '客户端密钥', + `code` varchar(128) DEFAULT NULL COMMENT '授权码', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_supplier_auth_client` (`supplierId`, `clientId`), + KEY `idx_points_mall_supplier_auth_code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城供应商授权客户端'; + +CREATE TABLE IF NOT EXISTS `points_mall_supplier_token_store` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `clientId` varchar(128) NOT NULL COMMENT '客户端ID', + `accessToken` varchar(1000) DEFAULT NULL COMMENT '访问令牌', + `refreshToken` varchar(1000) DEFAULT NULL COMMENT '刷新令牌', + `accessExpireTime` datetime DEFAULT NULL COMMENT '访问令牌过期时间', + `refreshExpireTime` datetime DEFAULT NULL COMMENT '刷新令牌过期时间', + `isValid` tinyint(1) DEFAULT 1 COMMENT '是否有效', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_supplier_token_client` (`supplierId`, `clientId`, `isValid`), + KEY `idx_points_mall_supplier_token_expire` (`accessExpireTime`, `refreshExpireTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城供应商Token'; + +CREATE TABLE IF NOT EXISTS `points_mall_user_points` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `userId` varchar(64) NOT NULL COMMENT '用户ID', + `totalPoints` decimal(18,2) DEFAULT 0.00 COMMENT '总积分', + `usedPoints` decimal(18,2) DEFAULT 0.00 COMMENT '已用积分', + `availablePoints` decimal(18,2) DEFAULT 0.00 COMMENT '可用积分', + `lockPoints` decimal(18,2) DEFAULT 0.00 COMMENT '锁定积分', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_user_points_user` (`userId`), + KEY `idx_points_mall_user_points_user` (`userId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城用户积分'; + +CREATE TABLE IF NOT EXISTS `points_mall_user_points_log` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `userId` varchar(64) NOT NULL COMMENT '用户ID', + `changeAmount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动积分', + `changeType` varchar(50) DEFAULT NULL COMMENT '变动类型', + `subOrderId` varchar(64) DEFAULT NULL COMMENT '子订单ID', + `templateId` varchar(32) DEFAULT NULL COMMENT '消息模板ID', + `remark` varchar(1000) DEFAULT NULL COMMENT '备注', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_points_log_user` (`userId`), + KEY `idx_points_mall_points_log_order` (`subOrderId`), + KEY `idx_points_mall_points_log_template` (`templateId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城用户积分流水'; + +CREATE TABLE IF NOT EXISTS `points_mall_main_points_lock` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `userId` varchar(64) NOT NULL COMMENT '用户ID', + `orderId` varchar(64) NOT NULL COMMENT '主订单ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `lockAmount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '锁定积分', + `status` int DEFAULT 0 COMMENT '状态:0锁定中,1已释放,2已扣减', + `lockTime` datetime DEFAULT NULL COMMENT '锁定时间', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_main_lock_order` (`orderId`, `supplierId`, `userId`), + KEY `idx_points_mall_main_lock_user_status` (`userId`, `status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城主订单积分锁定'; + +CREATE TABLE IF NOT EXISTS `points_mall_sub_points_lock` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `userId` varchar(64) NOT NULL COMMENT '用户ID', + `mainOrderId` varchar(64) NOT NULL COMMENT '主订单ID', + `subOrderId` varchar(64) NOT NULL COMMENT '子订单ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `changePoints` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '变动积分', + `orderSubStatus` int DEFAULT NULL COMMENT '子订单状态', + `lockTime` datetime DEFAULT NULL COMMENT '锁定时间', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_sub_lock_order` (`subOrderId`, `supplierId`, `userId`), + KEY `idx_points_mall_sub_lock_main` (`mainOrderId`, `supplierId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城子订单积分锁定'; + +CREATE TABLE IF NOT EXISTS `points_mall_user_points_grant_plan` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `planName` varchar(100) NOT NULL COMMENT '计划名称', + `planDescription` varchar(1000) DEFAULT NULL COMMENT '计划描述', + `grantMode` varchar(20) DEFAULT NULL COMMENT '发放模式:1手动,2定时', + `grantTime` varchar(50) DEFAULT NULL COMMENT '发放时间', + `cronExp` varchar(100) DEFAULT NULL COMMENT 'Cron表达式', + `status` varchar(20) DEFAULT NULL COMMENT '计划状态', + `userIdList` mediumtext COMMENT '发放用户ID列表', + `grantPoints` decimal(18,2) DEFAULT 0.00 COMMENT '发放积分', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_grant_plan_status` (`status`), + KEY `idx_points_mall_grant_plan_mode` (`grantMode`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城积分发放计划'; + +CREATE TABLE IF NOT EXISTS `points_mall_import_template_record` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `templateName` varchar(200) DEFAULT NULL COMMENT '模板名称', + `fileName` varchar(200) DEFAULT NULL COMMENT '文件名称', + `fileUrl` varchar(1000) DEFAULT NULL COMMENT '文件地址', + `totalCount` int DEFAULT 0 COMMENT '导入总数', + `successCount` int DEFAULT 0 COMMENT '成功数量', + `failCount` int DEFAULT 0 COMMENT '失败数量', + `status` int DEFAULT 0 COMMENT '状态:0处理中,1成功,2失败,3部分成功', + `failReason` mediumtext COMMENT '失败原因', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_import_template_status` (`status`), + KEY `idx_points_mall_import_template_created` (`createdAt`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城积分导入记录'; + +CREATE TABLE IF NOT EXISTS `points_mall_order_main` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `orderId` varchar(64) NOT NULL COMMENT '主订单ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `orderNumberId` varchar(64) DEFAULT NULL COMMENT '订单编号', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `aplName` varchar(100) DEFAULT NULL COMMENT '应用名称', + `orderCreateTime` datetime DEFAULT NULL COMMENT '订单创建时间', + `orderUpdateTime` datetime DEFAULT NULL COMMENT '订单更新时间', + `deliveryTime` datetime DEFAULT NULL COMMENT '发货时间', + `orderFinishTime` datetime DEFAULT NULL COMMENT '确认收货时间', + `orderCompleteTime` datetime DEFAULT NULL COMMENT '完成时间', + `deliveryStatus` varchar(20) DEFAULT NULL COMMENT '发货状态', + `freightCost` decimal(18,2) DEFAULT 0.00 COMMENT '运费积分', + `refund` decimal(18,2) DEFAULT 0.00 COMMENT '退款积分', + `logisticsOrderId` varchar(100) DEFAULT NULL COMMENT '物流单号', + `crrgBsnNm` varchar(100) DEFAULT NULL COMMENT '物流承运商', + `orderState` int DEFAULT NULL COMMENT '订单状态', + `totalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '订单总金额', + `wPayOrAPay` decimal(18,2) DEFAULT 0.00 COMMENT '现金金额', + `pointsPrice` decimal(18,2) DEFAULT 0.00 COMMENT '消费积分', + `invoiceCode` varchar(100) DEFAULT NULL COMMENT '发票代码', + `extJson` mediumtext COMMENT '商品扩展信息JSON', + `cashPaid` int DEFAULT NULL COMMENT '现金支付状态', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_order_main_order_supplier` (`orderId`, `supplierId`), + KEY `idx_points_mall_order_main_user` (`userId`), + KEY `idx_points_mall_order_main_state` (`orderState`), + KEY `idx_points_mall_order_main_created` (`orderCreateTime`), + KEY `idx_points_mall_order_main_logistics` (`logisticsOrderId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城主订单'; + +CREATE TABLE IF NOT EXISTS `points_mall_order_sub` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `orderId` varchar(64) NOT NULL COMMENT '子订单ID', + `orderNumberId` varchar(64) DEFAULT NULL COMMENT '订单编号', + `mainOrderId` varchar(64) DEFAULT NULL COMMENT '主订单ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `aplName` varchar(100) DEFAULT NULL COMMENT '应用名称', + `orderCreateTime` datetime DEFAULT NULL COMMENT '订单创建时间', + `orderUpdateTime` datetime DEFAULT NULL COMMENT '订单更新时间', + `deliveryTime` datetime DEFAULT NULL COMMENT '发货时间', + `orderFinishTime` datetime DEFAULT NULL COMMENT '确认收货时间', + `orderCompleteTime` datetime DEFAULT NULL COMMENT '完成时间', + `deliveryStatus` int DEFAULT NULL COMMENT '发货状态', + `freightCost` decimal(18,2) DEFAULT 0.00 COMMENT '运费积分', + `wPayOrAPayFreightCost` decimal(18,2) DEFAULT 0.00 COMMENT '运费现金', + `refund` decimal(18,2) DEFAULT 0.00 COMMENT '退款积分', + `wPayOrAPayRefund` decimal(18,2) DEFAULT 0.00 COMMENT '退款现金', + `logisticsOrderId` varchar(100) DEFAULT NULL COMMENT '物流单号', + `crrgBsnNm` varchar(100) DEFAULT NULL COMMENT '物流承运商', + `orderState` int DEFAULT NULL COMMENT '订单状态', + `totalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '订单总金额', + `wPayOrAPay` decimal(18,2) DEFAULT 0.00 COMMENT '现金金额', + `pointsPrice` decimal(18,2) DEFAULT 0.00 COMMENT '消费积分', + `invoiceCode` varchar(100) DEFAULT NULL COMMENT '发票代码', + `extJson` mediumtext COMMENT '扩展信息JSON', + `reconciliationStatus` int DEFAULT 0 COMMENT '对账状态', + `invoiceStatus` int DEFAULT 0 COMMENT '开票状态', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_order_sub_order_supplier` (`orderId`, `supplierId`), + KEY `idx_points_mall_order_sub_main` (`mainOrderId`, `supplierId`), + KEY `idx_points_mall_order_sub_user_supplier` (`orderId`, `userId`, `supplierId`), + KEY `idx_points_mall_order_sub_state` (`orderState`), + KEY `idx_points_mall_order_sub_invoice` (`invoiceStatus`), + KEY `idx_points_mall_order_sub_reconciliation` (`reconciliationStatus`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城子订单'; + +CREATE TABLE IF NOT EXISTS `points_mall_order_sub_product` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `orderSubId` varchar(64) NOT NULL COMMENT '子订单ID', + `sku` varchar(100) DEFAULT NULL COMMENT '商品SKU', + `number` int DEFAULT 0 COMMENT '购买数量', + `price` decimal(18,2) DEFAULT 0.00 COMMENT '商品单价', + `name` varchar(300) DEFAULT NULL COMMENT '商品名称', + `taxRate` decimal(10,4) DEFAULT NULL COMMENT '税率', + `picInfo` varchar(1000) DEFAULT NULL COMMENT '商品图片信息', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_order_sub_product_order` (`orderSubId`), + KEY `idx_points_mall_order_sub_product_sku` (`sku`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城子订单商品'; + +CREATE TABLE IF NOT EXISTS `points_mall_order_exchange` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `orderMainId` varchar(64) DEFAULT NULL COMMENT '关联主订单ID', + `orderId` varchar(64) NOT NULL COMMENT '售后订单ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `aplName` varchar(100) DEFAULT NULL COMMENT '应用名称', + `orderCreateTime` datetime DEFAULT NULL COMMENT '订单创建时间', + `orderUpdateTime` datetime DEFAULT NULL COMMENT '订单更新时间', + `orderCompleteTime` datetime DEFAULT NULL COMMENT '完成时间', + `deliveryStatus` int DEFAULT NULL COMMENT '发货状态', + `freightCost` decimal(18,2) DEFAULT 0.00 COMMENT '运费', + `refund` decimal(18,2) DEFAULT 0.00 COMMENT '退款', + `logisticsOrderId` varchar(100) DEFAULT NULL COMMENT '物流单号', + `crrgBsnNm` varchar(100) DEFAULT NULL COMMENT '物流承运商', + `orderState` int DEFAULT NULL COMMENT '订单状态', + `totalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '订单总金额', + `wPayOrAPay` decimal(18,2) DEFAULT 0.00 COMMENT '现金金额', + `pointsPrice` decimal(18,2) DEFAULT 0.00 COMMENT '消费积分', + `invoiceCode` varchar(100) DEFAULT NULL COMMENT '发票代码', + `extJson` mediumtext COMMENT '商品扩展信息JSON', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_order_exchange_order_supplier` (`orderId`, `supplierId`), + KEY `idx_points_mall_order_exchange_main` (`orderMainId`, `supplierId`), + KEY `idx_points_mall_order_exchange_user` (`userId`), + KEY `idx_points_mall_order_exchange_state` (`orderState`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城退换货订单'; + +CREATE TABLE IF NOT EXISTS `points_mall_order_event_log` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) DEFAULT NULL COMMENT '供应商编码', + `orderType` int DEFAULT NULL COMMENT '订单类型:1主单,2子单,3售后单', + `orderId` varchar(64) DEFAULT NULL COMMENT '订单ID', + `mainOrderId` varchar(64) DEFAULT NULL COMMENT '主订单ID', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `eventTime` datetime DEFAULT NULL COMMENT '事件时间', + `message` mediumtext COMMENT '事件消息', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_order_event_order` (`orderId`, `supplierId`), + KEY `idx_points_mall_order_event_main` (`mainOrderId`), + KEY `idx_points_mall_order_event_time` (`eventTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城订单事件日志'; + +CREATE TABLE IF NOT EXISTS `points_mall_invoice_main` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `settlementId` varchar(100) DEFAULT NULL COMMENT '结算单号', + `bSuccess` int DEFAULT NULL COMMENT '开票结果:0成功,1失败', + `sucOrderIds` mediumtext COMMENT '可开票订单列表', + `failOrderIds` mediumtext COMMENT '无法开票订单列表', + `failMsg` varchar(1000) DEFAULT NULL COMMENT '开票失败原因', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_invoice_main_settlement` (`settlementId`), + KEY `idx_points_mall_invoice_main_success` (`bSuccess`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城发票主表'; + +CREATE TABLE IF NOT EXISTS `points_mall_invoice_info` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `mainId` varchar(32) DEFAULT NULL COMMENT '发票主表ID', + `invoiceId` varchar(100) DEFAULT NULL COMMENT '发票号码', + `invoiceCode` varchar(100) DEFAULT NULL COMMENT '发票代码', + `invoiceDate` date DEFAULT NULL COMMENT '发票日期', + `invoiceNakeAmount` decimal(18,2) DEFAULT NULL COMMENT '发票裸价', + `invoiceTaxRate` decimal(10,4) DEFAULT NULL COMMENT '发票税率', + `invoiceTaxAmount` decimal(18,2) DEFAULT NULL COMMENT '发票税额', + `invoiceAmount` decimal(18,2) DEFAULT NULL COMMENT '价税合计', + `invoiceType` int DEFAULT NULL COMMENT '发票类型', + `url` varchar(1000) DEFAULT NULL COMMENT '电子发票地址', + `imageEncode` longtext COMMENT '发票Base64', + `fileType` varchar(20) DEFAULT NULL COMMENT '文件类型', + `checkCode` varchar(100) DEFAULT NULL COMMENT '校验码', + `invoiceAddress` varchar(500) DEFAULT NULL COMMENT '发票地址', + `invoiceBank` varchar(200) DEFAULT NULL COMMENT '开户银行', + `invoiceAccount` varchar(100) DEFAULT NULL COMMENT '开户账号', + `invoiceContact` varchar(100) DEFAULT NULL COMMENT '联系电话', + `invoiceContent` varchar(500) DEFAULT NULL COMMENT '开票内容', + `taxIdNumber` varchar(100) DEFAULT NULL COMMENT '纳税人识别号', + `remark` varchar(1000) DEFAULT NULL COMMENT '备注', + `biller` varchar(100) DEFAULT NULL COMMENT '开票人', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_invoice_info_main` (`mainId`), + KEY `idx_points_mall_invoice_info_invoice` (`invoiceId`, `invoiceCode`), + KEY `idx_points_mall_invoice_info_date` (`invoiceDate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城发票信息'; + +CREATE TABLE IF NOT EXISTS `points_mall_reconciliation_order` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `orderId` varchar(64) NOT NULL COMMENT '订单ID', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `totalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '总价含税', + `invoiceNakedPrice` decimal(18,2) DEFAULT 0.00 COMMENT '总价不含税', + `productInfo` mediumtext COMMENT '商品信息', + `queryScope` varchar(200) DEFAULT NULL COMMENT '查询范围', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_reconciliation_order` (`orderId`, `supplierId`), + KEY `idx_points_mall_reconciliation_order_user` (`userId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城供应商对账订单'; + +CREATE TABLE IF NOT EXISTS `points_mall_reconciliation_record` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `supplierId` varchar(64) NOT NULL COMMENT '供应商编码', + `orderId` varchar(64) NOT NULL COMMENT '订单ID', + `userId` varchar(64) DEFAULT NULL COMMENT '用户ID', + `pRefund` decimal(18,2) DEFAULT 0.00 COMMENT '平台退款', + `pTotalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '平台订单总金额', + `pCashAmount` decimal(18,2) DEFAULT 0.00 COMMENT '平台现金金额', + `pPointsPrice` decimal(18,2) DEFAULT 0.00 COMMENT '平台消费积分', + `sRefund` decimal(18,2) DEFAULT 0.00 COMMENT '供应商退款', + `sTotalPrice` decimal(18,2) DEFAULT 0.00 COMMENT '供应商订单总金额', + `sCashAmount` decimal(18,2) DEFAULT 0.00 COMMENT '供应商现金金额', + `sPointsPrice` decimal(18,2) DEFAULT 0.00 COMMENT '供应商消费积分', + `diffReason` varchar(1000) DEFAULT NULL COMMENT '差异原因', + `diffType` int DEFAULT NULL COMMENT '差异类型', + `batchNo` varchar(100) DEFAULT NULL COMMENT '对账批次号', + `noticeStatus` int DEFAULT 0 COMMENT '通知状态:0未推送,1成功,2失败', + `pushTime` int DEFAULT 0 COMMENT '推送次数', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_reconciliation_record_order` (`orderId`, `supplierId`), + KEY `idx_points_mall_reconciliation_record_batch` (`batchNo`), + KEY `idx_points_mall_reconciliation_record_notice` (`noticeStatus`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城对账记录'; + +CREATE TABLE IF NOT EXISTS `points_mall_message_template` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `name` varchar(100) NOT NULL COMMENT '模板名称', + `type` int DEFAULT NULL COMMENT '模板类型', + `content` mediumtext COMMENT '内容模板', + `contentLink` mediumtext COMMENT '内容链接', + `status` int DEFAULT 1 COMMENT '状态:0禁用,1启用', + `extParam` mediumtext COMMENT '扩展参数', + `isSendPoints` int DEFAULT 0 COMMENT '是否发放积分:0否,1是', + `sendPoints` decimal(18,2) DEFAULT 0.00 COMMENT '发放积分', + `deptIdList` mediumtext COMMENT '目标机构列表', + `unionIdList` mediumtext COMMENT '目标工会列表', + `festivalDate` varchar(100) DEFAULT NULL COMMENT '节日日期', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_message_template_type` (`type`), + KEY `idx_points_mall_message_template_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城消息模板'; + +CREATE TABLE IF NOT EXISTS `points_mall_message` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `uniqueNo` varchar(128) DEFAULT NULL COMMENT '业务唯一号', + `pushType` varchar(50) DEFAULT NULL COMMENT '推送类型', + `templateId` varchar(32) DEFAULT NULL COMMENT '模板ID', + `touser` mediumtext COMMENT '目标用户', + `toparty` mediumtext COMMENT '目标部门', + `totag` mediumtext COMMENT '目标标签', + `content` mediumtext COMMENT '推送内容', + `contentLink` mediumtext COMMENT '内容链接', + `planPushTime` datetime DEFAULT NULL COMMENT '计划推送时间', + `status` int DEFAULT 0 COMMENT '状态:0未推送,1已推送,2推送中,-1不推送', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_message_unique` (`uniqueNo`), + KEY `idx_points_mall_message_template` (`templateId`), + KEY `idx_points_mall_message_status_time` (`status`, `planPushTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城消息记录'; + +CREATE TABLE IF NOT EXISTS `points_mall_message_schedule` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `templateId` varchar(32) NOT NULL COMMENT '模板ID', + `triggerType` int DEFAULT NULL COMMENT '触发类型:1 Cron,2 间隔', + `cronExpression` varchar(100) DEFAULT NULL COMMENT 'Cron表达式', + `intervalValue` int DEFAULT NULL COMMENT '间隔值', + `intervalUnit` varchar(20) DEFAULT NULL COMMENT '间隔单位', + `status` int DEFAULT 1 COMMENT '状态:0禁用,1启用', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_points_mall_message_schedule_template` (`templateId`), + KEY `idx_points_mall_message_schedule_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城消息调度规则'; + +CREATE TABLE IF NOT EXISTS `points_mall_schedule_user_points_record` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `templateId` varchar(32) NOT NULL COMMENT '模板ID', + `userId` varchar(64) NOT NULL COMMENT '用户ID', + `year` int NOT NULL COMMENT '年度', + `sendPoints` decimal(18,2) DEFAULT 0.00 COMMENT '发放积分', + `status` int DEFAULT 0 COMMENT '状态:0待处理,1成功,2失败', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_points_mall_schedule_user_points` (`templateId`, `userId`, `year`, `delFlag`), + KEY `idx_points_mall_schedule_user_points_user` (`userId`), + KEY `idx_points_mall_schedule_user_points_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='积分商城定时发放积分记录'; diff --git a/src/main/resources/rebel.xml b/src/main/resources/rebel.xml new file mode 100644 index 00000000..82ed5d55 --- /dev/null +++ b/src/main/resources/rebel.xml @@ -0,0 +1,16 @@ + + + + + + zhgh_cug_v4 + + + + + + + diff --git a/src/main/resources/static/assets/mobile/img/mall/order/my-order.png b/src/main/resources/static/assets/mobile/img/mall/order/my-order.png new file mode 100644 index 00000000..f9a49d2c Binary files /dev/null and b/src/main/resources/static/assets/mobile/img/mall/order/my-order.png differ diff --git a/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-daily.png b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-daily.png new file mode 100644 index 00000000..d73d796e Binary files /dev/null and b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-daily.png differ diff --git a/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-quality.png b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-quality.png new file mode 100644 index 00000000..bf53ff51 Binary files /dev/null and b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-quality.png differ diff --git a/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-welfare.png b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-welfare.png new file mode 100644 index 00000000..3d79e254 Binary files /dev/null and b/src/main/resources/static/assets/mobile/img/mall/supplier-banner/supplier-banner-welfare.png differ diff --git a/src/main/resources/views/layouts/v4/msg.html b/src/main/resources/views/layouts/v4/msg.html index da3ad46e..6c141dad 100644 --- a/src/main/resources/views/layouts/v4/msg.html +++ b/src/main/resources/views/layouts/v4/msg.html @@ -130,6 +130,20 @@ layout("/layouts/v4/baseLayout.html"){ color: #303133; } + .detail-content { + white-space: pre-line; + } + + .detail-content ol { + list-style-type: decimal; + padding-left: 22px; + margin: 6px 0; + } + + .detail-content li { + display: list-item; + } + .pagination-container { margin-top: 20px; display: flex; @@ -297,7 +311,7 @@ layout("/layouts/v4/baseLayout.html"){ {{formatTime(currentMessage.createdAt)}} -
+
关闭 diff --git a/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html b/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html new file mode 100644 index 00000000..63be5be5 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/invoice/index.html @@ -0,0 +1,509 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + 开票申请 + 可开票订单 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ orderTotalPoints }} + 计算 + + + + 取消 + 提交申请 + + + + + + + + + + + + + + + + + + + + + + + + + 查询 + 重置 + 用于开票 + + + + + + + + + + + + + + + + + + + 关闭 + + + + + + + + + + + + + + + + 查询 + + + + + + + + + + + + + + + + + + 关闭 + + +
+ + + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/message/index.html b/src/main/resources/views/platform/zhgh/points-mall/message/index.html new file mode 100644 index 00000000..534d03b2 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/message/index.html @@ -0,0 +1,345 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + 新增 + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 发放 + 不发放 + + + + + + + + + + + + + + + + + + 取消 + 保存 + + + + + + + + + + + + + + + + + + + 查询 + 重置 + 确认推送 + 一键全部推送 + + + + + + + + + + + + + + + + + + + + +
+ + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/order/index.html b/src/main/resources/views/platform/zhgh/points-mall/order/index.html new file mode 100644 index 00000000..24ee1b72 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/order/index.html @@ -0,0 +1,395 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/points/index.html b/src/main/resources/views/platform/zhgh/points-mall/points/index.html new file mode 100644 index 00000000..86ee6f26 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/points/index.html @@ -0,0 +1,279 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + 批量增加积分 + 批量扣减积分 + 删除积分记录 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + 保存 + + + + + + + + + + + + + + + 取消 + 确认 + + + + + + + + + + + + + + + + + +
+ + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/pointsgrant/index.html b/src/main/resources/views/platform/zhgh/points-mall/pointsgrant/index.html new file mode 100644 index 00000000..e590fd0c --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/pointsgrant/index.html @@ -0,0 +1,199 @@ + +
+ + + + + + + + + + + + + + + + + + + + 新建 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 手动发放 + 定时发放 + + + + + + + + + + + + + + 取消 + 保存 + + +
+ + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html b/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html new file mode 100644 index 00000000..e0ee1f7d --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/reconciliation/index.html @@ -0,0 +1,448 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 对账 + 对账记录 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ reconcilePointsTotal }} + + + + + 取消 + 计算金额 + 确认对账 + + + + + + + + + + + + + + + + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + + + + + + + + + + + + 关闭 + + + + + + {{ currentOrder.orderId || "-" }} + {{ currentOrder.orderNumberId || "-" }} + {{ currentOrder.supplierName || currentOrder.supplierId || "-" }} + {{ currentOrder.userId || "-" }} + {{ currentOrder.orderCreateTime || "-" }} + {{ currentOrder.orderCompleteTime || "-" }} + {{ currentOrder.totalPrice || "0" }} + {{ currentOrder.wPayOrAPay || "0" }} + {{ currentOrder.pointsPrice || "0" }} + {{ currentOrder.refund || "0" }} + {{ currentOrder.logisticsOrderId || "-" }} + {{ currentOrder.crrgBsnNm || "-" }} + + + 关闭 + + +
+ + + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/reconciliationRecord/index.html b/src/main/resources/views/platform/zhgh/points-mall/reconciliationRecord/index.html new file mode 100644 index 00000000..b8f85a22 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/reconciliationRecord/index.html @@ -0,0 +1,196 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ currentRecord.batchNo || "-" }} + {{ currentRecord.orderId || "-" }} + {{ currentRecord.supplierName || currentRecord.supplierId || "-" }} + {{ currentRecord.userId || "-" }} + {{ currentRecord.pTotalPrice || "0" }} + {{ currentRecord.sTotalPrice || "0" }} + {{ currentRecord.pCashAmount || "0" }} + {{ currentRecord.sCashAmount || "0" }} + {{ currentRecord.pPointsPrice || "0" }} + {{ currentRecord.sPointsPrice || "0" }} + {{ currentRecord.pRefund || "0" }} + {{ currentRecord.sRefund || "0" }} + {{ diffTypeName(currentRecord.diffType) }} + {{ noticeStatusName(currentRecord.noticeStatus) }} + {{ currentRecord.pushTime || 0 }} + {{ currentRecord.diffReason || "-" }} + + + 关闭 + + +
+ + + + diff --git a/src/main/resources/views/platform/zhgh/points-mall/supplier/index.html b/src/main/resources/views/platform/zhgh/points-mall/supplier/index.html new file mode 100644 index 00000000..81ca2bf5 --- /dev/null +++ b/src/main/resources/views/platform/zhgh/points-mall/supplier/index.html @@ -0,0 +1,327 @@ + + +
+ + + + + + + + + + + + + + + + + 新建 + 删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + 保存 + + +
+ + + + diff --git a/src/main/resources/views/platform/zhghh5/points-mall/index.html b/src/main/resources/views/platform/zhghh5/points-mall/index.html new file mode 100644 index 00000000..b58408fb --- /dev/null +++ b/src/main/resources/views/platform/zhghh5/points-mall/index.html @@ -0,0 +1,517 @@ + +
+ + + +
+ + + + +
+
+ 供应商专区 + {{ filteredSuppliers.length }} 家 +
+ + +
+
+ +
+
+
+

{{ item.supplierName }}

+ 官方 +
+

{{ item.contactInfo || '精选服务,品质兑换' }}

+
+ 正品保障 + 积分兑换 + 售后无忧 +
+
+ {{ mallTypeName(item.mallType) }} + + {{ item.sroNo || '服务专区' }} +
+
+ 进入店铺 + +
+
+
+
+ +
+
+ + 正品保障品牌服务 +
+
+ + 便捷配送订单直达 +
+
+ + 售后无忧服务跟进 +
+
+
+ + + diff --git a/src/main/resources/views/platform/zhghh5/points-mall/order/index.html b/src/main/resources/views/platform/zhghh5/points-mall/order/index.html new file mode 100644 index 00000000..90947f73 --- /dev/null +++ b/src/main/resources/views/platform/zhghh5/points-mall/order/index.html @@ -0,0 +1,338 @@ + +
+ +
+
+
+
剩余积分
+
+ ¥ + {{ userPoints }} +
+
+
+ + +
+
+ +
+
+
+
+ {{ tab.name }} +
+
+
|
+
+
+ {{ tab.name }} +
+
+
+
+
+ + + +
+
+
+ {{ item.supplierName || "积分商城" }} + + {{ getOrderTypeName(item.orderType) }} +
+
+ {{ orderStateName(item.orderState) }} +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ 积分已支付,待“支付宝/微信/其他”支付 +
+
+ 关联发货单号: + {{ item.orderMainId }} +
+
+ 原发货单消耗积分: + {{ item.relPointsPrice }} +
+
+ 支付宝/微信/其他: + {{ item.wPayOrAPay }} +
+
+ 退回积分: + {{ item.refund }} +
+
+ 订单编号: + {{ item.orderNumberId }} +
+
+ 订单号: + {{ item.orderId }} +
+
+ 消费积分: + {{ item.pointsPrice || 0 }} +
+
+ 运费: + ¥ {{ item.freightCost }} +
+
+ 创建时间: + {{ item.orderCreateTime || "-" }} +
+
+ +
+
+
+ 确认收货 + 查看订单 +
+
+
+ +
+ +
+
+
+ + + diff --git a/src/main/resources/views/platform/zhghh5/sys/home/msg.js b/src/main/resources/views/platform/zhghh5/sys/home/msg.js index c211221b..02b7d4ab 100644 --- a/src/main/resources/views/platform/zhghh5/sys/home/msg.js +++ b/src/main/resources/views/platform/zhghh5/sys/home/msg.js @@ -1,130 +1,139 @@ -const msg = { +const msg= { template: /*language=HTML*/ `
- - -
-
- - - -
-
- - - - - - - -
- -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
-
- -
-
- -
-
-
- - - -

{{ message.title || '消息通知' }}

+ + + +
+
+
+ + {{getMessageTypeName(message.type)}} + + + + 未读 +
- - {{ message.isRead ? '已读' : '未读' }} - +
{{formatTime(message.sendTime || message.createdAt)}}
-
- {{ getMessageTypeName(message.type) }} · {{ formatTime(message.sendTime || message.readTime) }} +
{{getContentPreview(message.content)}}
+
+
-

{{ getContentPreview(message.content) }}

- -
-
暂无消息
+
- - + + + - + +
-

{{ detailMessage.title || '消息详情' }}

+

{{currentMessage.title}}

- - {{ getMessageTypeName(detailMessage.type) }} + + {{getMessageTypeName(currentMessage?.globalMessage?.type)}} - {{ formatTime(detailMessage.sendTime || currentMessage.readTime) }} + {{formatTime((currentMessage.globalMessage && currentMessage.globalMessage.sendTime) || currentMessage.createdAt)}}
-
+
`, - data() { - return { - activeTab: "-1", - heroVisible: true, - tabRevealProgress: 0, - scrollContainer: null, + data(){ + return{ + activeTab: '-1', loading: false, - skeletonLoading: true, refreshing: false, finished: false, messages: [], @@ -133,61 +142,40 @@ const msg = { unread: 0, read: 0 }, + filters: { + type: '' + }, pageForm: { pageNumber: 1, pageSize: 10, totalCount: 0, - isRead: -1, - type: null, - searchKeyword: "" + isRead: -1 }, detailVisible: false, currentMessage: {}, showTypePicker: false, typeColumns: [ - { text: "全部类型", value: null }, - { text: "系统公告", value: 1 }, - { text: "消息通知", value: 2 } + { text: '全部类型', value: '' }, + { text: '系统公告', value: '1' }, + { text: '消息通知', value: '2' } ] } }, - computed: { - detailMessage() { - return this.currentMessage.globalMessage || {} - } - }, methods: { - // Hero 接近顶部导航栏时按剩余高度渐显分类标签。 - initHeroObserver() { - this.scrollContainer = this.$el.closest(".page-wrapper") || document.scrollingElement - this.scrollContainer.addEventListener("scroll", this.updateHeroVisibility, {passive: true}) - this.updateHeroVisibility() - }, - - updateHeroVisibility() { - const hero = this.$refs.messageHero - const containerTop = this.scrollContainer ? this.scrollContainer.getBoundingClientRect().top : 0 - if (!hero) return - const heroBottom = hero.getBoundingClientRect().bottom - const revealProgress = (containerTop + 90 - heroBottom) / 44 - this.tabRevealProgress = Math.max(0, Math.min(1, revealProgress)) - this.heroVisible = this.tabRevealProgress < 1 - }, - + // 加载统计数据 async loadStats() { try { - const resp = await this.$axios.get("/platform/v4/msg/stats") + const resp = await this.$axios.get('/platform/v4/msg/stats') if (resp.code === 0) { this.stats = resp.data - this.$emit("unread-count-change", Number(this.stats.unread) || 0) } } catch (error) { - console.error("加载统计数据失败:", error) + console.error('加载统计数据失败:', error) } }, + + // 加载消息列表 async loadMessages(isRefresh = false) { - const showSkeleton = isRefresh || !this.messages.length - if (showSkeleton) this.skeletonLoading = true if (isRefresh) { this.pageForm.pageNumber = 1 this.messages = [] @@ -196,29 +184,41 @@ const msg = { this.loading = true try { - const resp = await this.$axios.post("/platform/v4/msg/pageData", this.pageForm) + const resp = await this.$axios.post('/platform/v4/msg/pageData', this.pageForm) if (resp.code === 0) { const newMessages = resp.data.list || [] - this.messages = isRefresh ? newMessages : this.messages.concat(newMessages) + if (isRefresh) { + this.messages = newMessages + } else { + this.messages = [...this.messages, ...newMessages] + } + this.pageForm.totalCount = resp.data.totalCount - this.finished = this.messages.length >= this.pageForm.totalCount + + // 判断是否还有更多数据 + if (this.messages.length >= this.pageForm.totalCount) { + this.finished = true + } } else { this.$toast(resp.msg) } } catch (error) { - this.$toast("加载消息失败") + this.$toast('加载消息失败') } finally { this.loading = false this.refreshing = false - if (showSkeleton) this.skeletonLoading = false } }, + + // 查看消息详情 async viewMessage(message) { try { - const resp = await this.$axios.post("/platform/v4/msg/detail/" + message.id) + const resp = await this.$axios.post(`/platform/v4/msg/detail/` + message.id) if (resp.code === 0) { this.currentMessage = resp.data this.detailVisible = true + + // 如果是未读消息,标记为已读 if (!message.isRead) { await this.markAsRead(message.id) message.isRead = true @@ -226,70 +226,65 @@ const msg = { } } } catch (error) { - this.$toast("加载消息详情失败") + this.$toast('加载消息详情失败') } }, + + // 标记单条消息为已读 async markAsRead(messageId) { try { - await this.$axios.post("/platform/v4/msg/read/" + messageId) + await this.$axios.post(`/platform/v4/msg/read/` + messageId) } catch (error) { - console.error("标记已读失败:", error) + console.error('标记已读失败:', error) } }, + + // 全部标记为已读 async markAllAsRead() { this.$dialog.confirm({ - title: "提示", - message: "确定要将所有未读消息标记为已读吗?" + title: '提示', + message: '确定要将所有未读消息标记为已读吗?' }).then(async () => { this.loading = true try { - const resp = await this.$axios.post("/platform/v4/msg/read/all") + const resp = await this.$axios.post('/platform/v4/msg/read/all') if (resp.code === 0) { - this.$toast.success("操作成功") + this.$toast.success('操作成功') this.loadMessages(true) this.loadStats() } else { this.$toast(resp.msg) } } catch (error) { - this.$toast("操作失败") + this.$toast('操作失败') } finally { this.loading = false } - }).catch(() => {}) + }).catch(() => { + // 用户取消 + }) }, + + // 切换标签页 handleTabChange(name) { this.pageForm.isRead = parseInt(name) this.loadMessages(true) - this.keepTabsVisible() - }, - // 切换分类后停留在 Hero 刚好收起的位置,避免列表清空时页面回到顶部。 - keepTabsVisible() { - this.heroVisible = false - this.tabRevealProgress = 1 - this.$nextTick(() => { - window.requestAnimationFrame(() => { - const hero = this.$refs.messageHero - const container = this.scrollContainer - if (!hero || !container) return - const containerTop = container.getBoundingClientRect().top - const targetTop = container.scrollTop + hero.getBoundingClientRect().bottom - containerTop - 45 - container.scrollTop = Math.max(0, targetTop) - }) - }) }, + + // 点击统计卡片切换标签 switchTab(tabValue) { this.activeTab = tabValue.toString() this.pageForm.isRead = tabValue this.loadMessages(true) }, - doSearch() { - this.loadMessages(true) - }, + + // 下拉刷新 onRefresh() { this.loadMessages(true) this.loadStats() }, + + // 上拉加载更多 onLoad() { if (this.finished) { return @@ -297,428 +292,320 @@ const msg = { this.pageForm.pageNumber++ this.loadMessages() }, + + // 消息类型选择确认 onTypeConfirm(value) { - this.pageForm.type = value.value + this.filters.type = value.value this.showTypePicker = false this.loadMessages(true) }, + + // 获取消息类型名称 getMessageTypeName(type) { - return type === 1 ? "系统公告" : "消息通知" - }, - getMessageIconClass(type) { - return type === 1 ? "message-card-icon--notice" : "message-card-icon--system" + return type === 1 ? '系统公告' : '消息通知' }, + + // 获取内容预览 getContentPreview(content) { - if (!content) { - return "暂无消息内容" - } - const text = content.replace(/<[^>]*>/g, "").trim() - return text.length > 52 ? text.substring(0, 52) + "..." : text + if (!content) return '' + // 移除HTML标签 + const text = content.replace(/<[^>]*>/g, '') + return text.length > 60 ? text.substring(0, 30) + '...' : text }, + + // 格式化时间 formatTime(timestamp) { - if (!timestamp) { - return "时间未知" - } - return this.$moment(timestamp).format("MM-DD HH:mm") + if (!timestamp) return '' + return this.$moment(timestamp).format('MM-DD HH:mm') } }, + created() { this.loadStats() this.loadMessages(true) }, - mounted() { - this.$nextTick(() => this.initHeroObserver()) - }, - beforeDestroy() { - if (this.scrollContainer) this.scrollContainer.removeEventListener("scroll", this.updateHeroVisibility) - }, + style: /*language=CSS*/ ` .message-center { + background-color: #f5f7fa; min-height: 100vh; - padding-bottom: 72px; - box-sizing: border-box; - background: #f5f7fb; + padding-bottom: 20px; } - /deep/ .message-nav, - /deep/ .message-nav .van-nav-bar { - background-color: #0879f9 !important; - } - /deep/ .message-nav .van-nav-bar::after { - border-bottom: 0; - } - /deep/ .message-nav .van-nav-bar__title { - color: #fff; - font-size: 16px; - font-weight: 500; - } - - .message-hero { + /* 自定义消息列表项样式 */ + /deep/ .custom-message-item { + background-color: #fff; + padding: 10px; + margin-bottom: 8px; + cursor: pointer; + transition: all 0.2s ease; position: relative; - height: 92px; - background-color: #0879f9; - } - - .message-statistics { - position: absolute; - top: 0; - right: 12px; - left: 12px; - display: flex; - height: 78px; - padding: 10px 6px 8px; - overflow: hidden; - box-sizing: border-box; - border-radius: 10px; - background: #fff; - box-shadow: 0 6px 18px rgba(45, 91, 145, 0.14); - } - - .message-statistic-item { - display: flex; - min-width: 0; - flex: 1; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 0; - border: 0; - outline: 0; - background: transparent; - } - - .message-statistic-item:active { - opacity: 0.7; - } - - .message-statistic-icon { - display: flex; - width: 24px; - height: 24px; - align-items: center; - justify-content: center; - border-radius: 50%; - color: #fff; - font-size: 15px; - } - - .message-statistic-icon--all { - background: #428bf4; - } - - .message-statistic-icon--unread { - background: #ffad3d; - } - - .message-statistic-icon--read { - background: #4cc994; - } - - .message-statistic-item strong { - margin-top: 3px; - color: #26364d; - font-size: 14px; - font-weight: 600; - line-height: 18px; - } - - .message-statistic-item > span:last-child { - color: #8d97a7; - font-size: 12px; - line-height: 15px; - } - - /deep/ .message-tabs .van-tabs__wrap { - height: 52px; - } - - /deep/ .message-tabs .van-tabs__nav { - background: #fff; - } - - /deep/ .message-tabs .van-tab { - position: relative; - color: #7f8998; - font-size: 14px; - transition: color 0.2s, font-size 0.2s; - } - - /deep/ .message-tabs .van-tab:not(:last-child)::after { - position: absolute; - top: 13px; - right: 0; - bottom: 13px; - width: 1px; - background: #edf1f6; - content: ""; - transform: skewX(-18deg); - } - - /deep/ .message-tabs .van-tab--active { - color: var(--color-primary, #1989fa); - font-size: 16px; - font-weight: 600; - } - - /deep/ .message-tabs .van-tabs__line { - bottom: 7px; - width: 24px; - height: 4px; - border-radius: 4px; - background-color: var(--color-primary, #1989fa); - } - - /deep/ .message-tabs--hero-replacement { - position: fixed; - top: 46px; - right: 0; - left: 0; - z-index: 100; - transition: opacity 0.12s linear; - } - - /deep/ .van-sticky--fixed { - background: #f5f7fb; - } - - .message-search-section { - display: flex; - padding: 10px 12px; - align-items: center; - background: #f5f7fb; - } - - .message-search-section .van-search { - min-width: 0; - flex: 1; - padding: 0; - overflow: hidden; - border-radius: 18px; - background: #fff; - } - - /deep/ .message-search-section .van-search__content { - border-radius: 18px; - background: #fff; - } - - /deep/ .message-search-section .van-field__control { - font-size: 14px; - } - - .message-filter-button { - display: flex; - width: 34px; - height: 34px; - flex: 0 0 34px; - align-items: center; - justify-content: center; - margin-left: 6px; - padding: 0; - border: 0; + border-bottom: 1px solid #f0f0f0; border-radius: 8px; - outline: 0; - background: #fff; - color: #2582ef; - font-size: 15px; } - .message-filter-button--active { - background: #eaf3ff; + /deep/ .custom-message-item:hover { + background-color: #f8f9fa; } - /deep/ .message-list { - min-height: calc(100vh - 140px); - padding: 8px 12px 0; + /deep/ .custom-message-item:active { + background-color: #f0f0f0; } - /deep/ .message-skeleton-list { - min-height: calc(100vh - 140px); - padding: 8px 12px 0; - box-sizing: border-box; - } - - /deep/ .message-skeleton-card { - margin-bottom: 10px; - padding: 18px 4px; - border-radius: 10px; - background: #fff; - box-shadow: 0 2px 10px rgba(31, 68, 113, .07); - } - - /deep/ .message-card { - margin-bottom: 9px; - padding: 11px 12px 8px; - border-radius: 9px; - background: #fff; - box-shadow: 0 2px 10px rgba(31, 68, 113, 0.07); - } - - /deep/ .message-card:active { - background: #f9fbfd; - } - - /deep/ .message-card-header { + /deep/ .message-header { display: flex; - min-width: 0; - align-items: center; justify-content: space-between; + align-items: flex-start; + margin-bottom: 12px; } - /deep/ .message-card-title-wrap { + /deep/ .message-time { + font-size: 12px; + color: #969799; + flex-shrink: 0; + margin-left: 12px; + } + + /deep/ .message-content { + font-size: 14px; + color: #646566; + line-height: 1.6; + margin-bottom: 8px; + padding-right: 20px; + } + + /deep/ .message-arrow { + position: absolute; + right: 16px; + top: 50%; + transform: translateY(-50%); + color: #c8c9cc; + font-size: 12px; + } + + /* 筛选区域样式 */ + /deep/ .filter-section { + padding: 12px 16px; + background-color: #fff; + } + + /deep/ .message-title-wrapper { display: flex; - min-width: 0; + align-items: center; + flex-wrap: wrap; flex: 1; - align-items: center; } - /deep/ .message-card-icon { - display: flex; - width: 24px; - height: 24px; - flex: 0 0 24px; - align-items: center; - justify-content: center; - margin-right: 8px; - border-radius: 6px; - font-size: 15px; - } - - /deep/ .message-card-icon--system { - color: #438df5; - background: #edf5ff; - } - - /deep/ .message-card-icon--notice { - color: #f3a032; - background: #fff5e8; - } - - /deep/ .message-card-title-wrap h3 { - overflow: hidden; - margin: 0; - color: #26364d; - font-size: 14px; - font-weight: 600; - line-height: 21px; - text-overflow: ellipsis; - white-space: nowrap; - } - - /deep/ .message-read-state { - flex: 0 0 auto; - margin-left: 8px; - padding: 3px 7px; - border-radius: 4px; - background: #fff5e8; - color: #f0a03a; - font-size: 12px; - line-height: 16px; - } - - /deep/ .message-read-state--read { - background: #f0f3f7; - color: #8b96a6; - } - - /deep/ .message-card-meta { - margin: 3px 0 0 32px; - color: #8d97a7; - font-size: 12px; - line-height: 18px; - } - - /deep/ .message-card-content { - overflow: hidden; - margin: 8px 0 0; - padding-top: 7px; - border-top: 1px solid #edf1f6; - color: #637083; - font-size: 13px; - line-height: 20px; - text-overflow: ellipsis; - white-space: nowrap; - } - - /deep/ .message-card-footer { - display: flex; - align-items: center; - justify-content: flex-end; - margin-top: 6px; - color: #2783ef; - font-size: 13px; - line-height: 18px; - } - - /deep/ .message-card-footer .van-icon { - margin-left: 2px; - } - - /deep/ .message-empty { - padding: 36px 0; - color: #9aa3b1; - font-size: 14px; - text-align: center; - } - - /deep/ .van-pull-refresh__track { - background: #f5f7fb; - } - - /deep/ .van-list__finished-text { - padding: 12px 0 16px; - color: #a1a9b5; - font-size: 13px; - } - - /deep/ .van-popup--bottom { - border-radius: 16px 16px 0 0; + /deep/ .message-title { + flex: 1; + /*font-weight: 500;*/ + color: #323233; } + /* 消息详情弹窗样式 */ /deep/ .message-detail { + padding: 20px; height: 100%; - padding: 22px 18px; overflow-y: auto; - box-sizing: border-box; - background: #fff; + background-color: #fff; } /deep/ .detail-header { - margin-bottom: 18px; - padding: 0 28px 15px 0; - border-bottom: 1px solid #edf1f6; + text-align: center; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #eee; } /deep/ .detail-header h3 { - margin: 0 0 9px; - color: #26364d; font-size: 18px; - font-weight: 600; - line-height: 26px; + font-weight: 500; + margin: 0 0 8px 0; + color: #323233; } /deep/ .detail-meta { display: flex; align-items: center; + justify-content: center; gap: 8px; - color: #8d97a7; - font-size: 12px; + font-size: 14px; + color: #666; + } + + /deep/ .detail-time { + color: #969799; } /deep/ .detail-content { - color: #323b4a; - font-size: 15px; line-height: 1.8; - word-break: break-word; + font-size: 15px; + color: #323233; + white-space: pre-line; } /deep/ .detail-content img { max-width: 100%; height: auto; + border-radius: 4px; margin: 8px 0; - border-radius: 6px; + } + + /deep/ .detail-content p { + margin: 12px 0; + } + + /deep/ .detail-content ol { + list-style-type: decimal; + padding-left: 22px; + margin: 6px 0; + } + + /deep/ .detail-content li { + display: list-item; + } + + /* 标签页样式优化 */ + /deep/ .van-tabs__nav { + background-color: #fff; + } + + /deep/ .van-tab { + font-size: 14px; + } + + /deep/ .van-tab--active { + color: var(--color-primary, #1989fa); + font-weight: 500; + } + + /deep/ .van-tabs__line { + background-color: var(--color-primary, #1989fa); + } + + /* 列表样式优化 */ + /deep/ .van-cell__title { + font-size: 15px; + } + + /deep/ .van-cell__label { + font-size: 13px; + color: #969799; + margin-top: 4px; + } + + /deep/ .van-cell__value { + font-size: 12px; + color: #969799; + } + + /* 标签样式 */ + /deep/ .van-tag--mini { + font-size: 10px; + padding: 2px 6px; + border-radius: 4px; + } + + /* 弹窗样式 */ + /deep/ .van-popup--bottom { + border-radius: 16px 16px 0 0; + } + + /* 下拉刷新和上拉加载样式 */ + /deep/ .van-pull-refresh__track { + background-color: #f5f7fa; + } + + /deep/ .van-list__finished-text { + color: #969799; + font-size: 12px; + padding: 16px 0; + } + + /* 空状态样式 */ + /deep/ .van-empty { + padding: 40px 0; + } + + /deep/ .van-empty__description { + color: #969799; + font-size: 14px; + } + + /* 简约动画效果 */ + + /* 基础过渡效果 */ + /deep/ .oa-message-item, + /deep/ .oa-stat-item, + /deep/ .oa-refresh-btn, + /deep/ .oa-search-input { + transition: all 0.2s ease; + } + + /* 页面进入动画 */ + /deep/ .page-enter { + animation: fadeIn 0.3s ease; + } + + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + /* 简化的响应式动画 */ + @media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + } + + /* 响应式设计 */ + @media (max-width: 480px) { + /deep/ .oa-header { + padding: 10px 12px; + } + + /deep/ .oa-header-title { + font-size: 15px; + } + + /deep/ .oa-stats-panel, + /deep/ .oa-toolbar, + /deep/ .oa-message-list { + margin: 6px 12px; + } + + /deep/ .oa-stat-item { + padding: 12px 8px; + } + + /deep/ .oa-stat-number { + font-size: 16px; + } + + /deep/ .oa-stat-label { + font-size: 11px; + } + + /deep/ .oa-message-item { + padding: 12px; + } + + /deep/ .oa-message-title { + font-size: 14px; + } + + /deep/ .oa-message-content { + font-size: 12px; + } } ` } diff --git a/src/test/java/com/budwk/app/MinioTest.java b/src/test/java/com/budwk/app/MinioTest.java new file mode 100644 index 00000000..eb5eceb4 --- /dev/null +++ b/src/test/java/com/budwk/app/MinioTest.java @@ -0,0 +1,147 @@ +package com.budwk.app; + +import io.minio.*; +import io.minio.errors.*; +import io.minio.messages.Item; +import org.apache.commons.io.FileUtils; + +import java.io.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +class MinioUtil { + MinioClient minioClient = null; + String bucketName = "njupt"; + String endpoint = "http://localhost:9000"; + String accessKey = "o1MwuzixNqAXh5zqEfMH"; + String secretKey = "RVnZum91JmGO79qtzWy2gKMBsF6OxAaibGrr7ets"; + + public MinioUtil() { + //构造方法 minio客户端 + minioClient = MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + System.out.println("Minio客户端链接成功"); + } + + /** + * 如果桶不存在则创建桶 + */ + private void createBucketName() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (exists) { + System.out.println(bucketName + " exists"); + } else { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + System.out.println(bucketName + " is created successfully"); + } + } + + public void fileUpload(String path, String fileUploadPath, String fileName) { + try { + FileInputStream fileInputStream = new FileInputStream(new File(path)); + fileUpload(fileInputStream, fileUploadPath, fileName); + } catch (Exception e) { + System.out.println(e); + } + } + + public void fileUpload(InputStream is, String fileUploadPath, String fileName) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + try { + this.createBucketName(); + } catch (ServerException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + minioClient.putObject( + PutObjectArgs + .builder() + .bucket(bucketName) + .object(fileUploadPath + "/" + fileName) + .stream(is, -1, 10485760) + .build()); + System.out.println("successfully uploaded as 【" + fileUploadPath + "】 to 【" + bucketName + "】bucket."); + } + + private void fileDownload(String filePath, String fileName) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + filePath = filePath.replace("\\", "/"); + minioClient.downloadObject( + DownloadObjectArgs.builder() + .bucket(bucketName) + .object(filePath) + .filename(fileName) + .build()); + System.out.println("successfully uploaded as 【" + filePath + "/" + fileName + "】 to 【" + bucketName + "】bucket."); + } + + public InputStream downloadInputSteam(String filePath) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + filePath = filePath.replace("\\", "/"); + return minioClient.getObject( + GetObjectArgs.builder() + .bucket(bucketName) + .object(filePath) + .build()); + } + + + public StatObjectResponse getFileInfo(String filePath) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + filePath = filePath.replace("\\", "/"); + return minioClient.statObject(StatObjectArgs.builder() + .bucket(bucketName) + .object(filePath) + .build()); + } + + public Iterable> getFiles(String path) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(path).build()); + } + + /** + * 删除文件 + * + * @param bucketName 库名字 + * @param fileName 文件路径 + * @return + */ + public boolean delete(String bucketName, String fileName) { + /* + * String bucketName = "test2"; + * String fileName = "/2023-04-07/16808560218465670_img.png"; + * address+bucketName+fileName 就是访问路径,删除需要后两个参数。 + */ + try { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build()); + return true; + } catch (Exception e) { + return false; + } + } +} + +public class MinioTest { + public static void main(String[] args) throws Exception { + + MinioUtil minioUtil = new MinioUtil(); +// Iterable> iterable = minioUtil.getFiles("data/"); +// for (Result itemResult : iterable) { +// Item item = itemResult.get(); +// System.out.println(item.objectName()); +// InputStream inputStream = minioUtil.downloadInputSteam(item.objectName()); +// String targetPath = "D:\\tmp\\" + item.objectName(); +// File file = new File(targetPath); +// FileUtils.copyInputStreamToFile(inputStream, file); +// Map map = PdfUtil.readPdf2Txt(file.getAbsolutePath()); +// for (Map.Entry entry : map.entrySet()) { +// System.out.println(entry.getKey() + " " + entry.getValue()); +// } +// } + String path = "E:\\DevOpsCode\\zhgh_cug_v4\\README.md"; + String fileUploadPath = "data"; + minioUtil.fileUpload(path, fileUploadPath, "README.md"); +// boolean success = minioUtil.delete("njupt", "data/README.md"); +// System.out.println(success?"成功":"失败"); + } +} \ No newline at end of file