feat: 芯烨云打印机接入
This commit is contained in:
1 parent
9681a18bd1
commit
1cc030d0db
19 files changed
+465
-17
No files matched your search
+5
@@ -41,6 +41,11 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode WECHAT_SHIPPING_DELIVERY_NO_REQUIRED = new ErrorCode(1008007026, "请填写同城配送单号");
|
||||
ErrorCode WECHAT_SHIPPING_UPLOAD_FAILED = new ErrorCode(1008007027, "微信发货信息上报失败");
|
||||
|
||||
ErrorCode STORE_ORDER_PRINT_DISABLED = new ErrorCode(1008007028, "芯烨云打印未启用或未配置开发者凭证");
|
||||
ErrorCode STORE_ORDER_PRINT_NOT_PAID = new ErrorCode(1008007029, "订单未支付,无法打印");
|
||||
ErrorCode STORE_ORDER_PRINT_TYPE_NOT_SUPPORT = new ErrorCode(1008007030, "仅支持自取/外卖订单打印");
|
||||
ErrorCode STORE_ORDER_PRINT_SN_EMPTY = new ErrorCode(1008007031, "门店未配置打印机SN");
|
||||
ErrorCode STORE_ORDER_PRINT_FAILED = new ErrorCode(1008007032, "打印失败:{}");
|
||||
|
||||
|
||||
}
|
||||
+8
-1
@@ -157,6 +157,13 @@ public class StoreOrderController {
|
||||
return success(storeOrderService.orderNotice());
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/printOrder")
|
||||
@Operation(summary = "补打小票")
|
||||
@Parameter(name = "id", description = "订单编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('order:store-order:update')")
|
||||
public CommonResult<Boolean> printOrder(@RequestParam("id") Long id) {
|
||||
storeOrderService.printStoreOrder(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -33,5 +33,8 @@ public interface RedisKeyConstants {
|
||||
"yshop_web_print_machine_cache:%s", // 参数为访问shopid
|
||||
STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
|
||||
|
||||
RedisKeyDefine YSHOP_ORDER_PRINT_KEY = new RedisKeyDefine("订单打印幂等",
|
||||
"yshop_order_print:%s", // 参数为 orderId
|
||||
STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package co.yixiang.yshop.module.order.dal.redis.order;
|
||||
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static co.yixiang.yshop.module.order.dal.redis.RedisKeyConstants.YSHOP_ORDER_PRINT_KEY;
|
||||
|
||||
/**
|
||||
* 订单自动打印幂等(防止支付回调重复出票)
|
||||
*/
|
||||
@Repository
|
||||
public class PrintOrderRedisDAO {
|
||||
|
||||
private static final long TTL_HOURS = 24;
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* @return true 表示首次标记成功,可以打印;false 表示已打印过
|
||||
*/
|
||||
public boolean tryMarkPrinted(String orderId) {
|
||||
String redisKey = formatKey(orderId);
|
||||
Boolean ok = stringRedisTemplate.opsForValue()
|
||||
.setIfAbsent(redisKey, "1", TTL_HOURS, TimeUnit.HOURS);
|
||||
return Boolean.TRUE.equals(ok);
|
||||
}
|
||||
|
||||
private static String formatKey(String orderId) {
|
||||
return String.format(YSHOP_ORDER_PRINT_KEY.getKeyTemplate(), orderId);
|
||||
}
|
||||
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package co.yixiang.yshop.module.order.print.xpyun;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import co.yixiang.yshop.module.order.dal.dataobject.storeorder.StoreOrderDO;
|
||||
import co.yixiang.yshop.module.order.dal.dataobject.storeordercartinfo.StoreOrderCartInfoDO;
|
||||
import co.yixiang.yshop.module.order.enums.OrderLogEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 自取 / 外卖小票内容组装(58mm)
|
||||
*/
|
||||
@Component
|
||||
public class OrderReceiptBuilder {
|
||||
|
||||
private static final DateTimeFormatter PAY_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final String LINE = "--------------------------------<BR>";
|
||||
|
||||
public String build(StoreOrderDO order, List<StoreOrderCartInfoDO> items) {
|
||||
boolean takeout = OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(order.getOrderType());
|
||||
String typeLabel = takeout ? "外卖" : "自取";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<C><B>").append(nullToEmpty(order.getShopName())).append("</B></C><BR>");
|
||||
sb.append("<C>").append(typeLabel).append("</C><BR>");
|
||||
if (order.getNumberId() != null) {
|
||||
sb.append("<CB>#").append(order.getNumberId()).append("</CB><BR>");
|
||||
}
|
||||
sb.append(LINE);
|
||||
|
||||
sb.append("商品").append(pad("数量", 6)).append(pad("单价", 6)).append("<BR>");
|
||||
sb.append(LINE);
|
||||
if (items != null) {
|
||||
for (StoreOrderCartInfoDO item : items) {
|
||||
String name = nullToEmpty(item.getTitle());
|
||||
if (StrUtil.isNotBlank(item.getSpec())) {
|
||||
name = name + "(" + item.getSpec() + ")";
|
||||
}
|
||||
sb.append(name).append("<BR>");
|
||||
Integer number = item.getNumber() == null ? 0 : item.getNumber();
|
||||
BigDecimal price = item.getPrice() == null ? BigDecimal.ZERO : item.getPrice();
|
||||
sb.append(" x").append(number)
|
||||
.append(" ").append(price.toPlainString())
|
||||
.append("<BR>");
|
||||
}
|
||||
}
|
||||
sb.append(LINE);
|
||||
|
||||
if (order.getCouponPrice() != null && order.getCouponPrice().compareTo(BigDecimal.ZERO) > 0) {
|
||||
sb.append("优惠:-").append(order.getCouponPrice().toPlainString()).append("<BR>");
|
||||
}
|
||||
if (takeout) {
|
||||
BigDecimal freight = order.getPayPostage() != null ? order.getPayPostage()
|
||||
: (order.getFreightPrice() != null ? order.getFreightPrice() : BigDecimal.ZERO);
|
||||
sb.append("配送费:").append(freight.toPlainString()).append("<BR>");
|
||||
}
|
||||
BigDecimal payPrice = order.getPayPrice() == null ? BigDecimal.ZERO : order.getPayPrice();
|
||||
sb.append("<R>实付:").append(payPrice.toPlainString()).append("元</R><BR>");
|
||||
sb.append(LINE);
|
||||
|
||||
if (StrUtil.isNotBlank(order.getRealName())) {
|
||||
sb.append("姓名:").append(order.getRealName()).append("<BR>");
|
||||
}
|
||||
if (StrUtil.isNotBlank(order.getUserPhone())) {
|
||||
sb.append("电话:").append(order.getUserPhone()).append("<BR>");
|
||||
}
|
||||
if (takeout && StrUtil.isNotBlank(order.getUserAddress())) {
|
||||
sb.append("地址:").append(order.getUserAddress()).append("<BR>");
|
||||
}
|
||||
if (order.getPayTime() != null) {
|
||||
sb.append("支付时间:").append(order.getPayTime().format(PAY_TIME_FMT)).append("<BR>");
|
||||
}
|
||||
if (StrUtil.isNotBlank(order.getMark())) {
|
||||
sb.append("备注:").append(order.getMark()).append("<BR>");
|
||||
}
|
||||
sb.append("订单号:").append(nullToEmpty(order.getOrderId())).append("<BR>");
|
||||
sb.append("<BR><BR>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String nullToEmpty(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
private static String pad(String text, int width) {
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
if (text.length() >= width) {
|
||||
return text;
|
||||
}
|
||||
return " ".repeat(width - text.length()) + text;
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package co.yixiang.yshop.module.order.print.xpyun;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(XpYunProperties.class)
|
||||
public class XpYunConfiguration {
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package co.yixiang.yshop.module.order.print.xpyun;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import co.yixiang.yshop.framework.common.util.json.JsonUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 芯烨云小票打印客户端
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class XpYunPrintClient {
|
||||
|
||||
@Resource
|
||||
private XpYunProperties properties;
|
||||
|
||||
/**
|
||||
* 打印小票
|
||||
*
|
||||
* @param sn 打印机编号
|
||||
* @param content 打印内容(标签语法)
|
||||
* @param copies 份数
|
||||
* @param voice 声音模式:0 取消 / 1 静音 / 2 来单
|
||||
* @param mode 0 检查在线;1 离线也入队
|
||||
* @return 云端响应
|
||||
*/
|
||||
public XpYunPrintResponse print(String sn, String content, int copies, int voice, int mode) {
|
||||
Map<String, Object> body = buildCommonParams();
|
||||
body.put("sn", sn);
|
||||
body.put("content", content);
|
||||
body.put("copies", copies);
|
||||
body.put("voice", voice);
|
||||
body.put("mode", mode);
|
||||
|
||||
String url = properties.getBaseUrl() + "/xprinter/print";
|
||||
String json = JsonUtils.toJsonString(body);
|
||||
log.info("[XpYun][print][sn({})] request={}", sn, json);
|
||||
String respText = HttpRequest.post(url)
|
||||
.header("Content-Type", "application/json;charset=UTF-8")
|
||||
.body(json)
|
||||
.timeout(15000)
|
||||
.execute()
|
||||
.body();
|
||||
log.info("[XpYun][print][sn({})] response={}", sn, respText);
|
||||
return JsonUtils.parseObject(respText, XpYunPrintResponse.class);
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return Boolean.TRUE.equals(properties.getEnabled())
|
||||
&& properties.getUser() != null && !properties.getUser().isBlank()
|
||||
&& properties.getUserKey() != null && !properties.getUserKey().isBlank();
|
||||
}
|
||||
|
||||
private Map<String, Object> buildCommonParams() {
|
||||
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||
String sign = DigestUtil.sha1Hex(properties.getUser() + properties.getUserKey() + timestamp);
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("user", properties.getUser());
|
||||
params.put("timestamp", timestamp);
|
||||
params.put("sign", sign);
|
||||
params.put("debug", "0");
|
||||
return params;
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package co.yixiang.yshop.module.order.print.xpyun;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 芯烨云通用响应
|
||||
*/
|
||||
@Data
|
||||
public class XpYunPrintResponse {
|
||||
|
||||
/**
|
||||
* 0 表示成功
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 成功时一般为打印订单号
|
||||
*/
|
||||
private String data;
|
||||
|
||||
private Long serverExecutedTime;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return code != null && code == 0;
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package co.yixiang.yshop.module.order.print.xpyun;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 芯烨云打印配置
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "yshop.xpyun")
|
||||
@Data
|
||||
public class XpYunProperties {
|
||||
|
||||
/**
|
||||
* 是否启用芯烨云打印
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 开发者 ID(邮箱或开发者 ID)
|
||||
*/
|
||||
private String user = "";
|
||||
|
||||
/**
|
||||
* 开发者密钥
|
||||
*/
|
||||
private String userKey = "";
|
||||
|
||||
/**
|
||||
* OpenAPI 根地址
|
||||
*/
|
||||
private String baseUrl = "https://open.xpyun.net/api/openapi";
|
||||
|
||||
}
|
||||
+13
@@ -67,6 +67,8 @@ import org.redisson.client.codec.StringCodec;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -568,6 +570,17 @@ public class AppStoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper,Store
|
||||
productName,orderInfo.getShopName());
|
||||
}
|
||||
|
||||
// 支付成功后异步打印小票(事务提交后再触发,避免读到未提交状态)
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
asyncStoreOrderService.printOrderAsync(orderId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
asyncStoreOrderService.printOrderAsync(orderId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+100
-6
@@ -1,16 +1,12 @@
|
||||
package co.yixiang.yshop.module.order.service.storeorder;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import co.yixiang.yshop.framework.common.enums.OrderInfoEnum;
|
||||
import co.yixiang.yshop.module.member.controller.app.user.vo.AppUserOrderCountVo;
|
||||
import co.yixiang.yshop.module.member.service.user.MemberUserService;
|
||||
import co.yixiang.yshop.module.order.controller.admin.storeorder.vo.ShoperOrderTimeDataVo;
|
||||
import co.yixiang.yshop.module.order.controller.app.order.vo.AppStoreOrderQueryVo;
|
||||
import co.yixiang.yshop.module.order.dal.dataobject.storeorder.StoreOrderDO;
|
||||
import co.yixiang.yshop.module.order.dal.dataobject.storeordercartinfo.StoreOrderCartInfoDO;
|
||||
import co.yixiang.yshop.module.order.dal.mysql.storeorder.StoreOrderMapper;
|
||||
@@ -18,22 +14,28 @@ import co.yixiang.yshop.module.order.dal.mysql.storeordercartinfo.StoreOrderCart
|
||||
import co.yixiang.yshop.module.order.dal.redis.order.AsyncCountRedisDAO;
|
||||
import co.yixiang.yshop.module.order.dal.redis.order.AsyncOrderRedisDAO;
|
||||
import co.yixiang.yshop.module.order.dal.redis.order.PrintMechinRedisDAO;
|
||||
import co.yixiang.yshop.module.order.dal.redis.order.PrintOrderRedisDAO;
|
||||
import co.yixiang.yshop.module.order.enums.OrderLogEnum;
|
||||
import co.yixiang.yshop.module.order.print.xpyun.OrderReceiptBuilder;
|
||||
import co.yixiang.yshop.module.order.print.xpyun.XpYunPrintClient;
|
||||
import co.yixiang.yshop.module.order.print.xpyun.XpYunPrintResponse;
|
||||
import co.yixiang.yshop.module.order.service.storeorder.dto.OrderTimeDataDto;
|
||||
import co.yixiang.yshop.module.product.service.storeproduct.StoreProductService;
|
||||
import co.yixiang.yshop.module.store.dal.dataobject.storeshop.StoreShopDO;
|
||||
import co.yixiang.yshop.module.store.dal.mysql.storeshop.StoreShopMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static co.yixiang.yshop.module.order.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 异步订单 Service 实现类
|
||||
*
|
||||
@@ -60,6 +62,12 @@ public class AsynStoreOrderServiceImpl implements AsyncStoreOrderService {
|
||||
private StoreShopMapper storeShopMapper;
|
||||
@Resource
|
||||
private StoreOrderCartInfoMapper storeOrderCartInfoMapper;
|
||||
@Resource
|
||||
private PrintOrderRedisDAO printOrderRedisDAO;
|
||||
@Resource
|
||||
private XpYunPrintClient xpYunPrintClient;
|
||||
@Resource
|
||||
private OrderReceiptBuilder orderReceiptBuilder;
|
||||
|
||||
/**
|
||||
* 获取某个用户的订单统计数据
|
||||
@@ -236,9 +244,95 @@ public class AsynStoreOrderServiceImpl implements AsyncStoreOrderService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public void printOrderAsync(String orderId) {
|
||||
try {
|
||||
printOrder(orderId, false);
|
||||
} catch (Exception e) {
|
||||
log.error("[printOrderAsync][orderId({}) 自动打印失败]", orderId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printOrder(String orderId, boolean force) {
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
if (force) {
|
||||
throw exception(PARAM_ERROR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!xpYunPrintClient.isReady()) {
|
||||
failOrSkip(force, STORE_ORDER_PRINT_DISABLED, "芯烨云未启用或凭证未配置");
|
||||
return;
|
||||
}
|
||||
|
||||
StoreOrderDO order = storeOrderMapper.selectOne(new LambdaQueryWrapper<StoreOrderDO>()
|
||||
.eq(StoreOrderDO::getOrderId, orderId));
|
||||
if (order == null) {
|
||||
failOrSkip(force, STORE_ORDER_NOT_EXISTS, "订单不存在");
|
||||
return;
|
||||
}
|
||||
if (!OrderInfoEnum.PAY_STATUS_1.getValue().equals(order.getPaid())) {
|
||||
failOrSkip(force, STORE_ORDER_PRINT_NOT_PAID, "订单未支付");
|
||||
return;
|
||||
}
|
||||
String orderType = order.getOrderType();
|
||||
if (!OrderLogEnum.ORDER_TAKE_IN.getValue().equals(orderType)
|
||||
&& !OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(orderType)) {
|
||||
failOrSkip(force, STORE_ORDER_PRINT_TYPE_NOT_SUPPORT, "订单类型不支持打印:" + orderType);
|
||||
return;
|
||||
}
|
||||
if (order.getShopId() == null) {
|
||||
failOrSkip(force, STORE_ORDER_PRINT_SN_EMPTY, "订单无门店");
|
||||
return;
|
||||
}
|
||||
StoreShopDO shop = storeShopMapper.selectById(order.getShopId());
|
||||
if (shop == null || StrUtil.isBlank(shop.getUniprintId())) {
|
||||
failOrSkip(force, STORE_ORDER_PRINT_SN_EMPTY, "门店未配置打印机SN");
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验通过后再做幂等,避免空 SN / 未启用误占 key
|
||||
if (!force && !printOrderRedisDAO.tryMarkPrinted(orderId)) {
|
||||
log.info("[printOrder][orderId({}) 已打印过,跳过]", orderId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<StoreOrderCartInfoDO> items = storeOrderCartInfoMapper.selectList(
|
||||
new LambdaQueryWrapper<StoreOrderCartInfoDO>()
|
||||
.eq(StoreOrderCartInfoDO::getOid, order.getId()));
|
||||
String content = orderReceiptBuilder.build(order, items);
|
||||
try {
|
||||
// mode=1 离线入队;voice=2 来单播报
|
||||
XpYunPrintResponse resp = xpYunPrintClient.print(shop.getUniprintId(), content, 1, 2, 1);
|
||||
if (resp == null || !resp.isSuccess()) {
|
||||
String msg = resp == null ? "无响应" : StrUtil.blankToDefault(resp.getMsg(), "未知错误");
|
||||
if (force) {
|
||||
throw exception(STORE_ORDER_PRINT_FAILED, msg);
|
||||
}
|
||||
log.error("[printOrder][orderId({}) 打印失败] {}", orderId, msg);
|
||||
} else {
|
||||
log.info("[printOrder][orderId({}) 打印成功] data={}", orderId, resp.getData());
|
||||
}
|
||||
} catch (co.yixiang.yshop.framework.common.exception.ServiceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
if (force) {
|
||||
throw exception(STORE_ORDER_PRINT_FAILED, e.getMessage());
|
||||
}
|
||||
log.error("[printOrder][orderId({}) 打印异常]", orderId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void failOrSkip(boolean force,
|
||||
co.yixiang.yshop.framework.common.exception.ErrorCode errorCode,
|
||||
String logMsg) {
|
||||
if (force) {
|
||||
throw exception(errorCode);
|
||||
}
|
||||
log.warn("[printOrder][skip] {}", logMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-9
@@ -1,11 +1,5 @@
|
||||
package co.yixiang.yshop.module.order.service.storeorder;
|
||||
|
||||
import co.yixiang.yshop.module.member.controller.app.user.vo.AppUserOrderCountVo;
|
||||
import co.yixiang.yshop.module.order.controller.admin.storeorder.vo.ShoperOrderTimeDataVo;
|
||||
import co.yixiang.yshop.module.order.controller.app.order.vo.AppStoreOrderQueryVo;
|
||||
import co.yixiang.yshop.module.order.dal.dataobject.storeorder.StoreOrderDO;
|
||||
import co.yixiang.yshop.module.order.service.storeorder.dto.OrderTimeDataDto;
|
||||
|
||||
/**
|
||||
* 异步订单 Service 接口
|
||||
*
|
||||
@@ -16,17 +10,27 @@ public interface AsyncStoreOrderService {
|
||||
/**
|
||||
* 计算某个用户的订单统计数据
|
||||
* @param uid uid>0 取用户 否则取所有
|
||||
* @return UserOrderCountVo
|
||||
*/
|
||||
void orderData(Long uid);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 异步后台数据统计
|
||||
*/
|
||||
void getOrderTimeData();
|
||||
|
||||
/**
|
||||
* 支付成功后异步打印小票(失败只记日志,不抛异常)
|
||||
*
|
||||
* @param orderId 业务订单号
|
||||
*/
|
||||
void printOrderAsync(String orderId);
|
||||
|
||||
/**
|
||||
* 打印小票
|
||||
*
|
||||
* @param orderId 业务订单号
|
||||
* @param force false=自动打印(幂等,校验失败跳过);true=补打(跳过幂等,校验失败抛业务异常)
|
||||
*/
|
||||
void printOrder(String orderId, boolean force);
|
||||
|
||||
}
|
||||
+6
-1
@@ -99,6 +99,11 @@ public interface StoreOrderService {
|
||||
*/
|
||||
Long orderNotice();
|
||||
|
||||
|
||||
/**
|
||||
* 补打小票(同步,跳过幂等)
|
||||
*
|
||||
* @param id 订单主键
|
||||
*/
|
||||
void printStoreOrder(Long id);
|
||||
|
||||
}
|
||||
+9
@@ -453,6 +453,15 @@ public class StoreOrderServiceImpl implements StoreOrderService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStoreOrder(Long id) {
|
||||
StoreOrderDO storeOrderDO = storeOrderMapper.selectById(id);
|
||||
if (storeOrderDO == null) {
|
||||
throw exception(STORE_ORDER_NOT_EXISTS);
|
||||
}
|
||||
asyncStoreOrderService.printOrder(storeOrderDO.getOrderId(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退回库存
|
||||
*
|
||||
|
||||
@@ -170,6 +170,11 @@ yshop:
|
||||
pay:
|
||||
order-notify-url: http://yshop.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
|
||||
refund-notify-url: http://yshop.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
|
||||
xpyun: # 芯烨云打印机 https://www.xpyun.net
|
||||
enabled: true
|
||||
user: neko_chocola@qq.com # 开发者 ID
|
||||
user-key: 36c8251d0ff340888dd0f5fa62aa81ee # 开发者密钥
|
||||
base-url: https://open.xpyun.net/api/openapi
|
||||
demo: true # 开启演示模式
|
||||
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||
file-path: /www/wwwroot/drink/file/
|
||||
|
||||
@@ -206,6 +206,11 @@ yshop:
|
||||
pay:
|
||||
order-notify-url: http://yshop.natapp1.cc/admin-api/pay/notify/order # 支付渠道的【支付】回调地址
|
||||
refund-notify-url: http://yshop.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
|
||||
xpyun: # 芯烨云打印机 https://www.xpyun.net
|
||||
enabled: true
|
||||
user: neko_chocola@qq.com # 开发者 ID
|
||||
user-key: 36c8251d0ff340888dd0f5fa62aa81ee # 开发者密钥
|
||||
base-url: https://open.xpyun.net/api/openapi
|
||||
access-log: # 访问日志的配置项
|
||||
enable: false
|
||||
demo: false # 关闭演示模式
|
||||
|
||||
Reference in new issue
Block a user