feat : client order management via Mabang API

pull/6261/head
Gauthier LO 2024-05-27 17:11:22 +02:00
parent a85c7b6beb
commit 0415ca6b5b
23 changed files with 562 additions and 49 deletions

View File

@ -1,18 +1,31 @@
package org.jeecg.modules.business.controller.admin;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.net.URLDecoder;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderStatus;
import org.jeecg.modules.business.domain.job.ThrottlingExecutorService;
import org.jeecg.modules.business.entity.Client;
import org.jeecg.modules.business.mapper.PlatformOrderMapper;
import org.jeecg.modules.business.vo.PlatformOrderQuantity;
import org.jeecg.modules.business.service.*;
import org.jeecg.modules.business.vo.*;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
@ -25,11 +38,10 @@ import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.business.entity.PlatformOrderContent;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.vo.PlatformOrderPage;
import org.jeecg.modules.business.service.IPlatformOrderService;
import org.jeecg.modules.business.service.IPlatformOrderContentService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.multipart.MultipartFile;
@ -38,10 +50,13 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import static org.jeecg.modules.business.vo.PlatformOrderOperation.Action.CANCEL;
import static org.jeecg.modules.business.vo.PlatformOrderOperation.Action.SUSPEND;
/**
* @Description:
@ -54,15 +69,28 @@ import org.jeecg.common.aspect.annotation.AutoLog;
@RequestMapping("/business/platformOrder")
@Slf4j
public class PlatformOrderController {
private final IPlatformOrderService platformOrderService;
@Autowired
private IClientService clientService;
@Autowired
private EmailService emailService;
@Autowired
private IPlatformOrderService platformOrderService;
@Autowired
private PlatformOrderMapper platformOrderMapper;
@Autowired
public PlatformOrderController(IPlatformOrderService platformOrderService) {
this.platformOrderService = platformOrderService;
}
private IPlatformOrderMabangService platformOrderMabangService;
@Autowired
private IShopService shopService;
@Autowired
private ISysDepartService sysDepartService;
@Autowired
private Environment env;
@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;
private static final Integer DEFAULT_NUMBER_OF_THREADS = 2;
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
/**
* Fetchs all orders with erp_status = 1, no logicistic channel and product available
@ -343,4 +371,121 @@ public class PlatformOrderController {
List<PlatformOrderQuantity> res = platformOrderService.monthOrderNumber();
return Result.OK(res);
}
/**
* Get all orders by shop with erp status 1, 2 and 3
* @return
*/
@GetMapping("/ordersByShop")
public Result<List<PlatformOrderOption>> ordersByShop(@RequestParam("shopID") String shopID) {
List<PlatformOrderOption> res = platformOrderService.ordersByShop(shopID);
return Result.OK(res);
}
@PostMapping("/orderManagement")
public Result<?> orderManagement(@RequestBody List<PlatformOrderOperation> orderOperations) throws IOException {
String companyOrgCode = sysDepartService.queryCodeByDepartName(env.getProperty("company.orgName"));
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
Client client;
if(!sysUser.getOrgCode().equals(companyOrgCode)) {
client = clientService.getCurrentClient();
if (client == null) {
return Result.error(500,"Client not found. Please contact administrator.");
}
String shopId = orderOperations.get(0).getShopId();
List<String> shopIds = shopService.listIdByClient(client.getId());
if(!shopIds.contains(shopId)) {
return Result.error(500,"You are not allowed to perform this operation.");
}
} else {
client = clientService.getByShopId(orderOperations.get(0).getShopId());
}
long suspendCount = 0, cancelCount = 0;
List<PlatformOrderOperation> ordersToSuspend = orderOperations.stream()
.filter(orderOperation -> orderOperation.getAction().equalsIgnoreCase(SUSPEND.getValue()))
.collect(Collectors.toList());
List<PlatformOrderOperation> ordersToCancel = orderOperations.stream()
.filter(orderOperation -> orderOperation.getAction().equalsIgnoreCase(CANCEL.getValue()))
.collect(Collectors.toList());
for(PlatformOrderOperation orderOperation : ordersToSuspend) {
suspendCount += orderOperation.getOrderIds().split(",").length;
}
for(PlatformOrderOperation orderOperation : ordersToCancel) {
cancelCount += orderOperation.getOrderIds().split(",").length;
}
log.info("{} Orders to suspend: {}", suspendCount, ordersToSuspend);
log.info("{} Orders to cancel: {}", cancelCount, ordersToCancel);
Responses cancelResponses = new Responses();
Responses suspendResponses = new Responses();
// Cancel orders
ExecutorService cancelExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Responses>> futuresCancel = ordersToCancel.stream()
.map(orderOperation -> CompletableFuture.supplyAsync(
() -> platformOrderMabangService.cancelOrders(orderOperation),
cancelExecutorService)
).collect(Collectors.toList());
List<Responses>cancelResults = futuresCancel.stream().map(CompletableFuture::join).collect(Collectors.toList());
cancelResults.forEach(res -> {
cancelResponses.getSuccesses().addAll(res.getSuccesses());
cancelResponses.getFailures().addAll(res.getFailures());
});
log.info("{}/{} orders cancelled successfully.", cancelResponses.getSuccesses().size(), cancelCount);
log.info("Failed to cancel orders: {}", cancelResponses.getFailures());
// Suspend orders
ExecutorService suspendExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Responses>> futuresSuspend = ordersToSuspend.stream()
.map(orderOperation -> CompletableFuture.supplyAsync(
() -> platformOrderMabangService.suspendOrder(orderOperation),
suspendExecutorService)
).collect(Collectors.toList());
List<Responses> suspendResults = futuresSuspend.stream().map(CompletableFuture::join).collect(Collectors.toList());
suspendResults.forEach(res -> {
suspendResponses.getSuccesses().addAll(res.getSuccesses());
suspendResponses.getFailures().addAll(res.getFailures());
});
log.info("{}/{} orders suspended successfully.", suspendResponses.getSuccesses().size(), suspendCount);
JSONObject result = new JSONObject();
result.put("cancelResult", cancelResponses);
result.put("suspendResult", suspendResponses);
// send mail
String subject = "[" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "] Orders management report";
String destEmail = sysUser.getEmail();
Properties prop = emailService.getMailSender();
Map<String, Object> templateModel = new HashMap<>();
templateModel.put("firstname", client.getFirstName());
templateModel.put("lastname", client.getSurname());
if(cancelCount > 0)
templateModel.put("cancelSuccessCount", cancelResponses.getSuccesses().size() + "/" + cancelCount);
if(suspendCount > 0)
templateModel.put("suspendSuccessCount", suspendResponses.getSuccesses().size() + "/" + suspendCount);
templateModel.put("cancelFailures", cancelResponses.getFailures());
templateModel.put("suspendFailures", suspendResponses.getFailures());
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("client/orderManagementNotification.ftl");
String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);
emailService.sendSimpleMessage(destEmail, subject, htmlBody, session);
log.info("Mail sent successfully !");
} catch (TemplateException | MessagingException e) {
throw new RuntimeException(e);
}
return Result.OK(result);
}
}

View File

@ -2,6 +2,8 @@ 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.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.business.domain.api.mabang.RequestBody;
@ -10,7 +12,11 @@ import java.util.function.Function;
public class ChangeOrderRequestBody implements RequestBody {
@Setter
@Getter
private String platformOrderId;
private String orderStatus;
private String remark;
private final HashSet<Pair<String, Integer>> oldSkuData;
@ -29,11 +35,13 @@ public class ChangeOrderRequestBody implements RequestBody {
}
}
public ChangeOrderRequestBody(String platformOrderId, HashSet<Pair<String, Integer>> oldSkuData,
HashSet<Pair<String, Integer>> newSkuData) {
public ChangeOrderRequestBody(String platformOrderId, String orderStatus, HashSet<Pair<String, Integer>> oldSkuData,
HashSet<Pair<String, Integer>> newSkuData, String remark) {
this.platformOrderId = platformOrderId;
this.oldSkuData = oldSkuData;
this.newSkuData = newSkuData;
this.orderStatus = orderStatus;
this.remark = remark;
}
@Override
@ -45,8 +53,10 @@ public class ChangeOrderRequestBody implements RequestBody {
public JSONObject parameters() {
JSONObject json = new JSONObject();
putNonNull(json, "platformOrderId", platformOrderId);
putNonNull(json, "orderStatus", orderStatus);
putNonNull(json, "remark", remark);
JSONArray stockDataArray = new JSONArray();
if (!oldSkuData.isEmpty()) {
if (oldSkuData != null && !oldSkuData.isEmpty()) {
for (Pair<String, Integer> oldSkuDatum : oldSkuData) {
JSONObject stockData = new JSONObject();
stockData.put("warehouseName", DEFAULT_WAREHOUSE_NAME);
@ -57,26 +67,20 @@ public class ChangeOrderRequestBody implements RequestBody {
}
}
for (Pair<String, Integer> newSkuDatum : newSkuData) {
JSONObject stockData = new JSONObject();
stockData.put("warehouseName", DEFAULT_WAREHOUSE_NAME);
stockData.put("stockSku", newSkuDatum.getKey());
stockData.put("quantity", newSkuDatum.getValue());
stockData.put("type", OperationType.ADD.code);
stockDataArray.add(stockData);
if(newSkuData != null) {
for (Pair<String, Integer> newSkuDatum : newSkuData) {
JSONObject stockData = new JSONObject();
stockData.put("warehouseName", DEFAULT_WAREHOUSE_NAME);
stockData.put("stockSku", newSkuDatum.getKey());
stockData.put("quantity", newSkuDatum.getValue());
stockData.put("type", OperationType.ADD.code);
stockDataArray.add(stockData);
}
}
json.put("stockData", stockDataArray.toJSONString());
putNonNull(json,"stockData", stockDataArray.toJSONString());
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);

View File

@ -0,0 +1,30 @@
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal;
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 OrderSuspendRequest extends Request {
public OrderSuspendRequest(OrderSuspendRequestBody body) {
super(body);
}
@Override
public OrderSuspendResponse send() {
ResponseEntity<String> res = rawSend();
return OrderSuspendResponse.parse(JSON.parseObject(res.getBody()));
}
}

View File

@ -0,0 +1,46 @@
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal;
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 OrderSuspendRequestBody implements RequestBody {
private String platformOrderId;
private final String abnormal_label_name = "客户要求暂时不处理";
private String description;
public OrderSuspendRequestBody(String platformOrderId, String description) {
this.platformOrderId = platformOrderId;
this.description = description;
}
@Override
public String api() {
return "order-do-order-abnormal";
}
@Override
public Map<String, Object> parameters() {
JSONObject json = new JSONObject();
putNonNull(json, "platformOrderId", platformOrderId);
putNonNull(json, "abnormal_label_name", abnormal_label_name);
putNonNull(json, "description", description);
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));
}
}
}

View File

@ -0,0 +1,11 @@
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal;
/**
* This class represents error that is returned by target order-do-order-abnormal API
* Message will contain error details.
*/
public class OrderSuspendRequestErrorException extends RuntimeException {
public OrderSuspendRequestErrorException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,42 @@
package org.jeecg.modules.business.domain.api.mabang.orderDoOrderAbnormal;
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 OrderSuspendResponse extends Response {
OrderSuspendResponse(Code successCode) {
super(successCode);
}
/**
* 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 OrderSuspendRequestErrorException if response code represents error.
*/
public static OrderSuspendResponse parse(JSONObject json) throws OrderSuspendRequestErrorException {
log.debug("Constructing a response by json.");
String code = json.getString("code");
if (code.equals("200"))
return new OrderSuspendResponse(Code.SUCCESS);
else
return new OrderSuspendResponse(Code.ERROR);
}
@Override
public String toString() {
return "OrderSuspendResponse{" +
", code=" + this.success() +
'}';
}
}

View File

@ -126,8 +126,8 @@ public class AddPortraitTubeJob implements Job {
HashSet<Pair<String, Integer>> adequateTubes = currentAndAdequateTubes.getRight();
// Do nothing if current tubes are the adequate tubes
if (!currentTubes.containsAll(adequateTubes) || !adequateTubes.containsAll(currentTubes)) {
ChangeOrderRequestBody changeOrderRequestBody = new ChangeOrderRequestBody(mabangOrder.getPlatformOrderId(),
currentTubes, adequateTubes);
ChangeOrderRequestBody changeOrderRequestBody = new ChangeOrderRequestBody(mabangOrder.getPlatformOrderId(), null,
currentTubes, adequateTubes, null);
changeOrderRequests.add(changeOrderRequestBody);
}
}

View File

@ -33,4 +33,6 @@ public interface ClientMapper extends BaseMapper<Client> {
Client getClientFromInvoice(@Param("invoiceNumber") String invoiceNumber);
void anonymizePersonalData(@Param("period") int directClientAnonymizationPeriod);
Client getByShopId(@Param("shopId") String shopId);
}

View File

@ -6,6 +6,7 @@ import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.entity.PlatformOrderShopSync;
import org.jeecg.modules.business.vo.OrderKpi;
import org.jeecg.modules.business.vo.PlatformOrderOption;
import org.jeecg.modules.business.vo.ShippingFeeBillableOrders;
import org.jeecg.modules.business.vo.clientPlatformOrder.ClientPlatformOrderPage;
import org.jeecg.modules.business.vo.clientPlatformOrder.section.OrderQuantity;
@ -221,4 +222,6 @@ public interface PlatformOrderMapper extends BaseMapper<PlatformOrder> {
Map<String, String> fetchShippingPeriodAndType(@Param("invoiceNumber") String invoiceNumber);
void anonymizePersonalData(@Param("period") int indirectClientAnonymizationPeriod);
List<PlatformOrderOption> ordersByShop(@Param("shopID") String shopID);
}

View File

@ -90,4 +90,10 @@
WHERE active = 0
AND IF (update_time IS NOT NULL, update_time, create_time) &lt; DATE_SUB(NOW(), INTERVAL #{period} YEAR);
</update>
<select id="getByShopId" resultType="org.jeecg.modules.business.entity.Client">
SELECT c.*
FROM client c
JOIN shop s ON c.id = s.owner_id
WHERE s.id = #{shopId};
</select>
</mapper>

View File

@ -891,4 +891,11 @@
SET recipient = UUID()
WHERE create_time &lt; DATE_SUB(NOW(), INTERVAL #{period} YEAR);
</update>
<select id="ordersByShop" resultType="org.jeecg.modules.business.vo.PlatformOrderOption">
SELECT platform_order_id as value, platform_order_number as label, erp_status, IF(erp_status = 1 OR erp_status = 2, false, true) as disabled
FROM platform_order
WHERE shop_id = #{shopID}
AND erp_status IN (1,2,3)
</select>
</mapper>

View File

@ -287,8 +287,8 @@
<select id="listSkus" resultType="org.jeecg.modules.business.entity.Sku">
SELECT *
FROM sku
JOIN client_sku cs ON sku.id = cs.sku_id
JOIN client c ON cs.client_id = c.id
JOIN client_sku ON sku.id = client_sku.sku_id
JOIN client c ON client_sku.client_id = c.id
WHERE c.active = 1;
</select>
<select id="getByErpCode" resultType="org.jeecg.modules.business.entity.Sku">

View File

@ -60,4 +60,6 @@ public interface IClientService extends IService<Client> {
Client getClientFromInvoice(String invoiceNumber);
void anonymizePersonalData(int directClientAnonymizationPeriod);
Client getByShopId(String shopId);
}

View File

@ -2,7 +2,8 @@ package org.jeecg.modules.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
import org.springframework.stereotype.Service;
import org.jeecg.modules.business.vo.PlatformOrderOperation;
import org.jeecg.modules.business.vo.Responses;
import java.util.Collection;
import java.util.List;
@ -10,7 +11,6 @@ import java.util.List;
/**
* Services related to operations on {@code Order} entity
*/
@Service
public interface IPlatformOrderMabangService extends IService<Order> {
/**
* Save orders to DB from mabang api.
@ -29,4 +29,7 @@ public interface IPlatformOrderMabangService extends IService<Order> {
*/
void updateMergedOrderFromMabang(Order mergedOrder, Collection<String> sourceOrderErpId);
Responses suspendOrder(PlatformOrderOperation orderOperation);
Responses cancelOrders(PlatformOrderOperation orderOperation);
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.business.controller.UserException;
import org.jeecg.modules.business.entity.*;
import org.jeecg.modules.business.vo.PlatformOrderOption;
import org.jeecg.modules.business.vo.PlatformOrderQuantity;
import org.jeecg.modules.business.vo.ShippingFeeBillableOrders;
import org.jeecg.modules.business.vo.SkuQuantity;
@ -245,4 +246,6 @@ public interface IPlatformOrderService extends IService<PlatformOrder> {
void anonymizePersonalData(int indirectClientAnonymizationPeriod);
List<PlatformOrderOption> ordersByShop(String shopID);
}

View File

@ -1,5 +1,6 @@
package org.jeecg.modules.business.service;
import com.aspose.cells.PdfSaveOptions;
import com.aspose.cells.SaveFormat;
import com.aspose.cells.Workbook;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -781,9 +782,12 @@ public class PlatformOrderShippingInvoiceService {
pdfFilePath = INVOICE_PDF_DIR + "/" + m.group(2) + ".pdf";
}
// Créé un classeur pour charger le fichier Excel
PdfSaveOptions saveOptions = new PdfSaveOptions();
saveOptions.setDefaultFont("Arial");
saveOptions.setCheckWorkbookDefaultFont(false);
Workbook workbook = new Workbook(excelFilePath);
// On enregistre le document au format PDF
workbook.save(pdfFilePath, SaveFormat.PDF);
workbook.save(pdfFilePath, saveOptions);
return pdfFilePath;
}
return "ERROR";

View File

@ -166,4 +166,9 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
public void anonymizePersonalData(int directClientAnonymizationPeriod) {
clientMapper.anonymizePersonalData(directClientAnonymizationPeriod);
}
@Override
public Client getByShopId(String shopId) {
return clientMapper.getByShopId(shopId);
}
}

View File

@ -2,20 +2,31 @@ package org.jeecg.modules.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderRequest;
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderRequestBody;
import org.jeecg.modules.business.domain.api.mabang.dochangeorder.ChangeOrderResponse;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.Order;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderItem;
import org.jeecg.modules.business.domain.api.mabang.getorderlist.OrderStatus;
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.domain.job.ThrottlingExecutorService;
import org.jeecg.modules.business.entity.PlatformOrder;
import org.jeecg.modules.business.mapper.PlatformOrderMabangMapper;
import org.jeecg.modules.business.service.IPlatformOrderMabangService;
import org.jeecg.modules.business.vo.PlatformOrderOperation;
import org.jeecg.modules.business.vo.Responses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -30,14 +41,11 @@ import static java.util.stream.Collectors.toList;
@Service
@Slf4j
public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMabangMapper, Order> implements IPlatformOrderMabangService {
private final PlatformOrderMabangMapper platformOrderMabangMapper;
@Autowired
public PlatformOrderMabangServiceImpl(PlatformOrderMabangMapper platformOrderMabangMapper) {
this.platformOrderMabangMapper = platformOrderMabangMapper;
}
private PlatformOrderMabangMapper platformOrderMabangMapper;
private static final Integer DEFAULT_NUMBER_OF_THREADS = 2;
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 10;
@Override
@Transactional
public void saveOrderFromMabang(List<Order> orders) {
@ -193,6 +201,72 @@ public class PlatformOrderMabangServiceImpl extends ServiceImpl<PlatformOrderMab
platformOrderMabangMapper.updateMergedOrderItems(targetID, sourceIDs);
}
@Override
public Responses suspendOrder(PlatformOrderOperation orderOperation) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<String> orderIds = Arrays.stream(orderOperation.getOrderIds().split(",")).map(String::trim).collect(toList());
// group id is the response from mabang API
Responses responses = new Responses();
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Responses>> futures = orderIds.stream()
.map(id -> CompletableFuture.supplyAsync(() -> {
OrderSuspendRequestBody body = new OrderSuspendRequestBody(id, sysUser.getRealname() + " : " + orderOperation.getReason());
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;
}, throttlingExecutorService))
.collect(toList());
List<Responses> results = futures.stream()
.map(CompletableFuture::join)
.collect(toList());
results.forEach(r -> {
responses.getSuccesses().addAll(r.getSuccesses());
responses.getFailures().addAll(r.getFailures());
});
log.info("{}/{} orders suspended successfully.", responses.getSuccesses().size(), orderIds.size());
return responses;
}
@Override
public Responses cancelOrders(PlatformOrderOperation orderOperation) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
List<String> orderIds = Arrays.stream(orderOperation.getOrderIds().split(",")).map(String::trim).collect(toList());
Responses responses = new Responses();
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
List<CompletableFuture<Responses>> futures = orderIds.stream()
.map(id -> CompletableFuture.supplyAsync(() -> {
ChangeOrderRequestBody body = new ChangeOrderRequestBody(id, "5",null, null, sysUser.getRealname() + " : " + orderOperation.getReason());
ChangeOrderRequest request = new ChangeOrderRequest(body);
ChangeOrderResponse response = request.send();
Responses r = new Responses();
if(response.success())
r.addSuccess(id);
else
r.addFailure(id);
return r;
}, throttlingExecutorService))
.collect(toList());
List<Responses> results = futures.stream()
.map(CompletableFuture::join)
.collect(toList());
results.forEach(r -> {
responses.getSuccesses().addAll(r.getSuccesses());
responses.getFailures().addAll(r.getFailures());
});
log.info("{}/{} orders cancelled successfully.", responses.getSuccesses().size(), orderIds.size());
return responses;
}
private void updateExistedOrders(List<Order> orders) {
}

View File

@ -496,4 +496,9 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
public void anonymizePersonalData(int indirectClientAnonymizationPeriod) {
platformOrderMap.anonymizePersonalData(indirectClientAnonymizationPeriod);
}
@Override
public List<PlatformOrderOption> ordersByShop(String shopID) {
return platformOrderMap.ordersByShop(shopID);
}
}

View File

@ -0,0 +1,31 @@
package org.jeecg.modules.business.vo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class PlatformOrderOperation {
@JSONField(name = "shopId")
private String shopId;
@JSONField(name = "orderIds")
private String orderIds;
@JSONField(name = "action")
private String action;
@JSONField(name = "reason")
private String reason;
public enum Action {
CANCEL("cancel"),
SUSPEND("suspend");
private final String action;
Action(String value) {
this.action = value;
}
public String getValue() {
return action;
}
}
}

View File

@ -0,0 +1,28 @@
package org.jeecg.modules.business.vo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class PlatformOrderOption {
/**
* PlatformOrder.PlatformOrderNumber
*/
@JSONField(name = "value")
private String value;
/**
* PlatformOrder.PlatformOrderNumber
*/
@JSONField(name = "label")
private String label;
/**
* PlatformOrder.ErpStatus
*/
@JSONField(name = "erp_status")
private String erpStatus;
/**
* PlatformOrder.ErpStatus = 1 or 2 ? false : true
*/
@JSONField(name = "disabled")
private boolean disabled;
}

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.business.vo;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class Responses {
private List<String> successes = new ArrayList<>();
private List<String> failures = new ArrayList<>();
public void addSuccess(String message) {
successes.add(message);
}
public void addFailure(String message) {
failures.add(message);
}
}

View File

@ -0,0 +1,43 @@
<#include "../components/header.ftl">
<tr>
<td style="padding:35px 0;">Cher(e) ${firstname} ${lastname},</td>
</tr>
<tr>
<td style="padding:0 0 35px 0;">Voici un récapitulatif des opérations que vous avez effectuées sur vos commandes :</td>
</tr>
<#if cancelSuccessCount??>
<tr>
<td style="padding:0 0 35px 0;">Demandes d'annulations de commande : <b>${cancelSuccessCount}</b> réussie(s)</td>
</tr>
<#if cancelFailures?size gt 0 >
<tr>
<td style="padding:0 0 35px 0;">Demandes d'annulations de commande échouées :<br/>
<ul>
<#list cancelFailures as failure>
<li>${failure}</li>
</#list>
</ul>
</td>
</tr>
</#if>
</#if>
<#if suspendSuccessCount??>
<tr>
<td style="padding:0 0 35px 0;">Demandes de suspension de commande : <b>${suspendSuccessCount}</b> réussie(s)</td>
</tr>
<#if suspendFailures?size gt 0 >
<tr>
<td style="padding:0 0 35px 0;">Demandes de suspension de commande échouées :<br/>
<ul>
<#list suspendFailures as failure>
<li>${failure}</li>
</#list>
</ul>
</td>
</tr>
</#if>
</#if>
<tr>
<td style="padding:0 0 35px 0;">Pour toute information complémentaire nous vous invitons à vous rapprocher de votre conseiller.</td>
</tr>
<#include "../components/footer.ftl">