| | |
| | | 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; |
| | |
| | | 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; |
| | | |
| | |
| | | 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; |
| | |
| | | // 客户数据 - 参考 listPage 查询私海客户(type = 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()))); |
| | |
| | | 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() |
| | |
| | | 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); |
| | |
| | | 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); |
| | | // 产品编码依赖自增的规格ID,插入后再回填 |
| | | 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("产品档案中不存在顶级节点:" + 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 |
| | |
| | | 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(); |