From 8ed97fed4a47c1e46fb533d34ecf50adf0af9ee9 Mon Sep 17 00:00:00 2001 From: wuweihua <88888888> Date: Fri, 30 Oct 2026 11:07:51 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/price/PriceSetController.java | 11 ++ .../main/resources/i18n/messages.properties | 2 + .../impl/BmsCustomerPriceSetServiceImpl.java | 8 - .../domain/PrcCustomerPriceSetBatch.java | 30 +++ .../price/service/IPrcPriceSetService.java | 4 + .../service/impl/PrcPriceSetServiceImpl.java | 136 ++++++++++++- .../price/PrcCustomerPriceSetMapper.xml | 1 + bms-front/src/api/price/set.js | 8 + .../components/detail/SetCustomerDialog.vue | 181 +++++++++++------- .../components/detail/SetCustomerTable.vue | 52 ++++- bms-front/src/views/price/set/detail.vue | 2 +- 11 files changed, 355 insertions(+), 80 deletions(-) create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java index de7c9e6c..9bad75c1 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/price/PriceSetController.java @@ -27,6 +27,7 @@ import com.bms.common.enums.BusinessType; import com.bms.common.utils.MessageUtils; import com.bms.common.utils.StringUtils; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcCustomerPriceSetBatch; import com.bms.price.domain.PrcPriceExcelFile; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetDetail; @@ -378,6 +379,16 @@ public class PriceSetController extends BaseController return toAjax(priceSetService.insertCustomer(customer)); } + @PreAuthorize("@ss.hasPermi('price:set:edit')") + @Log(title = "套餐客户批量绑定", businessType = BusinessType.INSERT) + @PostMapping("/customer/batch") + public AjaxResult batchAddCustomer(@RequestBody PrcCustomerPriceSetBatch batch) + { + batch.setCreateBy(getUsername()); + batch.setUpdateBy(getUsername()); + return success(priceSetService.batchInsertCustomers(batch)); + } + @PreAuthorize("@ss.hasPermi('price:set:edit')") @Log(title = "套餐客户绑定", businessType = BusinessType.UPDATE) @PutMapping("/customer") diff --git a/bms-back/bms-admin/src/main/resources/i18n/messages.properties b/bms-back/bms-admin/src/main/resources/i18n/messages.properties index ce897e9c..62b2bcf4 100644 --- a/bms-back/bms-admin/src/main/resources/i18n/messages.properties +++ b/bms-back/bms-admin/src/main/resources/i18n/messages.properties @@ -230,3 +230,5 @@ price.bill.export.tooMany=匹配订单超过上限 {0} 条,请缩小筛选范 price.bill.export.noMatch=未查询到匹配订单,无法导出 price.bill.export.notReady=导出文件尚未生成或已失败 price.bill.export.fileMissing=导出文件不存在或路径非法 +price.set.customer.batch.empty=请选择要绑定的客户 +price.set.customer.batch.done=批量绑定完成:共 {0} 个客户,新绑定 {1} 个,更换 {2} 个,跳过 {3} 个 diff --git a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java index 834d556c..049278e2 100644 --- a/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/customer/customerinfo/service/impl/BmsCustomerPriceSetServiceImpl.java @@ -69,10 +69,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi { throw new ServiceException("所选套餐不存在"); } - if (!"E".equals(set.getStatus())) - { - throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用"); - } if (bind.getBindPriority() == null || bind.getBindPriority() < 1) { bind.setBindPriority(1); @@ -141,10 +137,6 @@ public class BmsCustomerPriceSetServiceImpl implements IBmsCustomerPriceSetServi { throw new ServiceException("所选套餐不存在"); } - if (!"E".equals(set.getStatus())) - { - throw new ServiceException("仅生效中的套餐允许绑定,当前套餐状态不可用"); - } Integer priority = (req.getBindPriority() == null || req.getBindPriority() < 1) ? 1 : req.getBindPriority(); boolean overwrite = Boolean.TRUE.equals(req.getOverwriteExisting()); diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java new file mode 100644 index 00000000..55df1b50 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcCustomerPriceSetBatch.java @@ -0,0 +1,30 @@ +package com.bms.price.domain; + +import java.util.Date; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.bms.common.core.domain.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Price-set detail batch bind customers. + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class PrcCustomerPriceSetBatch extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private Long priceSetId; + private Integer bindPriority; + @JsonFormat(pattern = "yyyy-MM-dd") + private Date prBegindate; + @JsonFormat(pattern = "yyyy-MM-dd") + private Date prEnddate; + private String priceSetNote; + /** Customers to bind (need customerCode; id/name optional). */ + private List customers; + /** true = move already-bound customers onto this set; false = skip them. */ + private Boolean overwriteExisting; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java index 698793f6..4b10014c 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcPriceSetService.java @@ -5,6 +5,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.Map; import com.bms.price.domain.PrcCustomerPriceSet; +import com.bms.price.domain.PrcCustomerPriceSetBatch; import com.bms.price.domain.PrcPriceSet; import com.bms.price.domain.PrcPriceSetCheckItem; import com.bms.price.domain.PrcPriceSetDetail; @@ -92,6 +93,9 @@ public interface IPrcPriceSetService public int insertCustomer(PrcCustomerPriceSet customer); + /** Batch bind customers to a price set; returns summary message. */ + public String batchInsertCustomers(PrcCustomerPriceSetBatch batch); + public int updateCustomer(PrcCustomerPriceSet customer); public int deleteCustomerByIds(Long[] customerPricesetIds); 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 59760922..2562f3ca 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 @@ -2432,6 +2432,128 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } } + @Override + @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class) + public String batchInsertCustomers(com.bms.price.domain.PrcCustomerPriceSetBatch batch) + { + if (batch == null || batch.getCustomers() == null || batch.getCustomers().isEmpty()) + { + throw new ServiceException(MessageUtils.message("price.set.customer.batch.empty")); + } + if (batch.getPriceSetId() == null) + { + throw new ServiceException(MessageUtils.message("price.set.customer.customerId.empty")); + } + PrcPriceSet db = requireSet(batch.getPriceSetId()); + assertChildEditable(db); + Integer priority = (batch.getBindPriority() == null || batch.getBindPriority().intValue() < 1) + ? Integer.valueOf(1) : batch.getBindPriority(); + boolean overwrite = Boolean.TRUE.equals(batch.getOverwriteExisting()); + + java.util.LinkedHashMap uniq = new java.util.LinkedHashMap(); + for (PrcCustomerPriceSet item : batch.getCustomers()) + { + if (item == null || StringUtils.isEmpty(item.getCustomerCode())) + { + continue; + } + String code = item.getCustomerCode().trim(); + if (!uniq.containsKey(code)) + { + uniq.put(code, item); + } + } + if (uniq.isEmpty()) + { + throw new ServiceException(MessageUtils.message("price.set.customer.batch.empty")); + } + + int inserted = 0; + int replaced = 0; + int skipped = 0; + java.util.List skippedNames = new java.util.ArrayList(); + for (PrcCustomerPriceSet item : uniq.values()) + { + PrcCustomerPriceSet query = new PrcCustomerPriceSet(); + query.setCustomerCode(item.getCustomerCode().trim()); + java.util.List found = customerMapper.selectPrcCustomerPriceSetList(query); + PrcCustomerPriceSet existing = (found == null || found.isEmpty()) ? null : found.get(0); + + PrcCustomerPriceSet row = new PrcCustomerPriceSet(); + row.setPriceSetId(batch.getPriceSetId()); + row.setCustomerId(item.getCustomerId()); + row.setCustomerCode(item.getCustomerCode().trim()); + row.setCustomerName(item.getCustomerName()); + row.setBindPriority(priority); + row.setPrBegindate(batch.getPrBegindate()); + row.setPrEnddate(batch.getPrEnddate()); + row.setPriceSetNote(batch.getPriceSetNote()); + row.setCreateBy(batch.getCreateBy()); + row.setUpdateBy(batch.getUpdateBy()); + + if (existing != null) + { + boolean sameSet = batch.getPriceSetId().equals(existing.getPriceSetId()); + if (sameSet) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + if (!overwrite) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + row.setCustomerPricesetId(existing.getCustomerPricesetId()); + if (row.getPrBegindate() == null) + { + row.setPrBegindate(existing.getPrBegindate()); + } + if (row.getPrEnddate() == null) + { + row.setPrEnddate(existing.getPrEnddate()); + } + validateCustomer(row, db); + customerMapper.updatePrcCustomerPriceSet(row); + replaced++; + } + else + { + validateCustomer(row, db); + try + { + customerMapper.insertPrcCustomerPriceSet(row); + } + catch (Exception e) + { + if (isCustomerUniqueViolation(e)) + { + skipped++; + skippedNames.add(StringUtils.isNotEmpty(item.getCustomerName()) + ? item.getCustomerName() : item.getCustomerCode()); + continue; + } + throw e; + } + inserted++; + } + } + + String msg = MessageUtils.message("price.set.customer.batch.done", + Integer.valueOf(uniq.size()), Integer.valueOf(inserted), + Integer.valueOf(replaced), Integer.valueOf(skipped)); + if (skipped > 0 && !overwrite) + { + String names = skippedNames.stream().limit(10).collect(java.util.stream.Collectors.joining(", ")); + msg = msg + " [" + names + (skippedNames.size() > 10 ? "..." : "") + "]"; + } + return msg; + } + @Override public int updateCustomer(PrcCustomerPriceSet customer) { @@ -2962,6 +3084,8 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService { throw new ServiceException(MessageUtils.message("price.set.customer.customerId.empty")); } + // UI no longer collects bind dates: fall back to set / version / today. + // Do not reject empty begin date — always fill a value. if (customer.getPrBegindate() == null) { customer.setPrBegindate(set.getEffectiveDate()); @@ -2972,11 +3096,17 @@ public class PrcPriceSetServiceImpl implements IPrcPriceSetService } if (customer.getPrBegindate() == null) { - throw new ServiceException(MessageUtils.message("price.set.customer.beginDate.empty")); + customer.setPrBegindate(DateUtils.getNowDate()); } - if (customer.getPrEnddate() != null && customer.getPrBegindate().after(customer.getPrEnddate())) + if (customer.getPrEnddate() == null) { - throw new ServiceException(MessageUtils.message("price.set.customer.date.invalid")); + customer.setPrEnddate(set.getExpireDate()); + } + // Auto-filled dates may conflict (e.g. set expire already past); keep bind, clear end. + if (customer.getPrEnddate() != null && customer.getPrBegindate() != null + && customer.getPrBegindate().after(customer.getPrEnddate())) + { + customer.setPrEnddate(null); } if (customer.getBindPriority() != null && customer.getBindPriority().intValue() < 1) { diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcCustomerPriceSetMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcCustomerPriceSetMapper.xml index e440e26b..c8998783 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcCustomerPriceSetMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcCustomerPriceSetMapper.xml @@ -141,6 +141,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" update prc_customer_price_set + price_set_id = #{priceSetId}, customer_id = #{customerId}, customer_code = #{customerCode}, customer_name = #{customerName}, diff --git a/bms-front/src/api/price/set.js b/bms-front/src/api/price/set.js index 66adfbc3..32ca3220 100644 --- a/bms-front/src/api/price/set.js +++ b/bms-front/src/api/price/set.js @@ -407,6 +407,14 @@ export function addSetCustomer(data) { }) } +export function batchAddSetCustomer(data) { + return request({ + url: '/price/set/customer/batch', + method: 'post', + data: data + }) +} + export function updateSetCustomer(data) { return request({ url: '/price/set/customer', diff --git a/bms-front/src/views/price/set/components/detail/SetCustomerDialog.vue b/bms-front/src/views/price/set/components/detail/SetCustomerDialog.vue index d04fe643..0c63d658 100644 --- a/bms-front/src/views/price/set/components/detail/SetCustomerDialog.vue +++ b/bms-front/src/views/price/set/components/detail/SetCustomerDialog.vue @@ -7,22 +7,47 @@ class="ps-dialog" > - + + - +
{{ form.customerCode || '未选择' }} {{ form.customerName || '选择客户后自动带出' }}
+ +
+ 请从上方下拉多选客户 +
+
+ {{ c.customerName || c.customerCode }} +
+
+ 数字越小优先级越高,1 为最高;同一客户命中多个套餐时按此排序 - - - - - - - - - - - - - -
{{ bindRangeHint }}
+ + + 覆盖已绑定其他套餐的客户(不勾选则跳过) + +