mirror of https://github.com/jeecgboot/jeecg-boot
commit
d6090138d8
|
@ -0,0 +1,15 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.mabang.dochangeorder;
|
||||||
|
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.Request;
|
||||||
|
|
||||||
|
public class AddOrderAbnNumberRequest extends Request {
|
||||||
|
public AddOrderAbnNumberRequest(AddOrderAbnNumberRequestBody body) {
|
||||||
|
super(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangeOrderResponse send() {
|
||||||
|
String jsonString = rawSend().getBody();
|
||||||
|
return ChangeOrderResponse.parse(jsonString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.mabang.dochangeorder;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.RequestBody;
|
||||||
|
|
||||||
|
public class AddOrderAbnNumberRequestBody implements RequestBody {
|
||||||
|
|
||||||
|
private String platformOrderId;
|
||||||
|
private final String abnNumber;
|
||||||
|
|
||||||
|
// 韩国税号已添加在马帮系统中的ID是1042522
|
||||||
|
private static final String KOREAN_TAX_NUMBER_LABEL_ID = "1042522";
|
||||||
|
|
||||||
|
public AddOrderAbnNumberRequestBody(String platformOrderId, String abnNumber) {
|
||||||
|
this.platformOrderId = platformOrderId;
|
||||||
|
this.abnNumber = abnNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String api() {
|
||||||
|
return "order-do-change-order";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject parameters() {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
putNonNull(json, "platformOrderId", platformOrderId);
|
||||||
|
putNonNull(json, "abnnumber", abnNumber);
|
||||||
|
JSONArray labelId = new JSONArray();
|
||||||
|
labelId.add(KOREAN_TAX_NUMBER_LABEL_ID);
|
||||||
|
putNonNull(json, "labelId", labelId);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPlatformOrderId() {
|
||||||
|
return platformOrderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlatformOrderId(String platformOrderId) {
|
||||||
|
this.platformOrderId = platformOrderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> void putNonNull(JSONObject json, String key, E value) {
|
||||||
|
if (value != null) {
|
||||||
|
json.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,6 +89,11 @@ public class Order {
|
||||||
*/
|
*/
|
||||||
@JSONField(name = "postCode")
|
@JSONField(name = "postCode")
|
||||||
private String postcode;
|
private String postcode;
|
||||||
|
/**
|
||||||
|
* 税号
|
||||||
|
*/
|
||||||
|
@JSONField(name = "abnnumber")
|
||||||
|
private String taxNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
|
|
|
@ -22,6 +22,8 @@ public class OrderListRequestBody implements RequestBody {
|
||||||
private String cursor = "";
|
private String cursor = "";
|
||||||
private Integer page = 1;
|
private Integer page = 1;
|
||||||
private boolean hasNext = true;
|
private boolean hasNext = true;
|
||||||
|
// 1.Orders of any status 2.Default value, must set status
|
||||||
|
private final static String ALL = "1";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String api() {
|
public String api() {
|
||||||
|
@ -31,7 +33,11 @@ public class OrderListRequestBody implements RequestBody {
|
||||||
@Override
|
@Override
|
||||||
public JSONObject parameters() {
|
public JSONObject parameters() {
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
|
if (platformOrderIds != null) {
|
||||||
|
putNonNull(json, "allstatus", ALL);
|
||||||
|
} else {
|
||||||
putNonNull(json, "status", status, OrderStatus::getCode);
|
putNonNull(json, "status", status, OrderStatus::getCode);
|
||||||
|
}
|
||||||
putNonNull(json, "platformOrderIds", platformOrderIds, (ids) -> String.join(",", ids));
|
putNonNull(json, "platformOrderIds", platformOrderIds, (ids) -> String.join(",", ids));
|
||||||
if(datetimeType != null && platformOrderIds == null){
|
if(datetimeType != null && platformOrderIds == null){
|
||||||
putNonNull(json, datetimeType.text() + "Start", startDate, formatter::format);
|
putNonNull(json, datetimeType.text() + "Start", startDate, formatter::format);
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class CreateFulfillmentRequest extends ShopifyRequest {
|
||||||
POST_NL("https://postnl.post/", "PostNL International Mail", "LS[0-9]{9}NL"),
|
POST_NL("https://postnl.post/", "PostNL International Mail", "LS[0-9]{9}NL"),
|
||||||
COLI_COLI("https://www.colicoli.fr/trackings?id=%s", "Coli Coli", "CC[0-9]{14}[A-Z]*"),
|
COLI_COLI("https://www.colicoli.fr/trackings?id=%s", "Coli Coli", "CC[0-9]{14}[A-Z]*"),
|
||||||
LUXEMBOURG_POST("https://www.post.lu/particuliers/colis-courrier/track-and-trace#/search", "Luxembourg Post", "LL[0-9]{9}LU"),
|
LUXEMBOURG_POST("https://www.post.lu/particuliers/colis-courrier/track-and-trace#/search", "Luxembourg Post", "LL[0-9]{9}LU"),
|
||||||
|
CJ_LOGISTICS("https://www.cjlogistics.com/ko/tool/parcel/tracking", "CJ대한통운", "57575[0-9]{7}"),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String trackingUrl;
|
private final String trackingUrl;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.shopify;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
|
public class GetOrderListRequest extends ShopifyRequest {
|
||||||
|
|
||||||
|
public GetOrderListRequest(ShopifyRequestBody body) {
|
||||||
|
super(HttpMethod.GET, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JSONObject generateJson(ShopifyRequestBody body) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.shopify;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GetOrderListRequestBody extends ShopifyRequestBody {
|
||||||
|
|
||||||
|
public static final String ENDPOINT = "orders.json?status=open&limit=250&fields=id,note&ids=%s";
|
||||||
|
|
||||||
|
private final List<String> ids;
|
||||||
|
|
||||||
|
public GetOrderListRequestBody(String sitePrefix, String shopToken, List<String> ids) {
|
||||||
|
super(sitePrefix, shopToken);
|
||||||
|
this.ids = ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String endpoint() {
|
||||||
|
return String.format(ENDPOINT, String.join(",", ids));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.shopify;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class GetOrderListResponse {
|
||||||
|
|
||||||
|
@JsonProperty("orders")
|
||||||
|
private List<Order> orders;
|
||||||
|
|
||||||
|
public GetOrderListResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetOrderListResponse(List<Order> orders) {
|
||||||
|
this.orders = orders;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.jeecg.modules.business.domain.api.shopify;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class Order {
|
||||||
|
|
||||||
|
@JSONField(deserialize = false)
|
||||||
|
private BigInteger id;
|
||||||
|
|
||||||
|
@JsonProperty("note")
|
||||||
|
private String note;
|
||||||
|
|
||||||
|
public Order() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNote() {
|
||||||
|
return note != null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class ShopifyRequest {
|
public abstract class ShopifyRequest {
|
||||||
private final static String BASE_URL = "https://%1$s.myshopify.com/admin/api/2022-10/%2$s";
|
private final static String BASE_URL = "https://%1$s.myshopify.com/admin/api/2024-01/%2$s";
|
||||||
private final static String SHOPIFY_TOKEN_HEADER_NAME = "X-Shopify-Access-Token";
|
private final static String SHOPIFY_TOKEN_HEADER_NAME = "X-Shopify-Access-Token";
|
||||||
|
|
||||||
private final HttpMethod method;
|
private final HttpMethod method;
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
package org.jeecg.modules.business.domain.job;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.codehaus.jettison.json.JSONArray;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
import org.jeecg.common.api.dto.message.TemplateMessageDTO;
|
||||||
|
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.*;
|
||||||
|
import org.jeecg.modules.business.domain.api.mabang.getorderlist.*;
|
||||||
|
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||||
|
import org.jeecg.modules.business.entity.PlatformOrderShopSync;
|
||||||
|
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class AddOrderAbnNumberJob implements Job {
|
||||||
|
|
||||||
|
private static final List<String> DEFAULT_INCLUDED_SHOPS = Arrays.asList("JCH8 KR");
|
||||||
|
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||||
|
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 300;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IPlatformOrderService platformOrderService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
List<String> shops = DEFAULT_INCLUDED_SHOPS;
|
||||||
|
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||||
|
String parameter = ((String) jobDataMap.get("parameter"));
|
||||||
|
if (parameter != null) {
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject(parameter);
|
||||||
|
if (!jsonObject.isNull("includedShops")) {
|
||||||
|
JSONArray shopsArray = jsonObject.getJSONArray("includedShops");
|
||||||
|
List<String> shopList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < shopsArray.length(); i++) {
|
||||||
|
shopList.add(shopsArray.getString(i));
|
||||||
|
}
|
||||||
|
shops = shopList;
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PlatformOrder> platformOrders = platformOrderService.fetchOrderInShopsReadyForAbnNumberJob(shops);
|
||||||
|
Map<String, String> shopifyNoteMap = platformOrders.stream()
|
||||||
|
.collect(Collectors.toMap(PlatformOrder::getPlatformOrderId, PlatformOrder::getShopifyNote));
|
||||||
|
if (platformOrders.isEmpty()) {
|
||||||
|
log.info("No order with abnNumbers ready to be added via MabangAPI, quitting now.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("{} orders with abnNumbers ready to be added via MabangAPI", platformOrders.size());
|
||||||
|
List<String> platformOrderIds = new ArrayList<>(shopifyNoteMap.keySet());
|
||||||
|
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
||||||
|
List<OrderListRequestBody> requests = new ArrayList<>();
|
||||||
|
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
||||||
|
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Started retrieving those orders from Mabang for comparison to avoid re-writing");
|
||||||
|
List<Order> mabangOrders = new ArrayList<>();
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||||
|
List<CompletableFuture<Boolean>> futures = requests.stream()
|
||||||
|
.map(request -> CompletableFuture.supplyAsync(() -> {
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
OrderListRawStream rawStream = new OrderListRawStream(request);
|
||||||
|
OrderListStream stream = new OrderListStream(rawStream);
|
||||||
|
List<Order> orders = stream.all();
|
||||||
|
mabangOrders.addAll(orders);
|
||||||
|
success = !orders.isEmpty();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Error communicating with MabangAPI", e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}, executor))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Boolean> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||||
|
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||||
|
log.info("{}/{} requests have succeeded.", nbSuccesses, requests.size());
|
||||||
|
int syncedOrderNumber = mabangOrders.size();
|
||||||
|
log.info("{}/{} mabang orders have been retrieved.", syncedOrderNumber, platformOrderIds.size());
|
||||||
|
|
||||||
|
log.info("Started comparison of {} orders to construct order modification request", mabangOrders.size());
|
||||||
|
List<AddOrderAbnNumberRequestBody> addOrderAbnNumberRequestBodies = new ArrayList<>();
|
||||||
|
mabangOrders.forEach(order -> {
|
||||||
|
String taxNumber = order.getTaxNumber();
|
||||||
|
String platformOrderId = order.getPlatformOrderId();
|
||||||
|
String abnNumber = shopifyNoteMap.get(platformOrderId);
|
||||||
|
if (taxNumber == null || taxNumber.isEmpty()) {
|
||||||
|
addOrderAbnNumberRequestBodies.add(new AddOrderAbnNumberRequestBody(platformOrderId, abnNumber));
|
||||||
|
} else {
|
||||||
|
if (order.getTaxNumber().equalsIgnoreCase(abnNumber)) {
|
||||||
|
log.info("AbnNumber {} already present for order {}, ignoring", order.getTaxNumber(), platformOrderId);
|
||||||
|
} else {
|
||||||
|
// If the number EVER changes (EXTREMELY UNLIKELY), send it to Mabang anyway
|
||||||
|
addOrderAbnNumberRequestBodies.add(new AddOrderAbnNumberRequestBody(platformOrderId, abnNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
|
||||||
|
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
|
||||||
|
List<CompletableFuture<Boolean>> changeOrderFutures = addOrderAbnNumberRequestBodies.stream()
|
||||||
|
.map(addOrderAbnNumberRequestBody -> CompletableFuture.supplyAsync(() -> {
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
AddOrderAbnNumberRequest addOrderAbnNumberRequest = new AddOrderAbnNumberRequest(addOrderAbnNumberRequestBody);
|
||||||
|
ChangeOrderResponse response = addOrderAbnNumberRequest.send();
|
||||||
|
success = response.success();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Error communicating with MabangAPI", e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}, throttlingExecutorService))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||||
|
nbSuccesses = results.stream().filter(b -> b).count();
|
||||||
|
log.info("{}/{} order abnNumber adding requests have succeeded.", nbSuccesses, addOrderAbnNumberRequestBodies.size());
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,8 +43,11 @@ public class AddPortraitTubeJob implements Job {
|
||||||
private static final List<String> TUBE_SKUS = Arrays.asList(TUBE_30_SKU_SINGLE_DOUBLE, TUBE_50_SKU_MULTIPLE,
|
private static final List<String> TUBE_SKUS = Arrays.asList(TUBE_30_SKU_SINGLE_DOUBLE, TUBE_50_SKU_MULTIPLE,
|
||||||
TUBE_50_SKU_SINGLE, TUBE_40_SKU_MULTIPLE, TUBE_40_SKU_SINGLE);
|
TUBE_50_SKU_SINGLE, TUBE_40_SKU_MULTIPLE, TUBE_40_SKU_SINGLE);
|
||||||
private static final String PREFIX_50_CANVAS = "JJ2501";
|
private static final String PREFIX_50_CANVAS = "JJ2501";
|
||||||
|
private static final String PREFIX_50_CANVAS_CHROME = "JJ2001";
|
||||||
private static final String PREFIX_40_CANVAS = "JJ2500";
|
private static final String PREFIX_40_CANVAS = "JJ2500";
|
||||||
|
private static final String PREFIX_40_CANVAS_CHROME = "JJ2000";
|
||||||
private static final String PREFIX_30_CANVAS = "JJ2502";
|
private static final String PREFIX_30_CANVAS = "JJ2502";
|
||||||
|
private static final String PREFIX_30_CANVAS_CHROME = "JJ2002";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IPlatformOrderService platformOrderService;
|
private IPlatformOrderService platformOrderService;
|
||||||
|
@ -165,11 +168,11 @@ public class AddPortraitTubeJob implements Job {
|
||||||
int quantity = orderItem.getQuantity();
|
int quantity = orderItem.getQuantity();
|
||||||
if (TUBE_SKUS.contains(sku)) {
|
if (TUBE_SKUS.contains(sku)) {
|
||||||
currentTubes.add(Pair.of(sku, quantity));
|
currentTubes.add(Pair.of(sku, quantity));
|
||||||
} else if (sku.startsWith(PREFIX_50_CANVAS)) {
|
} else if (sku.startsWith(PREFIX_50_CANVAS) || sku.startsWith(PREFIX_50_CANVAS_CHROME)) {
|
||||||
canvas50Count += quantity;
|
canvas50Count += quantity;
|
||||||
} else if (sku.startsWith(PREFIX_40_CANVAS)) {
|
} else if (sku.startsWith(PREFIX_40_CANVAS) || sku.startsWith(PREFIX_40_CANVAS_CHROME)) {
|
||||||
canvas40Count += quantity;
|
canvas40Count += quantity;
|
||||||
} else if (sku.startsWith(PREFIX_30_CANVAS)) {
|
} else if (sku.startsWith(PREFIX_30_CANVAS) || sku.startsWith(PREFIX_30_CANVAS_CHROME)) {
|
||||||
canvas30Count += quantity;
|
canvas30Count += quantity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,7 @@ public class MabangOrderSyncJob implements Job {
|
||||||
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
||||||
List<OrderListRequestBody> requests = new ArrayList<>();
|
List<OrderListRequestBody> requests = new ArrayList<>();
|
||||||
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
||||||
// There's no other way to sync orders of all statuses, so we duplicate requests to make sure
|
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
||||||
// that we get all orders
|
|
||||||
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList).setStatus(OrderStatus.AllUnshipped));
|
|
||||||
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList).setStatus(OrderStatus.AllNonUnshipped));
|
|
||||||
}
|
}
|
||||||
List<Order> mabangOrders = new ArrayList<>();
|
List<Order> mabangOrders = new ArrayList<>();
|
||||||
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package org.jeecg.modules.business.domain.job;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.codehaus.jettison.json.JSONArray;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
import org.jeecg.modules.business.domain.api.shopify.*;
|
||||||
|
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||||
|
import org.jeecg.modules.business.entity.PlatformOrderShopSync;
|
||||||
|
import org.jeecg.modules.business.entity.Shop;
|
||||||
|
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||||
|
import org.jeecg.modules.business.service.IShopService;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobDataMap;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class ShopifyNoteJob implements Job {
|
||||||
|
|
||||||
|
private static final List<String> DEFAULT_INCLUDED_SHOPS = Arrays.asList("JCH8 KR");
|
||||||
|
|
||||||
|
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IPlatformOrderService platformOrderService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
List<String> shops = DEFAULT_INCLUDED_SHOPS;
|
||||||
|
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||||
|
String parameter = ((String) jobDataMap.get("parameter"));
|
||||||
|
if (parameter != null) {
|
||||||
|
try {
|
||||||
|
JSONObject jsonObject = new JSONObject(parameter);
|
||||||
|
if (!jsonObject.isNull("includedShops")) {
|
||||||
|
JSONArray shopsArray = jsonObject.getJSONArray("includedShops");
|
||||||
|
List<String> shopList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < shopsArray.length(); i++) {
|
||||||
|
shopList.add(shopsArray.getString(i));
|
||||||
|
}
|
||||||
|
shops = shopList;
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
|
||||||
|
List<PlatformOrderShopSync> ordersWithoutShopifyNote = platformOrderService.fetchOrderInShopsWithoutShopifyNote(shops);
|
||||||
|
log.info("Fetched {} orders without Shopify note", ordersWithoutShopifyNote.size());
|
||||||
|
Map<String, List<PlatformOrderShopSync>> ordersByShop = ordersWithoutShopifyNote.stream().collect(
|
||||||
|
groupingBy(PlatformOrderShopSync::getShopifyPrefix));
|
||||||
|
|
||||||
|
List<Order> orders = new ArrayList<>();
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||||
|
log.info("Constructing order retrieval requests");
|
||||||
|
List<GetOrderListRequestBody> getOrderListRequestBodyList = new ArrayList<>();
|
||||||
|
ordersByShop.values().forEach(platformOrderShopSyncs -> {
|
||||||
|
if (!platformOrderShopSyncs.isEmpty()) {
|
||||||
|
List<String> orderIds = platformOrderShopSyncs.stream().map(PlatformOrderShopSync::getPlatformOrderId).collect(Collectors.toList());
|
||||||
|
String shopifyPrefix = platformOrderShopSyncs.get(0).getShopifyPrefix();
|
||||||
|
String shopifyToken = platformOrderShopSyncs.get(0).getShopifyToken();
|
||||||
|
getOrderListRequestBodyList.add(new GetOrderListRequestBody(shopifyPrefix, shopifyToken, orderIds));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
List<CompletableFuture<Boolean>> futures = getOrderListRequestBodyList.stream()
|
||||||
|
.map(body -> CompletableFuture.supplyAsync(() -> {
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
GetOrderListRequest getOrderListRequest = new GetOrderListRequest(body);
|
||||||
|
String responseStr = getOrderListRequest.rawSend().getBody();
|
||||||
|
GetOrderListResponse response = mapper.readValue(responseStr, GetOrderListResponse.class);
|
||||||
|
orders.addAll(response.getOrders());
|
||||||
|
success = true;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.error("Error communicating with ShopifyAPI", e);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("Error processing json", e);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}, executor))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
List<Boolean> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||||
|
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||||
|
log.info("{}/{} order retrieval requests have succeeded.", nbSuccesses, getOrderListRequestBodyList.size());
|
||||||
|
log.info("{} orders have been retrieved.", orders.size());
|
||||||
|
|
||||||
|
log.info("Started adding Shopify notes to orders without one");
|
||||||
|
Map<String, String> orderNoteMap = orders.stream().filter(Order::hasNote).collect(toMap(order -> order.getId().toString(), Order::getNote));
|
||||||
|
if (orderNoteMap.isEmpty()) {
|
||||||
|
log.info("No notes can be added to orders, quitting now");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<PlatformOrder> platformOrders = platformOrderService.selectByPlatformOrderIds(new ArrayList<>(orderNoteMap.keySet()));
|
||||||
|
platformOrders.forEach(platformOrder -> platformOrder.setShopifyNote(orderNoteMap.get(platformOrder.getPlatformOrderId()).trim()));
|
||||||
|
platformOrderService.updateBatchById(platformOrders);
|
||||||
|
log.info("Finished adding Shopify notes to {} orders without one into DB.", platformOrders.size());
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,8 @@ import java.util.Date;
|
||||||
/**
|
/**
|
||||||
* @Description: 平台订单表
|
* @Description: 平台订单表
|
||||||
* @Author: jeecg-boot
|
* @Author: jeecg-boot
|
||||||
* @Date: 2023-11-28
|
* @Date: 2024-01-25
|
||||||
* @Version: V1.7
|
* @Version: V1.8
|
||||||
*/
|
*/
|
||||||
@ApiModel(value = "platform_order对象", description = "平台订单表")
|
@ApiModel(value = "platform_order对象", description = "平台订单表")
|
||||||
@Data
|
@Data
|
||||||
|
@ -234,4 +234,22 @@ public class PlatformOrder implements Serializable {
|
||||||
@Excel(name = "包材费", width = 15)
|
@Excel(name = "包材费", width = 15)
|
||||||
@ApiModelProperty(value = "包材费")
|
@ApiModelProperty(value = "包材费")
|
||||||
private java.math.BigDecimal packagingMaterialFee;
|
private java.math.BigDecimal packagingMaterialFee;
|
||||||
|
/**
|
||||||
|
* 收件人城市
|
||||||
|
*/
|
||||||
|
@Excel(name = "收件人城市", width = 15)
|
||||||
|
@ApiModelProperty(value = "收件人城市")
|
||||||
|
private java.lang.String city;
|
||||||
|
/**
|
||||||
|
* Shopify平台留言
|
||||||
|
*/
|
||||||
|
@Excel(name = "Shopify平台留言", width = 15)
|
||||||
|
@ApiModelProperty(value = "Shopify平台留言")
|
||||||
|
private java.lang.String shopifyNote;
|
||||||
|
/**
|
||||||
|
* 个人税号
|
||||||
|
*/
|
||||||
|
@Excel(name = "个人税号", width = 15)
|
||||||
|
@ApiModelProperty(value = "个人税号")
|
||||||
|
private java.lang.String taxNumber;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ import java.io.Serializable;
|
||||||
/**
|
/**
|
||||||
* @Description: 店铺
|
* @Description: 店铺
|
||||||
* @Author: jeecg-boot
|
* @Author: jeecg-boot
|
||||||
* @Date: 2021-08-11
|
* @Date: 2024-01-25
|
||||||
* @Version: V1.1
|
* @Version: V1.2
|
||||||
*/
|
*/
|
||||||
@ApiModel(value = "Shop对象", description = "店铺")
|
@ApiModel(value = "Shop对象", description = "店铺")
|
||||||
@Data
|
@Data
|
||||||
|
@ -91,10 +91,29 @@ public class Shop implements Serializable {
|
||||||
@Excel(name = "订单服务费", width = 15)
|
@Excel(name = "订单服务费", width = 15)
|
||||||
@ApiModelProperty(value = "订单服务费")
|
@ApiModelProperty(value = "订单服务费")
|
||||||
private java.math.BigDecimal orderServiceFee;
|
private java.math.BigDecimal orderServiceFee;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包材费
|
* 是否活跃
|
||||||
*/
|
*/
|
||||||
@Excel(name = "包材费", width = 15)
|
@Excel(name = "是否活跃", width = 15, dicCode = "yn")
|
||||||
@ApiModelProperty(value = "包材费")
|
@ApiModelProperty(value = "是否活跃")
|
||||||
|
private java.lang.String active;
|
||||||
|
/**
|
||||||
|
* Shopify前缀
|
||||||
|
*/
|
||||||
|
@Excel(name = "Shopify前缀", width = 15)
|
||||||
|
@ApiModelProperty(value = "Shopify前缀")
|
||||||
|
private java.lang.String shopifyPrefix;
|
||||||
|
/**
|
||||||
|
* Shopify API Token
|
||||||
|
*/
|
||||||
|
@Excel(name = "Shopify API Token", width = 15)
|
||||||
|
@ApiModelProperty(value = "Shopify API Token")
|
||||||
|
private java.lang.String shopifyToken;
|
||||||
|
/**
|
||||||
|
* 海外仓包材费
|
||||||
|
*/
|
||||||
|
@Excel(name = "海外仓包材费", width = 15)
|
||||||
|
@ApiModelProperty(value = "海外仓包材费")
|
||||||
private java.math.BigDecimal packagingMaterialFee;
|
private java.math.BigDecimal packagingMaterialFee;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,8 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
|
||||||
@Param("excludedTrackingNumbersRegex") String excludedTrackingNumbersRegex);
|
@Param("excludedTrackingNumbersRegex") String excludedTrackingNumbersRegex);
|
||||||
|
|
||||||
List<PlatformOrderShopSync> fetchOrderInShopsReadyForShopifySync(@Param("shops") List<String> shopCodes);
|
List<PlatformOrderShopSync> fetchOrderInShopsReadyForShopifySync(@Param("shops") List<String> shopCodes);
|
||||||
|
List<PlatformOrderShopSync> fetchOrderInShopsWithoutShopifyNote(@Param("shops") List<String> shopCodes);
|
||||||
|
List<PlatformOrder> fetchOrderInShopsReadyForAbnNumberJob(@Param("shops") List<String> shopCodes);
|
||||||
|
|
||||||
List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(@Param("startDate") String startDate,
|
List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(@Param("startDate") String startDate,
|
||||||
@Param("endDate") String endDate,
|
@Param("endDate") String endDate,
|
||||||
|
|
|
@ -96,6 +96,11 @@
|
||||||
when #{item.id} then #{item.postcode}
|
when #{item.id} then #{item.postcode}
|
||||||
</foreach>
|
</foreach>
|
||||||
end,
|
end,
|
||||||
|
tax_number = case id
|
||||||
|
<foreach collection="orders" separator=" " open="" close="" index="index" item="item">
|
||||||
|
when #{item.id} then #{item.taxNumber}
|
||||||
|
</foreach>
|
||||||
|
end,
|
||||||
erp_status = case id
|
erp_status = case id
|
||||||
<foreach collection="orders" separator=" " open="" close="" index="index" item="item">
|
<foreach collection="orders" separator=" " open="" close="" index="index" item="item">
|
||||||
when #{item.id} then #{item.status}
|
when #{item.id} then #{item.status}
|
||||||
|
@ -157,7 +162,7 @@
|
||||||
update_time, shop_id, logistic_channel_name,
|
update_time, shop_id, logistic_channel_name,
|
||||||
platform_order_id, platform_order_number, erp_order_id,
|
platform_order_id, platform_order_number, erp_order_id,
|
||||||
tracking_number, internal_tracking_number, order_time, shipping_time, recipient,
|
tracking_number, internal_tracking_number, order_time, shipping_time, recipient,
|
||||||
country, postcode, erp_status, product_available, can_send)
|
country, postcode, tax_number, erp_status, product_available, can_send)
|
||||||
VALUES
|
VALUES
|
||||||
<foreach collection="orders" separator="," open="" close="" item="order" index="index">
|
<foreach collection="orders" separator="," open="" close="" item="order" index="index">
|
||||||
(
|
(
|
||||||
|
@ -178,6 +183,7 @@
|
||||||
#{order.recipient},
|
#{order.recipient},
|
||||||
#{order.country},
|
#{order.country},
|
||||||
#{order.postcode},
|
#{order.postcode},
|
||||||
|
#{order.taxNumber},
|
||||||
#{order.status},
|
#{order.status},
|
||||||
#{order.productAvailable},
|
#{order.productAvailable},
|
||||||
#{order.canSend}
|
#{order.canSend}
|
||||||
|
|
|
@ -466,6 +466,45 @@
|
||||||
ORDER BY shipping_time;
|
ORDER BY shipping_time;
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="fetchOrderInShopsWithoutShopifyNote" resultType="org.jeecg.modules.business.entity.PlatformOrderShopSync">
|
||||||
|
SELECT po.platform_order_id, po.tracking_number, s.shopify_prefix, s.shopify_token, po.postcode
|
||||||
|
FROM platform_order po join shop s ON po.shop_id = s.id
|
||||||
|
WHERE erp_code IN
|
||||||
|
<foreach
|
||||||
|
collection="shops"
|
||||||
|
separator=","
|
||||||
|
open="("
|
||||||
|
close=")"
|
||||||
|
index="index"
|
||||||
|
item="shop"
|
||||||
|
>
|
||||||
|
#{shop}
|
||||||
|
</foreach>
|
||||||
|
AND erp_status <> 5
|
||||||
|
AND shopify_note IS NULL
|
||||||
|
ORDER BY order_time;
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="fetchOrderInShopsReadyForAbnNumberJob" resultType="org.jeecg.modules.business.entity.PlatformOrder">
|
||||||
|
SELECT po.*
|
||||||
|
FROM platform_order po join shop s ON po.shop_id = s.id
|
||||||
|
WHERE erp_code IN
|
||||||
|
<foreach
|
||||||
|
collection="shops"
|
||||||
|
separator=","
|
||||||
|
open="("
|
||||||
|
close=")"
|
||||||
|
index="index"
|
||||||
|
item="shop"
|
||||||
|
>
|
||||||
|
#{shop}
|
||||||
|
</foreach>
|
||||||
|
AND erp_status <> 5
|
||||||
|
AND shopify_note IS NOT NULL
|
||||||
|
AND (tax_number IS NULL OR tax_number = '')
|
||||||
|
ORDER BY order_time;
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="fetchUninvoicedShippedOrderIDInShops" resultType="org.jeecg.modules.business.entity.PlatformOrder">
|
<select id="fetchUninvoicedShippedOrderIDInShops" resultType="org.jeecg.modules.business.entity.PlatformOrder">
|
||||||
SELECT po.id
|
SELECT po.id
|
||||||
FROM platform_order po
|
FROM platform_order po
|
||||||
|
|
|
@ -142,6 +142,9 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
|
||||||
|
|
||||||
List<PlatformOrderShopSync> fetchOrderInShopsReadyForShopifySync(List<String> shopCodes);
|
List<PlatformOrderShopSync> fetchOrderInShopsReadyForShopifySync(List<String> shopCodes);
|
||||||
|
|
||||||
|
List<PlatformOrderShopSync> fetchOrderInShopsWithoutShopifyNote(List<String> shopCodes);
|
||||||
|
List<PlatformOrder> fetchOrderInShopsReadyForAbnNumberJob(List<String> shopCodes);
|
||||||
|
|
||||||
List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(String startDate, String endDate, List<String> shops, List<String> warehouses);
|
List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(String startDate, String endDate, List<String> shops, List<String> warehouses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -374,6 +374,16 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
||||||
return platformOrderMap.fetchOrderInShopsReadyForShopifySync(shopCodes);
|
return platformOrderMap.fetchOrderInShopsReadyForShopifySync(shopCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlatformOrderShopSync> fetchOrderInShopsWithoutShopifyNote(List<String> shopCodes) {
|
||||||
|
return platformOrderMap.fetchOrderInShopsWithoutShopifyNote(shopCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PlatformOrder> fetchOrderInShopsReadyForAbnNumberJob(List<String> shopCodes) {
|
||||||
|
return platformOrderMap.fetchOrderInShopsReadyForAbnNumberJob(shopCodes);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(String startDate, String endDate, List<String> shops, List<String> warehouses) {
|
public List<PlatformOrder> fetchUninvoicedShippedOrderIDInShops(String startDate, String endDate, List<String> shops, List<String> warehouses) {
|
||||||
return platformOrderMap.fetchUninvoicedShippedOrderIDInShops(startDate, endDate, shops, warehouses);
|
return platformOrderMap.fetchUninvoicedShippedOrderIDInShops(startDate, endDate, shops, warehouses);
|
||||||
|
|
Loading…
Reference in New Issue