package org.dromara.safety.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.exception.OssException;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.project.domain.BusConstructionUser;
import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.service.IBusConstructionUserService;
import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.safety.constant.HseSafetyConstant;
import org.dromara.safety.domain.HseSafetyInspection;
import org.dromara.safety.domain.HseTeamMeeting;
import org.dromara.safety.domain.enums.HseSafetyInspectionReviewTypeEnum;
import org.dromara.safety.domain.enums.HseSafetyInspectionStatusEnum;
import org.dromara.safety.domain.dto.safetyinspection.HseSafetyInspectionCreateReq;
import org.dromara.safety.domain.dto.safetyinspection.HseSafetyInspectionGisReq;
import org.dromara.safety.domain.dto.safetyinspection.HseSafetyInspectionQueryReq;
import org.dromara.safety.domain.dto.safetyinspection.HseSafetyInspectionUpdateReq;
import org.dromara.safety.domain.vo.safetyinspection.HseSafetyInspectionListGisVo;
import org.dromara.safety.domain.vo.safetyinspection.HseSafetyInspectionVo;
import org.dromara.safety.domain.vo.teammeeting.HseTeamMeetingGis;
import org.dromara.safety.mapper.HseSafetyInspectionMapper;
import org.dromara.safety.service.IHseSafetyInspectionService;
import org.dromara.safety.service.IHseTeamMeetingService;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.service.ISysDictDataService;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysUserService;
import org.dromara.utils.DocumentUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * 安全巡检工单Service业务层处理
 *
 * @author lcj
 * @date 2025-03-20
 */
@Slf4j
@Service
public class HseSafetyInspectionServiceImpl extends ServiceImpl<HseSafetyInspectionMapper, HseSafetyInspection>
    implements IHseSafetyInspectionService {

    @Resource
    private IBusConstructionUserService constructionUserService;

    @Resource
    private IBusProjectTeamService projectTeamService;

    @Resource
    private IBusProjectService projectService;

    @Resource
    private ISysUserService userService;

    @Resource
    private ISysDictDataService dictDataService;

    @Resource
    private ISysOssService ossService;

    @Resource
    private IHseTeamMeetingService teamMeetingService;

    /**
     * 查询安全巡检工单
     *
     * @param id 主键
     * @return 安全巡检工单
     */
    @Override
    public HseSafetyInspectionVo queryById(Long id) {
        HseSafetyInspection safetyInspection = this.getById(id);
        if (safetyInspection == null) {
            throw new ServiceException("安全巡检工单信息不存在", HttpStatus.NOT_FOUND);
        }
        return this.getVo(safetyInspection);
    }

    /**
     * 分页查询安全巡检工单列表
     *
     * @param req       查询条件
     * @param pageQuery 分页参数
     * @return 安全巡检工单分页列表
     */
    @Override
    public TableDataInfo<HseSafetyInspectionVo> queryPageList(HseSafetyInspectionQueryReq req, PageQuery pageQuery) {
        Page<HseSafetyInspection> result = this.page(pageQuery.build(), buildQueryWrapper(req));
        return TableDataInfo.build(this.getVoPage(result));
    }

    /**
     * 查询符合条件的安全巡检工单列表
     *
     * @param req 查询条件
     * @return 安全巡检工单列表
     */
    @Override
    public List<HseSafetyInspectionVo> queryList(HseSafetyInspectionQueryReq req) {
        List<HseSafetyInspection> safetyInspection = this.list(this.buildQueryWrapper(req));
        return safetyInspection.stream().map(this::getVo).toList();
    }

    /**
     * 查询大屏安全信息
     *
     * @param req 查询条件
     * @return 安全信息
     */
    @Override
    public HseSafetyInspectionListGisVo queryGisList(HseSafetyInspectionGisReq req) {
        Long projectId = req.getProjectId();
        if (projectId == null || projectService.getById(projectId) == null) {
            throw new ServiceException("项目信息不存在", HttpStatus.NOT_FOUND);
        }
        HseSafetyInspectionListGisVo gisVo = new HseSafetyInspectionListGisVo();
        List<HseTeamMeeting> teamMeetings = teamMeetingService.lambdaQuery()
            .eq(HseTeamMeeting::getProjectId, projectId)
            .list();
        if (CollUtil.isEmpty(teamMeetings)){
            return gisVo;
        }
        // 获取最新的班组列表
        List<HseTeamMeeting> topList = teamMeetings.stream()
            .sorted(Comparator.comparing(HseTeamMeeting::getCreateTime).reversed())
            .limit(Optional.ofNullable(req.getPageSize()).orElse(20))
            .toList();
        List<Long> teamIds = topList.stream().map(HseTeamMeeting::getTeamId).toList();
        Map<Long, List<BusProjectTeam>> teamMap = projectTeamService.lambdaQuery()
            .in(BusProjectTeam::getId, teamIds)
            .list().stream().collect(Collectors.groupingBy(BusProjectTeam::getId));
        List<HseTeamMeetingGis> gisList = topList.stream().map(teamMeeting -> {
            HseTeamMeetingGis gis = new HseTeamMeetingGis();
            BeanUtils.copyProperties(teamMeeting, gis);
            gis.setName("站班会");
            if (teamMap.containsKey(teamMeeting.getTeamId())) {
                gis.setTeamName(teamMap.get(teamMeeting.getTeamId()).get(0).getTeamName());
            }
            return gis;
        }).toList();
        // 获取整改情况
        List<HseSafetyInspection> safetyInspectionList = this.lambdaQuery()
            .eq(HseSafetyInspection::getProjectId, projectId).list();
        long passCount = 0L;
        for (HseSafetyInspection safetyInspection : safetyInspectionList) {
            if (HseSafetyInspectionStatusEnum.REVIEW.getValue().equals(safetyInspection.getStatus())
                && HseSafetyInspectionReviewTypeEnum.PASS.getValue().equals(safetyInspection.getReviewType())) {
                passCount++;
            }
        }
        gisVo.setTeamMeetingList(gisList);
        gisVo.setTeamMeetingCount((long) teamMeetings.size());
        gisVo.setSafetyInspectionCount((long) safetyInspectionList.size());
        gisVo.setCorrectSituationCount(passCount);
        gisVo.setCorrectSituation(String.format("%.2f", passCount * 100.0 / safetyInspectionList.size()));
        return gisVo;
    }

    /**
     * 新增安全巡检工单
     *
     * @param req 安全巡检工单
     * @return 是否新增成功
     */
    @Override
    public Long insertByBo(HseSafetyInspectionCreateReq req) {
        // 将实体类和 DTO 进行转换
        HseSafetyInspection safetyInspection = new HseSafetyInspection();
        BeanUtils.copyProperties(req, safetyInspection);
        // 数据校验
        validEntityBeforeSave(safetyInspection, true);
        // 写入数据库
        boolean save = this.save(safetyInspection);
        if (!save) {
            throw new ServiceException("新增安全巡检工单信息失败，数据库异常", HttpStatus.ERROR);
        }
        // 返回新写入的数据
        return safetyInspection.getId();
    }

    /**
     * 修改安全巡检工单
     *
     * @param req 安全巡检工单
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(HseSafetyInspectionUpdateReq req) {
        // 将实体类和 DTO 进行转换
        HseSafetyInspection safetyInspection = new HseSafetyInspection();
        BeanUtils.copyProperties(req, safetyInspection);
        // 数据校验
        validEntityBeforeSave(safetyInspection, false);
        // 判断是否存在
        HseSafetyInspection oldSafetyInspection = this.getById(safetyInspection.getId());
        if (oldSafetyInspection == null) {
            throw new ServiceException("修改安全巡检工单信息失败，数据不存在", HttpStatus.NOT_FOUND);
        }
        // 操作数据库
        return this.updateById(safetyInspection);
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(HseSafetyInspection entity, Boolean create) {
        // TODO 做一些数据校验,如唯一约束
        Long projectId = entity.getProjectId();
        Long correctorId = entity.getCorrectorId();
        String checkType = entity.getCheckType();
        String violationType = entity.getViolationType();
        // 创建时校验
        if (create) {
            if (projectId == null) {
                throw new ServiceException("项目id不能为空", HttpStatus.BAD_REQUEST);
            }
            if (correctorId == null) {
                throw new ServiceException("整改人id不能为空", HttpStatus.BAD_REQUEST);
            }
            if (StringUtils.isBlank(checkType)) {
                throw new ServiceException("检查类型不能为空", HttpStatus.BAD_REQUEST);
            }
            if (StringUtils.isBlank(violationType)) {
                throw new ServiceException("违规类型不能为空", HttpStatus.BAD_REQUEST);
            }
        }
        if (projectId != null && projectService.getById(projectId) == null) {
            throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
        }
        if (correctorId != null && constructionUserService.getById(correctorId) == null) {
            throw new ServiceException("对应整改人不存在", HttpStatus.NOT_FOUND);
        }
    }

    /**
     * 校验并批量删除安全巡检工单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // TODO 做一些业务上的校验,判断是否需要校验
            Long count = this.lambdaQuery().in(HseSafetyInspection::getPid, ids).count();
            if (count > 0) {
                throw new ServiceException("所选安全巡检工单包含子数据，不允许删除", HttpStatus.BAD_REQUEST);
            }
        }
        return this.removeBatchByIds(ids);
    }

    /**
     * 获取安全巡检工单视图对象
     *
     * @param safetyInspection 安全巡检工单对象
     * @return 安全巡检工单视图对象
     */
    @Override
    public HseSafetyInspectionVo getVo(HseSafetyInspection safetyInspection) {
        // 对象转封装类
        HseSafetyInspectionVo safetyInspectionVo = new HseSafetyInspectionVo();
        if (safetyInspection == null) {
            return safetyInspectionVo;
        }
        BeanUtils.copyProperties(safetyInspection, safetyInspectionVo);
        // 关联班组信息
        Long teamId = safetyInspection.getTeamId();
        if (teamId != null) {
            LambdaQueryWrapper<BusProjectTeam> teamLambdaQueryWrapper = Wrappers.lambdaQuery(BusProjectTeam.class)
                .select(BusProjectTeam::getId, BusProjectTeam::getTeamName)
                .eq(BusProjectTeam::getId, teamId);
            BusProjectTeam projectTeam = projectTeamService.getOne(teamLambdaQueryWrapper);
            safetyInspectionVo.setTeamId(projectTeam.getId());
            safetyInspectionVo.setTeamName(projectTeam.getTeamName());
        }
        // 关联整改人信息
        Long correctorId = safetyInspection.getCorrectorId();
        if (correctorId != null) {
            LambdaQueryWrapper<BusConstructionUser> constructionUserLambdaQueryWrapper = Wrappers.lambdaQuery(BusConstructionUser.class)
                .select(BusConstructionUser::getId, BusConstructionUser::getUserName)
                .eq(BusConstructionUser::getId, correctorId);
            BusConstructionUser constructionUser = constructionUserService.getOne(constructionUserLambdaQueryWrapper);
            safetyInspectionVo.setCorrectorId(constructionUser.getId());
            safetyInspectionVo.setCorrectorName(constructionUser.getUserName());
        }
        // 关联创建用户信息
        Long createBy = safetyInspection.getCreateBy();
        if (createBy != null) {
            SysUserVo createUser = userService.selectUserById(createBy);
            safetyInspectionVo.setCreatorId(createUser.getUserId());
            safetyInspectionVo.setCreatorName(createUser.getNickName());
        }
        return safetyInspectionVo;
    }

    /**
     * 获取安全巡检工单查询条件封装
     *
     * @param req 安全巡检工单查询条件
     * @return 安全巡检工单查询条件封装
     */
    @Override
    public LambdaQueryWrapper<HseSafetyInspection> buildQueryWrapper(HseSafetyInspectionQueryReq req) {
        LambdaQueryWrapper<HseSafetyInspection> lqw = Wrappers.lambdaQuery();
        if (req == null) {
            return lqw;
        }
        Long pid = req.getPid();
        Long projectId = req.getProjectId();
        String checkType = req.getCheckType();
        String violationType = req.getViolationType();
        Long teamId = req.getTeamId();
        Long correctorId = req.getCorrectorId();
        String status = req.getStatus();
        String reviewType = req.getReviewType();
        // 精准查询
        lqw.eq(ObjectUtils.isNotEmpty(pid), HseSafetyInspection::getPid, pid);
        lqw.eq(ObjectUtils.isNotEmpty(projectId), HseSafetyInspection::getProjectId, projectId);
        lqw.eq(ObjectUtils.isNotEmpty(checkType), HseSafetyInspection::getCheckType, checkType);
        lqw.eq(ObjectUtils.isNotEmpty(violationType), HseSafetyInspection::getViolationType, violationType);
        lqw.eq(ObjectUtils.isNotEmpty(teamId), HseSafetyInspection::getTeamId, teamId);
        lqw.eq(ObjectUtils.isNotEmpty(correctorId), HseSafetyInspection::getCorrectorId, correctorId);
        lqw.eq(ObjectUtils.isNotEmpty(status), HseSafetyInspection::getStatus, status);
        lqw.eq(ObjectUtils.isNotEmpty(reviewType), HseSafetyInspection::getReviewType, reviewType);
        return lqw;
    }

    /**
     * 获取安全巡检工单分页对象视图
     *
     * @param safetyInspectionPage 安全巡检工单分页对象
     * @return 安全巡检工单分页对象视图
     */
    @Override
    public Page<HseSafetyInspectionVo> getVoPage(Page<HseSafetyInspection> safetyInspectionPage) {
        // 获取安全巡检工单
        List<HseSafetyInspection> safetyInspectionList = safetyInspectionPage.getRecords();
        // 添加分页信息
        Page<HseSafetyInspectionVo> safetyInspectionVoPage = new Page<>(
            safetyInspectionPage.getCurrent(),
            safetyInspectionPage.getSize(),
            safetyInspectionPage.getTotal());
        if (CollUtil.isEmpty(safetyInspectionList)) {
            return safetyInspectionVoPage;
        }
        // 对象列表 => 封装对象列表
        List<HseSafetyInspectionVo> safetyInspectionVoList = safetyInspectionList.stream().map(this::getVo).toList();
        safetyInspectionVoPage.setRecords(safetyInspectionVoList);
        return safetyInspectionVoPage;
    }

    /**
     * 根据id导出word
     *
     * @param id       主键
     * @param response 响应
     */
    @Override
    public void exportWordById(Long id, HttpServletResponse response) {
        HseSafetyInspection safetyInspection = this.getById(id);
        if (safetyInspection == null) {
            throw new ServiceException("安全巡检工单不存在");
        }
        Map<String, String> replacementMap = getReplacementMap(safetyInspection);
        Path targetDir = Paths.get(HseSafetyConstant.getSafetyInspectionFileUrl(safetyInspection));
        // 如果存在目录则直接返回，不存在则生成文件并返回
        if (!Files.exists(targetDir)) {
            // 清理旧文件
            String baseUrl = HseSafetyConstant.SAFETY_INSPECTION_FILE_URL + safetyInspection.getId();
            try {
                Path dirPath = Paths.get(baseUrl);
                if (Files.exists(dirPath)) {
                    FileUtils.deleteDirectory(dirPath);
                }
            } catch (IOException e) {
                log.error("文件目录：{}，清理失败", baseUrl, e);
            }
            // 生成文件
            try (InputStream is = getClass().getClassLoader().getResourceAsStream(HseSafetyConstant.SAFETY_INSPECTION_TEMPLATE_PATH)) {
                if (is == null) {
                    throw new ServiceException("模板文件不存在");
                }
                try (XWPFDocument document = new XWPFDocument(is)) {
                    // 替换段落中的文本
                    for (XWPFParagraph paragraph : document.getParagraphs()) {
                        replaceInParagraph(paragraph, replacementMap, document, safetyInspection);
                    }
                    // 替换表格中的文本（如果模板中有表格）
                    for (XWPFTable table : document.getTables()) {
                        for (XWPFTableRow row : table.getRows()) {
                            for (XWPFTableCell cell : row.getTableCells()) {
                                for (XWPFParagraph paragraph : cell.getParagraphs()) {
                                    replaceInParagraph(paragraph, replacementMap, document, safetyInspection);
                                }
                            }
                        }
                    }
                    // 创建目标目录
                    if (!Files.exists(targetDir)) {
                        Files.createDirectories(targetDir);
                    }
                    // 组合目标文件名
                    String fileName = HseSafetyConstant.getSafetyInspectionFileName(safetyInspection);
                    // 保存修改后的文件
                    try (FileOutputStream fos = new FileOutputStream(targetDir.resolve(fileName).toFile())) {
                        document.write(fos);
                    }
                }
            } catch (IOException | InvalidFormatException e) {
                throw new OssException("生成Word文件失败，错误信息: " + e.getMessage());
            }
        }
        // 设置响应头，返回ZIP文件
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            DocumentUtils.zipDirectory(targetDir, targetDir, zos);
            zos.flush();
        } catch (Exception e) {
            throw new OssException("生成ZIP文件失败，错误信息: " + e.getMessage());
        }
    }

    /**
     * 根据实体获取Word的文本Map
     *
     * @param safetyInspection 安全巡检工单对象
     * @return Map
     */
    public Map<String, String> getReplacementMap(HseSafetyInspection safetyInspection) {
        Map<String, String> replacementMap = new HashMap<>();
        String createBy = userService.selectUserById(safetyInspection.getCreateBy()).getNickName();
        replacementMap.put("${createBy}", createBy);
        Date createTime = safetyInspection.getCreateTime();
        replacementMap.put("${createTime}", createTime != null ? DateUtils.formatDateTime(createTime) : "");
        String projectName = projectService.getById(safetyInspection.getProjectId()).getProjectName();
        replacementMap.put("${projectName}", projectName);
        String checkType = dictDataService.selectDictLabel(HseSafetyConstant.SAFETY_INSPECTION_CHECK_TYPE, safetyInspection.getCheckType());
        replacementMap.put("${checkType}", checkType);
        String violationType = dictDataService.selectDictLabel(HseSafetyConstant.SAFETY_INSPECTION_VIOLATION_TYPE, safetyInspection.getViolationType());
        replacementMap.put("${violationType}", violationType);
        Date checkTime = safetyInspection.getCheckTime();
        replacementMap.put("${checkTime}", checkTime != null ? DateUtils.formatDateTime(checkTime) : "");
        String correctorName = constructionUserService.getById(safetyInspection.getCorrectorId()).getUserName();
        replacementMap.put("${correctorName}", correctorName);
        replacementMap.put("${replyDate}", safetyInspection.getReplyDate());
        replacementMap.put("${hiddenDanger}", safetyInspection.getHiddenDanger());
        replacementMap.put("${checkFile}", safetyInspection.getCheckFile());
        String status = dictDataService.selectDictLabel(HseSafetyConstant.SAFETY_INSPECTION_TYPE, safetyInspection.getStatus());
        replacementMap.put("${status}", status);
        String teamName = projectTeamService.getById(safetyInspection.getTeamId()).getTeamName();
        replacementMap.put("${teamName}", teamName);
        Date rectificationTime = safetyInspection.getRectificationTime();
        replacementMap.put("${rectificationTime}", rectificationTime != null ? DateUtils.formatDateTime(rectificationTime) : "");
        replacementMap.put("${measure}", safetyInspection.getMeasure());
        replacementMap.put("${rectificationFile}", safetyInspection.getRectificationFile());
        Date reviewTime = safetyInspection.getReviewTime();
        replacementMap.put("${reviewTime}", reviewTime != null ? DateUtils.formatDateTime(reviewTime) : "");
        replacementMap.put("${review}", safetyInspection.getReview());
        return replacementMap;
    }

    /**
     * 替换段落中所有文本运行的占位符内容，对于 checkFile 和 rectificationFile
     * 插入图片或超链接（附件）展示。
     *
     * @param paragraph    当前段落
     * @param replacements 占位符与替换内容的映射
     * @param document     当前文档，用于插入图片或创建超链接
     */
    public void replaceInParagraph(XWPFParagraph paragraph, Map<String, String> replacements, XWPFDocument document, HseSafetyInspection safetyInspection) throws InvalidFormatException, IOException {
        // 先拷贝 paragraph 里所有的 run
        List<XWPFRun> runs = new ArrayList<>(paragraph.getRuns());
        // 在拷贝上遍历，修改原 paragraph（增删 run）都不会抛 CME
        for (XWPFRun run : runs) {
            String text = run.getText(0);
            if (text != null) {
                // 针对每个占位符进行检查
                for (Map.Entry<String, String> entry : replacements.entrySet()) {
                    String placeholder = entry.getKey();
                    String value = entry.getValue();
                    if (text.contains(placeholder)) {
                        // 针对 checkFile 和 rectificationFile 进行特殊处理
                        if (placeholder.equals("${checkFile}") || placeholder.equals("${rectificationFile}")) {
                            // 判断该 run 中的文本是否仅包含该占位符（建议模板中独占一行）
                            if (text.trim().equals(placeholder)) {
                                // 清空原有文本
                                run.setText("", 0);
                                // 根据附件的后缀决定以图片或超链接展示
                                if (StringUtils.isBlank(value)) {
                                    continue;
                                }
                                // 获取附件的ossId
                                List<Long> ossIdList = Arrays.stream(value.split(",")).map(Long::parseLong).toList();
                                List<SysOssVo> ossVoList = ossService.listByIds(ossIdList);
                                String baseFile = HseSafetyConstant.getSafetyInspectionFileUrl(safetyInspection) + "/file";
                                for (SysOssVo ossVo : ossVoList) {
                                    String lowerVal = ossVo.getUrl().toLowerCase();
                                    OssClient storage = OssFactory.instance(ossVo.getService());
                                    String fileDownload = storage.fileDownload(ossVo.getUrl(), ossVo.getOriginalName(), baseFile);
                                    if (lowerVal.endsWith(".png") || lowerVal.endsWith(".jpg") || lowerVal.endsWith(".jpeg") || lowerVal.endsWith(".gif")) {
                                        try {
                                            DocumentUtils.insertImageDynamic(run, fileDownload, document, 300);
                                        } catch (Exception e) {
                                            throw new ServiceException("插入图片失败");
                                        }
                                    } else {
                                        // —— 非图片：插入超链接 ——
                                        XWPFHyperlinkRun link = paragraph.createHyperlinkRun(ossVo.getUrl());
                                        link.setText(ossVo.getOriginalName());
                                        link.setColor("0000FF");
                                        link.setUnderline(UnderlinePatterns.SINGLE);
                                    }
                                }
                            } else {
                                // 如果占位符与其它文本混合，可进行文本替换，提示附件展示请单独使用占位符
                                text = text.replace(placeholder, "[附件]");
                                run.setText(text, 0);
                            }
                        } else {
                            // 普通文本占位符直接替换
                            if (StringUtils.isBlank(value)) {
                                // 如果填入值为空，清空原有文本
                                run.setText("", 0);
                                continue;
                            }
                            text = text.replace(placeholder, value);
                            run.setText(text, 0);
                        }
                    }
                }
            }
        }
    }

}
