61 Commits
pay ... test

Author SHA1 Message Date
33c6a10d19 文件 过大 问题 2026-01-09 18:37:12 +08:00
89a98123e7 文件 过大 问题 2026-01-09 18:34:13 +08:00
a6991905b8 文件 过大 问题 2026-01-09 18:27:06 +08:00
38f1dc9d1f 回填上传id 2026-01-09 18:21:44 +08:00
gong
021a837c1b 微信 Wechatpay-Serial 2026-01-09 18:18:53 +08:00
gong
3a5bd9e994 支付宝 直付通参数配置 2026-01-09 18:03:14 +08:00
gong
dfd2c74c33 微信支付修改 2026-01-09 17:33:32 +08:00
9b74b38799 更新 2026-01-09 17:06:44 +08:00
gong
0513cd5e65 微信请求 header 添加 Wechatpay-Serial 2026-01-09 17:06:11 +08:00
gong
8cbf20e751 EntryThirdRespDto 为 null 的情况 2026-01-09 16:58:26 +08:00
e203d7275a 进件标识 2026-01-09 16:55:40 +08:00
504e9b131c 进件标识 2026-01-09 16:52:03 +08:00
ef8eabb06c mq问题 2026-01-09 16:49:22 +08:00
16a814568d where 标签 2026-01-09 16:15:04 +08:00
e3bf70ab9e MQ 2026-01-09 16:10:04 +08:00
7ac540dfa9 MQ 2026-01-09 15:59:37 +08:00
423423ca0b 关联主键 2026-01-09 15:54:52 +08:00
gong
4e686c9c8e 微信进件 bug 修复 2026-01-09 15:53:24 +08:00
d7233545c7 提出字段
新增 列表接口
2026-01-09 15:46:18 +08:00
cab370d1e1 新增 支付宝授权参数 2026-01-09 11:29:53 +08:00
gong
3a70c1ced3 当面付进件修改1 2026-01-09 10:51:44 +08:00
gong
db3c635197 当面付进件修改1 2026-01-09 10:51:31 +08:00
gong
6180cc6801 Merge branch 'pay' into test 2026-01-08 18:19:40 +08:00
4d6715ac9e Merge branch 'pay' into test 2026-01-08 16:33:36 +08:00
36a6a21987 Merge branch 'pay' into test 2026-01-08 16:21:42 +08:00
ec0e281cfa 接口名称问题 2026-01-08 10:57:07 +08:00
59932b00f4 Merge branch 'pay' into test
# Conflicts:
#	cash-api/order-server/src/main/java/com/czg/mq/PrintMqListener.java
2026-01-08 10:50:18 +08:00
e8b2924d60 计算成本价问题 2026-01-05 14:32:19 +08:00
f3c42856fc 统计 2026-01-05 11:28:28 +08:00
0b02f66fb5 shop_user查询多个的问题 2026-01-05 09:38:17 +08:00
acf04b8534 开关标识 2025-12-31 09:24:09 +08:00
cc8be70559 重新打印问题 失败标识问题 2025-12-30 15:21:58 +08:00
ee635dcc32 日期格式问题 2025-12-30 15:17:52 +08:00
dc7146942b 赋值问题 2025-12-30 15:05:06 +08:00
93b2309fcd 修改标识 2025-12-30 14:25:37 +08:00
0684e0459f 修改标识 2025-12-30 14:11:38 +08:00
a4fc02dcc3 文档地址 2025-12-30 13:55:11 +08:00
d854ff9e30 MQ重试 2025-12-30 13:32:16 +08:00
befd2942a2 打印问题 2025-12-30 11:49:54 +08:00
7384b67c50 打印问题 2025-12-30 10:23:04 +08:00
329d9dfb3d 快捷菜单 2025-12-29 17:59:50 +08:00
0ca2a6c7f8 快捷菜单 2025-12-29 16:30:50 +08:00
58104d2afa 快捷菜单 2025-12-29 16:28:09 +08:00
3d6061342a Merge remote-tracking branch 'origin/test' into test 2025-12-29 15:49:01 +08:00
76f6e7776e 打印机类型 2025-12-29 15:47:02 +08:00
gong
87797e5812 关联商品查询同步商品 2025-12-29 15:32:23 +08:00
057f851dcf 返回数据 尝试 2025-12-29 14:51:26 +08:00
dc3be2f1d8 打印状态字段 2025-12-29 14:40:39 +08:00
232bfe84df 多余打印 2025-12-29 14:29:10 +08:00
ff09e8e92b 打印状态标识 允许重新操作 2025-12-29 14:26:51 +08:00
5d9c01f427 打印状态标识 允许重新操作 2025-12-29 14:23:05 +08:00
5d7aaa2dca 企业Id 信息 2025-12-29 10:08:25 +08:00
5595a8009b 绑定手机号 判断 是否成为 会员 2025-12-26 11:42:18 +08:00
233c226dca 精简sql 2025-12-26 11:34:22 +08:00
76c6e12c72 统计充值金额 2025-12-26 11:19:43 +08:00
5b030ea361 名字问题 2025-12-26 11:06:07 +08:00
266e782fc0 Merge branch 'dev' into prod 2025-12-26 10:50:58 +08:00
aa7483b1b1 临时菜标识
扣除积分 负数
2025-12-26 10:49:36 +08:00
7eeeb8a30f 分销 定时任务 2025-12-26 10:44:43 +08:00
70307e3833 分销问题
会员充值问题
2025-12-26 10:37:32 +08:00
8bde6b15f6 成长值问题 2025-12-26 09:24:28 +08:00
78 changed files with 1740 additions and 1081 deletions

View File

@@ -1,39 +0,0 @@
package com.czg.controller;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserFlowService;
import com.czg.account.service.ShopUserService;
import com.czg.account.service.TestService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author GYJoker
*/
@RestController
@RequestMapping("/notify/test")
public class TestController {
@Resource
private ShopUserFlowService shopUserFlowService;
@Resource
private ShopInfoService shopInfoService;
@Resource
private ShopUserService shopUserService;
@Resource
private TestService testService;
@RequestMapping("/hello")
public String hello() {
shopUserFlowService.list().forEach(item -> {
ShopUser shopUserInfo = shopUserService.getShopUserInfo(item.getShopId(), item.getUserId());
if (shopUserInfo != null) {
item.setShopUserId(shopUserInfo.getId());
shopUserFlowService.updateById(item);
}
});
// return testService.insertData();
return "";
}
}

View File

@@ -4,14 +4,18 @@ import com.czg.account.dto.menu.MenuAddDTO;
import com.czg.account.dto.menu.MenuDelDTO; import com.czg.account.dto.menu.MenuDelDTO;
import com.czg.account.dto.menu.MenuEditDTO; import com.czg.account.dto.menu.MenuEditDTO;
import com.czg.account.entity.CashMenu; import com.czg.account.entity.CashMenu;
import com.czg.account.entity.QuickMenu;
import com.czg.account.entity.SysMenu; import com.czg.account.entity.SysMenu;
import com.czg.account.service.QuickMenuService;
import com.czg.account.service.SysMenuService; import com.czg.account.service.SysMenuService;
import com.czg.account.vo.MenuVO; import com.czg.account.vo.MenuVO;
import com.czg.annotation.SaAdminCheckPermission; import com.czg.annotation.SaAdminCheckPermission;
import com.czg.annotation.SaAdminCheckRole; import com.czg.annotation.SaAdminCheckRole;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.sa.StpKit; import com.czg.sa.StpKit;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -19,6 +23,7 @@ import java.util.List;
/** /**
* 菜单管理 * 菜单管理
*
* @author zs * @author zs
*/ */
@RestController @RestController
@@ -27,9 +32,12 @@ public class MenuController {
@Resource @Resource
private SysMenuService menuService; private SysMenuService menuService;
@Resource
private QuickMenuService quickMenuService;
/** /**
* 获取当前用户菜单列表 * 获取当前用户菜单列表
*
* @return 菜单结构 * @return 菜单结构
*/ */
@GetMapping @GetMapping
@@ -40,6 +48,7 @@ public class MenuController {
/** /**
* 收银机菜单 * 收银机菜单
*
* @return 所有菜单 * @return 所有菜单
*/ */
@GetMapping("/list/cash") @GetMapping("/list/cash")
@@ -49,6 +58,7 @@ public class MenuController {
/** /**
* 获取所有菜单 * 获取所有菜单
*
* @return 菜单结构 * @return 菜单结构
*/ */
@SaAdminCheckPermission(parentName = "菜单管理", value = "menu:list", name = "菜单列表") @SaAdminCheckPermission(parentName = "菜单管理", value = "menu:list", name = "菜单列表")
@@ -62,6 +72,7 @@ public class MenuController {
/** /**
* 菜单详情 * 菜单详情
*
* @return 菜单结构 * @return 菜单结构
*/ */
@SaAdminCheckRole("管理员") @SaAdminCheckRole("管理员")
@@ -73,6 +84,7 @@ public class MenuController {
/** /**
* 菜单添加 * 菜单添加
*
* @return 是否成功 * @return 是否成功
*/ */
@SaAdminCheckRole("管理员") @SaAdminCheckRole("管理员")
@@ -84,6 +96,7 @@ public class MenuController {
/** /**
* 菜单修改 * 菜单修改
*
* @return 是否成功 * @return 是否成功
*/ */
@SaAdminCheckRole("管理员") @SaAdminCheckRole("管理员")
@@ -95,12 +108,15 @@ public class MenuController {
/** /**
* 菜单删除 * 菜单删除
*
* @return 是否成功 * @return 是否成功
*/ */
@SaAdminCheckRole("管理员") @SaAdminCheckRole("管理员")
@SaAdminCheckPermission(parentName = "菜单管理", value = "menu:del", name = "菜单删除") @SaAdminCheckPermission(parentName = "菜单管理", value = "menu:del", name = "菜单删除")
@DeleteMapping() @DeleteMapping()
@Transactional
public CzgResult<Boolean> edit(@RequestBody @Validated MenuDelDTO menuDelDTO) { public CzgResult<Boolean> edit(@RequestBody @Validated MenuDelDTO menuDelDTO) {
quickMenuService.remove(QueryWrapper.create().eq(QuickMenu::getMenuId, menuDelDTO.getId()));
return CzgResult.success(menuService.removeById(menuDelDTO.getId())); return CzgResult.success(menuService.removeById(menuDelDTO.getId()));
} }

View File

@@ -0,0 +1,70 @@
package com.czg.controller.admin;
import cn.hutool.core.collection.CollUtil;
import com.czg.account.entity.QuickMenu;
import com.czg.account.service.QuickMenuService;
import com.czg.annotation.SaAdminCheckPermission;
import com.czg.resp.CzgResult;
import com.czg.sa.StpKit;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
/**
* 悬浮窗/快捷菜单
*
* @author ww
* @description
*/
@RestController
@RequestMapping("/admin/quick")
@Slf4j
public class QuickMenuController {
@Resource
private QuickMenuService quickMenuService;
@SaAdminCheckPermission(parentName = "悬浮窗", value = "quick:list", name = "悬浮窗-列表")
@GetMapping
public CzgResult<List<QuickMenu>> getQuickList(@RequestParam(required = false) Integer status,
@RequestParam(required = false, defaultValue = "0") Integer isEdit) {
List<QuickMenu> list = quickMenuService.list(QueryWrapper.create()
.eq(QuickMenu::getShopId, StpKit.USER.getShopId())
.eq(QuickMenu::getStatus, status)
.orderBy(QuickMenu::getSort, true));
if (isEdit.equals(0)) {
if (CollUtil.isEmpty(list)) {
list = quickMenuService.list(QueryWrapper.create()
.eq(QuickMenu::getShopId, 1)
.eq(QuickMenu::getStatus, status)
.orderBy(QuickMenu::getSort, true));
}
}
return CzgResult.success(list);
}
@SaAdminCheckPermission(parentName = "悬浮窗", value = "quick:list", name = "悬浮窗-新增")
@PostMapping
public CzgResult<Boolean> saveQuick(@RequestBody @Validated QuickMenu quickMenu) {
quickMenu.setShopId(StpKit.USER.getShopId());
return CzgResult.success(quickMenuService.save(quickMenu));
}
@SaAdminCheckPermission(parentName = "悬浮窗", value = "quick:list", name = "悬浮窗-修改")
@PutMapping
public CzgResult<Boolean> updateQuick(@RequestBody @Validated QuickMenu quickMenu) {
return CzgResult.success(quickMenuService.update(quickMenu, QueryWrapper.create()
.eq(QuickMenu::getId, quickMenu.getId()).eq(QuickMenu::getShopId, StpKit.USER.getShopId())));
}
@SaAdminCheckPermission(parentName = "悬浮窗", value = "quick:list", name = "悬浮窗-删除")
@DeleteMapping
public CzgResult<Boolean> deleteQuick(@RequestBody Set<Long> ids) {
return CzgResult.success(quickMenuService.remove(QueryWrapper.create().in(QuickMenu::getId, ids).eq(QuickMenu::getShopId, StpKit.USER.getShopId())));
}
}

View File

@@ -13,6 +13,7 @@ import com.czg.market.vo.InviteUserVO;
import com.czg.market.vo.MkDistributionConfigVO; import com.czg.market.vo.MkDistributionConfigVO;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.sa.StpKit; import com.czg.sa.StpKit;
import com.czg.task.DistributionTask;
import com.czg.utils.AssertUtil; import com.czg.utils.AssertUtil;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -37,6 +38,22 @@ public class UDistributionController {
private MkDistributionWithdrawFlowService withdrawFlowService; private MkDistributionWithdrawFlowService withdrawFlowService;
@Resource @Resource
private MkDistributionFlowService distributionFlowService; private MkDistributionFlowService distributionFlowService;
@Resource
private DistributionTask distributionTask;
/**
* 分销员中心-获取配置
*/
@GetMapping("/task")
public CzgResult<String> task(@RequestParam Long shopId) {
try {
distributionTask.deliver(shopId);
} catch (Exception e) {
return CzgResult.failure(e.getMessage());
}
return CzgResult.success("任务执行成功");
}
/** /**
* 分销员中心-获取配置 * 分销员中心-获取配置

View File

@@ -6,6 +6,7 @@ import org.springframework.stereotype.Component;
/** /**
* market服务 任务总调度 * market服务 任务总调度
*
* @author ww * @author ww
*/ */
@Component @Component
@@ -18,9 +19,9 @@ public class AAMarketTasks {
// 分销延时发放 // 分销延时发放
@Scheduled(fixedRate = 30000) @Scheduled(cron = "0 0 0/2 * * ? ")
public void distributionTask() { public void distributionTask() {
distributionTask.deliver(); distributionTask.deliver(null);
} }
@@ -29,6 +30,7 @@ public class AAMarketTasks {
public void birthdayGiftTask() { public void birthdayGiftTask() {
birthdayGiftTask.deliver(); birthdayGiftTask.deliver();
} }
//会员生日弹窗提醒重置 每年1月1日 //会员生日弹窗提醒重置 每年1月1日
@Scheduled(cron = "0 0 0 1 1 ?") @Scheduled(cron = "0 0 0 1 1 ?")
public void birthdayGiftRemindTask() { public void birthdayGiftRemindTask() {
@@ -39,6 +41,7 @@ public class AAMarketTasks {
//优惠券 过期 //优惠券 过期
@Resource @Resource
private CouponTask couponTask; private CouponTask couponTask;
//每天每小时的30分 0秒 执行 //每天每小时的30分 0秒 执行
@Scheduled(cron = "0 30 * * * ? ") @Scheduled(cron = "0 30 * * * ? ")
public void couponTask() { public void couponTask() {
@@ -48,6 +51,7 @@ public class AAMarketTasks {
//会员奖励发放 //会员奖励发放
@Resource @Resource
private MemberTask memberTask; private MemberTask memberTask;
//每天1点 0分 0秒 执行 //每天1点 0分 0秒 执行
@Scheduled(cron = "0 0 1 * * ? ") @Scheduled(cron = "0 0 1 * * ? ")
public void memberTask() { public void memberTask() {
@@ -57,6 +61,7 @@ public class AAMarketTasks {
//满减活动/限时折扣 处理任务状态 定时任务 //满减活动/限时折扣 处理任务状态 定时任务
@Resource @Resource
private ActivityStatusTask activityStatusTask; private ActivityStatusTask activityStatusTask;
//每天0点 0分 1秒 执行 //每天0点 0分 1秒 执行
@Scheduled(cron = "1 0 0 * * ? ") @Scheduled(cron = "1 0 0 * * ? ")
public void activityStatusTask() { public void activityStatusTask() {
@@ -67,6 +72,7 @@ public class AAMarketTasks {
//月累计 发送条数 累计金额 //月累计 发送条数 累计金额
@Resource @Resource
private SmsShopMoneyTask smsShopMoneyTask; private SmsShopMoneyTask smsShopMoneyTask;
//每月1号 0点 0分 1秒 执行 //每月1号 0点 0分 1秒 执行
@Scheduled(cron = "1 0 0 1 * ?") @Scheduled(cron = "1 0 0 1 * ?")
public void smsShopMoneyTask() { public void smsShopMoneyTask() {

View File

@@ -1,18 +1,17 @@
package com.czg.task; package com.czg.task;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil; import com.czg.account.entity.ShopInfo;
import com.czg.account.entity.ShopUser;
import com.czg.account.service.ShopInfoService; import com.czg.account.service.ShopInfoService;
import com.czg.account.service.ShopUserService;
import com.czg.constant.TableValueConstant; import com.czg.constant.TableValueConstant;
import com.czg.constants.SystemConstants;
import com.czg.exception.CzgException;
import com.czg.market.entity.MkDistributionFlow; import com.czg.market.entity.MkDistributionFlow;
import com.czg.market.service.MkDistributionFlowService; import com.czg.market.service.MkDistributionFlowService;
import com.czg.market.service.MkDistributionUserService; import com.czg.market.service.MkDistributionUserService;
import com.czg.market.service.OrderInfoService; import com.czg.market.service.OrderInfoService;
import com.czg.order.entity.OrderInfo; import com.czg.order.entity.OrderInfo;
import com.czg.service.market.enums.OrderStatusEnums; import com.czg.service.market.enums.OrderStatusEnums;
import com.czg.utils.FunUtils;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -40,8 +39,6 @@ public class DistributionTask {
private OrderInfoService orderInfoService; private OrderInfoService orderInfoService;
@DubboReference @DubboReference
private ShopInfoService shopInfoService; private ShopInfoService shopInfoService;
@DubboReference
private ShopUserService shopUserService;
List<String> list = List.of(OrderStatusEnums.REFUND.getCode(), OrderStatusEnums.PART_REFUND.getCode()); List<String> list = List.of(OrderStatusEnums.REFUND.getCode(), OrderStatusEnums.PART_REFUND.getCode());
@@ -51,44 +48,55 @@ public class DistributionTask {
*/ */
// @Scheduled(cron = "0 0 0 * * ?") // @Scheduled(cron = "0 0 0 * * ?")
// @Scheduled(fixedRate = 30000) // @Scheduled(fixedRate = 30000)
public void deliver() { public void deliver(Long shopId) {
// 1. 订单完成支付时(判断是否分销)产生流水记录。 LocalDateTime now = LocalDateTime.now();
// 2. 判断入账时间。 List<ShopInfo> shopInfos = shopInfoService.list(QueryWrapper.create()
// 3. 如果是 0 天,再去判断商户余额是否足够。够则入账,不足则不管。 .eq(ShopInfo::getIsDeleted, SystemConstants.OneZero.ZERO)
// 4. 流水增加应该入账的时间(订单产生时带入) .isNotNull(ShopInfo::getExpireTime)
// 5. 定时任务 应该是一天执行一次。查询待入账状态和应入账时间小于当前时间的记录,循环处理:并且判断商户余额是否足够,余额不足忽略处理;余额足够变为已入账并扣除商户余额。 .lt(ShopInfo::getExpireTime, now)
// 6. 订单产生退款时,去流水表查询该订单的流水记录,如果未入账改为已入账,并插入一条退款扣钱的流水。 .gt(ShopInfo::getAmount, BigDecimal.ZERO)
.eq(ShopInfo::getId, shopId)
// shopInfo 查余额>0 );
// 循环 shopId 查询 如果金额不足 终止 if (CollUtil.isEmpty(shopInfos)) {
// log.info("分销延时分账 无符合条件的店铺,无需处理分账");
LocalDateTime localDateTime = DateUtil.date().toLocalDateTime(); return;
distributionFlowService.list(new QueryWrapper() }
.eq(MkDistributionFlow::getStatus, TableValueConstant.DistributionFlow.Status.PENDING.getCode()).le(MkDistributionFlow::getDeliverTime, localDateTime)).forEach(item -> { for (ShopInfo shopInfo : shopInfos) {
FunUtils.safeRunVoid(() -> { boolean breakCurrentShopFlow = false;
log.info("开始处理延时分账, id: {}, orderNo: {}, 类型: {}", item.getId(), item.getOrderNo(), item.getType()); List<MkDistributionFlow> flowList = distributionFlowService.list(new QueryWrapper()
.eq(MkDistributionFlow::getShopId, shopInfo.getId())
.eq(MkDistributionFlow::getStatus, TableValueConstant.DistributionFlow.Status.PENDING.getCode())
.le(MkDistributionFlow::getDeliverTime, now)
.orderBy(MkDistributionFlow::getId, true)
);
for (MkDistributionFlow item : flowList) {
if (breakCurrentShopFlow) {
break;
}
try {
log.info("分销延时分账, id: {}, orderNo: {}, 类型: {}", item.getId(), item.getOrderNo(), item.getType());
OrderInfo orderInfo = orderInfoService.getOne(new QueryWrapper().eq(OrderInfo::getOrderNo, item.getOrderNo())); OrderInfo orderInfo = orderInfoService.getOne(new QueryWrapper().eq(OrderInfo::getOrderNo, item.getOrderNo()));
if (orderInfo == null) { if (orderInfo == null) {
item.setStatus(TableValueConstant.DistributionFlow.Status.FAIL.getCode()); item.setStatus(TableValueConstant.DistributionFlow.Status.FAIL.getCode());
distributionFlowService.updateById(item); distributionFlowService.updateById(item);
log.warn("订单不存在, 订单号: {}", item.getOrderNo()); log.warn("分销延时分账。订单不存在, 订单号: {}", item.getOrderNo());
return; continue;
} }
if (list.contains(orderInfo.getStatus())) { if (list.contains(orderInfo.getStatus())) {
log.warn("订单已退款, 订单号: {}", item.getOrderNo()); log.warn("分销延时分账。订单已退款, 订单号: {}", item.getOrderNo());
distributionUserService.refund(orderInfo.getId(), orderInfo.getOrderNo()); distributionUserService.refund(orderInfo.getId(), orderInfo.getOrderNo());
} else { } else {
item.setStatus(TableValueConstant.DistributionFlow.Status.SUCCESS.getCode()); item.setStatus(TableValueConstant.DistributionFlow.Status.SUCCESS.getCode());
ShopUser shopUser = shopUserService.getById(item.getDistributionUserId()); distributionUserService.distributionUserAmount(item, orderInfo);
distributionUserService.updateShopInfoAmount(orderInfo.getShopId(), item.getRewardAmount().negate(), orderInfo.getId(), TableValueConstant.DistributionAmountFlow.Type.SUB, "分销扣减"); }
distributionUserService.updateIncome(item.getRewardAmount().negate(), item.getRewardAmount(), BigDecimal.ZERO, } catch (CzgException e) {
item.getDistributionUserId(), shopUser.getUserId(), item.getShopUserId(), item.getShopId(), item.getLevel()); log.error("店铺{}:{}分销延时分账异常:{}", shopInfo.getId(), shopInfo.getShopName(), e.getMessage());
distributionFlowService.updateById(item); breakCurrentShopFlow = true;
} catch (Exception e) {
log.error("店铺{}:{}分销延时分账异常", shopInfo.getId(), shopInfo.getShopName(), e);
breakCurrentShopFlow = true;
}
}
} }
});
});
} }
} }

View File

@@ -50,6 +50,8 @@ public class NotifyController {
@Resource @Resource
private MkShopConsumeDiscountRecordService consumeDiscountRecordService; private MkShopConsumeDiscountRecordService consumeDiscountRecordService;
//新客立减清除数据 测试用
@RequestMapping("clear") @RequestMapping("clear")
public String clear(@RequestParam Integer shopId) { public String clear(@RequestParam Integer shopId) {
consumeDiscountRecordService.remove(new QueryWrapper().eq(MkShopConsumeDiscountRecord::getShopId, shopId)); consumeDiscountRecordService.remove(new QueryWrapper().eq(MkShopConsumeDiscountRecord::getShopId, shopId));

View File

@@ -21,7 +21,7 @@ public class StatisticTaskController {
private StatisticTask statisticTask; private StatisticTask statisticTask;
/** /**
* 基础统计 * 基础统计 预留重置统计
* *
* @param date 日期yyyy-MM-dd * @param date 日期yyyy-MM-dd
*/ */

View File

@@ -3,13 +3,16 @@ package com.czg.controller.admin;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.czg.EntryManager; import com.czg.EntryManager;
import com.czg.annotation.Debounce; import com.czg.annotation.Debounce;
import com.czg.config.RabbitPublisher;
import com.czg.dto.req.AggregateMerchantDto; import com.czg.dto.req.AggregateMerchantDto;
import com.czg.dto.resp.BankBranchDto; import com.czg.dto.resp.BankBranchDto;
import com.czg.service.order.dto.AggregateMerchantVO; import com.czg.order.entity.ShopDirectMerchant;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.sa.StpKit; import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.dto.MerchantQueryDTO;
import com.czg.service.order.service.ShopDirectMerchantService;
import com.czg.task.EntryManagerTask; import com.czg.task.EntryManagerTask;
import com.mybatisflex.core.paginate.Page;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -31,6 +34,9 @@ public class EntryManagerController {
@Resource @Resource
private EntryManagerTask entryManagerTask; private EntryManagerTask entryManagerTask;
@Resource
private RabbitPublisher rabbitPublisher;
/** /**
* ocr识别填充 * ocr识别填充
* 阿里 ocr识别图片 * 阿里 ocr识别图片
@@ -62,13 +68,27 @@ public class EntryManagerController {
return CzgResult.success(EntryManager.queryBankBranchList(province, city, instId)); return CzgResult.success(EntryManager.queryBankBranchList(province, city, instId));
} }
@GetMapping("test")
public CzgResult<Void> test(String shopId, String licenceNo) {
rabbitPublisher.sendEntryManagerMsg(shopId + ":" + licenceNo);
return CzgResult.success();
}
/**
* 获取进件列表
*/
@GetMapping("list")
public CzgResult<Page<ShopDirectMerchant>> getEntryList(MerchantQueryDTO queryParam) {
return CzgResult.success(shopDirectMerchantService.getEntryList(queryParam));
}
/** /**
* 获取进件信息 * 获取进件信息
*/ */
@GetMapping @GetMapping
public CzgResult<AggregateMerchantVO> getEntry(Long shopId) { public CzgResult<AggregateMerchantVO> getEntry(Long shopId, String licenceNo) {
return CzgResult.success(shopDirectMerchantService.getEntry(shopId)); return CzgResult.success(shopDirectMerchantService.getEntry(shopId, licenceNo));
} }
/** /**
@@ -78,8 +98,8 @@ public class EntryManagerController {
*/ */
@GetMapping("queryEntry") @GetMapping("queryEntry")
@Debounce(value = "#shopId", interval = 1000 * 60 * 3) @Debounce(value = "#shopId", interval = 1000 * 60 * 3)
public CzgResult<Boolean> queryEntry(Long shopId) { public CzgResult<Boolean> queryEntry(Long shopId, String licenceNo) {
entryManagerTask.entryManager(shopId); entryManagerTask.entryManager(shopId, licenceNo);
return CzgResult.success(); return CzgResult.success();
} }

View File

@@ -4,14 +4,15 @@ import com.czg.annotation.Debounce;
import com.czg.order.dto.MkDistributionPayDTO; import com.czg.order.dto.MkDistributionPayDTO;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.service.order.service.DistributionPayService; import com.czg.service.order.service.DistributionPayService;
import com.czg.system.service.SysParamsService;
import com.czg.utils.AssertUtil; import com.czg.utils.AssertUtil;
import com.czg.utils.ServletUtil; import com.czg.utils.ServletUtil;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map; import java.util.Map;
@@ -26,16 +27,6 @@ import java.util.Map;
public class DistributionPayController { public class DistributionPayController {
@Resource @Resource
private DistributionPayService payService; private DistributionPayService payService;
@DubboReference
private SysParamsService paramsService;
// @PostMapping("/cashPay")
// @Debounce(value = "#payParam.checkOrderPay.orderId")
// public CzgResult<Object> cashPayOrder(@RequestHeader Long shopId, @Validated @RequestBody MkDistributionPayDTO payParam) {
// payParam.setShopId(shopId);
// return payService.cashPayOrder(payParam);
// }
/** /**
* 小程序支付 * 小程序支付
@@ -59,26 +50,4 @@ public class DistributionPayController {
AssertUtil.isBlank(payParam.getCode(), "微信code不为空"); AssertUtil.isBlank(payParam.getCode(), "微信code不为空");
return CzgResult.success(payService.mchRecharge(ServletUtil.getClientIP(request), payParam)); return CzgResult.success(payService.mchRecharge(ServletUtil.getClientIP(request), payParam));
} }
//
// /**
// * 正扫
// */
// @PostMapping("/scanPay")
// @Debounce(value = "#payParam.checkOrderPay.orderId")
// public CzgResult<Map<String, Object>> scanPayOrder(@RequestHeader Long shopId, HttpServletRequest request, @Validated @RequestBody MkDistributionPayDTO payParam) {
// payParam.setShopId(shopId);
// return payService.scanPayOrder(ServletUtil.getClientIP(request), payParam);
// }
//
// /**
// * 反扫
// * authCode 必填 扫描码
// */
// @PostMapping("/microPay")
// @Debounce(value = "#payParam.checkOrderPay.orderId")
// public CzgResult<Map<String, Object>> microPayOrder(@RequestHeader Long shopId, @Validated @RequestBody MkDistributionPayDTO payParam) {
// payParam.setShopId(shopId);
// return payService.microPayOrder(payParam);
// }
} }

View File

@@ -1,6 +1,7 @@
package com.czg.mq; package com.czg.mq;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.czg.EntryManager; import com.czg.EntryManager;
import com.czg.PayCst; import com.czg.PayCst;
import com.czg.config.RabbitConstants; import com.czg.config.RabbitConstants;
@@ -10,6 +11,7 @@ import com.czg.order.entity.ShopDirectMerchant;
import com.czg.service.RedisService; import com.czg.service.RedisService;
import com.czg.service.order.dto.AggregateMerchantVO; import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.service.ShopDirectMerchantService; import com.czg.service.order.service.ShopDirectMerchantService;
import com.mybatisflex.core.query.QueryWrapper;
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Channel;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -48,20 +50,33 @@ public class EntryManagerMqListener {
) )
@RabbitHandler @RabbitHandler
public void handle(Message message, Channel channel, String msg) throws IOException { public void handle(Message message, Channel channel, String msg) throws IOException {
String messageId = message.getMessageProperties().getMessageId(); log.info("进件1MQ对接开始 店铺标识:{}", msg);
if (hasMessageId(messageId)) { long deliveryTag = message.getMessageProperties().getDeliveryTag();
if (StrUtil.isBlank(msg)) {
channel.basicNack(deliveryTag, false, false);
return;
}
String[] split = msg.split(":");
if (split.length != 2) {
log.error("进件MQ对接参数异常 店铺标识:{}", msg);
channel.basicNack(deliveryTag, false, false);
return;
}
Long shopId = Long.valueOf(split[0]);
if (shopId == null) {
channel.basicNack(deliveryTag, false, false);
return;
}
if (hasMessageId(msg)) {
return; return;
} }
try { try {
Long shopId = Long.valueOf(msg);
// 将唯一标识添加到日志上下文 // 将唯一标识添加到日志上下文
ThreadContext.put("traceId", messageId); ThreadContext.put("traceId", String.valueOf(shopId));
log.info("进件2MQ对接开始shopId:{}", msg);
// 安全转换shopId // 安全转换shopId
if (shopId == null) { AggregateMerchantVO entry = shopDirectMerchantService.getEntry(shopId, split[1]);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false); log.info("进件3MQ对接开始shopId:{}", msg);
return;
}
AggregateMerchantVO entry = shopDirectMerchantService.getEntry(Long.valueOf(msg));
if (entry != null) { if (entry != null) {
EntryManager.uploadParamImage(entry); EntryManager.uploadParamImage(entry);
List<String> platform = new ArrayList<>(); List<String> platform = new ArrayList<>();
@@ -73,30 +88,33 @@ public class EntryManagerMqListener {
} }
EntryRespDto resp = EntryManager.entryMerchant(entry, platform.toArray(new String[0])); EntryRespDto resp = EntryManager.entryMerchant(entry, platform.toArray(new String[0]));
ShopDirectMerchant merchant = new ShopDirectMerchant(); ShopDirectMerchant merchant = new ShopDirectMerchant();
merchant.setShopId(entry.getShopId()); merchant.setMerchantBaseInfo(JSONObject.toJSONString(entry.getMerchantBaseInfo()));
merchant.setLegalPersonInfo(JSONObject.toJSONString(entry.getLegalPersonInfo()));
merchant.setBusinessLicenceInfo(JSONObject.toJSONString(entry.getBusinessLicenceInfo()));
merchant.setStoreInfo(JSONObject.toJSONString(entry.getStoreInfo()));
merchant.setSettlementInfo(JSONObject.toJSONString(entry.getSettlementInfo()));
merchant.setWechatApplyId(resp.getWechatApplyId()); merchant.setWechatApplyId(resp.getWechatApplyId());
merchant.setWechatStatus(resp.getWechatStatus()); merchant.setWechatStatus(resp.getWechatStatus());
merchant.setWechatErrorMsg(resp.getWechatErrorMsg()); merchant.setWechatErrorMsg(resp.getWechatErrorMsg());
merchant.setAlipayOrderId(resp.getAlipayOrderId()); merchant.setAlipayOrderId(resp.getAlipayOrderId());
merchant.setAlipayStatus(resp.getAlipayStatus()); merchant.setAlipayStatus(resp.getAlipayStatus());
merchant.setAlipayAuthInfo(resp.getAlipayAuthInfo());
merchant.setAlipayErrorMsg(resp.getAlipayErrorMsg()); merchant.setAlipayErrorMsg(resp.getAlipayErrorMsg());
shopDirectMerchantService.updateById(merchant); shopDirectMerchantService.update(merchant, new QueryWrapper().eq(ShopDirectMerchant::getShopId, shopId).eq(ShopDirectMerchant::getLicenceNo, split[1]));
} }
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); channel.basicAck(deliveryTag, false);
} catch (Exception e) { } catch (Exception e) {
log.error("进件MQ对接业务异常shopId:{}", msg, e); log.error("进件MQ对接业务异常shopId:{}", msg, e);
ShopDirectMerchant merchant = new ShopDirectMerchant(); ShopDirectMerchant merchant = new ShopDirectMerchant();
merchant.setShopId(Long.valueOf(msg));
merchant.setWechatStatus(PayCst.EntryStatus.REJECTED); merchant.setWechatStatus(PayCst.EntryStatus.REJECTED);
merchant.setAlipayStatus(PayCst.EntryStatus.REJECTED); merchant.setAlipayStatus(PayCst.EntryStatus.REJECTED);
merchant.setErrorMsg("系统错误,请联系管理员后重试。"); merchant.setErrorMsg("系统错误,请联系管理员后重试。");
shopDirectMerchantService.updateById(merchant); shopDirectMerchantService.update(merchant, new QueryWrapper().eq(ShopDirectMerchant::getShopId, shopId).eq(ShopDirectMerchant::getLicenceNo, split[1]));
channel.basicNack(deliveryTag, false, false);
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
} finally { } finally {
delMessageId(messageId); delMessageId(msg);
// 清除日志上下文信息 // 清除日志上下文信息
ThreadContext.remove("messageId"); ThreadContext.remove("messageId");
} }

View File

@@ -9,7 +9,7 @@ import com.czg.order.entity.MqLog;
import com.czg.order.service.MqLogService; import com.czg.order.service.MqLogService;
import com.czg.order.service.OrderInfoCustomService; import com.czg.order.service.OrderInfoCustomService;
import com.czg.order.service.OrderInfoRpcService; import com.czg.order.service.OrderInfoRpcService;
import com.czg.service.order.utils.FunUtil; import com.czg.service.RedisService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.annotation.RabbitListener;
@@ -31,7 +31,7 @@ public class OrderMqListener {
@Resource @Resource
private OrderInfoCustomService orderInfoCustomService; private OrderInfoCustomService orderInfoCustomService;
@Resource @Resource
private FunUtil funUtil; private RedisService redisService;
/** /**
* 订单上菜 * 订单上菜
@@ -44,7 +44,7 @@ public class OrderMqListener {
info = info.replace("UP_ORDER_DETAIL:", ""); info = info.replace("UP_ORDER_DETAIL:", "");
log.info("接收到修改菜品状态mq, info: {}", info); log.info("接收到修改菜品状态mq, info: {}", info);
String finalInfo = info; String finalInfo = info;
funUtil.debounce("UP_ORDER_DETAIL:" + info, 5, () -> { redisService.debounce("UP_ORDER_DETAIL:" + info, 5, () -> {
orderInfoCustomService.updateOrderDetailStatus(Long.valueOf(finalInfo)); orderInfoCustomService.updateOrderDetailStatus(Long.valueOf(finalInfo));
}); });

View File

@@ -4,14 +4,9 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.czg.EntryManager; import com.czg.EntryManager;
import com.czg.PayCst; import com.czg.PayCst;
import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopInfoService; import com.czg.account.service.ShopInfoService;
import com.czg.dto.resp.QueryStatusResp; import com.czg.dto.resp.QueryStatusResp;
import com.czg.order.entity.ShopDirectMerchant; import com.czg.order.entity.ShopDirectMerchant;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.service.ShopTableOrderStatisticService;
import com.czg.service.RedisService;
import com.czg.service.order.service.ShopDirectMerchantService; import com.czg.service.order.service.ShopDirectMerchantService;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@@ -20,8 +15,6 @@ import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@@ -42,16 +35,17 @@ public class EntryManagerTask {
public void run() { public void run() {
log.info("进件查询,定时任务执行"); log.info("进件查询,定时任务执行");
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
entryManager(null); entryManager(null, null);
log.info("进件查询,定时任务执行完毕,耗时:{}ms", start - System.currentTimeMillis()); log.info("进件查询,定时任务执行完毕,耗时:{}ms", start - System.currentTimeMillis());
} }
/** /**
* 查询状态为待处理、待签约、待审核的进件 * 查询状态为待处理、待签约、待审核的进件
*/ */
public void entryManager(Long shopId) { public void entryManager(Long shopId, String licenceNo) {
List<ShopDirectMerchant> list = shopDirectMerchantService.list(QueryWrapper.create() List<ShopDirectMerchant> list = shopDirectMerchantService.list(QueryWrapper.create()
.eq(ShopDirectMerchant::getShopId, shopId) .eq(ShopDirectMerchant::getShopId, shopId)
.eq(ShopDirectMerchant::getLicenceNo, licenceNo)
.in(ShopDirectMerchant::getWechatStatus, PayCst.EntryStatus.NEED_QUERY_LIST) .in(ShopDirectMerchant::getWechatStatus, PayCst.EntryStatus.NEED_QUERY_LIST)
.or(ShopDirectMerchant::getAlipayStatus).in(PayCst.EntryStatus.NEED_QUERY_LIST)); .or(ShopDirectMerchant::getAlipayStatus).in(PayCst.EntryStatus.NEED_QUERY_LIST));
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
@@ -80,7 +74,7 @@ public class EntryManagerTask {
} }
shopDirectMerchantService.updateById(shopDirectMerchant); shopDirectMerchantService.updateById(shopDirectMerchant);
if (StrUtil.isNotBlank(wechatMerchantId) || StrUtil.isNotBlank(alipayMerchantId)) { if (StrUtil.isNotBlank(wechatMerchantId) || StrUtil.isNotBlank(alipayMerchantId)) {
shopInfoService.editEntry(shopDirectMerchant.getShopId(), wechatMerchantId, alipayMerchantId); shopInfoService.editEntry(shopDirectMerchant.getShopId(), wechatMerchantId, alipayMerchantId, shopDirectMerchant.getAlipayAuthInfo());
} }
} }
} }

View File

@@ -40,7 +40,7 @@ public class OTimeTask {
@Resource @Resource
private CashierCartService cartService; private CashierCartService cartService;
@Resource @Resource
private OrderPaymentService orderPaymentService; private OrderPaymentService paymentService;
@Resource @Resource
private GbOrderService gbOrderService; private GbOrderService gbOrderService;
@Resource @Resource
@@ -118,7 +118,7 @@ public class OTimeTask {
LocalDateTime tenMinutesAgo = LocalDateTime.now().minusMinutes(10); LocalDateTime tenMinutesAgo = LocalDateTime.now().minusMinutes(10);
LocalDateTime thirdDayAgo = LocalDateTime.now().minusDays(3); LocalDateTime thirdDayAgo = LocalDateTime.now().minusDays(3);
List<OrderPayment> list = orderPaymentService.list(QueryWrapper.create() List<OrderPayment> list = paymentService.list(QueryWrapper.create()
.gt(OrderPayment::getUpdateTime, thirdDayAgo) .gt(OrderPayment::getUpdateTime, thirdDayAgo)
.lt(OrderPayment::getUpdateTime, tenMinutesAgo) .lt(OrderPayment::getUpdateTime, tenMinutesAgo)
.in(OrderPayment::getSourceType, ware) .in(OrderPayment::getSourceType, ware)

View File

@@ -25,7 +25,23 @@ spring:
port: 5672 port: 5672
username: chaozg username: chaozg
password: chaozg123 password: chaozg123
# 关键优化解决MissedHeartbeatException 心跳超时问题
connection-timeout: 10000 # 连接超时时间10秒避免连接建立过慢
requested-heartbeat: 30 # 心跳间隔调整为30秒原60秒过长降低超时概率过短易误触发
# 自动重连配置Spring AMQP 自带,关键兜底)
publisher-returns: true
template:
retry:
enabled: true # 开启消息发送重试
max-attempts: 3 # 最大重试次数
initial-interval: 3000 # 首次重试间隔2秒
multiplier: 1.5 # 重试间隔倍增因子
listener:
simple:
retry:
enabled: true # 开启消费者重试
max-attempts: 3 # 消费者最大重试次数
acknowledge-mode: auto # 确认模式可根据业务改为manual
dubbo: dubbo:
application: application:
name: order-server name: order-server

View File

@@ -54,7 +54,7 @@ public class FilteredNacosRegistry extends NacosRegistry {
public void register(URL url) { public void register(URL url) {
// 1. 获取原始注册的方法列表 // 1. 获取原始注册的方法列表
String originalMethods = url.getParameter("methods"); String originalMethods = url.getParameter("methods");
log.info("【过滤提示】服务 {} 注册方法:{}", url.getServiceInterface(), originalMethods); // log.info("【过滤提示】服务 {} 注册方法:{}", url.getServiceInterface(), originalMethods);
if (originalMethods != null && !originalMethods.isEmpty()) { if (originalMethods != null && !originalMethods.isEmpty()) {
// 2. 过滤黑名单中的方法名 // 2. 过滤黑名单中的方法名
List<String> filteredMethods = Arrays.stream(originalMethods.split(",")) List<String> filteredMethods = Arrays.stream(originalMethods.split(","))
@@ -67,12 +67,12 @@ public class FilteredNacosRegistry extends NacosRegistry {
// 3. 处理过滤后的结果 // 3. 处理过滤后的结果
if (filteredMethods.isEmpty()) { if (filteredMethods.isEmpty()) {
// 若所有方法都被过滤,直接终止注册(可选:根据业务决定是否保留服务注册) // 若所有方法都被过滤,直接终止注册(可选:根据业务决定是否保留服务注册)
log.info("【过滤提示】服务 {} 所有方法均被过滤,终止注册", url.getServiceInterface()); // log.info("【过滤提示】服务 {} 所有方法均被过滤,终止注册", url.getServiceInterface());
return; return;
} else { } else {
// 替换 URL 中的 methods 参数为过滤后的列表 // 替换 URL 中的 methods 参数为过滤后的列表
url = url.addParameter("methods", String.join(",", filteredMethods)); url = url.addParameter("methods", String.join(",", filteredMethods));
log.info("【过滤提示】服务 {} 注册方法:{}", url.getServiceInterface(), filteredMethods); // log.info("【过滤提示】服务 {} 注册方法:{}", url.getServiceInterface(), filteredMethods);
} }
} }

View File

@@ -1,5 +1,6 @@
package com.czg.config; package com.czg.config;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.audit.AuditManager; import com.mybatisflex.core.audit.AuditManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -17,8 +18,24 @@ public class MybatisFlexConfig {
//设置 SQL 审计收集器 //设置 SQL 审计收集器
AuditManager.setMessageCollector(auditMessage -> AuditManager.setMessageCollector(auditMessage ->
log.info("[sql] time: {}, size: {}, sql:\n{}", log.info("[sql] time: {}, size: {}, sql:\n{}",
auditMessage.getElapsedTime(), auditMessage.getQueryCount(), auditMessage.getFullSql()) auditMessage.getElapsedTime(), auditMessage.getQueryCount(), compressSql(auditMessage.getFullSql())));
);
} }
/**
* 精简SQL去除多余换行、制表符、连续空格保留语法必需空格
*
* @param originalSql 原始带换行/空格的SQL
* @return 精简后的SQL
*/
public static String compressSql(String originalSql) {
if (StrUtil.isBlank(originalSql)) {
return "";
}
// 1. 替换所有换行、制表符为单个空格
String tempSql = originalSql.replaceAll("\\r\\n|\\r|\\n|\\t", " ");
// 2. 替换多个连续空格为单个空格
tempSql = tempSql.replaceAll("\\s+", " ");
// 3. 去除首尾空格
return tempSql.trim();
}
} }

View File

@@ -131,7 +131,7 @@ public class RabbitPublisher {
} }
/** /**
* 订单商品状态消息 * 进件
*/ */
public void sendEntryManagerMsg(String shopId) { public void sendEntryManagerMsg(String shopId) {
sendMsg(RabbitConstants.Queue.SHOP_ENTRY_MANAGER, shopId); sendMsg(RabbitConstants.Queue.SHOP_ENTRY_MANAGER, shopId);

View File

@@ -6,14 +6,15 @@ import jakarta.annotation.Resource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Collection; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/** /**
* @author GYJoker * @author GYJoker
@@ -650,4 +651,106 @@ public class RedisService {
} }
return JSON.parseArray(jsonStr, type); return JSON.parseArray(jsonStr, type);
} }
public static int retryCount = 5;
/**
* 执行任务并保证锁唯一
*
* @param supplier 业务逻辑
* @param lockKey Redis锁的Key
* @return 业务逻辑返回值
*/
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
try {
// 尝试获取锁,超时时间 5 秒,防止死锁
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
int count = 0;
// 初始等待 10ms
int retryDelay = 10;
while (!lock) {
// 最多重试 10 次,大约 10 秒
if (count++ > 50) {
throw new RuntimeException("系统繁忙, 稍后再试");
}
Thread.sleep(retryDelay);
// 指数退避,最大等待 200ms
retryDelay = Math.min(retryDelay * 2, 200);
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
}
// 执行任务
return supplier.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("线程被中断", e);
} catch (Exception e) {
log.error("执行出错:{}", e.getMessage(), e);
throw e;
} finally {
// 释放锁(使用 Lua 脚本确保原子性)
unlock(lockKey, lockValue);
}
}
/**
* 使用 Lua 脚本确保释放锁的原子性
*
* @param lockKey 锁的 Key
* @param lockValue 当前线程的锁值
*/
private void unlock(String lockKey, String lockValue) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey), lockValue);
}
public static <T, R> R runFunAndRetry(
Supplier<R> function,
Function<R, Boolean> check, Consumer<R> errFun) {
R result = function.get();
boolean flag = check.apply(result);
while (flag && retryCount-- > 0) {
result = function.get();
flag = check.apply(result);
}
if (flag) {
errFun.accept(result);
}
return result;
}
/**
* 防抖函数:在指定秒数内相同 Key 的任务只会执行一次
*
* @param key 防抖使用的 Redis Key
* @param seconds 防抖时间(秒)
* @param task 要执行的业务逻辑
* @return true 执行了任务false 在防抖期内被拦截
*/
public boolean debounce(String key, long seconds, Runnable task) {
try {
Boolean success = redisTemplate.opsForValue().setIfAbsent(
key, "1", seconds, TimeUnit.SECONDS
);
if (Boolean.TRUE.equals(success)) {
task.run();
return true;
}
return false;
} catch (Exception e) {
log.error("防抖函数执行失败 key={} err={}", key, e.getMessage(), e);
return false;
}
}
} }

View File

@@ -285,4 +285,13 @@ public class ShopInfoEditDTO {
*/ */
private Integer isCountStick; private Integer isCountStick;
/**
* 企业id
*/
private String weworkId;
/**
* 企业接入链接
*/
private String weworkUrl;
} }

View File

@@ -0,0 +1,69 @@
package com.czg.account.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.io.Serial;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 悬浮窗配置 实体类。
*
* @author ww
* @since 2025-12-29
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("tb_quick_menu")
public class QuickMenu implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id(keyType = KeyType.Auto)
private Integer id;
/**
* 店铺Id
*/
private Long shopId;
/**
* 菜单图标
*/
private String url;
/**
* 菜单Id
*/
@NotNull(message = "关联菜单不能为空")
private Long menuId;
/**
* 排序
*/
private Integer sort;
/**
* 状态 1-启用 0-禁用
*/
private Integer status;
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
@Column(onInsertValue = "now()", onUpdateValue = "now()")
private LocalDateTime updateTime;
}

View File

@@ -135,5 +135,13 @@ public class ShopConfig implements Serializable {
private String dingAppKey; private String dingAppKey;
private String dingAppSecret; private String dingAppSecret;
/**
* 企业id
*/
private String weworkId;
/**
* 企业接入链接
*/
private String weworkUrl;
} }

View File

@@ -150,6 +150,10 @@ public class ShopInfo implements Serializable {
* 支付宝商户id * 支付宝商户id
*/ */
private String alipayMerchantId; private String alipayMerchantId;
/**
* 支付宝授权信息
*/
private String alipayAuthInfo;
/** /**
* 到期时间 * 到期时间
@@ -371,4 +375,15 @@ public class ShopInfo implements Serializable {
*/ */
private BigDecimal amount; private BigDecimal amount;
/**
* 企业id
*/
@Column(ignore = true)
private String weworkId;
/**
* 企业接入链接
*/
@Column(ignore = true)
private String weworkUrl;
} }

View File

@@ -0,0 +1,14 @@
package com.czg.account.service;
import com.mybatisflex.core.service.IService;
import com.czg.account.entity.QuickMenu;
/**
* 悬浮窗配置 服务层。
*
* @author ww
* @since 2025-12-29
*/
public interface QuickMenuService extends IService<QuickMenu> {
}

View File

@@ -23,6 +23,7 @@ public interface ShopInfoService extends IService<ShopInfo> {
/** /**
* 检测开关 * 检测开关
*
* @param shopId 店铺id * @param shopId 店铺id
* @param switchType ShopInfo的某列 开关 目前只支持Integer类型字段 * @param switchType ShopInfo的某列 开关 目前只支持Integer类型字段
* @return true:开启 false:关闭 * @return true:开启 false:关闭
@@ -40,7 +41,7 @@ public interface ShopInfoService extends IService<ShopInfo> {
/** /**
* 进件结果保存 * 进件结果保存
*/ */
Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId); Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId, String alipayAuthInfo);
ShopDetailDTO detail(Long id) throws CzgException; ShopDetailDTO detail(Long id) throws CzgException;

View File

@@ -7,10 +7,12 @@ import com.czg.exception.CzgException;
import com.czg.market.dto.MkDistributionUserDTO; import com.czg.market.dto.MkDistributionUserDTO;
import com.czg.market.dto.MkDistributionWithdrawFlowDTO; import com.czg.market.dto.MkDistributionWithdrawFlowDTO;
import com.czg.market.entity.MkDistributionConfig; import com.czg.market.entity.MkDistributionConfig;
import com.czg.market.entity.MkDistributionFlow;
import com.czg.market.entity.MkDistributionUser; import com.czg.market.entity.MkDistributionUser;
import com.czg.market.vo.DistributionCenterShopVO; import com.czg.market.vo.DistributionCenterShopVO;
import com.czg.market.vo.InviteUserVO; import com.czg.market.vo.InviteUserVO;
import com.czg.order.dto.MkDistributionPayDTO; import com.czg.order.dto.MkDistributionPayDTO;
import com.czg.order.entity.OrderInfo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService; import com.mybatisflex.core.service.IService;
@@ -123,6 +125,8 @@ public interface MkDistributionUserService extends IService<MkDistributionUser>
void refund(Long orderId, String orderNo); void refund(Long orderId, String orderNo);
void distributionUserAmount(MkDistributionFlow item, OrderInfo orderInfo);
/** /**
* 发放分销奖励 * 发放分销奖励
* *

View File

@@ -1,267 +0,0 @@
package com.czg.order.dto;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.alibaba.fastjson2.annotation.JSONField;
import java.io.Serial;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 订单表 实体类。
*
* @author ww
* @since 2025-02-13
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderInfoDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long id;
/**
* 订单编号
* pc 收银机客户端 PC+雪花ID
* wechat 微信小程序 WX+雪花ID
* alipay 支付宝小程序 ALI+雪花ID
* admin-pc PC管理端 WEB+雪花ID
* admin-app APP管理端 APP+雪花ID
*/
private String orderNo;
/**
* 店铺Id
*/
private Long shopId;
/**
* 用户Id user_info表的id
*/
private Long userId;
/**
* 退单金额
*/
private BigDecimal refundAmount;
/**
* 订单原金额 不含折扣价格
*/
private BigDecimal originAmount;
/**
* 抹零金额 减免多少钱
*/
private BigDecimal roundAmount;
/**
* 优惠总金额
*/
private BigDecimal discountAllAmount;
/**
* 订单金额 (扣除各类折扣)
*/
private BigDecimal orderAmount;
/**
* 实际支付金额
*/
private BigDecimal payAmount;
/**
* 积分抵扣金额
*/
private BigDecimal pointsDiscountAmount;
/**
* 使用的积分数量
*/
private Integer pointsNum;
/**
* 商品优惠券抵扣金额
*/
private BigDecimal productCouponDiscountAmount;
/**
* 用户使用的卡券
*/
private String couponInfoList;
/**
* 满减活动抵扣金额
*/
private BigDecimal discountActAmount;
/**
* 折扣金额
*/
private BigDecimal discountAmount;
// /**
// * 折扣比例
// */
// private BigDecimal discountRatio;
/**
* 打包费
*/
private BigDecimal packFee;
/**
* 台桌Id
*/
private String tableCode;
/**
* 台桌名称
*/
private String tableName;
/**
* 订单类型-
* cash收银(除小程序以外 都属于收银)
* miniapp小程序
*/
private String orderType;
/**
* 平台类型 pc 收银机客户端 wechat 微信小程序 alipay 支付宝小程序 admin-pc PC管理端 admin-app APP管理端
*/
private String platformType;
/**
* 用餐模式 堂食 dine-in 外带 take-out 外卖 take-away
*/
private String dineMode;
/**
* 支付模式:
* 后付费 after-pay
* 先付费 before-pay
* 无桌码 no-table
*/
private String payMode;
/**
* 支付类型
* 主扫 main-scan
* 被扫 back-scan
* 微信小程序 wechat-mini
* 支付宝小程序 alipay-mini
* 会员支付 vip-pay
* 现金支付 cash-pay
*/
private String payType;
/**
* 状态: unpaid-待支付;in-production 制作中;wait_out 待取餐;;done-订单完成;refunding-申请退单;refund-退单;part_refund 部分退单;cancelled-取消订单
*/
private String status;
/**
* 折扣信息 json
*/
private String discountInfo;
/**
* 限时折扣信息 json
*/
private String limitRate;
/**
* 是否支持退款1支持退单 0不支持退单
*/
private Integer refundAble;
/**
* 支付时间
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime paidTime;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
/**
* 支付订单号
* tb_order_payment.id
* tb_shop_user_flow.id
*/
private Long payOrderId;
/**
* 交易日期
*/
private String tradeDay;
/**
* 备注
*/
private String remark;
/**
* 取餐码
*/
private String takeCode;
/**
* 员工id
*/
private Long staffId;
/**
* 当前订单下单次数
*/
private Integer placeNum;
/**
* 用餐人数
*/
private Integer seatNum;
/**
* 餐位费
*/
private BigDecimal seatAmount;
/**
* 退款备注
*/
private String refundRemark;
/**
* 是否使用了霸王餐
*/
private Integer isFreeDine;
/**
* 是否等叫 0 否 1 等叫
*/
private Integer isWaitCall;
/**
* 挂账人id
*/
private Long creditBuyerId;
/**
* 是否回收站 0-否1回收站
*/
private Integer isDel;
private String failMsg;
}

View File

@@ -1,6 +1,8 @@
package com.czg.order.entity; package com.czg.order.entity;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.czg.order.dto.LimitRateDTO; import com.czg.order.dto.LimitRateDTO;
import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.Id;
@@ -298,8 +300,10 @@ public class OrderInfo implements Serializable {
private Integer isDel; private Integer isDel;
private String failMsg; private String failMsg;
/**
* 打印状态 Json格式
*/
private String printStatus;
public String getRefundRemark() { public String getRefundRemark() {
@@ -342,4 +346,41 @@ public class OrderInfo implements Serializable {
// 如果需要加上抹零金额,可以取消下面这行注释 // 如果需要加上抹零金额,可以取消下面这行注释
// .add(this.getRoundAmount() != null ? this.getRoundAmount() : BigDecimal.ZERO); // .add(this.getRoundAmount() != null ? this.getRoundAmount() : BigDecimal.ZERO);
} }
private JSONArray getPrintStatusAsArray() {
if (StrUtil.isBlank(printStatus)) {
return new JSONArray();
}
try {
return JSONArray.parseArray(printStatus.trim());
} catch (Exception e) {
return new JSONArray();
}
}
public void upPrintStatus(JSONObject printJson, boolean isPrintSuccess) {
String currentDeviceId = printJson.getString("id");
JSONArray oldPrintStatusArray = getPrintStatusAsArray();
// 3. 初始化新的打印状态JSON数组用于存储处理后的结果
JSONArray newPrintStatusArray = new JSONArray();
// 场景1打印成功 - 移除原有数组中与当前设备ID一致的记录保留其余记录
if (oldPrintStatusArray != null && !oldPrintStatusArray.isEmpty()) {
for (int i = 0; i < oldPrintStatusArray.size(); i++) {
JSONObject deviceObj = oldPrintStatusArray.getJSONObject(i);
String deviceId = deviceObj.getString("id");
// 仅保留非当前设备ID的记录
if (currentDeviceId != null && !currentDeviceId.equals(deviceId)) {
newPrintStatusArray.add(deviceObj);
}
}
}
if (!isPrintSuccess) {
newPrintStatusArray.add(printJson);
}
if (!newPrintStatusArray.isEmpty()) {
printStatus = newPrintStatusArray.toJSONString();
} else {
printStatus = "";
}
}
} }

View File

@@ -35,21 +35,39 @@ public class ShopDirectMerchant implements Serializable {
*/ */
@Id @Id
private Long shopId; private Long shopId;
/**
* 店铺名称
*/
@Column(ignore = true)
private String shopName;
/** /**
* 营业执照编号 * 营业执照编号
*/ */
@Id
private String licenceNo; private String licenceNo;
/** /**
* 支付宝账号 * 支付宝账号
*/ */
private String alipayAccount; private String alipayAccount;
/** /**
* 商户编号(在当前系统唯一) * 商户编号(在当前系统唯一)
*/ */
private String merchantCode; private String merchantCode;
/**
* 【必填】
* 商户类型
* 0: 个体商户;
* 1: 企业商户;
* 3: 小微商户 暂不支持
*/
private String userType;
/**
* 【必填】
* 商户简称--企业、个体必填
*/
private String shortName;
/** /**
* 商户基础信息 * 商户基础信息
*/ */
@@ -106,6 +124,11 @@ public class ShopDirectMerchant implements Serializable {
*/ */
private String alipayStatus; private String alipayStatus;
/**
* 支付宝授信息
*/
private String alipayAuthInfo;
/** /**
* 支付宝进件错误信息 * 支付宝进件错误信息
*/ */

View File

@@ -11,6 +11,8 @@ import com.czg.order.entity.PrintMachineLog;
* @since 2025-03-11 * @since 2025-03-11
*/ */
public interface PrintMachineLogService extends IService<PrintMachineLog> { public interface PrintMachineLogService extends IService<PrintMachineLog> {
void save(PrintMachine config, String bizType, String printContent, Object respJson); void save(Long orderId, PrintMachine config, String bizType, String printContent, String respJson);
void save(PrintMachine config, String bizType, String printContent, String respJson);
} }

View File

@@ -41,6 +41,6 @@ public class OrderDetailSmallVO implements Serializable {
private LocalDateTime startOrderTime; private LocalDateTime startOrderTime;
private LocalDateTime dishOutTime; private LocalDateTime dishOutTime;
private LocalDateTime foodServeTime; private LocalDateTime foodServeTime;
private Integer isTemporary;
} }

View File

@@ -133,6 +133,11 @@ public class OrderInfoVo implements Serializable {
* 备注 * 备注
*/ */
private String remark; private String remark;
/**
* 打印状态 Json格式
* [{"id":"124","name":"111","time":"2025-12-29 11:05:18"},{"id":"111","name":"标签","time":"2025-12-29 11:05:30"}]
*/
private String printStatus;
/** /**
* 是否使用了霸王餐 * 是否使用了霸王餐

View File

@@ -101,6 +101,7 @@ public class EntryManager {
entryRespDto.setAlipayOrderId(respDto.getEntryId()); entryRespDto.setAlipayOrderId(respDto.getEntryId());
entryRespDto.setAlipayStatus(respDto.getStatus()); entryRespDto.setAlipayStatus(respDto.getStatus());
entryRespDto.setAlipayErrorMsg(respDto.getErrorMsg()); entryRespDto.setAlipayErrorMsg(respDto.getErrorMsg());
entryRespDto.setAlipayAuthInfo(respDto.getAlipayAuthInfo());
} }
} }
return entryRespDto; return entryRespDto;
@@ -215,10 +216,10 @@ public class EntryManager {
String image = WechatEntryManager.uploadImage(null, dto.getUrl()); String image = WechatEntryManager.uploadImage(null, dto.getUrl());
dto.setWechatId(image); dto.setWechatId(image);
} }
if (StrUtil.isBlank(dto.getAlipayId())) { // if (StrUtil.isBlank(dto.getAlipayId())) {
String image = AlipayEntryManager.uploadImage(null, dto.getUrl()); // String image = AlipayEntryManager.uploadImage(null, dto.getUrl());
dto.setAlipayId(image); // dto.setAlipayId(image);
} // }
} }
} }
@@ -369,10 +370,10 @@ public class EntryManager {
// uploadParamImage(merchantDto); // uploadParamImage(merchantDto);
verifyEntryParam(merchantDto); verifyEntryParam(merchantDto);
// uploadParamImage(merchantDto); uploadParamImage(merchantDto);
// System.out.println(merchantDto); // System.out.println(merchantDto);
// entryMerchant(merchantDto, PayCst.Platform.WECHAT); entryMerchant(merchantDto, PayCst.Platform.WECHAT);
entryMerchant(merchantDto, PayCst.Platform.ALIPAY); // entryMerchant(merchantDto, PayCst.Platform.ALIPAY);
// entryMerchant(merchantDto, PayCst.Platform.WECHAT, PayCst.Platform.ALIPAY); // entryMerchant(merchantDto, PayCst.Platform.WECHAT, PayCst.Platform.ALIPAY);
} }

View File

@@ -15,6 +15,11 @@ public interface PayCst {
*/ */
String LONG_TERM_DATE = "2099-12-31"; String LONG_TERM_DATE = "2099-12-31";
/**
* 支付宝异常信息获取 key
*/
String ALIPAY_ERROR_MSG_KEY = "message";
/** /**
* 平台 * 平台
*/ */

View File

@@ -0,0 +1,47 @@
package com.czg;
import com.czg.dto.req.PayParamsDto;
import com.czg.third.alipay.AlipayIsvPayManager;
import com.czg.third.wechat.WechatPayManager;
/**
* @author yjjie
* @date 2026/1/9 11:24
*/
public class PayManager {
public static void jsapiPay(PayParamsDto paramsDto) {
paramsDto.verifyParams();
if (PayCst.Platform.WECHAT.equals(paramsDto.getPlatform())) {
WechatPayManager.jsapiPay(null, paramsDto);
} else if (PayCst.Platform.ALIPAY.equals(paramsDto.getPlatform())) {
AlipayIsvPayManager.jsapiPay(null, paramsDto);
}
}
public static void main(String[] args) {
// jsapiPay(new PayParamsDto()
// .setPlatform(PayCst.Platform.ALIPAY)
// .setAppId("2021004145625815")
// .setOpenId("123123123")
// .setOrderNo("1111231231213")
// .setTitle("1213")
// .setMerchantId("123312321")
// .setBody("1213")
// .setAmount(1000L)
// .setPayParams("{\"app_auth_token\": \"ssss\"}")
// .setNotifyUrl("https://www.baidu.com"));
jsapiPay(new PayParamsDto()
.setPlatform(PayCst.Platform.WECHAT)
.setAppId("wxd88fffa983758a30")
.setOpenId("123123123")
.setOrderNo("1111231231213")
.setTitle("1213")
.setMerchantId("1665469114")
.setBody("1213")
.setAmount(1000L)
.setPayParams("{\"app_auth_token\": \"ssss\"}")
.setNotifyUrl("https://www.baidu.com"));
}
}

View File

@@ -0,0 +1,104 @@
package com.czg.dto.req;
import com.alibaba.fastjson2.JSONObject;
import com.czg.PayCst;
import com.czg.exception.CzgException;
import com.czg.third.alipay.dto.AlipayAuthInfoDto;
import com.czg.utils.AssertUtil;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付参数
*
* @author yjjie
* @date 2026/1/9 11:12
*/
@Data
@Accessors(chain = true)
public class PayParamsDto {
/**
* 【必填】
* 支付方式
* {@link com.czg.PayCst.Platform}
*/
private String platform;
/**
* 【必填】
* 订单号
*/
private String orderNo;
/**
* 【必填】
* 金额,单位:分
*/
private Long amount;
/**
* 【必填】
* 订单标题
*/
private String title;
/**
* 【必填】
* 回调地址
*/
private String notifyUrl;
/**
* 【必填】
* appId
*/
private String appId;
/**
* 用户唯一标识
*/
private String openId;
/**
* 【微信必填】
* 商户ID
*/
private String merchantId;
/**
* 【支付宝必填】
* 支付参数
*/
private String payParams;
/**
* 商品描述
*/
private String body;
/**
* 支付宝 授权信息解析,不用传
*/
private AlipayAuthInfoDto alipayAuthInfo;
public void verifyParams() {
AssertUtil.isBlank(platform, "请选择支付方式");
AssertUtil.isBlank(orderNo, "订单号不能为空");
AssertUtil.isBlank(title, "订单标题不能为空");
AssertUtil.isBlank(notifyUrl, "回调地址不能为空");
AssertUtil.isBlank(appId, "appId不能为空");
AssertUtil.isBlank(openId, "用户唯一标识不能为空");
if (PayCst.Platform.WECHAT.equals(platform)) {
AssertUtil.isBlank(merchantId, "商户ID不能为空");
} else if (PayCst.Platform.ALIPAY.equals(platform)) {
AssertUtil.isBlank(payParams, "支付参数不能为空");
alipayAuthInfo = JSONObject.parseObject(payParams, AlipayAuthInfoDto.class);
AssertUtil.isNull(alipayAuthInfo, "支付参数错误");
AssertUtil.isBlank(alipayAuthInfo.getAppAuthToken(), "授权信息错误");
} else {
throw new CzgException("支付平台错误");
}
}
}

View File

@@ -33,6 +33,11 @@ public class EntryRespDto {
*/ */
private String alipayOrderId; private String alipayOrderId;
/**
* 支付宝授信息
*/
private String alipayAuthInfo;
/** /**
* 支付宝状态 * 支付宝状态
* {@link com.czg.PayCst.EntryStatus} * {@link com.czg.PayCst.EntryStatus}

View File

@@ -16,6 +16,11 @@ public class EntryThirdRespDto {
*/ */
private String entryId; private String entryId;
/**
* 支付宝授信息
*/
private String alipayAuthInfo;
/** /**
* 平台 * 平台
*/ */

View File

@@ -347,10 +347,10 @@ public class AlipayEntryManager {
public static void main(String[] args) throws ApiException { public static void main(String[] args) throws ApiException {
// queryBankBranchList("CMB", "陕西省", "西安市"); queryBankBranchList(null, "CMB", "陕西省", "西安市");
// uploadImage(null, "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg"); // uploadImage(null, "https://cashier-oss.oss-cn-beijing.aliyuncs.com/upload/20240312/31476c871c224389aea0ac4e17c964a3.jpg");
test(); // test();
} }
// https://opendocs.alipay.com/solution/0dec7x?pathHash=caec4753 直付通 // https://opendocs.alipay.com/solution/0dec7x?pathHash=caec4753 直付通

View File

@@ -9,6 +9,7 @@ import com.czg.dto.req.*;
import com.czg.dto.resp.EntryThirdRespDto; import com.czg.dto.resp.EntryThirdRespDto;
import com.czg.dto.resp.QueryStatusResp; import com.czg.dto.resp.QueryStatusResp;
import com.czg.exception.CzgException; import com.czg.exception.CzgException;
import com.czg.third.alipay.dto.AlipayAuthInfoDto;
import com.czg.third.alipay.dto.config.AlipayConfigDto; import com.czg.third.alipay.dto.config.AlipayConfigDto;
import com.czg.utils.UploadFileUtil; import com.czg.utils.UploadFileUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -25,9 +26,10 @@ import java.io.IOException;
@Slf4j @Slf4j
public class AlipayIsvEntryManager { public class AlipayIsvEntryManager {
private static final String ERR_MESSAGE_KEY = "message";
public static QueryStatusResp queryMerchantBatchStatus(AlipayConfigDto configDto, String batchNo) { public static QueryStatusResp queryMerchantBatchStatus(AlipayConfigDto configDto, String batchNo) {
if (configDto == null) {
configDto = AlipayConfigDto.getThirdDefaultConfig();
}
QueryStatusResp respDto = new QueryStatusResp() QueryStatusResp respDto = new QueryStatusResp()
.setPlatform(PayCst.Platform.ALIPAY); .setPlatform(PayCst.Platform.ALIPAY);
AlipayClient.setApiClient(configDto); AlipayClient.setApiClient(configDto);
@@ -87,7 +89,7 @@ public class AlipayIsvEntryManager {
log.error("支付宝查询进件状态异常: {}", body); log.error("支付宝查询进件状态异常: {}", body);
JSONObject object = JSONObject.parseObject(body); JSONObject object = JSONObject.parseObject(body);
respDto.setStatus(PayCst.EntryStatus.REJECTED); respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setFailReason(object.getString(ERR_MESSAGE_KEY)); respDto.setFailReason(object.getString(PayCst.ALIPAY_ERROR_MSG_KEY));
return respDto; return respDto;
} }
} }
@@ -98,11 +100,15 @@ public class AlipayIsvEntryManager {
* @param reqDto 请求信息 * @param reqDto 请求信息
*/ */
public static EntryThirdRespDto entryMerchant(AlipayConfigDto configDto, AggregateMerchantDto reqDto) { public static EntryThirdRespDto entryMerchant(AlipayConfigDto configDto, AggregateMerchantDto reqDto) {
if (configDto == null) {
configDto = AlipayConfigDto.getThirdDefaultConfig();
}
AlipayClient.setApiClient(configDto); AlipayClient.setApiClient(configDto);
EntryThirdRespDto respDto = new EntryThirdRespDto() EntryThirdRespDto respDto = new EntryThirdRespDto()
.setPlatform(PayCst.Platform.ALIPAY); .setPlatform(PayCst.Platform.ALIPAY);
try { try {
String batchNo = createRequest(reqDto); String batchNo = createRequest(configDto, reqDto);
respDto.setEntryId(batchNo);
AlipayOpenAgentFacetofaceSignModel signModel = buildFaceToFaceModel(reqDto, batchNo); AlipayOpenAgentFacetofaceSignModel signModel = buildFaceToFaceModel(reqDto, batchNo);
File businessLicensePic = UploadFileUtil.getFileByUrl(reqDto.getBusinessLicenceInfo().getLicensePic().getUrl()); File businessLicensePic = UploadFileUtil.getFileByUrl(reqDto.getBusinessLicenceInfo().getLicensePic().getUrl());
@@ -115,8 +121,8 @@ public class AlipayIsvEntryManager {
log.info("支付宝开启代商户签约: 响应={}", JSONObject.toJSONString(response)); log.info("支付宝开启代商户签约: 响应={}", JSONObject.toJSONString(response));
try { try {
String orderNo = confirmRequest(batchNo); String authInfo = confirmRequest(configDto, batchNo);
respDto.setEntryId(orderNo); respDto.setAlipayAuthInfo(authInfo);
respDto.setStatus(PayCst.EntryStatus.INIT); respDto.setStatus(PayCst.EntryStatus.INIT);
respDto.setErrorMsg(""); respDto.setErrorMsg("");
return respDto; return respDto;
@@ -130,12 +136,12 @@ public class AlipayIsvEntryManager {
log.error("支付宝开启代商户签约,创建应用事务异常: {}", body); log.error("支付宝开启代商户签约,创建应用事务异常: {}", body);
JSONObject object = JSONObject.parseObject(body); JSONObject object = JSONObject.parseObject(body);
respDto.setStatus(PayCst.EntryStatus.REJECTED); respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setErrorMsg(object.getString(ERR_MESSAGE_KEY)); respDto.setErrorMsg(object.getString(PayCst.ALIPAY_ERROR_MSG_KEY));
return respDto; return respDto;
} catch (IOException e) { } catch (Exception e) {
log.error("上传图片出错", e); log.error("支付宝开启代商户签约创建应用事务异常2", e);
respDto.setStatus(PayCst.EntryStatus.REJECTED); respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setErrorMsg("上传图片出错"); respDto.setErrorMsg(e.getMessage());
return respDto; return respDto;
} }
} }
@@ -145,7 +151,11 @@ public class AlipayIsvEntryManager {
* @param reqDto 请求信息 * @param reqDto 请求信息
* @return 请求ID * @return 请求ID
*/ */
public static String createRequest(AggregateMerchantDto reqDto) { public static String createRequest(AlipayConfigDto configDto, AggregateMerchantDto reqDto) {
if (configDto == null) {
configDto = AlipayConfigDto.getThirdDefaultConfig();
}
AlipayClient.setApiClient(configDto);
AlipayOpenAgentApi api = new AlipayOpenAgentApi(); AlipayOpenAgentApi api = new AlipayOpenAgentApi();
AlipayOpenAgentCreateModel openModel = new AlipayOpenAgentCreateModel(); AlipayOpenAgentCreateModel openModel = new AlipayOpenAgentCreateModel();
@@ -173,27 +183,40 @@ public class AlipayIsvEntryManager {
String body = e.getResponseBody(); String body = e.getResponseBody();
log.error("支付宝开启代商户签约,开启事务异常: {}", body); log.error("支付宝开启代商户签约,开启事务异常: {}", body);
JSONObject object = JSONObject.parseObject(body); JSONObject object = JSONObject.parseObject(body);
throw new CzgException("支付宝开启代商户签约,开启事务异常: " + object.getString(ERR_MESSAGE_KEY)); throw new CzgException("支付宝开启代商户签约,开启事务异常: " + object.getString(PayCst.ALIPAY_ERROR_MSG_KEY));
} }
} }
public static String confirmRequest(String batchNo) { public static String confirmRequest(AlipayConfigDto configDto, String batchNo) {
AlipayClient.setApiClient(null); if (configDto == null) {
configDto = AlipayConfigDto.getThirdDefaultConfig();
}
AlipayClient.setApiClient(configDto);
// 构造请求参数以调用接口 // 构造请求参数以调用接口
AlipayOpenAgentCommonsignApi api = new AlipayOpenAgentCommonsignApi(); AlipayOpenAgentApi api = new AlipayOpenAgentApi();
AlipayOpenAgentCommonsignConfirmModel data = new AlipayOpenAgentCommonsignConfirmModel(); AlipayOpenAgentConfirmModel data = new AlipayOpenAgentConfirmModel();
// 设置ISV 代商户操作事务编号 // 设置ISV 代商户操作事务编号
data.setBatchNo(batchNo); data.setBatchNo(batchNo);
try { try {
AlipayOpenAgentCommonsignConfirmResponseModel response = api.confirm(data); AlipayOpenAgentConfirmResponseModel response = api.confirm(data);
log.info("支付宝开启代商户签约,确认事务: 响应={}", response); log.info("支付宝开启代商户签约,确认事务: 响应={}", response);
return response.getOrderNo();
AlipayAuthInfoDto authInfoDto = new AlipayAuthInfoDto()
.setUserId(response.getUserId())
.setOpenId(response.getOpenId())
.setAuthAppId(response.getAuthAppId())
.setAppAuthToken(response.getAppAuthToken())
.setExpiresIn(response.getExpiresIn())
.setAppRefreshToken(response.getAppRefreshToken())
.setReExpiresIn(response.getReExpiresIn())
.setOrderNo(response.getOrderNo());
return JSONObject.toJSONString(authInfoDto);
} catch (ApiException e) { } catch (ApiException e) {
String body = e.getResponseBody(); String body = e.getResponseBody();
log.error("支付宝开启代商户签约,确认事务异常: {}", body); log.error("支付宝开启代商户签约,确认事务异常: {}", body);
JSONObject object = JSONObject.parseObject(body); JSONObject object = JSONObject.parseObject(body);
throw new CzgException(object.getString(ERR_MESSAGE_KEY)); throw new CzgException(object.getString(PayCst.ALIPAY_ERROR_MSG_KEY));
} }
} }

View File

@@ -0,0 +1,64 @@
package com.czg.third.alipay;
import com.alibaba.fastjson2.JSONObject;
import com.alipay.v3.ApiException;
import com.alipay.v3.api.AlipayTradeApi;
import com.alipay.v3.model.AlipayTradeCreateModel;
import com.alipay.v3.model.AlipayTradeCreateResponseModel;
import com.alipay.v3.model.ExtendParams;
import com.alipay.v3.util.model.CustomizedParams;
import com.czg.PayCst;
import com.czg.dto.req.PayParamsDto;
import com.czg.exception.CzgException;
import com.czg.third.alipay.dto.config.AlipayConfigDto;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
* @author yjjie
* @date 2026/1/9 09:30
*/
@Slf4j
public class AlipayIsvPayManager {
public static String jsapiPay(AlipayConfigDto configDto, PayParamsDto paramsDto) {
try {
AlipayClient.setApiClient(configDto);
AlipayTradeApi api = new AlipayTradeApi();
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo(paramsDto.getOrderNo());
model.setProductCode("JSAPI_PAY");
model.setOpAppId(paramsDto.getAppId());
model.setOpBuyerOpenId(paramsDto.getOpenId());
model.setTotalAmount(getYuanAmountByFen(paramsDto.getAmount()));
model.setSubject(paramsDto.getTitle());
model.setBody(paramsDto.getBody());
model.setNotifyUrl(paramsDto.getNotifyUrl());
model.setExtendParams(new ExtendParams());
CustomizedParams customizedParams = new CustomizedParams();
customizedParams.setAppAuthToken(paramsDto.getAlipayAuthInfo().getAppAuthToken());
AlipayTradeCreateResponseModel responseModel = api.create(model, customizedParams);
return responseModel.getTradeNo();
} catch (ApiException e) {
String body = e.getResponseBody();
log.error("支付宝 H5 api 支付异常: {}", body);
JSONObject object = JSONObject.parseObject(body);
throw new CzgException(object.getString(PayCst.ALIPAY_ERROR_MSG_KEY));
} catch (Exception e) {
log.error("支付宝 H5支付异常: {}", e.getMessage());
throw new CzgException(e.getMessage());
}
}
/**
* 金额转换
*/
private static String getYuanAmountByFen(Long amount) {
BigDecimal yuanAmount = new BigDecimal(amount).divide(new BigDecimal(100));
return yuanAmount.toString();
}
}

View File

@@ -0,0 +1,63 @@
package com.czg.third.alipay.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付宝授权信息
* @author yjjie
* @date 2026/1/9 11:31
*/
@Data
@Accessors(chain = true)
public class AlipayAuthInfoDto {
/**
* 授权商户的user_id
*/
@JSONField(name = "user_id")
private String userId;
/**
* 授权商户的open_id
*/
@JSONField(name = "open_id")
private String openId;
/**
* 授权商户的appid
*/
@JSONField(name = "auth_app_id")
private String authAppId;
/**
* 应用授权令牌
*/
@JSONField(name = "app_auth_token")
private String appAuthToken;
/**
* 应用授权令牌有效期
*/
@JSONField(name = "expires_in")
private String expiresIn;
/**
* 刷新令牌
*/
@JSONField(name = "app_refresh_token")
private String appRefreshToken;
/**
* 刷新令牌的有效时间
*/
@JSONField(name = "re_expires_in")
private String reExpiresIn;
/**
* 签约单号
*/
@JSONField(name = "order_no")
private String orderNo;
}

View File

@@ -32,7 +32,10 @@ public class AlipayConfigDto {
*/ */
private String domain; private String domain;
public static AlipayConfigDto getDefaultConfig() { /**
* 第三方应用 签约当面付
*/
public static AlipayConfigDto getThirdDefaultConfig() {
return new AlipayConfigDto() return new AlipayConfigDto()
.setAppId("2021006121646825") .setAppId("2021006121646825")
.setPrivateKey("MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz3FkdffxZibdwis9W7eOW1dEjGAbSvRDL2ikfeCIW5KZNoIjUqxI0mIoUlLRRKO71QLHZS1Vb2aJp8jeOAqIPa8e76HTneQEzk3FGA8gpraSGvbadHWzvxnmYKsts1TBiEZQL82ySJXhQTJvZ6jyDM7s6wHHUnrH+Qi29QpppQ1sxsoJeCtajUgRg3btD6XbBcyFAX3pzM56Kw9eaIjZoD8WToZOM/Y3sqNL2uo8lLqcIpTrI7Pq5ZOspmBQ+t8v3rS9IdDZZMvd0trzS67AXwHz8rKPBT/lL1A4iHnXCHUvktusX1fPs3/RGY/a7PIddaBLnfY0GcueE16K7QcrXAgMBAAECggEAbSdT2eckp75BWoaTcGEs1tRqeM7TDT/6moyKmnOQ1K3tE31SrSYpBUxxuC3LBNo/sw2RIZtrcTOyMnPyLTgB3DP/4lUf5X51MTTQ8LnI1ypvh+pIki9Sdm3QS33lOOZk149tdpdDk6ozyx/DEcvq74EMpoo2SuAIi5LkKVDrXuehvGA/WeXtpmuPgqRFdIA+JBlA3knHk5XEQY/k2Y31gq5oCwNL9iT9OAZqVkukE6EnvCXE9t2rAV4/snYilaf/UaO+ktgEwSbPBQ8YKlovDAarMBbGtgr6E174A9djlPyR+W/fgx8rlTwSWtieb9MkO8LN3KSxgVs0kY5U8OHg8QKBgQDljJq9kTFHare+W/fAXdUy3tJprfNQCAii6s+GuDfTQiviVQDtWmdtHAN+xU3to7MepvVhwHsqtQnZXKTtZuwwxn82FNl7A5RYD3GVFW+wG6AsGLIdESrWxySoL6Kx8QmNpMEVg8acT/ywzW/RnUXS5vU7GIi8GA0vtyBo24R9KQKBgQDIlf/R9+iNk9oXlbB/k4um9eVvBBS7l5cx4E5Id5Dpp4kGZfPZEa7oDsEUstZZM9mgQLJK/noNWbcf0+BohCR5ux7SC12qIoxwN3k4BzTDqrS8BzFuVVp5PELUsf/uCbRn05iMzpiDUhj3Vde04wvjHYIobfKlZO2HeSWXCpUH/wKBgQC8wSuU6ck90pEY5QMKmZ3wYK1g3PsQOirv3Gmde+nbu7PePsuuYQJfBAQTwCZeXJezgtKP+PjOm2Nn6vhrhpB9YxvD2s0ijET1TG23i5L1myHQYNZFdJJnXgXUjqcX7v5ODMYA7QTqEBPXRnbGRK7fx66rU3dMQ/LD46+wyaFeUQKBgA4QTk53dkuu6SSsLyLSwoDjTsHY5Gc+urAZjQORtoxbXcUgEtfOYJgOqMT9wP+iHgkZYCbX7tDO0IMfxOUvFqueTgvmFhwergAUM6CVCMMLTf689l9JBr3nVrw4+rvC3G5HLLP6rEDQ2cVFtIkPPj8fS4fwJYopKGpOOS9843QbAoGBAMoHH8LqoZ50FLsospx/hJe24Cd8wCgQTXSa/hMqNZ1999JDhftMt7R0ZdB1he2LReACe0K9ntBU4H4u225zZ3wZlyOfoyerAHuLK/ysNlgIIzblZlOxbBJ64Kul8leXzlYy3tOZuZ997KqBcWPCE3LUBBNvM6E3blJUnlmJAVoi") .setPrivateKey("MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz3FkdffxZibdwis9W7eOW1dEjGAbSvRDL2ikfeCIW5KZNoIjUqxI0mIoUlLRRKO71QLHZS1Vb2aJp8jeOAqIPa8e76HTneQEzk3FGA8gpraSGvbadHWzvxnmYKsts1TBiEZQL82ySJXhQTJvZ6jyDM7s6wHHUnrH+Qi29QpppQ1sxsoJeCtajUgRg3btD6XbBcyFAX3pzM56Kw9eaIjZoD8WToZOM/Y3sqNL2uo8lLqcIpTrI7Pq5ZOspmBQ+t8v3rS9IdDZZMvd0trzS67AXwHz8rKPBT/lL1A4iHnXCHUvktusX1fPs3/RGY/a7PIddaBLnfY0GcueE16K7QcrXAgMBAAECggEAbSdT2eckp75BWoaTcGEs1tRqeM7TDT/6moyKmnOQ1K3tE31SrSYpBUxxuC3LBNo/sw2RIZtrcTOyMnPyLTgB3DP/4lUf5X51MTTQ8LnI1ypvh+pIki9Sdm3QS33lOOZk149tdpdDk6ozyx/DEcvq74EMpoo2SuAIi5LkKVDrXuehvGA/WeXtpmuPgqRFdIA+JBlA3knHk5XEQY/k2Y31gq5oCwNL9iT9OAZqVkukE6EnvCXE9t2rAV4/snYilaf/UaO+ktgEwSbPBQ8YKlovDAarMBbGtgr6E174A9djlPyR+W/fgx8rlTwSWtieb9MkO8LN3KSxgVs0kY5U8OHg8QKBgQDljJq9kTFHare+W/fAXdUy3tJprfNQCAii6s+GuDfTQiviVQDtWmdtHAN+xU3to7MepvVhwHsqtQnZXKTtZuwwxn82FNl7A5RYD3GVFW+wG6AsGLIdESrWxySoL6Kx8QmNpMEVg8acT/ywzW/RnUXS5vU7GIi8GA0vtyBo24R9KQKBgQDIlf/R9+iNk9oXlbB/k4um9eVvBBS7l5cx4E5Id5Dpp4kGZfPZEa7oDsEUstZZM9mgQLJK/noNWbcf0+BohCR5ux7SC12qIoxwN3k4BzTDqrS8BzFuVVp5PELUsf/uCbRn05iMzpiDUhj3Vde04wvjHYIobfKlZO2HeSWXCpUH/wKBgQC8wSuU6ck90pEY5QMKmZ3wYK1g3PsQOirv3Gmde+nbu7PePsuuYQJfBAQTwCZeXJezgtKP+PjOm2Nn6vhrhpB9YxvD2s0ijET1TG23i5L1myHQYNZFdJJnXgXUjqcX7v5ODMYA7QTqEBPXRnbGRK7fx66rU3dMQ/LD46+wyaFeUQKBgA4QTk53dkuu6SSsLyLSwoDjTsHY5Gc+urAZjQORtoxbXcUgEtfOYJgOqMT9wP+iHgkZYCbX7tDO0IMfxOUvFqueTgvmFhwergAUM6CVCMMLTf689l9JBr3nVrw4+rvC3G5HLLP6rEDQ2cVFtIkPPj8fS4fwJYopKGpOOS9843QbAoGBAMoHH8LqoZ50FLsospx/hJe24Cd8wCgQTXSa/hMqNZ1999JDhftMt7R0ZdB1he2LReACe0K9ntBU4H4u225zZ3wZlyOfoyerAHuLK/ysNlgIIzblZlOxbBJ64Kul8leXzlYy3tOZuZ997KqBcWPCE3LUBBNvM6E3blJUnlmJAVoi")
@@ -47,6 +50,27 @@ public class AlipayConfigDto {
// .setAppId("2021004145625815") // .setAppId("2021004145625815")
// .setPrivateKey("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAjDBuS8K/IJb9ui+KuNm/sTUdEiaji4BNpZ92avO1N5JpNlGmac6ec4p3tNFT950sBLcQkClcUpQxUHQzAT6DYNNXOKyvfI/EmcqwCw6PaMNLs/8cV//J2WWZBUhLaOsjKurpm9/3W5MnTh4BGxIfBoeBMA8f8K3BgKdmyKtvIEV2h2cyjsMskdn+g6oNZcmWcms0pvpPHyH46mRaGFhpp0v19wX3WsamGldh1L2VntmaDN3C2XbSrXv90XYp5bEUqwTbLwXpMAlzTibF56d/iqv9oYi8cpAKougUFLOymnbutLNs2tLrEDSFwHcmG2/wbZHybZyYcIgFgv4arf+tAgMBAAECggEAf7hKKlw1y6Z6vvAtalxNZUuRZSfyog3p1bwYWxTavZPQcZ7Zs0lvVDmiO1u5m/7q96BbryY9IhCeUv0H5uF2lhwu/3s9AEL3qTPQkeb6eXxyhhX6A9RfPdM1Qbtg4CQHdHKg4qjP9znSVHwmDZ0y/QaEvdPdQzPjv92u9c2tn4N4x6XyBYcU5gzxiJNnIugCmBgcJo/3H2fgV+XXEhORPvy5of9b4oATHEaLS/8dAS2wuOjhzaGS4MXp3VkXn3XaYjwSzaL03qYWA+xm+aO5sJv8bmqZW7sNVck5o3sPo7cQ4VkBFVzyrRdmJcxcSRJ9MsB9JsrhoKI8pgaXrVie4QKBgQDU2vai0lpBIK/0jzRpPNoqdT8lnafnnWni8nU4kfAh+gCLi+HBPhQRT0kv4unQc2q2/gALE7sgZVO00JGY5a3R0orsojPoUSZlpypGW7GGqKy576NHn0nw4o/PdfysT92VWgt1hlfTf6qfCDhfE9APU+RGvlSWXcT8nxVel3iUaQKBgQCamoJN6+4v+chJvL2nqV8NVVRLp0vDIHxs1QOtKwUodx8Qp1D6CJYtavCXn8aNUFVNQJPJ7TQPpJjXP2rI4SN01weDwx+I+wh8PBGHV6/234R+6TvFgY1PrYgCdfNP4i/E7B4uyEhAxdU73PB8qkqRAeJGok05p7oG71KCOBiYpQKBgEZfGflcuDAeAW5GRhqg3rP4zWa/R7qgZVh9tll8jjp9b96y4XFE99d9MgId8BVVgyt6sEL5Q/2C4ni+F9TH4n6jMADp42VkJuCmsqhOOlP9whU67+2G8Sgtj0QUivPg964f9ffl8XVgGOW5DwIIB9p5btggptCLscufQK5kP545AoGADBvf6tR4wl8w9b2HqTMV08iEIqzGvVC1Dh0c/Zop/EJgN4CzUfIMOSBwGaAVAApzs+pD6QPgGP2OTwWTioo/qa4R05sbxDHNN1XJFa2jhZV6HiqMWOrNs5jm1zJ/zRjtHuJTdtyO9CvKiLbESy9XScY4/8lEfSiK5HIoJzTXkFUCgYAkYkvkW6psJpWj05XWq44UN0n6QOU/Igl35Um/iuOMVhsTmIt09STQVTuzJzfH82+sCqoRsD1blE5unKNUC1DK77aNKTv3Z0dxN9R7FAyfZRiYQXTrbBPBqWjay6FCNxn8e8UsJN4Z6FIV2LGlQI114krSap1MALKLVvnld0NaUQ==") // .setPrivateKey("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAjDBuS8K/IJb9ui+KuNm/sTUdEiaji4BNpZ92avO1N5JpNlGmac6ec4p3tNFT950sBLcQkClcUpQxUHQzAT6DYNNXOKyvfI/EmcqwCw6PaMNLs/8cV//J2WWZBUhLaOsjKurpm9/3W5MnTh4BGxIfBoeBMA8f8K3BgKdmyKtvIEV2h2cyjsMskdn+g6oNZcmWcms0pvpPHyH46mRaGFhpp0v19wX3WsamGldh1L2VntmaDN3C2XbSrXv90XYp5bEUqwTbLwXpMAlzTibF56d/iqv9oYi8cpAKougUFLOymnbutLNs2tLrEDSFwHcmG2/wbZHybZyYcIgFgv4arf+tAgMBAAECggEAf7hKKlw1y6Z6vvAtalxNZUuRZSfyog3p1bwYWxTavZPQcZ7Zs0lvVDmiO1u5m/7q96BbryY9IhCeUv0H5uF2lhwu/3s9AEL3qTPQkeb6eXxyhhX6A9RfPdM1Qbtg4CQHdHKg4qjP9znSVHwmDZ0y/QaEvdPdQzPjv92u9c2tn4N4x6XyBYcU5gzxiJNnIugCmBgcJo/3H2fgV+XXEhORPvy5of9b4oATHEaLS/8dAS2wuOjhzaGS4MXp3VkXn3XaYjwSzaL03qYWA+xm+aO5sJv8bmqZW7sNVck5o3sPo7cQ4VkBFVzyrRdmJcxcSRJ9MsB9JsrhoKI8pgaXrVie4QKBgQDU2vai0lpBIK/0jzRpPNoqdT8lnafnnWni8nU4kfAh+gCLi+HBPhQRT0kv4unQc2q2/gALE7sgZVO00JGY5a3R0orsojPoUSZlpypGW7GGqKy576NHn0nw4o/PdfysT92VWgt1hlfTf6qfCDhfE9APU+RGvlSWXcT8nxVel3iUaQKBgQCamoJN6+4v+chJvL2nqV8NVVRLp0vDIHxs1QOtKwUodx8Qp1D6CJYtavCXn8aNUFVNQJPJ7TQPpJjXP2rI4SN01weDwx+I+wh8PBGHV6/234R+6TvFgY1PrYgCdfNP4i/E7B4uyEhAxdU73PB8qkqRAeJGok05p7oG71KCOBiYpQKBgEZfGflcuDAeAW5GRhqg3rP4zWa/R7qgZVh9tll8jjp9b96y4XFE99d9MgId8BVVgyt6sEL5Q/2C4ni+F9TH4n6jMADp42VkJuCmsqhOOlP9whU67+2G8Sgtj0QUivPg964f9ffl8XVgGOW5DwIIB9p5btggptCLscufQK5kP545AoGADBvf6tR4wl8w9b2HqTMV08iEIqzGvVC1Dh0c/Zop/EJgN4CzUfIMOSBwGaAVAApzs+pD6QPgGP2OTwWTioo/qa4R05sbxDHNN1XJFa2jhZV6HiqMWOrNs5jm1zJ/zRjtHuJTdtyO9CvKiLbESy9XScY4/8lEfSiK5HIoJzTXkFUCgYAkYkvkW6psJpWj05XWq44UN0n6QOU/Igl35Um/iuOMVhsTmIt09STQVTuzJzfH82+sCqoRsD1blE5unKNUC1DK77aNKTv3Z0dxN9R7FAyfZRiYQXTrbBPBqWjay6FCNxn8e8UsJN4Z6FIV2LGlQI114krSap1MALKLVvnld0NaUQ==")
// .setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB") // .setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
// .setDomain("https://openapi.alipay.com/gateway.do");
}
/**
* 直付通商户配置
*/
public static AlipayConfigDto getDefaultConfig() {
// return new AlipayConfigDto()
// .setAppId("2021006121646825")
// .setPrivateKey("MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCz3FkdffxZibdwis9W7eOW1dEjGAbSvRDL2ikfeCIW5KZNoIjUqxI0mIoUlLRRKO71QLHZS1Vb2aJp8jeOAqIPa8e76HTneQEzk3FGA8gpraSGvbadHWzvxnmYKsts1TBiEZQL82ySJXhQTJvZ6jyDM7s6wHHUnrH+Qi29QpppQ1sxsoJeCtajUgRg3btD6XbBcyFAX3pzM56Kw9eaIjZoD8WToZOM/Y3sqNL2uo8lLqcIpTrI7Pq5ZOspmBQ+t8v3rS9IdDZZMvd0trzS67AXwHz8rKPBT/lL1A4iHnXCHUvktusX1fPs3/RGY/a7PIddaBLnfY0GcueE16K7QcrXAgMBAAECggEAbSdT2eckp75BWoaTcGEs1tRqeM7TDT/6moyKmnOQ1K3tE31SrSYpBUxxuC3LBNo/sw2RIZtrcTOyMnPyLTgB3DP/4lUf5X51MTTQ8LnI1ypvh+pIki9Sdm3QS33lOOZk149tdpdDk6ozyx/DEcvq74EMpoo2SuAIi5LkKVDrXuehvGA/WeXtpmuPgqRFdIA+JBlA3knHk5XEQY/k2Y31gq5oCwNL9iT9OAZqVkukE6EnvCXE9t2rAV4/snYilaf/UaO+ktgEwSbPBQ8YKlovDAarMBbGtgr6E174A9djlPyR+W/fgx8rlTwSWtieb9MkO8LN3KSxgVs0kY5U8OHg8QKBgQDljJq9kTFHare+W/fAXdUy3tJprfNQCAii6s+GuDfTQiviVQDtWmdtHAN+xU3to7MepvVhwHsqtQnZXKTtZuwwxn82FNl7A5RYD3GVFW+wG6AsGLIdESrWxySoL6Kx8QmNpMEVg8acT/ywzW/RnUXS5vU7GIi8GA0vtyBo24R9KQKBgQDIlf/R9+iNk9oXlbB/k4um9eVvBBS7l5cx4E5Id5Dpp4kGZfPZEa7oDsEUstZZM9mgQLJK/noNWbcf0+BohCR5ux7SC12qIoxwN3k4BzTDqrS8BzFuVVp5PELUsf/uCbRn05iMzpiDUhj3Vde04wvjHYIobfKlZO2HeSWXCpUH/wKBgQC8wSuU6ck90pEY5QMKmZ3wYK1g3PsQOirv3Gmde+nbu7PePsuuYQJfBAQTwCZeXJezgtKP+PjOm2Nn6vhrhpB9YxvD2s0ijET1TG23i5L1myHQYNZFdJJnXgXUjqcX7v5ODMYA7QTqEBPXRnbGRK7fx66rU3dMQ/LD46+wyaFeUQKBgA4QTk53dkuu6SSsLyLSwoDjTsHY5Gc+urAZjQORtoxbXcUgEtfOYJgOqMT9wP+iHgkZYCbX7tDO0IMfxOUvFqueTgvmFhwergAUM6CVCMMLTf689l9JBr3nVrw4+rvC3G5HLLP6rEDQ2cVFtIkPPj8fS4fwJYopKGpOOS9843QbAoGBAMoHH8LqoZ50FLsospx/hJe24Cd8wCgQTXSa/hMqNZ1999JDhftMt7R0ZdB1he2LReACe0K9ntBU4H4u225zZ3wZlyOfoyerAHuLK/ysNlgIIzblZlOxbBJ64Kul8leXzlYy3tOZuZ997KqBcWPCE3LUBBNvM6E3blJUnlmJAVoi")
// .setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
// .setDomain("https://openapi.alipay.com");
return new AlipayConfigDto()
.setAppId("2021004174605036")
.setPrivateKey("MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD48HaUoV7OH7os+9L01kHgxzwIhJick4OkFq4aHsntsXEJ J3gedhuEZtV8oHKZ30DPW12IJ4S8NXtpr8OWaqrAPFonf4wVaRY1d0yIAea57kfLEn9oOEEy4FzARgMNDkyxC+/3OUdGbLHpTjfVX3gusXsEhUDy1/WewihAkoNYF37+W3W/uVLzeWoPq0EcUbRv/G/t/p6gL69ltsMAiVFG4Q/Yk24YAN6lYgBPNLXUEwQ1Q+T+1omjfavHgvarKOp33z3JOUH+aGOmDsJ5Y9gyGtJzOCipAd8Zcv+T1ygsEzZYO1/gzcbPnfO1ShqStCHzssuw8FBVx2JdfQKXKMMNAgMBAAECggEAVTrO/pg5Q00titU1Jspsh67u6OOs9H605Ws2dI7yB8VmtAGlaJh7V1t14FN2qSP8poHbhhAxq9aLyGV7C3a9u09udnN+3J28EtYjh7VO732bavWMVXxdJjQWzWWrCb9JlpxFrlkYBA6W4w/6ob0sAqCVQ7jzwbEa0R4cde8ztOa5nysKSfr4YTSs0gqvoiC6fmg8eiRJraEQBoYz9VkKFtOhhh/4w5FhVcYQ2gQvZ3kK3QVuD1eJIQKlCtz8qaox9lXKDiZT4SCmnKshdUL0u5TYIcYeBjZmhJz0Q50KHcpZrCs5y7I0+vRBH3hU+TKSQt7ureymwhbwWMHScLV2gQKBgQD+58SHXhr5M8NGagAmTdsgmCnNv2kOYMd4STyPMY10SVwCv1Bk808ZuP+7e558J1b5/OuDLI5dLq6xrZ/1wLv1G++XqxI00hlFuWS5mUGJVcXotT1mw20rVeUILc7Qe3mLvbMGgfyKf4A7Qa5SSZ4bDeDTJYaFxyiQ281hMzDuPQKBgQD6AiL/Na2/uPH4CG6juwpjYvYVUcjK+7gbRwf3wWsWMpk90Z4ju2iUiP5c1J/oK9P+1T3PIr6M4Xjza8JJj+r9KC/PVB0gBv6vVM96cDpKUEy/UMpcn/T81vqj/Z+WEOODU8Ms6NiTTm+u9ldvpCjbu0u8M+9c0JeIyadJvSTFEQKBgQCsxmFyM3nq8YfpgU2qqNjfBeRH3faSVUy+nj1a/YZYjKS+A/i1BCnYUImeBVNN6chNV342ggvY4xxruDiU9Vcw8wd58O09Oi8BEIFSP6upL6cebUI6Fjo3xlegLJRiwV6INkNTJOYM5hD/mSxUACwXQFfkJipBINXBIgraWD1RLQKBgQCj49axWq0F6+WjZVOyPaD3uh37p9trRUxRhWTxw3fB23WdktaKMgbCqHOmwzP4bRLSEVQtf2dOz1gMqu14b8HqJvgAf/F/11YJ9hz09LEhmjZVjE68HZfqT7uK2W5OX8/lfXmK7TFcj6SjG5YB96lZMhTZ0WnufEd6QkdKDZYXIQKBgQD9GDTcIMbFwbEaKHnfZaTD3f876EGRgsgrCxwdEk7LBCRPwWo7yI929M4psIlpNwNeiyjBkBunWIVkpznp6qPtJqagIPUYesU4f5v6/okq5wcpaNKSkWbIvWVLaLGOiA1aeGJtbpMpyClbSr52puHpRRdvAiIEQ74yYh0JX8q96g==")
.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
.setDomain("https://openapi.alipay.com/gateway.do");
// return new AlipayConfigDto()
// .setAppId("2021004145625815")
// .setPrivateKey("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCAjDBuS8K/IJb9ui+KuNm/sTUdEiaji4BNpZ92avO1N5JpNlGmac6ec4p3tNFT950sBLcQkClcUpQxUHQzAT6DYNNXOKyvfI/EmcqwCw6PaMNLs/8cV//J2WWZBUhLaOsjKurpm9/3W5MnTh4BGxIfBoeBMA8f8K3BgKdmyKtvIEV2h2cyjsMskdn+g6oNZcmWcms0pvpPHyH46mRaGFhpp0v19wX3WsamGldh1L2VntmaDN3C2XbSrXv90XYp5bEUqwTbLwXpMAlzTibF56d/iqv9oYi8cpAKougUFLOymnbutLNs2tLrEDSFwHcmG2/wbZHybZyYcIgFgv4arf+tAgMBAAECggEAf7hKKlw1y6Z6vvAtalxNZUuRZSfyog3p1bwYWxTavZPQcZ7Zs0lvVDmiO1u5m/7q96BbryY9IhCeUv0H5uF2lhwu/3s9AEL3qTPQkeb6eXxyhhX6A9RfPdM1Qbtg4CQHdHKg4qjP9znSVHwmDZ0y/QaEvdPdQzPjv92u9c2tn4N4x6XyBYcU5gzxiJNnIugCmBgcJo/3H2fgV+XXEhORPvy5of9b4oATHEaLS/8dAS2wuOjhzaGS4MXp3VkXn3XaYjwSzaL03qYWA+xm+aO5sJv8bmqZW7sNVck5o3sPo7cQ4VkBFVzyrRdmJcxcSRJ9MsB9JsrhoKI8pgaXrVie4QKBgQDU2vai0lpBIK/0jzRpPNoqdT8lnafnnWni8nU4kfAh+gCLi+HBPhQRT0kv4unQc2q2/gALE7sgZVO00JGY5a3R0orsojPoUSZlpypGW7GGqKy576NHn0nw4o/PdfysT92VWgt1hlfTf6qfCDhfE9APU+RGvlSWXcT8nxVel3iUaQKBgQCamoJN6+4v+chJvL2nqV8NVVRLp0vDIHxs1QOtKwUodx8Qp1D6CJYtavCXn8aNUFVNQJPJ7TQPpJjXP2rI4SN01weDwx+I+wh8PBGHV6/234R+6TvFgY1PrYgCdfNP4i/E7B4uyEhAxdU73PB8qkqRAeJGok05p7oG71KCOBiYpQKBgEZfGflcuDAeAW5GRhqg3rP4zWa/R7qgZVh9tll8jjp9b96y4XFE99d9MgId8BVVgyt6sEL5Q/2C4ni+F9TH4n6jMADp42VkJuCmsqhOOlP9whU67+2G8Sgtj0QUivPg964f9ffl8XVgGOW5DwIIB9p5btggptCLscufQK5kP545AoGADBvf6tR4wl8w9b2HqTMV08iEIqzGvVC1Dh0c/Zop/EJgN4CzUfIMOSBwGaAVAApzs+pD6QPgGP2OTwWTioo/qa4R05sbxDHNN1XJFa2jhZV6HiqMWOrNs5jm1zJ/zRjtHuJTdtyO9CvKiLbESy9XScY4/8lEfSiK5HIoJzTXkFUCgYAkYkvkW6psJpWj05XWq44UN0n6QOU/Igl35Um/iuOMVhsTmIt09STQVTuzJzfH82+sCqoRsD1blE5unKNUC1DK77aNKTv3Z0dxN9R7FAyfZRiYQXTrbBPBqWjay6FCNxn8e8UsJN4Z6FIV2LGlQI114krSap1MALKLVvnld0NaUQ==")
// .setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiQkrz+emAuS1mB3KKDOMmAZRd/BlPbh7fAIHAqAj1+QCZNcV3o2BTLIIqnuKpSlFXDG3uDzp2VsBxcizXuBbFyPGylnD9CgCj5abyh3+FIHPAZ2IM3TtpqImZ0TSPGXrMli4Nir7MvZktgccCqQKCC4o6iaDGz+UwWwJUIPna8fm2tiTZ+KH150CZbKVj4ZGNpBh5XSV/1dRgyQIV9D/EwSbkZ0n6VgKQLJBi0C2UE3QB17aL1Ir6+gDXIDbknN8O7GUD3aMGdThYdSRUb5wp9CZ5qfV7vCS/CgaRo38nhH3NOzkTL+7v0m1ZDHPmqEkn9VzZN6sCQdL7PoAOjHOCwIDAQAB")
// .setDomain("https://openapi.alipay.com/gateway.do"); // .setDomain("https://openapi.alipay.com/gateway.do");
} }
} }

View File

@@ -15,11 +15,13 @@ import com.czg.third.wechat.dto.req.entry.business.WechatEntryLicenseReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryMiniProgramReqDto; import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryMiniProgramReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntrySalesInfoReqDto; import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntrySalesInfoReqDto;
import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryStoreInfoReqDto; import com.czg.third.wechat.dto.req.entry.business.sales.WechatEntryStoreInfoReqDto;
import com.czg.third.wechat.dto.req.entry.id.WechatEntryIdCardReqDto;
import com.czg.third.wechat.dto.resp.WechatAuditDetail; import com.czg.third.wechat.dto.resp.WechatAuditDetail;
import com.czg.third.wechat.dto.resp.WechatQueryStateResp; import com.czg.third.wechat.dto.resp.WechatQueryStateResp;
import com.czg.utils.UploadFileUtil; import com.czg.utils.UploadFileUtil;
import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.PrivacyEncryptor; import com.wechat.pay.java.core.cipher.PrivacyEncryptor;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.service.file.FileUploadService; import com.wechat.pay.java.service.file.FileUploadService;
import com.wechat.pay.java.service.file.model.FileUploadResponse; import com.wechat.pay.java.service.file.model.FileUploadResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -102,12 +104,12 @@ public class WechatEntryManager {
* @param reqDto 请求信息 * @param reqDto 请求信息
*/ */
public static EntryThirdRespDto entryMerchant(WechatPayConfigDto configDto, AggregateMerchantDto reqDto) { public static EntryThirdRespDto entryMerchant(WechatPayConfigDto configDto, AggregateMerchantDto reqDto) {
WechatEntryReqDto entryReqDto = buildEntryParams(configDto, reqDto);
EntryThirdRespDto respDto = new EntryThirdRespDto() EntryThirdRespDto respDto = new EntryThirdRespDto()
.setPlatform(PayCst.Platform.WECHAT); .setPlatform(PayCst.Platform.WECHAT);
log.info("微信进件参数:{}", JSONObject.toJSONString(entryReqDto));
try { try {
WechatEntryReqDto entryReqDto = buildEntryParams(configDto, reqDto);
log.info("微信进件参数:{}", JSONObject.toJSONString(entryReqDto));
String params = JSONObject.toJSONString(entryReqDto, JSONWriter.Feature.IgnoreEmpty); String params = JSONObject.toJSONString(entryReqDto, JSONWriter.Feature.IgnoreEmpty);
String respBody = WechatReqUtils.postReq(configDto, "/v3/applyment4sub/applyment/", params); String respBody = WechatReqUtils.postReq(configDto, "/v3/applyment4sub/applyment/", params);
JSONObject object = JSONObject.parseObject(respBody); JSONObject object = JSONObject.parseObject(respBody);
@@ -123,7 +125,7 @@ public class WechatEntryManager {
respDto.setErrorMsg(object.getString("message")); respDto.setErrorMsg(object.getString("message"));
} }
} catch (Exception e) { } catch (Exception e) {
log.error("微信进件报错:{}", e.getMessage(), e); log.error("微信进件报错:{}", e.getMessage());
respDto.setStatus(PayCst.EntryStatus.REJECTED); respDto.setStatus(PayCst.EntryStatus.REJECTED);
respDto.setEntryId(""); respDto.setEntryId("");
respDto.setErrorMsg(e.getMessage()); respDto.setErrorMsg(e.getMessage());
@@ -202,8 +204,10 @@ public class WechatEntryManager {
); );
return uploadResponse.getMediaId(); return uploadResponse.getMediaId();
} catch (MalformedMessageException e) {
log.error("微信上传图片报错1URL{}", url);
} catch (Exception e) { } catch (Exception e) {
log.error("微信上传图片报错URL{},错误信息:{}", url, e.getMessage(), e); log.error("微信上传图片报错2URL{}", url);
} }
return ""; return "";
} }
@@ -282,6 +286,16 @@ public class WechatEntryManager {
subjectInfo.setBusinessLicenseInfo(licenseReqDto); subjectInfo.setBusinessLicenseInfo(licenseReqDto);
WechatEntryIdentityReqDto identityInfo = new WechatEntryIdentityReqDto(); WechatEntryIdentityReqDto identityInfo = new WechatEntryIdentityReqDto();
identityInfo.setIdHolderType(PayCst.ContactPersonType.LEGAL); identityInfo.setIdHolderType(PayCst.ContactPersonType.LEGAL);
identityInfo.setIdDocType("IDENTIFICATION_TYPE_IDCARD");
WechatEntryIdCardReqDto idCardInfo = new WechatEntryIdCardReqDto();
idCardInfo.setIdCardCopy(legalPersonInfo.getIdCardFrontPic().getWechatId());
idCardInfo.setIdCardNational(legalPersonInfo.getIdCardFrontPic().getWechatId());
idCardInfo.setIdCardName(encryptor.encrypt(legalPersonInfo.getLegalPersonName()));
idCardInfo.setIdCardNumber(encryptor.encrypt(legalPersonInfo.getLegalPersonId()));
idCardInfo.setIdCardAddress(encryptor.encrypt(legalPersonInfo.getLegalAddress()));
idCardInfo.setCardPeriodBegin(legalPersonInfo.getLegalIdPersonStartDate());
idCardInfo.setCardPeriodEnd(legalPersonInfo.getLegalPersonIdEndDate());
identityInfo.setIdCardInfo(idCardInfo);
subjectInfo.setIdentityInfo(identityInfo); subjectInfo.setIdentityInfo(identityInfo);
entryParams.setSubjectInfo(subjectInfo); entryParams.setSubjectInfo(subjectInfo);
@@ -325,11 +339,11 @@ public class WechatEntryManager {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
queryMerchantEntryStatus(null, "20220106000000000001"); // queryMerchantEntryStatus(null, "20220106000000000001");
// int offset = 0; int offset = 0;
// Integer limit = 100; Integer limit = 100;
// JSONObject resp = queryBankList(null, offset, limit); JSONObject resp = queryBankList(null, offset, limit);
// queryBankBranchList(dto, "1000009561", "110000", offset, limit); // queryBankBranchList(dto, "1000009561", "110000", offset, limit);
// queryBankBranchList(dto, "1000009561", "29", offset, limit); // queryBankBranchList(dto, "1000009561", "29", offset, limit);

View File

@@ -1,11 +1,44 @@
package com.czg.third.wechat; package com.czg.third.wechat;
import com.alibaba.fastjson2.JSONObject;
import com.czg.dto.req.PayParamsDto;
import com.czg.third.wechat.dto.config.WechatPayConfigDto;
import lombok.extern.slf4j.Slf4j;
/** /**
* @author yjjie * @author yjjie
* @date 2025/12/26 09:15 * @date 2025/12/26 09:15
*/ */
@Slf4j
public class WechatPayManager { public class WechatPayManager {
public static void main(String[] args) { /**
* jsapi 小程序支付
* @param configDto 配置
* @param paramsDto 参数
*/
public static String jsapiPay(WechatPayConfigDto configDto, PayParamsDto paramsDto) {
if (configDto == null) {
configDto = WechatPayConfigDto.getDefaultConfig();
}
JSONObject reqData = new JSONObject();
reqData.put("sp_appid", paramsDto.getAppId());
reqData.put("sp_mchid", configDto.getMerchantId());
reqData.put("sub_mchid", paramsDto.getMerchantId());
reqData.put("description", paramsDto.getTitle());
reqData.put("out_trade_no", paramsDto.getOrderNo());
reqData.put("notify_url", paramsDto.getNotifyUrl());
JSONObject amount = new JSONObject();
amount.put("total", paramsDto.getAmount());
reqData.put("amount", amount);
JSONObject payer = new JSONObject();
payer.put("sp_openid", paramsDto.getOpenId());
reqData.put("payer", payer);
String string = WechatReqUtils.postReq(configDto, "/v3/pay/partner/transactions/jsapi", reqData.toJSONString());
log.info("微信支付请求结果: orderNo = {}, res = {}", paramsDto.getOrderNo(), string);
return "";
} }
} }

View File

@@ -7,6 +7,9 @@ import com.czg.exception.CzgException;
import com.czg.third.wechat.dto.config.WechatPayConfigDto; import com.czg.third.wechat.dto.config.WechatPayConfigDto;
import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.Signer; import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.http.HttpClient;
import com.wechat.pay.java.core.http.HttpMethod;
import com.wechat.pay.java.core.util.NonceUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.net.URLEncoder; import java.net.URLEncoder;
@@ -53,19 +56,22 @@ public class WechatReqUtils {
HttpRequest request = switch (method) { HttpRequest request = switch (method) {
case "POST" -> HttpUtil.createPost(configDto.getDomain() + url) case "POST" -> HttpUtil.createPost(configDto.getDomain() + url)
.header("Authorization", authorization) .header("Authorization", authorization)
.header("Content-Type", "application/json")
.header("Wechatpay-Serial", configDto.getPublicKeyId()) .header("Wechatpay-Serial", configDto.getPublicKeyId())
.charset(StandardCharsets.UTF_8)
.contentType("application/json")
.body(body ); .body(body );
case "GET" -> HttpUtil.createGet(configDto.getDomain() + url) case "GET" -> HttpUtil.createGet(configDto.getDomain() + url)
.header("Authorization", authorization) .header("Authorization", authorization)
.header("Content-Type", "application/json") .header("Wechatpay-Serial", configDto.getPublicKeyId())
.header("Wechatpay-Serial", configDto.getPublicKeyId()); .charset(StandardCharsets.UTF_8)
.contentType("application/json");
default -> throw new CzgException("不支持的请求方法"); default -> throw new CzgException("不支持的请求方法");
}; };
log.info("微信支付请求url = {} \nauthorization: {}", request.getUrl(), authorization);
HttpResponse response = request.execute(); HttpResponse response = request.execute();
String s = response.body(); String s = response.body();
log.info("微信支付请求url = {}, method: {}, body: {}, resp: {}", url, method, body, s); log.info("微信支付请求url = {}\n\tmethod: {}\n\tbody: {}\n\tresp: {}", url, method, body, s);
return s; return s;
} }
@@ -92,7 +98,7 @@ public class WechatReqUtils {
Config config = WechatConfig.getRsaConfig(configDto); Config config = WechatConfig.getRsaConfig(configDto);
Signer signer = config.createSigner(); Signer signer = config.createSigner();
String signature = signer.sign(encryptStr).getSign(); String signature = signer.sign(encryptStr).getSign();
log.info("微信签名 encryptStr = {},\nsignature{}", encryptStr, signature); log.info("微信签名 encryptStr = \n{} \nsignature{}", encryptStr, signature);
return signature; return signature;
} }
@@ -100,7 +106,7 @@ public class WechatReqUtils {
* 获取随机串 * 获取随机串
*/ */
private static String getNonceStr() { private static String getNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(); return NonceUtil.createNonce(32);
} }
/** /**

View File

@@ -0,0 +1,14 @@
package com.czg.service.account.mapper;
import com.mybatisflex.core.BaseMapper;
import com.czg.account.entity.QuickMenu;
/**
* 悬浮窗配置 映射层。
*
* @author ww
* @since 2025-12-29
*/
public interface QuickMenuMapper extends BaseMapper<QuickMenu> {
}

View File

@@ -14,9 +14,9 @@ import com.czg.config.RedisCst;
import com.czg.constants.ParamCodeCst; import com.czg.constants.ParamCodeCst;
import com.czg.exception.CzgException; import com.czg.exception.CzgException;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.service.RedisService;
import com.czg.service.account.mapper.CallQueueMapper; import com.czg.service.account.mapper.CallQueueMapper;
import com.czg.service.account.mapper.CallTableMapper; import com.czg.service.account.mapper.CallTableMapper;
import com.czg.service.account.util.FunUtil;
import com.czg.service.account.util.WechatMiniMsgUtil; import com.czg.service.account.util.WechatMiniMsgUtil;
import com.czg.system.dto.SysParamsDTO; import com.czg.system.dto.SysParamsDTO;
import com.czg.system.service.SysParamsService; import com.czg.system.service.SysParamsService;
@@ -51,10 +51,10 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
@Resource @Resource
private ShopUserService shopUserService; private ShopUserService shopUserService;
@Resource @Resource
private FunUtil funUtil;
@Resource
private StringRedisTemplate stringRedisTemplate; private StringRedisTemplate stringRedisTemplate;
@Resource @Resource
private RedisService redisService;
@Resource
private ShopInfoService shopInfoService; private ShopInfoService shopInfoService;
@Resource @Resource
private CallConfigService callConfigService; private CallConfigService callConfigService;
@@ -228,13 +228,13 @@ public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable
} }
private String getCallNumber(Long shopId, CallTable callTable) { private String getCallNumber(Long shopId, CallTable callTable) {
return funUtil.runFunAndCheckKey(() -> { return redisService.runFunAndCheckKey(() -> {
String callNumKey = RedisCst.getTableCallNumKey(shopId, callTable.getId()); String callNumKey = RedisCst.getTableCallNumKey(shopId, callTable.getId());
String value = stringRedisTemplate.opsForValue().get(callNumKey); String value = stringRedisTemplate.opsForValue().get(callNumKey);
AtomicReference<String> newVal = new AtomicReference<>(""); AtomicReference<String> newVal = new AtomicReference<>("");
// 初始化 // 初始化
if (StrUtil.isBlank(value)) { if (StrUtil.isBlank(value)) {
Boolean setFlag = FunUtil.runFunAndRetry(() -> stringRedisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag, Boolean setFlag = RedisService.runFunAndRetry(() -> stringRedisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
_ -> newVal.set(stringRedisTemplate.opsForValue().get(callNumKey))); _ -> newVal.set(stringRedisTemplate.opsForValue().get(callNumKey)));
if (setFlag) { if (setFlag) {

View File

@@ -0,0 +1,18 @@
package com.czg.service.account.service.impl;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.account.entity.QuickMenu;
import com.czg.account.service.QuickMenuService;
import com.czg.service.account.mapper.QuickMenuMapper;
import org.springframework.stereotype.Service;
/**
* 悬浮窗配置 服务层实现。
*
* @author ww
* @since 2025-12-29
*/
@Service
public class QuickMenuServiceImpl extends ServiceImpl<QuickMenuMapper, QuickMenu> implements QuickMenuService{
}

View File

@@ -99,7 +99,7 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
if (shopInfo == null) { if (shopInfo == null) {
throw new CzgException("店铺不存在"); throw new CzgException("店铺不存在");
} }
if (shopInfo.getExpireTime() != null && (DateUtil.date().toLocalDateTime().isAfter(shopInfo.getExpireTime()))) { if (shopInfo.getExpireTime() != null && (LocalDateTime.now().isAfter(shopInfo.getExpireTime()))) {
throw new CzgException("店铺已过期,请联系商家"); throw new CzgException("店铺已过期,请联系商家");
} }
if (SystemConstants.OneZero.ZERO == shopInfo.getOnSale() || shopInfo.getStatus() != SystemConstants.OneZero.ONE) { if (SystemConstants.OneZero.ZERO == shopInfo.getOnSale() || shopInfo.getStatus() != SystemConstants.OneZero.ONE) {
@@ -326,10 +326,11 @@ public class ShopInfoServiceImpl extends ServiceImpl<ShopInfoMapper, ShopInfo> i
@Override @Override
@CacheEvict(key = "#shopId") @CacheEvict(key = "#shopId")
public Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId) { public Boolean editEntry(Long shopId, String wechatMerchantId, String alipayMerchantId, String alipayAuthInfo) {
ShopInfo shopInfo = new ShopInfo(); ShopInfo shopInfo = new ShopInfo();
shopInfo.setWechatMerchantId(wechatMerchantId); shopInfo.setWechatMerchantId(wechatMerchantId);
shopInfo.setAlipayMerchantId(alipayMerchantId); shopInfo.setAlipayMerchantId(alipayMerchantId);
shopInfo.setAlipayAuthInfo(alipayAuthInfo);
return update(shopInfo, new QueryWrapper().eq(ShopInfo::getId, shopId)); return update(shopInfo, new QueryWrapper().eq(ShopInfo::getId, shopId));
} }

View File

@@ -22,6 +22,7 @@ import com.czg.market.vo.InviteUserVO;
import com.czg.market.vo.MemberConfigVO; import com.czg.market.vo.MemberConfigVO;
import com.czg.order.entity.OrderInfo; import com.czg.order.entity.OrderInfo;
import com.czg.service.account.mapper.ShopUserMapper; import com.czg.service.account.mapper.ShopUserMapper;
import com.czg.utils.FunUtils;
import com.czg.utils.PageUtil; import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
@@ -61,6 +62,7 @@ public class ShopUserServiceImpl extends ServiceImpl<ShopUserMapper, ShopUser> i
private MemberLevelConfigService memberLevelConfigService; private MemberLevelConfigService memberLevelConfigService;
@DubboReference @DubboReference
private TbMemberConfigService memberConfigService; private TbMemberConfigService memberConfigService;
private ShopUser getUserInfo(Long shopUserId) { private ShopUser getUserInfo(Long shopUserId) {
ShopUser shopUser = queryChain().eq(ShopUser::getId, shopUserId).one(); ShopUser shopUser = queryChain().eq(ShopUser::getId, shopUserId).one();
if (shopUser == null) { if (shopUser == null) {
@@ -240,9 +242,13 @@ public class ShopUserServiceImpl extends ServiceImpl<ShopUserMapper, ShopUser> i
shopUser.setBirthDay(null); shopUser.setBirthDay(null);
} }
shopUser.setNickName(userInfo.getNickName()); shopUser.setNickName(userInfo.getNickName());
if (shopUser.getJoinTime() == null) { // if (shopUser.getJoinTime() == null) {
shopUser.setJoinTime(LocalDateTime.now()); // shopUser.setJoinTime(LocalDateTime.now());
// }
boolean b = saveOrUpdate(shopUser);
if (b) {
FunUtils.transactionSafeRun(() -> memberConfigService.joinMemberByCondition(shopId, userId, shopUser));
} }
return saveOrUpdate(shopUser); return b;
} }
} }

View File

@@ -14,6 +14,7 @@ import com.czg.market.entity.MkPointsUser;
import com.czg.market.entity.MkShopCouponRecord; import com.czg.market.entity.MkShopCouponRecord;
import com.czg.market.service.MkPointsUserService; import com.czg.market.service.MkPointsUserService;
import com.czg.market.service.MkShopCouponRecordService; import com.czg.market.service.MkShopCouponRecordService;
import com.czg.market.service.TbMemberConfigService;
import com.czg.resp.CzgResult; import com.czg.resp.CzgResult;
import com.czg.service.RedisService; import com.czg.service.RedisService;
import com.czg.service.account.mapper.ShopConfigMapper; import com.czg.service.account.mapper.ShopConfigMapper;

View File

@@ -1,100 +0,0 @@
package com.czg.service.account.util;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author Administrator
*/
@Slf4j
@Component
public class FunUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public static int retryCount = 5;
/**
* 执行任务并保证锁唯一
* @param supplier 业务逻辑
* @param lockKey Redis锁的Key
* @return 业务逻辑返回值
*/
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
try {
// 尝试获取锁,超时时间 5 秒,防止死锁
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
int count = 0;
// 初始等待 10ms
int retryDelay = 10;
while (!lock) {
// 最多重试 10 次,大约 10 秒
if (count++ > 50) {
throw new RuntimeException("系统繁忙, 稍后再试");
}
Thread.sleep(retryDelay);
// 指数退避,最大等待 200ms
retryDelay = Math.min(retryDelay * 2, 200);
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
}
// 执行任务
return supplier.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("线程被中断", e);
} catch (Exception e) {
log.error("执行出错:{}", e.getMessage(), e);
throw e;
} finally {
// 释放锁(使用 Lua 脚本确保原子性)
unlock(lockKey, lockValue);
}
}
/**
* 使用 Lua 脚本确保释放锁的原子性
* @param lockKey 锁的 Key
* @param lockValue 当前线程的锁值
*/
private void unlock(String lockKey, String lockValue) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey), lockValue);
}
public static <T, R> R runFunAndRetry(
Supplier<R> function,
Function<R, Boolean> check, Consumer<R> errFun) {
log.info("工具类开始执行函数");
R result = function.get();
boolean flag = check.apply(result);
log.info("执行结果: {}", result);
while (flag && retryCount-- > 0) {
log.info("执行函数失败, 剩余尝试次数{}", retryCount);
result = function.get();
log.info("执行结果: {}", result);
flag = check.apply(result);
}
if (flag) {
errFun.accept(result);
}
return result;
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.account.mapper.QuickMenuMapper">
</mapper>

View File

@@ -98,7 +98,7 @@
<if test="amount != null"> <if test="amount != null">
AND a.amount >= #{amount} AND a.amount >= #{amount}
</if> </if>
group by a.id
ORDER BY a.create_time DESC ORDER BY a.create_time DESC
</select> </select>
<select id="selectVipCard_COUNT" resultType="java.lang.Long"> <select id="selectVipCard_COUNT" resultType="java.lang.Long">

View File

@@ -85,8 +85,6 @@ public class MkDistributionUserServiceImpl extends ServiceImpl<MkDistributionUse
private ShopUserService shopUserService; private ShopUserService shopUserService;
@DubboReference @DubboReference
private UserInfoService userInfoService; private UserInfoService userInfoService;
@DubboReference
private OrderPaymentService orderPaymentService;
@Resource @Resource
private OrderInfoService orderInfoService; private OrderInfoService orderInfoService;
@DubboReference @DubboReference
@@ -706,6 +704,16 @@ public class MkDistributionUserServiceImpl extends ServiceImpl<MkDistributionUse
}); });
} }
@Transactional
@Override
public void distributionUserAmount(MkDistributionFlow item, OrderInfo orderInfo) {
ShopUser shopUser = shopUserService.getById(item.getDistributionUserId());
updateShopInfoAmount(orderInfo.getShopId(), item.getRewardAmount().negate(), orderInfo.getId(), TableValueConstant.DistributionAmountFlow.Type.SUB, "分销扣减");
updateIncome(item.getRewardAmount().negate(), item.getRewardAmount(), BigDecimal.ZERO,
item.getDistributionUserId(), shopUser.getUserId(), item.getShopUserId(), item.getShopId(), item.getLevel());
distributionFlowService.updateById(item);
}
@Override @Override
public void distribute(Long sourceId, String orderNo, BigDecimal amount, Long sourceUserId, Long shopId, String type) { public void distribute(Long sourceId, String orderNo, BigDecimal amount, Long sourceUserId, Long shopId, String type) {
MkDistributionDeliver deliver = new MkDistributionDeliver().setSourceId(sourceId).setOrderNo(orderNo).setShopId(shopId).setType(type).setStatus("success"); MkDistributionDeliver deliver = new MkDistributionDeliver().setSourceId(sourceId).setOrderNo(orderNo).setShopId(shopId).setType(type).setStatus("success");

View File

@@ -192,7 +192,7 @@ public class MkPointsUserServiceImpl extends ServiceImpl<MkPointsUserMapper, MkP
.shopId(pointsUser.getShopId()) .shopId(pointsUser.getShopId())
.shopUserId(pointsUser.getShopUserId()) .shopUserId(pointsUser.getShopUserId())
.floatType(PointsConstant.SUB.getValue()) .floatType(PointsConstant.SUB.getValue())
.floatPoints(floatPoints) .floatPoints(-floatPoints)
.balancePoints(pointsUser.getPointBalance()) .balancePoints(pointsUser.getPointBalance())
.sourceId(orderId.toString()) .sourceId(orderId.toString())
.content(reason) .content(reason)

View File

@@ -175,9 +175,9 @@ public class MkShopRechargeServiceImpl extends ServiceImpl<MkShopRechargeMapper,
// 标准充值 // 标准充值
if (rechargeDetailId != null) { if (rechargeDetailId != null) {
FunUtils.asyncSafeRunVoid(() -> {
MkShopRechargeDetail rechargeDetail = shopRechargeDetailService.getById(rechargeDetailId); MkShopRechargeDetail rechargeDetail = shopRechargeDetailService.getById(rechargeDetailId);
shopUserMoneyEditDTO.setMoney(rechargeDetail.getAmount()); shopUserMoneyEditDTO.setMoney(rechargeDetail.getAmount());
FunUtils.asyncSafeRunVoid(() -> {
// 赠送金额 // 赠送金额
ShopUserMoneyEditDTO shopUserMoneyEditRewardDTO = new ShopUserMoneyEditDTO() ShopUserMoneyEditDTO shopUserMoneyEditRewardDTO = new ShopUserMoneyEditDTO()
.setId(shopUserId) .setId(shopUserId)

View File

@@ -361,6 +361,7 @@ public class TbMemberConfigServiceImpl extends ServiceImpl<TbMemberConfigMapper,
memberExpFlowService.save(expFlow); memberExpFlowService.save(expFlow);
upShopUser.setExperience(shopUser.getExperience() + exp); upShopUser.setExperience(shopUser.getExperience() + exp);
shopUser.setExperience(upShopUser.getExperience());
// 修改会员等级 // 修改会员等级
MemberLevelConfig nextConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopUser.getMainShopId()) MemberLevelConfig nextConfig = levelConfigService.getOne(new QueryWrapper().eq(MemberLevelConfig::getShopId, shopUser.getMainShopId())
.gt(MemberLevelConfig::getExperienceValue, levelVO.getExperienceValue()).orderBy(MemberLevelConfig::getExperienceValue, true).limit(1)); .gt(MemberLevelConfig::getExperienceValue, levelVO.getExperienceValue()).orderBy(MemberLevelConfig::getExperienceValue, true).limit(1));

View File

@@ -13,6 +13,8 @@ import java.time.LocalDateTime;
@Data @Data
public class AggregateMerchantVO extends AggregateMerchantDto{ public class AggregateMerchantVO extends AggregateMerchantDto{
private String shopName;
private LocalDateTime createTime; private LocalDateTime createTime;
private LocalDateTime updateTime; private LocalDateTime updateTime;
@@ -30,6 +32,24 @@ public class AggregateMerchantVO extends AggregateMerchantDto{
* 支付宝账号 * 支付宝账号
*/ */
private String alipayAccount; private String alipayAccount;
/**
* 商户编号(在当前系统唯一)
*/
private String merchantCode;
/**
* 【必填】
* 商户类型
* 0: 个体商户;
* 1: 企业商户;
* 3: 小微商户 暂不支持
*/
private String userType;
/**
* 【必填】
* 商户简称--企业、个体必填
*/
private String shortName;
/** /**
* 微信进件错误信息 * 微信进件错误信息

View File

@@ -0,0 +1,58 @@
package com.czg.service.order.dto;
import com.czg.BaseQueryParam;
import com.czg.utils.CzgStrUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 进件查询参数
* @author ww
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class MerchantQueryDTO extends BaseQueryParam {
/**
* 商户类型
* 0: 个体商户;
* 1: 企业商户;
*/
private String userType;
/**
* 店铺名称 模糊查询
*/
private String shopName;
/**
* 进件状态
* {@link com.czg.PayCst.EntryStatus}
* WAIT 待提交
* INIT 待处理
* AUDIT 待审核
* SIGN 待签约
* FINISH 已完成
* REJECTED 失败
*/
private String status;
/**
* 支付宝账号
*/
private String alipayAccount;
public String getUserType() {
return CzgStrUtils.getStrOrNull(userType);
}
public String getShopName() {
return CzgStrUtils.getStrOrNull(shopName);
}
public String getStatus() {
return CzgStrUtils.getStrOrNull(status);
}
public String getAlipayAccount() {
return CzgStrUtils.getStrOrNull(alipayAccount);
}
}

View File

@@ -1,7 +1,11 @@
package com.czg.service.order.mapper; package com.czg.service.order.mapper;
import com.czg.service.order.dto.MerchantQueryDTO;
import com.mybatisflex.core.BaseMapper; import com.mybatisflex.core.BaseMapper;
import com.czg.order.entity.ShopDirectMerchant; import com.czg.order.entity.ShopDirectMerchant;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* 商户进件 映射层。 * 商户进件 映射层。
@@ -11,4 +15,5 @@ import com.czg.order.entity.ShopDirectMerchant;
*/ */
public interface ShopDirectMerchantMapper extends BaseMapper<ShopDirectMerchant> { public interface ShopDirectMerchantMapper extends BaseMapper<ShopDirectMerchant> {
List<ShopDirectMerchant> getEntryList(@Param("queryParam") MerchantQueryDTO queryParam);
} }

View File

@@ -320,7 +320,7 @@ public interface ShopOrderStatisticMapper extends BaseMapper<ShopOrderStatistic>
" `order`.shop_id = #{shopId} " + " `order`.shop_id = #{shopId} " +
" and trade_day = #{tradeDay} " + " and trade_day = #{tradeDay} " +
" and paid_time is not null" + " and paid_time is not null" +
" order by detail.product_id, detail.sku_id") " group by detail.product_id, detail.sku_id")
List<ProductCostAmountVO> getOrderDetailProduct(Long shopId, LocalDate tradeDay); List<ProductCostAmountVO> getOrderDetailProduct(Long shopId, LocalDate tradeDay);
} }

View File

@@ -1,17 +1,18 @@
package com.czg.service.order.print; package com.czg.service.order.print;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.czg.account.dto.HandoverRecordDTO; import com.czg.account.dto.HandoverRecordDTO;
import com.czg.account.entity.HandoverRecord;
import com.czg.account.entity.PrintMachine; import com.czg.account.entity.PrintMachine;
import com.czg.account.entity.ShopInfo; import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopInfoService;
import com.czg.order.entity.OrderDetail; import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo; import com.czg.order.entity.OrderInfo;
import com.czg.service.order.enums.OrderStatusEnums; import com.czg.service.order.enums.OrderStatusEnums;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
@@ -20,24 +21,21 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* @author Administrator * @author Administrator
* 接口文档 <a href="https://help.feieyun.com/home/doc/zh;nav=1-1">
*/ */
@Component @Component
@Slf4j @Slf4j
public class FeiPrinter extends PrinterHandler implements PrinterImpl { public class FeiPrinter extends PrinterHandler implements PrinterImpl {
@Resource
private RestTemplate restTemplate;
@Resource
private ShopInfoService shopInfoService;
// API 地址 // API 地址
private static final String URL = "http://api.feieyun.cn/Api/Open/"; private static final String URL = "http://api.feieyun.cn/Api/Open/";
@@ -72,10 +70,8 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
String remark = orderDetail.getRemark(); String remark = orderDetail.getRemark();
String content = buildDishPrintData(false, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), String content = buildDishPrintData(false, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent());
Object o = sendPrintRequest(machine.getAddress(), content, null, "1"); String o = sendPrintRequest(machine.getAddress(), content, null, "1");
printMachineLogService.save(machine, "新订单", content, o); printMachineLogService.save(orderInfo.getId(), machine, "新订单", content, o);
// printMachineLogService.save(machine, "新订单", , );
} }
@@ -84,8 +80,8 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
String remark = orderDetail.getRemark(); String remark = orderDetail.getRemark();
String content = buildDishPrintData(true, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), String content = buildDishPrintData(true, getPickupNum(orderInfo), orderInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),
orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getReturnNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); orderDetail.getProductName(), orderDetail.getSkuName(), orderDetail.getReturnNum(), remark, orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent());
Object o = sendPrintRequest(machine.getAddress(), content, null, "1"); String o = sendPrintRequest(machine.getAddress(), content, null, "1");
printMachineLogService.save(machine, "退款单", content, o); printMachineLogService.save(orderInfo.getId(), machine, "退款单", content, o);
} }
@@ -106,8 +102,8 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
.setRemark(orderInfo.getRemark()) .setRemark(orderInfo.getRemark())
.setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); .setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String string = buildOrderPrintData(printInfoDTO, detailList); String string = buildOrderPrintData(printInfoDTO, detailList);
Object o = sendPrintRequest(machine.getAddress(), string, null, printerNum); String o = sendPrintRequest(machine.getAddress(), string, null, printerNum);
printMachineLogService.save(machine, "退款", string, o); printMachineLogService.save(orderInfo.getId(), machine, "结算", string, o);
} }
@@ -143,15 +139,15 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
} }
String string = buildOrderPrintData(printInfoDTO, detailList); String string = buildOrderPrintData(printInfoDTO, detailList);
Object resp = sendPrintRequest(machine.getAddress(), string, null, printerNum); String resp = sendPrintRequest(machine.getAddress(), string, null, printerNum);
printMachineLogService.save(machine, "结算单", string, resp); printMachineLogService.save(orderInfo.getId(), machine, "结算单", string, resp);
} }
@Override @Override
protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) { protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) {
String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一条新的排号记录\"}"; String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一条新的排号记录\"}";
String data = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime); String data = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime);
Object resp = sendPrintRequest(machine.getAddress(), data, voiceJson, "1"); String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, "1");
printMachineLogService.save(machine, "叫号单", data, resp); printMachineLogService.save(machine, "叫号单", data, resp);
} }
@@ -170,7 +166,7 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
} }
@Override @Override
public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) { public String sendPrintRequest(String address, String metaPrintData, String voiceData, String printerNum) {
log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData); log.info("飞蛾打印机开始发送打印请求, 设备地址: {}, 元数据: {}", address, metaPrintData);
String time = String.valueOf(System.currentTimeMillis() / 1000); String time = String.valueOf(System.currentTimeMillis() / 1000);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
@@ -189,12 +185,68 @@ public class FeiPrinter extends PrinterHandler implements PrinterImpl {
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
String result = restTemplate.postForObject(URL, requestEntity, String.class); String result = restTemplate.postForObject(URL, requestEntity, String.class);
log.info("打印结果: {}", result); log.info("飞鹅打印结果: {}", result);
return (R) result; return result;
} catch (Exception e) { } catch (Exception e) {
log.error("打印请求失败: {}", e.getMessage()); log.error("打印请求失败: {}", e.getMessage());
throw new RuntimeException("飞蛾打印请求失败", e); throw new RuntimeException("飞蛾打印请求失败", e);
} }
} }
/**
* 检查飞鹅打印机 打印任务是否已打印
*
* @param printOrderId 打印订单编号
* @return null-未知错误true-已打印false-未打印
*/
public Boolean checkFPrintStatus(String printOrderId) {
String time = String.valueOf(System.currentTimeMillis() / 1000);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("user", USER);
paramMap.put("stime", time);
paramMap.put("sig", signature(time));
paramMap.put("apiname", "Open_queryOrderState");
paramMap.put("orderid", printOrderId);
Boolean ret;
try {
String resp = HttpUtil.post(URL, paramMap, 1000 * 5);
//成功 {"msg":"ok","ret":0,"data":true,"serverExecutedTime":4}
//失败 {"msg":"ok","ret":0,"data":false,"serverExecutedTime":4}
JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp));
log.info("飞鹅打印机 打印任务状态响应: {}", json);
ret = json.getBool("data");
} catch (Exception e) {
ret = null;
}
return ret;
}
/**
* 检查飞鹅打印机是否在线
*
* @param sn 设备编号
* @return 在线,工作状态正常。/离线。/未知错误
*/
public String checkOnline(String sn) {
String time = String.valueOf(System.currentTimeMillis() / 1000);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("user", USER);
paramMap.put("stime", time);
paramMap.put("sig", signature(time));
paramMap.put("apiname", "Open_queryPrinterStatus");
paramMap.put("sn", sn);
String msg;
try {
String resp = HttpUtil.post(URL, paramMap, 1000 * 5);
//成功 开机 {"msg":"ok","ret":0,"data":"在线,工作状态正常。","serverExecutedTime":4}
//成功 离线 {"msg":"ok","ret":0,"data":"离线。","serverExecutedTime":7}
JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp));
log.info("飞鹅打印机状态响应: {}", json);
msg = json.getStr("data");
} catch (Exception e) {
msg = "未知错误";
}
return msg;
}
} }

View File

@@ -34,6 +34,7 @@ import lombok.ToString;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -49,10 +50,9 @@ public abstract class PrinterHandler {
@Setter @Setter
protected PrinterHandler nextPrinter; protected PrinterHandler nextPrinter;
protected String printerBrand; protected String printerBrand;
// 创建 ThreadLocal 变量
private static final ThreadLocal<String> ERR_MSG = ThreadLocal.withInitial(() -> "");
@Resource
protected RestTemplate restTemplate;
@Resource @Resource
protected OrderDetailService orderDetailService; protected OrderDetailService orderDetailService;
@Resource @Resource
@@ -84,7 +84,11 @@ public abstract class PrinterHandler {
@Getter @Getter
public enum PrintTypeEnum { public enum PrintTypeEnum {
HANDOVER("交班", "handover"), HANDOVER("交班", "handover"),
ORDER("订单", "order"), ONE("菜品", "one"), CALL("叫号", "call"), ONE_AND_ORDER("菜品和结算单同时打印", "oneAndOrder"), PRE_ORDER("预结算单", "preOrder"); ORDER("订单", "order"),
ONE("菜品", "one"),
CALL("叫号", "call"),
ONE_AND_ORDER("菜品和结算单同时打印", "oneAndOrder"),
PRE_ORDER("预结算单", "preOrder");
private final String name; private final String name;
private final String code; private final String code;
@@ -173,10 +177,6 @@ public abstract class PrinterHandler {
wrapper.like(PrintMachine::getPrintType, printType); wrapper.like(PrintMachine::getPrintType, printType);
} }
List<PrintMachine> list = printMachineService.list(wrapper); List<PrintMachine> list = printMachineService.list(wrapper);
// for (PrintMachine item : list) {
// //实际打印以传递的参数为准
// item.setPrintMethod(printMethod);
// }
if (list.isEmpty()) { if (list.isEmpty()) {
log.error("店铺未配置打印机店铺id: {}", shopId); log.error("店铺未配置打印机店铺id: {}", shopId);
return list; return list;
@@ -188,6 +188,7 @@ public abstract class PrinterHandler {
/** /**
* 处理打印 * 处理打印
*
* @param data 传递的数据 * @param data 传递的数据
* @param printTypeEnum order returnOrder preOrder one call handover * @param printTypeEnum order returnOrder preOrder one call handover
*/ */
@@ -341,6 +342,7 @@ public abstract class PrinterHandler {
case PrintTypeEnum.ORDER: case PrintTypeEnum.ORDER:
log.info("准备开始打印订单"); log.info("准备开始打印订单");
if (data instanceof OrderInfo orderInfo) { if (data instanceof OrderInfo orderInfo) {
redisService.set("order:print:" + orderInfo.getId(),"", 180);
List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId())); List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId()));
onlyFrontDesk(machine, false, orderInfo, orderDetailList); onlyFrontDesk(machine, false, orderInfo, orderDetailList);
} else { } else {
@@ -350,6 +352,7 @@ public abstract class PrinterHandler {
case PrintTypeEnum.PRE_ORDER: case PrintTypeEnum.PRE_ORDER:
log.info("准备开始打印预结算订单"); log.info("准备开始打印预结算订单");
if (data instanceof OrderInfo orderInfo) { if (data instanceof OrderInfo orderInfo) {
redisService.set("order:print:" + orderInfo.getId(),"", 180);
List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId())); List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId()));
onlyFrontDesk(machine, true, orderInfo, orderDetailList); onlyFrontDesk(machine, true, orderInfo, orderDetailList);
} else { } else {
@@ -359,6 +362,7 @@ public abstract class PrinterHandler {
case PrintTypeEnum.ONE: case PrintTypeEnum.ONE:
log.info("准备开始打印菜品单"); log.info("准备开始打印菜品单");
if (data instanceof OrderInfo orderInfo) { if (data instanceof OrderInfo orderInfo) {
redisService.set("order:print:" + orderInfo.getId(),"", 180);
List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId())); List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId()));
onlyKitchen(machine, orderInfo, orderDetailList); onlyKitchen(machine, orderInfo, orderDetailList);
} else { } else {
@@ -376,6 +380,7 @@ public abstract class PrinterHandler {
case PrintTypeEnum.ONE_AND_ORDER: case PrintTypeEnum.ONE_AND_ORDER:
log.info("准备开始打印菜品以及结算单"); log.info("准备开始打印菜品以及结算单");
if (data instanceof OrderInfo orderInfo) { if (data instanceof OrderInfo orderInfo) {
redisService.set("order:print:" + orderInfo.getId(),"", 180);
List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId())); List<OrderDetail> orderDetailList = orderDetailService.list(new QueryWrapper().eq(OrderDetail::getOrderId, orderInfo.getId()));
switch (machine.getPrintMethod()) { switch (machine.getPrintMethod()) {
case "all": case "all":
@@ -448,17 +453,7 @@ public abstract class PrinterHandler {
log.info("台位费商品,不打印"); log.info("台位费商品,不打印");
return; return;
} }
// ProdSku sku = prodSkuService.getById(item.getSkuId());
// if (isTemporary == 0 && sku == null) {
// log.error("商品不存在, id: {}", item.getSkuId());
// return;
// } else if (isTemporary == 1) {
// sku = new ProdSku();
// }
// String remark = StrUtil.isNotBlank(sku.getSpecInfo()) ? sku.getSpecInfo() : "";
// item.setRemark(remark);
if (item.getReturnNum().compareTo(BigDecimal.ZERO) > 0) { if (item.getReturnNum().compareTo(BigDecimal.ZERO) > 0) {
returnDishesPrint(orderInfo, item, machine); returnDishesPrint(orderInfo, item, machine);
} }
@@ -469,7 +464,8 @@ public abstract class PrinterHandler {
// 保存已打印信息 // 保存已打印信息
OrderDetail orderDetail = detailMap.get(item.getId()); OrderDetail orderDetail = detailMap.get(item.getId());
redisService.set(RedisCst.getPrintOrderDetailKey(orderInfo.getId(), item.getId()), JSONObject.toJSONString(new PrintDetailInfo().setPrint(item.getIsPrint() == 1).setDetailId(item.getId()) redisService.set(RedisCst.getPrintOrderDetailKey(orderInfo.getId(), item.getId()),
JSONObject.toJSONString(new PrintDetailInfo().setPrint(item.getIsPrint() == 1).setDetailId(item.getId())
.setPrintNum(orderDetail.getNum()).setPrintReturnNum(orderDetail.getReturnNum())), 3600 * 24); .setPrintNum(orderDetail.getNum()).setPrintReturnNum(orderDetail.getReturnNum())), 3600 * 24);
}); });

View File

@@ -9,7 +9,6 @@ import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.czg.account.dto.HandoverRecordDTO; import com.czg.account.dto.HandoverRecordDTO;
import com.czg.account.entity.HandoverRecord;
import com.czg.order.entity.OrderDetail; import com.czg.order.entity.OrderDetail;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@@ -69,10 +68,9 @@ public interface PrinterImpl {
* @param metaPrintData 打印元数据 * @param metaPrintData 打印元数据
* @param voiceData 语音信息 * @param voiceData 语音信息
* @param printNum 打印联数 * @param printNum 打印联数
* @param <R> 返回数据类型
* @return 打印结果 * @return 打印结果
*/ */
<R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum); String sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum);
/** /**
* 获取当前打印机标签信息 * 获取当前打印机标签信息
@@ -560,12 +558,6 @@ public interface PrinterImpl {
return str; return str;
} }
public static void main(String[] args) {
System.out.println("水煮肉片".length());
System.out.println(StrUtil.repeat(' ', 8));
System.out.println(StrUtil.fillAfter("水煮肉片", ' ', 21));
}
/** /**
* 获取填充字符串, 并且换行 * 获取填充字符串, 并且换行
* *

View File

@@ -4,11 +4,10 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.czg.account.dto.HandoverRecordDTO; import com.czg.account.dto.HandoverRecordDTO;
import com.czg.account.entity.HandoverRecord;
import com.czg.account.entity.PrintMachine; import com.czg.account.entity.PrintMachine;
import com.czg.account.entity.ShopInfo; import com.czg.account.entity.ShopInfo;
import com.czg.account.service.ShopInfoService;
import com.czg.order.entity.OrderDetail; import com.czg.order.entity.OrderDetail;
import com.czg.order.entity.OrderInfo; import com.czg.order.entity.OrderInfo;
import com.czg.service.order.enums.OrderStatusEnums; import com.czg.service.order.enums.OrderStatusEnums;
@@ -20,16 +19,14 @@ import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
/** /**
* 云享印打印机 * 博实结-云享印打印机
* * 接口文档 <a href="https://bsj2.yuque.com/bsj/izhmfn/rr8b5g?#ZbE6s">
* @author Administrator * @author Administrator
*/ */
@Slf4j @Slf4j
@@ -45,12 +42,6 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
//APPSECRET //APPSECRET
private static final String APP_SECRET = "2022bsjZF544GAH"; private static final String APP_SECRET = "2022bsjZF544GAH";
@Resource
private ShopInfoService shopInfoService;
@Resource
private RestTemplate restTemplate;
public YxyPrinter() { public YxyPrinter() {
super("云想印"); super("云想印");
} }
@@ -75,7 +66,7 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
@Override @Override
public <R> R sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) { public String sendPrintRequest(String address, String metaPrintData, String voiceData, String printNum) {
log.info("开始请求云享印,请求数据:{}, {}", voiceData, metaPrintData); log.info("开始请求云享印,请求数据:{}, {}", voiceData, metaPrintData);
//设备名称 //设备名称
//行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息 //行为方式 1:只打印数据 2:只播放信息 3:打印数据并播放信息
@@ -87,10 +78,9 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
String time = String.valueOf(System.currentTimeMillis()); String time = String.valueOf(System.currentTimeMillis());
String uuid = UUID.randomUUID().toString(); String uuid = UUID.randomUUID().toString();
Map<String, String> param = getToken(time, uuid);
//参数 //参数
MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>(); MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
multiValueMap.add("token", param.get("TOKEN")); multiValueMap.add("token", getToken(time, uuid));
multiValueMap.add("devName", address); multiValueMap.add("devName", address);
multiValueMap.add("actWay", 3); multiValueMap.add("actWay", 3);
multiValueMap.add("cn", printNum); multiValueMap.add("cn", printNum);
@@ -108,7 +98,7 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
httpEntity, String.class); httpEntity, String.class);
log.info("请求云享印成功,响应数据: {}", httpResponse); log.info("请求云享印成功,响应数据: {}", httpResponse);
return (R) httpResponse; return httpResponse;
} }
@Override @Override
@@ -116,10 +106,8 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
String buildDishPrintData = buildDishPrintData(false, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(), String buildDishPrintData = buildDishPrintData(false, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(),
orderDetail.getNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); orderDetail.getNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent());
String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; String resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1");
Object resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); printMachineLogService.save(orderInfo.getId(), machine, "新订单", buildDishPrintData, resp);
// sendPrintRequest(voiceJson, 3, 1, machine.getAddress(), data);
printMachineLogService.save(machine, "新订单", buildDishPrintData, resp);
} }
@@ -127,10 +115,9 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
protected void returnDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) { protected void returnDishesPrint(OrderInfo orderInfo, OrderDetail orderDetail, PrintMachine machine) {
String buildDishPrintData = buildDishPrintData(true, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(), String buildDishPrintData = buildDishPrintData(true, getPickupNum(orderInfo), DateUtil.format(orderDetail.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), orderDetail.getProductName(), orderDetail.getSkuName(),
orderDetail.getReturnNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent()); orderDetail.getReturnNum(), orderDetail.getRemark(), orderDetail.getProGroupInfo(), orderDetail.getId(), orderDetail.isUrgent());
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}"; String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
Object resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1"); String resp = sendPrintRequest(machine.getAddress(), buildDishPrintData, voiceJson, "1");
printMachineLogService.save(machine, "退款单", buildDishPrintData, resp); printMachineLogService.save(orderInfo.getId(), machine, "退款单", buildDishPrintData, resp);
} }
@@ -141,18 +128,17 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
.setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.format(orderInfo.getCreateTime(), "yyyy-MM-dd HH:mm:ss")).setOperator("【POS-1】001").setPayAmount(orderInfo.getPayAmount().toPlainString()) .setOrderNo(orderInfo.getOrderNo()).setTradeDate(DateUtil.format(orderInfo.getCreateTime(), "yyyy-MM-dd HH:mm:ss")).setOperator("【POS-1】001").setPayAmount(orderInfo.getPayAmount().toPlainString())
.setOriginalAmount(orderInfo.getOriginAmount().toPlainString()).setReturn(isReturn(orderInfo)) .setOriginalAmount(orderInfo.getOriginAmount().toPlainString()).setReturn(isReturn(orderInfo))
.setBalance(balance).setPayType((ObjectUtil.isEmpty(orderInfo.getPayType()) || ObjectUtil.isNull(orderInfo.getPayType()) ? "" : orderInfo.getPayType())).setIntegral("0") .setBalance(balance).setPayType((ObjectUtil.isEmpty(orderInfo.getPayType()) || ObjectUtil.isNull(orderInfo.getPayType()) ? "" : orderInfo.getPayType())).setIntegral("0")
.setOutNumber(orderInfo.getTakeCode()).setPrintTitle("结算") .setOutNumber(orderInfo.getTakeCode()).setPrintTitle("退款")
.setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString()); .setRemark(orderInfo.getRemark()).setDiscountAmount(orderInfo.getOriginAmount().subtract(orderInfo.getPayAmount()).toPlainString());
String data = buildOrderPrintData(printInfoDTO, detailList); String data = buildOrderPrintData(printInfoDTO, detailList);
String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
String printerNum = "1"; String printerNum = "1";
if (StrUtil.isNotBlank(machine.getPrintQty())) { if (StrUtil.isNotBlank(machine.getPrintQty())) {
printerNum = machine.getPrintQty().split("\\^")[1]; printerNum = machine.getPrintQty().split("\\^")[1];
} }
String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum); String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum);
printMachineLogService.save(machine, "新订", data, resp); printMachineLogService.save(orderInfo.getId(), machine, "退款", data, resp);
} }
@@ -183,23 +169,28 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
String data = buildOrderPrintData(printInfoDTO, detailList); String data = buildOrderPrintData(printInfoDTO, detailList);
String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}"; String voiceJson = "{\"PbizType\":\"2\",\"content\":\"您有一笔新的订单,请及时处理\"}";
// String voiceJson = "{\"bizType\":\"2\",\"content\":\"\"}";
String printerNum = "1"; String printerNum = "1";
if (StrUtil.isNotBlank(machine.getPrintQty())) { if (StrUtil.isNotBlank(machine.getPrintQty())) {
printerNum = machine.getPrintQty().split("\\^")[1]; printerNum = machine.getPrintQty().split("\\^")[1];
} }
String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum); String resp = sendPrintRequest(machine.getAddress(), data, voiceJson, printerNum);
// printMachineLogService.save(machine, printType, data, resp); printMachineLogService.save(orderInfo.getId(), machine, "结算单", data, resp);
printMachineLogService.save(machine, "新订单", data, resp);
} }
/**
* 叫号单打印
*/
@Override @Override
protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) { protected void callNumPrint(PrintMachine machine, String callNum, String shopName, String tableName, String tableNote, String preNum, String codeUrl, LocalDateTime takeTime, String shopNote) {
String resp = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime); String resp = buildCallTicketData(shopName, tableName, callNum, preNum, codeUrl, shopNote, takeTime);
sendPrintRequest(machine.getAddress(), resp, null, "1"); sendPrintRequest(machine.getAddress(), resp, null, "1");
} }
/**
* 交班单打印
*/
@Override @Override
protected void handoverPrint(PrintMachine machine, HandoverRecordDTO record) { protected void handoverPrint(PrintMachine machine, HandoverRecordDTO record) {
String string = buildHandoverData(record); String string = buildHandoverData(record);
@@ -214,9 +205,9 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
* @param requestId 请求ID自定义 * @param requestId 请求ID自定义
* @return token信息 * @return token信息
*/ */
private static Map<String, String> getToken(String timestamp, String requestId) { private static String getToken(String timestamp, String requestId) {
StringBuilder token = new StringBuilder(); StringBuilder token = new StringBuilder();
StringBuilder encode = new StringBuilder(); // StringBuilder encode = new StringBuilder();
SortedMap<String, Object> map = new TreeMap<>(); SortedMap<String, Object> map = new TreeMap<>();
map.put("appId", APP_ID); map.put("appId", APP_ID);
map.put("timestamp", timestamp); map.put("timestamp", timestamp);
@@ -226,13 +217,38 @@ public class YxyPrinter extends PrinterHandler implements PrinterImpl {
String key = next.getKey(); String key = next.getKey();
Object value = next.getValue(); Object value = next.getValue();
token.append(key).append(value); token.append(key).append(value);
encode.append(key).append("=").append(value).append("&"); // encode.append(key).append("=").append(value).append("&");
} }
Map<String, String> finalMap = new HashMap<>(); // Map<String, String> finalMap = new HashMap<>();
finalMap.put("ENCODE", encode.toString()); // finalMap.put("ENCODE", encode.toString());
finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase()); // finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase());
return finalMap; // return finalMap;
return SecureUtil.md5(token + APP_SECRET).toUpperCase();
} }
/**
* 检查打印状态
*
* @param devName 设备名称,(唯一) 对应配置表中的address字段即IP地址/打印机编号)
* @param taskId 打印任务id用于复查打印状态云想印=orderId
*/
public String checkPrintStatus(String devName, String taskId) {
String time = String.valueOf(System.currentTimeMillis());
String uuid = UUID.randomUUID().toString();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("devName", devName);
paramMap.put("orderId", taskId);
paramMap.put("token", getToken(time, uuid));
paramMap.put("appId", APP_ID);
paramMap.put("timestamp", time);
paramMap.put("requestId", uuid);
paramMap.put("userCode", USER_CODE);
String s = HttpUtil.get("https://ioe.car900.com/v1/openApi/dev/findOrder.json", paramMap, 1000 * 5);
log.info("云想印打印机:{},任务:{}状态:{}", devName, taskId, s);
return s;
}
} }

View File

@@ -2,6 +2,8 @@ package com.czg.service.order.service;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.czg.dto.req.AggregateMerchantDto; import com.czg.dto.req.AggregateMerchantDto;
import com.czg.service.order.dto.AggregateMerchantVO; import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.dto.MerchantQueryDTO;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService; import com.mybatisflex.core.service.IService;
import com.czg.order.entity.ShopDirectMerchant; import com.czg.order.entity.ShopDirectMerchant;
@@ -22,10 +24,12 @@ public interface ShopDirectMerchantService extends IService<ShopDirectMerchant>
*/ */
JSONObject getInfoByImg(String url, String type) throws Exception; JSONObject getInfoByImg(String url, String type) throws Exception;
Page<ShopDirectMerchant> getEntryList(MerchantQueryDTO queryParam);
/** /**
* 获取进件信息 * 获取进件信息
*/ */
AggregateMerchantVO getEntry(Long shopId); AggregateMerchantVO getEntry(Long shopId, String licenceNo);
/** /**
* 申请进件 * 申请进件

View File

@@ -35,7 +35,7 @@ public class DistributionPayServiceImpl implements DistributionPayService {
private final BigDecimal MONEY_RATE = new BigDecimal("100"); private final BigDecimal MONEY_RATE = new BigDecimal("100");
@Resource @Resource
private OrderPaymentService orderPaymentService; private OrderPaymentService paymentService;
@Resource @Resource
private MkDistributionConfigService configService; private MkDistributionConfigService configService;
@Resource @Resource
@@ -71,7 +71,7 @@ public class DistributionPayServiceImpl implements DistributionPayService {
.setPayType(PayTypeConstants.PayType.PAY) .setPayType(PayTypeConstants.PayType.PAY)
.setOrderNo(payParam.getPlatformType() + IdUtil.getSnowflakeNextId()) .setOrderNo(payParam.getPlatformType() + IdUtil.getSnowflakeNextId())
.setAmount(isRecharge ? payParam.getAmount() : detail.getPayAmount()); .setAmount(isRecharge ? payParam.getAmount() : detail.getPayAmount());
orderPaymentService.save(orderPayment); paymentService.save(orderPayment);
InitInfo initInfo = new InitInfo().setConfig(detail); InitInfo initInfo = new InitInfo().setConfig(detail);
if (isRecharge) { if (isRecharge) {

View File

@@ -1516,6 +1516,9 @@ public class OrderInfoCustomServiceImpl implements OrderInfoCustomService {
@Override @Override
public Boolean printOrder(Long shopId, OrderInfoPrintDTO orderInfoPrintDTO) { public Boolean printOrder(Long shopId, OrderInfoPrintDTO orderInfoPrintDTO) {
if (redisService.hasKey("order:print:" + orderInfoPrintDTO.getId())) {
throw new CzgException("网络打印机正在尝试打印中。如需重打,请稍后再试!");
}
OrderInfo orderInfo = orderInfoService.getOne(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getId, orderInfoPrintDTO.getId())); OrderInfo orderInfo = orderInfoService.getOne(new QueryWrapper().eq(OrderInfo::getShopId, shopId).eq(OrderInfo::getId, orderInfoPrintDTO.getId()));
if (orderInfo == null) { if (orderInfo == null) {
throw new CzgException("订单信息不存在"); throw new CzgException("订单信息不存在");

View File

@@ -22,10 +22,10 @@ public class OrderPaymentServiceImpl extends ServiceImpl<OrderPaymentMapper, Ord
@Override @Override
public BigDecimal countMemberInAmount(Long shopId, Long shopUserId) { public BigDecimal countMemberInAmount(Long shopId, Long shopUserId) {
return getOneAs(QueryWrapper.create().select("IFNULL(sum(amount), 0) as total_amount") return getOneAs(QueryWrapper.create().select("IFNULL(sum(amount), 0) as total_amount")
.eq(OrderPayment::getShopId, 143) .eq(OrderPayment::getShopId, shopId)
.eq(OrderPayment::getSourceType, PayTypeConstants.SourceType.MEMBER_IN) .eq(OrderPayment::getSourceType, PayTypeConstants.SourceType.MEMBER_IN)
.eq(OrderPayment::getPayType, PayTypeConstants.PayType.PAY) .eq(OrderPayment::getPayType, PayTypeConstants.PayType.PAY)
.eq(OrderPayment::getSourceId, 127452) .eq(OrderPayment::getSourceId, shopUserId)
.eq(OrderPayment::getPayStatus, PayTypeConstants.PayStatus.SUCCESS), BigDecimal.class); .eq(OrderPayment::getPayStatus, PayTypeConstants.PayStatus.SUCCESS), BigDecimal.class);
} }
} }

View File

@@ -2,32 +2,36 @@ package com.czg.service.order.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapProxy; import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil; import com.alibaba.fastjson2.JSONObject;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.czg.account.entity.PrintMachine; import com.czg.account.entity.PrintMachine;
import com.github.pagehelper.PageHelper; import com.czg.config.RedisCst;
import com.github.pagehelper.PageInfo; import com.czg.market.service.OrderInfoService;
import com.mybatisflex.core.query.QueryWrapper; import com.czg.order.entity.OrderInfo;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.czg.order.entity.PrintMachineLog; import com.czg.order.entity.PrintMachineLog;
import com.czg.order.service.PrintMachineLogService; import com.czg.order.service.PrintMachineLogService;
import com.czg.service.RedisService;
import com.czg.service.order.mapper.PrintMachineLogMapper; import com.czg.service.order.mapper.PrintMachineLogMapper;
import com.czg.service.order.print.FeiPrinter;
import com.czg.service.order.print.YxyPrinter;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils; import org.springframework.context.annotation.Lazy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* 店铺小票打印记录ServiceImpl * 店铺小票打印记录ServiceImpl
@@ -39,138 +43,46 @@ import java.util.*;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMapper, PrintMachineLog> implements PrintMachineLogService { public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMapper, PrintMachineLog> implements PrintMachineLogService {
//请求地址
private static final String URL_STR = "https://ioe.car900.com/v1/openApi/dev/customPrint.json";
//APPID
private static final String APP_ID = "ZF544";
//USERCODE
private static final String USER_CODE = "ZF544";
//APPSECRET
private static final String APP_SECRET = "2022bsjZF544GAH";
public static final String URL = "http://api.feieyun.cn/Api/Open/";//不需要修改 @Resource
private OrderInfoService orderInfoService;
@Lazy
@Resource
private YxyPrinter yxyPrinter;
@Lazy
@Resource
private FeiPrinter feiPrinter;
@Resource
private RedisService redisService;
public static final String USER = "chaozhanggui2022@163.com";//*必填*:账号名 Map<Integer, String> yxxStatusMap = Map.of(
public static final String UKEY = "UfWkhXxSkeSSscsU";//*必填*: 飞鹅云后台注册账号后生成的UKEY 【备注这不是填打印机的KEY】 0, "离线(设备上线后自动补打)",
public static final String SN = "960238952";//*必填*打印机编号必须要在管理后台里添加打印机或调用API接口添加之后才能调用API 1, "在线",
/** 2, "获取失败",
* 获取TOKEN值 3, "未激活",
* 4, "设备已禁用");
* @param timestamp 时间戳13位
* @param requestId 请求ID自定义
* @return
*/
private static Map<String, String> getToken(String timestamp, String requestId) {
StringBuilder token = new StringBuilder();
StringBuilder encode = new StringBuilder();
SortedMap<String, Object> map = new TreeMap<>();
map.put("appId", APP_ID);
map.put("timestamp", timestamp);
map.put("requestId", requestId);
map.put("userCode", USER_CODE);
for (Map.Entry<String, Object> next : map.entrySet()) {
String key = next.getKey();
Object value = next.getValue();
token.append(key).append(value);
encode.append(key).append("=").append(value).append("&");
}
System.out.println("token" + token);
Map<String, String> finalMap = new HashMap<>();
finalMap.put("ENCODE", encode.toString());
System.out.println("+++++++++++++++" + token + APP_SECRET);
finalMap.put("TOKEN", SecureUtil.md5(token + APP_SECRET).toUpperCase());
return finalMap;
}
/**
* 检查打印状态
*
* @param devName 设备名称,(唯一) 对应配置表中的address字段即IP地址/打印机编号)
* @param taskId 打印任务id用于复查打印状态云想印=orderId
* @return
*/
public static String checkPrintStatus(String devName, String taskId) {
String time = String.valueOf(System.currentTimeMillis());
String uuid = UUID.randomUUID().toString();
Map<String, String> param = getToken(time, uuid); @Async
String token = param.get("TOKEN"); @Override
Map<String, Object> paramMap = new HashMap<>(); public void save(PrintMachine config, String bizType, String printContent, String respJson) {
if (config == null) {
return;
}
save(null, config, bizType, printContent, respJson);
}
paramMap.put("devName", devName);
paramMap.put("orderId", taskId);
paramMap.put("token", token);
paramMap.put("appId", APP_ID);
paramMap.put("timestamp", time);
paramMap.put("requestId", uuid);
paramMap.put("userCode", USER_CODE);
return HttpUtil.get("https://ioe.car900.com/v1/openApi/dev/findOrder.json", paramMap, 1000 * 5);
}
private static String signature(String USER, String UKEY, String STIME) {
return DigestUtils.sha1Hex(USER + UKEY + STIME);
}
/**
* 检查飞鹅打印机打印任务是否已打印
*
* @param printOrderId 打印订单编号
* @return null-未知错误true-已打印false-未打印
*/
public static Boolean checkFPrintStatus(String printOrderId) {
String STIME = String.valueOf(System.currentTimeMillis() / 1000);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("user", USER);
paramMap.put("stime", STIME);
paramMap.put("sig", signature(USER, UKEY, STIME));
paramMap.put("apiname", "Open_queryOrderState");
paramMap.put("orderid", printOrderId);
Boolean ret;
try {
String resp = HttpUtil.post(URL, paramMap, 1000 * 5);
//成功 {"msg":"ok","ret":0,"data":true,"serverExecutedTime":4}
//失败 {"msg":"ok","ret":0,"data":false,"serverExecutedTime":4}
JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp));
ret = json.getBool("data");
} catch (Exception e) {
ret = null;
}
return ret;
}
/**
* 检查飞鹅打印机是否在线
*
* @param sn 设备编号
* @return 在线,工作状态正常。/离线。/未知错误
*/
public static String checkOnline(String sn) {
String STIME = String.valueOf(System.currentTimeMillis() / 1000);
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("user", USER);
paramMap.put("stime", STIME);
paramMap.put("sig", signature(USER, UKEY, STIME));
paramMap.put("apiname", "Open_queryPrinterStatus");
paramMap.put("sn", sn);
String msg;
try {
String resp = HttpUtil.post(URL, paramMap, 1000 * 5);
//成功 开机 {"msg":"ok","ret":0,"data":"在线,工作状态正常。","serverExecutedTime":4}
//成功 离线 {"msg":"ok","ret":0,"data":"离线。","serverExecutedTime":7}
JSONObject json = JSONUtil.parseObj(UnicodeUtil.toString(resp));
msg = json.getStr("data");
} catch (Exception e) {
msg = "未知错误";
}
return msg;
}
/** /**
* 保存打印记录 * 保存打印记录
* *
* @param orderId 订单Id
* @param config 打印机配置 * @param config 打印机配置
* @param bizType 业务类型 * @param bizType 业务类型
* @param printContent 打印内容 * @param printContent 打印内容
* @param respJson 打印机响应结果 * @param respJson 打印机响应结果
*/ */
@Async @Async
public void save(PrintMachine config, String bizType, String printContent, Object respJson) { @Override
public void save(Long orderId, PrintMachine config, String bizType, String printContent, String respJson) {
if (config == null) { if (config == null) {
return; return;
} }
@@ -179,51 +91,41 @@ public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMappe
int failFlag = 0; int failFlag = 0;
String respCode = "0"; String respCode = "0";
String respMsg = "打印中"; String respMsg = "打印中";
JSONObject resp = JSONObject.parseObject(respJson);
Map<Integer, String> yxxStatusMap = MapUtil.builder(0, "离线(设备上线后自动补打)").put(1, "在线").put(2, "获取失败").put(3, "未激活").put(4, "设备已禁用").build();
// 云想印 // 云想印
if ("".equals(config.getContentType())) { if ("".equals(config.getContentType())) {
cn.hutool.json.JSONObject resp = JSONUtil.parseObj(respJson); int code = resp.getIntValue("code");
int code = resp.getInt("code"); JSONObject respData = resp.getJSONObject("data");
cn.hutool.json.JSONObject data = resp.getJSONObject("data").getJSONObject("data"); JSONObject data = respData.getJSONObject("data");
//设备状态0: 离线, 1: 在线, 2: 获取失败, 3:未激活, 4:设备已禁用 //设备状态0: 离线, 1: 在线, 2: 获取失败, 3:未激活, 4:设备已禁用
int status = data.getInt("status"); int status = data.getIntValue("status");
if (code != 0) { if (code != 0) {
failFlag = 1; failFlag = 1;
respCode = code + ""; respCode = code + "";
respMsg = resp.getStr("msg"); respMsg = resp.getString("msg");
} else if (status != 1) { } else if (status != 1) {
failFlag = 1; failFlag = 1;
respCode = code + ""; respCode = code + "";
respMsg = status + "_" + yxxStatusMap.get(status); respMsg = status + "_" + yxxStatusMap.get(status);
} }
if (code == 0) { if (code == 0) {
String taskId = resp.getJSONObject("data").getStr("orderId"); entity.setTaskId(respData.getString("orderId"));
entity.setTaskId(taskId);
} }
// 飞鹅云打印机暂时没有适配先return不做打印记录 // 飞鹅云打印机暂时没有适配先return不做打印记录
} else if ("飞鹅".equals(config.getContentType())) { } else if ("飞鹅".equals(config.getContentType())) {
cn.hutool.json.JSONObject resp = JSONUtil.parseObj(respJson); int ret = resp.getIntValue("ret");
int ret = resp.getInt("ret");
if (ret != 0) { if (ret != 0) {
failFlag = 1; failFlag = 1;
respCode = ret + ""; respCode = ret + "";
respMsg = resp.getStr("msg"); respMsg = resp.getString("msg");
} else { } else {
String printOrderId = resp.getStr("data"); entity.setTaskId(resp.getString("data"));
entity.setTaskId(printOrderId);
} }
} else { } else {
// 其他打印机暂时没有适配先return不做打印记录 // 其他打印机暂时没有适配先return不做打印记录
return; return;
} }
entity.setBizType(bizType); entity.setBizType(bizType);
// entity.setCreateUserId(config.getCurrentUserId());
// entity.setCreateUserName(config.getCurrentUserName());
// if (StrUtil.isNotBlank(config.getCurrentUserNickName())) {
// entity.setCreateUserName(StrUtil.concat(true, config.getCurrentUserNickName(), " | ", config.getCurrentUserName()));
// }
entity.setPrintContent(printContent); entity.setPrintContent(printContent);
entity.setCreateTime(DateUtil.date().toLocalDateTime()); entity.setCreateTime(DateUtil.date().toLocalDateTime());
if (failFlag == 0) { if (failFlag == 0) {
@@ -233,66 +135,213 @@ public class PrintMachineLogServiceImpl extends ServiceImpl<PrintMachineLogMappe
entity.setRespCode(respCode); entity.setRespCode(respCode);
entity.setRespMsg(respMsg); entity.setRespMsg(respMsg);
super.save(entity); super.save(entity);
ThreadUtil.execAsync(() -> checkPrintStatus(orderId, config, entity));
}
// 云想印 /**
if ("云享印".equals(config.getContentType())) { * 类级别成员变量基于虚拟线程的固定大小5定时线程池
// 延迟3ms复查打印状态 (用户可以根据设备信息查询到当前设备的在线情况该接口只能提供参考设备的离线状态是在设备离线3分钟后才会生效) * // Java 21+ 虚拟线程工厂,支持命名
ThreadUtil.safeSleep(1000 * 5); */
String jsonStr = checkPrintStatus(config.getAddress(), entity.getTaskId()); private final ScheduledExecutorService virtualThreadScheduler = Executors.newScheduledThreadPool(
cn.hutool.json.JSONObject resp = JSONUtil.parseObj(jsonStr); 5,
int code = resp.getInt("code"); Thread.ofVirtual().name("print-query-vt-", 0).factory()
);
/**
* 打印机状态查询(解决 retryFuture 爆红问题 + 虚拟线程 + 轮询重试)
*
* @param orderId 订单Id
* @param config 打印机配置
* @param entity 打印日志实体
*/
public void checkPrintStatus(Long orderId, PrintMachine config, PrintMachineLog entity) {
// 最大重试次数
int maxRetryTimes = 5;
AtomicInteger executedTimes = new AtomicInteger(0);
// 原子引用包装ScheduledFuture用于后续取消轮询
AtomicReference<ScheduledFuture<?>> retryFutureRef = new AtomicReference<>();
// 核心查询任务(修正后,逻辑内聚)
Runnable printQueryTask = () -> {
int currentTimes = executedTimes.incrementAndGet();
boolean isPrintSuccess = false;
boolean isLastTask = false;
try {
// 1. 云想印打印机状态查询
if ("云想印".equals(config.getContentType())) {
String jsonStr = yxyPrinter.checkPrintStatus(config.getAddress(), entity.getTaskId());
log.info("云想印打印状态查询结果(第{}次,虚拟线程:{}{}",
currentTimes, Thread.currentThread().getName(), jsonStr);
JSONObject resp = JSONObject.parseObject(jsonStr);
int code = resp.getIntValue("code");
if (code == 0) { if (code == 0) {
cn.hutool.json.JSONObject data = resp.getJSONObject("data"); JSONObject data = resp.getJSONObject("data");
boolean status = data.containsKey("status"); if (data.containsKey("status")) {
if (!status) { isPrintSuccess = data.getBooleanValue("status", false);
return; updatePrintLogEntity(entity, isPrintSuccess);
} }
boolean success = data.getBool("status", false);
if (entity.getFailFlag() == 0 && success) {
entity.setFailFlag(0);
entity.setRespMsg("打印成功");
entity.setPrintTime(entity.getCreateTime());
} else if (entity.getFailFlag() == 1 && success) {
entity.setFailFlag(0);
entity.setPrintTime(DateUtil.date().toLocalDateTime());
entity.setRespMsg("打印成功");
// 如果设备在线 and 休眠5秒后查询结果是未打印即视为设备已离线云端3分钟后才会同步到离线信息
} else if (entity.getFailFlag() == 0 && !success) {
entity.setFailFlag(1);
entity.setPrintTime(null);
entity.setRespMsg("0_离线(设备上线后自动补打)");
} else {
entity.setFailFlag(1);
entity.setPrintTime(null);
entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", entity.getRespMsg()));
} }
super.updateById(entity);
} }
// 飞鹅云打印机 // 2. 飞鹅云打印机状态查询
} else if ("飞鹅".equals(config.getContentType())) { else if ("飞鹅".equals(config.getContentType())) {
ThreadUtil.safeSleep(1000 * 5); Boolean success = feiPrinter.checkFPrintStatus(entity.getTaskId());
Boolean success = checkFPrintStatus(entity.getTaskId());
if (success == null) { if (success == null) {
entity.setFailFlag(1); entity.setFailFlag(1);
entity.setRespMsg("打印失败,未知错误"); entity.setRespMsg("打印失败,未知错误");
entity.setPrintTime(null);
} else if (success) { } else if (success) {
entity.setFailFlag(0); isPrintSuccess = true;
entity.setPrintTime(DateUtil.date().toLocalDateTime()); updatePrintLogEntity(entity, true);
entity.setRespMsg("打印成功");
} else { } else {
String msg = checkOnline(entity.getAddress()); String msg = feiPrinter.checkOnline(entity.getAddress());
if (msg.indexOf("在线,工作状态正常") > 0) { if (msg.indexOf("在线,工作状态正常") > 0) {
entity.setFailFlag(0); isPrintSuccess = true;
entity.setPrintTime(DateUtil.date().toLocalDateTime()); updatePrintLogEntity(entity, true);
entity.setRespMsg("打印成功");
} else { } else {
isPrintSuccess = false;
entity.setFailFlag(1); entity.setFailFlag(1);
entity.setPrintTime(null); entity.setPrintTime(null);
entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg)); entity.setRespMsg(StrUtil.concat(true, "打印失败,", "_", msg));
} }
} }
super.updateById(entity); } else {
log.info("打印类型为其他类型,终止打印状态查询轮询任务");
ScheduledFuture<?> future = retryFutureRef.get();
if (future != null && !future.isCancelled()) {
boolean cancelSuccess = future.cancel(false); // 取消后续轮询(不中断当前任务)
log.info("其他打印类型,取消轮询任务:{}", cancelSuccess ? "成功" : "失败");
} }
} }
// 3. 打印成功:取消后续轮询任务
if (isPrintSuccess) {
isLastTask = true;
ScheduledFuture<?> future = retryFutureRef.get();
if (future != null && !future.isCancelled()) {
future.cancel(false); // 不中断当前任务,仅取消后续任务
}
return;
}
// 4. 达到最大重试次数:取消后续轮询任务
if (currentTimes >= maxRetryTimes) {
isLastTask = true;
ScheduledFuture<?> future = retryFutureRef.get();
if (future != null && !future.isCancelled()) {
future.cancel(false);
}
}
} catch (Exception e) {
log.error("第{}次打印机状态查询异常(虚拟线程:{}",
currentTimes, Thread.currentThread().getName(), e);
// 异常时达到最大重试次数,同样取消任务
if (currentTimes >= maxRetryTimes) {
ScheduledFuture<?> future = retryFutureRef.get();
if (future != null && !future.isCancelled()) {
boolean cancelSuccess = future.cancel(false);
log.info("查询异常且达到最大重试次数,取消轮询任务:{}", cancelSuccess ? "成功" : "失败");
}
}
} finally {
//仅当是最后一次任务时,才执行更新操作
if (isLastTask) {
super.updateById(entity);
updateOrderEntity(orderId, config, isPrintSuccess);
}
}
};
// 修正统一使用scheduleAtFixedRate避免任务重复执行
// 首次延迟10秒执行后续每隔30秒执行一次符合原逻辑的首次查询延迟后续轮询间隔
ScheduledFuture<?> retryFuture = virtualThreadScheduler.scheduleAtFixedRate(
printQueryTask,
10,
30,
TimeUnit.SECONDS
);
// 修正先赋值AtomicReference再让任务可能执行避免线程安全隐患
retryFutureRef.set(retryFuture);
// 修正:关闭钩子仅注册一次(通过静态代码块或类初始化时注册,避免重复注册)
registerShutdownHookOnce();
}
/**
* 更新状态
*/
private void updateOrderEntity(Long orderId, PrintMachine config, boolean isPrintSuccess) {
redisService.runFunAndCheckKey(() -> {
OrderInfo orderInfo = orderInfoService.getOne(query().select(OrderInfo::getPrintStatus).eq(OrderInfo::getId, orderId));
if (orderInfo == null) {
orderInfo = new OrderInfo();
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", config.getId());
jsonObject.put("name", config.getName());
jsonObject.put("time", LocalDateTimeUtil.formatNormal(LocalDateTime.now()));
orderInfo.upPrintStatus(jsonObject, isPrintSuccess);
orderInfoService.update(orderInfo, query().eq(OrderInfo::getId, orderId));
return orderInfo;
}, RedisCst.getLockKey("UP_ORDER_PRINT", orderId));
redisService.del("order:print:" + orderId);
}
/**
* 统一更新打印日志实体
*
* @param entity 打印日志实体
* @param isPrintSuccess 是否打印成功
*/
private void updatePrintLogEntity(PrintMachineLog entity, boolean isPrintSuccess) {
if (isPrintSuccess) {
entity.setFailFlag(0);
entity.setRespMsg("打印成功");
entity.setPrintTime(entity.getFailFlag() == 0 ? entity.getCreateTime() : DateUtil.date().toLocalDateTime());
} else {
entity.setFailFlag(1);
entity.setPrintTime(null);
if (entity.getFailFlag() == 0) {
entity.setRespMsg("0_离线(设备上线后自动补打)");
} else {
entity.setRespMsg(entity.getRespMsg());
}
}
}
// 静态标识,确保关闭钩子仅注册一次
private static volatile boolean shutdownHookRegistered = false;
// 锁对象,保证线程安全
private static final Object HOOK_LOCK = new Object();
/**
* 统一注册JVM关闭钩子仅执行一次
*/
private void registerShutdownHookOnce() {
if (!shutdownHookRegistered) {
synchronized (HOOK_LOCK) {
// 双重校验锁,避免多线程下重复注册
if (!shutdownHookRegistered) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (virtualThreadScheduler != null && !virtualThreadScheduler.isShutdown()) {
virtualThreadScheduler.shutdown();
try {
if (!virtualThreadScheduler.awaitTermination(10, TimeUnit.SECONDS)) {
log.warn("虚拟线程调度器10秒内未关闭强制关闭...");
virtualThreadScheduler.shutdownNow();
}
} catch (InterruptedException e) {
log.error("等待虚拟线程调度器终止时被中断,强制关闭", e);
virtualThreadScheduler.shutdownNow();
Thread.currentThread().interrupt(); // 保留中断状态
}
}
}, "PrinterScheduler-ShutdownHook"));
shutdownHookRegistered = true;
}
}
}
}
} }

View File

@@ -11,16 +11,22 @@ import com.czg.constants.ParamCodeCst;
import com.czg.dto.req.*; import com.czg.dto.req.*;
import com.czg.order.entity.ShopDirectMerchant; import com.czg.order.entity.ShopDirectMerchant;
import com.czg.service.order.dto.AggregateMerchantVO; import com.czg.service.order.dto.AggregateMerchantVO;
import com.czg.service.order.dto.MerchantQueryDTO;
import com.czg.service.order.mapper.ShopDirectMerchantMapper; import com.czg.service.order.mapper.ShopDirectMerchantMapper;
import com.czg.service.order.service.ShopDirectMerchantService; import com.czg.service.order.service.ShopDirectMerchantService;
import com.czg.system.service.SysParamsService; import com.czg.system.service.SysParamsService;
import com.czg.utils.FunUtils; import com.czg.utils.FunUtils;
import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl; import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@@ -51,8 +57,17 @@ public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchan
} }
@Override @Override
public AggregateMerchantVO getEntry(Long shopId) { public Page<ShopDirectMerchant> getEntryList(MerchantQueryDTO queryParam) {
ShopDirectMerchant merchant = getById(shopId); PageHelper.startPage(queryParam.getPage(), queryParam.getSize());
List<ShopDirectMerchant> entryList = mapper.getEntryList(queryParam);
return PageUtil.convert(new PageInfo<>(entryList));
}
@Override
public AggregateMerchantVO getEntry(Long shopId, String licenceNo) {
ShopDirectMerchant merchant = getOne(query()
.eq(ShopDirectMerchant::getLicenceNo, licenceNo)
.eq(ShopDirectMerchant::getShopId, shopId));
if (merchant == null) { if (merchant == null) {
return null; return null;
} }
@@ -75,6 +90,11 @@ public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchan
merchant.setMerchantCode(reqDto.getMerchantCode()); merchant.setMerchantCode(reqDto.getMerchantCode());
merchant.setLicenceNo(reqDto.getBusinessLicenceInfo().getLicenceNo()); merchant.setLicenceNo(reqDto.getBusinessLicenceInfo().getLicenceNo());
MerchantBaseInfoDto merchantBaseInfo = reqDto.getMerchantBaseInfo();
merchant.setAlipayAccount(merchantBaseInfo.getAlipayAccount());
merchant.setUserType(merchantBaseInfo.getUserType());
merchant.setShortName(merchantBaseInfo.getShortName());
merchant.setMerchantBaseInfo(JSONObject.toJSONString(reqDto.getMerchantBaseInfo())); merchant.setMerchantBaseInfo(JSONObject.toJSONString(reqDto.getMerchantBaseInfo()));
merchant.setLegalPersonInfo(JSONObject.toJSONString(reqDto.getLegalPersonInfo())); merchant.setLegalPersonInfo(JSONObject.toJSONString(reqDto.getLegalPersonInfo()));
merchant.setBusinessLicenceInfo(JSONObject.toJSONString(reqDto.getBusinessLicenceInfo())); merchant.setBusinessLicenceInfo(JSONObject.toJSONString(reqDto.getBusinessLicenceInfo()));
@@ -88,7 +108,7 @@ public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchan
result = updateById(merchant); result = updateById(merchant);
} }
//发送进件队列消息 //发送进件队列消息
FunUtils.transactionSafeRun(() -> rabbitPublisher.sendEntryManagerMsg(reqDto.getShopId().toString())); FunUtils.transactionSafeRun(() -> rabbitPublisher.sendEntryManagerMsg(reqDto.getShopId() + ":" + merchant.getLicenceNo()));
return result; return result;
} }
@@ -120,6 +140,7 @@ public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchan
AggregateMerchantVO vo = new AggregateMerchantVO(); AggregateMerchantVO vo = new AggregateMerchantVO();
vo.setShopId(entity.getShopId()); vo.setShopId(entity.getShopId());
vo.setMerchantCode(entity.getMerchantCode()); vo.setMerchantCode(entity.getMerchantCode());
vo.setShopName(entity.getShopName());
// 解析JSON字段 // 解析JSON字段
vo.setMerchantBaseInfo(JSONObject.parseObject(entity.getMerchantBaseInfo(), MerchantBaseInfoDto.class)); vo.setMerchantBaseInfo(JSONObject.parseObject(entity.getMerchantBaseInfo(), MerchantBaseInfoDto.class));
@@ -129,6 +150,8 @@ public class ShopDirectMerchantServiceImpl extends ServiceImpl<ShopDirectMerchan
vo.setSettlementInfo(JSONObject.parseObject(entity.getSettlementInfo(), SettlementInfoDto.class)); vo.setSettlementInfo(JSONObject.parseObject(entity.getSettlementInfo(), SettlementInfoDto.class));
// 设置其他字段 // 设置其他字段
vo.setUserType(entity.getUserType());
vo.setShortName(entity.getShortName());
vo.setAlipayAccount(entity.getAlipayAccount()); vo.setAlipayAccount(entity.getAlipayAccount());
vo.setCreateTime(entity.getCreateTime()); vo.setCreateTime(entity.getCreateTime());
vo.setUpdateTime(entity.getUpdateTime()); vo.setUpdateTime(entity.getUpdateTime());

View File

@@ -226,7 +226,7 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
*/ */
private BigDecimal getProductCostAmount(Long shopId, LocalDate day) { private BigDecimal getProductCostAmount(Long shopId, LocalDate day) {
BigDecimal productCostAmount = BigDecimal.ZERO; BigDecimal productCostAmount = BigDecimal.ZERO;
////获取orderDetail信息 productId skuId 数量 //获取orderDetail信息 productId skuId 数量
List<ProductCostAmountVO> orderDetailProduct = mapper.getOrderDetailProduct(shopId, day); List<ProductCostAmountVO> orderDetailProduct = mapper.getOrderDetailProduct(shopId, day);
orderDetailProduct = orderDetailProduct.stream() orderDetailProduct = orderDetailProduct.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)

View File

@@ -14,10 +14,7 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.*;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -167,44 +164,99 @@ public class ShopProdStatisticServiceImpl extends ServiceImpl<ShopProdStatisticM
* @return 合并后的数据 * @return 合并后的数据
*/ */
private List<ShopProdStatistic> mergeProdStatistic(List<ShopProdStatistic> realTimeDataByDay, List<ShopProdStatistic> dateRange) { private List<ShopProdStatistic> mergeProdStatistic(List<ShopProdStatistic> realTimeDataByDay, List<ShopProdStatistic> dateRange) {
if (realTimeDataByDay == null) { // 1. 使用防御性编程,确保非空列表
realTimeDataByDay = new ArrayList<>(); List<ShopProdStatistic> realTimeData = Optional.ofNullable(realTimeDataByDay).orElseGet(ArrayList::new);
} List<ShopProdStatistic> rangeData = Optional.ofNullable(dateRange).orElseGet(ArrayList::new);
if (dateRange == null) {
dateRange = new ArrayList<>();
}
return Stream.concat(realTimeDataByDay.stream(), dateRange.stream()) calculateValidData(realTimeData);
calculateValidData(rangeData);
return Stream.concat(realTimeData.stream(), rangeData.stream())
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap( .collect(Collectors.toMap(
ShopProdStatistic::getProdId, ShopProdStatistic::getProdId,
Function.identity(), Function.identity(),
(stat1, stat2) -> { this::mergeStatistics
// 创建合并后的对象
ShopProdStatistic merged = new ShopProdStatistic();
merged.setId(stat1.getId());
merged.setShopId(stat1.getShopId());
merged.setProdId(stat1.getProdId());
merged.setProductName(stat1.getProductName());
// 安全处理BigDecimal相加处理null值
merged.setSaleCount(safeAdd(stat1.getSaleCount(), stat2.getSaleCount()));
merged.setSaleAmount(safeAdd(stat1.getSaleAmount(), stat2.getSaleAmount()));
merged.setRefundCount(safeAdd(stat1.getRefundCount(), stat2.getRefundCount()));
merged.setRefundAmount(safeAdd(stat1.getRefundAmount(), stat2.getRefundAmount()));
return merged;
}
)) ))
.values() .values()
.stream() .stream()
.peek(this::recalculateValidData)
.toList(); .toList();
} }
/**
* 计算有效销售数据
*/
private void calculateValidData(List<ShopProdStatistic> statistics) {
if (statistics == null || statistics.isEmpty()) {
return;
}
statistics.forEach(stat -> {
if (stat != null) {
stat.setValidSaleCount(safeSubtract(stat.getSaleCount(), stat.getRefundCount()));
stat.setValidSaleAmount(safeSubtract(stat.getValidSaleAmount(), stat.getRefundAmount()));
}
});
}
/**
* 合并两个统计对象
*/
private ShopProdStatistic mergeStatistics(ShopProdStatistic stat1, ShopProdStatistic stat2) {
// 使用第一个非空对象作为基准
ShopProdStatistic baseStat = Optional.ofNullable(stat1).orElse(stat2);
ShopProdStatistic otherStat = stat1 == null ? null : stat2;
if (otherStat == null) {
return baseStat;
}
ShopProdStatistic merged = new ShopProdStatistic();
// 设置基本信息(优先使用非空值)
merged.setId(baseStat.getId());
merged.setShopId(baseStat.getShopId());
merged.setProdId(baseStat.getProdId());
merged.setProductName(
Optional.ofNullable(baseStat.getProductName())
.orElse(otherStat.getProductName())
);
// 合并数值字段
merged.setSaleCount(safeAdd(baseStat.getSaleCount(), otherStat.getSaleCount()));
merged.setSaleAmount(safeAdd(baseStat.getSaleAmount(), otherStat.getSaleAmount()));
merged.setRefundCount(safeAdd(baseStat.getRefundCount(), otherStat.getRefundCount()));
merged.setRefundAmount(safeAdd(baseStat.getRefundAmount(), otherStat.getRefundAmount()));
return merged;
}
/**
* 重新计算合并后的有效数据
*/
private void recalculateValidData(ShopProdStatistic stat) {
if (stat != null) {
stat.setValidSaleCount(safeSubtract(stat.getSaleCount(), stat.getRefundCount()));
stat.setValidSaleAmount(safeSubtract(stat.getSaleAmount(), stat.getRefundAmount()));
}
}
/**
* 安全的BigDecimal加法处理null值
*/
private BigDecimal safeAdd(BigDecimal num1, BigDecimal num2) { private BigDecimal safeAdd(BigDecimal num1, BigDecimal num2) {
BigDecimal safeNum1 = num1 != null ? num1 : BigDecimal.ZERO; BigDecimal safeNum1 = Optional.ofNullable(num1).orElse(BigDecimal.ZERO);
BigDecimal safeNum2 = num2 != null ? num2 : BigDecimal.ZERO; BigDecimal safeNum2 = Optional.ofNullable(num2).orElse(BigDecimal.ZERO);
return safeNum1.add(safeNum2); return safeNum1.add(safeNum2);
} }
/**
* 安全的BigDecimal减法处理null值
*/
private BigDecimal safeSubtract(BigDecimal num1, BigDecimal num2) {
BigDecimal safeNum1 = Optional.ofNullable(num1).orElse(BigDecimal.ZERO);
BigDecimal safeNum2 = Optional.ofNullable(num2).orElse(BigDecimal.ZERO);
return safeNum1.subtract(safeNum2);
}
} }

View File

@@ -1,125 +0,0 @@
package com.czg.service.order.utils;
import cn.hutool.core.lang.func.Func0;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author Administrator
*/
@Slf4j
@Component
public class FunUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public static int retryCount = 5;
/**
* 执行任务并保证锁唯一
* @param supplier 业务逻辑
* @param lockKey Redis锁的Key
* @return 业务逻辑返回值
*/
public <T> T runFunAndCheckKey(Supplier<T> supplier, String lockKey) {
String lockValue = String.valueOf(System.nanoTime() + Thread.currentThread().threadId());
try {
// 尝试获取锁,超时时间 5 秒,防止死锁
boolean lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
int count = 0;
// 初始等待 10ms
int retryDelay = 10;
while (!lock) {
// 最多重试 10 次,大约 10 秒
if (count++ > 50) {
throw new RuntimeException("系统繁忙, 稍后再试");
}
Thread.sleep(retryDelay);
// 指数退避,最大等待 200ms
retryDelay = Math.min(retryDelay * 2, 200);
lock = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS));
}
// 执行任务
return supplier.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("线程被中断", e);
} catch (Exception e) {
log.error("执行出错:{}", e.getMessage(), e);
throw e;
} finally {
// 释放锁(使用 Lua 脚本确保原子性)
unlock(lockKey, lockValue);
}
}
/**
* 使用 Lua 脚本确保释放锁的原子性
* @param lockKey 锁的 Key
* @param lockValue 当前线程的锁值
*/
private void unlock(String lockKey, String lockValue) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList(lockKey), lockValue);
}
public static <T, R> R runFunAndRetry(
Supplier<R> function,
Function<R, Boolean> check, Consumer<R> errFun) {
log.info("工具类开始执行函数");
R result = function.get();
boolean flag = check.apply(result);
log.info("执行结果: {}", result);
while (flag && retryCount-- > 0) {
log.info("执行函数失败, 剩余尝试次数{}", retryCount);
result = function.get();
log.info("执行结果: {}", result);
flag = check.apply(result);
}
if (flag) {
errFun.accept(result);
}
return result;
}
/**
* 防抖函数:在指定秒数内相同 Key 的任务只会执行一次
* @param key 防抖使用的 Redis Key
* @param seconds 防抖时间(秒)
* @param task 要执行的业务逻辑
* @return true 执行了任务false 在防抖期内被拦截
*/
public boolean debounce(String key, long seconds, Runnable task) {
try {
Boolean success = redisTemplate.opsForValue().setIfAbsent(
key, "1", seconds, TimeUnit.SECONDS
);
if (Boolean.TRUE.equals(success)) {
task.run();
return true;
}
return false;
} catch (Exception e) {
log.error("防抖函数执行失败 key={} err={}", key, e.getMessage(), e);
return false;
}
}
}

View File

@@ -4,4 +4,23 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.czg.service.order.mapper.ShopDirectMerchantMapper"> <mapper namespace="com.czg.service.order.mapper.ShopDirectMerchantMapper">
<select id="getEntryList" resultType="com.czg.order.entity.ShopDirectMerchant">
select merchant.*,shop.shop_name as shopName
from tb_shop_direct_merchant merchant
left join tb_shop_info shop on merchant.shop_id = shop.id
<where>
<if test="queryParam.userType != null">
and merchant.user_type = #{queryParam.userType}
</if>
<if test="queryParam.shopName != null">
and shop.shop_name like concat('%',#{queryParam.shopName},'%')
</if>
<if test="queryParam.status != null">
and (merchant.wechat_status = #{queryParam.status} or merchant.alipay_status = #{queryParam.status})
</if>
<if test="queryParam.alipayAccount != null">
and merchant.alipay_account like concat('%',#{queryParam.alipayAccount},'%')
</if>
</where>
</select>
</mapper> </mapper>

View File

@@ -153,6 +153,10 @@
<foreach item="item" collection="idList" separator="," open="(" close=")"> <foreach item="item" collection="idList" separator="," open="(" close=")">
#{item} #{item}
</foreach> </foreach>
or t1.sync_id in
<foreach item="item" collection="idList" separator="," open="(" close=")">
#{item}
</foreach>
</if> </if>
order by t1.sort desc,t1.id desc order by t1.sort desc,t1.id desc
</select> </select>