From 533d360cdc23a566d2bb884367f2e8bd10ce8fc8 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 9 Oct 2026 16:46:52 +0800 Subject: [PATCH] Implement product Excel file import functionality, including new service methods for handling Excel attachments. Update the ProductController to list imported Excel files by product code. Enhance BmsProduct and related entities to store references to the imported Excel files. Update front-end components to support displaying and managing Excel file attachments for products. --- .../controller/price/ProductController.java | 14 +++ .../java/com/bms/price/domain/BmsProduct.java | 2 + .../bms/price/domain/PrcPriceExcelFile.java | 35 ++++++ .../com/bms/price/domain/PrcPriceSet.java | 5 + .../bms/price/domain/PrcPriceSetProduct.java | 2 + .../price/mapper/PrcPriceExcelFileMapper.java | 24 ++++ .../mapper/PrcPriceSetProductMapper.java | 4 + .../service/IPrcPriceExcelFileService.java | 25 ++++ .../service/impl/BmsProductServiceImpl.java | 30 +++++ .../impl/PrcPriceExcelFileServiceImpl.java | 115 +++++++++++++++++ .../service/impl/PrcPriceSetServiceImpl.java | 68 +++++++++- .../mapper/price/BmsProductMapper.xml | 6 +- .../mapper/price/PrcPriceExcelFileMapper.xml | 116 ++++++++++++++++++ .../mapper/price/PrcPriceSetProductMapper.xml | 25 +++- bms-back/sql/price/prc_price_excel_file.sql | 42 +++---- bms-front/src/api/price/product.js | 8 ++ .../components/ProductExcelFileDrawer.vue | 65 ++++++++++ .../components/ProductExpiredDrawer.vue | 13 +- .../src/views/price/basic/product/index.vue | 4 + .../set/components/create/StepDetails.vue | 5 +- bms-front/src/views/price/set/create.vue | 12 ++ 21 files changed, 589 insertions(+), 31 deletions(-) create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceExcelFile.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceExcelFileMapper.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceExcelFileService.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceExcelFileServiceImpl.java create mode 100644 bms-back/bms-system/src/main/resources/mapper/price/PrcPriceExcelFileMapper.xml create mode 100644 bms-front/src/views/price/basic/product/components/ProductExcelFileDrawer.vue diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/ProductController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/ProductController.java index bf2bcd4e..b458ab6c 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/ProductController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/ProductController.java @@ -27,7 +27,9 @@ import com.bms.common.utils.MessageUtils; import com.bms.price.domain.BmsProduct; import com.bms.price.domain.BmsProductCountry; import com.bms.price.domain.BmsProductImportTask; +import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.service.IBmsProductService; +import com.bms.price.service.IPrcPriceExcelFileService; /** * Product list (async Excel import) @@ -41,6 +43,9 @@ public class ProductController extends BaseController @Autowired private IBmsProductService productService; + @Autowired + private IPrcPriceExcelFileService priceExcelFileService; + @PreAuthorize("@ss.hasPermi('price:product:list')") @GetMapping("/list") public TableDataInfo list(BmsProduct product) @@ -50,6 +55,15 @@ public class ProductController extends BaseController return getDataTable(list); } + /** Imported quote Excel attachments for this product_code (via product.pef_id). */ + @PreAuthorize("@ss.hasPermi('price:product:query')") + @GetMapping("/{productCode}/excelFiles") + public AjaxResult excelFiles(@PathVariable String productCode) + { + List list = priceExcelFileService.selectByProductCode(productCode); + return success(list); + } + /** * 产品下拉选项(非分页)。 * 供计费列表、客户绑定等场景选择产品;不做权限控制(仍需登录)。 diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/BmsProduct.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/BmsProduct.java index ea6c0d22..1d69272a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/BmsProduct.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/BmsProduct.java @@ -33,6 +33,8 @@ public class BmsProduct extends BaseEntity public static final String STATUS_EXPIRED = "2"; private String status; private String sourceSheet; + /** FK prc_price_excel_file.pef_id */ + private Long pefId; private Long tmsId; private Integer countryCount; private Integer surchargeCount; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceExcelFile.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceExcelFile.java new file mode 100644 index 00000000..aa0bfbc5 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceExcelFile.java @@ -0,0 +1,35 @@ +package com.bms.price.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bms.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Import quote Excel attachment: prc_price_excel_file + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PrcPriceExcelFile extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + public static final String TYPE_PRODUCT = "PRODUCT"; + public static final String TYPE_REDLINE = "REDLINE"; + public static final String TYPE_SALES = "SALES"; + + private Long pefId; + /** PRODUCT / REDLINE / SALES */ + private String excelType; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date effectiveTime; + private String fileName; + private String filePath; + private Long fileSize; + private String contentType; + private String fileExtension; + private String ossId; + /** Comma-separated sheet names (optional) */ + private String sheetNames; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java index 661693b3..fc0209c6 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSet.java @@ -48,4 +48,9 @@ public class PrcPriceSet extends BaseEntity private String pullFromLibrary; /** Y: even if version already has details, delete+re-pull from library */ private String forcePullLibrary; + /** + * Request-only: stamp this Excel attachment id onto prc_price_set_product.pef_id + * when saving IMPORT details (not a column on prc_price_set). + */ + private Long pefId; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetProduct.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetProduct.java index e87e10e4..c73066ca 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetProduct.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcPriceSetProduct.java @@ -37,6 +37,8 @@ public class PrcPriceSetProduct implements Serializable private Long sourceProductId; /** 来源Excel sheet */ private String sourceSheet; + /** 关联导入价表Excel附件 prc_price_excel_file.pef_id */ + private Long pefId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceExcelFileMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceExcelFileMapper.java new file mode 100644 index 00000000..bc16572b --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceExcelFileMapper.java @@ -0,0 +1,24 @@ +package com.bms.price.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.bms.price.domain.PrcPriceExcelFile; + +/** + * Import quote Excel attachment mapper + */ +public interface PrcPriceExcelFileMapper +{ + public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId); + + public List selectPrcPriceExcelFileList(PrcPriceExcelFile query); + + /** Distinct PRODUCT excel files linked by product.pef_id for this product_code */ + public List selectByProductCode(@Param("productCode") String productCode); + + public int insertPrcPriceExcelFile(PrcPriceExcelFile row); + + public int updatePrcPriceExcelFile(PrcPriceExcelFile row); + + public int deletePrcPriceExcelFileByIds(Long[] pefIds); +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetProductMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetProductMapper.java index 10efa5ba..fc4a685a 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetProductMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcPriceSetProductMapper.java @@ -34,4 +34,8 @@ public interface PrcPriceSetProductMapper /** Bind detail.psp_id from set product by psv + product_code. */ public int bindDetailPspByPsvId(@Param("psvId") Long psvId); + + /** Stamp Excel attachment id on set products under this version (optional code filter). */ + public int updatePefIdByPsvId(@Param("psvId") Long psvId, @Param("pefId") Long pefId, + @Param("productCodes") List productCodes); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceExcelFileService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceExcelFileService.java new file mode 100644 index 00000000..98433644 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceExcelFileService.java @@ -0,0 +1,25 @@ +package com.bms.price.service; + +import java.util.Date; +import java.util.List; +import com.bms.price.domain.PrcPriceExcelFile; + +/** + * Import quote Excel attachment service + */ +public interface IPrcPriceExcelFileService +{ + public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId); + + /** Import Excel attachments linked to this product_code via product.pef_id */ + public List selectByProductCode(String productCode); + + /** + * Persist uploaded Excel under upload/price/excel and insert metadata row. + * + * @param excelType PRODUCT / REDLINE / SALES + * @return inserted row with pefId + */ + public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes, + String filename, String operName) throws Exception; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/BmsProductServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/BmsProductServiceImpl.java index 9436d5a5..f0904e82 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/BmsProductServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/BmsProductServiceImpl.java @@ -41,7 +41,9 @@ import com.bms.price.domain.PrcZone; import com.bms.price.mapper.BmsProductCountryMapper; import com.bms.price.mapper.BmsProductMapper; import com.bms.price.mapper.PrcZoneMapper; +import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.service.IBmsProductService; +import com.bms.price.service.IPrcPriceExcelFileService; import com.bms.price.service.IPrcSurchargeRelService; import com.bms.price.task.BmsProductImportAsyncRunner; import com.bms.price.util.ZoneCodeAbbrevs; @@ -94,6 +96,9 @@ public class BmsProductServiceImpl implements IBmsProductService @Autowired private BmsProductImportAsyncRunner importAsyncRunner; + @Autowired + private IPrcPriceExcelFileService priceExcelFileService; + @Override public BmsProduct selectBmsProductById(Long productId) { @@ -313,6 +318,10 @@ public class BmsProductServiceImpl implements IBmsProductService assertImportProductsNoDupCountryWeight(workbook, products, regionByNameCn, euRegions, zoneByCountryAndCode); + PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel( + PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName); + Long pefId = excelFile != null ? excelFile.getPefId() : null; + int successNum = 0; int skipNum = 0; int countryNum = 0; @@ -331,6 +340,7 @@ public class BmsProductServiceImpl implements IBmsProductService try { product.setEffectiveDate(forcedEff); + product.setPefId(pefId); int[] nums = importOneProduct(workbook, product, operName, regionByNameCn, euRegions, zoneByCountryAndCode, unmatchedCountries, forcedEff); transactionManager.commit(tx); @@ -431,6 +441,10 @@ public class BmsProductServiceImpl implements IBmsProductService // Pre-check all products: reject whole import before any write if any is earlier than prev version assertImportProductsEffectiveNotBeforePrev(products, forcedEff); + PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel( + PrcPriceExcelFile.TYPE_PRODUCT, forcedEff, bytes, filename, operName); + Long pefId = excelFile != null ? excelFile.getPefId() : null; + int successNum = 0; int skipNum = 0; int countryNum = 0; @@ -458,6 +472,7 @@ public class BmsProductServiceImpl implements IBmsProductService product.setDestRegion(countryLabel); product.setSourceSheet(block.sheetName); product.setEffectiveDate(forcedEff); + product.setPefId(pefId); if (StringUtils.isEmpty(product.getTaxMode())) { product.setTaxMode(resolveTaxMode(product.getProductName())); @@ -649,6 +664,7 @@ public class BmsProductServiceImpl implements IBmsProductService if (existing != null) { product.setProductId(existing.getProductId()); + patchProductPefId(product, operName); int countryRows = saveCountries(product, countries); return new int[] { 0, 1, countryRows }; } @@ -672,6 +688,19 @@ public class BmsProductServiceImpl implements IBmsProductService return new int[] { 1, 0, countryRows }; } + private void patchProductPefId(BmsProduct product, String operName) + { + if (product == null || product.getProductId() == null || product.getPefId() == null) + { + return; + } + BmsProduct pefPatch = new BmsProduct(); + pefPatch.setProductId(product.getProductId()); + pefPatch.setPefId(product.getPefId()); + pefPatch.setUpdateBy(operName); + productMapper.updateBmsProduct(pefPatch); + } + /** * Save country rows by effective_date. @@ -2159,6 +2188,7 @@ public class BmsProductServiceImpl implements IBmsProductService if (existing != null) { product.setProductId(existing.getProductId()); + patchProductPefId(product, operName); int countryRows = saveCountries(product, countries); return new int[] { 0, 1, countryRows }; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceExcelFileServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceExcelFileServiceImpl.java new file mode 100644 index 00000000..5ea440a9 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceExcelFileServiceImpl.java @@ -0,0 +1,115 @@ +package com.bms.price.service.impl; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.bms.common.config.BmsConfig; +import com.bms.common.exception.ServiceException; +import com.bms.common.utils.DateUtils; +import com.bms.common.utils.StringUtils; +import com.bms.common.utils.file.FileUploadUtils; +import com.bms.common.utils.uuid.IdUtils; +import com.bms.price.domain.PrcPriceExcelFile; +import com.bms.price.mapper.PrcPriceExcelFileMapper; +import com.bms.price.service.IPrcPriceExcelFileService; + +/** + * Persist import Excel bytes and metadata + */ +@Service +public class PrcPriceExcelFileServiceImpl implements IPrcPriceExcelFileService +{ + private static final String SUB_DIR = "price/excel"; + + @Autowired + private PrcPriceExcelFileMapper excelFileMapper; + + @Override + public PrcPriceExcelFile selectPrcPriceExcelFileById(Long pefId) + { + return excelFileMapper.selectPrcPriceExcelFileById(pefId); + } + + @Override + public List selectByProductCode(String productCode) + { + if (StringUtils.isEmpty(productCode)) + { + return Collections.emptyList(); + } + List list = excelFileMapper.selectByProductCode(productCode.trim()); + return list == null ? Collections.emptyList() : list; + } + + @Override + public PrcPriceExcelFile saveImportExcel(String excelType, Date effectiveTime, byte[] bytes, + String filename, String operName) throws Exception + { + if (StringUtils.isEmpty(excelType)) + { + throw new ServiceException("excelType required"); + } + if (bytes == null || bytes.length == 0) + { + throw new ServiceException("excel empty"); + } + if (effectiveTime == null) + { + effectiveTime = new Date(); + } + String originalName = StringUtils.isNotEmpty(filename) ? filename : "import.xlsx"; + String ext = FilenameUtils.getExtension(originalName); + if (StringUtils.isEmpty(ext)) + { + ext = "xlsx"; + } + ext = ext.toLowerCase(Locale.ROOT); + + String uploadDir = BmsConfig.getUploadPath() + File.separator + SUB_DIR; + String pathName = DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + "." + ext; + File dest = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + FileOutputStream fos = null; + try + { + fos = new FileOutputStream(dest); + fos.write(bytes); + } + finally + { + IOUtils.close(fos); + } + String storedPath = FileUploadUtils.getPathFileName(uploadDir, pathName); + + PrcPriceExcelFile row = new PrcPriceExcelFile(); + row.setExcelType(excelType.trim().toUpperCase(Locale.ROOT)); + row.setEffectiveTime(effectiveTime); + row.setFileName(originalName); + row.setFilePath(storedPath); + row.setFileSize(Long.valueOf(bytes.length)); + row.setContentType(resolveContentType(ext)); + row.setFileExtension(ext); + row.setCreateBy(operName); + excelFileMapper.insertPrcPriceExcelFile(row); + return row; + } + + private static String resolveContentType(String ext) + { + if ("xls".equals(ext)) + { + return "application/vnd.ms-excel"; + } + if ("xlsx".equals(ext)) + { + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + } + return ""; + } +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java index 10649d04..c844ab6b 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcPriceSetServiceImpl.java @@ -22,10 +22,12 @@ import com.bms.common.exception.ServiceException; import com.bms.common.utils.DateUtils; import com.bms.common.utils.MessageUtils; import com.bms.common.utils.StringUtils; +import com.bms.common.utils.SecurityUtils; import com.bms.common.utils.uuid.IdUtils; import com.bms.price.domain.BmsProduct; import com.bms.price.domain.BmsProductCountry; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetCheckItem; import com.bms.price.domain.PrcPriceSetDetail; @@ -46,6 +48,7 @@ import com.bms.price.mapper.PrcPriceSetVersionSurchargeMapper; import com.bms.price.mapper.PrcSurchargeMapper; import com.bms.price.mapper.PrcSurchargeRelMapper; import com.bms.price.service.IBmsProductService; +import com.bms.price.service.IPrcPriceExcelFileService; import com.bms.price.service.IPrcPriceSetService; import com.bms.price.task.PrcPriceSetSaveAsyncRunner; @@ -117,6 +120,9 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService @Autowired private PrcPriceSetSaveAsyncRunner saveAsyncRunner; + @Autowired + private IPrcPriceExcelFileService priceExcelFileService; + @Override public PrcPriceSet selectPrcPriceSetById(Long priceSetId) { @@ -1071,6 +1077,18 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { assertUpdateOnlyWeightBandsMatch(byProduct, detailIndex); } + String operName = null; + try + { + operName = SecurityUtils.getUsername(); + } + catch (Exception ignored) + { + operName = null; + } + PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel( + PrcPriceExcelFile.TYPE_SALES, new Date(), bytes, filename, operName); + Long pefId = excelFile != null ? excelFile.getPefId() : null; Map productMap = new LinkedHashMap(); int updated = 0; @@ -1172,9 +1190,20 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService throw new ServiceException(MessageUtils.message("price.set.import.noDetailWritten")); } + if (pefId != null) + { + for (PrcPriceSetProduct sp : productMap.values()) + { + if (sp != null) + { + sp.setPefId(pefId); + } + } + } Map result = new HashMap(); result.put("products", new ArrayList(productMap.values())); result.put("details", details); + result.put("pefId", pefId); if (updateOnly) { result.put("message", MessageUtils.message("price.set.import.updateSuccess", @@ -1291,6 +1320,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { assertUpdateOnlyWeightBandsMatch(byProduct, detailIndex); } + PrcPriceSetVersion verForFile = versionMapper.selectPrcPriceSetVersionById(psvId); + Date excelEff = verForFile != null && verForFile.getEffectiveTime() != null + ? verForFile.getEffectiveTime() : new Date(); + PrcPriceExcelFile excelFile = priceExcelFileService.saveImportExcel( + PrcPriceExcelFile.TYPE_SALES, excelEff, bytes, filename, operName); + Long pefId = excelFile != null ? excelFile.getPefId() : null; int updated = 0; int inserted = 0; @@ -1321,7 +1356,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService Long pspId = null; if (!updateOnly) { - pspId = ensureSetProduct(priceSetId, psvId, productCode, productName); + pspId = ensureSetProduct(priceSetId, psvId, productCode, productName, pefId); } for (BmsProductCountry row : e.getValue()) { @@ -1385,6 +1420,11 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { setProductMapper.bindDetailPspByPsvId(psvId); } + if (pefId != null && (inserted > 0 || updated > 0)) + { + List codes = new ArrayList(byProduct.keySet()); + setProductMapper.updatePefIdByPsvId(psvId, pefId, codes); + } reportImportProgress(taskId, priceSetId, 95, "finishing"); if (inserted == 0 && updated == 0) { @@ -2935,7 +2975,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService d.setPriceSetId(set.getPriceSetId()); d.setPsvId(version.getPsvId()); Long pspId = ensureSetProduct(set.getPriceSetId(), version.getPsvId(), - d.getProductCode(), d.getProductName()); + d.getProductCode(), d.getProductName(), set.getPefId()); if (pspId != null) { d.setPspId(pspId); @@ -3059,6 +3099,12 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService * Uses request-scoped cache during bulk upsert to avoid N selects on same code. */ private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint) + { + return ensureSetProduct(priceSetId, psvId, productCode, productNameHint, null); + } + + private Long ensureSetProduct(Long priceSetId, Long psvId, String productCode, String productNameHint, + Long pefId) { if (psvId == null || StringUtils.isEmpty(productCode)) { @@ -3069,15 +3115,32 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService Map cache = setProductCacheTl.get(); if (cache != null && cache.containsKey(key)) { + if (pefId != null) + { + PrcPriceSetProduct pefPatch = new PrcPriceSetProduct(); + pefPatch.setPspId(cache.get(key)); + pefPatch.setPefId(pefId); + setProductMapper.updatePrcPriceSetProduct(pefPatch); + } return cache.get(key); } PrcPriceSetProduct existing = setProductMapper.selectByPsvIdAndCode(psvId, code); if (existing != null) { + boolean needUpdate = false; if (StringUtils.isEmpty(existing.getProductName()) && StringUtils.isNotEmpty(productNameHint) && !code.equals(productNameHint)) { existing.setProductName(productNameHint); + needUpdate = true; + } + if (pefId != null && (existing.getPefId() == null || !pefId.equals(existing.getPefId()))) + { + existing.setPefId(pefId); + needUpdate = true; + } + if (needUpdate) + { setProductMapper.updatePrcPriceSetProduct(existing); } if (cache != null) @@ -3090,6 +3153,7 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService row.setPriceSetId(priceSetId); row.setPsvId(psvId); row.setProductCode(code); + row.setPefId(pefId); BmsProduct lib = null; try { diff --git a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductMapper.xml index 93466b8b..501f8562 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/BmsProductMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/BmsProductMapper.xml @@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -30,7 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select p.product_id, p.product_code, p.product_name, p.service_level, p.cargo_attr, p.dest_region, p.update_note, p.tax_mode, p.effective_date, p.expire_date, p.status, - p.source_sheet, p.remark, p.tms_id, p.created_by, p.create_time, p.updated_by, p.update_time, + p.source_sheet, p.pef_id, p.remark, p.tms_id, p.created_by, p.create_time, p.updated_by, p.update_time, (select count(distinct c.country_name) from product_country c where c.product_id = p.product_id or (c.product_id is null and c.product_code = p.product_code @@ -139,6 +140,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" expire_date, status, source_sheet, + pef_id, remark, tms_id, created_by, @@ -155,6 +157,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{expireDate}, #{status}, #{sourceSheet}, + #{pefId}, #{remark}, #{tmsId}, #{createBy}, @@ -176,6 +179,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" expire_date = #{expireDate}, status = #{status}, source_sheet = #{sourceSheet}, + pef_id = #{pefId}, remark = #{remark}, tms_id = #{tmsId}, updated_by = #{updateBy}, diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceExcelFileMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceExcelFileMapper.xml new file mode 100644 index 00000000..2713d744 --- /dev/null +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceExcelFileMapper.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + select pef_id, excel_type, effective_time, file_name, file_path, file_size, content_type, + file_extension, oss_id, sheet_names, remark, created_by, create_time, updated_by, update_time + from prc_price_excel_file + + + + + + + + + + insert into prc_price_excel_file ( + excel_type, + effective_time, + file_name, + file_path, + file_size, + content_type, + file_extension, + oss_id, + sheet_names, + remark, + created_by, + create_time + ) values ( + #{excelType}, + #{effectiveTime}, + #{fileName}, + #{filePath}, + #{fileSize}, + #{contentType}, + #{fileExtension}, + #{ossId}, + #{sheetNames}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update prc_price_excel_file + + excel_type = #{excelType}, + effective_time = #{effectiveTime}, + file_name = #{fileName}, + file_path = #{filePath}, + file_size = #{fileSize}, + content_type = #{contentType}, + file_extension = #{fileExtension}, + oss_id = #{ossId}, + sheet_names = #{sheetNames}, + remark = #{remark}, + updated_by = #{updateBy}, + update_time = sysdate() + + where pef_id = #{pefId} + + + + delete from prc_price_excel_file where pef_id in + + #{pefId} + + + + diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetProductMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetProductMapper.xml index 382778e1..2b3a0495 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetProductMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcPriceSetProductMapper.xml @@ -17,13 +17,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + select psp_id, price_set_id, psv_id, product_code, product_name, service_level, cargo_attr, - dest_region, update_note, tax_mode, source_product_id, source_sheet, create_time, update_time + dest_region, update_note, tax_mode, source_product_id, source_sheet, pef_id, + create_time, update_time from prc_price_set_product @@ -52,10 +54,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into prc_price_set_product ( price_set_id, psv_id, product_code, product_name, service_level, cargo_attr, - dest_region, update_note, tax_mode, source_product_id, source_sheet, create_time + dest_region, update_note, tax_mode, source_product_id, source_sheet, pef_id, create_time ) values ( #{priceSetId}, #{psvId}, #{productCode}, #{productName}, #{serviceLevel}, #{cargoAttr}, - #{destRegion}, #{updateNote}, #{taxMode}, #{sourceProductId}, #{sourceSheet}, sysdate() + #{destRegion}, #{updateNote}, #{taxMode}, #{sourceProductId}, #{sourceSheet}, #{pefId}, sysdate() ) @@ -70,6 +72,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" tax_mode = #{taxMode}, source_product_id = #{sourceProductId}, source_sheet = #{sourceSheet}, + pef_id = #{pefId}, update_time = sysdate() where psp_id = #{pspId} @@ -88,11 +91,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" insert into prc_price_set_product ( price_set_id, psv_id, product_code, product_name, service_level, cargo_attr, - dest_region, update_note, tax_mode, source_product_id, source_sheet, create_time + dest_region, update_note, tax_mode, source_product_id, source_sheet, pef_id, create_time ) select price_set_id, #{newPsvId}, product_code, product_name, service_level, cargo_attr, - dest_region, update_note, tax_mode, source_product_id, source_sheet, sysdate() + dest_region, update_note, tax_mode, source_product_id, source_sheet, pef_id, sysdate() from prc_price_set_product where psv_id = #{srcPsvId} order by psp_id asc @@ -159,4 +162,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and (d.psp_id is null or d.psp_id <> sp.psp_id) + + update prc_price_set_product + set pef_id = #{pefId}, update_time = sysdate() + where psv_id = #{psvId} + + and product_code in + + #{code} + + + + diff --git a/bms-back/sql/price/prc_price_excel_file.sql b/bms-back/sql/price/prc_price_excel_file.sql index 4245c594..033e5101 100644 --- a/bms-back/sql/price/prc_price_excel_file.sql +++ b/bms-back/sql/price/prc_price_excel_file.sql @@ -1,26 +1,26 @@ -- ------------------------------------------------------------ --- 导入价表 Excel 附件记录(仅建表/字段,待确认后再接代码) +-- 导入价表 Excel 附件记录(建表/字段) -- 用途:产品价/红线价/销售价 Excel 导入时落库文件元数据 --- 关联:product.pef_id、prc_price_set_version.pef_id -> prc_price_excel_file.pef_id +-- 关联:product.pef_id、prc_price_set_product.pef_id -> prc_price_excel_file.pef_id -- ------------------------------------------------------------ -- 1) 主表:导入价表 Excel 附件 CREATE TABLE IF NOT EXISTS prc_price_excel_file ( - pef_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '1 主键', - excel_type varchar(20) NOT NULL COMMENT '2 价表类型 PRODUCT=产品价 REDLINE=红线价 SALES=销售价', - effective_time datetime NOT NULL COMMENT '3 生效时间(导入时选择/解析)', - file_name varchar(255) NOT NULL COMMENT '4 文件原始名称', - file_path varchar(500) NOT NULL COMMENT '5 文件存储路径(相对路径或完整URL)', - file_size bigint(20) DEFAULT 0 COMMENT '6 文件大小(字节)', - content_type varchar(100) DEFAULT '' COMMENT '7 MIME类型,如 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - file_extension varchar(20) DEFAULT '' COMMENT '8 扩展名,如 xlsx/xls', - oss_id varchar(64) DEFAULT NULL COMMENT '9 OSS/对象存储ID(可空)', - sheet_names varchar(1000) DEFAULT NULL COMMENT '10 导入涉及的 sheet 名(多个逗号分隔,可空)', - remark varchar(500) DEFAULT NULL COMMENT '11 备注', - created_by varchar(64) DEFAULT '' COMMENT '12 创建人', - create_time datetime DEFAULT NULL COMMENT '13 创建时间', - updated_by varchar(64) DEFAULT '' COMMENT '14 更新人', - update_time datetime DEFAULT NULL COMMENT '15 更新时间', + pef_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', + excel_type varchar(20) NOT NULL COMMENT '价表类型 PRODUCT=产品价 REDLINE=红线价 SALES=销售价', + effective_time datetime NOT NULL COMMENT '生效时间(导入时选择/解析)', + file_name varchar(255) NOT NULL COMMENT '文件原始名称', + file_path varchar(500) NOT NULL COMMENT '文件存储路径(相对路径或完整URL)', + file_size bigint(20) DEFAULT 0 COMMENT '文件大小(字节)', + content_type varchar(100) DEFAULT '' COMMENT 'MIME类型', + file_extension varchar(20) DEFAULT '' COMMENT '扩展名,如 xlsx/xls', + oss_id varchar(64) DEFAULT NULL COMMENT 'OSS/对象存储ID(可空)', + sheet_names varchar(1000) DEFAULT NULL COMMENT '导入涉及的 sheet 名(多个逗号分隔,可空)', + remark varchar(500) DEFAULT NULL COMMENT '备注', + created_by varchar(64) DEFAULT '' COMMENT '创建人', + create_time datetime DEFAULT NULL COMMENT '创建时间', + updated_by varchar(64) DEFAULT '' COMMENT '更新人', + update_time datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (pef_id), KEY idx_pef_type_eff (excel_type, effective_time), KEY idx_pef_create_time (create_time) @@ -31,7 +31,7 @@ ALTER TABLE product ADD COLUMN pef_id bigint(20) DEFAULT NULL COMMENT '关联导入价表Excel附件 prc_price_excel_file.pef_id' AFTER source_sheet, ADD KEY idx_product_pef_id (pef_id); --- 3) 套餐版本表:关联导入附件(销售价 Excel 建价) -ALTER TABLE prc_price_set_version - ADD COLUMN pef_id bigint(20) DEFAULT NULL COMMENT '关联导入价表Excel附件 prc_price_excel_file.pef_id' AFTER price_build_mode, - ADD KEY idx_psv_pef_id (pef_id); +-- 3) 套餐产品表:关联导入附件(销售价 Excel 建价) +ALTER TABLE prc_price_set_product + ADD COLUMN pef_id bigint(20) DEFAULT NULL COMMENT '关联导入价表Excel附件 prc_price_excel_file.pef_id' AFTER source_sheet, + ADD KEY idx_psp_pef_id (pef_id); diff --git a/bms-front/src/api/price/product.js b/bms-front/src/api/price/product.js index c798bc96..a90b5634 100644 --- a/bms-front/src/api/price/product.js +++ b/bms-front/src/api/price/product.js @@ -167,3 +167,11 @@ export async function importProductCountryExcelAsync(file, countryCode, effectiv } return pollProductImportTask(taskId, onProgress) } + +/** List imported Excel attachments for product_code */ +export function listProductExcelFiles(productCode) { + return request({ + url: '/price/product/' + encodeURIComponent(productCode) + '/excelFiles', + method: 'get' + }) +} diff --git a/bms-front/src/views/price/basic/product/components/ProductExcelFileDrawer.vue b/bms-front/src/views/price/basic/product/components/ProductExcelFileDrawer.vue new file mode 100644 index 00000000..ac329c47 --- /dev/null +++ b/bms-front/src/views/price/basic/product/components/ProductExcelFileDrawer.vue @@ -0,0 +1,65 @@ + + + diff --git a/bms-front/src/views/price/basic/product/components/ProductExpiredDrawer.vue b/bms-front/src/views/price/basic/product/components/ProductExpiredDrawer.vue index 44310150..8631cfeb 100644 --- a/bms-front/src/views/price/basic/product/components/ProductExpiredDrawer.vue +++ b/bms-front/src/views/price/basic/product/components/ProductExpiredDrawer.vue @@ -52,8 +52,16 @@ - +