mirror of https://github.com/jeecgboot/jeecg-boot
parent
556820e0e2
commit
58b11bac0b
|
@ -24,6 +24,7 @@ public class ChangeOrderRequestBody implements RequestBody {
|
|||
|
||||
private final static String DEFAULT_WAREHOUSE_NAME = "SZBA宝安仓";
|
||||
|
||||
@Getter
|
||||
public enum OperationType {
|
||||
MODIFY(1),
|
||||
REMOVE(2),
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.dochangeorder;
|
||||
|
||||
import org.jeecg.modules.business.domain.api.mabang.Request;
|
||||
|
||||
public class ChangeWarehouseRequest extends Request {
|
||||
public ChangeWarehouseRequest(ChangeWarehouseRequestBody body) {
|
||||
super(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChangeOrderResponse send() {
|
||||
String jsonString = rawSend().getBody();
|
||||
return ChangeOrderResponse.parse(jsonString);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.dochangeorder;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jeecg.modules.business.domain.api.mabang.RequestBody;
|
||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
|
||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderItem;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderRequestBody.OperationType;
|
||||
@Getter
|
||||
@Setter
|
||||
public class ChangeWarehouseRequestBody implements RequestBody {
|
||||
private Order order;
|
||||
private String platformOrderId;
|
||||
private String warehouseName;
|
||||
private String recipient;
|
||||
private String street1;
|
||||
private String street2;
|
||||
|
||||
public ChangeWarehouseRequestBody(Order order, String warehouseName) {
|
||||
this.platformOrderId = order.getPlatformOrderId();
|
||||
this.warehouseName = warehouseName;
|
||||
this.recipient = order.getRecipient();
|
||||
this.street1 = order.getAddress();
|
||||
this.street2 = order.getAddress2();
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String api() {
|
||||
return "order-do-change-order";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject parameters() {
|
||||
JSONObject json = new JSONObject();
|
||||
JSONArray stockDataArray = new JSONArray();
|
||||
putNonNull(json, "platformOrderId", platformOrderId);
|
||||
putNonNull(json, "buyerName", recipient);
|
||||
putNonNull(json, "street1", street1);
|
||||
putNonNull(json, "street2", street2);
|
||||
if(order.getOrderItems() != null && !order.getOrderItems().isEmpty()) {
|
||||
for(OrderItem orderItem : order.getOrderItems()) {
|
||||
if(!isSkuValidFormat(orderItem.getErpCode()))
|
||||
continue;
|
||||
JSONObject stockData = new JSONObject();
|
||||
stockData.put("type", OperationType.MODIFY.getCode());
|
||||
stockData.put("stockSku", orderItem.getErpCode());
|
||||
stockData.put("warehouseName", warehouseName);
|
||||
stockData.put("erpOrderItemId", orderItem.getErpOrderItemId());
|
||||
stockDataArray.add(stockData);
|
||||
}
|
||||
}
|
||||
putNonNull(json, "stockData", stockDataArray.toJSONString());
|
||||
return json;
|
||||
}
|
||||
private boolean isSkuValidFormat(String sku) {
|
||||
return sku != null && !sku.matches("^[0-9]+$");
|
||||
}
|
||||
private <E> void putNonNull(JSONObject json, String key, E value) {
|
||||
if (value != null) {
|
||||
json.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private <E, T> void putNonNull(JSONObject json, String key, E value, Function<E, T> mapper) {
|
||||
if (value != null) {
|
||||
json.put(key, mapper.apply(value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -89,6 +89,16 @@ public class Order {
|
|||
*/
|
||||
@JSONField(name = "postCode")
|
||||
private String postcode;
|
||||
/**
|
||||
* 买家地址1
|
||||
*/
|
||||
@JSONField(name = "street1")
|
||||
private String address;
|
||||
/**
|
||||
* 买家地址2
|
||||
*/
|
||||
@JSONField(name = "street2")
|
||||
private String address2;
|
||||
/**
|
||||
* 税号
|
||||
*/
|
||||
|
@ -127,6 +137,10 @@ public class Order {
|
|||
@TableField(exist = false)
|
||||
private List<OrderItem> orderItems;
|
||||
|
||||
@JSONField(name = "orderTypeNew")
|
||||
@TableField(exist = false)
|
||||
private OrderTypeNew orderType;
|
||||
|
||||
@JSONField(name = "phone1")
|
||||
private String phone1;
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@ public class OrderItem {
|
|||
@JSONField(name = "originOrderId")
|
||||
private String originOrderId;
|
||||
|
||||
@JSONField(name="erpOrderItemId")
|
||||
private String erpOrderItemId;
|
||||
@JSONField(name = "stockWarehouseName")
|
||||
private String warehouseName;
|
||||
|
||||
/**
|
||||
* 1 = 有货
|
||||
* 2 = 缺货
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.getorderlist;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderTypeLabel {
|
||||
@JSONField(name = "typeId")
|
||||
private String typeId;
|
||||
@JSONField(name = "name")
|
||||
private String name;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.getorderlist;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderTypeNew {
|
||||
@JSONField(name = "normalLabels")
|
||||
private OrderTypeLabel[] normalLabels;
|
||||
@JSONField(name = "abnormalLabels")
|
||||
private OrderTypeLabel[] abnormalLabels;
|
||||
}
|
|
@ -13,7 +13,7 @@ import java.util.function.Function;
|
|||
public class OrderSuspendRequestBody implements RequestBody {
|
||||
|
||||
private String platformOrderId;
|
||||
private final String abnormal_label_name = "客户要求暂时不处理";
|
||||
private String abnormal_label_name = "客户要求暂时不处理";
|
||||
private String description;
|
||||
|
||||
public OrderSuspendRequestBody(String platformOrderId, String description) {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.domain.api.mabang.Request;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* This class contains some key information and necessary procedures
|
||||
* to send a request to mabang "order-do-order-abnormal" API, for example: target URL,
|
||||
* correspondent HTTP method, procedure to generate authorization.
|
||||
* <p>
|
||||
* One can use static method {@code sendRequest} to send request with body,
|
||||
* and then get respective response. Or use instance of this class, see below.
|
||||
* <p>
|
||||
*/
|
||||
@Slf4j
|
||||
public class OrderToNormalRequest extends Request {
|
||||
|
||||
public OrderToNormalRequest(OrderToNormalRequestBody body) {
|
||||
super(body);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public OrderToNormalResponse send() {
|
||||
ResponseEntity<String> res = rawSend();
|
||||
return OrderToNormalResponse.parse(JSON.parseObject(res.getBody()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jeecg.modules.business.domain.api.mabang.RequestBody;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class OrderToNormalRequestBody implements RequestBody {
|
||||
|
||||
private String platformOrderId;
|
||||
|
||||
public OrderToNormalRequestBody(String platformOrderId) {
|
||||
this.platformOrderId = platformOrderId;
|
||||
}
|
||||
@Override
|
||||
public String api() {
|
||||
return "order-do-order-normal";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parameters() {
|
||||
JSONObject json = new JSONObject();
|
||||
putNonNull(json, "platformOrderId", platformOrderId);
|
||||
return json;
|
||||
}
|
||||
private <E> void putNonNull(JSONObject json, String key, E value) {
|
||||
if (value != null) {
|
||||
json.put(key, value);
|
||||
}
|
||||
}
|
||||
private <E, T> void putNonNull(JSONObject json, String key, E value, Function<E, T> mapper) {
|
||||
if (value != null) {
|
||||
json.put(key, mapper.apply(value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal;
|
||||
|
||||
/**
|
||||
* This class represents error that is returned by target order-do-order-abnormal API
|
||||
* Message will contain error details.
|
||||
*/
|
||||
public class OrderToNormalRequestErrorException extends RuntimeException {
|
||||
public OrderToNormalRequestErrorException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.business.domain.api.mabang.Response;
|
||||
|
||||
/**
|
||||
* Immutable object
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class OrderToNormalResponse extends Response {
|
||||
private final String message;
|
||||
|
||||
OrderToNormalResponse(Code successCode, String message) {
|
||||
super(successCode);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an instance by parsing json, it only checks validity of code.
|
||||
* if json is not valid, return null
|
||||
*
|
||||
* @param json the json to parse
|
||||
* @return Instance
|
||||
* @throws OrderToNormalRequestErrorException if response code represents error.
|
||||
*/
|
||||
public static OrderToNormalResponse parse(JSONObject json) throws OrderToNormalRequestErrorException {
|
||||
log.debug("Constructing a response by json.");
|
||||
String code = json.getString("code");
|
||||
String message = json.getString("message");
|
||||
if (code.equals("200"))
|
||||
return new OrderToNormalResponse(Code.SUCCESS, message);
|
||||
else
|
||||
return new OrderToNormalResponse(Code.ERROR, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OrderToNormalResponse{" +
|
||||
", code=" + this.success() +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ChangeWareHouseParam {
|
||||
private String shop;
|
||||
private List<String> skus;
|
||||
private List<String> countries;
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
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.modules.business.domain.api.mabang.dochangeorder.*;
|
||||
import org.jeecg.modules.business.domain.api.mabang.getorderlist.*;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal.OrderSuspendRequest;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal.OrderSuspendRequestBody;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal.OrderSuspendResponse;
|
||||
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||
import org.jeecg.modules.business.vo.Responses;
|
||||
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.text.Normalizer;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
@Slf4j
|
||||
public class ChangeWarehouseJob implements Job {
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
||||
private static final Integer DEFAULT_NUMBER_OF_DAYS = 5;
|
||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||
private final String DEFAULT_WAREHOUSE_NAME = "法国巴黎仓库-唯客路";
|
||||
private final String DEFAULT_ABNORMAL_LABEL_NAME = "AC海外仓非法国比利时不发货";
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
log.info("Executing ChangeWarehouseJob...");
|
||||
LocalDateTime endDateTime = LocalDateTime.now();
|
||||
LocalDateTime startDateTime = endDateTime.minusDays(DEFAULT_NUMBER_OF_DAYS);
|
||||
List<String> allSkus = new ArrayList<>();
|
||||
List<ChangeWareHouseParam> changeWareHouseParams = new ArrayList<>();
|
||||
|
||||
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||
String parameter = ((String) jobDataMap.get("parameter"));
|
||||
if (parameter != null) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(parameter);
|
||||
if (!jsonObject.isNull("startDateTime")) {
|
||||
String startDateStr = jsonObject.getString("startDateTime");
|
||||
startDateTime = LocalDateTime.parse(startDateStr);
|
||||
}
|
||||
if (!jsonObject.isNull("endDateTime")) {
|
||||
String endDateStr = jsonObject.getString("endDateTime");
|
||||
endDateTime = LocalDateTime.parse(endDateStr);
|
||||
}
|
||||
if (!jsonObject.isNull("data")) {
|
||||
JSONArray dataJsonArray = jsonObject.getJSONArray("data");
|
||||
for (int i = 0; i < dataJsonArray.length(); i++) {
|
||||
JSONObject object = dataJsonArray.getJSONObject(i);
|
||||
ChangeWareHouseParam param = new ChangeWareHouseParam();
|
||||
if(!object.isNull("shop")) {
|
||||
param.setShop(object.getString("shop"));
|
||||
}
|
||||
if(!object.isNull("skus")) {
|
||||
JSONArray skusJsonArray = object.getJSONArray("skus");
|
||||
List<String> skus = new ArrayList<>();
|
||||
for(int j = 0; j < skusJsonArray.length(); j++) {
|
||||
skus.add(skusJsonArray.getString(j));
|
||||
}
|
||||
param.setSkus(skus);
|
||||
allSkus.addAll(skus);
|
||||
}
|
||||
if(!object.isNull("countries")) {
|
||||
List<String> countries = new ArrayList<>();
|
||||
JSONArray countriesJsonArray = object.getJSONArray("countries");
|
||||
for (int j = 0; j < countriesJsonArray.length(); j++) {
|
||||
countries.add(countriesJsonArray.getString(j));
|
||||
}
|
||||
param.setCountries(countries);
|
||||
}
|
||||
changeWareHouseParams.add(param);
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!endDateTime.isAfter(startDateTime)) {
|
||||
throw new RuntimeException("EndDateTime must be strictly greater than StartDateTime !");
|
||||
}
|
||||
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||
|
||||
List<String> platformOrderIds = new ArrayList<>();
|
||||
List<String> ordersToSetAbnormal = new ArrayList<>();
|
||||
for(ChangeWareHouseParam param: changeWareHouseParams) {
|
||||
List<String> poIds = platformOrderService.fetchUninvoicedOrdersWithSkusInCountry(startDateTime, endDateTime, param.getShop(), param.getSkus(), param.getCountries());
|
||||
List<String> abnormalPoIds = platformOrderService.fetchUninvoicedOrdersWithSkusNotInCountry(startDateTime, endDateTime, param.getShop(), allSkus, param.getCountries());
|
||||
platformOrderIds.addAll(poIds);
|
||||
ordersToSetAbnormal.addAll(abnormalPoIds);
|
||||
}
|
||||
|
||||
// fetch orders from mabang
|
||||
log.info("Fetching orders from mabang...");
|
||||
List<List<String>> platformOrderIdLists = Lists.partition(platformOrderIds, 10);
|
||||
List<Order> mabangOrders = new ArrayList<>();
|
||||
List<OrderListRequestBody> requests = new ArrayList<>();
|
||||
for (List<String> platformOrderIdList : platformOrderIdLists) {
|
||||
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
||||
}
|
||||
List<CompletableFuture<Boolean>> completableFutures = 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 while fetching orders from mabang: {}", e.getMessage());
|
||||
}
|
||||
return success;
|
||||
}, executor))
|
||||
.collect(Collectors.toList());
|
||||
List<Boolean> fetchResults = completableFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
long fetchSuccessCount = fetchResults.stream().filter(Boolean::booleanValue).count();
|
||||
log.info("Successfully fetched {} out of {} orders from mabang.", fetchSuccessCount, fetchResults.size());
|
||||
|
||||
log.info("Updating recipients info...");
|
||||
replaceOrdersAccents(mabangOrders);
|
||||
|
||||
log.info("Updating warehouse name...");
|
||||
List<CompletableFuture<Boolean>> futures = mabangOrders.stream()
|
||||
.map(order -> CompletableFuture.supplyAsync(() -> {
|
||||
ChangeWarehouseRequestBody body = new ChangeWarehouseRequestBody(order, DEFAULT_WAREHOUSE_NAME);
|
||||
ChangeWarehouseRequest request = new ChangeWarehouseRequest(body);
|
||||
ChangeOrderResponse response = request.send();
|
||||
return response.success();
|
||||
}, executor))
|
||||
.collect(Collectors.toList());
|
||||
List<Boolean> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
long successCount = results.stream().filter(Boolean::booleanValue).count();
|
||||
log.info("Successfully changed warehouse for {} out of {} orders.", successCount, results.size());
|
||||
|
||||
log.info("Setting orders to abnormal...");
|
||||
Responses responses = new Responses();
|
||||
List<CompletableFuture<Responses>> abnormalFutures = ordersToSetAbnormal.stream()
|
||||
.map(id -> CompletableFuture.supplyAsync(() -> {
|
||||
OrderSuspendRequestBody body = new OrderSuspendRequestBody(id, "");
|
||||
body.setAbnormal_label_name(DEFAULT_ABNORMAL_LABEL_NAME);
|
||||
OrderSuspendRequest request = new OrderSuspendRequest(body);
|
||||
OrderSuspendResponse response = request.send();
|
||||
Responses r = new Responses();
|
||||
if(response.success())
|
||||
r.addSuccess(id);
|
||||
else
|
||||
r.addFailure(id);
|
||||
return r;
|
||||
}, executor))
|
||||
.collect(toList());
|
||||
List<Responses> abnormalResults = abnormalFutures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.collect(toList());
|
||||
abnormalResults.forEach(r -> {
|
||||
responses.getSuccesses().addAll(r.getSuccesses());
|
||||
responses.getFailures().addAll(r.getFailures());
|
||||
});
|
||||
log.info("Successfully set {}/{} orders to abnormal.", responses.getSuccesses().size(), ordersToSetAbnormal.size());
|
||||
executor.shutdown();
|
||||
}
|
||||
public void replaceOrdersAccents(List<Order> orders) {
|
||||
for(Order order: orders) {
|
||||
order.setRecipient(stripAccents(order.getRecipient()));
|
||||
order.setAddress(stripAccents(order.getAddress()));
|
||||
order.setAddress2(stripAccents(order.getAddress2()));
|
||||
}
|
||||
}
|
||||
public String stripAccents(String input) {
|
||||
input = Normalizer.normalize(input, Normalizer.Form.NFD);
|
||||
input = input.replaceAll("[^\\p{ASCII}]", "");
|
||||
return input;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import freemarker.template.Template;
|
||||
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.mabang.getorderlist.*;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal.OrderToNormalRequest;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal.OrderToNormalRequestBody;
|
||||
import org.jeecg.modules.business.domain.api.mabang.orderDoOrderNormal.OrderToNormalResponse;
|
||||
import org.jeecg.modules.business.entity.PlatformOrder;
|
||||
import org.jeecg.modules.business.entity.PlatformOrderContent;
|
||||
import org.jeecg.modules.business.service.EmailService;
|
||||
import org.jeecg.modules.business.service.IPlatformOrderService;
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
|
||||
import javax.mail.Authenticator;
|
||||
import javax.mail.PasswordAuthentication;
|
||||
import javax.mail.Session;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class SetOrderToNormalJob implements Job {
|
||||
|
||||
@Autowired
|
||||
private EmailService emailService;
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
@Autowired
|
||||
private FreeMarkerConfigurer freemarkerConfigurer;
|
||||
|
||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||
private final String WAREHOUSE_NAME = "法国巴黎仓库-唯客路";
|
||||
private final String ABNORMAL_LABEL_NAME = "自动创建SKU的订单";
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
log.info("Executing SetOrderToNormalJob...");
|
||||
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||
String parameter = ((String) jobDataMap.get("parameter"));
|
||||
|
||||
List<String> skus = new ArrayList<>();
|
||||
List<String> shops = new ArrayList<>();
|
||||
if (parameter != null) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(parameter);
|
||||
if(!jsonObject.isNull("skus")) {
|
||||
JSONArray skusJsonArray = jsonObject.getJSONArray("skus");
|
||||
for (int i = 0; i < skusJsonArray.length(); i++) {
|
||||
skus.add(skusJsonArray.getString(i));
|
||||
}
|
||||
}
|
||||
if(!jsonObject.isNull("shops")) {
|
||||
JSONArray shopsJsonArray = jsonObject.getJSONArray("shops");
|
||||
for (int i = 0; i < shopsJsonArray.length(); i++) {
|
||||
shops.add(shopsJsonArray.getString(i));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
List<String> orderIds = platformOrderService.findReadyAbnormalOrders(skus, shops);
|
||||
List<String> orderIdsWithSkus = platformOrderService.findReadyAbnormalOrdersWithSkus(skus);
|
||||
List<String> orderReadyWithSkus = orderIds.stream()
|
||||
.filter(orderIdsWithSkus::contains)
|
||||
.collect(Collectors.toList());
|
||||
if(!orderReadyWithSkus.isEmpty()) {
|
||||
Map<PlatformOrder, List<PlatformOrderContent>> orderContents = platformOrderService.fetchOrderData(orderReadyWithSkus);
|
||||
// Remove orders that contains sku from "skus" list and have wrong warehouse
|
||||
for(Map.Entry<PlatformOrder, List<PlatformOrderContent>> entry : orderContents.entrySet()) {
|
||||
for(PlatformOrderContent content : entry.getValue()) {
|
||||
if(!content.getWarehouseName().equals(WAREHOUSE_NAME)) {
|
||||
orderIds.remove(content.getPlatformOrderId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||
|
||||
List<String> platform_order_ids = platformOrderService.listByIds(orderIds).stream()
|
||||
.map(PlatformOrder::getPlatformOrderId)
|
||||
.collect(Collectors.toList());
|
||||
List<Order> mabangOrders = new ArrayList<>();
|
||||
|
||||
List<List<String>> partitionedPlatformOrderIds = Lists.partition(platform_order_ids, 10);
|
||||
List<OrderListRequestBody> requests = new ArrayList<>();
|
||||
for (List<String> platformOrderIdList : partitionedPlatformOrderIds) {
|
||||
requests.add(new OrderListRequestBody().setPlatformOrderIds(platformOrderIdList));
|
||||
}
|
||||
// Fetch orders from mabang
|
||||
List<CompletableFuture<Boolean>> mabangFutures = requests.stream()
|
||||
.map(request -> CompletableFuture.supplyAsync(() -> {
|
||||
boolean success = false;
|
||||
OrderListRawStream rawStream = new OrderListRawStream(request);
|
||||
OrderListStream stream = new OrderListStream(rawStream);
|
||||
List<Order> orders = stream.all();
|
||||
mabangOrders.addAll(orders);
|
||||
success = !orders.isEmpty();
|
||||
return success;
|
||||
}, executor))
|
||||
.collect(Collectors.toList());
|
||||
List<Boolean> fetchResults = mabangFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
long fetchSuccessCount = fetchResults.stream().filter(Boolean::booleanValue).count();
|
||||
log.info("Successfully fetched {} out of {} orders groups from mabang.", fetchSuccessCount, fetchResults.size());
|
||||
|
||||
List<String> poIdsToSetNormal = new ArrayList<>();
|
||||
if(!mabangOrders.isEmpty()) {
|
||||
for(Order order : mabangOrders) {
|
||||
OrderTypeLabel[] labels = order.getOrderType().getAbnormalLabels();
|
||||
if(labels.length > 1) {
|
||||
continue;
|
||||
}
|
||||
if(labels[0].getName().equals(ABNORMAL_LABEL_NAME)) {
|
||||
poIdsToSetNormal.add(order.getPlatformOrderId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<CompletableFuture<Boolean>> futures = poIdsToSetNormal.stream()
|
||||
.map(orderId -> CompletableFuture.supplyAsync(() -> {
|
||||
log.info("Setting order {} to normal", orderId);
|
||||
OrderToNormalRequestBody requestBody = new OrderToNormalRequestBody(orderId);
|
||||
OrderToNormalRequest request = new OrderToNormalRequest(requestBody);
|
||||
OrderToNormalResponse response = request.send();
|
||||
return response.success();
|
||||
}, executor))
|
||||
.collect(Collectors.toList());
|
||||
List<Boolean> results = futures.stream()
|
||||
.map(CompletableFuture::join)
|
||||
.collect(Collectors.toList());
|
||||
long successCount = results.stream().filter(Boolean::booleanValue).count();
|
||||
log.info("Successfully set {}/{} orders to normal", successCount, poIdsToSetNormal.size());
|
||||
|
||||
executor.shutdown();
|
||||
|
||||
// send list of order ids by mail
|
||||
if(!poIdsToSetNormal.isEmpty()) {
|
||||
String subject = "[" + LocalDate.now() + "] Set Orders To Normal Job Report";
|
||||
String destEmail = env.getProperty("spring.mail.username");
|
||||
Properties prop = emailService.getMailSender();
|
||||
Map<String, Object> templateModel = new HashMap<>();
|
||||
templateModel.put("orderIds", poIdsToSetNormal);
|
||||
templateModel.put("count", poIdsToSetNormal.size());
|
||||
Session session = Session.getInstance(prop, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(env.getProperty("spring.mail.username"), env.getProperty("spring.mail.password"));
|
||||
}
|
||||
});
|
||||
try {
|
||||
freemarkerConfigurer = emailService.freemarkerClassLoaderConfig();
|
||||
Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
|
||||
.getTemplate("admin/ordersSetToNormalJobReport.ftl");
|
||||
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
|
||||
emailService.sendSimpleMessage(destEmail, subject, htmlBody, session);
|
||||
log.info("Mail sent successfully !");
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending Set Orders To Normal Job report mail !");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -139,6 +139,12 @@ public class PlatformOrderContent implements Serializable {
|
|||
@Excel(name = "商品多属性", width = 15)
|
||||
@ApiModelProperty(value = "商品多属性")
|
||||
private java.lang.String customizationData;
|
||||
/**
|
||||
* 仓库
|
||||
*/
|
||||
@Excel(name = "Warehouse name", width = 15)
|
||||
@ApiModelProperty(value = "添加的商品仓库")
|
||||
private String warehouseName;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
|
|
@ -224,4 +224,11 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
|
|||
void anonymizePersonalData(@Param("period") int indirectClientAnonymizationPeriod);
|
||||
|
||||
List<PlatformOrderOption> ordersByShop(@Param("shopID") String shopID);
|
||||
|
||||
List<String> fetchUninvoicedOrdersWithSkusInCountry(@Param("startDate") LocalDateTime startDateTime, @Param("endDate") LocalDateTime endDateTime, @Param("shop") String shop, @Param("skus") List<String> skus, @Param("countries") List<String> countries);
|
||||
List<String> fetchUninvoicedOrdersWithSkusNotInCountry(@Param("startDate") LocalDateTime startDateTime, @Param("endDate") LocalDateTime endDateTime, @Param("shop") String shop, @Param("skus") List<String> skus, @Param("countries") List<String> countries);
|
||||
|
||||
List<String> findReadyAbnormalOrders(@Param("skus") List<String> skus, @Param("shops") List<String> shops);
|
||||
|
||||
List<String> findReadyAbnormalOrdersWithSkus(@Param("skus") List<String> skus);
|
||||
}
|
||||
|
|
|
@ -195,14 +195,14 @@
|
|||
INSERT INTO platform_order_content(
|
||||
id, create_by, create_time,
|
||||
update_by, update_time, platform_order_id,
|
||||
sku_id, quantity, erp_status, product_available, customization_data)
|
||||
sku_id, quantity, erp_status, product_available, customization_data, warehouse_name)
|
||||
VALUES
|
||||
<foreach collection="items" separator="," open="" close="" item="item" index="index">
|
||||
(
|
||||
UUID(), 'Mabang API', NOW(),
|
||||
'Mabang API', NOW(), #{item.platformOrderId},
|
||||
skuErpToId(#{item.erpCode}), #{item.quantity}, #{item.erpStatus},
|
||||
#{item.productAvailable}, #{item.specifics})
|
||||
#{item.productAvailable}, #{item.specifics}, #{item.warehouseName})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
|
|
|
@ -898,4 +898,170 @@
|
|||
WHERE shop_id = #{shopID}
|
||||
AND erp_status IN (1,2,3)
|
||||
</select>
|
||||
<select id="fetchUninvoicedOrdersWithSkusInCountry" resultType="java.lang.String">
|
||||
SELECT po.platform_order_id
|
||||
FROM platform_order po
|
||||
JOIN shop ON po.shop_id = shop.id
|
||||
JOIN country c ON po.country = c.name_en
|
||||
JOIN platform_order_content poc ON po.id = poc.platform_order_id
|
||||
JOIN sku s ON poc.sku_id = s.id
|
||||
WHERE po.erp_status IN (1,2)
|
||||
AND po.can_send = 2
|
||||
AND c.code IN
|
||||
<foreach
|
||||
collection="countries"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="country"
|
||||
>
|
||||
#{country}
|
||||
</foreach>
|
||||
AND shop.erp_code = #{shop}
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="skus"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="sku"
|
||||
>
|
||||
#{sku}
|
||||
</foreach>
|
||||
AND order_time >= #{startDate}
|
||||
AND order_time <= #{endDate}
|
||||
AND shipping_invoice_number IS NULL;
|
||||
</select>
|
||||
<select id="fetchUninvoicedOrdersWithSkusNotInCountry" resultType="java.lang.String">
|
||||
SELECT po.platform_order_id
|
||||
FROM platform_order po
|
||||
JOIN shop ON po.shop_id = shop.id
|
||||
JOIN country c ON po.country = c.name_en
|
||||
JOIN platform_order_content poc ON po.id = poc.platform_order_id
|
||||
JOIN sku s ON poc.sku_id = s.id
|
||||
WHERE po.erp_status IN (1,2)
|
||||
AND po.can_send = 2
|
||||
AND c.code NOT IN
|
||||
<foreach
|
||||
collection="countries"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="country"
|
||||
>
|
||||
#{country}
|
||||
</foreach>
|
||||
AND shop.erp_code = #{shop}
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="skus"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="sku"
|
||||
>
|
||||
#{sku}
|
||||
</foreach>
|
||||
AND order_time >= #{startDate}
|
||||
AND order_time <= #{endDate}
|
||||
AND shipping_invoice_number IS NULL;
|
||||
</select>
|
||||
<select id="findReadyAbnormalOrders" resultType="java.lang.String">
|
||||
WITH skusInAbnormalOrders AS (
|
||||
SELECT poc.sku_id , poc.platform_order_id, poc.erp_status
|
||||
FROM platform_order po
|
||||
JOIN platform_order_content poc ON po.id = poc.platform_order_id
|
||||
JOIN shop s ON po.shop_id = s.id
|
||||
WHERE can_send = 2
|
||||
AND po.erp_status IN (1,2)
|
||||
AND po.order_time >= NOW() - INTERVAL 5 DAY
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="shops"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="shop">
|
||||
#{shop}
|
||||
</foreach>
|
||||
),
|
||||
orders_with_bad_sku AS (
|
||||
SELECT DISTINCT
|
||||
CASE
|
||||
WHEN s.id IS NULL AND sao.erp_status <> 5 THEN sao.platform_order_id
|
||||
END as order_id
|
||||
FROM skusInAbnormalOrders sao
|
||||
LEFT JOIN sku s ON sao.sku_id = s.id
|
||||
),
|
||||
ac_orders AS (
|
||||
SELECT DISTINCT poc.platform_order_id as id
|
||||
FROM platform_order_content poc
|
||||
JOIN sku s ON poc.sku_id = s.id
|
||||
JOIN platform_order po ON poc.platform_order_id = po.id
|
||||
WHERE po.can_send IN (1,2)
|
||||
AND po.can_send = 2
|
||||
AND po.order_time >= NOW() - INTERVAL 5 DAY
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="skus"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="sku">
|
||||
#{sku}
|
||||
</foreach>
|
||||
AND poc.erp_status <> 5
|
||||
AND poc.warehouse_name != '法国巴黎仓库-唯客路'
|
||||
),
|
||||
orders_ready AS (
|
||||
SELECT po.id as id
|
||||
FROM platform_order po
|
||||
JOIN shop s ON po.shop_id = s.id
|
||||
LEFT JOIN orders_with_bad_sku owbs ON po.id = owbs.order_id
|
||||
WHERE owbs.order_id IS NULL
|
||||
AND po.erp_status IN (1,2)
|
||||
AND po.can_send = 2
|
||||
AND po.order_time >= NOW() - INTERVAL 5 DAY
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="shops"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="shop">
|
||||
#{shop}
|
||||
</foreach>
|
||||
)
|
||||
SELECT ors.id
|
||||
FROM orders_ready ors
|
||||
WHERE ors.id NOT IN (SELECT * FROM ac_orders);
|
||||
</select>
|
||||
<select id="findReadyAbnormalOrdersWithSkus" resultType="java.lang.String">
|
||||
SELECT DISTINCT poc.platform_order_id
|
||||
FROM platform_order_content poc
|
||||
JOIN sku s ON poc.sku_id = s.id
|
||||
JOIN platform_order po ON poc.platform_order_id = po.id
|
||||
WHERE po.can_send IN (1,2)
|
||||
AND po.can_send = 2
|
||||
AND po.order_time >= NOW() - INTERVAL 5 DAY
|
||||
AND s.erp_code IN
|
||||
<foreach
|
||||
collection="skus"
|
||||
separator=","
|
||||
open="("
|
||||
close=")"
|
||||
index="index"
|
||||
item="sku">
|
||||
#{sku}
|
||||
</foreach>
|
||||
AND poc.erp_status <> 5
|
||||
AND poc.warehouse_name = '法国巴黎仓库-唯客路';
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
@ -248,4 +248,11 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
|
|||
void anonymizePersonalData(int indirectClientAnonymizationPeriod);
|
||||
|
||||
List<PlatformOrderOption> ordersByShop(String shopID);
|
||||
|
||||
List<String> fetchUninvoicedOrdersWithSkusInCountry(LocalDateTime startDateTime, LocalDateTime endDateTime, String shop, List<String> skus, List<String> countries);
|
||||
List<String> fetchUninvoicedOrdersWithSkusNotInCountry(LocalDateTime startDateTime, LocalDateTime endDateTime, String shop, List<String> skus, List<String> countries);
|
||||
|
||||
List<String> findReadyAbnormalOrders(List<String> skus, List<String> shops);
|
||||
|
||||
List<String> findReadyAbnormalOrdersWithSkus(List<String> skus);
|
||||
}
|
||||
|
|
|
@ -501,4 +501,23 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
|
|||
public List<PlatformOrderOption> ordersByShop(String shopID) {
|
||||
return platformOrderMap.ordersByShop(shopID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fetchUninvoicedOrdersWithSkusInCountry(LocalDateTime startDateTime, LocalDateTime endDateTime, String shop, List<String> skus, List<String> countries) {
|
||||
return platformOrderMap.fetchUninvoicedOrdersWithSkusInCountry(startDateTime, endDateTime, shop, skus, countries);
|
||||
}
|
||||
@Override
|
||||
public List<String> fetchUninvoicedOrdersWithSkusNotInCountry(LocalDateTime startDateTime, LocalDateTime endDateTime, String shop, List<String> skus, List<String> countries) {
|
||||
return platformOrderMap.fetchUninvoicedOrdersWithSkusNotInCountry(startDateTime, endDateTime, shop, skus, countries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> findReadyAbnormalOrders(List<String> skus, List<String> shops) {
|
||||
return platformOrderMap.findReadyAbnormalOrders(skus, shops);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> findReadyAbnormalOrdersWithSkus(List<String> skus) {
|
||||
return platformOrderMap.findReadyAbnormalOrdersWithSkus(skus);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<#include "../components/header.ftl">
|
||||
<tr>
|
||||
<td style="padding:35px 0;">Cher Collègue</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 0 35px 0;">Voici la liste des commandes dont le status a été mis à jour à "Normal" :</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:10px 0;">Nombre de commandes :<b> ${count}</b><br/>
|
||||
<ul>
|
||||
<#list orderIds as orderId>
|
||||
<li>${orderId}</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<#include "../components/footer.ftl">
|
Loading…
Reference in New Issue