chenrui
2 天以前 ab01a0f611c0adb97662bc8f548ca4a911ec8045
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -2,27 +2,31 @@
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.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.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.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedger;
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 +36,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;
@@ -62,6 +70,13 @@
    private final TempFileMapper tempFileMapper;
    private final ReceiptPaymentMapper receiptPaymentMapper;
    private final InvoiceLedgerMapper invoiceLedgerMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -73,11 +88,7 @@
    @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);
    }
    public SalesLedgerDto getSalesLedgerWithProducts(SalesLedgerDto salesLedgerDto) {
@@ -91,6 +102,12 @@
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId());
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        for (SalesLedgerProduct product : products) {
            product.setOriginalNoInvoiceNum(product.getNoInvoiceNum());
            // 提供临时未开票数,未开票金额供前段计算
            product.setTempnoInvoiceAmount(product.getNoInvoiceAmount());
            product.setTempNoInvoiceNum(product.getNoInvoiceNum());
        }
        // 3.查询上传文件
        LambdaQueryWrapper<CommonFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>();
@@ -111,7 +128,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 +139,144 @@
                        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() {
        LocalDate now = LocalDate.now();
        YearMonth currentMonth = YearMonth.from(now);
        List<MonthlyAmountDto> monthlyAmounts = new ArrayList<>();
        for (int i = 0; i < 6; i++) {
            YearMonth targetMonth = currentMonth.minusMonths(i);
            LocalDate firstDayOfMonth = targetMonth.atDay(1);
            LocalDate firstDayOfNextMonth = targetMonth.plusMonths(1).atDay(1);
            LocalDateTime startOfMonth = firstDayOfMonth.atStartOfDay();
            LocalDateTime startOfNextMonth = firstDayOfNextMonth.atStartOfDay();
            LambdaQueryWrapper<ReceiptPayment> receiptPaymentLambdaQueryWrapper = new LambdaQueryWrapper<>();
            receiptPaymentLambdaQueryWrapper.ge(ReceiptPayment::getCreateTime, startOfMonth)
                    .lt(ReceiptPayment::getCreateTime, startOfNextMonth);
            LambdaQueryWrapper<InvoiceLedger> invoiceLedgerLambdaQueryWrapper = new LambdaQueryWrapper<>();
            invoiceLedgerLambdaQueryWrapper.ge(InvoiceLedger::getCreateTime, startOfMonth)
                    .lt(InvoiceLedger::getCreateTime, startOfNextMonth);
            // 获取回款金额
            List<ReceiptPayment> receiptPaymentList = receiptPaymentMapper.selectList(receiptPaymentLambdaQueryWrapper);
            //开票金额
            List<InvoiceLedger> invoiceLedgerList = invoiceLedgerMapper.selectList(invoiceLedgerLambdaQueryWrapper);
            // 使用 Stream 求和
            BigDecimal invoiceAmount = invoiceLedgerList.stream()
                    .map(InvoiceLedger::getInvoiceTotal)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal receiptAmount = receiptPaymentList.stream()
                    .map(ReceiptPayment::getReceiptPaymentAmount)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            MonthlyAmountDto monthlyAmount = new MonthlyAmountDto();
            monthlyAmount.setMonth(targetMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")));
            monthlyAmount.setInvoiceAmount(invoiceAmount);
            monthlyAmount.setReceiptAmount(receiptAmount);
            monthlyAmounts.add(monthlyAmount);
        }
        Collections.reverse(monthlyAmounts);
        return monthlyAmounts;
    }
    @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;
        }
    }
    /**
@@ -192,7 +347,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 +361,6 @@
            if (salesLedgerDto.getTempFileIds() != null && !salesLedgerDto.getTempFileIds().isEmpty()) {
                migrateTempFilesToFormal(salesLedger.getId(), salesLedgerDto.getTempFileIds());
            }
            return 1;
        } catch (IOException e) {
            throw new BaseException("文件迁移失败: " + e.getMessage());
@@ -287,7 +441,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))
@@ -307,6 +461,8 @@
        if (!insertList.isEmpty()) {
            for (SalesLedgerProduct salesLedgerProduct : insertList) {
                salesLedgerProduct.setType(type);
                salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity());
                salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice());
                salesLedgerProductMapper.insert(salesLedgerProduct);
            }
        }
@@ -347,11 +503,19 @@
                throw new RuntimeException("获取合同编号生成锁失败:超时");
            }
            // 2. 查询当天已存在的序列号(与原逻辑一致)
            // 2. 查询当天/公司已存在的序列号(与原逻辑一致)
            Integer 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";