package com.microservices.pms.project.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.JSONUtils;
import com.microservices.common.core.utils.PageUtils;
import com.microservices.common.core.utils.PinYinStringUtils;
import com.microservices.common.core.utils.bean.BeanUtils;
import com.microservices.common.core.web.page.GenericsTableDataInfo;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.pms.common.service.IPmsCommonService;
import com.microservices.pms.enterprise.domain.PmsEnterprise;
import com.microservices.pms.enterprise.service.IPmsEnterpriseService;
import com.microservices.pms.product.domain.PmsProductModule;
import com.microservices.pms.product.domain.PmsProductRequirement;
import com.microservices.pms.product.domain.enums.ProductReqStatus;
import com.microservices.pms.product.domain.vo.ProductReqSpecsAssociatedTestcaseDataVo;
import com.microservices.pms.product.domain.vo.TreeSelect;
import com.microservices.pms.product.service.IPmsProductModuleService;
import com.microservices.pms.product.service.IPmsProductRequirementService;
import com.microservices.pms.product.service.IPmsProductRequirementTagService;
import com.microservices.pms.project.domain.*;
import com.microservices.pms.project.domain.vo.*;
import com.microservices.pms.project.mapper.PmsProjectTestcaseMapper;
import com.microservices.pms.project.mapper.PmsProjectTestcaseStepMapper;
import com.microservices.pms.project.mapper.PmsProjectTestsheetCasesMapper;
import com.microservices.pms.project.service.IPmsProjectService;
import com.microservices.pms.project.service.IPmsProjectTestcaseService;
import com.microservices.pms.project.service.IPmsProjectTestcaseStepService;
import com.microservices.pms.project.service.IPmsProjectTestcaseTypeService;
import com.microservices.pms.utils.PmsConstants;
import com.microservices.pms.utils.PmsUtils;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.utils.FeignUtils;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.microservices.common.core.utils.PageUtils.startPage;

/**
 * 测试用例管理Service业务层处理
 *
 * @author otto
 * @date 2023-11-22
 */
@Service
public class PmsProjectTestcaseServiceImpl implements IPmsProjectTestcaseService {
    @Autowired
    private PmsProjectTestcaseMapper pmsProjectTestcaseMapper;
    @Autowired
    private IPmsProjectTestcaseTypeService pmsProjectTestcaseTypeService;
    @Autowired
    private IPmsCommonService pmsCommonService;
    @Autowired
    private IPmsProductModuleService pmsProductModuleService;
    @Autowired
    private IPmsProjectTestcaseStepService pmsProjectTestcaseStepService;
    @Autowired
    private IPmsEnterpriseService pmsEnterpriseService;
    @Autowired
    private IPmsProjectService pmsProjectService;
    @Autowired
    private IPmsProductRequirementTagService pmsProductRequirementTagService;
    @Autowired
    private PmsProjectTestsheetCasesMapper pmsProjectTestsheetCasesMapper;
    @Autowired
    private IPmsProductRequirementService pmsProductRequirementService;
    @Autowired
    private PmsProjectTestcaseStepMapper pmsProjectTestcaseStepMapper;
    @Autowired
    private RemoteUserService remoteUserService;

    /**
     * 查询测试用例管理
     *
     * @param id 测试用例管理主键
     * @return 测试用例管理
     */
    @Override
    public PmsProjectTestcase selectPmsProjectTestcaseById(Long id) {
        PmsProjectTestcase pmsProjectTestcase = pmsProjectTestcaseMapper.selectPmsProjectTestcaseById(id);
        if (pmsProjectTestcase == null) {
            throw new ServiceException("该测试用例不存在(测试用例ID[%s])", id);
        }
        if(pmsProjectTestcase.getIsImport() == null){
            pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
        }
        return pmsProjectTestcase;
    }

    /**
     * 查询测试用例管理列表
     *
     * @param pmsProjectTestcase 测试用例管理
     * @return 测试用例管理
     */
    @Override
    public GenericsTableDataInfo<PmsProjectTestcaseDataVo> selectPmsProjectTestcasePageList(PmsProjectTestcase pmsProjectTestcase) {
        pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
        startPage();
        List<PmsProjectTestcase> pmsProjectTestcaseList = pmsProjectTestcaseMapper.selectPmsProjectTestcaseList(pmsProjectTestcase);
        HashMap<String, Object> hashMap = new HashMap<>();
        return PageUtils.toPage(pmsProjectTestcaseList, x -> toPmsProjectTestcaseDataVo(x, hashMap));
    }

    private PmsProjectTestcaseDataVo toPmsProjectTestcaseDataVo(PmsProjectTestcase pmsProjectTestcase, HashMap<String, Object> hashMap) {
        PmsProjectTestcaseDataVo pmsProjectTestcaseDataVo = pmsProjectTestcase.toPmsProjectTestcaseDataVo();
        // 设置用例维护人
        pmsProjectTestcaseDataVo.setAssigneeUser(
                PmsUtils.getSelectObjectInCache(
                        hashMap
                        , pmsCommonService::getSimpleSysUserByGitlinkUserId
                        , pmsProjectTestcase.getAssigneeGitlinkId()));
        // 设置关联项目
        pmsProjectTestcaseDataVo.setPmsProject(
                PmsUtils.getSelectObjectInCache(
                        hashMap
                        , pmsProjectService::selectAndCheckPmsProjectById
                        , pmsProjectTestcase.getPmsProjectId()));
        // 设置用例类型
        pmsProjectTestcaseDataVo.setType(
                PmsUtils.getSelectObjectInCache(
                        hashMap
                        , pmsProjectTestcaseTypeService::selectPmsProjectTestcaseTypeById
                        , pmsProjectTestcase.getTypeId()));
        // 设置标记列表
        pmsProjectTestcaseDataVo.setTagList(
                PmsUtils.getSelectObjectInCache(
                        hashMap
                        , pmsProductRequirementTagService::selectTagListByIds
                        , pmsProjectTestcase.getTagIds()));
        // 设置用例模块
        if (pmsProjectTestcase.getPmsModuleId() != null) {
            pmsProjectTestcaseDataVo.setPmsModule(
                    PmsUtils.getSelectObjectInCache(
                            hashMap
                            , x -> pmsProductModuleService.selectPmsProductModuleById(x, false)
                            , pmsProjectTestcase.getPmsModuleId()));
        }
        if (pmsProjectTestcase.getIsImport() == null) {
            // 设置需求创建人
            pmsProjectTestcaseDataVo.setCreateBy(
                    PmsUtils.getSelectObjectInCache(
                            hashMap
                            , pmsCommonService::getNickNameByUsername
                            , pmsProjectTestcase.getCreateBy())
            );
            // 设置需求更新人
            pmsProjectTestcaseDataVo.setUpdateBy(
                    PmsUtils.getSelectObjectInCache(
                            hashMap
                            , pmsCommonService::getNickNameByUsername
                            , pmsProjectTestcase.getUpdateBy())
            );
        }
        // 设置产品需求
        if (pmsProjectTestcase.getProductReqSpecsId() != null) {
            pmsProjectTestcaseDataVo.setProductReqSpecs(
                    PmsUtils.getSelectObjectInCache(
                            hashMap
                            , x -> pmsProductRequirementService.selectPmsProductRequirementById(x, false)
                            , pmsProjectTestcase.getProductReqSpecsId())
            );
        }
        return pmsProjectTestcaseDataVo;
    }

    @Override
    public PmsProjectTestcaseDetailVo toPmsProjectTestcaseDetailVo(PmsProjectTestcase pmsProjectTestcase) {
        PmsProjectTestcaseDataVo pmsProjectTestcaseDataVo = toPmsProjectTestcaseDataVo(pmsProjectTestcase, null);
        PmsProjectTestcaseDetailVo pmsProjectTestcaseDetailVo = pmsProjectTestcaseDataVo.toPmsProjectTestcaseDetailVo();
        pmsProjectTestcaseDetailVo.setPreconditions(pmsProjectTestcase.getPreconditions());
        pmsProjectTestcaseDetailVo.setRemark(pmsProjectTestcase.getRemark());
        pmsProjectTestcaseDetailVo.setDescription(pmsProjectTestcase.getDescription());
        pmsProjectTestcaseDetailVo.setTestMethod(pmsProjectTestcase.getTestMethod());
        pmsProjectTestcaseDetailVo.setTerminationConditions(pmsProjectTestcase.getTerminationConditions());
        // 设置测试步骤列表
        PmsProjectTestcaseStep pmsProjectTestcaseStepSearch = new PmsProjectTestcaseStep();
        pmsProjectTestcaseStepSearch.setPmsTestcaseId(pmsProjectTestcase.getId());
        pmsProjectTestcaseDetailVo.setTestcaseStepList(pmsProjectTestcaseStepService.selectPmsProjectTestcaseStepList(pmsProjectTestcaseStepSearch));

        // 设置附件列表
        pmsProjectTestcaseDetailVo.setFileList(pmsCommonService.getAllFileByFileIdentifiers(pmsProjectTestcase.getFileIdentifiers()));
        if(pmsProjectTestcase.getIsImport() == null) {
            pmsCommonService.setBaseEntityVo(pmsProjectTestcaseDetailVo, pmsProjectTestcase);
        }
        return pmsProjectTestcaseDetailVo;
    }

    @Override
    public List<PmsProjectTestcase> selectPmsProjectTestcaseForSelectList(PmsProjectTestcase pmsProjectTestcase) {
        return pmsProjectTestcaseMapper.selectPmsProjectTestcaseForSelectList(pmsProjectTestcase);
    }

    @Override
    public List<PmsProjectTestcase> selectPmsProjectTestcaseForTestsheetList(PmsProjectTestcase pmsProjectTestcase) {
        return pmsProjectTestcaseMapper.selectPmsProjectTestcaseForTestsheetList(pmsProjectTestcase);
    }

    @Override
    public List<PmsProjectTestcase> selectPmsProjectTestcaseList(PmsProjectTestcase pmsProjectTestcaseSearch) {
        return pmsProjectTestcaseMapper.selectPmsProjectTestcaseList(pmsProjectTestcaseSearch);
    }

    @Override
    public List<ProductReqSpecsAssociatedTestcaseDataVo> selectReqSpecsAssociatedTestcaseList(Long reqSpecsId) {
        List<ProductReqSpecsAssociatedTestcaseDataVo> reqSpecsTestcaseDataVoList = new ArrayList<>();
        PmsProductRequirement pmsProductRequirement = pmsProductRequirementService.selectPmsProductRequirementById(reqSpecsId, false);
        if (ProductReqStatus.isUserReqStatus(pmsProductRequirement.getStatus())) {
            throw new ServiceException("原始需求无法查询关联的测试用例");
        }
        PmsProjectTestcase testcaseSearch = new PmsProjectTestcase();
        testcaseSearch.setProductReqSpecsId(reqSpecsId);
        List<PmsProjectTestcase> reqSpecsTestcaseList = pmsProjectTestcaseMapper.selectPmsProjectTestcaseList(testcaseSearch);
        HashMap<String, Object> hashMap = new HashMap<>();
        for (PmsProjectTestcase pmsProjectTestcase : reqSpecsTestcaseList) {
            ProductReqSpecsAssociatedTestcaseDataVo reqSpecsTestcaseDataVo = new ProductReqSpecsAssociatedTestcaseDataVo();
            BeanUtils.copyProperties(pmsProjectTestcase, reqSpecsTestcaseDataVo);
            reqSpecsTestcaseDataVo.setAssigneeUser(
                    PmsUtils.getSelectObjectInCache(
                            hashMap
                            , pmsCommonService::getSimpleSysUserByGitlinkUserId
                            , pmsProjectTestcase.getAssigneeGitlinkId()));
            PmsProjectTestsheetCases projectTestSheetCasesSearch = new PmsProjectTestsheetCases();
            projectTestSheetCasesSearch.setProjectTestcaseId(pmsProjectTestcase.getId());
            List<PmsProjectTestsheetCases> testsheetCasesList = pmsProjectTestsheetCasesMapper.selectPmsProjectTestsheetCasesList(projectTestSheetCasesSearch);
            reqSpecsTestcaseDataVo.setCountData(testsheetCasesList);
            reqSpecsTestcaseDataVoList.add(reqSpecsTestcaseDataVo);
        }
        return reqSpecsTestcaseDataVoList;
    }

    @Override
    public PmsProjectTestcase copyPmsProjectTestcase(Long testcaseId) {
        PmsProjectTestcase pmsProjectTestcase = selectPmsProjectTestcaseById(testcaseId);
        pmsProjectTestcase.setId(null);
        pmsProjectTestcase.setTags(pmsProjectTestcase.getTagIds());
        PmsProjectTestcaseStep pmsProjectTestcaseStepSearch = new PmsProjectTestcaseStep();
        pmsProjectTestcaseStepSearch.setPmsTestcaseId(testcaseId);
        List<PmsProjectTestcaseStepDataVo> selectPmsProjectTestcaseStepList = pmsProjectTestcaseStepService.selectPmsProjectTestcaseStepList(pmsProjectTestcaseStepSearch);
        List<PmsProjectTestcaseStep> pmsProjectTestcaseStepList = selectPmsProjectTestcaseStepList.stream()
                .map(x -> {
                    PmsProjectTestcaseStep pmsProjectTestcaseStep = new PmsProjectTestcaseStep();
                    BeanUtils.copyProperties(x, pmsProjectTestcaseStep);
                    pmsProjectTestcaseStep.setId(null);
                    return pmsProjectTestcaseStep;
                }).collect(Collectors.toList());
        pmsProjectTestcase.setTestcaseStepList(pmsProjectTestcaseStepList);
        insertPmsProjectTestcase(pmsProjectTestcase);
        return pmsProjectTestcase;
    }

    /**
     * 新增测试用例管理
     *
     * @param pmsProjectTestcase 测试用例管理
     * @return 结果
     */
    @Override
    public Long insertPmsProjectTestcase(PmsProjectTestcase pmsProjectTestcase) {
        pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
        if (pmsProjectTestcase.getAssigneeGitlinkId() == null) {
            // 未设置维护用户时默认为当前操作用户
            pmsProjectTestcase.setAssigneeGitlinkId(SecurityUtils.getGitlinkUserId());
        }
        if (pmsProjectTestcase.getTypeId() == null) {
            // 设置类型为默认值
            PmsProjectTestcaseType defaultType = pmsProjectTestcaseTypeService.selectDefaultPmsProjectTestcaseType();
            pmsProjectTestcase.setTypeId(defaultType.getId());
        }

        checkAndSetDefaultTestcase(pmsProjectTestcase, null);

        // 获取当前项目下测试用例的最大索引值
        Long maxIndex = pmsProjectTestcaseMapper.selectPmsProjectTestcaseMaxIndexByProjectId(pmsProjectTestcase.getPmsProjectId());
        if (maxIndex == null) {
            maxIndex = 1L;
        }
        pmsProjectTestcase.setIndex(++maxIndex);
        pmsProjectTestcase.setCreateBy(SecurityUtils.getUsername());
        pmsProjectTestcase.setCreateTime(DateUtils.getNowDate());
        pmsProjectTestcaseMapper.insertPmsProjectTestcase(pmsProjectTestcase);
        // 批量新增测试步骤
        pmsProjectTestcaseStepService.batchInsertPmsProjectTestcaseStep(pmsProjectTestcase.getId(), pmsProjectTestcase.getTestcaseStepList());
        return pmsProjectTestcase.getId();
    }

    /**
     * 校验测试用例数据是否正确并设置默认值
     *
     * @param pmsProjectTestcase 测试用例
     */
    private void checkAndSetDefaultTestcase(PmsProjectTestcase pmsProjectTestcase, PmsProjectTestcase oldPmsProjectTestcase) {
        PmsProject pmsProject = pmsProjectService.selectAndCheckPmsProjectById(pmsProjectTestcase.getPmsProjectId());
        if (pmsProjectTestcase.getAssigneeGitlinkId() != null) {
            if (oldPmsProjectTestcase == null
                    || !pmsProjectTestcase.getAssigneeGitlinkId().equals(oldPmsProjectTestcase.getAssigneeGitlinkId())) {
                // 检查维护用户id是否正确
                checkUserInEnterprise(pmsProjectTestcase.getAssigneeGitlinkId(), pmsProject);
            }
        }
        // 检查测试模块是否正确
        if (pmsProjectTestcase.getPmsModuleId() != null) {
            PmsProductModule pmsProductModule = pmsProductModuleService.selectPmsProductModuleById(pmsProjectTestcase.getPmsModuleId(), false);
            if (!pmsProductModule.getPmsProjectId().equals(pmsProjectTestcase.getPmsProjectId())) {
                throw new ServiceException("该模块不属于当前项目(模块名称[%s],项目名称[%s])", pmsProductModule.getModuleName(), pmsProject.getProjectName());
            }
        }
        // 检查测试用例类型是否正确
        if (pmsProjectTestcase.getTypeId() != null) {
            pmsProjectTestcaseTypeService.selectPmsProjectTestcaseTypeById(pmsProjectTestcase.getTypeId());
        }

        // 检查附件列表是否有异常数据
        pmsCommonService.getAllFileByFileIdentifiers(pmsProjectTestcase.getFileIdentifiers());

        // 校验标记是否存在
        pmsProductRequirementTagService.checkTags(pmsProjectTestcase.getTags(), PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE, pmsProject.getPmsEnterpriseId());
        pmsProjectTestcase.setTagIds(pmsProjectTestcase.getTags());

        if (pmsProjectTestcase.getProductReqSpecsId() != null) {
            PmsProductRequirement pmsProductRequirement = pmsProductRequirementService.selectPmsProductRequirementById(pmsProjectTestcase.getProductReqSpecsId(), false);
            if (ProductReqStatus.isUserReqStatus(pmsProductRequirement.getStatus())) {
                throw new ServiceException("该需求处于原始需求状态，不能关联测试用例");
            }
        }
    }

    private void checkUserInEnterprise(Long assigneeGitlinkId, PmsProject pmsProject) {
        SysUser sysUser = pmsCommonService.getSysUserByGitlinkId(assigneeGitlinkId);
        // 检查该用户是否在企业中
        boolean isExist = pmsEnterpriseService.checkUserInEnterprise(pmsProject.getPmsEnterpriseId(), sysUser.getUserId());
        if (!isExist) {
            PmsEnterprise pmsEnterprise = pmsEnterpriseService.selectPmsEnterpriseById(pmsProject.getPmsEnterpriseId());
            throw new ServiceException("该测试用例的维护人不在当前企业中(企业名称[%s],用户名[%s])", pmsEnterprise.getEnterpriseName(), sysUser.getUserName());
        }
    }

    /**
     * 修改测试用例管理
     *
     * @param pmsProjectTestcase 测试用例管理
     * @return 结果
     */
    @Override
    public int updatePmsProjectTestcase(PmsProjectTestcase pmsProjectTestcase) {
        PmsProjectTestcase oldPmsProjectTestcase = selectPmsProjectTestcaseById(pmsProjectTestcase.getId());
        pmsProjectTestcase.setPmsProjectId(oldPmsProjectTestcase.getPmsProjectId());
        checkAndSetDefaultTestcase(pmsProjectTestcase, oldPmsProjectTestcase);

        pmsProjectTestcase.setUpdateBy(SecurityUtils.getUsername());
        pmsProjectTestcase.setUpdateTime(DateUtils.getNowDate());

        // 批量更新测试步骤
        pmsProjectTestcaseStepService.batchUpdatePmsProjectTestcaseStep(pmsProjectTestcase.getId(), pmsProjectTestcase.getTestcaseStepList());
        return pmsProjectTestcaseMapper.updatePmsProjectTestcase(pmsProjectTestcase);
    }

    /**
     * 批量删除测试用例管理
     *
     * @param ids 需要删除的测试用例管理主键
     * @return 结果
     */
    @Override
    public int deletePmsProjectTestcaseByIds(Long[] ids) {
        int success = 0;
        for (Long id : ids) {
            success += deletePmsProjectTestcaseById(id);
        }
        return success;
    }

    /**
     * 删除测试用例管理信息
     *
     * @param id 测试用例管理主键
     * @return 结果
     */
    @Override
    public int deletePmsProjectTestcaseById(Long id) {
        PmsProjectTestcase pmsProjectTestcase = selectPmsProjectTestcaseById(id);
        // 删除测试用例下所有测试步骤
        pmsProjectTestcaseStepService.deletePmsProjectTestcaseStepByTestcaseId(pmsProjectTestcase.getId());
        // 删除测试用例关联测试单执行用例
        pmsProjectTestsheetCasesMapper.deletePmsProjectTestsheetCasesByTestcaseId(id);
        return pmsProjectTestcaseMapper.deletePmsProjectTestcaseById(id);
    }

    @Override
    public Integer selectPmsProjectTestcaseCount(PmsProjectTestcase pmsProjectTestcase) {
        pmsProjectService.selectPmsProjectById(pmsProjectTestcase.getPmsProjectId());
        return pmsProjectTestcaseMapper.selectPmsProjectTestcaseCount(pmsProjectTestcase);
    }

    @Override
    public PmsProjectTestcaseDetailVo selectPmsProjectTestcaseDetailVoById(Long id) {
        return toPmsProjectTestcaseDetailVo(selectPmsProjectTestcaseById(id));
    }

    @Override
    public void updatePmsProjectTestcaseToNoModule(Long moduleId) {
        pmsProjectTestcaseMapper.updatePmsProjectTestcaseToNoModule(moduleId, SecurityUtils.getUsername(), DateUtils.getNowDate());
    }

    @Override
    public int batchUpdatePmsProjectTestcase(JSONObject batchUpdateVo) {
        List<Long> idList = JSONUtils.getLongListByJsonArray(batchUpdateVo.getJSONArray("idList"));
        checkBatchUpdateInTheSameProject(idList);
        return batchUpdatePmsProjectTestcaseByJsonObject(idList, batchUpdateVo);
    }

    /**
     * 检查批量更新的测试用例是否属于同一个项目
     *
     * @param idList id列表
     */
    private void checkBatchUpdateInTheSameProject(List<Long> idList) {
        List<Long> projectIdList = pmsProjectTestcaseMapper.selectProjectIdListInTestcaseIdList(idList);
        if (projectIdList != null && projectIdList.size() == 1) {
            // 校验数据权限
            pmsProjectService.selectPmsProjectById(projectIdList.get(0));
            return;
        }
        throw new ServiceException("批量编辑测试用例时仅允许编辑同一项目下的测试用例");
    }


    private int batchUpdatePmsProjectTestcaseByJsonObject(List<Long> idList, JSONObject jsonObject) {
        PmsProjectTestcaseBatchUpdateVo pmsProjectTestcaseBatchUpdateVo = new PmsProjectTestcaseBatchUpdateVo();
        pmsProjectTestcaseBatchUpdateVo.setIdList(idList);
        if (jsonObject.containsKey("assigneeGitlinkId")) {
            pmsProjectTestcaseBatchUpdateVo.setAssigneeGitlinkId(jsonObject.getLong("assigneeGitlinkId"));
            pmsProjectTestcaseBatchUpdateVo.setAssigneeGitlinkIdIsChange(true);
        }
        if (jsonObject.containsKey("typeId")) {
            pmsProjectTestcaseBatchUpdateVo.setTypeId(jsonObject.getLong("typeId"));
            pmsProjectTestcaseBatchUpdateVo.setTypeIdIsChange(true);
        }
        if (jsonObject.containsKey("pmsModuleId")) {
            pmsProjectTestcaseBatchUpdateVo.setPmsModuleId(jsonObject.getLong("pmsModuleId"));
            pmsProjectTestcaseBatchUpdateVo.setPmsModuleIdIsChange(true);
        }
        if (jsonObject.containsKey("tags")) {
            pmsProjectTestcaseBatchUpdateVo.setTagIds(jsonObject.getString("tags"));
            pmsProjectTestcaseBatchUpdateVo.setTagIdsIsChange(true);
        }
        if (jsonObject.containsKey("productReqSpecsId")) {
            pmsProjectTestcaseBatchUpdateVo.setProductReqSpecsId(jsonObject.getLong("productReqSpecsId"));
            pmsProjectTestcaseBatchUpdateVo.setProductReqSpecsIdIsChange(true);
        }
        if (pmsProjectTestcaseBatchUpdateVo.thisIsChange()) {
            checkPmsProjectTestcaseBatchUpdateVo(pmsProjectTestcaseBatchUpdateVo);
            pmsProjectTestcaseBatchUpdateVo.setUpdateBy(SecurityUtils.getUsername());
            pmsProjectTestcaseBatchUpdateVo.setUpdateTime(DateUtils.getNowDate());
            return pmsProjectTestcaseMapper.batchUpdatePmsProjectTestcase(pmsProjectTestcaseBatchUpdateVo);
        }
        return 1;
    }

    private void checkPmsProjectTestcaseBatchUpdateVo(PmsProjectTestcaseBatchUpdateVo pmsProjectTestcaseBatchUpdateVo) {
        if (pmsProjectTestcaseBatchUpdateVo.getIdList() == null || pmsProjectTestcaseBatchUpdateVo.getIdList().isEmpty()) {
            return;
        }
        PmsProjectTestcase oldPmsProjectTestcase =
                pmsProjectTestcaseMapper.selectPmsProjectTestcaseById(pmsProjectTestcaseBatchUpdateVo.getIdList().get(0));
        PmsProject pmsProject = pmsProjectService.selectAndCheckPmsProjectById(oldPmsProjectTestcase.getPmsProjectId());
        if (pmsProjectTestcaseBatchUpdateVo.getAssigneeGitlinkIdIsChange()
                && pmsProjectTestcaseBatchUpdateVo.getAssigneeGitlinkId() != null) {
            // 检查维护用户id是否正确
            checkUserInEnterprise(pmsProjectTestcaseBatchUpdateVo.getAssigneeGitlinkId(), pmsProject);
        }

        // 检查测试模块是否正确
        if (pmsProjectTestcaseBatchUpdateVo.getPmsModuleIdIsChange()
                && pmsProjectTestcaseBatchUpdateVo.getPmsModuleId() != null) {
            PmsProductModule pmsProductModule = pmsProductModuleService.selectPmsProductModuleById(pmsProjectTestcaseBatchUpdateVo.getPmsModuleId(), false);
            if (!pmsProductModule.getPmsProjectId().equals(pmsProject.getId())) {
                throw new ServiceException("该模块不属于当前项目(模块名称[%s],项目名称[%s])", pmsProductModule.getModuleName(), pmsProject.getProjectName());
            }
        }
        // 检查测试用例类型是否正确
        if (pmsProjectTestcaseBatchUpdateVo.getTypeIdIsChange()
                && pmsProjectTestcaseBatchUpdateVo.getTypeId() != null) {
            pmsProjectTestcaseTypeService.selectPmsProjectTestcaseTypeById(pmsProjectTestcaseBatchUpdateVo.getTypeId());
        }

        if (pmsProjectTestcaseBatchUpdateVo.getTagIdsIsChange()) {
            // 校验标记是否存在
            pmsProductRequirementTagService.checkTags(
                    pmsProjectTestcaseBatchUpdateVo.getTagIds(),
                    PmsConstants.TESTCASE_TAG_AND_MODULE_TYPE,
                    pmsProject.getPmsEnterpriseId());
        }

        if (pmsProjectTestcaseBatchUpdateVo.getProductReqSpecsIdIsChange()) {
            PmsProductRequirement pmsProductRequirement = pmsProductRequirementService.selectPmsProductRequirementById(pmsProjectTestcaseBatchUpdateVo.getProductReqSpecsId(), false);
            if (ProductReqStatus.isUserReqStatus(pmsProductRequirement.getStatus())) {
                throw new ServiceException("该需求处于原始需求状态，不能关联测试用例");
            }
        }

    }

    //测试用例导出相关
    /**
     * 从数据库osredem-microservices，表pms_project_testcase中获取测试用例信息存到集合中
     *
     * @param item 项目id
     **/
    public List<PmsProjectTestcaseVo> getTestcase(Long item) {
        return pmsProjectTestcaseMapper.getTestcase(item);
    }

    /**
     * 从数据库osredem-microservices，表pms_project_testcase_step中获取测试步骤信息存到集合中
     *
     * @param item 表pms_project_testcase中index
     **/
    public List<PmsProjectTestcaseStepVo> getTestcaseStep(Long item) {
        return pmsProjectTestcaseStepMapper.getTestcaseStep(item);
    }

    private String typeIdtoSting(Long typeId) {
        switch (Math.toIntExact(typeId)) {
            case 1:
                return "功能测试";
            case 2:
                return "性能测试";
            case 3:
                return "接口测试";
            case 4:
                return "安装部署";
            case 5:
                return "安全相关";
            case 6:
                return "配置相关";
            default:
                return "其他";
        }
    }
    /**
     * 将项目id下所有测试用例信息存到集合中
     *
     * @param item 项目id
     **/
    public List<PendingOutputDataVo> GetPendingOutputData(Long item, Long moduleId) {
        List<PendingOutputDataVo> outputDatas = new ArrayList<PendingOutputDataVo>();
        // 数据库中信息类存到testcases集合中
        List<PmsProjectTestcaseVo> testcases = getTestcase(item);

        for (PmsProjectTestcaseVo testcase : testcases) {
            Long pmsModuleId = testcase.getPmsModuleId();
            boolean isEqual = Objects.equals(pmsModuleId, moduleId);
            if (isEqual) {
                List<PmsProjectTestcaseStepVo> steps = getTestcaseStep(testcase.getId());
                PendingOutputDataVo pendingOutputData = new PendingOutputDataVo();
                pendingOutputData.setId(testcase.getId());
                pendingOutputData.setTitle(testcase.getTitle());
                pendingOutputData.setTag_ids(testcase.getTagIds());
                pendingOutputData.setTest_type(typeIdtoSting(testcase.getTypeId()));
                pendingOutputData.setTitle(testcase.getTitle());
                pendingOutputData.setPreconditions(testcase.getPreconditions());
                pendingOutputData.setUpdate_by(testcase.getUpdateBy());
                pendingOutputData.setUpdate_time(testcase.getUpdateTime());
                pendingOutputData.setCreate_by(testcase.getCreateBy());
                pendingOutputData.setCreate_time(testcase.getCreateTime());
                pendingOutputData.setDescription(testcase.getDescription());
                pendingOutputData.setTest_method(testcase.getTestMethod());
                pendingOutputData.setTermination_conditions(testcase.getTerminationConditions());
                pendingOutputData.setIdentifier(testcase.getIdentifier());
                if (steps != null) {
                    for (PmsProjectTestcaseStepVo step : steps) {
                        pendingOutputData.addContent(step.getContent());
                        pendingOutputData.addExpected(step.getExpectedResult());
                    }
                }
                outputDatas.add(pendingOutputData);
            }
        }
        return outputDatas;
    }


    /**
     * 水平合并单元格（跨列合并）
     *
     * @param wordExtractor     表格对象
     * @param indentation       缩进值 1cm = 567
     * @param text              写入单元格字符串
     * @param isCenter          表格中段落是否居中
     * @param isBold            表格中段落是否加租
     */
    private void autoWrap(XWPFTableCell wordExtractor, Integer indentation, String text, boolean isCenter, boolean isBold) {
        XWPFParagraph paragraph = wordExtractor.getParagraphs().get(0);
        // 设置单元格文本垂直居中
        CTTcPr tcPr = wordExtractor.getCTTc().addNewTcPr();
        CTVerticalJc va = tcPr.addNewVAlign();
        va.setVal(STVerticalJc.CENTER);
        // 设置段前段后间距
        CTP ctp = paragraph.getCTP();
        CTPPr pPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
        CTSpacing spacing = pPr.isSetSpacing() ? pPr.getSpacing() : pPr.addNewSpacing();
        spacing.setBefore(BigInteger.valueOf(0)); // 段前间距
        spacing.setAfter(BigInteger.valueOf(0));
        //设置居中
        if (isCenter) {
            paragraph.setAlignment(ParagraphAlignment.CENTER);
        }
        // 设置缩进
        CTInd ind = pPr.isSetInd() ? pPr.getInd() : pPr.addNewInd();
        ind.setLeft(BigInteger.valueOf(indentation)); // 1cm = 567
        // 设置自动换行
        CTPPr pPr2 = paragraph.getCTP().addNewPPr();
        CTSpacing spacing2 = pPr2.addNewSpacing();
        spacing2.setLineRule(STLineSpacingRule.AUTO);
        // 设置字体样式
        XWPFRun run = paragraph.createRun();
        run.setText(text);//填写单元格内容
        //设置字体为五号 10.5*2
        CTRPr rPr = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();
        rPr.addNewSz().setVal(BigInteger.valueOf(21));
        rPr.addNewSzCs().setVal(BigInteger.valueOf(21));
        // 字体样式
        run.setFontFamily("Times New Roman");
        run.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
        run.setBold(isBold); // 是否加粗
    }

    /**
     * 水平合并单元格（跨列合并）
     *
     * @param table    表格对象
     * @param row      行索引
     * @param startCol 起始列索引
     * @param endCol   结束列索引
     */
    private static void mergeCellsHorizontally(XWPFTable table, int row, int startCol, int endCol) {
        for (int cellIndex = startCol; cellIndex <= endCol; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if (cellIndex == startCol) {
                // 第一个单元格设置合并范围
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // 后续单元格标记为继续合并
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * 隐藏表格的最后一行
     *
     * @param xwpfTabletable    表格对象
     */
    private void hideTableRow(XWPFTable xwpfTabletable) {
        XWPFTableRow templateRow = xwpfTabletable.getRow(xwpfTabletable.getNumberOfRows()-1); // 使用最后一行作为模板
        CTTrPr trPr = templateRow.getCtRow().isSetTrPr() ? templateRow.getCtRow().getTrPr() : templateRow.getCtRow().addNewTrPr();
        trPr.addNewHidden();//.setVal("on");  // 隐藏模板行

        //隐藏行中段落
        for (XWPFTableCell cell : templateRow.getTableCells()) {
            for (XWPFParagraph paragraph : cell.getParagraphs()) {
                CTPPr pPr = paragraph.getCTP().isSetPPr() ?
                        paragraph.getCTP().getPPr() :
                        paragraph.getCTP().addNewPPr();
                pPr.addNewRPr().addNewVanish();//.setVal("on");
            }
        }
    }


    /**
     * 设置表格边框格式
     *
     * @param xwpfTabletable    表格对象
     */
    private void setTableBorders(XWPFTable xwpfTabletable) {
        CTTblPr tblPr = xwpfTabletable.getCTTbl().getTblPr();
        CTTblBorders borders = tblPr.isSetTblBorders() ? tblPr.getTblBorders() : tblPr.addNewTblBorders();

        // 设置上边框
        CTBorder top = borders.isSetTop() ? borders.getTop() : borders.addNewTop();
        top.setVal(STBorder.SINGLE);  // 边框类型：单线
        top.setSz(BigInteger.valueOf(12));  // 边框大小：8 = 1pt
        top.setColor("000000");  // 边框颜色：黑色

        // 设置下边框
        CTBorder bottom = borders.isSetBottom() ? borders.getBottom() : borders.addNewBottom();
        bottom.setVal(STBorder.SINGLE);
        bottom.setSz(BigInteger.valueOf(6));
        bottom.setColor("000000");

        // 设置左边框
        CTBorder left = borders.isSetLeft() ? borders.getLeft() : borders.addNewLeft();
        left.setVal(STBorder.SINGLE);
        left.setSz(BigInteger.valueOf(12));
        left.setColor("000000");

        // 设置右边框
        CTBorder right = borders.isSetRight() ? borders.getRight() : borders.addNewRight();
        right.setVal(STBorder.SINGLE);
        right.setSz(BigInteger.valueOf(12));
        right.setColor("000000");

        // 设置内部水平边框
        CTBorder insideH = borders.isSetInsideH() ? borders.getInsideH() : borders.addNewInsideH();
        insideH.setVal(STBorder.SINGLE);
        insideH.setSz(BigInteger.valueOf(6));  // 比外边框细一些
        insideH.setColor("000000");

        // 设置内部垂直边框
        CTBorder insideV = borders.isSetInsideV() ? borders.getInsideV() : borders.addNewInsideV();
        insideV.setVal(STBorder.SINGLE);
        insideV.setSz(BigInteger.valueOf(6));
        insideV.setColor("000000");
    }

    private void fillTbale(XWPFTable xwpfTable, PendingOutputDataVo pendingOutputData) {
        for (XWPFTableRow row : xwpfTable.getRows()) {
            // 遍历行中的每个单元格
            for (XWPFTableCell cell : row.getTableCells()) {
                XWPFParagraph paragraph = cell.getParagraphs().get(0);
                // 设置单元格文本垂直居中
                CTTcPr tcPr = cell.getCTTc().addNewTcPr();
                CTVerticalJc va = tcPr.addNewVAlign();
                va.setVal(STVerticalJc.CENTER);
                // 设置段前段后间距
                CTP ctp = paragraph.getCTP();
                CTPPr pPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
                CTSpacing spacing = pPr.isSetSpacing() ? pPr.getSpacing() : pPr.addNewSpacing();
                spacing.setBefore(BigInteger.valueOf(0)); // 段前间距
                spacing.setAfter(BigInteger.valueOf(0));
            }
        }
        //固定文本部分
        autoWrap(xwpfTable.getRow(0).getCell(0), 125,"测试用例名称",false,true);
        autoWrap(xwpfTable.getRow(1).getCell(0), 125,"测试用例标识",false,true);
        autoWrap(xwpfTable.getRow(1).getCell(5), 125,"测试追踪",false,true);
        autoWrap(xwpfTable.getRow(2).getCell(0), 125,"测试用例描述",false,true);
        autoWrap(xwpfTable.getRow(3).getCell(0), 125,"测试方法",false,true);
        autoWrap(xwpfTable.getRow(4).getCell(0), 125,"测试类型",false,true);
        autoWrap(xwpfTable.getRow(5).getCell(0), 62,"前提和约束（包括初始化要求）",false,true);
        autoWrap(xwpfTable.getRow(6).getCell(0), 125,"测试终止条件",false,true);
        autoWrap(xwpfTable.getRow(7).getCell(0), 125,"测试过程",true,true);
        autoWrap(xwpfTable.getRow(8).getCell(0), 125,"序号",false,true);
        autoWrap(xwpfTable.getRow(8).getCell(1), 125,"输入及操作步骤",true,true);
        autoWrap(xwpfTable.getRow(8).getCell(4), 125,"期望测试结果",true,true);
        autoWrap(xwpfTable.getRow(8).getCell(6), 125,"评估准则",true,true);
        autoWrap(xwpfTable.getRow(8).getCell(8), 125,"实际测试结果",true,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 5).getCell(0), 62,"设计人员",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 5).getCell(4), 62,"设计日期",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 4).getCell(0), 62,"执行结果",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 3).getCell(0), 62,"问题标识",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 3).getCell(4), 62,"测试人员",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 2).getCell(0), 62,"测试监督员",false,true);
        autoWrap(xwpfTable.getRow(xwpfTable.getNumberOfRows() - 2).getCell(4), 62,"测试执行时间",false,true);

        //需要通过调取数据库中信息填入部分
        autoWrap(xwpfTable.getRow(0).getCell(3), 62,pendingOutputData.getTitle(),false,false);
        autoWrap(xwpfTable.getRow(1).getCell(3), 62,pendingOutputData.getIdentifier(),false,false);
        autoWrap(xwpfTable.getRow(2).getCell(3), 62,pendingOutputData.getDescription(),false,false);
        autoWrap(xwpfTable.getRow(3).getCell(3), 62,pendingOutputData.getTest_method(),false,false);
        autoWrap(xwpfTable.getRow(4).getCell(3), 62,pendingOutputData.getTest_type(),false,false);
        autoWrap(xwpfTable.getRow(5).getCell(3), 62,pendingOutputData.getPreconditions(),false,false);
        autoWrap(xwpfTable.getRow(6).getCell(3), 62,pendingOutputData.getTermination_conditions(),false,false);
        List<String> content = pendingOutputData.getContent_list();
        List<String> expected = pendingOutputData.getExpected_result();
        int i;
        for(i = 0;i<content.size();i++){
            autoWrap(xwpfTable.getRow(i + 9).getCell(0), 186,String.valueOf(i + 1),false,false);
            autoWrap(xwpfTable.getRow(i + 9).getCell(1), 62,content.get(i),false,false);
            autoWrap(xwpfTable.getRow(i + 9).getCell(4), 62,expected.get(i),false,false);
        }
        if(pendingOutputData.getUpdate_time()!=null ){
            String nikeName = Objects.requireNonNull(FeignUtils.getReturnData(remoteUserService.getSysUserByUserName(pendingOutputData.getUpdate_by(), SecurityConstants.INNER))).getNickName();
            autoWrap(xwpfTable.getRow(i + 9).getCell(2),62, nikeName, false,false);
            autoWrap(xwpfTable.getRow(i + 9).getCell(6),62, String.format("%1$tY-%1$tm-%1$td",pendingOutputData.getUpdate_time()), false,false);
        } else{
            if(pendingOutputData.getCreate_time()!=null ){
                String nikeName = Objects.requireNonNull(FeignUtils.getReturnData(remoteUserService.getSysUserByUserName(pendingOutputData.getCreate_by(), SecurityConstants.INNER))).getNickName();
                autoWrap(xwpfTable.getRow(i + 9).getCell(2),62, nikeName, false,false);
                autoWrap(xwpfTable.getRow(i + 9).getCell(6),62, String.format("%1$tY-%1$tm-%1$td",pendingOutputData.getCreate_time()), false,false);
            }else{
                autoWrap(xwpfTable.getRow(i + 10).getCell(6),62, "2025-01-01", false,false);
            }
        }

    }


    private void creatTableTemplate(XWPFDocument xwpfDocument,PendingOutputDataVo pendingOutputData) {
        //获取此用例步骤数
        int stepNumber = 1;
        if (pendingOutputData.getId() != null){
            stepNumber = pendingOutputData.getContent_list().size();
        }
        // 创建表格 - 宽度设置为所有列宽度之和
        XWPFTable table = xwpfDocument.createTable(stepNumber + 14, 9);

        // 不显示表格最后行
        hideTableRow(table);

        // 设置表格边框
        setTableBorders(table);

        // 设置表格宽度为页面宽度的100%
        table.setWidth("100%");

        // 设置表格布局为固定列宽
        CTTbl ctTable = table.getCTTbl();
        CTTblPr tblPr = ctTable.getTblPr() == null ? ctTable.addNewTblPr() : ctTable.getTblPr();
        CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
        tblPr.addNewTblLayout().setType(STTblLayoutType.FIXED);

        // 初始化表格行的宽度
        List<String> columnWidths = new ArrayList<>();
        columnWidths.add("7.6%");
        columnWidths.add("11%");
        columnWidths.add("4.8%");
        columnWidths.add("11.1%");
        columnWidths.add("19.8%");
        columnWidths.add("7.3%");
        columnWidths.add("9%");
        columnWidths.add("9.1%");
        columnWidths.add("20.1%");

        XWPFTableRow firstRow = table.getRow(0);
        for (int rowIndex = 0; rowIndex < table.getNumberOfRows(); rowIndex++) {
            XWPFTableRow row = table.getRow(rowIndex);
            for (int colIndex = 0; colIndex < columnWidths.size(); colIndex++) {
                row.getCell(colIndex).setWidth(columnWidths.get(colIndex));
            }
        }

        // 生成模板第1行（通过合并方式）
        mergeCellsHorizontally(table, 0, 0, 2);
        mergeCellsHorizontally(table, 0, 3, 8);
        // 生成模板第2行（通过合并方式）
        mergeCellsHorizontally(table, 1, 0, 2);
        mergeCellsHorizontally(table, 1, 3, 4);
        mergeCellsHorizontally(table, 1, 5, 6);
        mergeCellsHorizontally(table, 1, 7, 8);
        // 生成模板第3行（通过合并方式）
        mergeCellsHorizontally(table, 2, 0, 2);
        mergeCellsHorizontally(table, 2, 3, 8);
        // 生成模板第4行（通过合并方式）
        mergeCellsHorizontally(table, 3, 0, 2);
        mergeCellsHorizontally(table, 3, 3, 8);
        // 生成模板第5行（通过合并方式）
        mergeCellsHorizontally(table, 4, 0, 2);
        mergeCellsHorizontally(table, 4, 3, 8);
        // 生成模板第6行（通过合并方式）
        mergeCellsHorizontally(table, 5, 0, 2);
        mergeCellsHorizontally(table, 5, 3, 8);
        // 生成模板第7行（通过合并方式）
        mergeCellsHorizontally(table, 6, 0, 2);
        mergeCellsHorizontally(table, 6, 3, 8);
        // 生成模板第8行（通过合并方式）
        mergeCellsHorizontally(table, 7, 0, 8);
        // 生成模板第9行（通过合并方式）
        mergeCellsHorizontally(table, 8, 1, 3);
        mergeCellsHorizontally(table, 8, 4, 5);
        mergeCellsHorizontally(table, 8, 6, 7);
        // 生成模板操作步骤
        for (int stepRowIndex = 0; stepRowIndex < stepNumber; stepRowIndex++) {
            mergeCellsHorizontally(table, stepRowIndex + 9, 1, 3);
            mergeCellsHorizontally(table, stepRowIndex + 9, 4, 5);
            mergeCellsHorizontally(table, stepRowIndex + 9, 6, 7);
        }

        //生成模版倒数第四行
        mergeCellsHorizontally(table, table.getNumberOfRows() - 5, 0, 1);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 5, 2, 3);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 5, 4, 5);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 5, 6, 8);
        //生成模版倒数第三行
        mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 0, 1);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 4, 2, 8);
        //生成模版倒数第二行
        mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 0, 1);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 2, 3);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 4, 5);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 3, 6, 8);
        //生成模版倒数第一行
        mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 0, 1);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 2, 3);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 4, 5);
        mergeCellsHorizontally(table, table.getNumberOfRows() - 2, 6, 8);

        // 在表格中填值
        fillTbale(table, pendingOutputData);

    }


    // 添加表格标题（应该是<章节号>-<表格序号>）
    private static void addNumberedTableTitle(XWPFDocument doc, String titleText, int tableCount) {
        int chapter = 4;
        XWPFParagraph titleParagraph = doc.createParagraph();
        titleParagraph.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun run = titleParagraph.createRun();
        run.setText("表" + chapter + " - ");
        // 设置标题样式
        run.setFontFamily("Times New Roman");
        run.setFontFamily("黑体", XWPFRun.FontCharRange.eastAsia);
        run.setFontSize(12);
        CTRPr rPr0 = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();
        rPr0.addNewSz().setVal(BigInteger.valueOf(21));
        rPr0.addNewSzCs().setVal(BigInteger.valueOf(21));

        // 创建域开始字符
        XWPFRun startRun = titleParagraph.createRun();
        CTFldChar startFldChar = startRun.getCTR().addNewFldChar();
        startFldChar.setFldCharType(STFldCharType.BEGIN);

        // 创建域代码
        XWPFRun codeRun = titleParagraph.createRun();
        String domainCode = " SEQ Table \\* ARABIC \\s "; //+ chapter;
        codeRun.setText(domainCode);
        // 设置字体大小，可根据需要调整
        CTRPr rPr1 = codeRun.getCTR().isSetRPr() ? codeRun.getCTR().getRPr() : codeRun.getCTR().addNewRPr();
        rPr1.addNewSz().setVal(BigInteger.valueOf(21));
        rPr1.addNewSzCs().setVal(BigInteger.valueOf(21));
        codeRun.setFontFamily("Times New Roman");
        codeRun.setFontFamily("黑体", XWPFRun.FontCharRange.eastAsia);

        // 创建域分隔字符
        XWPFRun sepRun = titleParagraph.createRun();
        CTFldChar sepFldChar = sepRun.getCTR().addNewFldChar();
        // 设置字体大小，可根据需要调整
        CTRPr rPr2 = sepRun.getCTR().isSetRPr() ? sepRun.getCTR().getRPr() : sepRun.getCTR().addNewRPr();
        rPr2.addNewSz().setVal(BigInteger.valueOf(21));
        rPr2.addNewSzCs().setVal(BigInteger.valueOf(21));
        sepFldChar.setFldCharType(STFldCharType.SEPARATE);

        // 创建域结果
        XWPFRun resultRun = titleParagraph.createRun();
        resultRun.setText(String.valueOf(tableCount));
        // 设置字体大小，可根据需要调整
        CTRPr rPr3 = resultRun.getCTR().isSetRPr() ? resultRun.getCTR().getRPr() : resultRun.getCTR().addNewRPr();
        rPr3.addNewSz().setVal(BigInteger.valueOf(21));
        rPr3.addNewSzCs().setVal(BigInteger.valueOf(21));
        resultRun.setFontFamily("Times New Roman");
        resultRun.setFontFamily("黑体", XWPFRun.FontCharRange.eastAsia);

        // 创建域结束字符
        XWPFRun endRun = titleParagraph.createRun();
        CTFldChar endFldChar = endRun.getCTR().addNewFldChar();
        endFldChar.setFldCharType(STFldCharType.END);

        XWPFRun titlerun = titleParagraph.createRun();
        titlerun.setText(titleText);
        // 设置字体大小，可根据需要调整
        CTRPr rPr4 = titlerun.getCTR().isSetRPr() ? titlerun.getCTR().getRPr() : titlerun.getCTR().addNewRPr();
        rPr4.addNewSz().setVal(BigInteger.valueOf(21));
        rPr4.addNewSzCs().setVal(BigInteger.valueOf(21));
        titlerun.setFontFamily("Times New Roman");
        titlerun.setFontFamily("黑体", XWPFRun.FontCharRange.eastAsia);
    }

    /**
     * 创建标题样式
     * @param document 文档
     * @param styleId 样式 ID
     * @param styleName 样式名称
     * @param fontSize 字体大小
     */
    private static void createTitleStyle(XWPFDocument document, String styleId, String styleName, int fontSize, int level, BigInteger numId) {
        // 获取文档的样式管理器
        XWPFStyles styles = document.createStyles();
        // 创建新的样式
        CTStyle ctStyle = CTStyle.Factory.newInstance();
        ctStyle.setStyleId(styleId);

        CTString styleNameCT = CTString.Factory.newInstance();
        styleNameCT.setVal(styleName);
        ctStyle.setName(styleNameCT);

        // 设置样式类型为段落样式
        CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
        indentNumber.setVal(BigInteger.valueOf(1));
        ctStyle.setType(STStyleType.PARAGRAPH);

        // 设置字体和字号
        CTPPr ppr = ctStyle.addNewPPr();
        CTSpacing spacing = ppr.addNewSpacing();
        spacing.setAfter(BigInteger.valueOf(0));
        spacing.setLineRule(STLineSpacingRule.AUTO);

        CTDecimalNumber outlineLvl = ppr.addNewOutlineLvl();
        // 根据样式 ID 确定大纲级别
        if ("Heading1".equals(styleId)) {
            outlineLvl.setVal(BigInteger.valueOf(0));
        } else if ("Heading2".equals(styleId)) {
            outlineLvl.setVal(BigInteger.valueOf(1));
        } else if ("Heading3".equals(styleId)) {
            outlineLvl.setVal(BigInteger.valueOf(2));
        } else if ("Heading4".equals(styleId)) {
            outlineLvl.setVal(BigInteger.valueOf(3));
        } else if ("Heading5".equals(styleId)) {
            outlineLvl.setVal(BigInteger.valueOf(4));
        }

        // 关联编号
        CTNumPr numPr = ppr.addNewNumPr();
        numPr.addNewNumId().setVal(numId);
        numPr.addNewIlvl().setVal(BigInteger.valueOf(level - 1));

        // 设置字体样式
        CTRPr rpr = ctStyle.addNewRPr();
        CTFonts fonts = rpr.addNewRFonts();
        fonts.setEastAsia("黑体");
        fonts.setAscii("Times New Roman");
        fonts.setHAnsi("Times New Roman");
        rpr.addNewSz().setVal(BigInteger.valueOf(21));
        rpr.addNewSzCs().setVal(BigInteger.valueOf(21));

        // 将样式添加到样式管理器中
        styles.addStyle(new XWPFStyle(ctStyle));
    }

    // 创建多级列表编号模板
    private static BigInteger createMultiLevelNumbering(XWPFDocument doc) {
        XWPFNumbering numbering = doc.createNumbering();
        // 获取或创建 CTNumbering（POI 4.1.2 兼容方式）
        CTNumbering ctNumbering;
        try {
            Field f = XWPFNumbering.class.getDeclaredField("ctNumbering");
            f.setAccessible(true);
            ctNumbering = (CTNumbering) f.get(numbering);
            if (ctNumbering == null) {
                ctNumbering = CTNumbering.Factory.newInstance();
                f.set(numbering, ctNumbering);
            }
        } catch (Exception e) {
            throw new RuntimeException("无法初始化编号系统", e);
        }

        CTAbstractNum abstractNum = CTAbstractNum.Factory.newInstance();
        abstractNum.setAbstractNumId(BigInteger.ZERO);

        // 级别1样式：1.
        CTLvl lvl1 = abstractNum.addNewLvl();
        lvl1.setIlvl(BigInteger.ZERO);
        lvl1.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
        lvl1.addNewLvlText().setVal("%1.");
        lvl1.addNewLvlJc().setVal(STJc.LEFT);
        lvl1.addNewStart().setVal(BigInteger.ONE);

        // 级别2样式：1.1
        CTLvl lvl2 = abstractNum.addNewLvl();
        lvl2.setIlvl(BigInteger.ONE);
        lvl2.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
        lvl2.addNewLvlText().setVal("%1.%2.");
        lvl2.addNewLvlJc().setVal(STJc.LEFT);
        lvl2.addNewStart().setVal(BigInteger.ONE);

        // 级别3样式：1.1.1
        CTLvl lvl3 = abstractNum.addNewLvl();
        lvl3.setIlvl(BigInteger.valueOf(2));
        lvl3.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
        lvl3.addNewLvlText().setVal("%1.%2.%3.");
        lvl3.addNewLvlJc().setVal(STJc.LEFT);
        lvl3.addNewStart().setVal(BigInteger.ONE);

        // 级别4样式：1.1.1.1
        CTLvl lvl4 = abstractNum.addNewLvl();
        lvl4.setIlvl(BigInteger.valueOf(3));
        lvl4.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
        lvl4.addNewLvlText().setVal("%1.%2.%3.%4.");
        lvl4.addNewLvlJc().setVal(STJc.LEFT);
        lvl4.addNewStart().setVal(BigInteger.ONE);

        // 级别5样式：1.1.1.1.1
        CTLvl lvl5 = abstractNum.addNewLvl();
        lvl5.setIlvl(BigInteger.valueOf(4));
        lvl5.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
        lvl5.addNewLvlText().setVal("%1.%2.%3.%4.%5.");
        lvl5.addNewLvlJc().setVal(STJc.LEFT);
        lvl5.addNewStart().setVal(BigInteger.ONE);

        // 注册到文档
        ctNumbering.addNewAbstractNum().set(abstractNum);

        CTNum num = ctNumbering.addNewNum();
        num.setNumId(BigInteger.ONE);
        num.addNewAbstractNumId().setVal(BigInteger.ZERO);

        return BigInteger.ONE;
    }

    private static String getFirstPinyin(String text){
        String regEx = "\\pP|\\pS|\\s+";
        String reqTitleIdentifier = Pattern.compile(regEx).matcher(text).replaceAll("").trim();
        reqTitleIdentifier = PinYinStringUtils.toFirstChar(reqTitleIdentifier).toUpperCase();
        text = text + reqTitleIdentifier;
        return text;
    }

    private static void addNumberedHeading(XWPFDocument doc, String text, int level , boolean isGetPinYin) {
        XWPFParagraph para = doc.createParagraph();
        para.setStyle("Heading" + level);
        XWPFRun run = para.createRun();
        if (isGetPinYin) {
            text = getFirstPinyin(text);
        }
        run.setText(text);
    }

    // 插入表格
    private int insertTable(XWPFDocument doc,List<PendingOutputDataVo> pendingOutputDatas, int counts) {
        for (int i = 0; i < pendingOutputDatas.size(); i++) {
            counts = counts + 1;
            PendingOutputDataVo pendingOutputData = pendingOutputDatas.get(i);
            addNumberedTableTitle(doc, pendingOutputData.getTitle(), counts);
            creatTableTemplate(doc,pendingOutputData);
        }
        return counts;
    }

    private int insertTable(XWPFDocument doc,List<PendingOutputDataVo> pendingOutputDatas, int counts, List<Long> idList) {
        for (int i = 0; i < pendingOutputDatas.size(); i++) {
            PendingOutputDataVo pendingOutputData = pendingOutputDatas.get(i);
            if (idList.contains(pendingOutputData.getId())) {
                counts = counts + 1;
                addNumberedTableTitle(doc, pendingOutputData.getTitle(), counts);
                creatTableTemplate(doc,pendingOutputData);
            }
        }
        return counts;
    }

    private void fillMainContent(XWPFDocument document, String text) {
        XWPFParagraph paragraph = document.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.LEFT); // 居中
        // 400 = 2 * 200（一个中文字符大约200单位）
        paragraph.setIndentationFirstLine(400);
        // 设置自动换行
        paragraph.setWordWrapped(true);
        XWPFRun titleRun = paragraph.createRun();
        titleRun.setText(text);
        // 字体大小
        //设置字体为五号 10.5*2
        CTRPr rPr = titleRun.getCTR().isSetRPr() ? titleRun.getCTR().getRPr() : titleRun.getCTR().addNewRPr();
        rPr.addNewSz().setVal(BigInteger.valueOf(21));
        rPr.addNewSzCs().setVal(BigInteger.valueOf(21));
        // 字体样式
        titleRun.setFontFamily("Times New Roman");
        titleRun.setFontFamily("宋体", XWPFRun.FontCharRange.eastAsia);
    }

    private void fillFixedContent(XWPFDocument document, Long projectId, List<TreeSelect> treeSelects){
        String mainText = "格式说明： 正文（宋体、五号、段落间距固定值20磅），各级标题（黑体、五号、段落间距固定值20磅）";
        fillMainContent(document,mainText);
        // 写入第一章内容
        addNumberedHeading(document,"范围",1, false);
        // 1.1
        addNumberedHeading(document,"标识",2, false);
        mainText = "本条应描述本文档所适用系统和软件的完整标识，（若适用）包括其标识号、名称、缩略名、版本号和发布号。";
        fillMainContent(document,mainText);
        //1.2
        addNumberedHeading(document,"系统概述",2, false);
        mainText = "本条应概述本文档适用的系统和软件的用途；描述系统和软件的一般特性(如规模、安全性、可靠性、实时性、技术风险等特性)；"+
                "概述系统开发、运行和维护的历史；标识项目的需方、用户、开发方和保障机构等；标识当前和计划的运行现场；列出其他有关文档。";
        fillMainContent(document,mainText);
        //1.3
        addNumberedHeading(document,"文档概述",2, false);
        mainText = "本条应概述本文档的用途和内容，并描述与它的使用有关的安全保密方面的要求。";
        fillMainContent(document,mainText);

        // 写入第二章内容
        addNumberedHeading(document,"引用文档",1, false);
        mainText = "本章应列出引用文档的编号、标题、编写单位、修订版及日期，还应给出不能通过正常渠道得到的文档的来源。";
        fillMainContent(document,mainText);

        // 写入第三章内容
        addNumberedHeading(document,"测试准备",1, false);
        mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示，以及保密性考虑。";
        fillMainContent(document,mainText);
        // 3.1
        addNumberedHeading(document,"（测试唯一标识符）",2, false);
        mainText = "本条应使用项目的唯一标识符来标识一个测试，并对测试进行简要说明，同时应分为以下子条描述。" +
                "若与另一个测试所要求的信息存在重复时，可直接引用。";
        fillMainContent(document,mainText);
        // 3.1.1
        addNumberedHeading(document,"硬件准备",3, false);
        mainText = "本条应描述测试工作所需的硬件准备规程。有关这些规程，可以引用已发布的操作手册。(若适用)应提供以下内容：";
        fillMainContent(document,mainText);
        mainText = "a) 用名称和(若适用)编号标识要使用的特定硬件；";
        fillMainContent(document,mainText);
        mainText = "b) 所有连接硬件所需的开关装置和电缆；";
        fillMainContent(document,mainText);
        mainText = "c) 说明硬件、互联控制和数据路径的一个或多个图示；";
        fillMainContent(document,mainText);
        mainText = "d) 使硬件处于就绪状态的逐步的操作说明；";
        fillMainContent(document,mainText);
        //3.1.2
        addNumberedHeading(document,"软件准备",3, false);
        mainText = "本条应描述准备被测项、相关软件以及数据的必要规程。有关这些规程，可以引用已发布的软件手册。(若适用)应提供下述信息：";
        fillMainContent(document,mainText);
        mainText = "a) 测试中要使用的特定软件；";
        fillMainContent(document,mainText);
        mainText = "b) 被测项的存储介质(如光盘、磁盘)；";
        fillMainContent(document,mainText);
        mainText = "c) 所有相关软件(如模拟器、测试驱动程序、数据库)的存储介质；";
        fillMainContent(document,mainText);
        mainText = "d) 加载软件的说明，包括所需的顺序；";
        fillMainContent(document,mainText);
        mainText = "e) 多个测试用例共用的软件初始化说明；";
        fillMainContent(document,mainText);
        //3.1.3
        addNumberedHeading(document,"其它测试前准备",3, false);
        mainText = "本条应描述其他测试前所需的人员活动、准备工作或规程。";
        fillMainContent(document,mainText);

        // 写入第四章内容
        addNumberedHeading(document,"测试说明",1, false);
        mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示，以及保密性考虑。";
        fillMainContent(document,mainText);
        // 添加带自动编号的内容
        iterateTraversl(projectId, treeSelects, document);

        //写入第五章内容
        addNumberedHeading(document,"需求的可追踪性",1, false);
        mainText = "本条应描述：";
        fillMainContent(document,mainText);
        mainText = "a)  从软件测试说明中的测试用例到它所涉及的系统或CSCI需求的可追踪性。" +
                "若一个测试用例涉及多个需求，应包含从每一组测试规程步骤到所涉及的需求的可追踪性(亦可在4.X.Y.1中提供)。";
        fillMainContent(document,mainText);
        mainText = "b)  从本软件测试说明所提及的每项系统或CSCI需求到涉及它们的测试用例的可追踪性。" +
                "对于CSCI测试，是从CSCI的软件需求规格说明(SRS)和有关接口需求规格说明(IRS)中的每项CSCI需求到涉及它们的测试用例的可追踪性。" +
                "对于系统测试，是从系统的系统/子系统规格说明(SSS)及有关IRS中的每项系统需求到涉及它们的测试用例的可追踪性。" +
                "如果一个测试用例涉及多项需求，则可追踪性应指明涉及每项需求的特定测试规程步骤。";
        fillMainContent(document,mainText);
        mainText = "注：软件测试说明可通过软件测试计划建立与系统或CSCI需求双向可追踪性";
        fillMainContent(document,mainText);

        //写入第六章内容
        addNumberedHeading(document,"注释",1, false);
        mainText = "本章应包括有助于了解文档的所有信息(例如：背景、术语、缩略语或公式)。";
        fillMainContent(document,mainText);

    }

    private void fillFixedContent(XWPFDocument document, Long projectId, List<TreeSelect> treeSelects, List<Long> idList){
        String mainText = "格式说明： 正文（宋体、五号、段落间距固定值20磅），各级标题（黑体、五号、段落间距固定值20磅）";
        fillMainContent(document,mainText);
        // 写入第一章内容
        addNumberedHeading(document,"范围",1, false);
        // 1.1
        addNumberedHeading(document,"标识",2, false);
        mainText = "本条应描述本文档所适用系统和软件的完整标识，（若适用）包括其标识号、名称、缩略名、版本号和发布号。";
        fillMainContent(document,mainText);
        //1.2
        addNumberedHeading(document,"系统概述",2, false);
        mainText = "本条应概述本文档适用的系统和软件的用途；描述系统和软件的一般特性(如规模、安全性、可靠性、实时性、技术风险等特性)；"+
                "概述系统开发、运行和维护的历史；标识项目的需方、用户、开发方和保障机构等；标识当前和计划的运行现场；列出其他有关文档。";
        fillMainContent(document,mainText);
        //1.3
        addNumberedHeading(document,"文档概述",2, false);
        mainText = "本条应概述本文档的用途和内容，并描述与它的使用有关的安全保密方面的要求。";
        fillMainContent(document,mainText);

        // 写入第二章内容
        addNumberedHeading(document,"引用文档",1, false);
        mainText = "本章应列出引用文档的编号、标题、编写单位、修订版及日期，还应给出不能通过正常渠道得到的文档的来源。";
        fillMainContent(document,mainText);

        // 写入第三章内容
        addNumberedHeading(document,"测试准备",1, false);
        mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示，以及保密性考虑。";
        fillMainContent(document,mainText);
        // 3.1
        addNumberedHeading(document,"（测试唯一标识符）",2, false);
        mainText = "本条应使用项目的唯一标识符来标识一个测试，并对测试进行简要说明，同时应分为以下子条描述。" +
                "若与另一个测试所要求的信息存在重复时，可直接引用。";
        fillMainContent(document,mainText);
        // 3.1.1
        addNumberedHeading(document,"硬件准备",3, false);
        mainText = "本条应描述测试工作所需的硬件准备规程。有关这些规程，可以引用已发布的操作手册。(若适用)应提供以下内容：";
        fillMainContent(document,mainText);
        mainText = "a) 用名称和(若适用)编号标识要使用的特定硬件；";
        fillMainContent(document,mainText);
        mainText = "b) 所有连接硬件所需的开关装置和电缆；";
        fillMainContent(document,mainText);
        mainText = "c) 说明硬件、互联控制和数据路径的一个或多个图示；";
        fillMainContent(document,mainText);
        mainText = "d) 使硬件处于就绪状态的逐步的操作说明；";
        fillMainContent(document,mainText);
        //3.1.2
        addNumberedHeading(document,"软件准备",3, false);
        mainText = "本条应描述准备被测项、相关软件以及数据的必要规程。有关这些规程，可以引用已发布的软件手册。(若适用)应提供下述信息：";
        fillMainContent(document,mainText);
        mainText = "a) 测试中要使用的特定软件；";
        fillMainContent(document,mainText);
        mainText = "b) 被测项的存储介质(如光盘、磁盘)；";
        fillMainContent(document,mainText);
        mainText = "c) 所有相关软件(如模拟器、测试驱动程序、数据库)的存储介质；";
        fillMainContent(document,mainText);
        mainText = "d) 加载软件的说明，包括所需的顺序；";
        fillMainContent(document,mainText);
        mainText = "e) 多个测试用例共用的软件初始化说明；";
        fillMainContent(document,mainText);
        //3.1.3
        addNumberedHeading(document,"其它测试前准备",3, false);
        mainText = "本条应描述其他测试前所需的人员活动、准备工作或规程。";
        fillMainContent(document,mainText);

        // 写入第四章内容
        addNumberedHeading(document,"测试说明",1, false);
        mainText = "本章应分为以下子条。(若适用)应包括用“警告”或“注意”所标志的安全提示，以及保密性考虑。";
        fillMainContent(document,mainText);
        // 添加带自动编号的内容
        iterateTraversl(projectId, treeSelects, document, idList);

        //写入第五章内容
        addNumberedHeading(document,"需求的可追踪性",1, false);
        mainText = "本条应描述：";
        fillMainContent(document,mainText);
        mainText = "a)  从软件测试说明中的测试用例到它所涉及的系统或CSCI需求的可追踪性。" +
                "若一个测试用例涉及多个需求，应包含从每一组测试规程步骤到所涉及的需求的可追踪性(亦可在4.X.Y.1中提供)。";
        fillMainContent(document,mainText);
        mainText = "b)  从本软件测试说明所提及的每项系统或CSCI需求到涉及它们的测试用例的可追踪性。" +
                "对于CSCI测试，是从CSCI的软件需求规格说明(SRS)和有关接口需求规格说明(IRS)中的每项CSCI需求到涉及它们的测试用例的可追踪性。" +
                "对于系统测试，是从系统的系统/子系统规格说明(SSS)及有关IRS中的每项系统需求到涉及它们的测试用例的可追踪性。" +
                "如果一个测试用例涉及多项需求，则可追踪性应指明涉及每项需求的特定测试规程步骤。";
        fillMainContent(document,mainText);
        mainText = "注：软件测试说明可通过软件测试计划建立与系统或CSCI需求双向可追踪性";
        fillMainContent(document,mainText);

        //写入第六章内容
        addNumberedHeading(document,"注释",1, false);
        mainText = "本章应包括有助于了解文档的所有信息(例如：背景、术语、缩略语或公式)。";
        fillMainContent(document,mainText);

    }


    private void iterateTraversl(Long projectId, List<TreeSelect> treeSelects, XWPFDocument document){
        // 使用栈进行迭代遍历
        Stack<TreeSelect> stack = new Stack<>();
        // 先将所有根节点压入栈
        if (treeSelects != null) {
            for (int i = treeSelects.size() - 1; i >= 0; i--) {
                stack.push(treeSelects.get(i));
            }
        }

        int tableCounts = 0;
        while (!stack.isEmpty()) {
            TreeSelect current = stack.pop();

            // 处理当前节点，这里简单打印节点信息，你可以根据需求修改
            //System.out.println("ID: " + current.getId() + ", 名称: " + current.getLabel() + ", 层级：" + current.getLevel() + ", 父ID: " + current.getParentId() + ", 是否叶子节点: " + current.getIsLeaf() );
            if (current.getIsLeaf()) {
                if(current.getId() > 0){
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                    List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, current.getId());
                    tableCounts = insertTable(document, pendingOutputDatas, tableCounts);
                } else {
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                    List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, null);
                    tableCounts = insertTable(document, pendingOutputDatas, tableCounts);
                }
            } else{
                if (current.getId() > 0){
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                }
            }
            //处理当前节点结束

            // 将子节点压入栈
            List<TreeSelect> children = current.getChildren();
            if (children != null) {
                for (int i = children.size() - 1; i >= 0; i--) {
                    stack.push(children.get(i));
                }
            }
        }
    }

    private void iterateTraversl(Long projectId, List<TreeSelect> treeSelects, XWPFDocument document, List<Long> idList){
        // 使用栈进行迭代遍历
        Stack<TreeSelect> stack = new Stack<>();
        // 先将所有根节点压入栈
        if (treeSelects != null) {
            for (int i = treeSelects.size() - 1; i >= 0; i--) {
                stack.push(treeSelects.get(i));
            }
        }

        int tableCounts = 0;
        while (!stack.isEmpty()) {
            TreeSelect current = stack.pop();

            // 处理当前节点，这里简单打印节点信息，你可以根据需求修改
            //System.out.println("ID: " + current.getId() + ", 名称: " + current.getLabel() + ", 层级：" + current.getLevel() + ", 父ID: " + current.getParentId() + ", 是否叶子节点: " + current.getIsLeaf() );
            if (current.getIsLeaf()) {
                if(current.getId() > 0){
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                    List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, current.getId());
                    tableCounts = insertTable(document, pendingOutputDatas, tableCounts, idList);
                } else {
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                    List<PendingOutputDataVo> pendingOutputDatas = GetPendingOutputData(projectId, null);
                    tableCounts = insertTable(document, pendingOutputDatas, tableCounts, idList);
                }
            } else{
                if (current.getId() > 0){
                    addNumberedHeading(document, current.getLabel(), current.getLevel() + 1, true);
                }
            }
            //处理当前节点结束

            // 将子节点压入栈
            List<TreeSelect> children = current.getChildren();
            if (children != null) {
                for (int i = children.size() - 1; i >= 0; i--) {
                    stack.push(children.get(i));
                }
            }
        }
    }

    public void generateWord(List<TreeSelect> treeSelects, Long projectId, HttpServletResponse response) throws IOException {
        // 创建一个新的 Word 文档
        XWPFDocument document = new XWPFDocument();

        // 创建多级编号样式
        BigInteger numId = createMultiLevelNumbering(document);
        // 创建关联编号的标题样式
        createTitleStyle(document, "Heading1", "标题1", 18, 1, numId);
        createTitleStyle(document, "Heading2", "标题2", 16, 2, numId);
        createTitleStyle(document, "Heading3", "标题3", 14, 3, numId);
        createTitleStyle(document, "Heading4", "标题4", 12, 4, numId);
        createTitleStyle(document, "Heading5", "标题5", 12, 5, numId);
        // 写入标题"测试用例"
        XWPFParagraph title = document.createParagraph();
        title.setAlignment(ParagraphAlignment.CENTER); // 居中
        XWPFRun titleRun = title.createRun();
        titleRun.setText("测试用例");
        titleRun.setFontSize(22); // 字体大小
        titleRun.setFontFamily("宋体"); // 字体样式
        // 增加固定格式内容
        fillFixedContent(document,projectId, treeSelects);

        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", "attachment; filename=testCase_" + projectId + ".docx");

        // 将文档写入响应输出流
        OutputStream outputStream = response.getOutputStream();
        document.write(outputStream);
        outputStream.flush();
        outputStream.close();
        document.close();
    }

    @Override
    public void generateWordByTestcaseId(List<TreeSelect> treeSelects, Long projectId, String ids, HttpServletResponse response) throws IOException{
        List<Long> idList = Arrays.stream(ids.split(",")).map(String::trim).map(Long::parseLong).collect(Collectors.toList());
        checkBatchUpdateInTheSameProject(idList);
        // 创建一个新的 Word 文档
        XWPFDocument document = new XWPFDocument();

        // 创建多级编号样式
        BigInteger numId = createMultiLevelNumbering(document);
        // 创建关联编号的标题样式
        createTitleStyle(document, "Heading1", "标题1", 18, 1, numId);
        createTitleStyle(document, "Heading2", "标题2", 16, 2, numId);
        createTitleStyle(document, "Heading3", "标题3", 14, 3, numId);
        createTitleStyle(document, "Heading4", "标题4", 12, 4, numId);
        createTitleStyle(document, "Heading5", "标题5", 12, 5, numId);
        // 写入标题"测试用例"
        XWPFParagraph title = document.createParagraph();
        title.setAlignment(ParagraphAlignment.CENTER); // 居中
        XWPFRun titleRun = title.createRun();
        titleRun.setText("测试用例");
        titleRun.setFontSize(22); // 字体大小
        titleRun.setFontFamily("宋体"); // 字体样式
        // 增加固定格式内容
        fillFixedContent(document,projectId, treeSelects, idList);

        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", "attachment; filename=testCase_" + projectId + ".docx");

        // 将文档写入响应输出流
        OutputStream outputStream = response.getOutputStream();
        document.write(outputStream);
        outputStream.flush();
        outputStream.close();
        document.close();
    }

    // 导入测试用例相关
    /**
     * 处理docx文件将测试用例导入到数据库
     * @param projectId 项目id
     * @param file 导入的docx文件
     */
    public void testcaseImport(Long projectId, MultipartFile file) throws IOException {
        List<Map<String, List<List<String>>>> allTables = extractTableDates(file);
        insertTestcaseValue(allTables, projectId);
    }

    /**
     * 获取word中所有表格中的行列数据，并存储到集合中
     * @param file 导入的docx文件
     * @return  List<Map<String, List<List<String>>>> 存储表格内行列数据的集合
     */
    private List<Map<String, List<List<String>>>>  extractTableDates(MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        XWPFDocument document = new XWPFDocument(inputStream);

        List<Map<String, List<List<String>>>> allTables = new ArrayList<>();
        // 获取文档中的所有表格
        List<XWPFTable> tables = document.getTables();

        for (int i = 0; i < tables.size(); i++) {
            XWPFTable table = tables.get(i);
            Map<String, List<List<String>>> tableData = new HashMap<>();
            List<List<String>> rows = new ArrayList<>();

            // 遍历表格中的每一行
            for (XWPFTableRow row : table.getRows()) {
                List<String> rowData = new ArrayList<>();
                // 遍历行中的每一列
                for (XWPFTableCell cell : row.getTableCells()) {
                    // 获取单元格文本
                    StringBuilder cellText = new StringBuilder();
                    for (XWPFParagraph paragraph : cell.getParagraphs()) {
                        for (XWPFRun run : paragraph.getRuns()) {
                            cellText.append(run.getText(0));
                        }
                    }
                    rowData.add(cellText.toString().trim());
                }
                rows.add(rowData);
            }

            tableData.put("table_" + (i + 1), rows);
            if(tableData.get("table_" + (i + 1)).get(0).size() == 2 && tableData.get("table_" + (i + 1)).get(1).size() == 4 && tableData.get("table_" + (i + 1)).get(2).size() == 2){}
            allTables.add(tableData);
        }
        return allTables;
    }

    /**
     * 将集合中表格行列数据写入数据库中
     * @param tableRows 单个表格的行列数据
     * @return  boolean 返回是否时测试用例表格数据
     */
    private boolean testcaseTableValidation(List<List<String>> tableRows) {
        if (tableRows.size()>12) {
            return (tableRows.get(0).size() == 2 && tableRows.get(1).size() == 4 && tableRows.get(8).size() == 5) ||
                    (tableRows.get(0).size() == 9 && tableRows.get(1).size() == 9 && tableRows.get(8).size() == 9);
        }
        return false;
    }

    private PmsProjectTestcase testcaseSetVal(List<List<String>> tableRows, Long projectId, boolean isTablerule) {
        PmsProjectTestcase testcase = new PmsProjectTestcase();
        List<PmsProjectTestcaseStep> testcaseSteps = new ArrayList<>();
        if (isTablerule) {
            testcase.setPmsProjectId(projectId);
            testcase.setTitle(getTableCellValue(tableRows,0,3));
            testcase.setIdentifier(getTableCellValue(tableRows,1,3));
            testcase.setDescription(getTableCellValue(tableRows,2,3));
            testcase.setTestMethod(getTableCellValue(tableRows,3,3));
            String testType = getTableCellValue(tableRows,4,3);
            testcase.setTypeId(TypeToId(testType));
            testcase.setPreconditions(getTableCellValue(tableRows,5,3));
            testcase.setTerminationConditions(getTableCellValue(tableRows,6,3));
            for(int i = 9; i < tableRows.size(); i++){
                if (!Objects.equals(getTableCellValue(tableRows, i, 0), "设计人员")){
                    PmsProjectTestcaseStep testcaseStep = new PmsProjectTestcaseStep();
                    testcaseStep.setIndex(i - 8L);
                    testcaseStep.setContent(getTableCellValue(tableRows, i, 1));
                    testcaseStep.setExpectedResult(getTableCellValue(tableRows, i, 4));
                    testcaseSteps.add(testcaseStep);
                } else {
                    testcase.setCreateBy(getTableCellValue(tableRows, i, 2));
                    Date date = stringToDateUsingDateTimeFormatter(getTableCellValue(tableRows, i , 6), "yyyy-MM-dd");
                    testcase.setCreateTime(date);
                    break;
                }
            }
        }else{
            testcase.setPmsProjectId(projectId);
            testcase.setTitle(getTableCellValue(tableRows,0,1));
            testcase.setIdentifier(getTableCellValue(tableRows,1,1));
            testcase.setDescription(getTableCellValue(tableRows,2,1));
            testcase.setTestMethod(getTableCellValue(tableRows,3,1));
            String testType = getTableCellValue(tableRows,4,1);
            testcase.setTypeId(TypeToId(testType));
            testcase.setPreconditions(getTableCellValue(tableRows,5,1));
            testcase.setTerminationConditions(getTableCellValue(tableRows,6,1));
            for(int i = 9; i < tableRows.size(); i++){
                if (tableRows.get(i).size() > 4 && getTableCellValue(tableRows, i, 1) != null){
                    PmsProjectTestcaseStep testcaseStep = new PmsProjectTestcaseStep();
                    testcaseStep.setIndex(i - 8L);
                    testcaseStep.setContent(getTableCellValue(tableRows, i, 1));
                    testcaseStep.setExpectedResult(getTableCellValue(tableRows, i, 2));
                    testcaseSteps.add(testcaseStep);
                } else {
                    testcase.setCreateBy(SecurityUtils.getUsername());
                    try{
                        Date date = stringToDateUsingDateTimeFormatter(getTableCellValue(tableRows, i , 3), "yyyy-MM-dd");
                        testcase.setCreateTime(date);
                    } catch (DateTimeException e) {
                        throw  new ServiceException("日期解析失败，请按模板格式填写设计日期");
                    }
                    break;
                }
            }
        }
        testcase.setIsImport(1L);
        testcase.setTestcaseStepList(testcaseSteps);
        return testcase;
    }

    /**
     * 将集合中表格行列数据写入数据库中
     * @param allTables 所有表格的行列数据
     * @param projectId 当前项目ID
     */
    private void insertTestcaseValue(List<Map<String, List<List<String>>>> allTables, Long projectId) throws IOException {
        // 遍历所有表格
        for (int tableIndex = 0; tableIndex < allTables.size(); tableIndex++) {
            Map<String, List<List<String>>> tableMap = allTables.get(tableIndex);
            // 假设每个 map 只有一个键值对
            String tableName = tableMap.keySet().iterator().next();
            List<List<String>> tableRows = tableMap.get(tableName);
            System.out.println("Table " + tableName + " rows: " + tableRows.size());
            if(testcaseTableValidation(tableRows)){
                PmsProjectTestcase testcase = testcaseSetVal(tableRows, projectId, tableRows.get(0).size() == 9);

                if (testcase.getAssigneeGitlinkId() == null) {
                    // 未设置维护用户时默认为当前操作用户
                    testcase.setAssigneeGitlinkId(SecurityUtils.getGitlinkUserId());
                }
                if (testcase.getTypeId() == null) {
                    // 设置类型为默认值
                    PmsProjectTestcaseType defaultType = pmsProjectTestcaseTypeService.selectDefaultPmsProjectTestcaseType();
                    testcase.setTypeId(defaultType.getId());
                }
                // testcase.setAssigneeGitlinkId(1L);
                // 获取当前项目下测试用例的最大索引值
                Long maxIndex = pmsProjectTestcaseMapper.selectPmsProjectTestcaseMaxIndexByProjectId(testcase.getPmsProjectId());
                if (maxIndex == null) {
                    maxIndex = 1L;
                }
                testcase.setIndex(++maxIndex);
                pmsProjectTestcaseMapper.insertPmsProjectTestcase(testcase);
                // 批量新增测试步骤
                pmsProjectTestcaseStepService.batchInsertPmsProjectTestcaseStep(testcase.getId(), testcase.getTestcaseStepList());
            }
        }
    }

    /**
     * 使用 DateTimeFormatter 和 LocalDate 将 String 转换为 Date
     * @param dateStr 日期字符串
     * @param pattern 日期格式模式
     * @return 转换后的 Date 对象
     */
    public static Date stringToDateUsingDateTimeFormatter(String dateStr, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        LocalDate localDate = LocalDate.parse(dateStr, formatter);
        Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }


    private Long TypeToId(String type){
        switch (type) {
            case "功能测试":
                return 1L;
            case "性能测试":
                return 2L;
            case "接口测试":
                return 3L;
            case "安装部署":
                return 4L;
            case "安全相关":
                return 5L;
            case "配置相关":
                return 6L;
            default:
                // 其它类型
                return 7L;
        }
    }

    /**
     * 使用 DateTimeFormatter 和 LocalDate 将 String 转换为 Date
     * @param rows 单个表格所有行列数据
     * @param targetRowIndex 定位单元格的行索引
     * @param targetColIndex 定位单元格的列索引
     * @return cell获取的表格中字符串
     */
    private String getTableCellValue(List<List<String>> rows, int targetRowIndex, int targetColIndex) {
        String cell = null;
        if (targetRowIndex < rows.size() && targetColIndex < rows.get(targetRowIndex).size()) {
            cell = rows.get(targetRowIndex).get(targetColIndex);
        }
        return cell;
    }

}
