liding
2 天以前 6733a32d4bcd7ad3ec3f109da0f3d2524766f7bb
main-business/src/main/java/com/ruoyi/business/service/impl/PendingInventoryServiceImpl.java
@@ -1,11 +1,56 @@
package com.ruoyi.business.service.impl;
import com.ruoyi.business.entity.PendingInventory;
import com.ruoyi.business.mapper.PendingInventoryMapper;
import com.ruoyi.business.service.PendingInventoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import com.mchange.v2.lang.SystemUtils;
import com.ruoyi.basic.dto.CoalFieldDto;
import com.ruoyi.basic.entity.CoalField;
import com.ruoyi.basic.entity.CoalInfo;
import com.ruoyi.basic.entity.CoalPlan;
import com.ruoyi.basic.entity.CoalValue;
import com.ruoyi.basic.mapper.CoalFieldMapper;
import com.ruoyi.basic.mapper.CoalInfoMapper;
import com.ruoyi.basic.mapper.CoalPlanMapper;
import com.ruoyi.basic.mapper.CoalValueMapper;
import com.ruoyi.basic.service.CoalFieldService;
import com.ruoyi.basic.service.CoalPlanService;
import com.ruoyi.basic.service.CoalValueService;
import com.ruoyi.business.dto.PendingInventoryDto;
import com.ruoyi.business.entity.OfficialInventory;
import com.ruoyi.business.entity.PendingInventory;
import com.ruoyi.business.entity.SalesRecord;
import com.ruoyi.business.mapper.InventorySummaryMapper;
import com.ruoyi.business.mapper.OfficialInventoryMapper;
import com.ruoyi.business.mapper.PendingInventoryMapper;
import com.ruoyi.business.mapper.SalesRecordMapper;
import com.ruoyi.business.service.InputInventoryRecordService;
import com.ruoyi.business.service.InventorySummaryService;
import com.ruoyi.business.service.PendingInventoryService;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
 * <p>
@@ -16,7 +61,439 @@
 * @since 2025-06-04
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PendingInventoryServiceImpl extends ServiceImpl<PendingInventoryMapper, PendingInventory> implements PendingInventoryService {
    private final PendingInventoryMapper pendingInventoryMapper;
    private final OfficialInventoryMapper officialInventoryMapper;
    private final CoalValueMapper coalValueMapper;
    private final CoalFieldMapper coalFieldMapper;
    private final CoalInfoMapper coalInfoMapper;
    private final SysUserMapper sysUserMapper;
    private final InputInventoryRecordService inputInventoryRecordService;
    private final InventorySummaryService inventorySummaryService;
    private final CoalPlanMapper coalPlanMapper;
    private final CoalValueService coalValueService;
    @Override
    public IPage<PendingInventoryDto> selectPendingInventoryList(Page page, PendingInventoryDto pendingInventoryDto) {
        // 1. 构建主查询
        LambdaQueryWrapper<PendingInventory> queryWrapper = new LambdaQueryWrapper<>();
        if (pendingInventoryDto.getRegistrationDate() != null) {
            queryWrapper.eq(PendingInventory::getRegistrationDate, pendingInventoryDto.getRegistrationDate());
        }
        // 按煤种名称查询
        if (StringUtils.isNotBlank(pendingInventoryDto.getCoal())) {
            LambdaQueryWrapper<CoalInfo> coalQueryWrapper = new LambdaQueryWrapper<>();
            coalQueryWrapper.like(CoalInfo::getCoal, pendingInventoryDto.getCoal());
            List<CoalInfo> coalInfos = coalInfoMapper.selectList(coalQueryWrapper);
            if (!coalInfos.isEmpty()) {
                List<Long> coalIds = coalInfos.stream()
                        .map(CoalInfo::getId)
                        .collect(Collectors.toList());
                queryWrapper.in(PendingInventory::getCoalId, coalIds);
            } else {
                // 如果没有匹配的煤种,直接返回空结果
                queryWrapper.eq(PendingInventory::getCoalId, -1L); // 使用不可能存在的ID
            }
        }
        queryWrapper.orderByDesc(PendingInventory::getCreateTime);
        // 2. 执行主表分页查询
        IPage<PendingInventory> pendingInventoryPage = pendingInventoryMapper.selectPage(page, queryWrapper);
        // 3. 无数据快速返回
        if (CollectionUtils.isEmpty(pendingInventoryPage.getRecords())) {
            return new Page<>(page.getCurrent(), page.getSize(), pendingInventoryPage.getTotal());
        }
        // 4. 提取所有待处理库存ID
        List<Long> pendingIds = pendingInventoryPage.getRecords().stream()
                .map(PendingInventory::getId)
                .collect(Collectors.toList());
        // 5. 批量查询关联的煤炭信息和正式库存信息
        List<Long> coalIds = pendingInventoryPage.getRecords().stream()
                .map(PendingInventory::getCoalId)
                .distinct()
                .collect(Collectors.toList());
        // 批量查询CoalInfo
        Map<Long, CoalInfo> coalInfoMap;
        if (!coalIds.isEmpty()) {
            List<CoalInfo> coalInfos = coalInfoMapper.selectList(new LambdaQueryWrapper<CoalInfo>().in(CoalInfo::getId, coalIds));
            coalInfoMap = coalInfos.stream().collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
        } else {
            coalInfoMap = new HashMap<>();
        }
        // 5. 批量查询登记人id
        List<Long> registrantIds = pendingInventoryPage.getRecords().stream()
                .map(PendingInventory::getRegistrantId)
                .distinct()
                .toList();
        // 批量查询登记人
        Map<Long, SysUser> sysUserMap;
        if (!registrantIds.isEmpty()) {
            List<SysUser> sysUsers = sysUserMapper.selectList(registrantIds);
            sysUserMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getUserId, Function.identity()));
        } else {
            sysUserMap = new HashMap<>();
        }
        // 批量查询正式库存信息
        Map<Long, Long> pendingToOfficialMap = getOfficialInventoryMap(pendingIds);
        // 6. 转换DTO并设置相关字段
        return pendingInventoryPage.convert(record -> {
            PendingInventoryDto dto = new PendingInventoryDto();
            BeanUtils.copyProperties(record, dto);
            // 设置Coal信息
            CoalInfo coalInfo = coalInfoMap.get(record.getCoalId());
            if (coalInfo != null) {
                dto.setCoal(coalInfo.getCoal());
            }
            // 设置登记人
            SysUser sysUser = sysUserMap.get(record.getRegistrantId());
            if (sysUser != null) {
                dto.setRegistrant(sysUser.getNickName());
            }
            // 从预加载的Map中获取officialId
            dto.setOfficialId(pendingToOfficialMap.getOrDefault(record.getId(), null));
            return dto;
        });
    }
    // 批量获取待处理库存与正式库存的映射关系
    private Map<Long, Long> getOfficialInventoryMap(List<Long> pendingIds) {
        if (CollectionUtils.isEmpty(pendingIds)) {
            return Collections.emptyMap();
        }
        // 查询关联的正式库存数据
        LambdaQueryWrapper<OfficialInventory> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(OfficialInventory::getId, OfficialInventory::getPendingId)
                .in(OfficialInventory::getPendingId, pendingIds);
        return officialInventoryMapper.selectList(wrapper)
                .stream()
                .collect(Collectors.toMap(
                        OfficialInventory::getPendingId,
                        OfficialInventory::getId,
                        (existing, replacement) -> existing // 如果有重复,保留第一个
                ));
    }
    @Override
    public int addOrEditPending(PendingInventoryDto pendingInventoryDto) {
        PendingInventory pendingInventory = new PendingInventory();
        BeanUtils.copyProperties(pendingInventoryDto, pendingInventory);
        if (Objects.isNull(pendingInventoryDto.getId())) {
            return pendingInventoryMapper.insert(pendingInventory);
        } else {
            return pendingInventoryMapper.updateById(pendingInventory);
        }
    }
    @Override
    public int delByIds(Long[] ids) {
        // 检查参数
        if (ids == null || ids.length == 0) {
            return 0;
        }
        // 构造更新条件
        UpdateWrapper<PendingInventory> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", ids)
                .set("deleted", 1);  // 设置 deleted 为 1 表示已删除
        // 执行批量逻辑删除
        return pendingInventoryMapper.update(null, updateWrapper);
    }
    @Transactional
    @Override
    public int addOrEditCoalValue(PendingInventoryDto pendingInventoryDto) {
        // Step 1: 删除原有 planId 相关数据
        coalValueMapper.delete(new LambdaQueryWrapper<CoalValue>()
                .eq(CoalValue::getPlanId, pendingInventoryDto.getPId()));
        // Step 2: 构建基础 CoalValue 对象
        CoalValue coalValue = new CoalValue();
        BeanUtils.copyProperties(coalValue, pendingInventoryDto);
        coalValue.setPlanId(pendingInventoryDto.getPId());
        List<Map<String, String>> fieldValue = pendingInventoryDto.getFieldValue();
        if (CollectionUtils.isEmpty(fieldValue)) {
            throw new BaseException("字段值不能为空");
        }
        // Step 3: 提前获取所有 field -> CoalField 映射,避免重复查库
        Set<String> allFields = fieldValue.stream()
                .flatMap(map -> map.keySet().stream())
                .collect(Collectors.toSet());
        List<CoalField> coalFields = coalFieldMapper.selectList(
                new LambdaQueryWrapper<CoalField>().in(CoalField::getFields, allFields));
        Map<String, String> fieldMap = coalFields.stream()
                .collect(Collectors.toMap(
                        CoalField::getFields,
                        CoalField::getFieldName));
        // Step 4: 批量插入
        int i = 0;
        for (Map<String, String> fieldMapEntry : fieldValue) {
            for (Map.Entry<String, String> entry : fieldMapEntry.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                String fieldName = fieldMap.get(key);
                if (fieldName == null) {
                    throw new BaseException("字段名不存在");
                }
                coalValue.setId(null); // 清空 id,确保每次都是新记录
                coalValue.setCoalValue(value);
                coalValue.setFields(key);
                coalValue.setFieldName(fieldName);
                coalValue.setType(String.valueOf(1));
                i = coalValueMapper.insert(coalValue);
            }
        }
        if (i > 0) {
            BigDecimal quantity = pendingInventoryDto.getInventoryQuantity();
            PendingInventory pendingInventory = pendingInventoryMapper.selectById(pendingInventoryDto.getPId());
            if (pendingInventory == null) {
                throw new BaseException("待入库记录不存在");
            }
            BigDecimal left = pendingInventory.getInventoryQuantity().subtract(quantity);
            if (left.compareTo(BigDecimal.ZERO) > 0) {
                pendingInventory.setInventoryQuantity(left);
                pendingInventory.setCoalPlanId(pendingInventoryDto.getCoalPlanId());
                pendingInventoryMapper.updateById(pendingInventory);
            } else {
                pendingInventoryMapper.deleteById(pendingInventoryDto.getPId());
            }
            //正式库
            if (pendingInventoryDto.getOfficialId() == null) {
                OfficialInventory officialInventory = new OfficialInventory();
                BeanUtils.copyProperties(pendingInventory, officialInventory);
                officialInventory.setId(null);
                officialInventory.setCoalPlanId(pendingInventoryDto.getCoalPlanId());
                officialInventory.setPendingId(pendingInventoryDto.getPId());
                officialInventory.setInventoryQuantity(quantity);
                officialInventory.setRegistrantId(1L);
                officialInventory.setSupplierId(pendingInventoryDto.getSupplierId());
                officialInventoryMapper.insert(officialInventory);
            } else {
                OfficialInventory officialInventory = officialInventoryMapper.selectById(pendingInventoryDto.getOfficialId());
                officialInventory.setInventoryQuantity(quantity.add(officialInventory.getInventoryQuantity()));
                officialInventoryMapper.updateById(officialInventory);
            }
        }
        return i;
    }
    @Override
    @Transactional
    public boolean addPending(PendingInventoryDto pendingInventoryDto) {
        try {
            // 1. 获取配煤计算器方案
            CoalPlan coalPlan = coalPlanMapper.selectOne(
                    new LambdaQueryWrapper<CoalPlan>().eq(CoalPlan::getPlan, "配煤计算器方案"));
            if (coalPlan == null) {
                log.error("配煤计算器方案不存在");
                return false;
            }
            // 2. 准备待入库记录
            Long userId = SecurityUtils.getUserId();
            PendingInventory pendingInventory = new PendingInventory();
            pendingInventory.setUnit("t");
            pendingInventory.setRegistrantId(userId);
            pendingInventory.setRegistrationDate(LocalDate.now());
            pendingInventory.setSupplierName("配煤计算器方案入库");
            pendingInventory.setCoalPlanId(coalPlan.getId());
            // 3. 安全获取字段结果
            Map<String, Object> map = Optional.ofNullable(pendingInventoryDto.getFieldsResultList())
                    .orElseThrow(() -> new BaseException("字段结果列表不能为空"));
            // 4. 处理煤炭ID
            if (map.get("coalId") == null) {
                String coalName = safeGetString(map, "createCoal");
                if (coalName == null || coalName.isEmpty()) {
                    throw new BaseException("煤炭名称不能为空");
                }
                CoalInfo coalInfo = new CoalInfo();
                coalInfo.setCoal(coalName);
                coalInfo.setMaintainerId(userId);
                coalInfo.setMaintenanceDate(LocalDate.now());
                if (coalInfoMapper.insert(coalInfo) <= 0) {
                    log.error("创建煤炭信息失败");
                    return false;
                }
                pendingInventory.setCoalId(coalInfo.getId());
            } else {
                Long coalId = safeGetLong(map, "coalId");
                if (coalId == null) {
                    throw new BaseException("煤炭ID格式错误");
                }
                pendingInventory.setCoalId(coalId);
            }
            // 5. 设置价格和数量
            BigDecimal cost = safeGetBigDecimal(map, "cost");
            BigDecimal tonnage = safeGetBigDecimal(map, "totalTonnage");
            if (cost == null || tonnage == null) {
                throw new BaseException("成本或吨位不能为空");
            }
            pendingInventory.setPriceIncludingTax(cost);
            pendingInventory.setInventoryQuantity(tonnage);
            pendingInventory.setTotalPriceIncludingTax(cost.multiply(tonnage));
            BigDecimal costExcludingTax = cost.divide(BigDecimal.valueOf(1.13), 2, RoundingMode.HALF_UP);
            pendingInventory.setPriceExcludingTax(costExcludingTax);
            pendingInventory.setTotalPriceExcludingTax(costExcludingTax.multiply(tonnage));
            // 6. 处理煤质字段值
            String coalFields = coalPlan.getCoalFields();
            if (coalFields == null || coalFields.isEmpty()) {
                throw new BaseException("煤质字段配置不能为空");
            }
            String[] coalFieldList = coalFields.split(",");
            for (String field : coalFieldList) {
                CoalField coalField = coalFieldMapper.selectOne(
                        new LambdaQueryWrapper<CoalField>().eq(CoalField::getFields, field));
                if (coalField == null) {
                    log.warn("未找到煤质字段配置: {}", field);
                    continue;
                }
                CoalValue coalValue = new CoalValue();
                coalValue.setPlanId(coalPlan.getId());
                coalValue.setFields(field);
                coalValue.setFieldName(coalField.getFieldName());
                coalValue.setType("1");
                // 安全设置煤质值
                switch (coalField.getFieldName()) {
                    case "发热量":
                        coalValue.setCoalValue(safeGetString(map, "cv"));
                        break;
                    case "硫分":
                        coalValue.setCoalValue(safeGetString(map, "sulfur"));
                        break;
                    case "灰分":
                        coalValue.setCoalValue(safeGetString(map, "ash"));
                        break;
                    case "水分":
                        coalValue.setCoalValue(safeGetString(map, "moisture"));
                        break;
                    default:
                        log.warn("未知煤质字段: {}", coalField.getFieldName());
                        continue;
                }
                if (coalValueMapper.insert(coalValue) <= 0) {
                    log.error("保存煤质值失败: {}", field);
                    return false;
                }
            }
            // 7. 插入待入库记录
            if (pendingInventoryMapper.insert(pendingInventory) <= 0) {
                log.error("插入待入库记录失败");
                return false;
            }
            // 8. 更新正式库
            List<Map<String, Object>> coalResults = Optional.ofNullable(pendingInventoryDto.getCoalResultList())
                    .orElseThrow(() -> new BaseException("煤炭结果列表不能为空"));
            for (Map<String, Object> coalResult : coalResults) {
                Long officialId = safeGetLong(coalResult, "officialId");
                BigDecimal quantity = safeGetBigDecimal(coalResult, "quantity");
                if (officialId == null || quantity == null) {
                    throw new BaseException("正式库存ID或数量不能为空");
                }
                OfficialInventory officialInventory = officialInventoryMapper.selectById(officialId);
                if (officialInventory == null) {
                    throw new BaseException("找不到正式库存记录: " + officialId);
                }
                if (officialInventory.getInventoryQuantity().compareTo(quantity) < 0) {
                    throw new BaseException("库存数量不足,添加至待入库失败");
                }
                officialInventory.setInventoryQuantity(officialInventory.getInventoryQuantity().subtract(quantity));
                if (officialInventoryMapper.updateById(officialInventory) <= 0) {
                    log.error("更新正式库存失败: {}", officialId);
                    return false;
                }
            }
            return true;
        } catch (Exception e) {
            log.error("添加待入库失败", e);
            throw new BaseException("添加待入库失败: " + e.getMessage());
        }
    }
    // 安全类型转换辅助方法
    private String safeGetString(Map<String, Object> map, String key) {
        Object value = map.get(key);
        if (value == null) {
            return null;
        }
        return value.toString();
    }
    private Long safeGetLong(Map<String, Object> map, String key) {
        Object value = map.get(key);
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        try {
            return Long.parseLong(value.toString());
        } catch (NumberFormatException e) {
            log.warn("无法转换为Long: key={}, value={}", key, value);
            return null;
        }
    }
    private BigDecimal safeGetBigDecimal(Map<String, Object> map, String key) {
        Object value = map.get(key);
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        }
        try {
            return new BigDecimal(value.toString());
        } catch (NumberFormatException e) {
            log.warn("无法转换为BigDecimal: key={}, value={}", key, value);
            return null;
        }
    }
}