mirror of https://github.com/jeecgboot/jeecg-boot
Merge remote-tracking branch 'origin/feature/creditPage' into feature/creditPage
commit
fdec4d77f1
|
@ -121,6 +121,8 @@ public class InvoiceController {
|
|||
|
||||
@Value("${jeecg.path.shippingInvoiceDetailDir}")
|
||||
private String INVOICE_DETAIL_DIR;
|
||||
@Value("${jeecg.path.shippingInvoicePdfDir}")
|
||||
private String INVOICE_PDF_DIR;
|
||||
|
||||
private final String SECTION_START = "<section><ul>";
|
||||
private final String SECTION_END = "</ul></section>";
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.jeecg.modules.business.controller.admin.shippingInvoice;
|
||||
|
||||
import com.aspose.cells.SaveFormat;
|
||||
import com.aspose.cells.Workbook;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
@ -49,8 +47,6 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -77,6 +73,8 @@ public class ShippingInvoiceController {
|
|||
@Autowired
|
||||
private IPurchaseOrderService purchaseOrderService;
|
||||
@Autowired
|
||||
private PlatformOrderShippingInvoiceService platformOrderShippingInvoiceService;
|
||||
@Autowired
|
||||
private ISavRefundService savRefundService;
|
||||
@Autowired
|
||||
private IShippingInvoiceService shippingInvoiceService;
|
||||
|
@ -88,10 +86,6 @@ public class ShippingInvoiceController {
|
|||
private String INVOICE_LOCATION;
|
||||
@Value("${jeecg.path.shippingInvoiceDetailDir}")
|
||||
private String INVOICE_DETAIL_LOCATION;
|
||||
@Value("${jeecg.path.shippingInvoicePdfDir}")
|
||||
private String INVOICE_PDF_LOCATION;
|
||||
@Value("${jeecg.path.shippingInvoiceDetailPdfDir}")
|
||||
private String INVOICE_DETAIL_PDF_LOCAION;
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
@ -280,8 +274,6 @@ public class ShippingInvoiceController {
|
|||
return Result.OK("文件导入失败!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Downloads the invoice and returns it in form of bytearray
|
||||
* @param invoiceNumber the invoice we want to download
|
||||
|
@ -354,7 +346,7 @@ public class ShippingInvoiceController {
|
|||
*/
|
||||
@GetMapping(value = "/downloadPdf")
|
||||
public ResponseEntity<?> downloadPdf(@RequestParam("invoiceNumber") String invoiceNumber) throws Exception {
|
||||
String pdfFilePath = convertToPdf(invoiceNumber, "invoice");
|
||||
String pdfFilePath = platformOrderShippingInvoiceService.convertToPdf(invoiceNumber, "invoice");
|
||||
if(!pdfFilePath.equals("ERROR")) {
|
||||
File file = new File(pdfFilePath);
|
||||
HttpHeaders header = new HttpHeaders();
|
||||
|
|
|
@ -21,12 +21,13 @@ import java.util.List;
|
|||
*/
|
||||
@Slf4j
|
||||
public class HLRequest {
|
||||
private final static String URL = "http://www.antugj.com:8082/selectTrack.htm";
|
||||
private static final RequestConfig REQUEST_CONFIG = RequestConfig.custom().build();
|
||||
private final String url;
|
||||
|
||||
private final List<String> billCodes;
|
||||
|
||||
public HLRequest(List<String> billCodes) {
|
||||
public HLRequest(String url, List<String> billCodes) {
|
||||
this.url = url;
|
||||
this.billCodes = billCodes;
|
||||
}
|
||||
|
||||
|
@ -40,7 +41,7 @@ public class HLRequest {
|
|||
while (attempts++ < 5) {
|
||||
try {
|
||||
HttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(REQUEST_CONFIG).build();
|
||||
HttpPost request = new HttpPost(URL);
|
||||
HttpPost request = new HttpPost(url);
|
||||
|
||||
// adding the form data
|
||||
request.setEntity(new UrlEncodedFormEntity(generateFormData(), "UTF-8"));
|
||||
|
|
|
@ -126,14 +126,14 @@ public class Order {
|
|||
private String phone1;
|
||||
|
||||
public void setTrackingNumber(String trackingNumber) {
|
||||
if (trackingNumber != null && trackingNumber.length() == 0) {
|
||||
if (trackingNumber != null && trackingNumber.isEmpty()) {
|
||||
this.trackingNumber = null;
|
||||
} else
|
||||
this.trackingNumber = trackingNumber;
|
||||
}
|
||||
|
||||
public void setTrackingNumber1(String trackingNumber) {
|
||||
if (trackingNumber != null && trackingNumber.length() == 0) {
|
||||
if (trackingNumber != null && trackingNumber.isEmpty()) {
|
||||
this.trackingNumber1 = null;
|
||||
} else
|
||||
this.trackingNumber1 = trackingNumber;
|
||||
|
@ -164,7 +164,7 @@ public class Order {
|
|||
}
|
||||
|
||||
public void setShippingTime(String shippingTime) {
|
||||
if (shippingTime != null && shippingTime.length() == 0) {
|
||||
if (shippingTime != null && shippingTime.isEmpty()) {
|
||||
this.shippingTime = null;
|
||||
} else
|
||||
this.shippingTime = shippingTime;
|
||||
|
|
|
@ -34,6 +34,9 @@ public class OrderItem {
|
|||
|
||||
private String productAvailable;
|
||||
|
||||
@JSONField(name = "specifics")
|
||||
private String specifics;
|
||||
|
||||
/**
|
||||
* Status :
|
||||
* 2 = Normal
|
||||
|
|
|
@ -156,28 +156,49 @@ public abstract class AbstractInvoice<E, F, G, H, I> {
|
|||
// if the number of rows of data is greater than the available space in the default template
|
||||
// we shift down the rows after the table and we clone the row in the table
|
||||
int dataRowNumber = LAST_ROW - FIRST_ROW;
|
||||
int additionalRowNum = data.size() - dataRowNumber - 1;
|
||||
TOTAL_ROW = LAST_ROW+additionalRowNum;
|
||||
|
||||
int additionalRowNum = Math.max(data.size() - dataRowNumber - 1, 0);
|
||||
TOTAL_ROW = LAST_ROW + additionalRowNum;
|
||||
Sheet sheet = factory.getWorkbook().getSheetAt(0);
|
||||
org.apache.poi.ss.usermodel.Row sourceRow = sheet.getRow(FIRST_ROW);
|
||||
if(data.size() > dataRowNumber)
|
||||
int footerRow = TOTAL_ROW + 1; // la ligne à laquelle le footer commence (1 ligne avant le total)
|
||||
int imgShift = additionalRowNum; // le nombre de ligne qu'on va décaler les images (signatures et tampon)
|
||||
if(data.size() > dataRowNumber + 1)
|
||||
{
|
||||
int startRow = LAST_ROW+1;
|
||||
int fileLastRow = sheet.getLastRowNum();
|
||||
// shifting the footer of the file, to X rows below
|
||||
// making sure the whole footer is in the same page (13 lines) and we fill the end of page with blank data lines
|
||||
if(additionalRowNum%PAGE_ROW_MAX <= 13) {
|
||||
TOTAL_ROW = PAGE_ROW_MAX-2;
|
||||
sheet.shiftRows(startRow, fileLastRow, PAGE_ROW_MAX - LAST_ROW, true, false);
|
||||
|
||||
// si le nombre de lignes de data rentre dans 1 page A4
|
||||
if(data.size() < 44) {
|
||||
if(TOTAL_ROW > LAST_ROW + 3) { // s'il ne reste pas assez de place pour le footer
|
||||
// on shift le footer à la page suivante (total + signature etc..)
|
||||
sheet.shiftRows(startRow, fileLastRow, PAGE_ROW_MAX - LAST_ROW - 1, true, false);
|
||||
footerRow = PAGE_ROW_MAX - LAST_ROW + 1;
|
||||
imgShift = PAGE_ROW_MAX - LAST_ROW + 1;
|
||||
TOTAL_ROW = PAGE_ROW_MAX;
|
||||
}
|
||||
else {
|
||||
sheet.shiftRows(startRow, fileLastRow, additionalRowNum, true, false);
|
||||
footerRow = additionalRowNum + 1;
|
||||
TOTAL_ROW = LAST_ROW + additionalRowNum+1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// +6 because if we use US template there's one more row
|
||||
sheet.shiftRows(startRow, fileLastRow, additionalRowNum, true, false);
|
||||
else {// on dépasse forcément le format A4 d'un PDF
|
||||
if(((TOTAL_ROW - 44) % 63) < 13) {
|
||||
sheet.shiftRows(startRow, fileLastRow, TOTAL_ROW - LAST_ROW + ((TOTAL_ROW-44)%63), true, false);
|
||||
footerRow = additionalRowNum + ((TOTAL_ROW-44)%63) + 1;
|
||||
imgShift = TOTAL_ROW-44 + ((TOTAL_ROW-44)%63) -1;
|
||||
TOTAL_ROW += ((TOTAL_ROW-44)%63) + 1;
|
||||
}
|
||||
else {
|
||||
sheet.shiftRows(startRow, fileLastRow, TOTAL_ROW - LAST_ROW, true, false);
|
||||
footerRow = TOTAL_ROW - LAST_ROW +1;
|
||||
}
|
||||
}
|
||||
|
||||
// inserting new rows after row 42
|
||||
for(int i = 0; i < (data.size() - dataRowNumber - 1 <= 13 ? PAGE_ROW_MAX-LAST_ROW+1 : additionalRowNum+1); i++) {
|
||||
for(int i = 0; i < footerRow; i++) {
|
||||
sheet.createRow(startRow-1 + i);
|
||||
org.apache.poi.ss.usermodel.Row newRow = sheet.getRow(startRow-1 + i);
|
||||
newRow.setHeight(sourceRow.getHeight());
|
||||
|
@ -192,7 +213,7 @@ public abstract class AbstractInvoice<E, F, G, H, I> {
|
|||
cellStyle.setBorderLeft(BorderStyle.DOUBLE);
|
||||
cell.setCellStyle(cellStyle);
|
||||
}
|
||||
if(startRow + i < PAGE_ROW_MAX-2) {
|
||||
if((startRow + i <= TOTAL_ROW && data.size() >= 44) || (startRow + i <= PAGE_ROW_MAX - 1 && data.size() < 44)) {
|
||||
if (j >= 2 && j <= 7) {
|
||||
middleCellStyle.setBorderLeft(BorderStyle.THIN);
|
||||
middleCellStyle.setBorderRight(BorderStyle.THIN);
|
||||
|
@ -209,11 +230,14 @@ public abstract class AbstractInvoice<E, F, G, H, I> {
|
|||
XSSFPicture picture = (XSSFPicture)shape;
|
||||
XSSFClientAnchor anchor = picture.getClientAnchor();
|
||||
|
||||
anchor.setRow1(data.size() - dataRowNumber - 1 <= 13 ? anchor.getRow1() + PAGE_ROW_MAX-LAST_ROW : anchor.getRow1() + additionalRowNum);
|
||||
anchor.setRow2(data.size() - dataRowNumber - 1 <= 13 ? anchor.getRow2() + PAGE_ROW_MAX-LAST_ROW : anchor.getRow2() + additionalRowNum);
|
||||
anchor.setRow1(anchor.getRow1() + imgShift);
|
||||
anchor.setRow2(anchor.getRow2() + imgShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
TOTAL_ROW = LAST_ROW + additionalRowNum + 1;
|
||||
}
|
||||
// table section
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
lineNum = i + FIRST_ROW;
|
||||
|
@ -298,16 +322,16 @@ public abstract class AbstractInvoice<E, F, G, H, I> {
|
|||
totalDueCellStyle.setBorderTop(BorderStyle.THIN);
|
||||
totalDueCellStyle.setFont(arialBold);
|
||||
|
||||
if(additionalRowNum%PAGE_ROW_MAX <= 13) {
|
||||
totalDueRow = sheet.getRow(PAGE_ROW_MAX + 2);
|
||||
if(((LAST_ROW+additionalRowNum - 44) % 63) < 13 && ((LAST_ROW+additionalRowNum - 44) % 63) > 0) {
|
||||
totalDueRow = sheet.getRow( data.size() < 44 ? PAGE_ROW_MAX + 1 : TOTAL_ROW + 1);
|
||||
Cell totalDueCell = totalDueRow.createCell(7);
|
||||
totalDueCell.setCellFormula("H" + (PAGE_ROW_MAX - 2) + "-G" + (PAGE_ROW_MAX - 2));
|
||||
totalDueCell.setCellFormula("H" + (TOTAL_ROW) + "-G" + (TOTAL_ROW));
|
||||
totalDueCell.setCellStyle(totalDueCellStyle);
|
||||
}
|
||||
else {
|
||||
totalDueRow = sheet.getRow(TOTAL_ROW + 2);
|
||||
totalDueRow = sheet.getRow(data.size() < 44 ? TOTAL_ROW + 1 : TOTAL_ROW + 2);
|
||||
Cell totalDueCell = totalDueRow.createCell(7);
|
||||
totalDueCell.setCellFormula("H" + (TOTAL_ROW+1) + "-G" + (TOTAL_ROW+1));
|
||||
totalDueCell.setCellFormula("H" + (data.size() < 44 ? TOTAL_ROW : TOTAL_ROW + 1) + "-G" + (data.size() < 44 ? TOTAL_ROW : TOTAL_ROW + 1));
|
||||
totalDueCell.setCellStyle(totalDueCellStyle);
|
||||
}
|
||||
}
|
||||
|
@ -315,13 +339,13 @@ public abstract class AbstractInvoice<E, F, G, H, I> {
|
|||
if (targetClient.getCurrency().equals("USD")) {
|
||||
org.apache.poi.ss.usermodel.Row dollarRow;
|
||||
String formula;
|
||||
if (additionalRowNum % PAGE_ROW_MAX <= 13) {
|
||||
dollarRow = sheet.getRow(TOTAL_ROW + 5);
|
||||
formula = "H"+ (TOTAL_ROW+5) +" *" + exchangeRate;
|
||||
if ((((LAST_ROW + additionalRowNum - 44) % 63) < 13) && ((LAST_ROW + additionalRowNum - 44) % 63) > 0) {
|
||||
dollarRow = sheet.getRow(TOTAL_ROW + 2);
|
||||
formula = "H"+ (TOTAL_ROW + 2) +" *" + exchangeRate;
|
||||
}
|
||||
else {
|
||||
dollarRow = sheet.getRow(TOTAL_ROW + 3);
|
||||
formula = "H" + (TOTAL_ROW + 3) + " *" + exchangeRate;
|
||||
dollarRow = sheet.getRow(data.size() >= 44 ? TOTAL_ROW + 3 : TOTAL_ROW + 2);
|
||||
formula = "H" + (data.size() >= 44 ? TOTAL_ROW + 3 : TOTAL_ROW + 2) + " *" + exchangeRate;
|
||||
}
|
||||
Cell dollarCell = dollarRow.createCell(7); // column H
|
||||
CellStyle cellStyle = factory.getWorkbook().createCellStyle();
|
||||
|
|
|
@ -35,14 +35,16 @@ public class AddPortraitTubeJob implements Job {
|
|||
|
||||
private static final List<String> DEFAULT_SHOPS = Arrays.asList("JCH3", "JCH4", "JCH5");
|
||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||
private static final String TUBE_30_SKU_SINGLE_DOUBLE = "PJ95310032-WIA";
|
||||
private static final String TUBE_40_SKU_SINGLE = "PJ95430032-WIA";
|
||||
private static final String TUBE_40_SKU_MULTIPLE = "PJ95430040-WIA";
|
||||
private static final String TUBE_50_SKU_SINGLE = "PJ95530032-WIA";
|
||||
private static final String TUBE_50_SKU_MULTIPLE = "PJ95530040-WIA";
|
||||
private static final List<String> TUBE_SKUS = Arrays.asList(TUBE_50_SKU_MULTIPLE, TUBE_50_SKU_SINGLE,
|
||||
TUBE_40_SKU_MULTIPLE, TUBE_40_SKU_SINGLE);
|
||||
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);
|
||||
private static final String PREFIX_50_CANVAS = "JJ2501";
|
||||
private static final String PREFIX_40_CANVAS = "JJ2500";
|
||||
private static final String PREFIX_30_CANVAS = "JJ2502";
|
||||
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
@ -153,6 +155,7 @@ public class AddPortraitTubeJob implements Job {
|
|||
* @return Set of pairs
|
||||
*/
|
||||
private Pair<HashSet<Pair<String, Integer>>, HashSet<Pair<String, Integer>>> findCurrentAndAdequateTubes(List<OrderItem> orderItems) {
|
||||
int canvas30Count = 0;
|
||||
int canvas40Count = 0;
|
||||
int canvas50Count = 0;
|
||||
HashSet<Pair<String, Integer>> currentTubes = new HashSet<>();
|
||||
|
@ -166,31 +169,75 @@ public class AddPortraitTubeJob implements Job {
|
|||
canvas50Count += quantity;
|
||||
} else if (sku.startsWith(PREFIX_40_CANVAS)) {
|
||||
canvas40Count += quantity;
|
||||
} else if (sku.startsWith(PREFIX_30_CANVAS)) {
|
||||
canvas30Count += quantity;
|
||||
}
|
||||
}
|
||||
|
||||
int canvas30RemainderCount = canvas30Count % MAXIMUM_CANVAS_IN_TUBE.intValue();
|
||||
int canvas40RemainderCount = canvas40Count % MAXIMUM_CANVAS_IN_TUBE.intValue();
|
||||
int canvas50RemainderCount = canvas50Count % MAXIMUM_CANVAS_IN_TUBE.intValue();
|
||||
int totalRemainderCount = canvas30RemainderCount + canvas40RemainderCount + canvas50RemainderCount;
|
||||
int tube50SingleCount = 0;
|
||||
int tube50MultipleCount = (int) Math.floor(canvas50Count / MAXIMUM_CANVAS_IN_TUBE);
|
||||
int tube40SingleCount = 0;
|
||||
int tube40MultipleCount = (int) Math.floor(canvas40Count / MAXIMUM_CANVAS_IN_TUBE);
|
||||
if (canvas50RemainderCount > 0 && canvas40RemainderCount > 0) {
|
||||
tube50MultipleCount++;
|
||||
if (canvas50RemainderCount == 2 && canvas40RemainderCount == 2) {
|
||||
tube50SingleCount++;
|
||||
int tube30SingleDoubleCount = 0;
|
||||
// 3 canvas of 30cm also go into 40 multiple tubes
|
||||
tube40MultipleCount += (int) Math.floor(canvas30Count / MAXIMUM_CANVAS_IN_TUBE);
|
||||
// When remaining 1 to 3 canvases
|
||||
if (totalRemainderCount > 0 && totalRemainderCount < 4) {
|
||||
if (canvas50RemainderCount > 0) {
|
||||
// It only takes one 50cm canvas with any other canvas to impose the use of 50cm multiple tube
|
||||
if (totalRemainderCount > 1) {
|
||||
tube50MultipleCount++;
|
||||
} else {
|
||||
tube50SingleCount++;
|
||||
}
|
||||
} else {
|
||||
if (totalRemainderCount > 1) {
|
||||
if (canvas40RemainderCount > 0) {
|
||||
tube40MultipleCount++;
|
||||
} else {
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
} else {
|
||||
if (canvas40RemainderCount > 0) {
|
||||
tube40SingleCount++;
|
||||
} else if (canvas30RemainderCount > 0){
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (canvas40RemainderCount == 1) {
|
||||
tube40SingleCount++;
|
||||
} else if (canvas40RemainderCount == 2) {
|
||||
tube40MultipleCount++;
|
||||
} else if (canvas50RemainderCount == 1) {
|
||||
tube50SingleCount++;
|
||||
} else if (canvas50RemainderCount == 2) {
|
||||
} else if (totalRemainderCount >= 4) {
|
||||
// When remaining 4 to 6 canvases, one 50cm canvas imposes one 50cm multiple tube
|
||||
if (canvas50RemainderCount > 0) {
|
||||
tube50MultipleCount++;
|
||||
if (canvas50RemainderCount > 1) {
|
||||
// If we have two 50cm canvases and a total of 5 of 6 canvases
|
||||
if (totalRemainderCount > 4) {
|
||||
if (canvas40RemainderCount > 1) {
|
||||
tube40MultipleCount++;
|
||||
} else {
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
} else {
|
||||
if (canvas40RemainderCount > 1) {
|
||||
tube40SingleCount++;
|
||||
} else {
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
} else {
|
||||
// No 50cm canvases means only one combination possible: two 40cm canvases and two 30cm canvases
|
||||
tube40MultipleCount++;
|
||||
tube30SingleDoubleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tube50SingleCount > 0) {
|
||||
adequateTubes.add(Pair.of(TUBE_50_SKU_SINGLE, tube50SingleCount));
|
||||
}
|
||||
|
@ -203,6 +250,9 @@ public class AddPortraitTubeJob implements Job {
|
|||
if (tube40MultipleCount > 0) {
|
||||
adequateTubes.add(Pair.of(TUBE_40_SKU_MULTIPLE, tube40MultipleCount));
|
||||
}
|
||||
if (tube30SingleDoubleCount > 0) {
|
||||
adequateTubes.add(Pair.of(TUBE_30_SKU_SINGLE_DOUBLE, tube30SingleDoubleCount));
|
||||
}
|
||||
return Pair.of(currentTubes, adequateTubes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ 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;
|
||||
|
||||
@Slf4j
|
||||
|
@ -31,6 +32,7 @@ public class ArchiveOrderJob implements Job {
|
|||
|
||||
private static final List<String> DEFAULT_EXCLUDED_SHOPS = Arrays.asList("JCH3", "JCH4", "JCH5", "FB2");
|
||||
private static final Integer DEFAULT_NUMBER_OF_THREADS = 10;
|
||||
private static final Integer MABANG_API_RATE_LIMIT_PER_MINUTE = 300;
|
||||
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
@ -76,7 +78,8 @@ public class ArchiveOrderJob implements Job {
|
|||
|
||||
List<String> platformOrderIds = platformOrderService.fetchInvoicedShippedOrdersNotInShops(startDateTime, endDateTime, shops, excludedTrackingNumbersRegex);
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(DEFAULT_NUMBER_OF_THREADS);
|
||||
ExecutorService throttlingExecutorService = ThrottlingExecutorService.createExecutorService(DEFAULT_NUMBER_OF_THREADS,
|
||||
MABANG_API_RATE_LIMIT_PER_MINUTE, TimeUnit.MINUTES);
|
||||
|
||||
log.info("Constructing order archiving requests");
|
||||
List<ArchiveOrderRequestBody> archiveOrderRequestBodies = new ArrayList<>();
|
||||
|
@ -95,7 +98,7 @@ public class ArchiveOrderJob implements Job {
|
|||
log.error("Error communicating with MabangAPI", e);
|
||||
}
|
||||
return success;
|
||||
}, executor))
|
||||
}, throttlingExecutorService))
|
||||
.collect(Collectors.toList());
|
||||
List<Boolean> results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
|
||||
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||
|
|
|
@ -44,8 +44,8 @@ public class CWJob implements Job {
|
|||
private static final Integer DEFAULT_MAXIMUM_NUMBER_OF_PARCELS_PER_TRANSACTION = 800;
|
||||
private static final List<String> DEFAULT_TRANSPORTERS = Arrays.asList("诚稳法邮普货" , "诚稳法邮膏体");
|
||||
|
||||
private final static String APP_TOKEN = "yhidp1u86us23hx79xx2t8b7h47wa9usv";
|
||||
private final static String APP_KEY = "ymk3899nifsjw4d9igkyk58sfl6u48869ahr749axjtnq2kn07n4y6izn1ywcptkh";
|
||||
private final static String APP_TOKEN = "y7j1p5o4obncsdhbk1zgasunb2erpyzvh";
|
||||
private final static String APP_KEY = "ym27kj0wy5wgx69f58pgd7crm60w07p0l15flj1bacrf5n0e38vqjtrjkkvosd61p";
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
|
|
|
@ -35,6 +35,8 @@ public class HLJob implements Job {
|
|||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
||||
private final static String URL = "http://www.antugj.com:8082/selectTrack.htm";
|
||||
|
||||
private static final Integer DEFAULT_NUMBER_OF_DAYS = 15;
|
||||
private static final Integer DEFAULT_MAXIMUM_NUMBER_OF_PARCELS_PER_TRANSACTION = 800;
|
||||
private static final List<String> DEFAULT_TRANSPORTERS = Arrays.asList("法国专线普货");
|
||||
|
@ -91,7 +93,7 @@ public class HLJob implements Job {
|
|||
List<HLResponseItem> parcelTraces = new ArrayList<>();
|
||||
List<HLRequest> hlRequests = new ArrayList<>();
|
||||
billCodeLists.forEach(billcodeList -> {
|
||||
HLRequest hlRequest = new HLRequest(billcodeList);
|
||||
HLRequest hlRequest = new HLRequest(URL, billcodeList);
|
||||
hlRequests.add(hlRequest);
|
||||
});
|
||||
List<Boolean> results = new ArrayList<>();
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* Extends {@code LinkedHashMap} into a fixed sized ordered cache
|
||||
* for allocating and tracking limited resources in <em>intervals</em>. It also
|
||||
* tracks the allocation rate as a moving average.
|
||||
* <p>
|
||||
* The {@code IntervalWindow} is created with a cache that consists of the
|
||||
* present <em>interval</em> and at least one past <em>interval</em>.
|
||||
* As the number of cached <em>intervals</em> exceed the windows size, they are
|
||||
* removed and the moving average updated. The allocation method is
|
||||
* thread-safe to ensure over allocation is avoided.
|
||||
*
|
||||
* @author martinb
|
||||
* @since 2015-08-17
|
||||
*/
|
||||
public class IntervalWindow extends LinkedHashMap<Long, Integer>
|
||||
{
|
||||
/**
|
||||
* Serial
|
||||
*/
|
||||
private static final long serialVersionUID = 201508171315L;
|
||||
|
||||
/**
|
||||
* The upper execution limit per interval
|
||||
*/
|
||||
private final int INTERVAL_LIMIT;
|
||||
|
||||
/**
|
||||
* Number of intervals to track
|
||||
*/
|
||||
private final int INTERVAL_WINDOW_SIZE;
|
||||
|
||||
/**
|
||||
* The current interval being filled.
|
||||
*/
|
||||
private long currentInterval = 0;
|
||||
|
||||
/**
|
||||
* The moving total of slots used in the window
|
||||
*/
|
||||
private int slotsUsedInWindow = 0;
|
||||
|
||||
/**
|
||||
* The minimum interval index that can be considered.
|
||||
*/
|
||||
private long minimumInterval = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value in the map, or a default.
|
||||
* Implemented in JSE8
|
||||
*
|
||||
* @param key
|
||||
* @param defaultValue
|
||||
* @return the value
|
||||
*/
|
||||
private final int getOrDefault(Long key,
|
||||
Integer defaultValue)
|
||||
{
|
||||
if (get(key) != null)
|
||||
{
|
||||
return get(key);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decreases the running total by the number of slots used in the
|
||||
* interval leaving the moving window.
|
||||
* <p>
|
||||
* The value in map is the number of free slots left in the interval.
|
||||
*
|
||||
* @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
|
||||
*/
|
||||
protected boolean removeEldestEntry(Map.Entry<Long, Integer> eldest)
|
||||
{
|
||||
if (INTERVAL_WINDOW_SIZE < size())
|
||||
{
|
||||
slotsUsedInWindow -= (INTERVAL_LIMIT - eldest.getValue());
|
||||
minimumInterval = eldest.getKey();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to allocate a slot in the given interval within the rate limit.
|
||||
*
|
||||
* @param interval
|
||||
* the interval
|
||||
* @return true is a slot was allocated in the interval
|
||||
*/
|
||||
public boolean allocateSlot(long interval)
|
||||
{
|
||||
boolean isSlotAllocated = false;
|
||||
int freeSlots = 0; // Free slots in the interval
|
||||
|
||||
if (interval > minimumInterval)
|
||||
/*
|
||||
* Cheap range check is OK
|
||||
*/
|
||||
{
|
||||
synchronized (this)
|
||||
/*
|
||||
* Synchronize allocate on this object to ensure that cache is consistent
|
||||
*/
|
||||
{
|
||||
if ((freeSlots = getOrDefault(interval,
|
||||
INTERVAL_LIMIT)) > 0)
|
||||
/*
|
||||
* There are free slots in this interval to execute this thread
|
||||
* Break out of the loop and return.
|
||||
*/
|
||||
{
|
||||
if (currentInterval > 0 && currentInterval != interval)
|
||||
/*
|
||||
* Update the running total of slots used in window
|
||||
* with past values only once past the first interval.
|
||||
*/
|
||||
{
|
||||
slotsUsedInWindow +=
|
||||
INTERVAL_LIMIT
|
||||
- getOrDefault(currentInterval,
|
||||
0);
|
||||
}
|
||||
|
||||
put(currentInterval = interval, freeSlots - 1); // Maximum is RATE_LIMIT - 1
|
||||
isSlotAllocated = true;
|
||||
} // if
|
||||
} // synchronized
|
||||
} // if
|
||||
|
||||
return isSlotAllocated;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the moving average number of slots allocated for work during
|
||||
* the present window but excluding the currently filling interval
|
||||
*
|
||||
* @return the average number of slots used
|
||||
*/
|
||||
public float getAverageSlotUsed()
|
||||
{
|
||||
return slotsUsedInWindow / (INTERVAL_WINDOW_SIZE - 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check window size parameters for range.
|
||||
*
|
||||
* @param intervalWindowSize
|
||||
* the proposed window size
|
||||
* @return the window size
|
||||
*/
|
||||
private static int checkWindowSize(int intervalWindowSize)
|
||||
{
|
||||
if (intervalWindowSize < 2)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Interval Window Size cannot be smaller than 2");
|
||||
}
|
||||
return intervalWindowSize;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@code IntervalWindow} of a window size of two that limits the
|
||||
* number of successful allocations in each interval.
|
||||
*
|
||||
* @param intervalLimit
|
||||
* the maximum number of allocations per interval.
|
||||
*/
|
||||
public IntervalWindow(int intervalLimit)
|
||||
{
|
||||
super(2, 1);
|
||||
INTERVAL_WINDOW_SIZE = 2;
|
||||
INTERVAL_LIMIT = intervalLimit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@code IntervalWindow} of a given window size that limits the
|
||||
* number of successful allocations in each interval.
|
||||
*
|
||||
* @param intervalLimit
|
||||
* the maximum number of allocations per interval.
|
||||
* @param intervalWindow
|
||||
* the number if intervals to track, must be at least two
|
||||
*/
|
||||
public IntervalWindow(int intervalLimit, int intervalWindow)
|
||||
{
|
||||
super(checkWindowSize(intervalWindow),
|
||||
1);
|
||||
INTERVAL_WINDOW_SIZE = intervalWindow;
|
||||
INTERVAL_LIMIT = intervalLimit;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import static java.lang.Integer.MAX_VALUE;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A {@code ExecutorService} that throttles the amount of work released
|
||||
* for execution per time period.
|
||||
*
|
||||
* @author martinb
|
||||
* @since 2015-08-17
|
||||
*/
|
||||
public class ThrottlingExecutorService extends ThreadPoolExecutor {
|
||||
/**
|
||||
* The interval window cache
|
||||
*/
|
||||
private final IntervalWindow INTERVAL_WINDOW;
|
||||
|
||||
/**
|
||||
* The rate limit interval in milliseconds
|
||||
*/
|
||||
private final long RATE_INTERVAL_MILLISECONDS;
|
||||
|
||||
|
||||
/**
|
||||
* Caching, dynamic rate limiting {@code ExecutorService}
|
||||
*
|
||||
* @param rateLimit
|
||||
* the rate limit
|
||||
* @param unit
|
||||
* the rate limit time unit
|
||||
*/
|
||||
private ThrottlingExecutorService(
|
||||
int rateLimit,
|
||||
TimeUnit unit)
|
||||
{
|
||||
/*
|
||||
* Create a CACHING ExecutorService
|
||||
*/
|
||||
super(0, MAX_VALUE,
|
||||
60L, SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
INTERVAL_WINDOW = new IntervalWindow(rateLimit);
|
||||
RATE_INTERVAL_MILLISECONDS = unit.toMillis(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixed size rate limiting {@code ExecutorService}
|
||||
*
|
||||
* @param parallelism
|
||||
* @param rateLimit
|
||||
* @param unit
|
||||
*/
|
||||
private ThrottlingExecutorService(int parallelism,
|
||||
int rateLimit,
|
||||
TimeUnit unit)
|
||||
{
|
||||
/*
|
||||
* Create a FIXED ExecutorService
|
||||
*/
|
||||
super(parallelism, parallelism, 0, MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>());
|
||||
|
||||
INTERVAL_WINDOW = new IntervalWindow(rateLimit);
|
||||
RATE_INTERVAL_MILLISECONDS = unit.toMillis(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produces a throttling ExecutorService
|
||||
* <p>
|
||||
* Evaluates the parameters and generates an appropriate ExecutorService
|
||||
*
|
||||
* @param parallelism
|
||||
* how many threads
|
||||
* @param rateLimit
|
||||
* work per time unit
|
||||
* @param unit
|
||||
* the time unit
|
||||
* @return the ExecutorService
|
||||
*/
|
||||
public static ExecutorService createExecutorService(int parallelism,
|
||||
int rateLimit,
|
||||
TimeUnit unit)
|
||||
{
|
||||
if (parallelism > 0)
|
||||
/*
|
||||
* Fixed ExecutorService
|
||||
*/
|
||||
{
|
||||
return new ThrottlingExecutorService(parallelism,
|
||||
rateLimit > 0 ? rateLimit : MAX_VALUE,
|
||||
unit);
|
||||
}
|
||||
else
|
||||
/*
|
||||
* Caching ExecutorService
|
||||
*/
|
||||
{
|
||||
return new ThrottlingExecutorService(
|
||||
rateLimit > 0 ? rateLimit : MAX_VALUE,
|
||||
unit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throttles the execution before executing the task to achieve the desired
|
||||
* rate.
|
||||
*
|
||||
* @see java.util.concurrent.ThreadPoolExecutor#execute(java.lang.Runnable)
|
||||
*/
|
||||
@Override
|
||||
public void execute(final Runnable task)
|
||||
{
|
||||
throttle();
|
||||
super.execute(task);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throttles if the thread can not be allocated in the current time
|
||||
* interval,
|
||||
* forcing it to wait to the next interval.
|
||||
*/
|
||||
private void throttle()
|
||||
{
|
||||
long interval = 0; // The interval index
|
||||
long milliTime = System.currentTimeMillis(); // The current time
|
||||
long offset = milliTime % RATE_INTERVAL_MILLISECONDS; // Interval offset
|
||||
|
||||
while (!INTERVAL_WINDOW.allocateSlot(
|
||||
(interval = (milliTime + offset) / RATE_INTERVAL_MILLISECONDS)))
|
||||
/*
|
||||
* Cannot allocate free slots in this interval.
|
||||
* Calculate the required pause to get to the next interval and sleep
|
||||
*/
|
||||
{
|
||||
int pause = (int) (((interval + 1)
|
||||
* RATE_INTERVAL_MILLISECONDS)
|
||||
- milliTime + offset);
|
||||
try
|
||||
/*
|
||||
* Try to sleep the thread for a pause of nanoseconds
|
||||
*/
|
||||
{
|
||||
Thread.sleep(pause);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
}
|
||||
|
||||
milliTime = System.currentTimeMillis();
|
||||
|
||||
} // while
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package org.jeecg.modules.business.domain.job;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
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.hualei.HLRequest;
|
||||
import org.jeecg.modules.business.domain.api.hualei.HLResponse;
|
||||
import org.jeecg.modules.business.domain.api.hualei.HLResponseItem;
|
||||
import org.jeecg.modules.business.service.IParcelService;
|
||||
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 java.io.IOException;
|
||||
import java.sql.Date;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class YPJob implements Job {
|
||||
|
||||
@Autowired
|
||||
private IParcelService parcelService;
|
||||
@Autowired
|
||||
private IPlatformOrderService platformOrderService;
|
||||
|
||||
private final static String URL = "http://www.yuanpeng56.com:8082/selectTrack.htm";
|
||||
|
||||
private static final Integer DEFAULT_NUMBER_OF_DAYS = 15;
|
||||
private static final Integer DEFAULT_MAXIMUM_NUMBER_OF_PARCELS_PER_TRANSACTION = 800;
|
||||
private static final List<String> DEFAULT_TRANSPORTERS = Arrays.asList("法国普货小包专线");
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
LocalDate startDate = endDate.minusDays(DEFAULT_NUMBER_OF_DAYS);
|
||||
List<String> transporters = DEFAULT_TRANSPORTERS;
|
||||
boolean overrideRestriction = false;
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
JobDataMap jobDataMap = context.getMergedJobDataMap();
|
||||
String parameter = ((String) jobDataMap.get("parameter"));
|
||||
if (parameter != null) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(parameter);
|
||||
if (!jsonObject.isNull("startDate")) {
|
||||
String startDateStr = jsonObject.getString("startDate");
|
||||
startDate = LocalDate.parse(startDateStr);
|
||||
}
|
||||
if (!jsonObject.isNull("endDate")) {
|
||||
String endDateStr = jsonObject.getString("endDate");
|
||||
endDate = LocalDate.parse(endDateStr);
|
||||
}
|
||||
if (!jsonObject.isNull("transporters")) {
|
||||
JSONArray transporterArray = jsonObject.getJSONArray("transporters");
|
||||
List<String> transporterList = new ArrayList<>();
|
||||
for (int i = 0; i < transporterArray.length(); i++) {
|
||||
transporterList.add(transporterArray.getString(i));
|
||||
}
|
||||
transporters = transporterList;
|
||||
}
|
||||
if (!jsonObject.isNull("override")) {
|
||||
overrideRestriction = jsonObject.getBoolean("override");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
log.error("Error while parsing parameter as JSON, falling back to default parameters.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!endDate.isAfter(startDate)) {
|
||||
throw new RuntimeException("EndDate must be strictly greater than StartDate !");
|
||||
} else if (endDate.minusDays(30).isAfter(startDate) && !overrideRestriction) {
|
||||
throw new RuntimeException("No more than 30 days can separate startDate and endDate !");
|
||||
}
|
||||
|
||||
log.info("Starting to retrieve parcel traces of {} from {} to {}", transporters, startDate, endDate);
|
||||
List<String> billCodes = platformOrderService.fetchBillCodesOfParcelsWithoutTrace(
|
||||
Date.valueOf(startDate), Date.valueOf(endDate), transporters);
|
||||
log.info("{} parcels without trace in total", billCodes.size());
|
||||
List<List<String>> billCodeLists = Lists.partition(billCodes, 40);
|
||||
log.info("Requests will be divided in to {} parts", billCodeLists.size());
|
||||
List<HLResponseItem> parcelTraces = new ArrayList<>();
|
||||
List<HLRequest> hlRequests = new ArrayList<>();
|
||||
billCodeLists.forEach(billcodeList -> {
|
||||
HLRequest hlRequest = new HLRequest(URL, billcodeList);
|
||||
hlRequests.add(hlRequest);
|
||||
});
|
||||
List<Boolean> results = new ArrayList<>();
|
||||
for (HLRequest request : hlRequests) {
|
||||
boolean success = false;
|
||||
HttpEntity entity = request.send().getEntity();
|
||||
try {
|
||||
// String of the response
|
||||
String responseString = EntityUtils.toString(entity, "UTF-8");
|
||||
// Geniuses at HL decided to put the sole response object in an array....
|
||||
HLResponse[] hlResponseArray = mapper.readValue(responseString, HLResponse[].class);
|
||||
parcelTraces.addAll(hlResponseArray[0].getResponseItems());
|
||||
success = true;
|
||||
} catch (IOException e) {
|
||||
log.error("Error while parsing response into String", e);
|
||||
} finally {
|
||||
results.add(success);
|
||||
}
|
||||
log.info("{} parcel added to the queue to be inserted into DB.", parcelTraces.size());
|
||||
}
|
||||
long nbSuccesses = results.stream().filter(b -> b).count();
|
||||
log.info("{}/{} lots of 40 parcel traces have been retrieved.", nbSuccesses, hlRequests.size());
|
||||
|
||||
List<List<HLResponseItem>> parcelTraceList = Lists.partition(parcelTraces, DEFAULT_MAXIMUM_NUMBER_OF_PARCELS_PER_TRANSACTION);
|
||||
for (List<HLResponseItem> parcelTracesPerTransaction : parcelTraceList) {
|
||||
parcelService.saveHLParcelAndTraces(parcelTracesPerTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -19,9 +19,9 @@ import java.util.Objects;
|
|||
* @Description: 平台订单内容
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2023-11-28
|
||||
* @Version: V1.4
|
||||
* @Version: V1.5
|
||||
*/
|
||||
@ApiModel(value = "platform_order对象", description = "平台订单表")
|
||||
@ApiModel(value = "platform_order_content对象", description = "平台订单内容")
|
||||
@Data
|
||||
@TableName("platform_order_content")
|
||||
public class PlatformOrderContent implements Serializable {
|
||||
|
@ -133,6 +133,13 @@ public class PlatformOrderContent implements Serializable {
|
|||
@Excel(name = "海外仓操作费", width = 15)
|
||||
@ApiModelProperty(value = "海外仓操作费")
|
||||
private java.math.BigDecimal pickingFee;
|
||||
/**
|
||||
* 商品多属性
|
||||
*/
|
||||
@Excel(name = "商品多属性", width = 15)
|
||||
@ApiModelProperty(value = "商品多属性")
|
||||
private java.lang.String customizationData;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -189,14 +189,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)
|
||||
sku_id, quantity, erp_status, product_available, customization_data)
|
||||
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.productAvailable}, #{item.specifics})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.jeecg.modules.business.service;
|
||||
|
||||
import com.aspose.cells.SaveFormat;
|
||||
import com.aspose.cells.Workbook;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
|
@ -34,7 +36,10 @@ import java.text.SimpleDateFormat;
|
|||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
|
@ -106,6 +111,11 @@ public class PlatformOrderShippingInvoiceService {
|
|||
|
||||
@Value("${jeecg.path.shippingInvoiceDetailDir}")
|
||||
private String INVOICE_DETAIL_DIR;
|
||||
@Value("${jeecg.path.shippingInvoicePdfDir}")
|
||||
private String INVOICE_PDF_DIR;
|
||||
@Value("${jeecg.path.shippingInvoiceDetailPdfDir}")
|
||||
private String INVOICE_DETAIL_PDF_DIR;
|
||||
private static final String EXTENSION = ".xlsx";
|
||||
|
||||
private final static String[] DETAILS_TITLES = {
|
||||
"Boutique",
|
||||
|
@ -563,6 +573,107 @@ public class PlatformOrderShippingInvoiceService {
|
|||
return invoiceList;
|
||||
}
|
||||
|
||||
|
||||
/** Finds the absolute path of invoice file by recursively walking the directory and it's subdirectories
|
||||
*
|
||||
* @param dirPath
|
||||
* @param invoiceNumber
|
||||
* @return List of paths for the file but should only find one result
|
||||
*/
|
||||
public List<Path> getPath(String dirPath, String invoiceNumber) {
|
||||
List<Path> pathList = new ArrayList<>();
|
||||
//Recursively list all files
|
||||
//The walk() method returns a Stream by walking the file tree beginning with a given starting file/directory in a depth-first manner.
|
||||
try (Stream<Path> stream = Files.walk(Paths.get(dirPath))) {
|
||||
pathList = stream.map(Path::normalize)
|
||||
.filter(Files::isRegularFile) // directories, hidden files and files without extension are not included
|
||||
.filter(path -> path.getFileName().toString().contains(invoiceNumber))
|
||||
.filter(path -> path.getFileName().toString().endsWith(EXTENSION))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return pathList;
|
||||
}
|
||||
|
||||
/** Finds the absolute path of invoice file by recursively walking the directory and it's subdirectories
|
||||
*
|
||||
* @param dirPath
|
||||
* @param invoiceNumber
|
||||
* @return List of paths for the file but should only find one result
|
||||
*/
|
||||
public List<Path> getPath(String dirPath, String invoiceNumber, String invoiceEntity) {
|
||||
List<Path> pathList = new ArrayList<>();
|
||||
//Recursively list all files
|
||||
//The walk() method returns a Stream by walking the file tree beginning with a given starting file/directory in a depth-first manner.
|
||||
try (Stream<Path> stream = Files.walk(Paths.get(dirPath))) {
|
||||
pathList = stream.map(Path::normalize)
|
||||
.filter(Files::isRegularFile) // directories, hidden files and files without extension are not included
|
||||
.filter(path -> path.getFileName().toString().contains(invoiceNumber))
|
||||
.filter(path -> path.getFileName().toString().contains(invoiceEntity))
|
||||
.filter(path -> path.getFileName().toString().endsWith(EXTENSION))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return pathList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the absolute path of invoice file and return the path
|
||||
* @param invoiceNumber
|
||||
* @param filetype if it's an invoice or invoice detail
|
||||
* @return String returns the path of the invoice file
|
||||
*/
|
||||
public String getInvoiceList(String invoiceNumber, String filetype) {
|
||||
log.info("Invoice number : " + invoiceNumber);
|
||||
List<Path> pathList = new ArrayList<>();
|
||||
if(filetype.equals("invoice")) {
|
||||
log.info("File asked is of type invoice");
|
||||
pathList = getPath(INVOICE_DIR, invoiceNumber);
|
||||
}
|
||||
if(filetype.equals("detail")) {
|
||||
log.info("File asked is of type invoice detail");
|
||||
pathList = getPath(INVOICE_DETAIL_DIR, invoiceNumber);
|
||||
}
|
||||
if(pathList.isEmpty()) {
|
||||
log.error("NO INVOICE FILE FOUND : " + invoiceNumber);
|
||||
return "ERROR";
|
||||
}
|
||||
else {
|
||||
for (Path path : pathList) {
|
||||
log.info(path.toString());
|
||||
}
|
||||
return pathList.get(0).toString();
|
||||
}
|
||||
}
|
||||
public String convertToPdf(String invoiceNumber, String fileType) throws Exception {
|
||||
String excelFilePath = getInvoiceList(invoiceNumber, fileType);// (C:\PATH\filename.xlsx)
|
||||
|
||||
if(!excelFilePath.equals("ERROR")) {
|
||||
String pdfFilePath= INVOICE_PDF_DIR + "/" + invoiceNumber + ".pdf";
|
||||
if(fileType.equals("invoice")){
|
||||
pdfFilePath = INVOICE_PDF_DIR + "/Invoice N°" + invoiceNumber + ".pdf";
|
||||
}
|
||||
if(fileType.equals("detail")) {
|
||||
pdfFilePath = INVOICE_DETAIL_PDF_DIR + "/Détail_calcul_de_facture_" + invoiceNumber + ".pdf";
|
||||
}
|
||||
|
||||
Pattern p = Pattern.compile("^(.*)[\\/\\\\](.*)(\\.[a-z]+)"); //group(1): "C:\PATH" , group(2) : "filename", group(3): ".xlsx"
|
||||
Matcher m = p.matcher(excelFilePath);
|
||||
if (m.matches()) {
|
||||
pdfFilePath = INVOICE_PDF_DIR + "/" + m.group(2) + ".pdf";
|
||||
}
|
||||
// Créé un classeur pour charger le fichier Excel
|
||||
Workbook workbook = new Workbook(excelFilePath);
|
||||
// On enregistre le document au format PDF
|
||||
workbook.save(pdfFilePath, SaveFormat.PDF);
|
||||
return pdfFilePath;
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
@Transactional
|
||||
public String zipInvoices(List<String> invoiceList) throws IOException {
|
||||
log.info("Zipping Invoices ...");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.jeecg.modules.business.service.impl;
|
||||
|
||||
import freemarker.cache.ClassTemplateLoader;
|
||||
import freemarker.cache.FileTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
|
@ -118,11 +119,9 @@ public class EmailServiceImpl implements EmailService {
|
|||
}
|
||||
@Override
|
||||
@Transactional
|
||||
public FreeMarkerConfigurer freemarkerClassLoaderConfig() throws IOException {
|
||||
public FreeMarkerConfigurer freemarkerClassLoaderConfig() {
|
||||
Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
|
||||
TemplateLoader templateLoader = new FileTemplateLoader(new File(env.getProperty("jeecg.path.emailTemplateDir"))) {
|
||||
};
|
||||
configuration.setTemplateLoader(templateLoader);
|
||||
configuration.setTemplateLoader(new ClassTemplateLoader(getClass(), "/templates"));
|
||||
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
|
||||
freeMarkerConfigurer.setConfiguration(configuration);
|
||||
return freeMarkerConfigurer;
|
||||
|
|
Loading…
Reference in New Issue