package com.yeejoin.amos.knowledgebase.face.service;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.amos.feign.systemctl.model.DictionarieValueModel;
import com.yeejoin.amos.knowledgebase.face.enumeration.DynamicsFunctional;
import com.yeejoin.amos.knowledgebase.face.enumeration.KnowledgeRoleName;
import com.yeejoin.amos.knowledgebase.face.enumeration.OperateType;
import com.yeejoin.amos.knowledgebase.face.enumeration.OptionDataType;
import com.yeejoin.amos.knowledgebase.face.model.*;
import com.yeejoin.amos.knowledgebase.face.orm.entity.ESDocEntity;
import com.yeejoin.amos.knowledgebase.face.orm.entity.ESTagEntity;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeDocContent;
import com.yeejoin.amos.knowledgebase.face.orm.entity.KnowledgeTag;
import com.yeejoin.amos.knowledgebase.face.util.*;
import com.yeejoin.amos.knowledgebase.face.util.FileExporter.FileType;
import com.yeejoin.amos.knowledgebase.face.util.excel.ExcelImportConfig;
import com.yeejoin.amos.knowledgebase.face.util.excel.ExcelParser;
import com.yeejoin.amos.knowledgebase.face.util.pdf.PdfBuilder;
import com.yeejoin.amos.knowledgebase.face.util.sql.BaseSqlCondition;
import com.yeejoin.amos.knowledgebase.face.util.word.DocxBuilder;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.exception.BaseException;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.DateUtil;
import org.typroject.tyboot.core.foundation.utils.TreeBuilder;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.rdbms.annotation.Operator;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;
import org.typroject.tyboot.core.restful.exception.instance.DataNotFound;
import org.typroject.tyboot.core.restful.exception.instance.RequestForbidden;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

import static com.yeejoin.amos.knowledgebase.face.util.DataTransformator.htmlContent2Text;
import static com.yeejoin.amos.knowledgebase.face.util.FileExporter.FileType.getInstance;
import static com.yeejoin.amos.knowledgebase.face.util.excel.ExcelParser.convertCellValueToString;

/**
 * <p>
 * 文档库管理服务类
 * </p>
 *
 * @author 子杨
 * @since 2020-08-05
 */
@Component
public class DocLibraryService {

    @Autowired
    private InteractionRecordService interactionRecordService;
    @Autowired
    private DocContentService docContentService;
    @Autowired
    private DynamicsValueService dynamicsValueService;
    @Autowired
    private InteractionCountService interactionCountService;
    //    @Autowired
//    private DynamicsGroupService dynamicsGroupService;
    @Autowired
    private DynamicsOptionService dynamicsOptionService;
    @Autowired
    private DocCommentsService docCommentsService;
    @Autowired
    private TagService tagService;
    @Autowired
    private ESDocService esDocService;
    @Autowired
    private TagInstanceService tagInstanceService;
    @Autowired
    private TagValueService tagValueService;
    @Autowired
    private Sequence sequence;
    @Autowired
    private DocCategoryService docCategoryService;

    private static final Integer BATCH_IMPORT_ONCE_NUM = 200;
    private static final List auditStatusList;

    static {
        List<String> list = new ArrayList<>();
        list.add(DocAuditService.DOC_AUDIT_STATUS_SUBMITTED);
        list.add(DocAuditService.DOC_AUDIT_STATUS_PASSED);
        auditStatusList = Collections.unmodifiableList(list);
    }

    /**
     * 收藏/取消
     *
     * @param id    被收藏的文档
     * @param favor 收藏/取消收藏
     * @return
     */
    public boolean favorite(Long id, boolean favor) {
        KnowledgeDocContentModel docContentModel = docContentService.queryBySeq(id);
        if (ValidationUtil.isEmpty(docContentModel)) {
            return false;
        }
        KnowledgeInteractionRecordModel recordModel = new KnowledgeInteractionRecordModel();
        recordModel.setOperateType(OperateType.COLLECT.name());
        recordModel.setUserId(RequestContext.getExeUserId());
        recordModel.setEntityType(KnowledgeDocContentModel.class.getSimpleName());
        recordModel.setEntityId(String.valueOf(id));
        KnowledgeInteractionRecordModel oldModel = interactionRecordService.queryUniqueModel(recordModel.getUserId(), recordModel.getEntityType(), recordModel.getEntityId(), recordModel.getOperateType());
        if (favor) {
            if (ValidationUtil.isEmpty(oldModel)) {
                recordModel.setAgencyCode(RequestContext.getAgencyCode());
                interactionRecordService.createWithModel(recordModel);
            }
        } else {
            if (!ValidationUtil.isEmpty(oldModel)) {
                interactionRecordService.deleteBySeq(oldModel.getSequenceNbr());
            }
        }
        return true;
    }

    public Page queryDocList(int offset, int end, Long directoryId, String docTitle, String[] auditStatus, String docStatus,
                             Map<String, String[]> requestMap, String createTimeLeft, String createTimeRight, boolean favor, boolean permission) {
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("userId", RequestContext.getExeUserId());
        paramsMap.put("start", offset);
        paramsMap.put("limit", end > offset ? end - offset + 1 : 50);
        // 封装权限过滤条件
        if (permission) {
            addPermissionFilters(paramsMap);
        }
        List<Long> directoryIdList = new ArrayList<>();
        if (directoryId != null) {
            Collection<KnowledgeDocCategoryModel> childCategoryModels = docCategoryService.queryDocCategoryTree(RequestContext.getAgencyCode(), directoryId);
            if (!childCategoryModels.isEmpty()) {
                directoryIdList.addAll(BaseUtil.getModelIds(TreeUtil.getAllChildren(childCategoryModels)));
            }
            //获取所有子分类ID
            directoryIdList.add(directoryId);
        }

        // 封装基本筛选条件
        addCommonFilters(paramsMap, directoryIdList, docTitle, docStatus, auditStatus, createTimeLeft, createTimeRight);
        // 是否收藏作为过滤条件
        if (favor) {
            paramsMap.put("collect", "COLLECT");
        }
        // 封装动态字段及动态条件过滤
        addExtraFieldsAndFilters(paramsMap, requestMap);
        Page page = new Page();
        int total = docContentService.getBaseMapper().queryDocBaseInfoTotal(paramsMap);
        page.setTotal(total);
        if (total > 0 && total > offset) {
            List<Map<String, Object>> list = docContentService.getBaseMapper().queryDocBaseInfoList(paramsMap);
            page.setRecords(list);
        }
        return page;
    }

    private void addExtraFieldsAndFilters(Map<String, Object> paramsMap, Map<String, String[]> requestMap) {
        List<KnowledgeDynamicsOptionModel> optionModels = dynamicsOptionService.queryByFunctional(RequestContext.getAppKey(), DynamicsFunctional.DOC_BASEINFO.name());
        if (ValidationUtil.isEmpty(optionModels)) {
            return;
        }
        List<String> extraFields = new ArrayList<>();
        List<String> extraStrFilters = new ArrayList<>();
        paramsMap.put("extraFields", extraFields);
        paramsMap.put("extraStrFilters", extraStrFilters);
        for (KnowledgeDynamicsOptionModel optionModel : optionModels) {
            String fieldName = optionModel.getFieldName();
            extraFields.add(fieldName);
            if (requestMap.containsKey(fieldName)) {
                String filterStr = buildFilterStr(fieldName, requestMap.get(fieldName), optionModel.getDataType(), optionModel.getQueryStrategy());
                if (!ValidationUtil.isEmpty(filterStr)) {
                    extraStrFilters.add(filterStr);
                }
            }
        }
    }

    private String buildFilterStr(String fieldName, String[] values, String dataType, String queryStrategy) {
        switch (OptionDataType.valueOf(dataType)) {
            case Enum:
            case String:
                break;
            case Double:
                fieldName = "CONVERT(" + fieldName + ",DECIMAL)";
            case Integer:
                fieldName = "CONVERT(" + fieldName + ",SIGNED)";
                break;
            case datetime:
                fieldName = "STR_TO_DATE(" + fieldName + ", '%Y-%m-%d %H:%i:%s')";
                break;
            case Date:
                fieldName = "STR_TO_DATE(" + fieldName + ", '%Y-%m-%d')";
                break;
            default:
                throw new RuntimeException("错误的数据类型");
        }
        switch (Operator.valueOf(queryStrategy)) {
            case eq:
                return ValidationUtil.isEmpty(values[0]) ? null : fieldName + " = \"" + values[0] + "\"";
            case like:
                return ValidationUtil.isEmpty(values[0]) ? null : fieldName + " LIKE \"%" + values[0] + "%\"";
            case between:
                if (ValidationUtil.isEmpty(values[0]) && ValidationUtil.isEmpty(values[1])) {
                    return null;
                }
                StringBuilder builder = new StringBuilder();
                if (!ValidationUtil.isEmpty(values[0])) {
                    builder.append(fieldName).append(" >= \"").append(values[0]).append("\"");
                    if (values.length>1 && !ValidationUtil.isEmpty(values[1])) {
                        builder.append(" AND ");
                    }
                }
                if (values.length>1 && !ValidationUtil.isEmpty(values[1])) {
                    builder.append(fieldName).append(" <= \"").append(values[1]).append("\"");
                }
                return builder.toString();
            default:
                throw new RuntimeException("动态字段查询方式有误.");
        }
    }

    private void addPermissionFilters(Map<String, Object> paramsMap) {
        Map<String, Object> permissionFiltersMap = new HashMap<>();
        paramsMap.put("permissionFilters", permissionFiltersMap);
        String inputerOrg = RemoteData.getOrgWithCurUserAndRole(KnowledgeRoleName.INPUTER.getRoleName());
        String auditorOrg = RemoteData.getOrgWithCurUserAndRole(KnowledgeRoleName.AUDITOR.getRoleName());
        if (ValidationUtil.isEmpty(inputerOrg) && ValidationUtil.isEmpty(auditorOrg)) {
            throw new RequestForbidden("用户角色错误");
        }
        if (!ValidationUtil.isEmpty(inputerOrg)) {
            permissionFiltersMap.put("userId", RequestContext.getExeUserId());
        }
        if (!ValidationUtil.isEmpty(auditorOrg)) {
            permissionFiltersMap.put("orgCode", auditorOrg);
            permissionFiltersMap.put("auditStatusList", auditStatusList);
        }
    }

    private void addCommonFilters(Map<String, Object> paramsMap, List<Long> directoryIdList, String docTitle, String docStatus, String[] auditStatus, String createTimeLeft, String createTimeRight) {
        Map<String, Object> commonFiltersMap = new HashMap<>(16);
        paramsMap.put("commonFilters", commonFiltersMap);
        if (!ValidationUtil.isEmpty(directoryIdList)) {
            commonFiltersMap.put("directoryIdList", directoryIdList);
        }
        if (!ValidationUtil.isEmpty(docTitle)) {
            commonFiltersMap.put("docTitle", docTitle);
        }
        if (!ValidationUtil.isEmpty(docStatus)) {
            commonFiltersMap.put("docStatus", docStatus);
        }
        if (!ValidationUtil.isEmpty(auditStatus)) {
            commonFiltersMap.put("auditStatus", Arrays.asList(auditStatus));
        }
        try {
            if (!ValidationUtil.isEmpty(createTimeLeft)) {
                commonFiltersMap.put("createTimeLeft", DateUtil.formatStringToDate(createTimeLeft, null));
            }
            if (!ValidationUtil.isEmpty(createTimeRight)) {
                commonFiltersMap.put("createTimeLeft", DateUtil.formatStringToDate(createTimeRight, null));
            }
        } catch (Exception e) {
            throw new BadRequest("时间范围参数格式有误");
        }
    }

    /**
     * 高级检索
     *
     * @param page        分页信息
     * @param queryParams sql条件
     * @return 分页数据
     */
    public Page searchAdvanced(Page page, Map<String, Object> queryParams) {
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<>();
        wrapper.select("SEQUENCE_NBR")
               .eq("DOC_STATUS", DocContentService.DOC_STATUS_PUBLISHED)
               .orderByAsc("SORT_STR");

        BaseSqlCondition sqlCondition = BaseSqlCondition.getInstance(queryParams);
        if (sqlCondition.getTotalStatement() > 0) {
            sqlCondition.compose(wrapper);
        }
        IPage<KnowledgeDocContent> iPage = docContentService.page(page, wrapper);
        List<ESDocEntity> esList = new ArrayList<>();
        iPage.getRecords().forEach(doc -> {
            ESDocEntity esDocEntity = esDocService.queryById(doc.getSequenceNbr());
            if (ValidationUtil.isEmpty(esDocEntity)) {
                throw new BadRequest("ES库数据丢失，请联系管理员协助解决");
            }
            Set<String> involvedTagIds = sqlCondition.getTagSeqs();
            deleteRepeatedTags(esDocEntity.getDocTags(), involvedTagIds);
            deleteRepeatedTags(esDocEntity.getContentTags(), involvedTagIds);
            esList.add(esDocEntity);
        });
        fillDirectoryName(esList);
        return page.setRecords(esList).setCurrent(iPage.getCurrent()).setSize(iPage.getSize()).setTotal(iPage.getTotal());
    }

    /**
     * 删除未命中的标签，带去重功能
     */
    private static void deleteRepeatedTags(List<ESTagEntity> list, Set<String> tagIdSet) {
        if (ValidationUtil.isEmpty(list) || ValidationUtil.isEmpty(tagIdSet)) {
            return;
        }
        Iterator<ESTagEntity> tagIterator = list.iterator();
        while (tagIterator.hasNext()) {
            ESTagEntity tag = tagIterator.next();
            String tid = tag.getTagSeq().toString();
            if (tagIdSet.contains(tid)) {
                tagIdSet.remove(tid);
                tag.setTagInfo(null);
            } else {
                tagIterator.remove();
            }
        }
    }

//    public Page queryDocPage(Page page, Map<String, String[]> paramMap, boolean filterByCollection, Date createTimeLeft, Date createTimeRight) {
//        List<Long> collecion = getMyFavoriteDocIdList();
//        Page resPage = queryDocPageByCollection(page, RequestContext.getAppKey(), paramMap, new ArrayList<>(collecion), filterByCollection, createTimeLeft, createTimeRight);
//        List<Map<String, Object>> records = resPage.getRecords();
//        for (Map<String, Object> record : records) {
//            Long sequenceNbr = (Long) record.get("sequenceNbr");
//            record.put("collect", collecion.contains(sequenceNbr));
//            record.put("referenceNum", getReferenceCount(sequenceNbr));
//            record.put("collectionNum", getCollectionCount(sequenceNbr));
//        }
//        return resPage;
//    }

//    private Page queryDocPageByCollection(Page page, String appKey, Map<String, String[]> paramMap, List<Long> myFavorites, boolean filterByCollection, Date createTimeLeft, Date createTimeRight) {
//        KnowledgeDynamicsGroupModel groupModel = dynamicsGroupService.queryByFunctional(appKey, DynamicsFunctional.DOC_BASEINFO.name());
//        Set<Long> instanceIds = intersection(groupModel.getSequenceNbr(), paramMap);
//        if (!ValidationUtil.isEmpty(instanceIds)) {
//            Set<Long> idCollection = dynamicsValueService.withCollection(instanceIds, filterByCollection, myFavorites);
//            if (!ValidationUtil.isEmpty(idCollection)) {
//                page = docContentService.queryDocPageByIdsAndParams(page, DocContentService.DOC_STATUS_PUBLISHED, idCollection, createTimeLeft, createTimeRight);
//            }
//        }
//        List<KnowledgeDocContentModel> records = page.getRecords();
//        fillDirectoryName(records);
//        List<Map> returnList = fillDynamics(records, true);
//        page.setRecords(returnList);
//        return page;
//    }

//    private Set<Long> intersection(Long groupSeq, Map<String, String[]> paramMap) {
//        Set<Long> set = new HashSet<>();
//        List<KnowledgeDynamicsValue> entities;
//        if (ValidationUtil.isEmpty(paramMap)) {
//            QueryWrapper<KnowledgeDynamicsValue> wrapper = asymbleWrapper(groupSeq, paramMap);
//            wrapper.select("INSTANCE_ID");
//            entities = dynamicsValueService.list(wrapper);
//            if (!ValidationUtil.isEmpty(entities)) {
//                Map map = Bean.listToMap(entities, "instanceId", KnowledgeDynamicsValue.class);
//                set = map.keySet();
//            }
//        } else {
//            List<Set<Long>> setList = new ArrayList<>();
//            for (String key : paramMap.keySet()) {
//                Map<String, String[]> newParams = new HashMap<>();
//                newParams.put(key, paramMap.get(key));
//                QueryWrapper<KnowledgeDynamicsValue> wrapper = asymbleWrapper(groupSeq, newParams);
//                wrapper.select("INSTANCE_ID");
//                entities = dynamicsValueService.list(wrapper);
//                if (!ValidationUtil.isEmpty(entities)) {
//                    Map map = Bean.listToMap(entities, "instanceId", KnowledgeDynamicsValue.class);
//                    Set<Long> newSet = map.keySet();
//                    setList.add(newSet);
//                } else {
//                    setList.add(new HashSet<>());
//                }
//            }
//            set = new HashSet<>(dynamicsValueService.intersection(setList));
//        }
//        return set;
//    }

//    private QueryWrapper<KnowledgeDynamicsValue> asymbleWrapper(Long groupSeq, Map<String, String[]> paramMap) {
//        QueryWrapper<KnowledgeDynamicsValue> wrapper = new QueryWrapper<>();
//        wrapper.eq("GROUP_SEQ", groupSeq);
//        if (!ValidationUtil.isEmpty(paramMap)) {
//            wrapper.and(innerWrapper -> {
//                List<KnowledgeDynamicsValueModel> list = dynamicsValueService.queryByGroupWithoutAgency(groupSeq);
//                if (!ValidationUtil.isEmpty(list)) {
//                    for (int i = 0, count = 0; i < list.size(); i++) {
//                        KnowledgeDynamicsValueModel valueModel = list.get(i);
//                        if (paramMap.keySet().contains(valueModel.getFieldName())) {
//                            dynamicsValueService.asymbleWrapper(innerWrapper, valueModel, paramMap, count);
//                            count++;
//                        }
//                    }
//                }
//            });
//        }
//
//        return wrapper;
//    }

    public Map<String, Object> countDoc(Long id) {
        Map<String, Object> res = new HashMap<>();
        res.put("commentsNum", docCommentsService.getCommentsTotal(id));
        res.put("collectionNum", getCollectionCount(id));
        return res;
    }

    //获取我的文档收藏列表
    private List<Long> getMyFavoriteDocIdList() {
        List<KnowledgeInteractionRecordModel> recordModelList = interactionRecordService.queryListByUser(RequestContext.getExeUserId(), OperateType.COLLECT.name(), KnowledgeDocContentModel.class.getSimpleName(), RequestContext.getAgencyCode());
        List<Long> idList = new ArrayList<>();
        for (KnowledgeInteractionRecordModel recordModel : recordModelList) {
            idList.add(Long.valueOf(recordModel.getEntityId()));
        }
        return idList;
    }

    //获取文档被收藏总数
    public int getCollectionCount(Long sequenceNbr) {
        return interactionRecordService.countByInstance(OperateType.COLLECT.name(), KnowledgeDocContentModel.class.getSimpleName(), sequenceNbr.toString());
    }

    //获取文档被引用总数
    public int getReferenceCount(Long sequenceNbr) {
        return interactionCountService.getOperateCount(InteractionCountService.QUOTE_TYPE_PUBLISH, InteractionCountService.QUOTE_ENTITY_DOCUMENT, sequenceNbr.toString());
    }

    /**
     * 根据关键字联想标签名称和文档名称
     *
     * @param queryStr 关键字
     * @return
     */
    public List<String> associate(String queryStr) {
        List resList = new ArrayList<>();
        List<KnowledgeTag> tagList = tagService.getBaseMapper().queryTagByNameInPublishedDoc(queryStr);
        resList.addAll(Bean.listToMap(tagList, "tagName", KnowledgeTag.class).keySet());
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<KnowledgeDocContent>()
                .select("DOC_TITLE").eq("DOC_STATUS", DocContentService.DOC_STATUS_PUBLISHED).like("DOC_TITLE", queryStr);
        List<KnowledgeDocContent> docContentList = docContentService.list(wrapper);
        docContentList.forEach(doc -> {
            if (!resList.contains(doc.getDocTitle())) {
                resList.add(doc.getDocTitle());
            }
        });
        return resList;
    }

    /**
     * 统计所有文档数与最新文档数
     *
     * @return
     */
    public Map<String, Integer> count(int days) {
        Map<String, Integer> res = new HashMap<>();
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<>();
        wrapper.eq("doc_status", DocContentService.DOC_STATUS_PUBLISHED);
        res.put("totalAll", docContentService.count(wrapper));
        Date rightRange = new Date();
        Date leftRange = getDateBeforeDays(rightRange, days - 1);
        wrapper.between("rec_date", leftRange, rightRange);
        res.put("totalNew", docContentService.count(wrapper));
        return res;
    }

    /**
     * 查询最新文档列表
     *
     * @param top 前top条
     * @return
     */
    public List queryNewDocs(int top) {
        List<KnowledgeDocContentModel> list = docContentService.queryNewDocsWithStatus(top, DocContentService.DOC_STATUS_PUBLISHED);
        fillDirectoryName(list);
        return fillDynamics(list, false);
    }

    /**
     * 查询我的收藏列表
     *
     * @return
     */
    public List queryMyCollectedDocs() {
        List<Long> myFavoriteDocIdList = getMyFavoriteDocIdList();
        if (ValidationUtil.isEmpty(myFavoriteDocIdList)) {
            return Collections.emptyList();
        }
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<>();
        wrapper.in("sequence_nbr", myFavoriteDocIdList).orderByDesc("rec_date");
        List<KnowledgeDocContent> list = docContentService.list(wrapper);
        List<KnowledgeDocContentModel> docContentModels = Bean.toModels(list, KnowledgeDocContentModel.class);
        fillDirectoryName(docContentModels);
        return fillDynamics(docContentModels, false);
    }

    /**
     * 填充动态字段
     */
    private List<Map> fillDynamics(List<KnowledgeDocContentModel> list, boolean simple) {
        List<Map> returnList = new ArrayList<>();
        if (!ValidationUtil.isEmpty(list)) {
            Set userSet = Bean.listToMap(list, "userId", KnowledgeDocContentModel.class).keySet();
            Map<String, String> userMap = new HashMap<>();
            if (!simple) {
                userMap = RemoteData.getUserMap(userSet);
            }
            for (KnowledgeDocContentModel contentModel : list) {
                if (simple) {
                    contentModel.setSummary(null);
                }
                contentModel.setHtmlContent(null);
                Map contentMap = Bean.BeantoMap(contentModel);
                List<KnowledgeDynamicsValueModel> listByInstance = dynamicsValueService.queryByInstanceId(contentModel.getSequenceNbr());
                Map<Object, Object> returnMap = Bean.listToMap(listByInstance, "fieldName", "fieldValue", KnowledgeDynamicsValueModel.class);
                if (!simple) {
                    returnMap.put("referenceNum", getReferenceCount(contentModel.getSequenceNbr()));
                    returnMap.put("collectionNum", getCollectionCount(contentModel.getSequenceNbr()));
                    returnMap.put("userName", userMap.get(contentModel.getUserId()));
                }
                contentMap.putAll(returnMap);
                returnList.add(contentMap);
            }
        }
        return returnList;
    }

    private Date getDateBeforeDays(Date date, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, -days);
        return calendar.getTime();
    }

    public void export(long id, String type, HttpServletResponse response) {
        KnowledgeDocContent docContent = docContentService.getById(id);
        if (ValidationUtil.isEmpty(docContent)) {
            throw new DataNotFound("文档不存在");
        }
        String docTitle = docContent.getDocTitle();
        byte[] bytes;
        FileType fileType = getInstance("." + type.trim());
        switch (fileType) {
            case docx:
                bytes = new DocxBuilder(docContent.getHtmlContent()).build();
                break;
            case pdf:
                bytes = PdfBuilder.html2Pdf(docContent.getHtmlContent(), docTitle);
                break;
            default:
                throw new BadRequest("不识别的导出类型");
        }
        FileExporter.exportFile(fileType, docTitle, bytes, response);
    }

    @Transactional(rollbackFor = {Exception.class, BaseException.class})
    public List importExcel(MultipartFile file, ExcelImportConfig excelConfig) {
        String inputerOrg = RemoteData.getOrgWithCurUserAndRole(KnowledgeRoleName.INPUTER.getRoleName());
        if (ValidationUtil.isEmpty(inputerOrg)) {
            throw new RequestForbidden("非录入者角色用户无权录入");
        }
        int finishedNum = 0;
        if (!excelConfig.check()) {
            throw new BadRequest("模板配置数据有误");
        }
        Workbook workbook = getBookWithFile(file);
        Sheet sheet = workbook.getSheetAt(0);
        if (sheet == null) {
            throw new BadRequest("文档sheet页为空");
        }
        int rowNumber = getRowNumber(sheet, excelConfig);
        if (rowNumber <= 0) {
            throw new BadRequest("数据条目为零");
        }
        Set<Long> involvedTagIds = excelConfig.getTagColumns().keySet();
        // 组装标签信息Map
        Map<Long, KnowledgeTagModel> tagMap = new HashMap();
        if (!ValidationUtil.isEmpty(involvedTagIds)) {
            List<KnowledgeTagModel> tagModelList = tagService.queryByIds(involvedTagIds);
            for (KnowledgeTagModel tagModel : tagModelList) {
                tagMap.put(tagModel.getSequenceNbr(), tagModel);
            }
        }
        // 组装文档动态字段数据类型Map
        Map<String, KnowledgeDynamicsOptionModel> fieldTypeMap = new HashMap<>();
        List<KnowledgeDynamicsOptionModel> optionModels = dynamicsOptionService.queryByFunctional(RequestContext.getAppKey(), DynamicsFunctional.DOC_BASEINFO.name());
        for (KnowledgeDynamicsOptionModel optionModel : optionModels) {
            fieldTypeMap.put(optionModel.getFieldName(), optionModel);
        }

        // 组装标签值类型Map
        Map<Long, String> tagValueTypeMap = new HashMap<>();
        if (!ValidationUtil.isEmpty(involvedTagIds)) {
            List<KnowledgeDynamicsValueModel> dynamicsValueList = dynamicsValueService.queryFieldValueByIds(new ArrayList(involvedTagIds), "valueType");
            for (KnowledgeDynamicsValueModel dynamicsValueModel : dynamicsValueList) {
                tagValueTypeMap.put(dynamicsValueModel.getInstanceId(), dynamicsValueModel.getFieldValue());
            }
        }
        // 组装字典中英文Map
        Map<String, Map<String, String>> enumCnEnMap = new HashMap<>();
        Set<String> enumNameList = new HashSet<>(excelConfig.getEnumMap().values());
        for (String enumName : enumNameList) {
            try {
                List<DictionarieValueModel> dictList = RemoteData.queryDict(enumName);
                Map<String, String> cnEnMap = new HashMap<>();
                for (DictionarieValueModel dictionarieValueModel : dictList) {
                    cnEnMap.put(dictionarieValueModel.getDictDataValue(), dictionarieValueModel.getDictDataKey());
                }
                enumCnEnMap.put(enumName, cnEnMap);
            } catch (Exception e) {
                throw new RequestForbidden("获取字典数据出错，解析无法进行");
            }
        }
        // 组装标签单位Map
        Map<Long, String> tagUnitMap = new HashMap<>();
        if (!ValidationUtil.isEmpty(involvedTagIds)) {
            List<KnowledgeDynamicsValueModel> dynamicsValueList = dynamicsValueService.queryFieldValueByIds(new ArrayList(involvedTagIds), "unit");
            for (KnowledgeDynamicsValueModel dynamicsValueModel : dynamicsValueList) {
                tagUnitMap.put(dynamicsValueModel.getInstanceId(), dynamicsValueModel.getFieldValue());
            }
        }

        ExcelParser excelParser = new ExcelParser(tagMap, excelConfig, fieldTypeMap, tagValueTypeMap, enumCnEnMap, tagUnitMap, this.sequence, inputerOrg);
        // 逐行解析
        while (finishedNum < rowNumber) {
            for (int i = 0; i < BATCH_IMPORT_ONCE_NUM && finishedNum < rowNumber; i++, finishedNum++) {
                Row row = sheet.getRow(excelConfig.getStartRowIndex() + finishedNum);
                excelParser.parseRow(row);
            }
            batchInsertDocDataAll(excelParser.getAndFlushDatas());
            QuoteCountFlushTiming.needFlushTag();
        }
        return excelParser.getImportResults();
    }

    void batchInsertDocDataAll(ExcelParser.TablesDataList datas) {
        docContentService.saveBatch(datas.getDocContentList());
        dynamicsValueService.saveBatch(datas.getDynamicsValueList());
        tagInstanceService.saveBatch(datas.getTagInstanceList());
        tagValueService.saveBatch(datas.getTagValueList());
    }

    public List<KnowledgeDocContentModel> efficientList(Collection<Long> docSeqList) {
        if (ValidationUtil.isEmpty(docSeqList)) {
            return Collections.emptyList();
        }
        List<KnowledgeDocContentModel> contentModelList = docContentService.queryByIds(docSeqList);
        fillDirectoryName(contentModelList);
        Map<Object, KnowledgeDocContentModel> docContentModelMap = Bean.listToMap(contentModelList, "sequenceNbr", KnowledgeDocContentModel.class);
        if (!contentModelList.isEmpty()) {
            List<KnowledgeTagInstanceModel> tagInstanceModelList = tagInstanceService.queryListByTargetSeqs(docSeqList);
            Map<Object, KnowledgeTagInstanceModel> tagInstanceModelMap = Bean.listToMap(tagInstanceModelList, "sequenceNbr", KnowledgeTagInstanceModel.class);
            List<KnowledgeTagValueModel> tagValueModelList = tagValueService.queryTagValuesByDocIds(docSeqList);
            List<KnowledgeDynamicsValueModel> dynamicsValueModelList = dynamicsValueService.queryByInstanceIds(docSeqList);
            // 标签值组装到标签实例
            tagValueModelList.forEach(tagValueModel -> {
                KnowledgeTagInstanceModel instanceModel = tagInstanceModelMap.get(tagValueModel.getInstanceSeq());
                if (null != instanceModel) {
                    List<KnowledgeTagValueModel> tagValues = instanceModel.getTagValues();
                    if (null == tagValues) {
                        tagValues = new ArrayList<>();
                        instanceModel.setTagValues(tagValues);
                    }
                    tagValues.add(tagValueModel);
                }
            });
            // 标签实例组装到文档实例
            tagInstanceModelList.forEach(tagInstanceModel -> {
                KnowledgeDocContentModel docContentModel = docContentModelMap.get(tagInstanceModel.getTargetSeq());
                if (null != docContentModel) {
                    boolean isDoc = TagInstanceService.MARKING_TYPE_DOC.equals(tagInstanceModel.getMarkingType());
                    List<KnowledgeTagInstanceModel> tagList = isDoc ? docContentModel.getDocTags() : docContentModel.getDocContentTags();
                    if (null == tagList) {
                        tagList = new ArrayList<>();
                        if (isDoc) {
                            docContentModel.setDocTags(tagList);
                        } else {
                            docContentModel.setDocContentTags(tagList);
                        }
                    }
                    tagList.add(tagInstanceModel);
                }
            });
            // 动态字段组装到文档实例
            dynamicsValueModelList.forEach(dynamicsValueModel -> {
                KnowledgeDocContentModel docContentModel = docContentModelMap.get(dynamicsValueModel.getInstanceId());
                if (null != docContentModel) {
                    Map<String, Object> docBaseInfo = docContentModel.getDocBaseInfo();
                    if (null == docBaseInfo) {
                        docBaseInfo = new HashMap<>();
                        docContentModel.setDocBaseInfo(docBaseInfo);
                    }
                    docBaseInfo.put(dynamicsValueModel.getFieldName(), dynamicsValueModel.getFieldValue());
                }
            });
        }
        return contentModelList;
    }

    /**
     * 根据文件获得workbook对象
     *
     * @param file 文件
     * @return 对象
     */
    private Workbook getBookWithFile(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
        try {
            if ("xls".equals(fileType)) {
                return new HSSFWorkbook(new ByteArrayInputStream(file.getBytes()));
            } else if ("xlsx".equals(fileType)) {
                return new XSSFWorkbook(new ByteArrayInputStream(file.getBytes()));
            } else {
                throw new BadRequest("不支持的文件格式");
            }
        } catch (IOException e) {
            throw new BadRequest("文件内容错误");
        }
    }

    /**
     * 获取有效行数行数
     *
     * @param sheet
     * @param excelConfig
     */
    private int getRowNumber(Sheet sheet, ExcelImportConfig excelConfig) {
        int res = 0;
        for (int rowIndex = excelConfig.getStartRowIndex(); rowIndex < sheet.getPhysicalNumberOfRows(); rowIndex++) {
            Row row = sheet.getRow(rowIndex);
            if (row == null) {
                break;
            }
            Cell cell = row.getCell(excelConfig.getStopColIndex());
            if (cell == null || ValidationUtil.isEmpty(convertCellValueToString(cell))) {
                break;
            }
            res++;
        }
        return res;
    }

    /**
     * 获取字典数据中英文
     *
     * @return
     */
    public Map<String, Map<String, String>> getBaseEnumMap() {
        Map<String, Map<String, String>> resMap = new HashMap<>();
        List<KnowledgeDynamicsOptionModel> optionModels = dynamicsOptionService.queryByFunctional(RequestContext.getAppKey(), DynamicsFunctional.DOC_BASEINFO.name());
        for (KnowledgeDynamicsOptionModel optionModel : optionModels) {
            if ("Enum".equals(optionModel.getDataType())) {
                String fieldName = optionModel.getFieldName();
                String frontEndConfig = optionModel.getFrontEndConfig();
                if (ValidationUtil.isEmpty(frontEndConfig)) {
                    continue;
                }
                try {
                    String enumName = (String) JSON.parseObject(frontEndConfig).get("typeData");
                    List<DictionarieValueModel> dictList = RemoteData.queryDict(enumName);
                    Map<String, String> enCnMap = new HashMap<>();
                    for (DictionarieValueModel dictionarieValueModel : dictList) {
                        enCnMap.put(dictionarieValueModel.getDictDataKey(), dictionarieValueModel.getDictDataValue());
                    }
                    resMap.put(fieldName, enCnMap);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return resMap;
    }

    /**
     * 刷新历史文档数据以支持高级检索
     *
     * @return
     */
    public boolean init() {
        int count = docContentService.count();
        int finishNmu = 0;
        int current = 0;
        while (count > finishNmu) {
            com.baomidou.mybatisplus.extension.plugins.pagination.Page page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page(current++, 30);
            IPage resPage = docContentService.page(page);
            List<KnowledgeDocContent> records = resPage.getRecords();
            records.forEach(doc -> {
                if (ValidationUtil.isEmpty(doc.getTextContent()) && ValidationUtil.isEmpty(doc.getSortStr())) {
                    doc.setTextContent(htmlContent2Text(doc.getHtmlContent()));
                    doc.setSortStr(DocSortUtil.getSortStr(tagInstanceService.queryByTargetAndType(null, doc.getSequenceNbr(), TagInstanceService.MARKING_TYPE_DOC)));
                }
            });
            docContentService.updateBatchById(records);
            finishNmu += records.size();
        }
        return true;
    }

    /**
     * 为文档添加分类名称
     *
     * @param docList 文档列表
     * @param <T>     集合类型
     */
    public <T extends Collection> void fillDirectoryName(T docList) {
        if (ValidationUtil.isEmpty(docList)) {
            return;
        }
        List<KnowledgeDocCategoryModel> categoryModelList = docCategoryService.queryForKnowledgeDocCategoryList(null);
        if (ValidationUtil.isEmpty(categoryModelList)) {
            return;
        }
        Map<Object, KnowledgeDocCategoryModel> categoryModelMap = Bean.listToMap(categoryModelList, "sequenceNbr", KnowledgeDocCategoryModel.class);
        docList.forEach(doc -> {
            if (doc instanceof KnowledgeDocContentModel) {
                KnowledgeDocContentModel doc1 = (KnowledgeDocContentModel) doc;
                KnowledgeDocCategoryModel categoryModel = categoryModelMap.get(doc1.getDirectoryId());
                doc1.setDirectoryName(ValidationUtil.isEmpty(categoryModel) ? null : categoryModel.getCategoryName());
            } else if (doc instanceof ESDocEntity) {
                ESDocEntity doc1 = (ESDocEntity) doc;
                KnowledgeDocCategoryModel categoryModel = categoryModelMap.get(doc1.getDirectoryId());
                doc1.setDirectoryName(ValidationUtil.isEmpty(categoryModel) ? null : categoryModel.getCategoryName());
            }
        });
    }

    public Page queryDocPage(Page page, String docTitle, String code) {
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<>();
        wrapper.eq("DOC_STATUS", DocContentService.DOC_STATUS_PUBLISHED);
        if (!ValidationUtil.isEmpty(docTitle)) {
            wrapper.like("DOC_TITLE", docTitle);
        }
        if (!ValidationUtil.isEmpty(code)) {
            wrapper.inSql("SEQUENCE_NBR", "SELECT INSTANCE_ID FROM KNOWLEDGE_DYNAMICS_VALUE kdv WHERE kdv.FIELD_NAME = 'code' AND kdv.FIELD_VALUE LIKE '%" + code.replace("'", "''") + "%'");
        }
        page = (Page) docContentService.page(page, wrapper);
        if (!page.getRecords().isEmpty()) {
            ArrayList list = Bean.toModels(page.getRecords(), KnowledgeDocContentModel.class);
            fillDirectoryName(list);
            page.setRecords(fillDynamics(list, false));
        }
        return page;
    }

    public List selectByDirsRand(List<Long> directoryList, Integer total) {
        Set<Long> directoryIdSet = new HashSet<>();
        List<KnowledgeDocCategoryModel> categoryList = Bean.toModels(docCategoryService.list(), KnowledgeDocCategoryModel.class);
        directoryList.forEach(
                directoryId -> {
                    directoryIdSet.addAll(BaseUtil.getModelIds(TreeUtil.getAllChildren(TreeBuilder.bulid(categoryList, directoryId))));
                    if (!directoryId.equals(DocCategoryService.ROOT)) {
                        directoryIdSet.add(directoryId);
                    }
                }
        );
        QueryWrapper<KnowledgeDocContent> wrapper = new QueryWrapper<>();
        wrapper.eq("DOC_STATUS", DocContentService.DOC_STATUS_PUBLISHED)
               .in("DIRECTORY_ID", directoryIdSet);
        if (total != null) {
            wrapper.last("ORDER BY RAND() LIMIT "+ total);
        }
        List<KnowledgeDocContent> contentList = docContentService.list(wrapper);
        List<KnowledgeDocContentModel> list = Bean.toModels(contentList, KnowledgeDocContentModel.class);
        fillDirectoryName(list);
        return fillDynamics(list, false);
    }
}
