package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usky.common.core.bean.CommonPage;
import com.usky.common.core.exception.BusinessException;
import com.usky.common.security.utils.SecurityUtils;
import com.usky.vpp.domain.VppContract;
import com.usky.vpp.domain.VppContractTemplate;
import com.usky.vpp.domain.VppCustomer;
import com.usky.vpp.mapper.VppContractMapper;
import com.usky.vpp.mapper.VppContractTemplateMapper;
import com.usky.vpp.mapper.VppCustomerMapper;
import com.usky.vpp.service.VppContractService;
import com.usky.vpp.service.vo.VppContractExpireStatVO;
import com.usky.vpp.service.vo.VppContractOverviewVO;
import com.usky.vpp.service.vo.VppContractRequestVO;
import com.usky.vpp.service.vo.VppContractResponseVO;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppContractStatusHelper;
import com.usky.vpp.util.VppFieldValidator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * VppContractService 默认实现
 * @author fyc
 * @email yuchuan.fu@chinausky.com
 * @date 2026/7/4
 */
@Service
public class VppContractServiceImpl implements VppContractService {

    @Autowired
    private VppContractMapper vppContractMapper;
    @Autowired
    private VppContractTemplateMapper vppContractTemplateMapper;
    @Autowired
    private VppCustomerMapper vppCustomerMapper;

    private final RestTemplate restTemplate = new RestTemplate();

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean create(VppContractRequestVO vo) {
        if (StringUtils.isBlank(vo.getContractNo())) {
            throw new BusinessException("合同编号不能为空！");
        }
        VppContractTemplate template = getTemplateById(vo.getTemplateId());
        String contractNo = vo.getContractNo().trim();
        checkContractNoUnique(contractNo, null);
        // 关联校验必须在入库前完成，避免失败后留下脏数据导致“合同编号已存在”
        validateRelatedContract(null, vo.getRelatedContractId(), template);

        VppContract contract = new VppContract();
        contract.setContractNo(contractNo);
        contract.setCustomerId(vo.getCustomerId());
        contract.setTemplateId(vo.getTemplateId());
        contract.setRelatedContractId(vo.getRelatedContractId());
        contract.setContractType(vo.getContractType());
        contract.setContractName(vo.getContractName());
        contract.setPartyA(vo.getPartyA());
        contract.setPartyB(vo.getPartyB());
        contract.setContractAmount(vo.getContractAmount());
        contract.setContractStatus(VppContractStatusHelper.resolveForPersist(
                vo.getContractStatus(), vo.getEffectiveDate(), vo.getExpireDate()));
        contract.setSignDate(vo.getSignDate());
        contract.setEffectiveDate(vo.getEffectiveDate());
        contract.setExpireDate(vo.getExpireDate());
        contract.setFileUrl(vo.getFileUrl());
        contract.setCustomerRatio(vo.getCustomerRatio());
        contract.setOperatorRatio(vo.getOperatorRatio());
        contract.setPriceJson(VppFieldValidator.validateJson(vo.getPriceJson(), "价格定义"));
        contract.setAccountInfoJson(VppFieldValidator.validateJson(vo.getAccountInfoJson(), "账户信息"));
        contract.setRemark(vo.getRemark());

        VppAuditHelper.fillCreate(contract);
        int insert = vppContractMapper.insert(contract);
        if (insert > 0 && vo.getRelatedContractId() != null) {
            syncPeerRelated(contract.getId(), vo.getRelatedContractId(), null);
        }
        return insert > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(VppContractRequestVO vo) {
        if (vo.getId() == null) {
            throw new BusinessException("主键ID不能为空！");
        }

        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getId, vo.getId())
                .eq(VppContract::getDeleteFlag, 0);
        VppContract existingRecord = vppContractMapper.selectOne(queryWrapper);

        if (existingRecord == null) {
            throw new BusinessException("合同不存在或已被删除！");
        }

        Long previousRelatedId = existingRecord.getRelatedContractId();

        if (StringUtils.isNotBlank(vo.getContractNo())) {
            String contractNo = vo.getContractNo().trim();
            if (!contractNo.equals(existingRecord.getContractNo())) {
                checkContractNoUnique(contractNo, vo.getId());
            }
            existingRecord.setContractNo(contractNo);
        }
        if (vo.getCustomerId() != null) {
            existingRecord.setCustomerId(vo.getCustomerId());
        }
        if (vo.getTemplateId() != null) {
            existingRecord.setTemplateId(vo.getTemplateId());
        }
        if (vo.getContractType() != null) {
            existingRecord.setContractType(vo.getContractType());
        }
        if (StringUtils.isNotBlank(vo.getContractName())) {
            existingRecord.setContractName(vo.getContractName());
        }
        if (vo.getPartyA() != null) {
            existingRecord.setPartyA(vo.getPartyA());
        }
        if (vo.getPartyB() != null) {
            existingRecord.setPartyB(vo.getPartyB());
        }
        if (vo.getContractAmount() != null) {
            existingRecord.setContractAmount(vo.getContractAmount());
        }
        if (vo.getSignDate() != null) {
            existingRecord.setSignDate(vo.getSignDate());
        }
        if (vo.getEffectiveDate() != null) {
            existingRecord.setEffectiveDate(vo.getEffectiveDate());
        }
        if (vo.getExpireDate() != null) {
            existingRecord.setExpireDate(vo.getExpireDate());
        }
        if (StringUtils.isNotBlank(vo.getFileUrl())) {
            existingRecord.setFileUrl(vo.getFileUrl());
        }
        if (vo.getCustomerRatio() != null) {
            existingRecord.setCustomerRatio(vo.getCustomerRatio());
        }
        if (vo.getOperatorRatio() != null) {
            existingRecord.setOperatorRatio(vo.getOperatorRatio());
        }
        if (vo.getPriceJson() != null) {
            existingRecord.setPriceJson(VppFieldValidator.validateJson(vo.getPriceJson(), "价格定义"));
        }
        if (vo.getAccountInfoJson() != null) {
            existingRecord.setAccountInfoJson(VppFieldValidator.validateJson(vo.getAccountInfoJson(), "账户信息"));
        }
        if (vo.getRemark() != null) {
            existingRecord.setRemark(vo.getRemark());
        }

        Integer requestedStatus = vo.getContractStatus() != null
                ? vo.getContractStatus() : existingRecord.getContractStatus();
        existingRecord.setContractStatus(VppContractStatusHelper.resolveForPersist(
                requestedStatus, existingRecord.getEffectiveDate(), existingRecord.getExpireDate()));

        // 关联变更：入库前先校验；写入 relatedContractId 后同步对端
        boolean relatedChanged = vo.getRelatedContractId() != null
                && !Objects.equals(vo.getRelatedContractId(), previousRelatedId);
        if (relatedChanged) {
            VppContractTemplate template = getTemplateById(existingRecord.getTemplateId());
            validateRelatedContract(existingRecord.getId(), vo.getRelatedContractId(), template);
            existingRecord.setRelatedContractId(vo.getRelatedContractId());
        }

        VppAuditHelper.fillUpdate(existingRecord);
        int result = vppContractMapper.updateById(existingRecord);
        if (result > 0 && relatedChanged) {
            syncPeerRelated(existingRecord.getId(), vo.getRelatedContractId(), previousRelatedId);
        }
        return result > 0;
    }

    @Override
    public CommonPage<VppContractResponseVO> page(Long id, LocalDate signDateStart, LocalDate signDateEnd,
                                                   Long templateId, Integer contractType, Long customerId,
                                                   String customerName, LocalDate signDateRangeStart, LocalDate signDateRangeEnd,
                                                   Integer contractStatus, Integer pageNum, Integer pageSize) {
        IPage<VppContract> page = new Page<>(pageNum, pageSize);

        // 客户名称模糊匹配：先反查符合条件的客户ID
        List<Long> customerIdsByName = null;
        if (StringUtils.isNotBlank(customerName)) {
            LambdaQueryWrapper<VppCustomer> customerWrapper = new LambdaQueryWrapper<>();
            customerWrapper.like(VppCustomer::getCustomerName, customerName)
                    .eq(VppCustomer::getDeleteFlag, 0)
                    .select(VppCustomer::getId);
            List<VppCustomer> matchedCustomers = vppCustomerMapper.selectList(customerWrapper);
            customerIdsByName = matchedCustomers.stream().map(VppCustomer::getId).collect(Collectors.toList());
            if (customerIdsByName.isEmpty()) {
                return new CommonPage<>(Collections.emptyList(), 0L, pageSize, pageNum);
            }
        }

        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getDeleteFlag, 0)
                .eq(id != null, VppContract::getId, id)
                .ge(signDateStart != null, VppContract::getSignDate, signDateStart)
                .le(signDateEnd != null, VppContract::getSignDate, signDateEnd)
                .ge(signDateRangeStart != null, VppContract::getSignDate, signDateRangeStart)
                .le(signDateRangeEnd != null, VppContract::getSignDate, signDateRangeEnd)
                .eq(templateId != null, VppContract::getTemplateId, templateId)
                .eq(contractType != null, VppContract::getContractType, contractType)
                .eq(customerId != null, VppContract::getCustomerId, customerId)
                .in(customerIdsByName != null, VppContract::getCustomerId, customerIdsByName);
        VppContractStatusHelper.applyStatusFilter(queryWrapper, contractStatus);
        queryWrapper.orderByDesc(VppContract::getCreateTime);
        page = vppContractMapper.selectPage(page, queryWrapper);

        List<VppContract> records = page.getRecords();
        if (records == null || records.isEmpty()) {
            return new CommonPage<>(Collections.emptyList(), page.getTotal(), pageSize, pageNum);
        }

        // ========== 批量反查客户名称和模板名称 ==========
        Set<Long> customerIds = records.stream().map(VppContract::getCustomerId).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> templateIds = records.stream().map(VppContract::getTemplateId).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> relatedIds = records.stream().map(VppContract::getRelatedContractId).filter(Objects::nonNull).collect(Collectors.toSet());

        final Map<Long, String> customerNameMap = customerIds.isEmpty() ? Collections.emptyMap()
                : vppCustomerMapper.selectBatchIds(customerIds).stream()
                        .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));

        final Map<Long, String> templateNameMap = templateIds.isEmpty() ? Collections.emptyMap()
                : vppContractTemplateMapper.selectBatchIds(templateIds).stream()
                        .collect(Collectors.toMap(VppContractTemplate::getId, VppContractTemplate::getTemplateName, (a, b) -> a));

        final Map<Long, VppContract> relatedMap = relatedIds.isEmpty() ? Collections.emptyMap()
                : vppContractMapper.selectBatchIds(relatedIds).stream()
                        .collect(Collectors.toMap(VppContract::getId, c -> c, (a, b) -> a));

        // ========== 组装响应VO ==========
        List<VppContractResponseVO> list = records.stream().map(entity -> {
            VppContractResponseVO vo = toResponseVO(entity, customerNameMap, templateNameMap, relatedMap);
            return vo;
        }).collect(Collectors.toList());

        return new CommonPage<>(list, page.getTotal(), pageSize, pageNum);
    }

    @Override
    public VppContractOverviewVO overview(Long id) {
        if (id == null) {
            throw new BusinessException("主键ID不能为空！");
        }
        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getId, id)
                .eq(VppContract::getDeleteFlag, 0);
        VppContract contract = vppContractMapper.selectOne(queryWrapper);
        if (contract == null) {
            throw new BusinessException("合同不存在或已被删除！");
        }
        VppContractOverviewVO vo = new VppContractOverviewVO();
        vo.setId(contract.getId());
        vo.setContractNo(contract.getContractNo());
        vo.setContractName(contract.getContractName());
        vo.setPartyA(contract.getPartyA());
        vo.setPartyB(contract.getPartyB());
        vo.setSignDate(contract.getSignDate());
        vo.setContractAmount(contract.getContractAmount());
        vo.setEffectiveDate(contract.getEffectiveDate());
        vo.setExpireDate(contract.getExpireDate());
        vo.setContractStatus(VppContractStatusHelper.resolve(
                contract.getContractStatus(), contract.getEffectiveDate(), contract.getExpireDate()));
        return vo;
    }

    @Override
    public Boolean delete(Long id) {
        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getId, id)
                .eq(VppContract::getDeleteFlag, 0);
        VppContract contract = vppContractMapper.selectOne(queryWrapper);

        if (contract == null) {
            throw new BusinessException("合同不存在或已被删除！");
        }

        // 解除一对一关联
        if (contract.getRelatedContractId() != null) {
            clearRelatedLink(contract.getId(), contract.getRelatedContractId());
        }
        // 若有对方指向自己，一并解除
        List<VppContract> reverseLinks = vppContractMapper.selectList(new LambdaQueryWrapper<VppContract>()
                .eq(VppContract::getRelatedContractId, id)
                .eq(VppContract::getDeleteFlag, 0));
        for (VppContract reverse : reverseLinks) {
            reverse.setRelatedContractId(null);
            VppAuditHelper.fillUpdate(reverse);
            vppContractMapper.updateById(reverse);
        }

        VppAuditHelper.fillSoftDelete(contract);

        int i = vppContractMapper.updateById(contract);
        return i > 0;
    }

    @Override
    public VppContractExpireStatVO expireStat() {
        VppContractExpireStatVO result = new VppContractExpireStatVO();
        Integer tenantId = SecurityUtils.getTenantId();

        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getDeleteFlag, 0)
                .eq(tenantId != null, VppContract::getTenantId, tenantId)
                .in(VppContract::getContractStatus, VppContractStatusHelper.PENDING,
                        VppContractStatusHelper.EFFECTIVE, VppContractStatusHelper.EXPIRED)
                .isNotNull(VppContract::getCustomerId)
                .isNotNull(VppContract::getExpireDate)
                .orderByDesc(VppContract::getCreateTime);
        List<VppContract> allContracts = vppContractMapper.selectList(queryWrapper);
        if (allContracts == null || allContracts.isEmpty()) {
            return result;
        }

        // 每个客户仅保留创建时间最新的一份合同
        Map<Long, VppContract> latestByCustomer = new LinkedHashMap<>();
        for (VppContract contract : allContracts) {
            latestByCustomer.putIfAbsent(contract.getCustomerId(), contract);
        }

        LocalDate today = LocalDate.now();
        LocalDate upcomingDeadline = today.plusMonths(3);
        long expiredCount = 0L;
        long upcomingExpireCount = 0L;
        for (VppContract contract : latestByCustomer.values()) {
            LocalDate expireDate = contract.getExpireDate();
            if (today.isAfter(expireDate)) {
                expiredCount++;
            } else if (!expireDate.isAfter(upcomingDeadline)) {
                // today <= expireDate <= today+3个月
                upcomingExpireCount++;
            }
        }

        result.setExpiredCount(expiredCount);
        result.setUpcomingExpireCount(upcomingExpireCount);
        return result;
    }

    @Override
    public void download(Long id, HttpServletResponse response) {
        LambdaQueryWrapper<VppContract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VppContract::getId, id)
                .eq(VppContract::getDeleteFlag, 0);
        VppContract contract = vppContractMapper.selectOne(queryWrapper);

        if (contract == null) {
            writeErrorResponse(response, HttpStatus.BAD_REQUEST.value(), "合同不存在或已被删除！");
            return;
        }

        String fileUrl = contract.getFileUrl();
        String fileName = contract.getContractName();
        if (StringUtils.isBlank(fileUrl)) {
            writeErrorResponse(response, HttpStatus.BAD_REQUEST.value(), "合同文件URL为空，无法下载！");
            return;
        }

        // ========== 设置响应头 ==========
        String downloadFileName = fileName;
        int dotIndex = fileUrl.lastIndexOf('.');
        if (dotIndex > 0 && !fileName.contains(".")) {
            downloadFileName = fileName + fileUrl.substring(dotIndex);
        }
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=" + encodeFileName(downloadFileName));

        // ========== 下载文件并写入响应流 ==========
        try {
            byte[] fileBytes = restTemplate.getForObject(fileUrl, byte[].class);
            if (fileBytes == null || fileBytes.length == 0) {
                writeErrorResponse(response, HttpStatus.NOT_FOUND.value(), "合同文件内容为空！");
                return;
            }
            ServletOutputStream sos = response.getOutputStream();
            sos.write(fileBytes);
            sos.flush();
        } catch (Exception e) {
            if (!response.isCommitted()) {
                writeErrorResponse(response, HttpStatus.INTERNAL_SERVER_ERROR.value(),
                        "下载合同文件失败: " + e.getMessage());
            }
        }
    }

    // ========== 内部辅助方法 ==========

    private VppContractResponseVO toResponseVO(VppContract entity,
                                               Map<Long, String> customerNameMap,
                                               Map<Long, String> templateNameMap,
                                               Map<Long, VppContract> relatedMap) {
        VppContractResponseVO vo = new VppContractResponseVO();
        vo.setId(entity.getId());
        vo.setContractNo(entity.getContractNo());
        vo.setCustomerId(entity.getCustomerId());
        vo.setCustomerName(customerNameMap.get(entity.getCustomerId()));
        vo.setTemplateId(entity.getTemplateId());
        vo.setTemplateName(templateNameMap.get(entity.getTemplateId()));
        vo.setRelatedContractId(entity.getRelatedContractId());
        VppContract related = relatedMap.get(entity.getRelatedContractId());
        if (related != null) {
            vo.setRelatedContractNo(related.getContractNo());
            vo.setRelatedContractName(related.getContractName());
        }
        vo.setContractType(entity.getContractType());
        vo.setContractName(entity.getContractName());
        vo.setPartyA(entity.getPartyA());
        vo.setPartyB(entity.getPartyB());
        vo.setContractAmount(entity.getContractAmount());
        vo.setContractStatus(VppContractStatusHelper.resolve(
                entity.getContractStatus(), entity.getEffectiveDate(), entity.getExpireDate()));
        vo.setSignDate(entity.getSignDate());
        vo.setEffectiveDate(entity.getEffectiveDate());
        vo.setExpireDate(entity.getExpireDate());
        vo.setFileUrl(entity.getFileUrl());
        vo.setCustomerRatio(entity.getCustomerRatio());
        vo.setOperatorRatio(entity.getOperatorRatio());
        vo.setPriceJson(entity.getPriceJson());
        vo.setAccountInfoJson(entity.getAccountInfoJson());
        vo.setRemark(entity.getRemark());
        vo.setTenantId(entity.getTenantId());
        vo.setCreateTime(entity.getCreateTime());
        vo.setUpdateTime(entity.getUpdateTime());
        vo.setCreatedBy(entity.getCreatedBy());
        vo.setUpdatedBy(entity.getUpdatedBy());
        return vo;
    }

    /**
     * 关联校验（入库前调用）：跨类、一对一占用、不能关联自身
     */
    private void validateRelatedContract(Long selfId, Long relatedId, VppContractTemplate selfTemplate) {
        if (relatedId == null) {
            return;
        }
        if (selfId != null && relatedId.equals(selfId)) {
            throw new BusinessException("不能关联合同自身！");
        }
        if (selfTemplate == null || selfTemplate.getTemplateKind() == null) {
            throw new BusinessException("当前合同模板未配置分类（居间/普通），无法关联！");
        }
        if (selfTemplate.getTemplateKind() != 1 && selfTemplate.getTemplateKind() != 2) {
            throw new BusinessException("模板分类无效，仅支持 1居间 2普通！");
        }

        VppContract related = vppContractMapper.selectOne(new LambdaQueryWrapper<VppContract>()
                .eq(VppContract::getId, relatedId)
                .eq(VppContract::getDeleteFlag, 0));
        if (related == null) {
            throw new BusinessException("关联合同不存在或已被删除！");
        }
        VppContractTemplate relatedTemplate = getTemplateById(related.getTemplateId());
        if (relatedTemplate.getTemplateKind() == null) {
            throw new BusinessException("关联合同模板未配置分类（居间/普通）！");
        }
        if (Objects.equals(selfTemplate.getTemplateKind(), relatedTemplate.getTemplateKind())) {
            throw new BusinessException("仅允许居间合同与普通合同互相一对一关联！");
        }

        if (related.getRelatedContractId() != null
                && (selfId == null || !related.getRelatedContractId().equals(selfId))) {
            throw new BusinessException("目标合同已被其他合同关联，请先解除原关联！");
        }
        Integer occupied = vppContractMapper.selectCount(new LambdaQueryWrapper<VppContract>()
                .eq(VppContract::getRelatedContractId, relatedId)
                .eq(VppContract::getDeleteFlag, 0)
                .ne(selfId != null, VppContract::getId, selfId));
        if (occupied != null && occupied > 0) {
            throw new BusinessException("目标合同已被其他合同关联，请先解除原关联！");
        }
    }

    /**
     * 同步对端一对一关联（调用前须已完成 validateRelatedContract）
     */
    private void syncPeerRelated(Long selfId, Long relatedId, Long previousRelatedId) {
        if (previousRelatedId != null && !previousRelatedId.equals(relatedId)) {
            clearRelatedLink(selfId, previousRelatedId);
        }
        if (relatedId == null || selfId == null) {
            return;
        }
        VppContract related = vppContractMapper.selectOne(new LambdaQueryWrapper<VppContract>()
                .eq(VppContract::getId, relatedId)
                .eq(VppContract::getDeleteFlag, 0));
        if (related == null) {
            throw new BusinessException("关联合同不存在或已被删除！");
        }
        related.setRelatedContractId(selfId);
        VppAuditHelper.fillUpdate(related);
        vppContractMapper.updateById(related);
    }

    private void clearRelatedLink(Long selfId, Long relatedId) {
        if (relatedId == null) {
            return;
        }
        VppContract related = vppContractMapper.selectById(relatedId);
        if (related != null && Objects.equals(related.getRelatedContractId(), selfId)) {
            related.setRelatedContractId(null);
            VppAuditHelper.fillUpdate(related);
            vppContractMapper.updateById(related);
        }
    }

    /**
     * 根据模板ID查询模板信息
     */
    private VppContractTemplate getTemplateById(Long templateId) {
        if (templateId == null) {
            throw new BusinessException("模板ID不能为空！");
        }
        LambdaQueryWrapper<VppContractTemplate> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(VppContractTemplate::getId, templateId)
                .eq(VppContractTemplate::getDeleteFlag, 0);
        VppContractTemplate template = vppContractTemplateMapper.selectOne(wrapper);
        if (template == null) {
            throw new BusinessException("合同模板不存在或已被删除！");
        }
        return template;
    }

    /**
     * 合同编号全租户唯一性校验
     */
    private void checkContractNoUnique(String contractNo, Long excludeId) {
        LambdaQueryWrapper<VppContract> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(VppContract::getContractNo, contractNo)
                .eq(VppContract::getDeleteFlag, 0)
                .ne(excludeId != null, VppContract::getId, excludeId);
        Integer count = vppContractMapper.selectCount(wrapper);
        if (count != null && count > 0) {
            throw new BusinessException("合同编号【" + contractNo + "】已存在，请重新提交！");
        }
    }

    private void writeErrorResponse(HttpServletResponse response, int status, String message) {
        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        try {
            response.getWriter().write("{\"code\":\"" + status + "\",\"msg\":\"" + message + "\"}");
        } catch (IOException e) {
            // 忽略写入异常
        }
    }

    private String encodeFileName(String fileName) {
        try {
            return URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            return fileName;
        }
    }
}
