maven
3 天以前 366b67926809852c1090f14c5a24fe00109a81b5
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -2,27 +2,34 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.production.mapper.SalesLedgerSchedulingMapper;
import com.ruoyi.production.pojo.SalesLedgerScheduling;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.sales.dto.MonthlyAmountDto;
import com.ruoyi.sales.dto.SalesLedgerDto;
import com.ruoyi.sales.mapper.SalesLedgerFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerFile;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.*;
import com.ruoyi.sales.service.ISalesLedgerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
@@ -32,9 +39,13 @@
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.nio.file.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -58,9 +69,18 @@
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final SalesLedgerFileMapper salesLedgerFileMapper;
    private final CommonFileMapper commonFileMapper;
    private final TempFileMapper tempFileMapper;
    private final ReceiptPaymentMapper receiptPaymentMapper;
    private final InvoiceLedgerMapper invoiceLedgerMapper;
    private final SalesLedgerSchedulingMapper salesLedgerSchedulingMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -73,13 +93,10 @@
    @Override
    public List<SalesLedger> selectSalesLedgerList(SalesLedgerDto salesLedgerDto) {
        LambdaQueryWrapper<SalesLedger> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(salesLedgerDto.getCustomerName())) {
            queryWrapper.eq(SalesLedger::getCustomerName, salesLedgerDto.getCustomerName());
        }
        return salesLedgerMapper.selectList(queryWrapper);
        return salesLedgerMapper.selectSalesLedgerList(salesLedgerDto);
    }
    @Override
    public SalesLedgerDto getSalesLedgerWithProducts(SalesLedgerDto salesLedgerDto) {
        // 1. 查询主表
        SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerDto.getId());
@@ -90,12 +107,21 @@
        // 2. 查询子表
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId());
        productWrapper.eq(SalesLedgerProduct::getType, 1);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        for (SalesLedgerProduct product : products) {
            product.setOriginalNoInvoiceNum(product.getNoInvoiceNum());
            // 提供临时未开票数,未开票金额供前段计算
            product.setTempnoInvoiceAmount(product.getNoInvoiceAmount());
            product.setTempNoInvoiceNum(product.getNoInvoiceNum());
            product.setRegister(SecurityUtils.getLoginUser().getUser().getNickName());
            product.setRegisterDate(LocalDateTime.now());
        }
        // 3.查询上传文件
        LambdaQueryWrapper<SalesLedgerFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>();
        salesLedgerFileWrapper.eq(SalesLedgerFile::getLedgerId, salesLedger.getId());
        List<SalesLedgerFile> salesLedgerFiles = salesLedgerFileMapper.selectList(salesLedgerFileWrapper);
        LambdaQueryWrapper<CommonFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>();
        salesLedgerFileWrapper.eq(CommonFile::getCommonId, salesLedger.getId());
        List<CommonFile> salesLedgerFiles = commonFileMapper.selectList(salesLedgerFileWrapper);
        // 4. 转换 DTO
        SalesLedgerDto resultDto = new SalesLedgerDto();
@@ -111,7 +137,7 @@
    @Override
    public List<Map<String, Object>> getSalesNo() {
        LambdaQueryWrapper<SalesLedger> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(SalesLedger::getId, SalesLedger::getSalesContractNo);
        queryWrapper.select(SalesLedger::getId, SalesLedger::getSalesContractNo, SalesLedger::getProjectName);
        // 获取原始查询结果
        List<Map<String, Object>> result = salesLedgerMapper.selectMaps(queryWrapper);
@@ -122,6 +148,168 @@
                        entry -> underlineToCamel(entry.getKey()),
                        Map.Entry::getValue))
        ).collect(Collectors.toList());
    }
    @Override
    public BigDecimal getContractAmount() {
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        // 创建LambdaQueryWrapper
        LambdaQueryWrapper<SalesLedger> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 大于等于本月第一天
                .lt(SalesLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 小于下月第一天
        // 执行查询并计算总和
        List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(queryWrapper);
        BigDecimal totalContractAmount = salesLedgers.stream()
                .map(SalesLedger::getContractAmount)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return totalContractAmount;
    }
    @Override
    public List getTopFiveList() {
        // 查询原始数据
        LambdaQueryWrapper<SalesLedger> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(SalesLedger::getCustomerId,
                        SalesLedger::getCustomerName,
                        SalesLedger::getContractAmount)
                .orderByDesc(SalesLedger::getContractAmount);
        List<SalesLedger> records = salesLedgerMapper.selectList(queryWrapper);
        // 按客户ID分组并聚合金额
        Map<Long, GroupedCustomer> groupedMap = new LinkedHashMap<>(); // 使用LinkedHashMap保持排序
        for (SalesLedger record : records) {
            groupedMap.computeIfAbsent(record.getCustomerId(),
                            k -> new GroupedCustomer(record.getCustomerId(), record.getCustomerName()))
                    .addAmount(record.getContractAmount());
        }
        // 转换为结果列表并取前5
        return groupedMap.values().stream()
                .sorted(Comparator.comparing(GroupedCustomer::getTotalAmount).reversed())
                .limit(5)
                .map(customer -> {
                    Map<String, Object> result = new HashMap<>();
                    result.put("customerId", customer.getCustomerId());
                    result.put("customerName", customer.getCustomerName());
                    result.put("totalAmount", customer.getTotalAmount());
                    return result;
                })
                .collect(Collectors.toList());
    }
    @Override
    public List<MonthlyAmountDto> getAmountHalfYear(Integer type) {
        LocalDate now = LocalDate.now();
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 根据 type 确定查询的时间间隔(天数)和总查询天数
        int daysPerPeriod;
        int totalDays;
        switch (type) {
            case 1:
                daysPerPeriod = 5;   // 每5天查一次
                totalDays = 30;       // 6次 × 5天 = 30天
                break;
            case 2:
                daysPerPeriod = 15;    // 每15天查一次
                totalDays = 90;       // 6次 × 15天 = 90天
                break;
            case 3:
                daysPerPeriod = 30;   // 每30天查一次
                totalDays = 180;      // 6次 × 30天 = 180天
                break;
            default:
                throw new IllegalArgumentException("Invalid type value: " + type);
        }
        List<MonthlyAmountDto> result = new ArrayList<>();
        // 循环6次,每次查询一个时间段的数据
        for (int i = 0; i < 6; i++) {
            // 计算当前时间段的起始和结束时间
            LocalDateTime endTime = currentDateTime.minusDays(i * daysPerPeriod);
            LocalDateTime startTime = endTime.minusDays(daysPerPeriod);
            // 查询回款金额
            LambdaQueryWrapper<ReceiptPayment> receiptPaymentQuery = new LambdaQueryWrapper<>();
            receiptPaymentQuery
                    .ge(ReceiptPayment::getCreateTime, startTime)
                    .lt(ReceiptPayment::getCreateTime, endTime);
            List<ReceiptPayment> receiptPayments = receiptPaymentMapper.selectList(receiptPaymentQuery);
            // 查询开票金额
            LambdaQueryWrapper<InvoiceLedger> invoiceLedgerQuery = new LambdaQueryWrapper<>();
            invoiceLedgerQuery
                    .ge(InvoiceLedger::getCreateTime, startTime)
                    .lt(InvoiceLedger::getCreateTime, endTime);
            List<InvoiceLedger> invoiceLedgers = invoiceLedgerMapper.selectList(invoiceLedgerQuery);
            // 计算回款总额
            BigDecimal receiptAmount = receiptPayments.stream()
                    .map(ReceiptPayment::getReceiptPaymentAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 计算开票总额
            BigDecimal invoiceAmount = invoiceLedgers.stream()
                    .map(InvoiceLedger::getInvoiceTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 构造返回的 DTO
            MonthlyAmountDto dto = new MonthlyAmountDto();
            dto.setMonth(startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " ~ " +
                    endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            dto.setReceiptAmount(receiptAmount);
            dto.setInvoiceAmount(invoiceAmount);
            result.add(dto);
        }
        // 反转列表,使时间顺序从早到晚
        Collections.reverse(result);
        return result;
    }
    @Override
    public IPage<SalesLedger> selectSalesLedgerListPage(Page page, SalesLedgerDto salesLedgerDto) {
        return salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto);
    }
    // 内部类用于存储聚合结果
    private static class GroupedCustomer {
        private final Long customerId;
        private final String customerName;
        private BigDecimal totalAmount = BigDecimal.ZERO;
        public GroupedCustomer(Long customerId, String customerName) {
            this.customerId = customerId;
            this.customerName = customerName;
        }
        public void addAmount(BigDecimal amount) {
            if (amount != null) {
                this.totalAmount = this.totalAmount.add(amount);
            }
        }
        public Long getCustomerId() {
            return customerId;
        }
        public String getCustomerName() {
            return customerName;
        }
        public BigDecimal getTotalAmount() {
            return totalAmount;
        }
    }
    /**
@@ -152,15 +340,23 @@
        List<Long> idList = Arrays.stream(ids)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(idList)) {
            return 0;
        }
        // 生产订单有待排产数据,台账不可删除
        LambdaQueryWrapper<SalesLedgerScheduling> salesLedgerSchedulingLambdaQueryWrapper = new LambdaQueryWrapper<SalesLedgerScheduling>()
                .in(SalesLedgerScheduling::getSalesLedgerId, idList);
        if (salesLedgerSchedulingMapper.selectCount(salesLedgerSchedulingLambdaQueryWrapper) > 0) {
            throw new BaseException("有排产数据,不可删除");
        }
        // 1. 先删除子表数据
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.in(SalesLedgerProduct::getSalesLedgerId, idList);
        salesLedgerProductMapper.delete(productWrapper);
        // 删除生产订单数据
        LambdaQueryWrapper<SalesLedgerScheduling> in = new LambdaQueryWrapper<SalesLedgerScheduling>()
                .in(SalesLedgerScheduling::getSalesLedgerId, idList);
        salesLedgerSchedulingMapper.delete(in);
        // 2. 再删除主表数据
        return salesLedgerMapper.deleteBatchIds(idList);
    }
@@ -192,7 +388,7 @@
            // 4. 处理子表数据
            List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
            if (productList != null && !productList.isEmpty()) {
                handleSalesLedgerProducts(salesLedger.getId(), productList,salesLedgerDto.getType());
                handleSalesLedgerProducts(salesLedger.getId(), productList, salesLedgerDto.getType());
                updateMainContractAmount(
                        salesLedger.getId(),
                        productList,
@@ -206,7 +402,6 @@
            if (salesLedgerDto.getTempFileIds() != null && !salesLedgerDto.getTempFileIds().isEmpty()) {
                migrateTempFilesToFormal(salesLedger.getId(), salesLedgerDto.getTempFileIds());
            }
            return 1;
        } catch (IOException e) {
            throw new BaseException("文件迁移失败: " + e.getMessage());
@@ -266,12 +461,14 @@
                log.info("文件迁移成功: {} -> {}", tempFile.getTempPath(), formalFilePath);
                // 更新文件记录(关联到业务ID)
                SalesLedgerFile fileRecord = new SalesLedgerFile();
                fileRecord.setLedgerId(businessId);
                CommonFile fileRecord = new CommonFile();
                fileRecord.setCommonId(businessId);
                fileRecord.setName(originalFilename);
                fileRecord.setUrl(formalFilePath.toString());
                fileRecord.setCreateTime(LocalDateTime.now());
                salesLedgerFileMapper.insert(fileRecord);
                //销售
                fileRecord.setType(FileNameType.SALE.getValue());
                commonFileMapper.insert(fileRecord);
                // 删除临时文件记录
                tempFileMapper.deleteById(tempFile);
@@ -286,7 +483,7 @@
    }
    private void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products,Integer type) {
    private void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, Integer type) {
        // 按ID分组,区分新增和更新的记录
        Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream()
                .peek(p -> p.setSalesLedgerId(salesLedgerId))
@@ -306,6 +503,8 @@
        if (!insertList.isEmpty()) {
            for (SalesLedgerProduct salesLedgerProduct : insertList) {
                salesLedgerProduct.setType(type);
                salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity());
                salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProductMapper.insert(salesLedgerProduct);
            }
        }
@@ -346,11 +545,19 @@
                throw new RuntimeException("获取合同编号生成锁失败:超时");
            }
            // 2. 查询当天已存在的序列号(与原逻辑一致)
            // 2. 查询当天/公司已存在的序列号(与原逻辑一致)
            Long tenantId = SecurityUtils.getLoginUser().getTenantId();
            if (null != tenantId) {
                //获取公司编号
                SysDept sysDept = sysDeptMapper.selectDeptById(tenantId.longValue());
                if (!ObjectUtils.isEmpty(sysDept)) {
                    datePart = (StringUtils.isEmpty(sysDept.getDeptNick()) ? "" : sysDept.getDeptNick()) + datePart;
                }
            }
            List<Integer> existingSequences = salesLedgerMapper.selectSequencesByDate(datePart);
            int nextSequence = findFirstMissingSequence(existingSequences);
            return datePart + String.format("%02d", nextSequence);
            return datePart + String.format("%03d", nextSequence);
        } finally {
            // 3. 释放锁(使用Lua脚本保证原子性,避免误删其他线程的锁)
            String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";