create: 新增抖店订单推送,抓取

This commit is contained in:
Damonny 2025-02-19 14:02:31 +08:00
parent 6e1da5f795
commit 2e351ce1ff
26 changed files with 1702 additions and 2 deletions

View File

@ -48,4 +48,6 @@ public interface ErrorCodeConstants {
ErrorCode BLACK_LIST_NOT_EXISTS = new ErrorCode(1_824_003_005, "黑名单不存在");
ErrorCode SMS_TASK_NOT_EXISTS = new ErrorCode(1_824_123_005, "短信任务不存在");
ErrorCode ORDER_OPERATE_LOG_NOT_EXISTS = new ErrorCode(1_826_001_001, "订单操作日志不存在");
ErrorCode ORDER_SYNC_LOG_NOT_EXISTS = new ErrorCode(1_827_001_001, "抓单记录不存在");
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.haoka.controller.admin.orderCatch;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.po.Message;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.vo.OrderCatchRepVO;
import cn.iocoder.yudao.module.haoka.service.orderCatch.OrderCatchService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import org.springframework.scheduling.annotation.Async;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "抓取订单")
@RestController
@RequestMapping("/haoka/orderCatch")
@Validated
@PermitAll
public class OrderCatchController {
@Resource
private OrderCatchService orderCatchService;
@PermitAll
@RequestMapping("/catch")
@Operation(summary = "抓取订单")
public String catchOrders(@Valid @RequestBody List<Message> messages) {
// 异步处理订单数据
execCatchOrders(messages);
// 立即返回推送所需的响应数据
return "{\"code\":0,\"msg\":\"success\"}";
}
/**
* 异步处理订单数据
* @param messages
*/
@Async
public void execCatchOrders(List<Message> messages) {
orderCatchService.catchOrders(messages);
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.po;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Message<T> {
@JsonProperty("tag")
private String tag;
@JsonProperty("msg_id")
private String msgId;
@JsonProperty("data")
private T data;
// Getters and Setters
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "抖音消息推送接收实体")
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class OrderCatchRepVO {
@Schema(description = "消息", example = "success")
private String tag;
@Schema(description = "消息", example = "success")
private String msg_id;
@Schema(description = "消息", example = "success")
private String data;
}

View File

@ -294,5 +294,34 @@ public class OrdersRespVO {
*/
@Schema(description = "身份证人脸")
private String face;
/**
* 店铺ID
*/
@Schema(description = "店铺ID")
private String shopId;
/**
* 店铺名称
*/
@Schema(description = "店铺名称")
private String shopName;
/**
* 直播间ID
*/
@Schema(description = "直播间ID")
private String roomId;
/**
* 直播间名称
*/
@Schema(description = "直播间名称")
private String roomName;
/**
* 销售归属
*/
@Schema(description = "销售归属")
private String salesAttribution;
/**
* 销售归属名称
*/
@Schema(description = "销售归属名称")
private String salesAttributionName;
}

View File

@ -223,4 +223,34 @@ public class OrdersSaveReqVO {
*/
@Schema(description = "上游订单状态")
private Integer upstreamOrderStatus;
/**
* 店铺ID
*/
@Schema(description = "店铺ID")
private String shopId;
/**
* 店铺名称
*/
@Schema(description = "店铺名称")
private String shopName;
/**
* 直播间ID
*/
@Schema(description = "直播间ID")
private String roomId;
/**
* 直播间名称
*/
@Schema(description = "直播间名称")
private String roomName;
/**
* 销售归属
*/
@Schema(description = "销售归属")
private String salesAttribution;
/**
* 销售归属名称
*/
@Schema(description = "销售归属名称")
private String salesAttributionName;
}

View File

@ -27,6 +27,10 @@ public class OrderSourceRespVO {
@DictFormat("haoka_order_channel") // TODO 代码优化建议设置到对应的 DictTypeConstants 枚举类中
private Long channel;
@Schema(description = "店铺ID", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty(value = "店铺ID")
private Long shopId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;

View File

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.*;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.module.haoka.service.ordersynclog.OrderSyncLogService;
@Tag(name = "管理后台 - 抓单记录")
@RestController
@RequestMapping("/haoka/order-sync-log")
@Validated
public class OrderSyncLogController {
@Resource
private OrderSyncLogService orderSyncLogService;
@PostMapping("/create")
@Operation(summary = "创建抓单记录")
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:create')")
public CommonResult<Long> createOrderSyncLog(@Valid @RequestBody OrderSyncLogSaveReqVO createReqVO) {
return success(orderSyncLogService.createOrderSyncLog(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新抓单记录")
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:update')")
public CommonResult<Boolean> updateOrderSyncLog(@Valid @RequestBody OrderSyncLogSaveReqVO updateReqVO) {
orderSyncLogService.updateOrderSyncLog(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除抓单记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:delete')")
public CommonResult<Boolean> deleteOrderSyncLog(@RequestParam("id") Long id) {
orderSyncLogService.deleteOrderSyncLog(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得抓单记录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:query')")
public CommonResult<OrderSyncLogRespVO> getOrderSyncLog(@RequestParam("id") Long id) {
OrderSyncLogDO orderSyncLog = orderSyncLogService.getOrderSyncLog(id);
return success(BeanUtils.toBean(orderSyncLog, OrderSyncLogRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得抓单记录分页")
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:query')")
public CommonResult<PageResult<OrderSyncLogRespVO>> getOrderSyncLogPage(@Valid OrderSyncLogPageReqVO pageReqVO) {
PageResult<OrderSyncLogDO> pageResult = orderSyncLogService.getOrderSyncLogPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, OrderSyncLogRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出抓单记录 Excel")
@PreAuthorize("@ss.hasPermission('haoka:order-sync-log:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportOrderSyncLogExcel(@Valid OrderSyncLogPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<OrderSyncLogDO> list = orderSyncLogService.getOrderSyncLogPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "抓单记录.xls", "数据", OrderSyncLogRespVO.class,
BeanUtils.toBean(list, OrderSyncLogRespVO.class));
}
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 抓单记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class OrderSyncLogPageReqVO extends PageParam {
@Schema(description = "同步类型(电商渠道)", example = "1")
private String syncType;
@Schema(description = "同步店铺ID", example = "13239")
private String syncShopId;
@Schema(description = "同步结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Long[] syncEndTime;
@Schema(description = "同步开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Long[] syncCreateTime;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 抓单记录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class OrderSyncLogRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28786")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "同步类型(电商渠道)", example = "1")
@ExcelProperty(value = "同步类型(电商渠道)", converter = DictConvert.class)
@DictFormat("haoka_order_channel") // TODO 代码优化建议设置到对应的 DictTypeConstants 枚举类中
private String syncType;
@Schema(description = "同步店铺ID", example = "13239")
@ExcelProperty("同步店铺ID")
private String syncShopId;
@Schema(description = "同步结束时间")
@ExcelProperty("同步结束时间")
private Long syncEndTime;
@Schema(description = "同步开始时间")
@ExcelProperty("同步开始时间")
private Long syncCreateTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 抓单记录新增/修改 Request VO")
@Data
public class OrderSyncLogSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28786")
private Long id;
@Schema(description = "同步类型(电商渠道)", example = "1")
private String syncType;
@Schema(description = "同步店铺ID", example = "13239")
private String syncShopId;
@Schema(description = "同步结束时间")
private Long syncEndTime;
@Schema(description = "同步开始时间")
private Long syncCreateTime;
}

View File

@ -326,5 +326,29 @@ public class OrdersDO extends BaseDO {
* 是否加急 0或空 不加急 1 加急
*/
private Integer isUrgent;
/**
* 店铺ID
*/
private String shopId;
/**
* 店铺名称
*/
private String shopName;
/**
* 直播间ID
*/
private String roomId;
/**
* 直播间名称
*/
private String roomName;
/**
* 销售归属
*/
private String salesAttribution;
/**
* 销售归属名称
*/
private String salesAttributionName;
}

View File

@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 抓单记录 DO
*
* @author 超级管理员
*/
@TableName("haoka_order_sync_log")
@KeySequence("haoka_order_sync_log_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderSyncLogDO extends BaseDO {
/**
* 主键ID
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 同步类型(电商渠道)
*
* 枚举 {@link TODO haoka_order_channel 对应的类}
*/
private String syncType;
/**
* 同步店铺ID
*/
private String syncShopId;
/**
* 同步结束时间
*/
private Long syncEndTime;
/**
* 同步开始时间
*/
private Long syncCreateTime;
}

View File

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.haoka.dal.mysql.ordersynclog;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.*;
/**
* 抓单记录 Mapper
*
* @author 超级管理员
*/
@Mapper
public interface OrderSyncLogMapper extends BaseMapperX<OrderSyncLogDO> {
default PageResult<OrderSyncLogDO> selectPage(OrderSyncLogPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<OrderSyncLogDO>()
.eqIfPresent(OrderSyncLogDO::getSyncType, reqVO.getSyncType())
.eqIfPresent(OrderSyncLogDO::getSyncShopId, reqVO.getSyncShopId())
.betweenIfPresent(OrderSyncLogDO::getSyncEndTime, reqVO.getSyncEndTime())
.betweenIfPresent(OrderSyncLogDO::getSyncCreateTime, reqVO.getSyncCreateTime())
.betweenIfPresent(OrderSyncLogDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(OrderSyncLogDO::getCreateTime));
}
}

View File

@ -0,0 +1,109 @@
package cn.iocoder.yudao.module.haoka.schedule;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.OrderSyncLogPageReqVO;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.OrderSyncLogSaveReqVO;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.module.haoka.service.orderCatch.OrderCatchService;
import cn.iocoder.yudao.module.haoka.service.ordersynclog.OrderSyncLogService;
import com.doudian.open.core.AccessToken;
import com.doudian.open.core.AccessTokenBuilder;
import com.doudian.open.core.GlobalConfig;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
/**
* 抖音抓单
*/
@Slf4j
@Component
public class DouDianOrderCatchSchdule {
private static final String SYNC_APP_KEY = "7381306825771091495"; // initAppKey
private static final String SYNC_APP_SECRET = "09c06553-b77c-4712-aed9-84cbfe16ca13"; // initAppSecret
/**
* 天猫 0
* 快手 1
* 抖音 2
* 拼多多 3
* 京东 4
*/
private static final String SYNC_TYPE = "2"; // 同步类型 抖音
private static final Long SYNC_SHOP_ID = 20811777L; // 抖店店铺id
@Resource
OrderCatchService orderCatchService;
@Resource
private OrderSyncLogService orderSyncLogService;
/**
* 每20分钟执行一次
*/
// @Scheduled(cron = "0 */20 * * * ?")
public void catchOrder() {
// refreshToken();
GlobalConfig.initAppKey(SYNC_APP_KEY);
GlobalConfig.initAppSecret(SYNC_APP_SECRET);
//入参为code
AccessToken accessToken= AccessTokenBuilder.build(SYNC_SHOP_ID);
// 从数据库获取上一次同步的结束时间
Long lastSyncTime = getLatestSyncTime();
// 获取当前时间
LocalDateTime now = LocalDateTime.now();
long currentTimestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond();
// 如果没有同步记录初始化为1天前
LocalDateTime startDateTime;
if (lastSyncTime == null) {
startDateTime = now.minus(1, ChronoUnit.DAYS);
} else {
startDateTime = Instant.ofEpochSecond(lastSyncTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
// 将LocalDateTime转换为秒级时间戳
long createTimeStart = startDateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
long createTimeEnd = currentTimestamp;
orderCatchService.syncOrder(accessToken, createTimeStart, createTimeEnd);
log.info("结束-----------抖音--更新订单结束--------------------");
}
/**
* 从数据库获取上一次同步的结束时间
*
* @return 上一次同步的结束时间戳
*/
private Long getLatestSyncTime() {
// 示例代码实际需要通过你的数据库操作类获取
OrderSyncLogPageReqVO orderSyncLogPageReqVO = new OrderSyncLogPageReqVO();
orderSyncLogPageReqVO.setSyncType(SYNC_TYPE);
orderSyncLogPageReqVO.setSyncShopId(String.valueOf(SYNC_SHOP_ID));
PageResult<OrderSyncLogDO> orderSyncLogPage = orderSyncLogService.getOrderSyncLogPage(orderSyncLogPageReqVO);
Long total = orderSyncLogPage.getTotal();
if(total>0){
return orderSyncLogPage.getList().get(0).getSyncEndTime();
}
return null;
}
/**
* 保存同步记录到数据库
*
* @param createTimeEnd 同步结束时间
* @param createTimeStart 同步开始时间
*/
private void saveSyncLog(long createTimeEnd,long createTimeStart) {
OrderSyncLogSaveReqVO saveReqVO = new OrderSyncLogSaveReqVO();
saveReqVO.setSyncType(SYNC_TYPE);
saveReqVO.setSyncEndTime(createTimeEnd);
saveReqVO.setSyncCreateTime(createTimeStart); // 当前时间戳
orderSyncLogService.createOrderSyncLog(saveReqVO); // 插入同步记录
}
}

View File

@ -0,0 +1,651 @@
package cn.iocoder.yudao.module.haoka.service.orderCatch;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.haoka.controller.admin.onsaleproduct.vo.OnSaleProductPreOrderRespVO;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.po.Message;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.vo.OrderCatchRepVO;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.OrderSyncLogPageReqVO;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.OrderSyncLogSaveReqVO;
import cn.iocoder.yudao.module.haoka.dal.dataobject.onsaleproduct.OnSaleProductDO;
import cn.iocoder.yudao.module.haoka.dal.dataobject.orders.OrdersDO;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.module.haoka.dal.mysql.onsaleproduct.OnSaleProductMapper;
import cn.iocoder.yudao.module.haoka.dal.mysql.orders.OrdersMapper;
import cn.iocoder.yudao.module.haoka.service.onsaleproduct.OnSaleProductService;
import cn.iocoder.yudao.module.haoka.service.ordersynclog.OrderSyncLogService;
import cn.iocoder.yudao.module.haoka.service.ordersynclog.OrderSyncLogServiceImpl;
import cn.iocoder.yudao.module.haoka.utils.GroupListUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.doudian.open.api.order_batchDecrypt.OrderBatchDecryptRequest;
import com.doudian.open.api.order_batchDecrypt.OrderBatchDecryptResponse;
import com.doudian.open.api.order_batchDecrypt.data.DecryptInfosItem;
import com.doudian.open.api.order_batchDecrypt.data.OrderBatchDecryptData;
import com.doudian.open.api.order_batchDecrypt.param.CipherInfosItem;
import com.doudian.open.api.order_batchDecrypt.param.OrderBatchDecryptParam;
import com.doudian.open.api.order_orderDetail.OrderOrderDetailRequest;
import com.doudian.open.api.order_orderDetail.OrderOrderDetailResponse;
import com.doudian.open.api.order_orderDetail.data.OrderOrderDetailData;
import com.doudian.open.api.order_orderDetail.data.ShopOrderDetail;
import com.doudian.open.api.order_orderDetail.param.OrderOrderDetailParam;
import com.doudian.open.api.order_searchList.OrderSearchListRequest;
import com.doudian.open.api.order_searchList.OrderSearchListResponse;
import com.doudian.open.api.order_searchList.data.*;
import com.doudian.open.api.order_searchList.param.OrderSearchListParam;
import com.doudian.open.api.token_refresh.TokenRefreshRequest;
import com.doudian.open.api.token_refresh.TokenRefreshResponse;
import com.doudian.open.api.token_refresh.param.TokenRefreshParam;
import com.doudian.open.core.AccessToken;
import com.doudian.open.core.AccessTokenBuilder;
import com.doudian.open.core.GlobalConfig;
import com.doudian.open.gson.Gson;
import com.doudian.open.msg.refund_RefundAgreed.param.RefundRefundAgreedParam;
import com.doudian.open.msg.refund_RefundCreated.param.RefundRefundCreatedParam;
import com.doudian.open.msg.refund_ReturnApplyAgreed.param.RefundReturnApplyAgreedParam;
import com.doudian.open.msg.trade_TradeCanceled.param.TradeTradeCanceledParam;
import com.doudian.open.msg.trade_TradePaid.param.TradeTradePaidParam;
import com.doudian.open.msg.trade_TradePending.param.TradeTradePendingParam;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
@Validated
public class DouDianOrderCatchServiceImpl implements OrderCatchService {
private static final String SYNC_APP_KEY = "7381306825771091495"; // initAppKey
private static final String SYNC_APP_SECRET = "09c06553-b77c-4712-aed9-84cbfe16ca13"; // initAppSecret
private static final String SYNC_TYPE = "2"; // 同步类型 抖店
private static final Long SYNC_SHOP_ID = 20811777L; // 抖店店铺id
@Resource
private OrdersMapper ordersMapper;
@Resource
private OnSaleProductMapper onSaleProductMapper;
@Resource
private OnSaleProductService onSaleProductService;
@Resource
private OrderSyncLogService orderSyncLogService;
@Override
public void syncOrder(AccessToken accessToken, long createTimeStart, long createTimeEnd) {
// 分页查询订单
List<ShopOrderListItem> allOrders = fetchOrders(accessToken, createTimeStart, createTimeEnd);
// 将获取的订单插入到数据库
insertOrdersToDatabase(allOrders);
}
/**
* 抖音消息推送订单抓取/更新 订单
* @param catchRepVO
*/
@Override
public void catchOrders(List<Message> catchRepVO) {
// 插入日志
Gson gson = new Gson();
for (Message message : catchRepVO) {
String tag = message.getTag();
String msgId = message.getMsgId();
Object data = message.getData();
// 插入日志
if("doudian_refund_RefundCreated".equals(tag)){
// 买家发起售后申请消息
RefundRefundCreatedParam refundRefundCreatedParam = gson.fromJson(message.getData()+"", RefundRefundCreatedParam.class);
} else if("doudian_refund_RefundAgreed".equals(tag)) {
// 同意退款消息
RefundRefundAgreedParam refundRefundAgreedParam = gson.fromJson(message.getData()+"", RefundRefundAgreedParam.class);
} else if("doudian_refund_ReturnApplyAgreed".equals(tag)) {
// 同意退货申请消息
RefundReturnApplyAgreedParam refundReturnApplyAgreedParam = gson.fromJson(message.getData()+"", RefundReturnApplyAgreedParam.class);
} else if("doudian_trade_TradePaid".equals(tag)) {
// 订单支付/确认消息 抓取订单
TradeTradePaidParam tradeTradePaidParam = gson.fromJson(message.getData()+"", TradeTradePaidParam.class);
// 订单id
Long pId = tradeTradePaidParam.getPId();
// 根据订单id查询订单详情
OrderOrderDetailData orderDetail = orderDetail(String.valueOf(pId));
ShopOrderDetail order = orderDetail.getShopOrderDetail();
// 所有的订单数据
List<com.doudian.open.api.order_orderDetail.data.SkuOrderListItem> skuOrderList = order.getSkuOrderList();
// 店铺信息
Long shopId = order.getShopId();
String shopName = order.getShopName();
// 物流信息
List<com.doudian.open.api.order_orderDetail.data.LogisticsInfoItem> logisticsInfo = order.getLogisticsInfo();
/**
* 需判断订单是否存在
* 1.不存在 --- 新增
* 2.存在 --- 更新
* 2.1 需更新的数据 订单状态 退款状态
*/
// 所有在售商品
List<OnSaleProductDO> onSaleProductDOS = onSaleProductMapper.selectList(new QueryWrapper<OnSaleProductDO>().eq("deleted", 0).eq("onSale", true));
// 所有在售商品sku
List<String> skuList = onSaleProductDOS.stream().map(OnSaleProductDO::getSku).toList();
// 买家备注
String buyerWords = order.getBuyerWords();
// 收件人电话
// String postTel = order.getPostTel();
String encryptPostTel = order.getEncryptPostTel();
// 收件人姓名
// String postReceiver = order.getPostReceiver();
String encryptPostReceiver = order.getEncryptPostReceiver();
// 收件人地址
com.doudian.open.api.order_orderDetail.data.PostAddr postAddr = order.getPostAddr();
//
com.doudian.open.api.order_orderDetail.data.Province province = postAddr.getProvince();
//
com.doudian.open.api.order_orderDetail.data.City city = postAddr.getCity();
//
com.doudian.open.api.order_orderDetail.data.Town town = postAddr.getTown();
// 街道/
com.doudian.open.api.order_orderDetail.data.Street street = postAddr.getStreet();
// String detail = postAddr.getDetail();
String encryptDetail = postAddr.getEncryptDetail();
//用户信息
com.doudian.open.api.order_orderDetail.data.UserIdInfo userIdInfo = order.getUserIdInfo();
// String idCardNo = userIdInfo.getIdCardNo();
String encryptIdCardNo = userIdInfo.getEncryptIdCardNo();
// String idCardName = userIdInfo.getIdCardName();
String encryptIdCardName = userIdInfo.getEncryptIdCardName();
List<OrdersDO> orderList = new ArrayList<>();
List<OrdersDO> orderListAll = new ArrayList<>();
for (com.doudian.open.api.order_orderDetail.data.SkuOrderListItem skuOrderListItem : skuOrderList) {
String orderId = skuOrderListItem.getOrderId();
OrdersDO ordersDO = new OrdersDO();
// 售后信息
com.doudian.open.api.order_orderDetail.data.AfterSaleInfo afterSaleInfo = skuOrderListItem.getAfterSaleInfo();
// 售后状态 0-售后初始化 6-售后申请 7-售后退货中 27-拒绝售后申请 12-售后成功 28-售后失败 11-售后已发货 29-退货后拒绝退款 13-售后换货商家发货 14-售后换货用户收货 51-取消成功 53-逆向交易完成
Long afterSaleStatus = afterSaleInfo.getAfterSaleStatus();
// 1-待退款3-退款成功 4-退款失败当买家发起售后后又主动取消售后此时after_sale_status=28并且refund_status=1的状态不变不会流转至4状态
Long refundStatus = afterSaleInfo.getRefundStatus();
// 商品sku 判断是否抓单
String code = skuOrderListItem.getCode();
if(skuList.contains(code)){
for (OnSaleProductDO onSaleProductDO : onSaleProductDOS) {
if(onSaleProductDO.getSku().equals(code)){
// 注入产品生产方式 记录初始产品+接口相关信息
OnSaleProductPreOrderRespVO onSaleProductPreOrder = onSaleProductService.getOnSaleProductPreOrder(onSaleProductDO.getId());
ordersDO.setAutoType(onSaleProductPreOrder.getParentProduct().getAutoType());
ordersDO.setOnSaleProductId(onSaleProductPreOrder.getId());
ordersDO.setProductId(onSaleProductPreOrder.getParentProduct().getId());
ordersDO.setSuperiorProductConfigId(onSaleProductPreOrder.getSuperiorProductConfigRespVO().getId());
ordersDO.setSuperiorApiId(onSaleProductPreOrder.getSuperiorApiRespVO().getId());
}
}
// 在售商品
ordersDO.setSupplierProductSku(code);
Long orderStatus = skuOrderListItem.getOrderStatus();
// 1 待确认/待支付-订单创建完毕;105-已支付; 2-备货中 101-部分发货; 3-已发货全部发货;4- 已取消;5 已完成已收货;
Long HaokaOrderStatus;// 号卡订单状态
Long HaokaOrderRefundStatus;// 号卡订单退款状态
// 解析订单状态
if(orderStatus == 105L){
HaokaOrderStatus = 100L;
ordersDO.setStatus(HaokaOrderStatus);
}else if(orderStatus == 101L || orderStatus == 3L){
HaokaOrderStatus = 500L;
ordersDO.setStatus(HaokaOrderStatus);
}else {
// 更新订单退款状态 订单状态不变
}
ordersDO.setStatusName(skuOrderListItem.getOrderStatusDesc());
// 解析售后状态
if(0L == afterSaleStatus){
// 未进入售后
HaokaOrderRefundStatus = 800L;
} else if(afterSaleStatus == 28L || refundStatus == 1L){
// 未进入售后
HaokaOrderRefundStatus = 800L;
} else if(refundStatus == 3L){
// 进入售后
HaokaOrderRefundStatus = 300L;
} else {
// 进入售后
HaokaOrderRefundStatus = 200L;
}
ordersDO.setRefundStatus(String.valueOf(HaokaOrderRefundStatus));
// 获取计划手机号
for (com.doudian.open.api.order_orderDetail.data.LogisticsInfoItem logisticsInfoItem : logisticsInfo) {
for (com.doudian.open.api.order_orderDetail.data.ProductInfoItem productInfoItem : logisticsInfoItem.getProductInfo()) {
String skuOrderId = productInfoItem.getSkuOrderId();
if(skuOrderId.equals(orderId)){
String productName = productInfoItem.getProductName();
ordersDO.setSupplierProductName(productName);
List<com.doudian.open.api.order_orderDetail.data.SkuSpecsItem> skuSpecs = productInfoItem.getSkuSpecs();
for (com.doudian.open.api.order_orderDetail.data.SkuSpecsItem skuSpec : skuSpecs) {
String name = skuSpec.getName();
String planMobile = getPlanMobile(name);
if(planMobile != null){
ordersDO.setPlanMobile(planMobile);
}
}
}
}
}
// 查询订单是否存在 不存在则新增存在则更新
List<OrdersDO> ordersDOS = ordersMapper.selectList(new QueryWrapper<OrdersDO>().eq("real_source_id", orderId).eq("deleted", 0));
if(CollectionUtil.isNotEmpty(ordersDOS)){
// 更新订单
OrdersDO ordersDO1 = ordersDOS.get(0);
ordersDO1.setRefundStatus(String.valueOf(HaokaOrderRefundStatus));
ordersMapper.updateById(ordersDO1);
}else {
// 新增订单
ordersDO.setShopId(String.valueOf(shopId));
ordersDO.setShopName(shopName);
ordersDO.setRoomId(skuOrderListItem.getRoomIdStr());
// 真实订单
ordersDO.setRealSourceId(orderId);
ordersDO.setSourceId(orderId);
// 买家备注
ordersDO.setBuyerMemo(buyerWords);
//证件信息
// ordersDO.setIdCardNum(idCardNo);
ordersDO.setEncryptIdCardNum(encryptIdCardNo);
// ordersDO.setIdCardName(idCardName);
ordersDO.setEncryptIdCardName(encryptIdCardName);
// 收件人信息
// ordersDO.setAddressMobile(postTel);
ordersDO.setEncryptAddressMobile(encryptPostTel);
// ordersDO.setAddressName(postReceiver);
ordersDO.setEncryptAddressName(encryptPostReceiver);
// 收件人地址
ordersDO.setAddressProvince(province.getName());
ordersDO.setAddressProvinceCode(province.getId());
//
ordersDO.setAddressCity(city.getName());
ordersDO.setAddressCityCode(city.getId());
//
ordersDO.setAddressDistrict(town.getName());
ordersDO.setAddressDistrictCode(town.getId());
// 街道/
ordersDO.setTown(street.getName());
// 详细地址
// ordersDO.setAddress(detail);
ordersDO.setEncryptAddress(encryptDetail);
orderList.add(ordersDO);
}
}
}
orderListAll.addAll(orderList);
ordersMapper.insertBatch(orderList);
// 执行解密解密后更新数据库
for (List<OrdersDO> ordersDOS : GroupListUtil.groupList(orderListAll, 50)) {
batchDecryptOrderAndUpdate(ordersDOS);
}
} else if("doudian_trade_TradeCanceled".equals(tag)) {
// 订单取消消息
TradeTradeCanceledParam tradeTradeCanceledParam = gson.fromJson(message.getData()+"", TradeTradeCanceledParam.class);
} else if("doudian_trade_TradePending".equals(tag)) {
// 订单已支付待处理
TradeTradePendingParam tradeTradePendingParam = gson.fromJson(message.getData()+"", TradeTradePendingParam.class);
} else {
// 未接入的消息类型
}
}
}
/**
* 将订单数据插入到数据库
* @param orders 订单数据
*/
private void insertOrdersToDatabase(List<ShopOrderListItem> orders) {
// 所有在售商品
List<OnSaleProductDO> onSaleProductDOS = onSaleProductMapper.selectList(new QueryWrapper<OnSaleProductDO>().eq("deleted", 0).eq("onSale", true));
// 所有在售商品sku
List<String> skuList = onSaleProductDOS.stream().map(OnSaleProductDO::getSku).toList();
//数据分片处理 分片理由 每次最多徐解密50条数据一条订单解密5个数据10个订单解密50条
List<OrdersDO> orderListAll = new ArrayList<>();
List<List<ShopOrderListItem>> orderGroupList = GroupListUtil.groupList(orders, 10);
// orderGroupList size 最大为10
for (List<ShopOrderListItem> orderListItems : orderGroupList) {
List<OrdersDO> orderList = new ArrayList<>();
for (ShopOrderListItem order : orderListItems) {
// 抖音订单id
// String orderId = order.getOrderId();
// 买家备注
String buyerWords = order.getBuyerWords();
// 店铺id
Long shopId = order.getShopId();
// 店铺名称
String shopName = order.getShopName();
// 收件人电话
// String postTel = order.getPostTel();
String encryptPostTel = order.getEncryptPostTel();
// 收件人姓名
// String postReceiver = order.getPostReceiver();
String encryptPostReceiver = order.getEncryptPostReceiver();
// 收件人地址
PostAddr postAddr = order.getPostAddr();
//
Province province = postAddr.getProvince();
//
City city = postAddr.getCity();
//
Town town = postAddr.getTown();
// 街道/
Street street = postAddr.getStreet();
// String detail = postAddr.getDetail();
String encryptDetail = postAddr.getEncryptDetail();
//用户信息
UserIdInfo userIdInfo = order.getUserIdInfo();
// String idCardNo = userIdInfo.getIdCardNo();
String encryptIdCardNo = userIdInfo.getEncryptIdCardNo();
// String idCardName = userIdInfo.getIdCardName();
String encryptIdCardName = userIdInfo.getEncryptIdCardName();
// 商品信息
List<SkuOrderListItem> skuOrderList = order.getSkuOrderList();
List<LogisticsInfoItem> logisticsInfo = order.getLogisticsInfo();
for (SkuOrderListItem skuOrderListItem : skuOrderList) {
String orderId = skuOrderListItem.getOrderId();
OrdersDO ordersDO = new OrdersDO();
// 售后信息
AfterSaleInfo afterSaleInfo = skuOrderListItem.getAfterSaleInfo();
// 售后状态 0-售后初始化 6-售后申请 7-售后退货中 27-拒绝售后申请 12-售后成功 28-售后失败 11-售后已发货 29-退货后拒绝退款 13-售后换货商家发货 14-售后换货用户收货 51-取消成功 53-逆向交易完成
Long afterSaleStatus = afterSaleInfo.getAfterSaleStatus();
// 1-待退款3-退款成功 4-退款失败当买家发起售后后又主动取消售后此时after_sale_status=28并且refund_status=1的状态不变不会流转至4状态
Long refundStatus = afterSaleInfo.getRefundStatus();
// 商品sku 判断是否抓单
String code = skuOrderListItem.getCode();
if(skuList.contains(code)){
for (OnSaleProductDO onSaleProductDO : onSaleProductDOS) {
if(onSaleProductDO.getSku().equals(code)){
// 注入产品生产方式 记录初始产品+接口相关信息
OnSaleProductPreOrderRespVO onSaleProductPreOrder = onSaleProductService.getOnSaleProductPreOrder(onSaleProductDO.getId());
ordersDO.setAutoType(onSaleProductPreOrder.getParentProduct().getAutoType());
ordersDO.setOnSaleProductId(onSaleProductPreOrder.getId());
ordersDO.setProductId(onSaleProductPreOrder.getParentProduct().getId());
ordersDO.setSuperiorProductConfigId(onSaleProductPreOrder.getSuperiorProductConfigRespVO().getId());
ordersDO.setSuperiorApiId(onSaleProductPreOrder.getSuperiorApiRespVO().getId());
}
}
// 在售商品
ordersDO.setSupplierProductSku(code);
Long orderStatus = skuOrderListItem.getOrderStatus();
// 1 待确认/待支付-订单创建完毕;105-已支付; 2-备货中 101-部分发货; 3-已发货全部发货;4- 已取消;5 已完成已收货;
Long HaokaOrderStatus;// 号卡订单状态
Long HaokaOrderRefundStatus;// 号卡订单退款状态
// 解析订单状态
if(orderStatus == 105L){
HaokaOrderStatus = 100L;
ordersDO.setStatus(HaokaOrderStatus);
}else if(orderStatus == 101L || orderStatus == 3L){
HaokaOrderStatus = 500L;
ordersDO.setStatus(HaokaOrderStatus);
}else {
// 更新订单退款状态 订单状态不变
}
// 解析售后状态
if(0L == afterSaleStatus){
// 未进入售后
HaokaOrderRefundStatus = 800L;
} else if(afterSaleStatus == 28L || refundStatus == 1L){
// 未进入售后
HaokaOrderRefundStatus = 800L;
} else if(refundStatus == 3L){
// 进入售后
HaokaOrderRefundStatus = 300L;
} else {
// 进入售后
HaokaOrderRefundStatus = 200L;
}
ordersDO.setRefundStatus(String.valueOf(HaokaOrderRefundStatus));
ordersDO.setStatusName(skuOrderListItem.getOrderStatusDesc());
// 获取计划手机号
for (LogisticsInfoItem logisticsInfoItem : logisticsInfo) {
for (ProductInfoItem productInfoItem : logisticsInfoItem.getProductInfo()) {
String skuOrderId = productInfoItem.getSkuOrderId();
if(skuOrderId.equals(orderId)){
String productName = productInfoItem.getProductName();
ordersDO.setSupplierProductName(productName);
List<SkuSpecsItem> skuSpecs = productInfoItem.getSkuSpecs();
for (SkuSpecsItem skuSpec : skuSpecs) {
String name = skuSpec.getName();
String planMobile = getPlanMobile(name);
if(planMobile != null){
ordersDO.setPlanMobile(planMobile);
}
}
}
}
}
// 查询订单是否存在 不存在则新增存在则更新
List<OrdersDO> ordersDOS = ordersMapper.selectList(new QueryWrapper<OrdersDO>().eq("real_source_id", orderId).eq("deleted", 0));
if(CollectionUtil.isNotEmpty(ordersDOS)){
// 更新订单
OrdersDO ordersDO1 = ordersDOS.get(0);
ordersDO1.setRefundStatus(String.valueOf(HaokaOrderRefundStatus));
ordersMapper.updateById(ordersDO1);
}else {
// 新增订单
ordersDO.setShopId(String.valueOf(shopId));
ordersDO.setShopName(shopName);
// 真实订单
ordersDO.setRealSourceId(orderId);
ordersDO.setSourceId(orderId);
// 买家备注
ordersDO.setBuyerMemo(buyerWords);
//证件信息
// ordersDO.setIdCardNum(idCardNo);
ordersDO.setEncryptIdCardNum(encryptIdCardNo);
// ordersDO.setIdCardName(idCardName);
ordersDO.setEncryptIdCardName(encryptIdCardName);
// 收件人信息
// ordersDO.setAddressMobile(postTel);
ordersDO.setEncryptAddressMobile(encryptPostTel);
// ordersDO.setAddressName(postReceiver);
ordersDO.setEncryptAddressName(encryptPostReceiver);
// 收件人地址
ordersDO.setAddressProvince(province.getName());
ordersDO.setAddressProvinceCode(province.getId());
//
ordersDO.setAddressCity(city.getName());
ordersDO.setAddressCityCode(city.getId());
//
ordersDO.setAddressDistrict(town.getName());
ordersDO.setAddressDistrictCode(town.getId());
// 街道/
ordersDO.setTown(street.getName());
// 详细地址
// ordersDO.setAddress(detail);
ordersDO.setEncryptAddress(encryptDetail);
orderList.add(ordersDO);
}
}
}
}
orderListAll.addAll(orderList);
ordersMapper.insertBatch(orderList);
}
// 执行解密解密后更新数据库
for (List<OrdersDO> ordersDOS : GroupListUtil.groupList(orderListAll, 50)) {
batchDecryptOrderAndUpdate(ordersDOS);
}
}
private CipherInfosItem createCipherItem(String cipherText, String authId) {
CipherInfosItem item = new CipherInfosItem();
item.setCipherText(cipherText);
item.setAuthId(authId);
return item;
}
private void updateOrderFields(OrdersDO order, String cipherText, String decryptText) {
// 检查并更新每个加密字段
if (cipherText.equals(order.getEncryptAddress())) {
order.setEncryptAddress(decryptText); // 更新为解密后的值
} else if (cipherText.equals(order.getEncryptIdCardName())) {
order.setEncryptIdCardName(decryptText);
} else if (cipherText.equals(order.getEncryptAddressName())) {
order.setEncryptAddressName(decryptText);
} else if (cipherText.equals(order.getEncryptIdCardNum())) {
order.setEncryptIdCardNum(decryptText);
} else if (cipherText.equals(order.getEncryptAddressMobile())) {
order.setEncryptAddressMobile(decryptText);
}
}
private void batchDecryptOrderAndUpdate(List<OrdersDO> orderList) {
List<CipherInfosItem> cipherInfos = new ArrayList<>();
for (OrdersDO ordersDO : orderList) {
// 构建 CipherInfosItem 列表
cipherInfos.add(createCipherItem(ordersDO.getEncryptAddress(), ordersDO.getRealSourceId()));
cipherInfos.add(createCipherItem(ordersDO.getEncryptIdCardName(), ordersDO.getRealSourceId()));
cipherInfos.add(createCipherItem(ordersDO.getEncryptAddressName(), ordersDO.getRealSourceId()));
cipherInfos.add(createCipherItem(ordersDO.getEncryptIdCardNum(), ordersDO.getRealSourceId()));
cipherInfos.add(createCipherItem(ordersDO.getEncryptAddressMobile(), ordersDO.getRealSourceId()));
}
List<DecryptInfosItem> objects = batchDecrypt(cipherInfos);
for (DecryptInfosItem object : objects) {
String authId = object.getAuthId();
String decryptText = object.getDecryptText();
String cipherText = object.getCipherText();
for (OrdersDO ordersDO : orderList) {
if (ordersDO.getRealSourceId().equals(authId)) {
// 将解密后的字段替换到 order
updateOrderFields(ordersDO, cipherText, decryptText);
}
}
}
ordersMapper.updateBatch(orderList);
}
/**
* 使用正则表达式获取手机号
* @param skuName
* @return
*/
private String getPlanMobile(String skuName) {
// 去除空格
String noSpace = StringUtils.deleteWhitespace(skuName);
String pattern = "(?<=\\/)\\d{11}";
Pattern compile = Pattern.compile(pattern);
Matcher matcher = compile.matcher(noSpace);
if(matcher.find()){
// 找到匹配手机号
return matcher.group(1);
}else {
return null;
}
}
/**
* 分页查询订单
* @param accessToken 访问令牌
* @param createTimeStart 创建时间起始
* @param createTimeEnd 创建时间结束
* @return 所有订单数据
*/
private List<ShopOrderListItem> fetchOrders(AccessToken accessToken, long createTimeStart, long createTimeEnd) {
OrderSearchListRequest request = new OrderSearchListRequest();
OrderSearchListParam param = request.getParam();
// param.setProduct("3473196049974326153");
// param.setBType(2L);
param.setAfterSaleStatusDesc("refund_success");
// param.setTrackingNo("435435");
// param.setPresellType(0L);
// param.setOrderType(1L);
param.setCreateTimeStart(createTimeStart);
param.setCreateTimeEnd(createTimeEnd);
// param.setAbnormalOrder(1L);
// param.setTradeType(1L);
// param.setUpdateTimeStart(1617355413L);
// param.setUpdateTimeEnd(1617355413L);
param.setSize(100L);
param.setPage(0L);
param.setOrderBy("create_time");
param.setOrderAsc(false);
// param.setFulfilStatus("no_accept");
List<ShopOrderListItem> allOrders = new ArrayList<>();
while (true) {
try {
OrderSearchListResponse response = request.execute(accessToken);
OrderSearchListData data = response.getData();
List<ShopOrderListItem> shopOrderList = data.getShopOrderList();
Long total = data.getTotal();
Long size = data.getSize();
Long currentPage = data.getPage();
allOrders.addAll(shopOrderList);
if (currentPage * size >= total) {
break;
}
param.setPage(++currentPage);
} catch (Exception e) {
// do nothing
break;
}
}
return allOrders;
}
/**
* 批量解密接口
* @param cipherInfos 待解密数据 一次性最多50条
* @return
*/
private List<DecryptInfosItem> batchDecrypt(List<CipherInfosItem> cipherInfos){
GlobalConfig.initAppKey(SYNC_APP_KEY);
GlobalConfig.initAppSecret(SYNC_APP_SECRET);
//入参为code
AccessToken accessToken= AccessTokenBuilder.build(SYNC_SHOP_ID);
OrderBatchDecryptRequest request = new OrderBatchDecryptRequest();
OrderBatchDecryptParam param = request.getParam();
param.setCipherInfos(cipherInfos);
OrderBatchDecryptResponse response = request.execute(accessToken);
OrderBatchDecryptData data = response.getData();
List<DecryptInfosItem> decryptInfos = data.getDecryptInfos();
return decryptInfos;
}
public OrderOrderDetailData orderDetail(String shopOrderId) {
GlobalConfig.initAppKey(SYNC_APP_KEY);
GlobalConfig.initAppSecret(SYNC_APP_SECRET);
//入参为code
AccessToken accessToken= AccessTokenBuilder.build(SYNC_SHOP_ID);
OrderOrderDetailRequest request = new OrderOrderDetailRequest();
OrderOrderDetailParam param = request.getParam();
param.setShopOrderId(shopOrderId);
OrderOrderDetailResponse response = request.execute(accessToken);
return response.getData();
}
public void refreshToken() {
//设置appKey和appSecret全局设置一次
GlobalConfig.initAppKey("7381306825771091495");
GlobalConfig.initAppSecret("09c06553-b77c-4712-aed9-84cbfe16ca13");
//入参为code
AccessToken accessToken= AccessTokenBuilder.build(20811777L);
String refreshToken = accessToken.getRefreshToken();//16dde1a3-2d6f-4946-aef2-afbd29d2eb92是code
TokenRefreshRequest request = new TokenRefreshRequest();
TokenRefreshParam param = request.getParam();
param.setRefreshToken(refreshToken);
param.setGrantType("refresh_token");
TokenRefreshResponse response = request.execute(accessToken);
String refreshToken1 = response.getData().getRefreshToken();
System.out.println(response);
}
}

View File

@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.haoka.service.orderCatch;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.po.Message;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.vo.OrderCatchRepVO;
import com.doudian.open.core.AccessToken;
import java.util.List;
public interface OrderCatchService {
/**
* 查询订单列表
*/
void syncOrder(AccessToken accessToken, long createTimeStart, long createTimeEnd);
void catchOrders(List<Message> catchRepVO);
}

View File

@ -114,7 +114,7 @@ public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, OrdersDO> imple
OnSaleProductPreOrderRespVO onSaleProductPreOrder = onSaleProductService.getOnSaleProductPreOrder(createReqVO.getOnSaleProductId());
orders.setAutoType(onSaleProductPreOrder.getParentProduct().getAutoType());
orders.setOnSaleProductId(onSaleProductPreOrder.getId());
orders.setProducerId(onSaleProductPreOrder.getParentProduct().getId());
orders.setProductId(onSaleProductPreOrder.getParentProduct().getId());
orders.setSuperiorProductConfigId(onSaleProductPreOrder.getSuperiorProductConfigRespVO().getId());
orders.setSuperiorApiId(onSaleProductPreOrder.getSuperiorApiRespVO().getId());
// 订单就绪

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.haoka.service.ordersynclog;
import java.util.*;
import jakarta.validation.*;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.*;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* 抓单记录 Service 接口
*
* @author 超级管理员
*/
public interface OrderSyncLogService {
/**
* 创建抓单记录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createOrderSyncLog(@Valid OrderSyncLogSaveReqVO createReqVO);
/**
* 更新抓单记录
*
* @param updateReqVO 更新信息
*/
void updateOrderSyncLog(@Valid OrderSyncLogSaveReqVO updateReqVO);
/**
* 删除抓单记录
*
* @param id 编号
*/
void deleteOrderSyncLog(Long id);
/**
* 获得抓单记录
*
* @param id 编号
* @return 抓单记录
*/
OrderSyncLogDO getOrderSyncLog(Long id);
/**
* 获得抓单记录分页
*
* @param pageReqVO 分页查询
* @return 抓单记录分页
*/
PageResult<OrderSyncLogDO> getOrderSyncLogPage(OrderSyncLogPageReqVO pageReqVO);
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.haoka.service.ordersynclog;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.*;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.haoka.dal.mysql.ordersynclog.OrderSyncLogMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.haoka.enums.ErrorCodeConstants.*;
/**
* 抓单记录 Service 实现类
*
* @author 超级管理员
*/
@Service
@Validated
public class OrderSyncLogServiceImpl implements OrderSyncLogService {
@Resource
private OrderSyncLogMapper orderSyncLogMapper;
@Override
public Long createOrderSyncLog(OrderSyncLogSaveReqVO createReqVO) {
// 插入
OrderSyncLogDO orderSyncLog = BeanUtils.toBean(createReqVO, OrderSyncLogDO.class);
orderSyncLogMapper.insert(orderSyncLog);
// 返回
return orderSyncLog.getId();
}
@Override
public void updateOrderSyncLog(OrderSyncLogSaveReqVO updateReqVO) {
// 校验存在
validateOrderSyncLogExists(updateReqVO.getId());
// 更新
OrderSyncLogDO updateObj = BeanUtils.toBean(updateReqVO, OrderSyncLogDO.class);
orderSyncLogMapper.updateById(updateObj);
}
@Override
public void deleteOrderSyncLog(Long id) {
// 校验存在
validateOrderSyncLogExists(id);
// 删除
orderSyncLogMapper.deleteById(id);
}
private void validateOrderSyncLogExists(Long id) {
if (orderSyncLogMapper.selectById(id) == null) {
throw exception(ORDER_SYNC_LOG_NOT_EXISTS);
}
}
@Override
public OrderSyncLogDO getOrderSyncLog(Long id) {
return orderSyncLogMapper.selectById(id);
}
@Override
public PageResult<OrderSyncLogDO> getOrderSyncLogPage(OrderSyncLogPageReqVO pageReqVO) {
return orderSyncLogMapper.selectPage(pageReqVO);
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.haoka.utils;
import java.util.ArrayList;
import java.util.List;
/**
* @author xiongxiong
* @description: 数组分片工具
* @date 2024/12/30
*/
public class GroupListUtil {
public static <T> List<List<T>> groupList(List<T> originalList, int groupSize) {
if (groupSize <= 0) {
throw new IllegalArgumentException("Group size must be positive.");
}
List<List<T>> result = new ArrayList<>();
int size = originalList.size();
for (int i = 0; i < size; i += groupSize) {
int endIndex = i + groupSize;
if (endIndex > size) {
endIndex = size;
}
List<T> sub = new ArrayList<>(originalList.subList(i, endIndex));
result.add(sub);
}
return result;
}
}

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.haoka.dal.mysql.ordersynclog.OrderSyncLogMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.haoka.service.orderCatch;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.haoka.controller.admin.orderCatch.po.Message;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.doudian.open.api.order_orderDetail.data.OrderOrderDetailData;
import com.doudian.open.gson.Gson;
import com.doudian.open.msg.refund_RefundCreated.param.RefundRefundCreatedParam;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import java.util.List;
@Import(DouDianOrderCatchServiceImpl.class)
public class DouDianOrderCatchServiceImplTest extends BaseDbUnitTest {
@Resource
private DouDianOrderCatchServiceImpl orderCatchService;
@Test
public void syncOrder() {
// long createTimeStart, long createTimeEnd
// orderCatchService.syncOrder();
}
@Test
public void testOrderDetail() {
// 创建 DouDianOrderCatchServiceImpl 对象
DouDianOrderCatchServiceImpl service = new DouDianOrderCatchServiceImpl();
// 模拟传入的 shopOrderId
String shopOrderId = "6939514703116113670";
// 调用 orderDetail 方法
OrderOrderDetailData data = service.orderDetail(shopOrderId);
// 进行断言检查返回的数据是否符合预期
System.out.println(data);
// 可以根据具体的业务逻辑添加更多的断言
}
@Test
public void testOrderDetail1() {
String orderString = "[{\"tag\":\"xxx\",\"msgId\":\"0443477XXXXXXX48874::xxx:1618990587:6837374XXXXXXX89128\",\"data\":{\"aftersale_id\":7000893114000949000,\"aftersale_status\":6,\"aftersale_type\":2,\"apply_time\":1630022518,\"p_id\":4835804142146757000,\"reason_code\":1,\"refund_amount\":8900,\"refund_post_amount\":0,\"refund_voucher_num\":0,\"return_book_info\":{\"encrypt_addr\":\"北京市海淀区海淀街道海淀街道海淀大街x号xx大厦x楼xxx室\",\"send_type\":1,\"time_begin\":\"09:00\",\"time_end\":\"11:00\"},\"s_id\":4835804142146757000,\"shop_id\":10437917,\"update_time\":\"2022-09-02T10:27:50+08:00\"}}]";
Gson gson = new Gson();
List<Message> parse = JSON.parseArray(orderString, Message.class);
for (Message message : parse) {
RefundRefundCreatedParam refundRefundCreatedParam = gson.fromJson(message.getData()+"", RefundRefundCreatedParam.class);
System.out.println();
}
}
}

View File

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.haoka.service.ordersynclog;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import jakarta.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.haoka.controller.admin.ordersynclog.vo.*;
import cn.iocoder.yudao.module.haoka.dal.dataobject.ordersynclog.OrderSyncLogDO;
import cn.iocoder.yudao.module.haoka.dal.mysql.ordersynclog.OrderSyncLogMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.haoka.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link OrderSyncLogServiceImpl} 的单元测试类
*
* @author 超级管理员
*/
@Import(OrderSyncLogServiceImpl.class)
public class OrderSyncLogServiceImplTest extends BaseDbUnitTest {
@Resource
private OrderSyncLogServiceImpl orderSyncLogService;
@Resource
private OrderSyncLogMapper orderSyncLogMapper;
@Test
public void testCreateOrderSyncLog_success() {
// 准备参数
OrderSyncLogSaveReqVO createReqVO = randomPojo(OrderSyncLogSaveReqVO.class).setId(null);
// 调用
Long orderSyncLogId = orderSyncLogService.createOrderSyncLog(createReqVO);
// 断言
assertNotNull(orderSyncLogId);
// 校验记录的属性是否正确
OrderSyncLogDO orderSyncLog = orderSyncLogMapper.selectById(orderSyncLogId);
assertPojoEquals(createReqVO, orderSyncLog, "id");
}
@Test
public void testUpdateOrderSyncLog_success() {
// mock 数据
OrderSyncLogDO dbOrderSyncLog = randomPojo(OrderSyncLogDO.class);
orderSyncLogMapper.insert(dbOrderSyncLog);// @Sql: 先插入出一条存在的数据
// 准备参数
OrderSyncLogSaveReqVO updateReqVO = randomPojo(OrderSyncLogSaveReqVO.class, o -> {
o.setId(dbOrderSyncLog.getId()); // 设置更新的 ID
});
// 调用
orderSyncLogService.updateOrderSyncLog(updateReqVO);
// 校验是否更新正确
OrderSyncLogDO orderSyncLog = orderSyncLogMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, orderSyncLog);
}
@Test
public void testUpdateOrderSyncLog_notExists() {
// 准备参数
OrderSyncLogSaveReqVO updateReqVO = randomPojo(OrderSyncLogSaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> orderSyncLogService.updateOrderSyncLog(updateReqVO), ORDER_SYNC_LOG_NOT_EXISTS);
}
@Test
public void testDeleteOrderSyncLog_success() {
// mock 数据
OrderSyncLogDO dbOrderSyncLog = randomPojo(OrderSyncLogDO.class);
orderSyncLogMapper.insert(dbOrderSyncLog);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbOrderSyncLog.getId();
// 调用
orderSyncLogService.deleteOrderSyncLog(id);
// 校验数据不存在了
assertNull(orderSyncLogMapper.selectById(id));
}
@Test
public void testDeleteOrderSyncLog_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> orderSyncLogService.deleteOrderSyncLog(id), ORDER_SYNC_LOG_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值然后删除 @Disabled 注解
public void testGetOrderSyncLogPage() {
// mock 数据
OrderSyncLogDO dbOrderSyncLog = randomPojo(OrderSyncLogDO.class, o -> { // 等会查询到
o.setSyncType(null);
o.setSyncShopId(null);
o.setSyncEndTime(null);
o.setSyncCreateTime(null);
o.setCreateTime(null);
});
orderSyncLogMapper.insert(dbOrderSyncLog);
// 测试 syncType 不匹配
orderSyncLogMapper.insert(cloneIgnoreId(dbOrderSyncLog, o -> o.setSyncType(null)));
// 测试 syncShopId 不匹配
orderSyncLogMapper.insert(cloneIgnoreId(dbOrderSyncLog, o -> o.setSyncShopId(null)));
// 测试 syncEndTime 不匹配
orderSyncLogMapper.insert(cloneIgnoreId(dbOrderSyncLog, o -> o.setSyncEndTime(null)));
// 测试 syncCreateTime 不匹配
orderSyncLogMapper.insert(cloneIgnoreId(dbOrderSyncLog, o -> o.setSyncCreateTime(null)));
// 测试 createTime 不匹配
orderSyncLogMapper.insert(cloneIgnoreId(dbOrderSyncLog, o -> o.setCreateTime(null)));
// 准备参数
OrderSyncLogPageReqVO reqVO = new OrderSyncLogPageReqVO();
reqVO.setSyncType(null);
reqVO.setSyncShopId(null);
// reqVO.setSyncEndTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// reqVO.setSyncCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<OrderSyncLogDO> pageResult = orderSyncLogService.getOrderSyncLogPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbOrderSyncLog, pageResult.getList().get(0));
}
}

View File

@ -0,0 +1,9 @@
-- 订单新增参数
delete from haoka_orders;
ALTER TABLE haoka_orders
ADD COLUMN shop_id varchar(50) COMMENT '店铺ID' AFTER is_urgent,
ADD COLUMN shop_name varchar(50) COMMENT '店铺名称' AFTER shop_id,
ADD COLUMN room_id varchar(50) COMMENT '直播间ID' AFTER shop_name,
ADD COLUMN room_name varchar(50) COMMENT '直播间名称' AFTER room_id,
ADD COLUMN sales_attribution varchar(50) COMMENT '销售归属' AFTER room_name,
ADD COLUMN sales_attribution_name varchar(50) COMMENT '销售归属名称' AFTER sales_attribution;

View File

@ -0,0 +1,70 @@
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'抓单记录管理', '', 2, 0, 3016,
'order-sync-log', '', 'haoka/ordersynclog/index', 0, 'OrderSyncLog'
);
-- 按钮父菜单ID
-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'抓单记录查询', 'haoka:order-sync-log:query', 3, 1, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'抓单记录创建', 'haoka:order-sync-log:create', 3, 2, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'抓单记录更新', 'haoka:order-sync-log:update', 3, 3, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'抓单记录删除', 'haoka:order-sync-log:delete', 3, 4, @parentId,
'', '', '', 0
);
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'抓单记录导出', 'haoka:order-sync-log:export', 3, 5, @parentId,
'', '', '', 0
);
CREATE TABLE `haoka_order_sync_log` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`sync_type` varchar(50) DEFAULT NULL COMMENT '同步类型(电商渠道)',
`sync_shop_id` varchar(50) DEFAULT NULL COMMENT '同步店铺ID',
`sync_end_time` bigint(20) DEFAULT NULL COMMENT '同步结束时间',
`sync_create_time` bigint(20) DEFAULT NULL COMMENT '同步开始时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='抓单记录表';