From d51549c2dba215c002d2e00f8e80bc42c2d9448c Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Mon, 21 Aug 2023 11:14:57 +0200 Subject: [PATCH 01/23] Minor code optimization --- .../business/domain/api/mabang/getorderlist/Order.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/Order.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/Order.java index a1403011d..d87a18e20 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/Order.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/Order.java @@ -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; From 049d48f928485a19439d90dd4ab9a560f05b9e3f Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Mon, 21 Aug 2023 14:36:35 +0200 Subject: [PATCH 02/23] Retrieve specifics aka customization data of PlatformOrderContent --- .../business/domain/api/mabang/getorderlist/OrderItem.java | 3 +++ .../modules/business/mapper/xml/PlatformOrderMabangMapper.xml | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/OrderItem.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/OrderItem.java index 4f84ccfce..771fc776c 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/OrderItem.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/mabang/getorderlist/OrderItem.java @@ -34,6 +34,9 @@ public class OrderItem { private String productAvailable; + @JSONField(name = "specifics") + private String specifics; + /** * Status : * 2 = Normal diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/mapper/xml/PlatformOrderMabangMapper.xml b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/mapper/xml/PlatformOrderMabangMapper.xml index 684b8c313..351832dff 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/mapper/xml/PlatformOrderMabangMapper.xml +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/mapper/xml/PlatformOrderMabangMapper.xml @@ -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 ( UUID(), 'Mabang API', NOW(), 'Mabang API', NOW(), #{item.platformOrderId}, skuErpToId(#{item.erpCode}), #{item.quantity}, #{item.erpStatus}, - #{item.productAvailable}) + #{item.productAvailable}, #{item.specifics}) From 997b87a085e4966fcabd0846454a254f1267e185 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Mon, 21 Aug 2023 15:05:20 +0200 Subject: [PATCH 03/23] Update PlatformOrderContent model --- .../business/entity/PlatformOrderContent.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/entity/PlatformOrderContent.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/entity/PlatformOrderContent.java index 56b5eac0c..95664f805 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/entity/PlatformOrderContent.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/entity/PlatformOrderContent.java @@ -18,10 +18,10 @@ import java.util.Objects; /** * @Description: 平台订单内容 * @Author: jeecg-boot - * @Date: 2023-02-09 - * @Version: V1.3 + * @Date: 2023-08-18 + * @Version: V1.4 */ -@ApiModel(value = "platform_order对象", description = "平台订单表") +@ApiModel(value = "platform_order_content对象", description = "平台订单内容") @Data @TableName("platform_order_content") public class PlatformOrderContent implements Serializable { @@ -127,6 +127,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; From 9d2697affa21711eebf105e1e5414f825e5ef678 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Fri, 25 Aug 2023 11:33:12 +0200 Subject: [PATCH 04/23] Use classpath loader for FreeMarker template --- .../modules/business/service/impl/EmailServiceImpl.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/impl/EmailServiceImpl.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/impl/EmailServiceImpl.java index e88236497..80ed19448 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/impl/EmailServiceImpl.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/impl/EmailServiceImpl.java @@ -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; @@ -66,11 +67,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; From cf2b51c0f7ddaea788016e378f4c20cb54fe1100 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Wed, 13 Sep 2023 16:21:26 +0200 Subject: [PATCH 05/23] Add tube rule for 30X40 portraits --- .../jeecg/modules/business/domain/job/AddPortraitTubeJob.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java index 725b28d3e..37553a4ac 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java @@ -43,6 +43,7 @@ public class AddPortraitTubeJob implements Job { 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; @@ -166,6 +167,8 @@ public class AddPortraitTubeJob implements Job { canvas50Count += quantity; } else if (sku.startsWith(PREFIX_40_CANVAS)) { canvas40Count += quantity; + } else if (sku.startsWith(PREFIX_30_CANVAS)) { + canvas40Count += quantity; } } From c21a8b50fe2de5e793731935ec593e224d3414d6 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Wed, 13 Sep 2023 16:40:40 +0200 Subject: [PATCH 06/23] Update project version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9bfd92c41..5bac8b14e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.6.0 + 1.6.1 pom WIA APP ${project.version} From bcf57915cc76f6c79e73968367acdb40d598d31a Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Thu, 14 Sep 2023 16:12:40 +0200 Subject: [PATCH 07/23] Add thread executor service with throttling --- .../business/domain/job/IntervalWindow.java | 205 ++++++++++++++++++ .../domain/job/ThrottlingExecutorService.java | 166 ++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/IntervalWindow.java create mode 100644 jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ThrottlingExecutorService.java diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/IntervalWindow.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/IntervalWindow.java new file mode 100644 index 000000000..778e87ea7 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/IntervalWindow.java @@ -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 intervals. It also + * tracks the allocation rate as a moving average. + *

+ * The {@code IntervalWindow} is created with a cache that consists of the + * present interval and at least one past interval. + * As the number of cached intervals 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 +{ + /** + * 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. + *

+ * 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 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; + } + +} \ No newline at end of file diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ThrottlingExecutorService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ThrottlingExecutorService.java new file mode 100644 index 000000000..b30e37511 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ThrottlingExecutorService.java @@ -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()); + + 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()); + + INTERVAL_WINDOW = new IntervalWindow(rateLimit); + RATE_INTERVAL_MILLISECONDS = unit.toMillis(1); + } + + + /** + * Produces a throttling ExecutorService + *

+ * 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 + } +} From a0db23999404bbbde2139f820f3da56b77e876d4 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Thu, 14 Sep 2023 16:13:48 +0200 Subject: [PATCH 08/23] Use ThrottlingExecutorService in ArchiveOrderJob as MabangAPI has a 300 requests per minute limit --- .../jeecg/modules/business/domain/job/ArchiveOrderJob.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ArchiveOrderJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ArchiveOrderJob.java index 982b54fe3..89e9485b8 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ArchiveOrderJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/ArchiveOrderJob.java @@ -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 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 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 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 results = changeOrderFutures.stream().map(CompletableFuture::join).collect(Collectors.toList()); long nbSuccesses = results.stream().filter(b -> b).count(); From 0561f8a87059d7040be46f03c71f2c6b10d1c5d2 Mon Sep 17 00:00:00 2001 From: Gauthier LO Date: Fri, 15 Sep 2023 11:02:50 +0200 Subject: [PATCH 09/23] fix: Excel display bug in invoices --- .../domain/invoice/AbstractInvoice.java | 79 +++++++++++++------ .../domain/invoice/InvoiceStyleFactory.java | 2 +- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java index ffb1540e4..091904698 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java @@ -156,28 +156,53 @@ public abstract class AbstractInvoice { // 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; + System.out.println("initial TOTAL_ROW : " + TOTAL_ROW); 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(); + int fileLastRow = sheet.getLastRowNum(); //62 // 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); + System.out.println("additionalRowNum : " + additionalRowNum); + System.out.println("PAGE_ROW_MAX : " + PAGE_ROW_MAX); + System.out.println("additionalRowNum%PAGE_ROW_MAX : " + additionalRowNum%PAGE_ROW_MAX); + + // 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 +217,7 @@ public abstract class AbstractInvoice { 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 +234,14 @@ public abstract class AbstractInvoice { 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 +326,16 @@ public abstract class AbstractInvoice { 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 +343,14 @@ public abstract class AbstractInvoice { 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; + System.out.println(((LAST_ROW + additionalRowNum - 44) % 63)); + 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(); diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/InvoiceStyleFactory.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/InvoiceStyleFactory.java index cdb61645d..64f07119a 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/InvoiceStyleFactory.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/InvoiceStyleFactory.java @@ -92,7 +92,7 @@ public class InvoiceStyleFactory { rightSideDecimalStyle.setVerticalAlignment(VerticalAlignment.CENTER); // decimal // DataFormat format =workbook.createDataFormat(); - leftSideStyle.setDataFormat(creationHelper.createDataFormat().getFormat("#,##0.00")); + rightSideDecimalStyle.setDataFormat(creationHelper.createDataFormat().getFormat("#,##0.00")); // font Font font = workbook.createFont(); font.setFontName("Arial"); From abada459bb59c8e281a18c277c24ad62c010c722 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Fri, 15 Sep 2023 11:20:28 +0200 Subject: [PATCH 10/23] Update AbstractInvoice.java Delete system.out.println --- .../modules/business/domain/invoice/AbstractInvoice.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java index 091904698..da1086e6a 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java @@ -158,7 +158,6 @@ public abstract class AbstractInvoice { int dataRowNumber = LAST_ROW - FIRST_ROW; int additionalRowNum = Math.max(data.size() - dataRowNumber - 1, 0); TOTAL_ROW = LAST_ROW + additionalRowNum; - System.out.println("initial TOTAL_ROW : " + TOTAL_ROW); Sheet sheet = factory.getWorkbook().getSheetAt(0); org.apache.poi.ss.usermodel.Row sourceRow = sheet.getRow(FIRST_ROW); int footerRow = TOTAL_ROW + 1; // la ligne à laquelle le footer commence (1 ligne avant le total) @@ -169,10 +168,7 @@ public abstract class AbstractInvoice { int fileLastRow = sheet.getLastRowNum(); //62 // 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 - System.out.println("additionalRowNum : " + additionalRowNum); - System.out.println("PAGE_ROW_MAX : " + PAGE_ROW_MAX); - System.out.println("additionalRowNum%PAGE_ROW_MAX : " + additionalRowNum%PAGE_ROW_MAX); - + // 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 @@ -343,7 +339,6 @@ public abstract class AbstractInvoice { if (targetClient.getCurrency().equals("USD")) { org.apache.poi.ss.usermodel.Row dollarRow; String formula; - System.out.println(((LAST_ROW + additionalRowNum - 44) % 63)); 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; From 8228862f3996373a6362f01964e38d2216a0d8e1 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Fri, 15 Sep 2023 11:21:37 +0200 Subject: [PATCH 11/23] Update AbstractInvoice.java Cleanup comments --- .../jeecg/modules/business/domain/invoice/AbstractInvoice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java index da1086e6a..6b4be84f5 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/invoice/AbstractInvoice.java @@ -165,7 +165,7 @@ public abstract class AbstractInvoice { if(data.size() > dataRowNumber + 1) { int startRow = LAST_ROW+1; - int fileLastRow = sheet.getLastRowNum(); //62 + 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 From c42015520cef2fc3ef6e0abc073dd7f4f095ff38 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Fri, 15 Sep 2023 11:39:10 +0200 Subject: [PATCH 12/23] Update project version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5bac8b14e..8f68c629e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.6.1 + 1.6.2 pom WIA APP ${project.version} From a02f3dbdb4ee0c71838d32408aef89769780e537 Mon Sep 17 00:00:00 2001 From: Gauthier LO Date: Tue, 10 Oct 2023 12:00:55 +0200 Subject: [PATCH 13/23] feature : generation of pdf for invoice during breakdown invoicing --- .../shippingInvoice/InvoiceController.java | 3 + .../ShippingInvoiceController.java | 129 ++---------------- .../PlatformOrderShippingInvoiceService.java | 116 +++++++++++++++- 3 files changed, 126 insertions(+), 122 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/InvoiceController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/InvoiceController.java index e57a67884..fa5f385ef 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/InvoiceController.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/InvoiceController.java @@ -84,6 +84,8 @@ public class InvoiceController { @Value("${jeecg.path.shippingInvoiceDetailDir}") private String INVOICE_DETAIL_DIR; + @Value("${jeecg.path.shippingInvoicePdfDir}") + private String INVOICE_PDF_DIR; @Autowired Environment env; @@ -484,6 +486,7 @@ public class InvoiceController { if (metaData.getInvoiceCode().equals("error")) { metaDataErrorList.add(metaData); } else { + filenameList.add(INVOICE_PDF_DIR + "//" + "Invoice N°" + metaData.getInvoiceCode() + " (" + metaData.getInvoiceEntity() + ").pdf"); filenameList.add(INVOICE_DIR + "//" + metaData.getFilename()); List factureDetails = shippingInvoiceService.getInvoiceDetail(metaData.getInvoiceCode()); List refunds = savRefundWithDetailService.getRefundsByInvoiceNumber(metaData.getInvoiceCode()); diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/ShippingInvoiceController.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/ShippingInvoiceController.java index 64fe943c5..bcb5216af 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/ShippingInvoiceController.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/controller/admin/shippingInvoice/ShippingInvoiceController.java @@ -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,11 +47,7 @@ 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; - /** * @Description: 物流发票 * @Author: jeecg-boot @@ -72,6 +66,8 @@ public class ShippingInvoiceController { @Autowired private IPlatformOrderService platformOrderService; @Autowired + private PlatformOrderShippingInvoiceService platformOrderShippingInvoiceService; + @Autowired private ISavRefundService savRefundService; @Autowired private IShippingInvoiceService shippingInvoiceService; @@ -79,15 +75,10 @@ public class ShippingInvoiceController { private FreeMarkerConfigurer freemarkerConfigurer; @Autowired private EmailService emailService; - private static final String EXTENSION = ".xlsx"; @Value("${jeecg.path.shippingInvoiceDir}") 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; @@ -276,82 +267,6 @@ public class ShippingInvoiceController { return Result.OK("文件导入失败!"); } - /** 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 getPath(String dirPath, String invoiceNumber) { - List 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 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 getPath(String dirPath, String invoiceNumber, String invoiceEntity) { - List 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 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 pathList = new ArrayList<>(); - if(filetype.equals("invoice")) { - log.info("File asked is of type invoice"); - pathList = getPath(INVOICE_LOCATION, invoiceNumber); - } - if(filetype.equals("detail")) { - log.info("File asked is of type invoice detail"); - pathList = getPath(INVOICE_DETAIL_LOCATION, 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(); - } - } - /** * Downloads the invoice and returns it in form of bytearray * @param invoiceNumber the invoice we want to download @@ -361,7 +276,7 @@ public class ShippingInvoiceController { */ @GetMapping(value = "/downloadCompleteInvoiceExcel") public ResponseEntity download(@RequestParam("invoiceNumber") String invoiceNumber, @RequestParam("filetype") String filetype) throws IOException { - String filename = getInvoiceList(invoiceNumber, filetype); + String filename = platformOrderShippingInvoiceService.getInvoiceList(invoiceNumber, filetype); if(!filename.equals("ERROR")) { File file = new File(filename); @@ -391,32 +306,6 @@ public class ShippingInvoiceController { .body("Couldn't find the invoice file for : " + invoiceNumber); } } - - 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_LOCATION + "/" + invoiceNumber + ".pdf"; - if(fileType.equals("invoice")){ - pdfFilePath = INVOICE_PDF_LOCATION + "/Invoice N°" + invoiceNumber + ".pdf"; - } - if(fileType.equals("detail")) { - pdfFilePath = INVOICE_DETAIL_PDF_LOCAION + "/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_LOCATION + "/" + 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"; - } /** * * @param invoiceNumber @@ -424,7 +313,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(); @@ -453,7 +342,7 @@ public class ShippingInvoiceController { @RequestParam("invoiceID") String invoiceID, @RequestParam("email") String email, @RequestParam("invoiceEntity") String invoiceEntity) throws Exception { - String filePath = getInvoiceList(invoiceNumber, "detail"); + String filePath = platformOrderShippingInvoiceService.getInvoiceList(invoiceNumber, "detail"); String fileType = "Détails de facture"; String subject = "Détails de facture N°" + invoiceNumber; Properties prop = emailService.getMailSender(); @@ -513,8 +402,8 @@ public class ShippingInvoiceController { shippingInvoiceService.delMain(id); log.info("Deleting invoice files ..."); String invoiceEntity = clientService.getClientEntity(clientId); - List invoicePathList = getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity); - List detailPathList = getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity); + List invoicePathList = platformOrderShippingInvoiceService.getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity); + List detailPathList = platformOrderShippingInvoiceService.getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity); boolean invoiceDeleted = false, detailDeleted = false; if(invoicePathList.isEmpty()) { @@ -577,8 +466,8 @@ public class ShippingInvoiceController { for(int i = 0; i < ids.size(); i++) { String invoiceNumber = invoiceNumbers.get(i); String invoiceEntity = clientService.getClientEntity(clientIds.get(i)); - List invoicePathList = getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity); - List detailPathList = getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity); + List invoicePathList = platformOrderShippingInvoiceService.getPath(INVOICE_LOCATION, invoiceNumber, invoiceEntity); + List detailPathList = platformOrderShippingInvoiceService.getPath(INVOICE_DETAIL_LOCATION, invoiceNumber, invoiceEntity); if(invoicePathList.isEmpty()) { log.error("FILE NOT FOUND : " + invoiceNumber + ", " + invoiceEntity); diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/PlatformOrderShippingInvoiceService.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/PlatformOrderShippingInvoiceService.java index 917b28983..99c3c34e9 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/PlatformOrderShippingInvoiceService.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/service/PlatformOrderShippingInvoiceService.java @@ -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; @@ -32,7 +34,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; @@ -91,9 +96,13 @@ public class PlatformOrderShippingInvoiceService { @Value("${jeecg.path.shippingInvoiceDir}") private String INVOICE_DIR; - @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", @@ -470,8 +479,10 @@ public class PlatformOrderShippingInvoiceService { metaData = makeInvoice(param); else metaData = makeCompleteInvoicePostShipping(param, "post"); + + convertToPdf(metaData.getInvoiceCode(), "invoice"); invoiceList.add(metaData); - } catch (UserException | IOException | ParseException e) { + } catch (Exception e) { invoiceList.add(new InvoiceMetaData("", "error", entry.getKey(), e.getMessage())); log.error(e.getMessage()); } @@ -480,6 +491,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 getPath(String dirPath, String invoiceNumber) { + List 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 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 getPath(String dirPath, String invoiceNumber, String invoiceEntity) { + List 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 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 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 invoiceList) throws IOException { log.info("Zipping Invoices ..."); From e1d7b36c13c0b471eae4c6f7f81c87b62bb5f844 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Fri, 13 Oct 2023 10:09:51 +0200 Subject: [PATCH 14/23] Release 1.7.0: Export Excel file to PDF by default after invoice generation --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8f68c629e..949ff0ffc 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.6.2 + 1.7.0 pom WIA APP ${project.version} From 43b31e5e9a57b3374cd92e67bd350e1936830d28 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Wed, 25 Oct 2023 11:40:04 +0200 Subject: [PATCH 15/23] Hotfix: Update CWJob API key and secret to retrieve parcel traces of another YDH account --- .../java/org/jeecg/modules/business/domain/job/CWJob.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/CWJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/CWJob.java index 8911991e1..ec3c1e002 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/CWJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/CWJob.java @@ -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 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 { From b05fd4445efaab55e3476f05e90e6eba270bf2ab Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Wed, 25 Oct 2023 11:40:41 +0200 Subject: [PATCH 16/23] Update project version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 949ff0ffc..304374b83 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.7.0 + 1.7.1 pom WIA APP ${project.version} From a2f27094c3e8b16d49732621c10020ccd646034f Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Mon, 13 Nov 2023 16:10:22 +0100 Subject: [PATCH 17/23] Create a new method for finding adequate tubes and a main to test for regression --- .../domain/job/AddPortraitTubeJob.java | 235 +++++++++++++++++- 1 file changed, 232 insertions(+), 3 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java index 37553a4ac..b89caf958 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java @@ -35,12 +35,13 @@ public class AddPortraitTubeJob implements Job { private static final List 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 TUBE_SKUS = Arrays.asList(TUBE_50_SKU_MULTIPLE, TUBE_50_SKU_SINGLE, - TUBE_40_SKU_MULTIPLE, TUBE_40_SKU_SINGLE); + private static final List 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"; @@ -153,7 +154,7 @@ public class AddPortraitTubeJob implements Job { * @param orderItems List of order items * @return Set of pairs */ - private Pair>, HashSet>> findCurrentAndAdequateTubes(List orderItems) { + private static Pair>, HashSet>> findCurrentAndAdequateTubes(List orderItems) { int canvas40Count = 0; int canvas50Count = 0; HashSet> currentTubes = new HashSet<>(); @@ -208,4 +209,232 @@ public class AddPortraitTubeJob implements Job { } return Pair.of(currentTubes, adequateTubes); } + + private static Pair>, HashSet>> newFindCurrentAndAdequateTubes(List orderItems) { + int canvas30Count = 0; + int canvas40Count = 0; + int canvas50Count = 0; + HashSet> currentTubes = new HashSet<>(); + HashSet> adequateTubes = new HashSet<>(); + for (OrderItem orderItem : orderItems) { + String sku = orderItem.getErpCode(); + int quantity = orderItem.getQuantity(); + if (TUBE_SKUS.contains(sku)) { + currentTubes.add(Pair.of(sku, quantity)); + } else if (sku.startsWith(PREFIX_50_CANVAS)) { + 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); + 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) { + tube40MultipleCount++; + } else { + if (canvas40RemainderCount > 0) { + tube40SingleCount++; + } else if (canvas30RemainderCount > 0){ + tube30SingleDoubleCount++; + } + } + } + } else if (totalRemainderCount >= 4) { + // When remaining 4 to 6 canvases, one 50cm canvas imposes one 50cm multiple tube + if (canvas50RemainderCount > 0) { + tube50MultipleCount++; + // If we have two 50cm canvases and a total of 5 of 6 canvases, then we have to use 2 50cm multiple tubes + if (totalRemainderCount > 4) { + tube50MultipleCount++; + } else { + // There's one remaining canvas, an adequate single tube suffices + if (canvas50RemainderCount > 1) { + tube50SingleCount++; + } else if (canvas40RemainderCount > 0) { + tube40SingleCount++; + } else if (canvas30RemainderCount > 0){ + 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)); + } + if (tube50MultipleCount > 0) { + adequateTubes.add(Pair.of(TUBE_50_SKU_MULTIPLE, tube50MultipleCount)); + } + if (tube40SingleCount > 0) { + adequateTubes.add(Pair.of(TUBE_40_SKU_SINGLE, tube40SingleCount)); + } + 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); + } + + public static void main(String[] args) { + List orderItems = buildOrderItems(1, 0); + compare(orderItems); + + orderItems = buildOrderItems(0, 1); + compare(orderItems); + + orderItems = buildOrderItems(1, 1); + compare(orderItems); + + orderItems = buildOrderItems(2, 0); + compare(orderItems); + + orderItems = buildOrderItems(0, 2); + compare(orderItems); + + orderItems = buildOrderItems(2, 1); + compare(orderItems); + + orderItems = buildOrderItems(1, 2); + compare(orderItems); + + orderItems = buildOrderItems(2, 2); + compare(orderItems); + + orderItems = buildOrderItems(3, 0); + compare(orderItems); + + orderItems = buildOrderItems(0, 3); + compare(orderItems); + + orderItems = buildOrderItems(3, 1); + compare(orderItems); + + orderItems = buildOrderItems(1, 3); + compare(orderItems); + + orderItems = buildOrderItems(3, 2); + compare(orderItems); + + orderItems = buildOrderItems(2, 3); + compare(orderItems); + + orderItems = buildOrderItems(3, 3); + compare(orderItems); + + orderItems = buildOrderItems(4, 0); + compare(orderItems); + + orderItems = buildOrderItems(0, 4); + compare(orderItems); + + orderItems = buildOrderItems(4, 1); + compare(orderItems); + + orderItems = buildOrderItems(1, 4); + compare(orderItems); + + orderItems = buildOrderItems(4, 2); + compare(orderItems); + + orderItems = buildOrderItems(2, 4); + compare(orderItems); + + orderItems = buildOrderItems(4, 3); + compare(orderItems); + + orderItems = buildOrderItems(3, 4); + compare(orderItems); + + orderItems = buildOrderItems(4, 4); + compare(orderItems); + + orderItems = buildOrderItems(5, 0); + compare(orderItems); + + orderItems = buildOrderItems(0, 5); + compare(orderItems); + + orderItems = buildOrderItems(5, 1); + compare(orderItems); + + orderItems = buildOrderItems(1, 5); + compare(orderItems); + + orderItems = buildOrderItems(5, 2); + compare(orderItems); + + orderItems = buildOrderItems(2, 5); + compare(orderItems); + + orderItems = buildOrderItems(5, 3); + compare(orderItems); + + orderItems = buildOrderItems(3, 5); + compare(orderItems); + + orderItems = buildOrderItems(5, 4); + compare(orderItems); + + orderItems = buildOrderItems(4, 5); + compare(orderItems); + + orderItems = buildOrderItems(5, 5); + compare(orderItems); + + } + + private static void compare(List orderItems) { + HashSet> tubesOldWay = findCurrentAndAdequateTubes(orderItems).getRight(); + HashSet> tubesNewWay = newFindCurrentAndAdequateTubes(orderItems).getRight(); + System.out.println("old way : " + tubesOldWay); + System.out.println("new way : " + tubesNewWay); + System.out.println(tubesOldWay.containsAll(tubesNewWay) && tubesNewWay.containsAll(tubesOldWay) ? "we're good" : "shit"); + } + + private static List buildOrderItems(Integer canvas40q, Integer canvas50q) { + List orderItems = new ArrayList<>(); + if (canvas40q != null) { + orderItems.add(buildOrderItem(PREFIX_40_CANVAS, canvas40q)); + } + if (canvas50q != null) { + orderItems.add(buildOrderItem(PREFIX_50_CANVAS, canvas50q)); + } + return orderItems; + } + + private static OrderItem buildOrderItem(String erpCode, Integer quantity) { + OrderItem orderItem = new OrderItem(); + orderItem.setErpCode(erpCode); + orderItem.setQuantity(quantity); + return orderItem; + } + } From 0c57d68a12b0941f0f6de76c9cea60f1e1391561 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 14 Nov 2023 14:20:23 +0100 Subject: [PATCH 18/23] Validate tests with improvement in case of two 40cm canvases and 50cm canvases --- .../domain/job/AddPortraitTubeJob.java | 118 ++++++++---------- 1 file changed, 53 insertions(+), 65 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java index b89caf958..c2e390267 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java @@ -252,7 +252,11 @@ public class AddPortraitTubeJob implements Job { } } else { if (totalRemainderCount > 1) { - tube40MultipleCount++; + if (canvas40RemainderCount > 0) { + tube40MultipleCount++; + } else { + tube30SingleDoubleCount++; + } } else { if (canvas40RemainderCount > 0) { tube40SingleCount++; @@ -265,18 +269,23 @@ public class AddPortraitTubeJob implements Job { // When remaining 4 to 6 canvases, one 50cm canvas imposes one 50cm multiple tube if (canvas50RemainderCount > 0) { tube50MultipleCount++; - // If we have two 50cm canvases and a total of 5 of 6 canvases, then we have to use 2 50cm multiple tubes - if (totalRemainderCount > 4) { - tube50MultipleCount++; - } else { - // There's one remaining canvas, an adequate single tube suffices - if (canvas50RemainderCount > 1) { - tube50SingleCount++; - } else if (canvas40RemainderCount > 0) { - tube40SingleCount++; - } else if (canvas30RemainderCount > 0){ - tube30SingleDoubleCount++; + 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 @@ -304,111 +313,86 @@ public class AddPortraitTubeJob implements Job { } public static void main(String[] args) { - List orderItems = buildOrderItems(1, 0); + List orderItems = buildOrderItems(0,0,0); compare(orderItems); - orderItems = buildOrderItems(0, 1); + orderItems = buildOrderItems(0,0,1); compare(orderItems); - orderItems = buildOrderItems(1, 1); + orderItems = buildOrderItems(0,0,2); compare(orderItems); - orderItems = buildOrderItems(2, 0); + orderItems = buildOrderItems(0,1,0); compare(orderItems); - orderItems = buildOrderItems(0, 2); + orderItems = buildOrderItems(0,1,1); compare(orderItems); - orderItems = buildOrderItems(2, 1); + orderItems = buildOrderItems(0,1,2); compare(orderItems); - orderItems = buildOrderItems(1, 2); + orderItems = buildOrderItems(0,2,0); compare(orderItems); - orderItems = buildOrderItems(2, 2); + orderItems = buildOrderItems(0,2,1); compare(orderItems); - orderItems = buildOrderItems(3, 0); + orderItems = buildOrderItems(0,2,2); compare(orderItems); - orderItems = buildOrderItems(0, 3); + orderItems = buildOrderItems(1,0,0); compare(orderItems); - orderItems = buildOrderItems(3, 1); + orderItems = buildOrderItems(1,0,1); compare(orderItems); - orderItems = buildOrderItems(1, 3); + orderItems = buildOrderItems(1,0,2); compare(orderItems); - orderItems = buildOrderItems(3, 2); + orderItems = buildOrderItems(1,1,0); compare(orderItems); - orderItems = buildOrderItems(2, 3); + orderItems = buildOrderItems(1,1,1); compare(orderItems); - orderItems = buildOrderItems(3, 3); + orderItems = buildOrderItems(1,1,2); compare(orderItems); - orderItems = buildOrderItems(4, 0); + orderItems = buildOrderItems(1,2,0); compare(orderItems); - orderItems = buildOrderItems(0, 4); + orderItems = buildOrderItems(1,2,1); compare(orderItems); - orderItems = buildOrderItems(4, 1); + orderItems = buildOrderItems(1,2,2); compare(orderItems); - orderItems = buildOrderItems(1, 4); + orderItems = buildOrderItems(2,0,0); compare(orderItems); - orderItems = buildOrderItems(4, 2); + orderItems = buildOrderItems(2,0,1); compare(orderItems); - orderItems = buildOrderItems(2, 4); + orderItems = buildOrderItems(2,0,2); compare(orderItems); - orderItems = buildOrderItems(4, 3); + orderItems = buildOrderItems(2,1,0); compare(orderItems); - orderItems = buildOrderItems(3, 4); + orderItems = buildOrderItems(2,1,1); compare(orderItems); - orderItems = buildOrderItems(4, 4); + orderItems = buildOrderItems(2,1,2); compare(orderItems); - orderItems = buildOrderItems(5, 0); + orderItems = buildOrderItems(2,2,0); compare(orderItems); - orderItems = buildOrderItems(0, 5); + orderItems = buildOrderItems(2,2,1); compare(orderItems); - orderItems = buildOrderItems(5, 1); + orderItems = buildOrderItems(2,2,2); compare(orderItems); - - orderItems = buildOrderItems(1, 5); - compare(orderItems); - - orderItems = buildOrderItems(5, 2); - compare(orderItems); - - orderItems = buildOrderItems(2, 5); - compare(orderItems); - - orderItems = buildOrderItems(5, 3); - compare(orderItems); - - orderItems = buildOrderItems(3, 5); - compare(orderItems); - - orderItems = buildOrderItems(5, 4); - compare(orderItems); - - orderItems = buildOrderItems(4, 5); - compare(orderItems); - - orderItems = buildOrderItems(5, 5); - compare(orderItems); - } private static void compare(List orderItems) { @@ -419,14 +403,18 @@ public class AddPortraitTubeJob implements Job { System.out.println(tubesOldWay.containsAll(tubesNewWay) && tubesNewWay.containsAll(tubesOldWay) ? "we're good" : "shit"); } - private static List buildOrderItems(Integer canvas40q, Integer canvas50q) { + private static List buildOrderItems(Integer canvas30q, Integer canvas40q, Integer canvas50q) { List orderItems = new ArrayList<>(); + if (canvas30q != null) { + orderItems.add(buildOrderItem(PREFIX_30_CANVAS, canvas30q)); + } if (canvas40q != null) { orderItems.add(buildOrderItem(PREFIX_40_CANVAS, canvas40q)); } if (canvas50q != null) { orderItems.add(buildOrderItem(PREFIX_50_CANVAS, canvas50q)); } + System.out.println(canvas30q + " x 30cm " + canvas40q + " x 40cm " + canvas50q + " x 50cm"); return orderItems; } From 140997994a76b920c6b565417a07630849b3b69d Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 14 Nov 2023 14:26:05 +0100 Subject: [PATCH 19/23] Replace old algo by new algo --- .../domain/job/AddPortraitTubeJob.java | 172 +----------------- 1 file changed, 1 insertion(+), 171 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java index c2e390267..95ee6fba1 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/AddPortraitTubeJob.java @@ -154,63 +154,7 @@ public class AddPortraitTubeJob implements Job { * @param orderItems List of order items * @return Set of pairs */ - private static Pair>, HashSet>> findCurrentAndAdequateTubes(List orderItems) { - int canvas40Count = 0; - int canvas50Count = 0; - HashSet> currentTubes = new HashSet<>(); - HashSet> adequateTubes = new HashSet<>(); - for (OrderItem orderItem : orderItems) { - String sku = orderItem.getErpCode(); - int quantity = orderItem.getQuantity(); - if (TUBE_SKUS.contains(sku)) { - currentTubes.add(Pair.of(sku, quantity)); - } else if (sku.startsWith(PREFIX_50_CANVAS)) { - canvas50Count += quantity; - } else if (sku.startsWith(PREFIX_40_CANVAS)) { - canvas40Count += quantity; - } else if (sku.startsWith(PREFIX_30_CANVAS)) { - canvas40Count += quantity; - } - } - - int canvas40RemainderCount = canvas40Count % MAXIMUM_CANVAS_IN_TUBE.intValue(); - int canvas50RemainderCount = canvas50Count % MAXIMUM_CANVAS_IN_TUBE.intValue(); - 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++; - } - } else { - if (canvas40RemainderCount == 1) { - tube40SingleCount++; - } else if (canvas40RemainderCount == 2) { - tube40MultipleCount++; - } else if (canvas50RemainderCount == 1) { - tube50SingleCount++; - } else if (canvas50RemainderCount == 2) { - tube50MultipleCount++; - } - } - if (tube50SingleCount > 0) { - adequateTubes.add(Pair.of(TUBE_50_SKU_SINGLE, tube50SingleCount)); - } - if (tube50MultipleCount > 0) { - adequateTubes.add(Pair.of(TUBE_50_SKU_MULTIPLE, tube50MultipleCount)); - } - if (tube40SingleCount > 0) { - adequateTubes.add(Pair.of(TUBE_40_SKU_SINGLE, tube40SingleCount)); - } - if (tube40MultipleCount > 0) { - adequateTubes.add(Pair.of(TUBE_40_SKU_MULTIPLE, tube40MultipleCount)); - } - return Pair.of(currentTubes, adequateTubes); - } - - private static Pair>, HashSet>> newFindCurrentAndAdequateTubes(List orderItems) { + private Pair>, HashSet>> findCurrentAndAdequateTubes(List orderItems) { int canvas30Count = 0; int canvas40Count = 0; int canvas50Count = 0; @@ -311,118 +255,4 @@ public class AddPortraitTubeJob implements Job { } return Pair.of(currentTubes, adequateTubes); } - - public static void main(String[] args) { - List orderItems = buildOrderItems(0,0,0); - compare(orderItems); - - orderItems = buildOrderItems(0,0,1); - compare(orderItems); - - orderItems = buildOrderItems(0,0,2); - compare(orderItems); - - orderItems = buildOrderItems(0,1,0); - compare(orderItems); - - orderItems = buildOrderItems(0,1,1); - compare(orderItems); - - orderItems = buildOrderItems(0,1,2); - compare(orderItems); - - orderItems = buildOrderItems(0,2,0); - compare(orderItems); - - orderItems = buildOrderItems(0,2,1); - compare(orderItems); - - orderItems = buildOrderItems(0,2,2); - compare(orderItems); - - orderItems = buildOrderItems(1,0,0); - compare(orderItems); - - orderItems = buildOrderItems(1,0,1); - compare(orderItems); - - orderItems = buildOrderItems(1,0,2); - compare(orderItems); - - orderItems = buildOrderItems(1,1,0); - compare(orderItems); - - orderItems = buildOrderItems(1,1,1); - compare(orderItems); - - orderItems = buildOrderItems(1,1,2); - compare(orderItems); - - orderItems = buildOrderItems(1,2,0); - compare(orderItems); - - orderItems = buildOrderItems(1,2,1); - compare(orderItems); - - orderItems = buildOrderItems(1,2,2); - compare(orderItems); - - orderItems = buildOrderItems(2,0,0); - compare(orderItems); - - orderItems = buildOrderItems(2,0,1); - compare(orderItems); - - orderItems = buildOrderItems(2,0,2); - compare(orderItems); - - orderItems = buildOrderItems(2,1,0); - compare(orderItems); - - orderItems = buildOrderItems(2,1,1); - compare(orderItems); - - orderItems = buildOrderItems(2,1,2); - compare(orderItems); - - orderItems = buildOrderItems(2,2,0); - compare(orderItems); - - orderItems = buildOrderItems(2,2,1); - compare(orderItems); - - orderItems = buildOrderItems(2,2,2); - compare(orderItems); - } - - private static void compare(List orderItems) { - HashSet> tubesOldWay = findCurrentAndAdequateTubes(orderItems).getRight(); - HashSet> tubesNewWay = newFindCurrentAndAdequateTubes(orderItems).getRight(); - System.out.println("old way : " + tubesOldWay); - System.out.println("new way : " + tubesNewWay); - System.out.println(tubesOldWay.containsAll(tubesNewWay) && tubesNewWay.containsAll(tubesOldWay) ? "we're good" : "shit"); - } - - private static List buildOrderItems(Integer canvas30q, Integer canvas40q, Integer canvas50q) { - List orderItems = new ArrayList<>(); - if (canvas30q != null) { - orderItems.add(buildOrderItem(PREFIX_30_CANVAS, canvas30q)); - } - if (canvas40q != null) { - orderItems.add(buildOrderItem(PREFIX_40_CANVAS, canvas40q)); - } - if (canvas50q != null) { - orderItems.add(buildOrderItem(PREFIX_50_CANVAS, canvas50q)); - } - System.out.println(canvas30q + " x 30cm " + canvas40q + " x 40cm " + canvas50q + " x 50cm"); - return orderItems; - } - - private static OrderItem buildOrderItem(String erpCode, Integer quantity) { - OrderItem orderItem = new OrderItem(); - orderItem.setErpCode(erpCode); - orderItem.setQuantity(quantity); - return orderItem; - } - } From 171baab56ede7e54187cac3abc981f425098e2cb Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 14 Nov 2023 15:27:25 +0100 Subject: [PATCH 20/23] Release 1.8.0: Improve tube adding algo and 30cm tubes --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 304374b83..9b0d02724 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.7.1 + 1.8.0 pom WIA APP ${project.version} From 09bcd7938c32dc001f7f9f81d9ab5d31a6337de5 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 21 Nov 2023 15:22:01 +0100 Subject: [PATCH 21/23] Refactor HLJob to make it possible to change request URL --- .../modules/business/domain/api/hualei/HLRequest.java | 7 ++++--- .../java/org/jeecg/modules/business/domain/job/HLJob.java | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/hualei/HLRequest.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/hualei/HLRequest.java index 55651f820..a8611da9d 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/hualei/HLRequest.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/api/hualei/HLRequest.java @@ -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 billCodes; - public HLRequest(List billCodes) { + public HLRequest(String url, List 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")); diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/HLJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/HLJob.java index 27e776f7d..3f12dc81b 100644 --- a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/HLJob.java +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/HLJob.java @@ -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 DEFAULT_TRANSPORTERS = Arrays.asList("法国专线普货"); @@ -91,7 +93,7 @@ public class HLJob implements Job { List parcelTraces = new ArrayList<>(); List hlRequests = new ArrayList<>(); billCodeLists.forEach(billcodeList -> { - HLRequest hlRequest = new HLRequest(billcodeList); + HLRequest hlRequest = new HLRequest(URL, billcodeList); hlRequests.add(hlRequest); }); List results = new ArrayList<>(); From 6ac3ce6bf6a2b8a63c4601c0eb4680d31a5687c8 Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 21 Nov 2023 15:22:21 +0100 Subject: [PATCH 22/23] Create YPJob --- .../modules/business/domain/job/YPJob.java | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/YPJob.java diff --git a/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/YPJob.java b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/YPJob.java new file mode 100644 index 000000000..0871cbb25 --- /dev/null +++ b/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/business/domain/job/YPJob.java @@ -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 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 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 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 billCodes = platformOrderService.fetchBillCodesOfParcelsWithoutTrace( + Date.valueOf(startDate), Date.valueOf(endDate), transporters); + log.info("{} parcels without trace in total", billCodes.size()); + List> billCodeLists = Lists.partition(billCodes, 40); + log.info("Requests will be divided in to {} parts", billCodeLists.size()); + List parcelTraces = new ArrayList<>(); + List hlRequests = new ArrayList<>(); + billCodeLists.forEach(billcodeList -> { + HLRequest hlRequest = new HLRequest(URL, billcodeList); + hlRequests.add(hlRequest); + }); + List 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> parcelTraceList = Lists.partition(parcelTraces, DEFAULT_MAXIMUM_NUMBER_OF_PARCELS_PER_TRANSACTION); + for (List parcelTracesPerTransaction : parcelTraceList) { + parcelService.saveHLParcelAndTraces(parcelTracesPerTransaction); + } + } + +} From c494a9cd022e0ca22ce4fa0d00a3ee6c6a33669f Mon Sep 17 00:00:00 2001 From: Qiuyi LI Date: Tue, 21 Nov 2023 15:27:25 +0100 Subject: [PATCH 23/23] Update project version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9b0d02724..c42e2d69b 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.jeecgframework.boot jeecg-boot-parent - 1.8.0 + 1.8.1 pom WIA APP ${project.version}