From fcccabc2af00c6f433210945566e24891efaf8fa Mon Sep 17 00:00:00 2001
From: yuan <123@>
Date: 星期一, 31 八月 2026 19:12:35 +0800
Subject: [PATCH] fix: 优化销售导入功能
---
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java | 261 +++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 217 insertions(+), 44 deletions(-)
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index 7b71ef3..ff1ef13 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -15,9 +15,13 @@
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.mapper.CustomerMapper;
+import com.ruoyi.basic.mapper.CustomerUserMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.basic.pojo.CustomerUser;
+import com.ruoyi.basic.pojo.Product;
+import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
@@ -62,6 +66,8 @@
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionAspectSupport;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -91,8 +97,13 @@
private static final String LOCK_PREFIX = "contract_no_lock:";
private static final long LOCK_WAIT_TIMEOUT = 10; // 閿佺瓑寰呰秴鏃舵椂闂达紙绉掞級
private static final long LOCK_EXPIRE_TIME = 30; // 閿佽嚜鍔ㄨ繃鏈熸椂闂达紙绉掞級
+ /** 瀵煎叆鑷姩鏂板瀹㈡埛鏃讹紝闄ゅ鎴峰悕绉板鍏朵綑淇℃伅鐨勯粯璁ゅ~鍏呭�� */
+ private static final String IMPORT_DEFAULT_VALUE = "/";
+ /** 瀵煎叆鑷姩鏂板浜у搧澶х被鏃舵寕杞界殑椤剁骇浜у搧鑺傜偣鍚嶇О */
+ private static final String FINISHED_PRODUCT_NAME = "鎴愬搧";
private final SalesLedgerMapper salesLedgerMapper;
private final CustomerMapper customerMapper;
+ private final CustomerUserMapper customerUserMapper;
private final SalesLedgerProductMapper salesLedgerProductMapper;
private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl;
private final CommonFileMapper commonFileMapper;
@@ -333,22 +344,11 @@
if (CollectionUtils.isEmpty(salesLedgerProductImportDtoList)) return AjaxResult.error("閿�鍞骇鍝佹暟鎹负绌猴紒");
// 瀹㈡埛鏁版嵁 - 鍙傝�� listPage 鏌ヨ绉佹捣瀹㈡埛锛坱ype = 0锛�
// type = 0锛堢娴峰鎴凤級鎴栬�� type = 1锛堝叕娴峰鎴凤級涓斿凡琚垎閰嶏紝骞朵笖鏄嚜宸遍鐢ㄣ�佽嚜宸卞垱寤烘垨鑰呭叡浜粰鑷繁鐨勫鎴�
+ // 瀹㈡埛涓嶅瓨鍦ㄦ椂鑷姩鏂板骞舵寕鍒拌嚜宸卞悕涓嬶紝鍏捣瀹㈡埛鑷姩棰嗙敤/鍏变韩缁欒嚜宸�
Long loginUserId = loginUser.getUser().getUserId();
- List<Customer> customers = customerMapper.selectList(new QueryWrapper<Customer>()
- .in("customer_name", salesLedgerImportDtoList.stream()
- .map(SalesLedgerImportDto::getCustomerName).collect(Collectors.toList()))
- .and(wrapper -> wrapper.eq("type", 0)
- .or(wrapper2 -> wrapper2.eq("type", 1).eq("is_assigned", 1)))
- .and(wrapper -> wrapper.eq("usage_user", loginUserId)
- .or(wrapper2 -> wrapper2.eq("create_user", loginUserId)
- .or(wrapper3 -> wrapper3.exists("select 1 from customer_user cu where cu.customer_id = customer.id and cu.user_id = " + loginUserId)))));
-// // 瑙勬牸鍨嬪彿鏁版嵁
-// List<ProductModel> productModels = productModelMapper.selectList(new LambdaQueryWrapper<ProductModel>().in(ProductModel::getModel,
-// salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getSpecificationModel).collect(Collectors.toList())));
-// // 浜у搧澶х被鏁版嵁
-// List<Product> productList = productMapper.selectList(new LambdaQueryWrapper<Product>().in(Product::getProductName,
-// salesLedgerProductImportDtoList.stream().map(SalesLedgerImportDto::getProductCategory).collect(Collectors.toList())));
- List<Map<String, Object>> list = productModelMapper.getProductAndModelList();
+ Map<String, Customer> customerCache = new HashMap<>();
+ // 浜у搧澶х被銆佽鏍煎瀷鍙风紦瀛橈紝key = 浜у搧澶х被 + | + 瑙勬牸鍨嬪彿锛屼笉瀛樺湪鏃惰嚜鍔ㄦ柊澧�
+ Map<String, ProductModel> productModelCache = new HashMap<>();
// 褰曞叆浜烘暟鎹�
List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getNickName,
salesLedgerImportDtoList.stream().map(SalesLedgerImportDto::getEntryPerson).collect(Collectors.toList())));
@@ -370,15 +370,11 @@
BeanUtils.copyProperties(salesLedgerImportDto, salesLedger);
salesLedger.setExecutionDate(DateUtils.toLocalDate(salesLedgerImportDto.getExecutionDate()));
salesLedger.setDeliveryDate(DateUtils.toLocalDate(salesLedgerImportDto.getDeliveryDate()));
- // 閫氳繃瀹㈡埛鍚嶇О鏌ヨ瀹㈡埛ID锛屽鎴峰悎鍚屽彿
- Optional<Customer> customerOptional = customers.stream()
- .filter(customer -> customer.getCustomerName().equals(salesLedger.getCustomerName()))
- .findFirst();
- if (customerOptional.isEmpty()) {
- throw new RuntimeException("瀹㈡埛:" + salesLedger.getCustomerName() + "涓嶅瓨鍦紒鎴栬�呴潪绉佹捣鐢ㄦ埛");
- }
- salesLedger.setCustomerId(customerOptional.get().getId());
- salesLedger.setCustomerContractNo(customerOptional.get().getTaxpayerIdentificationNumber());
+ // 閫氳繃瀹㈡埛鍚嶇О鏌ヨ瀹㈡埛ID锛屽鎴峰悎鍚屽彿锛屽鎴蜂笉瀛樺湪鏃惰嚜鍔ㄦ柊澧�
+ Customer customer = resolveImportCustomer(salesLedger.getCustomerName(), loginUserId,
+ loginUser.getNickName(), customerCache);
+ salesLedger.setCustomerId(customer.getId());
+ salesLedger.setCustomerContractNo(customer.getTaxpayerIdentificationNumber());
Long aLong = sysUsers.stream()
.filter(sysUser -> sysUser.getNickName().equals(salesLedger.getEntryPerson()))
.findFirst()
@@ -405,27 +401,17 @@
salesLedgerProduct.setSalesLedgerId(salesLedger.getId());
salesLedgerProduct.setType(1);
// 璁$畻涓嶅惈绋庢�讳环
- salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 2, RoundingMode.HALF_UP));
- // 鏍¢獙浜у搧瑙勬牸鏄惁瀛樺湪
- Optional<Map<String, Object>> productModelOptional = list.stream()
- .filter(map -> Objects.equals(map.get("productName"), salesLedgerProduct.getProductCategory()) && Objects.equals(map.get("model"), salesLedgerProduct.getSpecificationModel()))
- .findFirst();
- if (productModelOptional.isEmpty()) {
- throw new RuntimeException("浜у搧澶х被:" + salesLedgerProduct.getProductCategory() + ",瑙勬牸鍨嬪彿:" + salesLedgerProduct.getSpecificationModel() + "涓嶅瓨鍦紒");
+ salesLedgerProduct.setTaxExclusiveTotalPrice(salesLedgerProduct.getTaxInclusiveTotalPrice().divide(new BigDecimal(1).add(salesLedgerProduct.getTaxRate().divide(new BigDecimal(100))), 6, RoundingMode.HALF_UP));
+ // 鏍¢獙浜у搧瑙勬牸鏄惁瀛樺湪锛屼骇鍝佸ぇ绫汇�佽鏍煎瀷鍙蜂笉瀛樺湪鏃惰嚜鍔ㄦ柊澧�
+ ProductModel productModel = resolveImportProductModel(salesLedgerProduct.getProductCategory(),
+ salesLedgerProduct.getSpecificationModel(), salesLedgerProduct.getUnit(), productModelCache);
+ salesLedgerProduct.setProductModelId(productModel.getId());
+ salesLedgerProduct.setProductId(productModel.getProductId());
+ // 瑙勬牸鍨嬪彿涓虹┖鏃跺彇浜у搧澶х被锛屽崟浣嶄负绌烘椂鍙栬鏍煎瀷鍙蜂笂鐨勫崟浣�
+ salesLedgerProduct.setSpecificationModel(productModel.getModel());
+ if (StringUtils.isEmpty(salesLedgerProduct.getUnit())) {
+ salesLedgerProduct.setUnit(productModel.getUnit());
}
- Map<String, Object> productModelMap = productModelOptional.get();
- salesLedgerProduct.setProductModelId(Long.parseLong(productModelMap.get("modelId").toString()));
- salesLedgerProduct.setProductId(Long.parseLong(productModelMap.get("id").toString()));
-// salesLedgerProduct.setProductId(productList.stream()
-// .filter(product -> product.getProductName().equals(salesLedgerProduct.getProductCategory()))
-// .findFirst()
-// .map(Product::getId)
-// .orElse(null));
-// salesLedgerProduct.setProductModelId(productModels.stream()
-// .filter(productModel -> productModel.getModel().equals(salesLedgerProduct.getSpecificationModel()))
-// .findFirst()
-// .map(ProductModel::getId)
-// .orElse(null));
salesLedgerProduct.setRegister(loginUser.getNickName());
salesLedgerProduct.setRegisterDate(LocalDateTime.now());
salesLedgerProduct.setApproveStatus(0);
@@ -439,8 +425,191 @@
return AjaxResult.success("瀵煎叆鎴愬姛");
} catch (Exception e) {
e.printStackTrace();
+ // 瀵煎叆杩囩▼涓細鑷姩鏂板瀹㈡埛銆佷骇鍝佸ぇ绫汇�佽鏍煎瀷鍙凤紝澶辫触鏃堕渶瑕佸洖婊氾紝閬垮厤娈嬬暀鑴忔暟鎹�
+ if (TransactionSynchronizationManager.isActualTransactionActive()) {
+ TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+ }
return AjaxResult.error("瀵煎叆澶辫触锛�" + e.getMessage());
}
+ }
+
+ /**
+ * 瀵煎叆鏃惰幏鍙栧鎴凤紝瀹㈡埛涓嶅瓨鍦ㄥ垯鑷姩鏂板骞舵寕鍒板綋鍓嶇櫥褰曚汉鍚嶄笅
+ * <p>
+ * 1銆佸綋鍓嶇櫥褰曚汉鍙敤鐨勫鎴凤紙绉佹捣锛屾垨鍏捣宸插垎閰嶄笖鑷繁棰嗙敤/鍒涘缓/鍏变韩锛夛細鐩存帴浣跨敤锛�
+ * 2銆佸悓鍚嶅叕娴峰鎴锋湭鍒嗛厤锛氶鐢ㄥ埌鑷繁鍚嶄笅锛�
+ * 3銆佸悓鍚嶅叕娴峰鎴峰凡鍒嗛厤缁欏埆浜猴細鍏变韩缁欒嚜宸憋紱
+ * 4銆佸悓鍚嶅鎴峰湪鍒汉绉佹捣锛氭姤閿欙紝涓嶆柊寤猴紱
+ * 5銆佸鎴蜂笉瀛樺湪锛氭柊澧炵娴峰鎴锋寕鍒拌嚜宸卞悕涓嬶紝绾崇◣浜鸿瘑鍒彿銆佺數璇濈瓑淇℃伅濉� "/"銆�
+ */
+ private Customer resolveImportCustomer(String customerName, Long loginUserId, String nickName,
+ Map<String, Customer> customerCache) {
+ String name = StringUtils.trim(customerName);
+ if (StringUtils.isEmpty(name)) {
+ throw new RuntimeException("瀹㈡埛鍚嶇О涓嶈兘涓虹┖锛�");
+ }
+ Customer cachedCustomer = customerCache.get(name);
+ if (cachedCustomer != null) {
+ return cachedCustomer;
+ }
+ // 褰撳墠鐧诲綍浜哄彲鐢ㄧ殑瀹㈡埛
+ Customer customer = customerMapper.selectOne(new QueryWrapper<Customer>()
+ .eq("customer_name", name)
+ .and(wrapper -> wrapper.eq("type", 0)
+ .or(wrapper2 -> wrapper2.eq("type", 1).eq("is_assigned", 1)))
+ .and(wrapper -> wrapper.eq("usage_user", loginUserId)
+ .or(wrapper2 -> wrapper2.eq("create_user", loginUserId)
+ .or(wrapper3 -> wrapper3.exists("select 1 from customer_user cu where cu.customer_id = customer.id and cu.user_id = " + loginUserId))))
+ .last("limit 1"));
+ if (customer == null) {
+ customer = claimOrCreateImportCustomer(name, loginUserId, nickName);
+ }
+ customerCache.put(name, customer);
+ return customer;
+ }
+
+ /**
+ * 鍚屽悕瀹㈡埛涓嶅睘浜庡綋鍓嶇櫥褰曚汉鏃剁殑澶勭悊锛氬叕娴锋湭鍒嗛厤鐨勯鐢ㄥ埌鑷繁鍚嶄笅锛屽叕娴峰凡鍒嗛厤鐨勫叡浜粰鑷繁锛�
+ * 鍦ㄥ埆浜虹娴风殑鐩存帴鎶ラ敊锛岄兘涓嶅瓨鍦ㄥ垯鏂板瀹㈡埛
+ */
+ private Customer claimOrCreateImportCustomer(String customerName, Long loginUserId, String nickName) {
+ List<Customer> sameNameCustomers = customerMapper.selectList(new QueryWrapper<Customer>()
+ .eq("customer_name", customerName)
+ .orderByAsc("id"));
+ // 鍏捣鏈垎閰嶅鎴凤紝棰嗙敤鍒拌嚜宸卞悕涓�
+ Optional<Customer> publicUnassigned = sameNameCustomers.stream()
+ .filter(item -> Objects.equals(item.getType(), 1)
+ && (item.getIsAssigned() == null || item.getIsAssigned() == 0))
+ .findFirst();
+ if (publicUnassigned.isPresent()) {
+ Customer customer = publicUnassigned.get();
+ customer.setIsAssigned(1);
+ customer.setUsageStatus(1L);
+ customer.setUsageUser(loginUserId);
+ customerMapper.updateById(customer);
+ return customer;
+ }
+ // 鍏捣宸插垎閰嶇粰鍒汉鐨勫鎴凤紝鍏变韩缁欒嚜宸�
+ Optional<Customer> publicAssigned = sameNameCustomers.stream()
+ .filter(item -> Objects.equals(item.getType(), 1) && Objects.equals(item.getIsAssigned(), 1))
+ .findFirst();
+ if (publicAssigned.isPresent()) {
+ Customer customer = publicAssigned.get();
+ CustomerUser customerUser = new CustomerUser();
+ customerUser.setCustomerId(customer.getId());
+ customerUser.setUserId(loginUserId);
+ customerUserMapper.insert(customerUser);
+ return customer;
+ }
+ // 鍓╀笅鐨勯兘鏄埆浜虹殑绉佹捣瀹㈡埛
+ if (!sameNameCustomers.isEmpty()) {
+ throw new RuntimeException("瀹㈡埛:" + customerName + "宸插湪鍏朵粬浜虹娴蜂腑锛屾棤娉曞鍏ワ紒");
+ }
+ // 瀹㈡埛涓嶅瓨鍦紝鏂板绉佹捣瀹㈡埛骞舵寕鍒拌嚜宸卞悕涓�
+ Customer customer = new Customer();
+ customer.setCustomerName(customerName);
+ customer.setCustomerType(IMPORT_DEFAULT_VALUE);
+ customer.setTaxpayerIdentificationNumber(IMPORT_DEFAULT_VALUE);
+ customer.setCompanyAddress(IMPORT_DEFAULT_VALUE);
+ customer.setCompanyPhone(IMPORT_DEFAULT_VALUE);
+ customer.setContactPerson(IMPORT_DEFAULT_VALUE);
+ customer.setContactPhone(IMPORT_DEFAULT_VALUE);
+ customer.setBasicBankAccount(IMPORT_DEFAULT_VALUE);
+ customer.setBankAccount(IMPORT_DEFAULT_VALUE);
+ customer.setBankCode(IMPORT_DEFAULT_VALUE);
+ customer.setMaintainer(nickName);
+ customer.setMaintenanceTime(new Date());
+ // 绉佹捣瀹㈡埛锛岄鐢ㄤ汉涓哄綋鍓嶇櫥褰曚汉
+ customer.setType(0);
+ customer.setIsAssigned(0);
+ customer.setUsageStatus(1L);
+ customer.setUsageUser(loginUserId);
+ customerMapper.insert(customer);
+ return customer;
+ }
+
+ /**
+ * 瀵煎叆鏃惰幏鍙栬鏍煎瀷鍙凤紝浜у搧澶х被銆佽鏍煎瀷鍙蜂笉瀛樺湪鍒欒嚜鍔ㄦ柊澧�
+ * <p>
+ * 瑙勬牸鍨嬪彿涓虹┖鏃跺彇浜у搧澶х被浣滀负瑙勬牸鍨嬪彿锛涗骇鍝佸ぇ绫讳笉瀛樺湪鏃舵寕鍒伴《绾�"鎴愬搧"鑺傜偣涓嬶紱
+ * 鏂板瑙勬牸鍨嬪彿鏃舵寜"瑙勬牸鍨嬪彿 + 瑙勬牸ID + _ + 涓変綅娴佹按鍙�"鐢熸垚浜у搧缂栫爜
+ */
+ private ProductModel resolveImportProductModel(String productCategory, String specificationModel, String unit,
+ Map<String, ProductModel> productModelCache) {
+ String category = StringUtils.trim(productCategory);
+ if (StringUtils.isEmpty(category)) {
+ throw new RuntimeException("浜у搧澶х被涓嶈兘涓虹┖锛�");
+ }
+ // 瑙勬牸鍨嬪彿涓虹┖鏃跺彇浜у搧澶х被褰撲骇鍝佽鏍�
+ String model = StringUtils.trim(specificationModel);
+ if (StringUtils.isEmpty(model)) {
+ model = category;
+ }
+ String cacheKey = category + "|" + model;
+ ProductModel cachedProductModel = productModelCache.get(cacheKey);
+ if (cachedProductModel != null) {
+ return cachedProductModel;
+ }
+ // 浜у搧澶х被涓嶅瓨鍦ㄥ垯鏂板鍒伴《绾�"鎴愬搧"鑺傜偣涓�
+ Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+ .eq(Product::getProductName, category)
+ .orderByAsc(Product::getId)
+ .last("limit 1"));
+ if (product == null) {
+ product = new Product();
+ product.setParentId(getFinishedProductId());
+ product.setProductName(category);
+ productMapper.insert(product);
+ }
+ // 瑙勬牸鍨嬪彿涓嶅瓨鍦ㄥ垯鏂板
+ ProductModel productModel = productModelMapper.selectOne(new LambdaQueryWrapper<ProductModel>()
+ .eq(ProductModel::getProductId, product.getId())
+ .eq(ProductModel::getModel, model)
+ .orderByAsc(ProductModel::getId)
+ .last("limit 1"));
+ if (productModel == null) {
+ productModel = new ProductModel();
+ productModel.setProductId(product.getId());
+ productModel.setModel(model);
+ productModel.setUnit(StringUtils.isEmpty(unit) ? IMPORT_DEFAULT_VALUE : StringUtils.trim(unit));
+ productModelMapper.insert(productModel);
+ // 浜у搧缂栫爜渚濊禆鑷鐨勮鏍糏D锛屾彃鍏ュ悗鍐嶅洖濉�
+ productModel.setProductCode(generateProductCode(productModel.getModel(), productModel.getId()));
+ productModelMapper.updateById(productModel);
+ }
+ productModelCache.put(cacheKey, productModel);
+ return productModel;
+ }
+
+ /**
+ * 鏌ヨ椤剁骇"鎴愬搧"浜у搧鑺傜偣锛堜骇鍝佸悕绉颁负鎴愬搧涓旂埗ID涓虹┖锛�
+ */
+ private Long getFinishedProductId() {
+ Product finishedProduct = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+ .eq(Product::getProductName, FINISHED_PRODUCT_NAME)
+ .isNull(Product::getParentId)
+ .orderByAsc(Product::getId)
+ .last("limit 1"));
+ if (finishedProduct == null) {
+ throw new RuntimeException("浜у搧妗f涓笉瀛樺湪椤剁骇鑺傜偣:" + FINISHED_PRODUCT_NAME + "锛屾棤娉曡嚜鍔ㄦ柊澧炰骇鍝佸ぇ绫伙紒");
+ }
+ return finishedProduct.getId();
+ }
+
+ /**
+ * 鐢熸垚浜у搧缂栫爜锛岃鍒欙細瑙勬牸鍨嬪彿 + 瑙勬牸ID + _ + 涓変綅娴佹按鍙凤紝缂栫爜宸插瓨鍦ㄦ椂娴佹按鍙烽�掑
+ */
+ private String generateProductCode(String model, Long productModelId) {
+ String prefix = model + productModelId + "_";
+ for (int sequence = 1; sequence < 1000; sequence++) {
+ String productCode = prefix + String.format("%03d", sequence);
+ Long count = productModelMapper.selectCount(new LambdaQueryWrapper<ProductModel>()
+ .eq(ProductModel::getProductCode, productCode));
+ if (count == null || count == 0) {
+ return productCode;
+ }
+ }
+ throw new RuntimeException("瑙勬牸鍨嬪彿:" + model + "鐨勪骇鍝佺紪鐮佹祦姘村彿宸茶秴鍑鸿寖鍥达紒");
}
@Override
@@ -571,6 +740,10 @@
SalesLedger salesLedger = convertToEntity(salesLedgerDto);
salesLedger.setCustomerName(customer.getCustomerName());
salesLedger.setTenantId(customer.getTenantId());
+ // 褰曞叆浜猴細濡傛灉鏈紶鍒欎娇鐢ㄥ綋鍓嶇櫥褰曠敤鎴�
+ if (StringUtils.isEmpty(salesLedger.getEntryPerson())) {
+ salesLedger.setEntryPerson(String.valueOf(SecurityUtils.getUserId()));
+ }
// 3. 鏂板鎴栨洿鏂颁富琛�
if (salesLedger.getId() == null) {
String contractNo = salesLedger.getSalesContractNo();
--
Gitblit v1.9.3