package com.gxwebsoft.credit.controller;

import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.credit.entity.CreditGqdj;
import com.gxwebsoft.credit.param.CreditGqdjImportParam;
import com.gxwebsoft.credit.param.CreditGqdjParam;
import com.gxwebsoft.credit.service.CreditCompanyService;
import com.gxwebsoft.credit.service.CreditCompanyRecordCountService;
import com.gxwebsoft.credit.service.CreditGqdjService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 股权冻结控制器
 *
 * @author 科技小王子
 * @since 2025-12-19 19:50:37
 */
@Tag(name = "股权冻结管理")
@RestController
@RequestMapping("/api/credit/credit-gqdj")
public class CreditGqdjController extends BaseController {
    @Resource
    private CreditGqdjService creditGqdjService;

    @Resource
    private BatchImportSupport batchImportSupport;

    @Resource
    private CreditCompanyService creditCompanyService;

    @Resource
    private CreditCompanyRecordCountService creditCompanyRecordCountService;

    @Operation(summary = "分页查询股权冻结")
    @GetMapping("/page")
    public ApiResult<PageResult<CreditGqdj>> page(CreditGqdjParam param) {
        // 使用关联查询
        return success(creditGqdjService.pageRel(param));
    }

    @Operation(summary = "查询全部股权冻结")
    @GetMapping()
    public ApiResult<List<CreditGqdj>> list(CreditGqdjParam param) {
        // 使用关联查询
        return success(creditGqdjService.listRel(param));
    }

    @Operation(summary = "根据id查询股权冻结")
    @GetMapping("/{id}")
    public ApiResult<CreditGqdj> get(@PathVariable("id") Integer id) {
        // 使用关联查询
        return success(creditGqdjService.getByIdRel(id));
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:save')")
    @OperationLog
    @Operation(summary = "添加股权冻结")
    @PostMapping()
    public ApiResult<?> save(@RequestBody CreditGqdj creditGqdj) {
        // 记录当前登录用户id
        // User loginUser = getLoginUser();
        // if (loginUser != null) {
        // creditGqdj.setUserId(loginUser.getUserId());
        // }
        if (creditGqdjService.save(creditGqdj)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:update')")
    @OperationLog
    @Operation(summary = "修改股权冻结")
    @PutMapping()
    public ApiResult<?> update(@RequestBody CreditGqdj creditGqdj) {
        if (creditGqdjService.updateById(creditGqdj)) {
            return success("修改成功");
        }
        return fail("修改失败");
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:remove')")
    @OperationLog
    @Operation(summary = "删除股权冻结")
    @DeleteMapping("/{id}")
    public ApiResult<?> remove(@PathVariable("id") Integer id) {
        if (batchImportSupport.hardRemoveById(CreditGqdj.class, id)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:save')")
    @OperationLog
    @Operation(summary = "批量添加股权冻结")
    @PostMapping("/batch")
    public ApiResult<?> saveBatch(@RequestBody List<CreditGqdj> list) {
        if (creditGqdjService.saveBatch(list)) {
            return success("添加成功");
        }
        return fail("添加失败");
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:update')")
    @OperationLog
    @Operation(summary = "批量修改股权冻结")
    @PutMapping("/batch")
    public ApiResult<?> removeBatch(@RequestBody BatchParam<CreditGqdj> batchParam) {
        if (batchParam.update(creditGqdjService, "id")) {
            return success("修改成功");
        }
        return fail("修改失败");
    }

    @PreAuthorize("hasAuthority('credit:creditGqdj:remove')")
    @OperationLog
    @Operation(summary = "批量删除股权冻结")
    @DeleteMapping("/batch")
    public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
        if (batchImportSupport.hardRemoveByIds(CreditGqdj.class, ids)) {
            return success("删除成功");
        }
        return fail("删除失败");
    }

    /**
     * 根据当事人/企业名称匹配企业并更新 companyId（匹配 CreditCompany.name / CreditCompany.matchName）
     *
     * <p>默认仅更新 companyId 为空/0 的记录；如需覆盖更新，传 onlyNull=false。</p>
     */
    @PreAuthorize("hasAuthority('credit:creditGqdj:update')")
    @OperationLog
    @Operation(summary = "根据企业名称匹配并更新companyId")
    @PostMapping("/company-id/refresh")
    public ApiResult<Map<String, Object>> refreshCompanyIdByCompanyName(
        @RequestParam(value = "onlyNull", required = false, defaultValue = "true") Boolean onlyNull,
        @RequestParam(value = "limit", required = false) Integer limit
    ) {
        User loginUser = getLoginUser();
        Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;

        // Match companyId by any party/company-name column (e.g. plaintiff/appellant, defendant/appellee).
        BatchImportSupport.CompanyIdRefreshStats stats = batchImportSupport.refreshCompanyIdByCompanyNames(
            creditGqdjService,
            creditCompanyService,
            currentTenantId,
            onlyNull,
            limit,
            CreditGqdj::getId,
            CreditGqdj::setId,
            CreditGqdj::getCompanyId,
            CreditGqdj::setCompanyId,
            CreditGqdj::getHasData,
            CreditGqdj::setHasData,
            CreditGqdj::getTenantId,
            CreditGqdj::new,
            CreditGqdj::getPlaintiffAppellant,
            CreditGqdj::getAppellee
        );

        if (!stats.anyDataRead) {
            return success("无可更新数据", stats.toMap());
        }
        return success("更新完成，更新" + stats.updated + "条", stats.toMap());
    }

    /**
     * 批量导入股权冻结司法大数据
     */
    @PreAuthorize("hasAuthority('credit:creditGqdj:save')")
    @Operation(summary = "批量导入股权冻结司法大数据")
    @PostMapping("/import")
    public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file,
                                               @RequestParam(value = "companyId", required = false) Integer companyId) {
        List<String> errorMessages = new ArrayList<>();
        int successCount = 0;
        Set<Integer> touchedCompanyIds = new HashSet<>();

        try {
            int sheetIndex = ExcelImportSupport.findSheetIndex(file, "股权冻结", 0);
            // Prefer the "best" header configuration; many upstream files have extra title rows or multi-row headers.
            ExcelImportSupport.ImportResult<CreditGqdjImportParam> importResult = ExcelImportSupport.readBest(
                file,
                CreditGqdjImportParam.class,
                this::isEmptyImportRow,
                // Score rows that look like real data (at least has a case number in either column).
                p -> p != null
                    && (!ImportHelper.isBlank(p.getCaseNumber())
                    || !ImportHelper.isBlank(p.getCaseNumber2())
                ),
                sheetIndex
            );
            List<CreditGqdjImportParam> list = importResult.getData();
            int usedTitleRows = importResult.getTitleRows();
            int usedHeadRows = importResult.getHeadRows();
            int usedSheetIndex = importResult.getSheetIndex();

            if (CollectionUtils.isEmpty(list)) {
                // Fallback: try other sheets if the named/default sheet doesn't match.
                importResult = ExcelImportSupport.readAnySheetBest(
                    file,
                    CreditGqdjImportParam.class,
                    this::isEmptyImportRow,
                    p -> p != null
                        && (!ImportHelper.isBlank(p.getCaseNumber())
                        || !ImportHelper.isBlank(p.getCaseNumber2()))
                );
                list = importResult.getData();
                usedTitleRows = importResult.getTitleRows();
                usedHeadRows = importResult.getHeadRows();
                usedSheetIndex = importResult.getSheetIndex();
            }

            if (CollectionUtils.isEmpty(list)) {
                return fail("未读取到数据，请确认模板表头与示例格式一致", null);
            }

            User loginUser = getLoginUser();
            Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
            Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
            // easypoi 默认不会读取单元格超链接地址；url 通常挂在“案号/执行通知文书号”列的超链接中，需要额外读取回填。
            String caseNumberHeader = "执行通知文书号";
            Map<String, String> urlByCaseNumber = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader);
            // Some upstream sources use "案号" as the case number header.
            Map<String, String> urlByCaseNumberFromAh = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
            urlByCaseNumberFromAh.forEach(urlByCaseNumber::putIfAbsent);
            // Some upstream sources use "暗号" as the case number header.
            Map<String, String> urlByCaseNumberFromAh2 = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号");
            urlByCaseNumberFromAh2.forEach(urlByCaseNumber::putIfAbsent);
            // 有些源文件会单独提供“url/网址/链接”等列（可能是纯文本也可能是超链接）
            Map<String, String> urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "url");
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "URL");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "网址");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "链接");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                // Try again with "案号" as the key column name.
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "url");
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "URL");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "网址");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "链接");
                }
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                // Try again with "暗号" as the key column name.
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "url");
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "URL");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "网址");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "链接");
                }
            }

            final int chunkSize = 500;
            final int mpBatchSize = 500;
            List<CreditGqdj> chunkItems = new ArrayList<>(chunkSize);
            List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);

            for (int i = 0; i < list.size(); i++) {
                CreditGqdjImportParam param = list.get(i);
                try {
                    CreditGqdj item = convertImportParamToEntity(param);
                    if (!ImportHelper.isBlank(item.getCaseNumber())) {
                        String key = item.getCaseNumber().trim();
                        String link = urlByCaseNumber.get(key);
                        if (ImportHelper.isBlank(link)) {
                            link = urlByCaseNumberFromUrlCol.get(key);
                        }
                        if (link != null && !link.isEmpty()) {
                            item.setUrl(link);
                        }
                    }

                    if (item.getCompanyId() == null && companyId != null) {
                        item.setCompanyId(companyId);
                    }
                    if (item.getCompanyId() != null && item.getCompanyId() > 0) {
                        touchedCompanyIds.add(item.getCompanyId());
                    }
                    if (item.getUserId() == null && currentUserId != null) {
                        item.setUserId(currentUserId);
                    }
                    if (item.getTenantId() == null && currentTenantId != null) {
                        item.setTenantId(currentTenantId);
                    }
                    if (item.getStatus() == null) {
                        item.setStatus(0);
                    }
                    if (item.getRecommend() == null) {
                        item.setRecommend(0);
                    }
                    if (item.getDeleted() == null) {
                        item.setDeleted(0);
                    }

                    int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
                    if (ImportHelper.isBlank(item.getCaseNumber())) {
                        errorMessages.add("第" + excelRowNumber + "行：案号不能为空");
                        continue;
                    }

                    if (item.getCompanyId() != null && item.getCompanyId() > 0) {
                        touchedCompanyIds.add(item.getCompanyId());
                    }

                    chunkItems.add(item);
                    chunkRowNumbers.add(excelRowNumber);
                    if (chunkItems.size() >= chunkSize) {
                        successCount += batchImportSupport.persistInsertOnlyChunk(
                            creditGqdjService,
                            chunkItems,
                            chunkRowNumbers,
                            mpBatchSize,
                            CreditGqdj::getCaseNumber,
                            "",
                            errorMessages
                        );
                        chunkItems.clear();
                        chunkRowNumbers.clear();
                    }
                } catch (Exception e) {
                    int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
                    errorMessages.add("第" + excelRowNumber + "行：" + e.getMessage());
                    e.printStackTrace();
                }
            }

            if (!chunkItems.isEmpty()) {
                successCount += batchImportSupport.persistInsertOnlyChunk(
                    creditGqdjService,
                    chunkItems,
                    chunkRowNumbers,
                    mpBatchSize,
                    CreditGqdj::getCaseNumber,
                    "",
                    errorMessages
                );
            }

            creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.GQDJ, touchedCompanyIds);

            if (errorMessages.isEmpty()) {
                return success("成功导入" + successCount + "条数据", null);
            } else {
                return success("导入完成，成功" + successCount + "条，失败" + errorMessages.size() + "条", errorMessages);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return fail("导入失败：" + e.getMessage(), null);
        }
    }

    /**
     * 批量导入历史股权冻结（仅解析“历史股权冻结”选项卡）
     * 规则：使用数据库唯一索引约束，重复数据不导入。
     */
    @PreAuthorize("hasAuthority('credit:creditGqdj:save')")
    @Operation(summary = "批量导入历史股权冻结司法大数据")
    @PostMapping("/import/history")
    public ApiResult<List<String>> importHistoryBatch(@RequestParam("file") MultipartFile file,
                                                      @RequestParam(value = "companyId", required = false) Integer companyId) {
        List<String> errorMessages = new ArrayList<>();
        int successCount = 0;
        Set<Integer> touchedCompanyIds = new HashSet<>();

        try {
            int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史股权冻结");
            if (sheetIndex < 0) {
                return fail("未读取到数据，请确认文件中存在“历史股权冻结”选项卡且表头与示例格式一致", null);
            }

            ExcelImportSupport.ImportResult<CreditGqdjImportParam> importResult = ExcelImportSupport.readBest(
                file,
                CreditGqdjImportParam.class,
                this::isEmptyImportRow,
                p -> p != null
                    && (!ImportHelper.isBlank(p.getCaseNumber())
                    || !ImportHelper.isBlank(p.getCaseNumber2())),
                sheetIndex
            );
            List<CreditGqdjImportParam> list = importResult.getData();
            int usedTitleRows = importResult.getTitleRows();
            int usedHeadRows = importResult.getHeadRows();
            int usedSheetIndex = importResult.getSheetIndex();

            if (CollectionUtils.isEmpty(list)) {
                return fail("未读取到数据，请确认模板表头与示例格式一致", null);
            }

            User loginUser = getLoginUser();
            Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
            Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;

            String caseNumberHeader = "执行通知文书号";
            Map<String, String> urlByCaseNumber = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader);
            Map<String, String> urlByCaseNumberFromAh = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
            urlByCaseNumberFromAh.forEach(urlByCaseNumber::putIfAbsent);
            Map<String, String> urlByCaseNumberFromAh2 = ExcelImportSupport.readHyperlinksByHeaderKey(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号");
            urlByCaseNumberFromAh2.forEach(urlByCaseNumber::putIfAbsent);
            Map<String, String> urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "url");
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "URL");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "网址");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "链接");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "url");
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "URL");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "网址");
                }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "链接");
            }
            if (urlByCaseNumberFromUrlCol.isEmpty()) {
                urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                    file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "url");
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "URL");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "网址");
                }
                if (urlByCaseNumberFromUrlCol.isEmpty()) {
                    urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
                        file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "链接");
                }
            }
            }

            final int chunkSize = 500;
            final int mpBatchSize = 500;
            List<CreditGqdj> chunkItems = new ArrayList<>(chunkSize);
            List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);

            for (int i = 0; i < list.size(); i++) {
                CreditGqdjImportParam param = list.get(i);
                int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
                try {
                    CreditGqdj item = convertImportParamToEntity(param);
                    if (item.getCaseNumber() != null) {
                        item.setCaseNumber(item.getCaseNumber().trim());
                    }
                    if (ImportHelper.isBlank(item.getCaseNumber())) {
                        errorMessages.add("第" + excelRowNumber + "行：案号不能为空");
                        continue;
                    }

                    String key = item.getCaseNumber();
                    String link = urlByCaseNumber.get(key);
                    if (ImportHelper.isBlank(link)) {
                        link = urlByCaseNumberFromUrlCol.get(key);
                    }
                    if (!ImportHelper.isBlank(link)) {
                        item.setUrl(link.trim());
                    }

                    if (item.getCompanyId() == null && companyId != null) {
                        item.setCompanyId(companyId);
                    }
                    if (item.getUserId() == null && currentUserId != null) {
                        item.setUserId(currentUserId);
                    }
                    if (item.getTenantId() == null && currentTenantId != null) {
                        item.setTenantId(currentTenantId);
                    }
                    if (item.getStatus() == null) {
                        item.setStatus(0);
                    }
                    if (item.getDeleted() == null) {
                        item.setDeleted(0);
                    }
                    // 历史导入的数据统一标记为“失效”
                    item.setDataType("失效");

                    if (item.getRecommend() == null) {
                        item.setRecommend(0);
                    }
                    if (item.getCompanyId() != null && item.getCompanyId() > 0) {
                        touchedCompanyIds.add(item.getCompanyId());
                    }

                    chunkItems.add(item);
                    chunkRowNumbers.add(excelRowNumber);
                    if (chunkItems.size() >= chunkSize) {
                        successCount += batchImportSupport.persistInsertOnlyChunk(
                            creditGqdjService,
                            chunkItems,
                            chunkRowNumbers,
                            mpBatchSize,
                            CreditGqdj::getCaseNumber,
                            "",
                            errorMessages
                        );
                        chunkItems.clear();
                        chunkRowNumbers.clear();
                    }
                } catch (Exception e) {
                    errorMessages.add("第" + excelRowNumber + "行：" + e.getMessage());
                    e.printStackTrace();
                }
            }

            if (!chunkItems.isEmpty()) {
                successCount += batchImportSupport.persistInsertOnlyChunk(
                    creditGqdjService,
                    chunkItems,
                    chunkRowNumbers,
                    mpBatchSize,
                    CreditGqdj::getCaseNumber,
                    "",
                    errorMessages
                );
            }

            creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.GQDJ, touchedCompanyIds);

            if (errorMessages.isEmpty()) {
                return success("成功导入" + successCount + "条数据", null);
            }
            return success("导入完成，成功" + successCount + "条，失败" + errorMessages.size() + "条", errorMessages);
        } catch (Exception e) {
            e.printStackTrace();
            return fail("导入失败：" + e.getMessage(), null);
        }
    }

    /**
     * 下载股权冻结导入模板
     */
    @Operation(summary = "下载股权冻结导入模板")
    @GetMapping("/import/template")
    public void downloadTemplate(HttpServletResponse response) throws IOException {
        List<CreditGqdjImportParam> templateList = new ArrayList<>();

        CreditGqdjImportParam example = new CreditGqdjImportParam();
        example.setDataType("股权冻结");
        example.setPlaintiffAppellant("原告示例");
        example.setAppellee("被告示例");
        example.setCaseNumber("（2024）示例案号");
        example.setInvolvedAmount("100000");
        example.setCourtName("示例法院");
        example.setDataStatus("已公开");
        templateList.add(example);

        Workbook workbook = ExcelImportSupport.buildTemplate("股权冻结导入模板", "股权冻结", CreditGqdjImportParam.class, templateList);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=credit_gqdj_import_template.xlsx");

        workbook.write(response.getOutputStream());
        workbook.close();
    }

    private boolean isEmptyImportRow(CreditGqdjImportParam param) {
        if (param == null) {
            return true;
        }
        // Don't over-filter here: if some columns are mapped but case number header differs,
        // we still want to read the row and report "案号不能为空" instead of "未读取到数据".
        return ImportHelper.isBlank(param.getCaseNumber())
            && ImportHelper.isBlank(param.getCaseNumber2())
            && ImportHelper.isBlank(param.getAppellee())
            && ImportHelper.isBlank(param.getAppellee2())
            && ImportHelper.isBlank(param.getPlaintiffAppellant())
            && ImportHelper.isBlank(param.getPlaintiffAppellant2())
            && ImportHelper.isBlank(param.getInvolvedAmount())
            && ImportHelper.isBlank(param.getCourtName())
            && ImportHelper.isBlank(param.getDataType())
            && ImportHelper.isBlank(param.getDataStatus())
            && ImportHelper.isBlank(param.getDataStatus2())
            && ImportHelper.isBlank(param.getFreezeDateStart())
            && ImportHelper.isBlank(param.getFreezeDateEnd())
            && ImportHelper.isBlank(param.getFreezeDateStart2())
            && ImportHelper.isBlank(param.getFreezeDateEnd2())
            && ImportHelper.isBlank(param.getPublicDate());
    }

    private CreditGqdj convertImportParamToEntity(CreditGqdjImportParam param) {
        CreditGqdj entity = new CreditGqdj();

        // Template compatibility: some sources use alternate headers for the same columns.
        String appellee = !ImportHelper.isBlank(param.getAppellee()) ? param.getAppellee() : param.getAppellee2();
        String plaintiffAppellant = !ImportHelper.isBlank(param.getPlaintiffAppellant())
            ? param.getPlaintiffAppellant()
            : param.getPlaintiffAppellant2();

        if (!ImportHelper.isBlank(param.getCaseNumber2())) {
            entity.setCaseNumber(param.getCaseNumber2());
        } else {
            entity.setCaseNumber(param.getCaseNumber());
        }
        entity.setAppellee(appellee);
        entity.setPlaintiffAppellant(plaintiffAppellant);
        entity.setInvolvedAmount(param.getInvolvedAmount());
        entity.setCourtName(param.getCourtName());
        if (!ImportHelper.isBlank(param.getDataStatus2())) {
            entity.setDataStatus(param.getDataStatus2());
        } else {
            entity.setDataStatus(param.getDataStatus());
        }
        entity.setDataType(param.getDataType());
        entity.setPublicDate(param.getPublicDate());
        if (!ImportHelper.isBlank(param.getFreezeDateStart2())) {
            entity.setFreezeDateStart(param.getFreezeDateStart2());
        } else {
            entity.setFreezeDateStart(param.getFreezeDateStart());
        }
        if (!ImportHelper.isBlank(param.getFreezeDateEnd2())) {
            entity.setFreezeDateEnd(param.getFreezeDateEnd2());
        } else {
            entity.setFreezeDateEnd(param.getFreezeDateEnd());
        }

        return entity;
    }

}
