fuccc

复制代码
package com.atta.csorder.service.impl;

/**
 * @author caozhongsheng
 * @date 2024-12-06
 */
@Slf4j
@Service
public class CSWorkOrderServiceImpl implements CSWorkOrderService {
    private static final Pattern phonePattern = Pattern.compile("[1][1-9][0-9]{9}");

    private static final Pattern HKPhonePattern = Pattern.compile("^([6|9])\\d{7}$");

    @Value("${atta.crm.workOrder.autoCancelledDays}")
    private int autoCancelledDays;
    @Value("${atta.crm.pre.workOrder.form.version:v3}")
    private String preWorkOrderFormVersion;
    @Value("${atta.crm.leads.workorderEndDay:1}")
    private int workorderEndDay;
    @Value("${atta.crm.leads.workorderEndHour:21}")
    private int workorderEndHour;
    @Value("${atta.crm.leads.max.SuspendOrderCount:3}")
    private int maxSuspendOrderCount;
    @Value("${atta.workorder.survey.questionnaireId}")
    private String questionnaireId;
    @Value("${atta.crm.workOrder.primaryGroup}")
    private String primaryGroup;
    @Value("${atta.crm.workOrder.consultWorkOrderLimit:100}")
    private int consultWorkOrderLimit;
    @Value("${atta.crm.workOrder.preDayLimit:90}")
    private Integer preDayLimit;
    @Value("${atta.crm.workOrder.secondGroup}")
    private String secondGroup;
    //true: 取查询数据, false: 取数仓数据
    @Value("${crm.customer.partner.data.enable:true}")
    private boolean CRM_CUSTOMER_PARTNER_DATA_ENABLE;
    @Value("${crm.workOrder.appointment.remarkLength:100}")
    private Integer remarkLength;
    @Value("#{${crm.workOrder.consultingTypeMap:null}}")
    private HashMap<String, String> consultingTypeMap;
    @Value("#{'${atta.crm.workOrder.preAuditConfigIds:13990100165000022070400016401}'.split(',')}")
    private Set<String> preAuditConfigIds;
    @Value("#{'${atta.crm.workOrder.preWorkOrderConfigIds:13990100165000020052200020001,13990100165000022030100010105,13990100165000023031600024001}'.split(',')}")
    private Set<String> preWorkOrderConfigIds;
    @Value("#{'${atta.crm.workOrder.customerAuditConfigId:13990100165000020052200020001,13990100165000022030100010105,13990100165000022072100017601,13990100165000022112300021801,13990100165000023011000023401}'.split(',')}")
    private Set<String> customerAuditConfigId;
    @Value("#{'${atta.crm.workOrder.allowedEditFirmInfoConfigId:13990100165000020052200020001,13990100165000022030200000541}'.split(',')}")
    private Set<String> allowedEditFirmInfoConfigId;

    @Value("#{'${crm.coop.workorder.notify.dingIds:}'.empty ? null : '${crm.coop.workorder.notify.dingIds:}'.split(',')}")
    public List<String> dingUserIds;
    @Value("#{'${crm.workorder.complete.check.configIds:123}'.split(',')}")
    private Set<String> needCheckWorkConfigIds = Sets.newHashSet();
    @Value("${atta.crm.work.order.submit.bossUserId:04990100116000021033000019079}")
    private String submitBossUserId;
    @Value("${atta.crm.work.order.submit.bossUserName:刘智平}")
    private String submitBossUserName;
    @Value("#{${cswo.email.work.order.email.account.map}}")
    private Map<String, String> emailWorkOrderEmailAccountMap = new HashMap<>();
    /**
     * 客服工单查询是否走doris
     */
    @Value("${atta.crm.customer.work.order.switch:true}")
    private boolean customerOrderSwitch;
    @Value("${atta.crm.customer.coop.work.order.limit:9000}")
    private int coop_workoder_query_limit;
    @Value("#{'${cswo.email.config.ids}'.split(',')}")
    private Set<String> emailCsWorkOrderConfigIds = new HashSet<>();
    @Value("${cswo.event.center.enabled:false}")
    private boolean openEventCenterEnabled;
    @Value("${crm.workOrder.url.pre:https://sitxt47.xtrfr.cn/boss2/magpiebridge/customerservice/edit-customer-service-order?orderId=}")
    private String workOrderUrlPre;

    private static final String REQUEST_SOURCE = "crmserv";

    private static final String APP_NAME = "xtransfer";

    private static final String DING_NOTIFY_TYPE = "1";
    private static final String CHANGE_USER_MASTER_PHONE = "更换用户主手机号";
    private static final String ACCOUNT_SECURITY = "账户安全";
    private static final String UN_BIND_X = "解绑X盾";
    private static final String PRE_AUDIT_RESULT = "preAuditResult";
    private static final String PASS_RESULT = "Pass";
    private static final String NO_PASS_RESULT = "NoPass";
    private static final String FIRST_AUDIT = "首次认证预审核";
    private static final String WEALTH_MANAGEMENT_AUDIT = "理财业务预审核";
    private static final String FIRST_AUDIT_TOW = "首次认证预审";
    private static final String UPDATE_AUDIT = "更新认证预审";
    private static final String CUSTOMER_ANNUAL_AUDIT = "客户年审预审";
    private static final String FIRM_SHAREHOLDER_AUDIT = "大陆企业股东结汇预审";
    private static final String WEALTH_MANAGEMENT_AUDIT_PRE = "理财业务预审";

    private static final String DEFAULT_MESSAGE_TEMPLATE = "DEFAULT_MESSAGE_TEMPLATE";

    private static final String PRE_AUDIT_RESULT_KEY = "preAuditResult";
    private static final String PRE_AUDIT_SUGGESTION_KEY = "preAuditSuggestion";

    private static final String TODO_TITLE = "客服工单跟进提醒";

    private static final String TODO_DESC = "您负责的工单有新的跟进记录,请及时查看处理%s%s";

    private static final String SALE_SESSION_WORK_ORDER_TODO_DESC = "销售%s提交材料,留言%s,工单id:%s";

    private static final Map<CooperationDomain, BizType> COOPERATION_DOMAIN_BIZ_TYPE_MAP = new HashMap<CooperationDomain, BizType>() {{
        put(CooperationDomain.risk, BizType.RISK_CONTROL);
        put(CooperationDomain.customer_asset, BizType.CUSTOMER_ASSET);
        put(CooperationDomain.sale, BizType.SALE);
    }};

    private final Set<CSWorkOrderStatus> processCSWorkOrderStatus = Sets.newHashSet(CSWorkOrderStatus.Processing
            , CSWorkOrderStatus.UpgradeProcessing);

    @Autowired
    private CSOrderConfig csOrderConfig;
    @Autowired
    private RedissonLock redissonLock;
    @Autowired
    private IMQProvider imqProvider;
    @Autowired
    private EventProducer eventProducer;
    @Autowired
    private WorkorderMessageProducer workorderMessageProducer;
    @Autowired
    private TransactionLazyExecutor transactionLazyExecutor;
    @Autowired
    private TransactionUtil transactionUtil;
    @Autowired
    private UserSecretClient userSecretClient;
    @Autowired
    private EmailSendClient emailSendClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private FirmClient firmClient;
    @Autowired
    private FirmProfileClient firmProfileClient;
    @Autowired
    private FundAccountClient fundAccountClient;
    @Autowired
    private BossUserClient bossUserClient;
    @Autowired
    private BossUserGroupClient bossUserGroupClient;
    @Autowired
    private FormServAnswerClient formServAnswerClient;
    @Autowired
    private CallCenterClient callCenterClient;
    @Autowired
    private CSWorkOrderSearchClient csWorkOrderSearchClient;
    @Autowired
    private RiskWorkOrderClient riskWorkOrderClient;
    @Autowired
    private ManualAuditSnapshotClient manualAuditSnapshotClient;
    @Autowired
    private FormServClient formServClient;
    @Autowired
    private GwDingTalkTodoClient gwDingTalkTodoClient;
    @Autowired
    private MessageFacadeClient messageFacadeClient;
    @Autowired
    private CSWorkOrderLogService csWorkOrderLogService;
    @Autowired
    private CSWorkOrderConfigService cSWorkOrderConfigService;
    @Autowired
    private CSWorkOrderCommRecordService commRecordService;
    @Autowired
    private FeedbackMessageInstructionService feedbackMessageInstructionService;
    @Autowired
    private SLAEscalationService slaEscalationService;
    @Resource
    private SLAOrganizerService slaOrganizerService;
    @Autowired
    private ConsultProgressPushService consultProgressPushService;
    @Autowired
    private FileStorageUtils fileStorageUtils;
    @Autowired
    private AttachmentFacadeImpl attachmentFacade;
    @Autowired
    private SkylarkManager skylarkManager;
    @Autowired
    private CSWorkOrderUserService csWorkOrderUserService;
    @Autowired
    private CSWorkOrderAutomaticAllocationService csWorkOrderAutomaticAllocationService;
    @Autowired
    private WorkOrderConfigAndTagRelationService workOrderConfigAndTagRelationService;
    @Autowired
    private CSWorkOrderInstructionService cSWorkOrderInstructionService;

    @Autowired
    private MongoTransactionTemplate mongoTransactionTemplate;
    @Autowired
    private CSWorkOrderDAO csWorkOrderDAO;
    @Autowired
    private CSWorkOrderConfigDAO csWorkOrderConfigDAO;
    @Autowired
    private CSWorkOrderOpLogDAO csWorkOrderOpLogDAO;
    @Autowired
    private CSWorkOrderCommRecordDAO csWorkOrderCommRecordDAO;
    @Autowired
    private WorkOrderInstructionDAO workOrderInstructionDAO;
    @Autowired
    private ActionDAO actionDAO;
    @Autowired
    private CSWorkOrderReceiveEmailRecordDAO csWorkOrderReceiveEmailRecordDAO;
    @Autowired
    private CSWorkOrderRecordTemplateDAO csWorkOrderRecordTemplateDAO;
    @Autowired
    private CSWorkOrderRecdTmplOpLogDAO csWorkOrderRecdTmplOpLogDAO;

    @Autowired
    private CoopWorkOrderMessageHolder coopWorkOrderMessageHolder;
    @Autowired
    private MessageManager messageManager;
    @Autowired
    private TodoTaskManager todoTaskManager;
    @Autowired
    private QuestionnaireManager questionnaireManager;
    @Autowired
    private WorkOrderConfig workOrderConfig;
    @Autowired
    private CSWOSlaEventService cswoSlaEventService;
    @Autowired
    private AllocateRouteService allocateHandleService;
    @Autowired
    private IntelligentHostingService hostingService;
    @Autowired
    private AssignBindProducer assignBindProducer;
    @Autowired
    private CSWorkOrderHostingEndProducer csWorkOrderHostingEndProducer;
    @Resource(name = "commonExecutor")
    private ExecutorService commonExecutor;
    @Autowired
    private StudioClient studioClient;
    @Autowired
    private CSWorkOrderAITopicService csWorkOrderAITopicService;

    @Override
    public String create(CSWorkOrderCreateRequest request) {
        currentWorkOrderConfigIsUsable(request.getWorkorderTypeId(), request.getCreateUserId());
        //校验跟进方式
        checkWorkOrderTrackWay(request);
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        String accountName = request.getAccountName();
        String userId = request.getUserId();
        String accountType = request.getAccountType();
        String userAccountId = request.getUserAccountId();
        if (Objects.equals(request.getAccountType(), FundAccountTypeEnum.PERSONAL.name())
                && StringUtils.isNotBlank(request.getUserAccountId())) {
            if (StringUtils.isBlank(accountName)) {
                FacadeResponse<FundAccountVO> response = fundAccountClient.queryAccountByAccountId(request.getUserAccountId());
                if (response.isSucceeded() && Objects.nonNull(response.getValue())) {
                    accountName = response.getValue().getAccountName();
                    userId = response.getValue().getUserId();
                }
            }
        }
        if (StringUtils.isNotBlank(userId) && !CollectionUtils.isEmpty(request.getBusinessInfoVOList())) {
            Optional<BusinessInfoVO> optional = request.getBusinessInfoVOList().stream()
                    .filter(item -> Objects.equals(item.getType(), BusinessInfoType.FeedbackWorkOrderAttachment))
                    .findAny();
            if (optional.isPresent()) {
                FacadeResponse<FundAccountVO> defaultAccount = fundAccountClient.getDefaultAccount(userId);
                if (defaultAccount.isSucceeded() && Objects.nonNull(defaultAccount.getValue())) {
                    accountType = defaultAccount.getValue().getAccountType();
                    accountName = defaultAccount.getValue().getAccountName();
                    userAccountId = defaultAccount.getValue().getAccountId();
                }
            }
        }
        WorkOrderAddRequest addRequest = WorkOrderAddRequest.builder()
                .userId(userId)
                .workOrderConfigId(request.getWorkorderTypeId())
                .sceneConfigId(request.getSceneConfigId())
                .problemScene(request.getProblemScene())
                .createUserId(request.getCreateUserId())
                .businessInfoVOList(request.getBusinessInfoVOList())
                .email(request.getEmail())
                .feedbackTime(request.getFeedbackTime())
                .firmId(request.getFirmId())
                .firmName(request.getFirmName())
                .principalUserId(request.getPrincipalUserId())
                .source(request.getSource())
                .currentDealUserId(request.getCurrentDealUserId())
                .workOrderEndTime(request.getWorkorderEndTime())
                .invitationCode(request.getInvitationCode())
                .phone(request.getPhone())
                .orderLevel(request.getOrderLevel())
                .referenceId(request.getReferenceId())
                .generatedReason(request.getGeneratedReason())
                .vaAccount(request.getVaAccount())
                .currency(request.getCurrency())
                .amount(request.getAmount())
                .serviceProvider(request.getServiceProvider())
                .assignId(request.getAssignId())
                .phoneAreaCode(request.getPhoneAreaCode())
                .newPhoneAreaCode(request.getNewPhoneAreaCode())
                .site(request.getSite())
                .serviceObject(StringUtils.isNotBlank(request.getSaleBossUserId()) ? "sale" : "user")
                .callCenterId(request.getCallCenterId())
                .skylarkCollaborationId(request.getSkylarkCollaborationId())
                .accountType(accountType)
                .userAccountId(userAccountId)
                .accountName(accountName)
                .receiptTradeId(request.getReceiptTradeId())
                .customerShowSwitch(request.getCustomerShowSwitch())
                .upgradeSource(request.getUpgradeSource())
                .consultTopic(request.getConsultTopic())
                .build();

        if (StringUtils.isNotBlank(request.getSaleBossUserId())) {
            User user = bossUserClient.queryByBossUserId(request.getSaleBossUserId());
            addRequest.setSaleBossUserId(Optional.ofNullable(user).map(User::getBossUserId).orElse(null));
            addRequest.setSaleBossUserName(Optional.ofNullable(user).map(User::getUserName).orElse(null));
        }
        // 打标 判断升级来源
        addRequest.setUpgradeSource(allocateHandleService.judgeSourceByWorkOrderTypeId(addRequest));

        // 插入 workOrderDO
        CSWorkOrderDO insertWorkOrder = insertWorkOrder(commanders, addRequest);

        String workOrderId = insertWorkOrder.getWorkorderId();

        // 关联问卷
        relateQuestionnaire(workOrderId, request.getWorkorderTypeId(), request.getSceneConfigId());

        // 插入跟踪记录
        csWorkOrderLogService.insertOpLog(commanders,
                buildOpLogAddRequest(workOrderId, CSWorkOrderStatus.Created, request.getCreateUserId(), "创建工单"));
        // 插入沟通记录
        ComRecordAddRequest comRecordAddRequest = ComRecordAddRequest.builder()
                .attachments(request.getAttachments())
                .workOrderId(workOrderId)
                .callCenterIds(request.getCallCenterIds())
                .operatorUserId(request.getCurrentDealUserId())
                .commMode(request.getCommMode())
                .content(request.getCommRecord())
                .recordTemplateId(request.getRecordTemplateId())
                .sessionIds(StringUtils.isNotEmpty(request.getAssignId()) ? Sets.newHashSet(request.getAssignId()) : request.getSessionIds())
                .workOrderTrackWay(request.getWorkOrderTrackWay())
                .csWorkOrderEmailRequest(request.getCsWorkOrderEmailRequest())
                .buttonSource(CsWoButtonSource.NEW_SUBMIT.name())
                .feedbackTime(request.getFeedbackTime())
                .build();
        String recordId = commRecordService.insertCommRecord(commanders, comRecordAddRequest);

        FormAnswerRequest formAnswerRequest = request.getFormAnswer();
        if (formAnswerRequest != null) {
            formAnswerRequest.setBizId(workOrderId);
            FacadeResponse formAnswerResponse = formServAnswerClient.commitAnswer(formAnswerRequest);
            CSExceptionAssert.isTrue(formAnswerResponse.isSucceeded()
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "commit answer failed!");
        }

        if (request.getFeedbackTime() != null && StringUtils.isNotBlank(request.getPrincipalUserId())) {
            FeedbackMessageInstructionReq messageInstructionReq = FeedbackMessageInstructionReq.builder()
                    .workerOrderId(workOrderId)
                    .feedbackTime(request.getFeedbackTime())
                    .principalUserId(request.getPrincipalUserId())
                    .build();
            feedbackMessageInstructionService.addWorkOrderInstructions(commanders, messageInstructionReq);
        }

        transactionLazyExecutor.executeCommanders(commanders);
        if (Objects.equals(request.getWorkOrderTrackWay(), WorkOrderTrackWay.EMAIL.name()) && Objects.nonNull(request.getCsWorkOrderEmailRequest())) {
            sendWorkOrderEmail(request.getCsWorkOrderEmailRequest(), workOrderId, request.getWorkorderTypeId());
        }

        TodoTaskEvent todoTaskEvent = new TodoTaskEvent();
        convertWorkOrderAddRequest2TodoTaskEvent(addRequest, todoTaskEvent);
        todoTaskEvent.setReferenceId(workOrderId);
        todoTaskEvent.setActionId(workOrderId);
        //工单完成后给代办发送消息
        if (StringUtils.isNotEmpty(todoTaskEvent.getPrincipalUserId())) {
            imqProvider.push(TodoTaskEvent.QUEUE_NAME, todoTaskEvent, QueueType.TOPIC);
        }
        //TODO 迁移变更:此处废弃了【优质客户计划】相关的跟进记录同步到客户池的逻辑

        createWorkOrderIntoEventCenter(workOrderId, addRequest.getSource(), request.getWorkorderTypeId());
        //上报sla数据
        slaEscalationService.escalationNewWorkOrderSla(insertWorkOrder);
        consultProgressPushService.newWorkOrder(insertWorkOrder, request.getButtonSource());
        // 创建工单时发送会话绑定消息
        CSAssignBindEvent assignBindEvent = new CSAssignBindEvent();
        // 创建工单时兼容老逻辑-先判断assignId是否有值,否则取sessionIds
        Set<String> assignBindEventAssignIds = StringUtils.isNotEmpty(request.getAssignId()) ? Sets.newHashSet(request.getAssignId()) : request.getSessionIds();
        assignBindEvent.setAssignIds(assignBindEventAssignIds);
        assignBindEvent.setWorkOrderId(workOrderId);
        assignBindEvent.setFirmId(request.getFirmId());
        assignBindProducer.sendAssignBind(assignBindEvent);
        return workOrderId;
    }

    @Override
    public String edit(CSWorkOrderEditRequest request) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());
        if (csWorkOrderDO == null) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "工单不存在");
        }
        String oldCustomerShowSwitch =  csWorkOrderDO.getCustomerShowSwitch() == null ? CsWoCustomerShowSwitchEnum.CLOSE.name() : csWorkOrderDO.getCustomerShowSwitch();
        CsWoCustomerShowSwitchEnum oldCustomerShowSwitchEnum = CsWoCustomerShowSwitchEnum.valOf(oldCustomerShowSwitch);
        CsWoCustomerShowSwitchEnum customerShowSwitchEnum = CsWoCustomerShowSwitchEnum.valOf(request.getCustomerShowSwitch());
        csWorkOrderDO.setCustomerShowSwitch(customerShowSwitchEnum.name());

        List<DataPersistenceCommander> commanders = new ArrayList<>();

        OpLogAddRequest logAddRequest = OpLogAddRequest.builder()
                .workOrderId(request.getWorkOrderId())
                .status(csWorkOrderDO.getStatus())
                .operatorUserId(request.getCurrentDealUserId())
                .suggestion(String.format("对客展示开关由【%s】变更为【%s】", covertCustomerShowSwitch(oldCustomerShowSwitchEnum), covertCustomerShowSwitch(customerShowSwitchEnum)))
                .build();
        csWorkOrderLogService.insertOpLog(commanders, logAddRequest);
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR, "更新失败");
        });
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
            consultProgressPushService.sendCustomerShowSwitchChange(csWorkOrderDO, customerShowSwitchEnum);
        } catch (Exception e) {
            log.error("edit workOrder failed. e is: ", e);
            CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, "edit workOrder failed");
        }
        return request.getWorkOrderId();
    }

    @Override
    public String extractTopic(CSWorkOrderExtractTopicRequest request) {
        // 根据会话id获取会话内容
        String imMessage = getIMMessageByScene(request);
        if (StringUtils.isBlank(imMessage)) {
            log.warn("Failed to get IM message for session id: {}", request.getSessionId());
            return null;
        }

        // 封装调用模型接口参数
        Map<String, Object> inputMap = new HashMap<>();
        inputMap.put(CsOrderConstants.WORKFLOW_INPUT_WORKORDER_TYPE, request.getWorkorderType());
        inputMap.put(CsOrderConstants.WORKFLOW_INPUT_WORKORDER_PROBLEM_TYPE, request.getProblemType());
        inputMap.put(CsOrderConstants.WORKFLOW_INPUT_CONVERSATION, imMessage);

        WorkFlowRunRequest workFlow = WorkFlowRunRequest.createSync(
                CsOrderConstants.WORKFLOW_CONSULTATION_THEME_EXTRACTION,
                null,
                UUID.randomUUID().toString(),
                inputMap);

        // 执行工作流
        FacadeResponse<WorkFlowRunResponse> facadeResponse = studioClient.workflowRun(workFlow);
        if (!facadeResponse.isSucceeded() || null == facadeResponse.getValue()) {
            log.error("extractTopic workflow invoke failed, request: {}, response: {}",
                    request.getSessionId(), facadeResponse.getResponseMsg());
            return null;
        }

        // 处理大模型返回结果,提取theme字段
        return getConsultTheme(facadeResponse.getValue().getOutputJson());
    }


    private String getConsultTheme(String outputJson) {
        // 如果输出JSON为空,直接返回null
        if (StringUtils.isBlank(outputJson)) {
            return null;
        }
        try {
            Map<String, String> resultMap = JSON.parseObject(outputJson, new TypeReference<Map<String, String>>() {});
            // 获取theme字段值
            String theme = resultMap.get(WORKFLOW_OUTPUT_THEME);

            if (StringUtils.isBlank(theme)) {
                log.warn("getConsultTheme theme field is null in outputJson: {}", outputJson);
                return null;
            }
            return theme;
        } catch (Exception e) {
            log.warn("getConsultTheme Failed to parse outputJson or extract theme, returning null. OutputJson: {}",
                    outputJson, e);
            return null;
        }
    }

    /**
     * 根据场景获取IM消息/400消息
     *
     * @param request 请求参数
     * @return IM消息内容/400消息
     */
    private String getIMMessageByScene(CSWorkOrderExtractTopicRequest request) {
        if (CSWorkOrderAISummaryScene.IM.equals(request.getScene())) {
            // 获取im会话消息
            return csWorkOrderAITopicService.getIMTopicBySessionId(request.getSessionId());
        } else if (CSWorkOrderAISummaryScene.CALL_CENTER_400.equals(request.getScene())) {
            // 获取400会话记录
            return csWorkOrderAITopicService.get400TopicBySessionId(request.getSessionId());
        } else {
            log.warn("Unsupported scene: {}", request.getScene());
            return null;
        }
    }

    private String covertCustomerShowSwitch(CsWoCustomerShowSwitchEnum customerShowSwitchEnum) {
        return CsWoCustomerShowSwitchEnum.OPEN.equals(customerShowSwitchEnum) ? "展示" : "不展示";
    }

    /**
     * 该创建协同单的方法已经由云雀平台承接
     * SLA客服工单工作台无需上报信息
     *
     * @param request
     * @return
     */
    @Override
    @Deprecated
    public String createCoop(CSWorkOrderCreateRequest request) {
        currentWorkOrderConfigIsUsable(request.getWorkorderTypeId(), request.getCreateUserId());
        WorkOrderAddRequest addRequest = WorkOrderAddRequest.builder()
                .userId(request.getUserId())
                .workOrderConfigId(request.getWorkorderTypeId())
                .createUserId(request.getCreateUserId())
                .businessInfoVOList(request.getBusinessInfoVOList())
                .email(request.getEmail())
                .feedbackTime(request.getFeedbackTime())
                .firmId(request.getFirmId())
                .firmName(request.getFirmName())
                .principalUserId(request.getPrincipalUserId())
                .source(request.getSource())
                .currentDealUserId(request.getCurrentDealUserId())
                .workOrderEndTime(request.getWorkorderEndTime())
                .invitationCode(request.getInvitationCode())
                .phone(request.getPhone())
                .orderLevel(request.getOrderLevel())
                .referenceId(request.getReferenceId())
                .generatedReason(request.getGeneratedReason())
                .vaAccount(request.getVaAccount())
                .serviceProvider(request.getServiceProvider())
                .amount(request.getAmount())
                .currency(request.getCurrency())
                .build();

        // 插入 workOrderDO
        CSWorkOrderDO csWorkOrderDO = insertWorkOrder(addRequest);

        // 关联问卷
        relateQuestionnaire(csWorkOrderDO.getWorkorderId(), request.getWorkorderTypeId(), request.getSceneConfigId());

        CSWorkOrderOpLogDO opLog = csWorkOrderLogService.createOpLog(buildOpLogAddRequest(csWorkOrderDO.getWorkorderId(),
                CSWorkOrderStatus.Created, "system", "创建工单"));
        FormAnswerRequest formAnswerRequest = request.getFormAnswer();
        if (formAnswerRequest != null) {
            //此处关联工单id
            formAnswerRequest.setBizId(csWorkOrderDO.getWorkorderId());
            FacadeResponse formAnswerResponse = formServAnswerClient.commitAnswer(formAnswerRequest);
            CSExceptionAssert.isTrue(formAnswerResponse.isSucceeded()
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "commit answer failed!");
        }
        Boolean execute = mongoTransactionTemplate.execute(status -> {
            try {
                CSExceptionAssert.isTrue(csWorkOrderDAO.insert(csWorkOrderDO) != null
                        , CSOrderBizErrorCode.SYSTEM_ERROR
                        , "insert workOrderDO failed.");
                CSExceptionAssert.isTrue(csWorkOrderOpLogDAO.insert(opLog) != null
                        , CSOrderBizErrorCode.INSERT__FAILED
                        , "insert oplog failed.");
                return true;

            } catch (Exception e) {
                log.error(e.getMessage());
                status.setRollbackOnly();
                return false;

            }
        });
        if (Boolean.FALSE.equals(execute)) {
            return null;
        }
        return csWorkOrderDO.getWorkorderId();
    }

    @Override
    public PageSerializable<CSWorkOrderVO> pageQueryBySales(CSWorkOrderBySalesRequest request) {
        PageSerializable<CSWorkOrderVO> pageSerializable = new PageSerializable<>();
        if (Objects.isNull(request)) {
            return pageSerializable;
        }
        if (StringUtils.isEmpty(request.getSaleBossUserId())) {
            log.error("query sales work order failed, sales bossUseId is empty.");
            return pageSerializable;
        }
        Page<CSWorkOrderDO> csWorkOrderDOS = csWorkOrderDAO.pageQueryBySales(request.getSaleBossUserId(),
                request.getSearchText(), request.getPage(), request.getSize());

        if (CollectionUtils.isEmpty(csWorkOrderDOS.getResult()) || csWorkOrderDOS.getTotal() == 0) {
            return pageSerializable;
        }

        List<CSWorkOrderVO> csWorkOrderVOS = new ArrayList<>();
        for (CSWorkOrderDO csWorkOrderDO : csWorkOrderDOS.getResult()) {
            FacadeResponse<CSWorkOrderVO> facadeResponse = toWorkOrderVOList(csWorkOrderDO);
            if (!facadeResponse.isSucceeded()) {
                CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, facadeResponse.getResponseMsg());
                continue;
            }
            if (Objects.isNull(facadeResponse.getValue())) {
                continue;
            }
            csWorkOrderVOS.add(facadeResponse.getValue());
        }

        pageSerializable.setTotal(csWorkOrderDOS.getTotal());
        pageSerializable.setList(csWorkOrderVOS);
        return pageSerializable;
    }

    @Override
    public void own(CSWorkOrderOwnRequest request) {
        ValidateResult validateResult = ValidateUtil.validate(request);
        CSExceptionAssert.isTrue(validateResult.isPassed()
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , validateResult.getField() + validateResult.getReason());
        List<CSWorkOrderDO> csWorkOrderDOS = csWorkOrderDAO.queryOrdersByWorkOrderIds(request.getWorkorderIds());

        List<DataPersistenceCommander> commanders = new ArrayList<>();
        for (CSWorkOrderDO csWorkOrderDO : csWorkOrderDOS) {
            if (csWorkOrderDO == null || !CSWorkOrderStatus.Created.equals(csWorkOrderDO.getStatus())) {
                continue;
            }

            OrderAssignReq assignReq = OrderAssignReq.builder()
                    .workOrderId(csWorkOrderDO.getWorkorderId())
                    .reason("申领工单")
                    .toPrincipalUserId(request.getCurrentDealUserId())
                    .operatorUserId(request.getCurrentDealUserId())
                    .build();
            try {
                transferPrincipal(commanders, assignReq);
            } catch (Exception e) {
                log.warn("apply fail,workOrderId is: {}.principalUserId is :{},", csWorkOrderDO.getWorkorderId(), assignReq.getOperatorUserId());
            }
        }
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
        } catch (Exception e) {
            log.error("own workOrder failed. e is: ", e);
            CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, "own workOrder failed");
        }
        log.info("Leads assign workOrder complete. {}", request);
    }

    @Override
    public void transfer(CSWorkOrderTransferRequest request) {
        User principalUser = bossUserClient.queryByBossUserId(request.getToPrincipalUserId());
        if (principalUser == null) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "unable to obtain transferee information, modification failed");
        }

        List<CSWorkOrderDO> workOrderDOS = csWorkOrderDAO.find(new Query().addCriteria(Criteria.where("workorderId").in(request.getWorkorderIds())));

        String suggestion;
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        for (CSWorkOrderDO workOrderDO : workOrderDOS) {
            //撤销和结案状态的工单不能转移
            if (finalCSWorkOrderStatus.contains(workOrderDO.getStatus())) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.NOT_SUPPORT, "撤销和结案状态的工单不能转移");
            }

            //不能转移给原工单负责人
            if (request.getToPrincipalUserId().equals(workOrderDO.getPrincipalUserId())) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.NOT_SUPPORT, "不能转移给原工单负责人");
            }

            suggestion = String.format("转移负责人至【%s】,分配理由:%s", principalUser.getNick(), request.getReason());
            OrderAssignReq assignReq = OrderAssignReq.builder()
                    .workOrderId(workOrderDO.getWorkorderId())
                    .toPrincipalUserId(request.getToPrincipalUserId())
                    .reason(suggestion)
                    .operatorUserId(request.getCurrentDealUserId())
                    .build();
            try {
                transferPrincipal(commanders, assignReq);
            } catch (Exception e) {
                log.warn("transfer failed. e is: ", e);
            }
        }
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
        } catch (Exception e) {
            log.error("transfer workOrder failed. e is: ", e);
            CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, "transfer workOrder failed");
        }
    }

    @Override
    public void saveCommRecordAndAttachments(CSWorkOrderCommRecordSaveRequest request) {
        CSWorkOrderDO workOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkorderId());
        CSExceptionAssert.isTrue(workOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "work order on end state, can't edit");
        CSExceptionAssert.isTrue(!finalCSWorkOrderStatus.contains(workOrderDO.getStatus())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "work order on end state, can't edit");

        //校验一下:结案工单时对客处理意见必选情况
        CsWoCustomerShowSwitchEnum oldCustomerShowSwitch =  CsWoCustomerShowSwitchEnum.valOf(workOrderDO.getCustomerShowSwitch());
        if(StringUtils.isNotBlank(request.getButtonSource()) && CsWoButtonSource.EDIT_COMPLETE.name().equalsIgnoreCase(request.getButtonSource())
                && CsWoCustomerShowSwitchEnum.OPEN.equals(oldCustomerShowSwitch) && StringUtils.isBlank(request.getCustomerShowSuggestion())){
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR
                    , "结案必须选择对客处理意见!!!");
        }

        CSWorkorderConfigVO configVO = cSWorkOrderConfigService.getConfigVOByConfigIdIgnoreStatus(workOrderDO.getWorkOrderConfigId());

        List<DataPersistenceCommander> commanders = new ArrayList<>();

        ComRecordAddRequest addRequest = ComRecordAddRequest.builder()
                .attachments(request.getAttachments())
                .workOrderId(request.getWorkorderId())
                .callCenterIds(request.getCallCenterIds())
                .content(request.getCommRecord())
                .operatorUserId(request.getCurrentDealUserId())
                .commMode(request.getCommMode())
                .recordTemplateId(request.getRecordTemplateId())
                .sessionIds(request.getSessionIds())
                .workOrderTrackWay(request.getWorkOrderTrackWay())
                .csWorkOrderEmailRequest(request.getCsWorkOrderEmailRequest())
                .optBySale(request.isOptBySale())
                .feedbackTime(request.getFeedbackTime())
                .customerShowSuggestion(request.getCustomerShowSuggestion())
                .customerShowNotify(request.getCustomerShowNotify())
                .buttonSource(request.getButtonSource())
                .showToSale(request.getShowToSale() == null ? Boolean.FALSE : request.getShowToSale())
                .attachmentsToCustomer(request.getAttachmentsToCustomer())
                .build();
        String recordId = commRecordService.insertCommRecord(commanders, addRequest);
        FormAnswerRequest formAnswerRequest = request.getFormAnswer();
        if (formAnswerRequest != null) {
            formAnswerRequest.setBizId(recordId);
            FacadeResponse formAnswerResponse = formServAnswerClient.commitAnswer(formAnswerRequest);
            CSExceptionAssert.isTrue(formAnswerResponse.isSucceeded()
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "commit answer failed!");
        }

        if (request.getFeedbackTime() != null && StringUtils.isNotBlank(workOrderDO.getPrincipalUserId())) {
            FeedbackMessageInstructionReq messageInstructionReq = FeedbackMessageInstructionReq.builder()
                    .workerOrderId(workOrderDO.getWorkorderId())
                    .feedbackTime(request.getFeedbackTime())
                    .principalUserId(workOrderDO.getPrincipalUserId())
                    .build();
            feedbackMessageInstructionService.addWorkOrderInstructions(commanders, messageInstructionReq);
        }

        OpLogAddRequest opLogAddRequest = OpLogAddRequest.builder()
                .status(workOrderDO.getStatus())
                .suggestion("添加沟通记录/附件")
                .workOrderId(request.getWorkorderId())
                .operatorUserId(request.getCurrentDealUserId())
                .commRecordId(recordId)
                .build();
        if (Objects.equals(request.getWorkOrderTrackWay(), WorkOrderTrackWay.EMAIL.name())) {
            if (request.getCsWorkOrderEmailRequest() != null && StringUtils.isNotBlank(request.getCsWorkOrderEmailRequest().getSubject())) {
                opLogAddRequest.setSuggestion(request.getCsWorkOrderEmailRequest().getSubject());
            } else {
                opLogAddRequest.setSuggestion("发送service邮件");
            }
        }
        csWorkOrderLogService.insertOpLog(commanders, opLogAddRequest);

        workOrderDO.setConsultingType(request.getConsultingType());
//        workOrderDO.setShowToCustomer(request.getShowToCustomer());
        workOrderDO.setCurrentDealUserId(request.getCurrentDealUserId());
        workOrderDO.setFeedbackTime(request.getFeedbackTime());
        workOrderDO.setLastFollowedTime(new Date());
        if (CollectionUtils.isEmpty(workOrderDO.getHistoryDealUsers()) || !workOrderDO.getHistoryDealUsers().contains(request.getCurrentDealUserId())) {
            if (CollectionUtils.isEmpty(workOrderDO.getHistoryDealUsers())) {
                workOrderDO.setHistoryDealUsers(Sets.newHashSet());
            }
            workOrderDO.getHistoryDealUsers().add(request.getCurrentDealUserId());
        }

        //销售留言自动绑定销售
        if (request.isOptBySale()) {
            bindSaleBySaleOpt(workOrderDO, request.getCurrentDealUserId());
        }

        commanders.add(() -> CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(workOrderDO) != null
                , CSOrderBizErrorCode.SYSTEM_ERROR, "update workOrderDO failed."));

        transactionLazyExecutor.executeCommanders(commanders);
        if (Objects.equals(request.getWorkOrderTrackWay(), WorkOrderTrackWay.EMAIL.name()) && Objects.nonNull(request.getCsWorkOrderEmailRequest())) {
            sendWorkOrderEmail(request.getCsWorkOrderEmailRequest(), request.getWorkorderId(), workOrderDO.getWorkOrderConfigId());
        }
        //TODO 迁移变更:此处废弃了【优质客户计划】相关的跟进记录同步到客户池的逻辑

        CompletableFuture.runAsync(() -> judgeAndSendAgent(recordId, request.getCurrentDealUserId(), workOrderDO.getPrincipalUserId(), workOrderDO.getWorkorderId()), commonExecutor);
        if (request.isOptBySale()) {
            CompletableFuture.runAsync(() -> judgeSaleSessionWorkOrderAndSendAgent(recordId, request.getCurrentDealUserId(), workOrderDO.getPrincipalUserId(),
                    workOrderDO.getWorkorderId(), request.getCommRecord()), commonExecutor);
        }

        slaEscalationService.escalationFollowUpWorkOrderSla(workOrderDO, request.getFeedbackTime());
        consultProgressPushService.followUpWorkOrder(workOrderDO, addRequest);
        // 跟进工单时-发送会话绑定消息
        if(!CollectionUtils.isEmpty(request.getSessionIds())) {
            CSAssignBindEvent assignBindEvent = new CSAssignBindEvent();
            assignBindEvent.setAssignIds(addRequest.getSessionIds());
            assignBindEvent.setWorkOrderId(addRequest.getWorkOrderId());
            assignBindEvent.setFirmId(workOrderDO.getFirmId());
            assignBindProducer.sendAssignBind(assignBindEvent);
        }
        // 推送mq-判断是否需要结束托管
        pushHostingComplete(request);
    }

    private void bindSaleBySaleOpt(CSWorkOrderDO workOrderDO, String currentDealUserId){
        if(StringUtils.isNotBlank(workOrderDO.getSaleBossUserId())){
            return;
        }
        User user = bossUserClient.queryByBossUserId(currentDealUserId);
        if(user == null){
            log.warn("CSWorkOrder bindSaleBySaleOpt 销售不存在 currentDealUserId:{}", currentDealUserId);
            return;
        }
        workOrderDO.setSaleBossUserId(user.getBossUserId());
        workOrderDO.setSaleUserName(user.getUserName());
        log.warn("CSWorkOrder bindSaleBySaleOpt 绑定销售 工单ID:{},currentDealUserId:{}", workOrderDO.getWorkorderId(), currentDealUserId);
    }

    @Override
    public void cancelRelate(CSWorkOrderCallRecordCancelRequest request) {
        CSWorkOrderCommRecordDO commRecordDO = csWorkOrderCommRecordDAO.getCommRecordsByRecordId(request.getReferenceId());
        CSWorkOrderDO workOrderDO = findWorkOrderOne(commRecordDO.getWorkorderId());
        if (workOrderDO == null || workOrderDO.getStatus() == CSWorkOrderStatus.Completed || CSWorkOrderStatus.Cancelled == workOrderDO.getStatus()) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR
                    , "WorkOrder does not exist or the status is cancelled! referenceId: " + request.getReferenceId());
        }

        CancelCallRecordRelateRequest cancelRelationRequest = new CancelCallRecordRelateRequest();
        cancelRelationRequest.setCallRecordRelationId(request.getCallRecordRelationId());
        cancelRelationRequest.setOperatorUserId(request.getCurrentDealUserId());
        FacadeResponse<Void> success = callCenterClient.cancelRelate(cancelRelationRequest);
        if (!success.isSucceeded()) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR
                    , "cancel relate failed.");
        }
    }

    @Override
    public PageSerializable<RecordTemplateVO> queryRecordTemplatePageList(RecordTemplateQueryRequest request) {
        Page<RecordTemplateDO> recordTemplateDOs = csWorkOrderRecordTemplateDAO.findPageByNameAndStatus(request.getRecordTemplateName(), request.getStatus()
                , request.getPage()
                , request.getSize());
        List<RecordTemplateVO> recordTemplateVOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(recordTemplateDOs)) {
            recordTemplateVOS = recordTemplateDOs
                    .stream().map(recordTemplateDO -> {
                        RecordTemplateVO recordTemplateVO = WorkOrderConverter.toRecordTemplateVO(recordTemplateDO);
                        if (StringUtils.isNotEmpty(recordTemplateDO.getFormId())) {
                            FacadeResponse<FormVO> facadeResponse = formServClient.queryDetails(recordTemplateDO.getFormId());
                            if (facadeResponse.isSucceeded() && null != facadeResponse.getValue()) {
                                recordTemplateVO.setFormVO(facadeResponse.getValue());
                            }
                        }
                        if (!CollectionUtils.isEmpty(recordTemplateDO.getRelatedGroupId())) {
                            List<String> groupNames = new ArrayList<>();
                            List<String> groupIds = new ArrayList<>();
                            for (String groupId : recordTemplateDO.getRelatedGroupId()) {
                                BossUserGroupQueryRequest bossUserGroupQueryRequest = new BossUserGroupQueryRequest();
                                bossUserGroupQueryRequest.setPage(1);
                                bossUserGroupQueryRequest.setSize(1);
                                bossUserGroupQueryRequest.setGroupId(groupId);
                                FacadeResponse<PageSerializable<BossUserGroupVO>> facadeResponse = bossUserClient.listGroups(bossUserGroupQueryRequest);
                                if (facadeResponse.isSucceeded() && !CollectionUtils.isEmpty(facadeResponse.getValue().getList())) {
                                    groupNames.add(facadeResponse.getValue().getList().get(0).getGroupName());
                                    groupIds.add(facadeResponse.getValue().getList().get(0).getGroupId());
                                }
                            }
                            recordTemplateVO.setRelatedGroupName(groupNames);
                            recordTemplateVO.setRelatedGroupId(groupIds);
                        }
                        return recordTemplateVO;
                    }).collect(Collectors.toList());
        }
        PageSerializable<RecordTemplateVO> pageSerializable = PageSerializable.of(recordTemplateVOS);
        pageSerializable.setTotal(recordTemplateDOs.getTotal());
        return pageSerializable;
    }

    @Override
    public String createRecordTemplate(RecordTemplateCreateRequest request) {
        List<RecordTemplateDO> recordTemplateDOLocal = csWorkOrderRecordTemplateDAO.findByTemplateName(request.getRecordTemplateName());
        CSExceptionAssert.isTrue(recordTemplateDOLocal.size() == 0, CSOrderBizErrorCode.RECORD_TEMPLATE_EXIST, "跟进记录模版名称重复,请调整!");
        RecordTemplateDO recordTemplateDO = WorkOrderConverter.createRequest2RecordTemplateDO(request);
        recordTemplateDO.setTemplateId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_RECORD_TEMPLATE));
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderRecordTemplateDAO.insert(recordTemplateDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert recordTemplateDO failed.");
        });
        insertRecordTemplateLog(commanders, recordTemplateDO, request);
        transactionLazyExecutor.executeCommanders(commanders);
        return recordTemplateDO.getTemplateId();
    }

    @Override
    public void editRecordTemplate(RecordTemplateEditRequest request) {
        RecordTemplateDO recordTemplateDO = csWorkOrderRecordTemplateDAO.findByTemplateId(request.getRecordTemplateId());
        CSExceptionAssert.isTrue(recordTemplateDO != null
                , CSOrderBizErrorCode.SYSTEM_ERROR, " recordTemplate not fund!");
        if (!request.getRecordTemplateName().equals(recordTemplateDO.getTemplateName())) {
            List<RecordTemplateDO> recordTemplateDOs = csWorkOrderRecordTemplateDAO.findByTemplateName(request.getRecordTemplateName());
            CSExceptionAssert.isTrue(recordTemplateDOs.size() == 0
                            || (recordTemplateDOs.size() == 1 && request.getRecordTemplateName().equals(recordTemplateDOs.get(0).getTemplateName()))
                    , CSOrderBizErrorCode.RECORD_TEMPLATE_EXIST, "edit recordTemplate failed,templateName is exited");

        }
        recordTemplateDO.setTemplateName(request.getRecordTemplateName());
        recordTemplateDO.setFormId(request.getFormId());
        recordTemplateDO.setRelatedGroupId(request.getRelatedGroupId());
        recordTemplateDO.setStatus(request.getStatus());
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderRecordTemplateDAO.updateByIdSelective(recordTemplateDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "update recordTemplateDO failed.");
        });
        updateRecordTemplateLog(commanders, recordTemplateDO, request);
        transactionLazyExecutor.executeCommanders(commanders);
    }

    @Override
    public List<RecordTemplateVO> queryRecordTemplateByName(String recordTemplateName) {
        List<RecordTemplateDO> recordTemplateDOs = csWorkOrderRecordTemplateDAO.findByTemplateLikeName(recordTemplateName);
        List<RecordTemplateVO> recordTemplateVOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(recordTemplateDOs)) {
            recordTemplateVOS = recordTemplateDOs
                    .stream().map(recordTemplateDO -> {
                        RecordTemplateVO recordTemplateVO = WorkOrderConverter.toRecordTemplateVO(recordTemplateDO);
                        if (StringUtils.isNotEmpty(recordTemplateDO.getFormId())) {
                            FacadeResponse<FormVO> facadeResponse = formServClient.queryDetails(recordTemplateDO.getFormId());
                            if (facadeResponse.isSucceeded() && null != facadeResponse.getValue()) {
                                recordTemplateVO.setFormVO(facadeResponse.getValue());
                            }
                        }
                        return recordTemplateVO;
                    }).collect(Collectors.toList());
        }
        return recordTemplateVOS;
    }

    @Override
    public RecordDetailVO queryRecordDetail(String recordId) {
        CSWorkOrderCommRecordDO csWorkOrderCommRecordDO = csWorkOrderCommRecordDAO.getCommRecordsByRecordId(recordId);
        CSExceptionAssert.isTrue(csWorkOrderCommRecordDO != null
                , CSOrderBizErrorCode.SYSTEM_ERROR
                , "record not found!");
        RecordDetailVO recordDetailVO = WorkOrderConverter.toRecordDetailVO(csWorkOrderCommRecordDO);
        User user = bossUserClient.queryByBossUserId(csWorkOrderCommRecordDO.getOperator());
        if (user != null) {
            recordDetailVO.setOperator(user.getUserName());
        }
        try {
            List<AttachmentVO> attachmentVOList = attachmentFacade.getFileByReferenceId(csWorkOrderCommRecordDO.getRecordId());
            recordDetailVO.setAttachmentVOS(attachmentVOList);
        } catch (Exception e) {
            log.error("upload attachment failed.e:", e);
        }
        if (StringUtils.isNotEmpty(recordDetailVO.getRecordTemplateId())) {
            RecordTemplateDO recordTemplateDO = csWorkOrderRecordTemplateDAO.findByTemplateId(recordDetailVO.getRecordTemplateId());
            CSExceptionAssert.isTrue(recordTemplateDO != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "recordTemplate not found!");
            FormAnswerQueryReq formAnswerQueryReq = FormAnswerQueryReq
                    .builder()
                    .formId(recordTemplateDO.getFormId())
                    .formVersion(recordTemplateDO.getFormVersion())
                    .bizId(recordId)
                    .build();
            FacadeResponse<FormAnswerVO> facadeResponse = formServAnswerClient.getFormAnswerByBizId(formAnswerQueryReq);
            if (facadeResponse.isSucceeded() && facadeResponse.getValue() != null) {
                recordDetailVO.setFormAnswerVO(facadeResponse.getValue());
            }
        }
        return recordDetailVO;
    }

    @Override
    public List<RecordTemplateVO> queryRecordTemplateList(RecordTemplateInGroupQuery request) {
        BossUserGroupListQueryRequest bossUserGroupQueryRequest = new BossUserGroupListQueryRequest();
        bossUserGroupQueryRequest.setUserId(request.getUserId());
        bossUserGroupQueryRequest.setStatus(CfgStatus.Enabled);
        FacadeResponse<List<BossUserGroupVO>> facadeResponse = bossUserClient.listGroupsByUserId(bossUserGroupQueryRequest);
        List<RecordTemplateVO> recordTemplateVOS = new ArrayList<>();
        if (facadeResponse.isSucceeded() && facadeResponse.getValue() != null) {
            List<String> groupIds = facadeResponse.getValue().stream().map(BossUserGroupVO::getGroupId).collect(Collectors.toList());
            List<RecordTemplateDO> recordTemplateDOs = csWorkOrderRecordTemplateDAO.findByRelatedGroupIds(groupIds);
            if (!CollectionUtils.isEmpty(recordTemplateDOs)) {
                recordTemplateVOS = recordTemplateDOs
                        .stream().map(recordTemplateDO -> {
                            RecordTemplateVO recordTemplateVO = WorkOrderConverter.toRecordTemplateVO(recordTemplateDO);
                            if (StringUtils.isNotEmpty(recordTemplateDO.getFormId())) {
                                FacadeResponse<FormVO> formFacadeResponse = formServClient.queryDetails(recordTemplateDO.getFormId());
                                if (formFacadeResponse.isSucceeded() && null != formFacadeResponse.getValue()) {
                                    recordTemplateVO.setFormVO(formFacadeResponse.getValue());
                                }
                            }
                            return recordTemplateVO;
                        }).collect(Collectors.toList());
            }
        }
        return recordTemplateVOS;
    }

    @Override
    public PageSerializable<RecordTemplateOpLogVO> queryRecordTemplateOpLogList(RecordTemplateOpLogQueryRequest request) {
        Page<RecordTemplateOpLogDO> recordTemplateOpLogDOS = csWorkOrderRecdTmplOpLogDAO.findPageByTemplateId(request.getRecordTemplateId(), request.getPage(), request.getSize());
        List<RecordTemplateOpLogVO> recordTemplateOpLogVOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(recordTemplateOpLogDOS)) {
            recordTemplateOpLogVOS = recordTemplateOpLogDOS.stream().map(recordTemplateOpLogDO -> RecordTemplateOpLogVO.builder()
                    .opLogId(recordTemplateOpLogDO.getOpLogId())
                    .templateId(recordTemplateOpLogDO.getTemplateId())
                    .opUserId(recordTemplateOpLogDO.getOpUserId())
                    .opType(recordTemplateOpLogDO.getOpType())
                    .opSuggestion(recordTemplateOpLogDO.getOpSuggestion())
                    .opTime(recordTemplateOpLogDO.getOpTime())
                    .build()).collect(Collectors.toList());
        }
        PageSerializable<RecordTemplateOpLogVO> pageSerializable = PageSerializable.of(recordTemplateOpLogVOS);
        pageSerializable.setTotal(recordTemplateOpLogDOS.getTotal());
        return pageSerializable;
    }

    @Override
    public void rejectPreAudit(CSWorkOrderRejectRequest req) {
        CSWorkOrderDO workOrderDO = findWorkOrderOne(req.getWorkorderId());
        if (workOrderDO == null) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.RecordNotFoundError, CSOrderBizErrorCode.NOT_FOUND, "该工单不存在!");
        }
        if (!CSWorkOrderStatus.Created.equals(workOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.IllegalState, CSOrderBizErrorCode.NOT_SUPPORT, "工单当前状态不能驳回!");
        }

        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.queryValidByConfigId(workOrderDO.getWorkOrderConfigId());
        if (csWorkOrderConfigDO == null) {
            log.error("query workOrder config failed,workOrder config id is :{}", workOrderDO.getWorkOrderConfigId());
            throw new CustomerServiceException(CSOrderSystemErrorCode.IllegalState, CSOrderBizErrorCode.NOT_SUPPORT, "工单类型查询失败!");
        }
        CSExceptionAssert.isTrue(!StringUtils.isEmpty(csWorkOrderConfigDO.getConfigType())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "configType should not be empty, configType is " + WorkOrderConfigType.CertificationPreAudit);
        if (!WorkOrderConfigType.CertificationPreAudit.name().equals(csWorkOrderConfigDO.getConfigType())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.NOT_SUPPORT, "只能驳回预审工单!");
        }
        workOrderDO.setStatus(CSWorkOrderStatus.Completed);
        workOrderDO.setCompletedTime(new Date());
        workOrderDO.setLastUpdatedBy(req.getPrincipalUserId());
        workOrderDO.setLastUpdateDate(new Date());
        workOrderDO.setCurrentDealUserId(req.getPrincipalUserId());
        boolean updateRes = updateWorkOrder(workOrderDO, createdOpLogDO(workOrderDO.getWorkorderId(), workOrderDO.getStatus(), req.getPrincipalUserId(), req.getSuggestion()));
        if (!updateRes) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.IllegalState, CSOrderBizErrorCode.SYSTEM_ERROR, "更新失败!");
        }
        CompletableFuture.runAsync(() -> doInstruction(workOrderDO.getWorkorderId()), commonExecutor);
    }

    @Override
    public String complete(CSWorkOrderCompleteRequest request) {
        CSExceptionAssert.isTrue(finalCSWorkOrderStatus.contains(request.getStatus())
                , CSOrderBizErrorCode.VALIDATION_ERROR, "status should be final status.");

        CSWorkOrderDO workOrderDO = findWorkOrderOne(request.getWorkorderId());

        CSExceptionAssert.isTrue(workOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "workOrder is not exist, WorkOrderId is: " + request.getWorkorderId());

        if (finalCSWorkOrderStatus.contains(workOrderDO.getStatus())) {
            return workOrderDO.getWorkorderId();
        }
        if (StringUtils.isNotBlank(workOrderDO.getReferenceId())
                &&
                needCheckWorkConfigIds.contains(workOrderDO.getWorkOrderConfigId())
                && preAuditNeedCheck(workOrderDO.getWorkorderId())
        ) {
            FacadeResponse<AuditSnapshotCheckResult> res =
                    manualAuditSnapshotClient.auditSnapshotFieldCheck(workOrderDO.getReferenceId());
            CSExceptionAssert.isTrue(res.isSucceeded()
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "call manualAuditSnapshotClient.auditSnapshotFieldCheck failed");
            if (res.getValue() != null && Objects.equals(res.getValue().isSuccess(), false)) {
                log.warn("auditSnapshotFieldCheck failed, workOrderId is: {}, referenceId is: {}, message is: {}"
                        , workOrderDO.getWorkorderId(), workOrderDO.getReferenceId(), res.getValue().getAbsentField());
                String errorMsg = "";
                if (!CollectionUtils.isEmpty(res.getValue().getAbsentField())) {
                    String tips = res.getValue().getAbsentField().stream().
                            map(ManualAuditSnapshotRequest.ManualAuditSnapshotUpdateEnum::getDescByType)
                            .collect(Collectors.joining(","));
                    errorMsg = "请先补充资料, 缺失的有: " + tips;
                }
                if (StringUtils.isNotEmpty(res.getValue().getErrCode())) {
                    errorMsg += ErrorInfoMap.getErrorInfo(res.getValue().getErrCode());
                }
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.NOT_SUPPORT, errorMsg);
            }
        }

        //云雀发起的被动协同工单,存在created状态下还未分配就被动关闭的情况
        if (Boolean.FALSE.equals(request.isSkylarkCallBack())) {
            CSExceptionAssert.isTrue(CSWorkOrderStatus.Created != workOrderDO.getStatus()
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "worker order not be applied. status is: " + workOrderDO.getStatus());
        }

        String skylarkCollaborationId = workOrderDO.getSkylarkCollaborationId();
        //是否有未完成的云雀协同工单
        //云雀回调不检查协同单状态,默认是关闭的。云雀目前把协同单的状态修改和SPI调用放在了一个事务中,下面的查询拿不到status终态
        if (!Objects.isNull(skylarkCollaborationId) && Boolean.FALSE.equals(request.isSkylarkCallBack())) {
            CollaborationVO collaborationVO = skylarkManager.getCollaborationDetail(skylarkCollaborationId);
            CSExceptionAssert.isTrue(Objects.nonNull(collaborationVO) && CollaborationStatus.COMPLETED.name().equals(collaborationVO.getStatus())
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "worker order has uncompleted skylark collaboration. status is: " + collaborationVO.getStatus());
        }

        // 如果是人工结案二线工单
        if (!SystemConst.SYSTEM_USER_ID.equals(request.getOperatorUserId()) && workOrderDO.getOrderLevel() == OrderLevel.OL2
                && !WORK_ORDER_UPGRADE_LABEL_UL1.equals(workOrderDO.getUpgradeLabel())) {
            BossUserInGroupReq bossUserInGroupReq = BossUserInGroupReq.builder()
                    .bossUserId(request.getOperatorUserId())
                    .groupIds(Sets.newHashSet(secondGroup))
                    .build();
            FacadeResponse<Boolean> facadeResponse = bossUserGroupClient.bossUserInSpecifiedGroup(bossUserInGroupReq);
            CSExceptionAssert.isTrue(facadeResponse.isSucceeded(), CSOrderBizErrorCode.SYSTEM_ERROR, "call bossUserGroupClient.bossUserInSpecifiedGroup failed");
            CSExceptionAssert.isTrue(Boolean.TRUE.equals(facadeResponse.getValue())
                    , CSOrderBizErrorCode.VALIDATION_ERROR, "非二线人员,不能结案二线工单");
        }

        String suggestion = "结案工单";
        if (org.springframework.util.StringUtils.hasText(request.getSuggestion())) {
            suggestion = request.getSuggestion();
        }
        return workOrderClose(request.getWorkorderId(), request.getOperatorUserId(), request.getStatus(), suggestion);
    }

    @Override
    public Boolean reopen(CSWorkOrderReopenRequest request) {
        String workOrderId = request.getWorkOrderId();
        String operatorUserId = request.getOperatorUserId();
        CSExceptionAssert.isTrue(!Objects.isNull(workOrderId) || !Objects.isNull(operatorUserId)
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "客服工单ID或操作人为空");
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
        CSExceptionAssert.isTrue(!Objects.isNull(csWorkOrderDO),
                CSOrderBizErrorCode.VALIDATION_ERROR,
                "客服工单不存在");
        //云雀上线前的历史工单不支持重开
        CSExceptionAssert.isTrue(Boolean.TRUE.equals(csWorkOrderDO.getNewCollaboration()),
                CSOrderBizErrorCode.VALIDATION_ERROR,
                "历史工单不支持重开");
        //预审工单不支持重开
        CSExceptionAssert.isTrue(!isPreAuditWorkOrder(csWorkOrderDO),
                CSOrderBizErrorCode.VALIDATION_ERROR,
                "预审工单不支持重开");
        //账号功能工单不支持重开
        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(csWorkOrderDO.getWorkOrderConfigId());
        CSExceptionAssert.isTrue(!BusinessType.ACCOUNT_FUNCTION.equals(csWorkOrderConfigDO.getBusinessType()),
                CSOrderBizErrorCode.VALIDATION_ERROR,
                "账号功能工单不支持重开");
        CSExceptionAssert.isTrue(CSWorkOrderStatus.Completed == csWorkOrderDO.getStatus(),
                CSOrderBizErrorCode.VALIDATION_ERROR,
                "工单状态非结案");

        boolean needNotify = true;
        //重开后工单状态区分工单级别,普通工单是处理中;升级工单是升级处理中
        //不管工单是普通还是升级工单,路由给关闭前的工单处理人
        CSWorkOrderStatus reopenStatus;
        if (OrderLevel.OL2.equals(csWorkOrderDO.getOrderLevel())) {
            reopenStatus = CSWorkOrderStatus.UpgradeProcessing;
            csWorkOrderDO.setStatus(reopenStatus);
        } else if ((OrderLevel.OL1.equals(csWorkOrderDO.getOrderLevel()) || Objects.isNull(csWorkOrderDO.getOrderLevel())) && !SystemConst.SYSTEM_USER_ID.equals(operatorUserId)) {
            //一线客服工单被手动重开分配给当前操作人
            reopenStatus = CSWorkOrderStatus.Processing;
            csWorkOrderDO.setStatus(reopenStatus);
            csWorkOrderDO.setPrincipalUserId(operatorUserId);
            csWorkOrderDO.setCurrentDealUserId(operatorUserId);
            needNotify = false;
        } else {
            reopenStatus = CSWorkOrderStatus.Processing;
            csWorkOrderDO.setStatus(reopenStatus);
        }
        csWorkOrderDO.setCompletedTime(null);

        //有重开权限的员工可以操作重开,不限普通工单、升级工单,落下工单重开的实际操作人
        CSWorkOrderOpLogDO opLogDO = createdOpLogDO(workOrderId
                , reopenStatus
                , operatorUserId
                , "工单重开");

        boolean isOk = transactionUtil.executeMongo(() -> {
            CSExceptionAssert.isTrue(csWorkOrderOpLogDAO.insert(opLogDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert opLogDO failed.");

            CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "reopen workOrderDO failed.");
        });

        CSExceptionAssert.isTrue(isOk
                , CSOrderBizErrorCode.SYSTEM_ERROR
                , "reopen work order failed");
        //发送通知
        if (needNotify && !Strings.isNullOrEmpty(csWorkOrderDO.getPrincipalUserId())) {
            //String content = String.format("工单编号:%s已被重开,请尽快处理!", workOrderId);
            //messageManager.sendMessage(workOrderId, csWorkOrderDO.getPrincipalUserId(), content, bossAndDingding, MessageDomain.boss);
            messageManager.sendReopenMessage(workOrderId, csWorkOrderDO.getPrincipalUserId(), WorkOrderOptType.REOPEN);
        }
        slaEscalationService.escalationReopenWorkOrderSla(csWorkOrderDO);
        return true;
    }

    @Override
    public PageSerializable<CSWorkOrderVO> pageList(CSWorkOrderPageListRequest request) {
        Set<String> stringSet = null;
        Set<String> excludeConfigIds = null;

        if (!Strings.isNullOrEmpty(request.getProblemType())
                || !Strings.isNullOrEmpty(request.getWorkorderType())
                || request.getBusinessType() != null
                || request.getMainType() != null) {
            stringSet = csWorkOrderConfigDAO.getConfigIds(request.getBusinessType()
                    , request.getWorkorderType()
                    , request.getProblemType()
                    , request.getCreateType()
                    , request.getMainType());
            if (CollectionUtils.isEmpty(stringSet)) {
                return null;
            }
        }
        if (request.getExcludePreWorkOrder() != null && request.getExcludePreWorkOrder()) {
            excludeConfigIds = preWorkOrderConfigIds;
        }

        if (request.getFilterSalesWorkOrder() != null && request.getFilterSalesWorkOrder()) {
            Set<String> filterMarketingConfigIds = csWorkOrderConfigDAO.getConfigIdsByBusinessType(BusinessType.MARKETING);
            if (CollectionUtils.isEmpty(stringSet)) {
                stringSet = filterMarketingConfigIds;
            }
        }

        Set<String> ids = new HashSet<>();
        if (null != request.getCooperationDomain()) {
            buildCoopCondition(request, ids);
        }
        if (StringUtils.isNotBlank(request.getSenderEmail())) {
            List<String> csIds = csWorkOrderReceiveEmailRecordDAO.queryCsWorkOrderIdsBySender(request.getSenderEmail());
            if (!CollectionUtils.isEmpty(csIds)) {
                ids.addAll(csIds);
            }
        }

        if (buildEmailCondition(request, ids)) return null;

        if (StringUtils.isNotBlank(request.getWorkerOrderId()) && CollectionUtils.isEmpty(ids)) {
            ids.add(request.getWorkerOrderId());
        } else if (StringUtils.isNotBlank(request.getWorkerOrderId()) && !ids.contains(request.getWorkerOrderId())) {
            return null;
        }
        Page<CSWorkOrderDO> CSWorkOrderDOS = new Page<>();  // NOSONAR
        if (customerOrderSwitch) {
            searchByWideTable(request, stringSet, ids, CSWorkOrderDOS, excludeConfigIds);
        } else {
            CSWorkOrderDOS = this.csWorkOrderDAO.findPage(MongoQueryFactory.create(request, stringSet, ids), request.getPage(), request.getSize());
        }
        List<CSWorkOrderVO> workOrderVOS = new LinkedList<>();
        List<CSWorkOrderDO> collect = CSWorkOrderDOS;
        if (!CollectionUtils.isEmpty(CSWorkOrderDOS)) {
            //走优化后逻辑
            Set<String> bossUserIds = collect.stream().map(CSWorkOrderDO::getUserId).collect(Collectors.toSet());
            //查询全量user
//            FacadeResponse<List<User>> allValidUsers = bossUserClient.listValidUser();
//            FacadeResponse<List<User>> allValidUsers = bossUserClient.queryByBossUserIds(bossUserIds);
            List<User> users = bossUserClient.queryByBossUserIds(bossUserIds);
            //查ActionDO
            Query query = new Query();
            List<String> workOrderIds = collect.stream().map(CSWorkOrderDO::getWorkorderId).collect(Collectors.toList());
            Criteria criteria = Criteria.where(ActionDO.SCENE_ID).in(workOrderIds).and("tagKey").is("preAudit");
            query.addCriteria(criteria);
            List<ActionDO> actionDOs = actionDAO.find(query);
            //TODO 迁移变更:查CooperationWorkOrderDO 废弃
//            List<CooperationWorkOrderDO> cooperationWorkOrderDOs = cooperationWorkOrderDAO.queryByCooperationOrderIds(workOrderIds);

            for (CSWorkOrderDO workOrderDO : collect) {
//                FacadeResponse<CSWorkOrderVO> facadeResponse1 = toWorkOrderVOListOpt(workOrderDO, allValidUsers.getValue(), actionDOs);
                FacadeResponse<CSWorkOrderVO> facadeResponse1 = toWorkOrderVOListOpt(workOrderDO, users, actionDOs);
                if (!facadeResponse1.isSucceeded()) {
                    CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, facadeResponse1.getResponseMsg());
                }
                workOrderVOS.add(facadeResponse1.getValue());
            }
            //释放内存
            if (!CollectionUtils.isEmpty(users)) {
                users.clear();
            }
            if (!CollectionUtils.isEmpty(actionDOs)) {
                actionDOs.clear();
            }
//            if (!CollectionUtils.isEmpty(cooperationWorkOrderDOs)) {
//                cooperationWorkOrderDOs.clear();
//            }
        }

        PageSerializable<CSWorkOrderVO> pageSerializable = new PageSerializable<>();
        pageSerializable.setTotal(CSWorkOrderDOS.getTotal());
        pageSerializable.setList(workOrderVOS);
        return pageSerializable;
    }

    private void searchByWideTable(CSWorkOrderPageListRequest request, Set<String> stringSet, Set<String> ids, Page<CSWorkOrderDO> CSWorkOrderDOS, Set<String> excludeConfigIds) {
        List<String> bossUserIdsFromOP = null;
        if (SLAPageSource.OP.name().equalsIgnoreCase(request.getSlaPageSource())) {
            bossUserIdsFromOP = slaOrganizerService.getBossUserIds(request.getOrganizer());
        }
        FacadeResponse<PageableResponse<String>> searchRes = csWorkOrderSearchClient
                .searchCSWorkOrderPage(MongoQueryToDorisQueryConvert.convert(request, stringSet, ids, bossUserIdsFromOP, excludeConfigIds));
        if (!searchRes.isSucceeded()) {
            log.error("query from doris failed {}", searchRes.getResponseMsg());
            return;
        }
        if (searchRes.getValue() == null) {
            return;
        }
        PageableResponse<String> value = searchRes.getValue();
        if (CollectionUtils.isEmpty(value.getData())) {
            return;
        }
        List<CSWorkOrderDO> result;
        if (SLAPageSource.SELF.name().equalsIgnoreCase(request.getSlaPageSource()) || SLAPageSource.OP.name().equalsIgnoreCase(request.getSlaPageSource())) {
            result = csWorkOrderDAO.queryOrdersByWorkOrderIdsSortSlaTodoEndTime(Sets.newHashSet(value.getData()));
        } else {
            result = csWorkOrderDAO.queryOrdersByWorkOrderIdsSortCreateTimeDesc(Sets.newHashSet(value.getData()));
        }
        CSWorkOrderDOS.setTotal(value.getTotal());
        CSWorkOrderDOS.addAll(result);
    }

    private boolean buildEmailCondition(CSWorkOrderPageListRequest request, Set<String> ids) {
        if (StringUtils.isNotBlank(request.getReceiveEmail())) {
            Set<String> tempIds = new HashSet<>();
            List<CSWorkOrderCommRecordDO> commRecordsByEmailTO = csWorkOrderCommRecordDAO.getCommRecordsByEmailTO(request.getReceiveEmail());
            if (!CollectionUtils.isEmpty(commRecordsByEmailTO)) {
                List<String> collect = commRecordsByEmailTO.stream().map(CSWorkOrderCommRecordDO::getWorkorderId).collect(Collectors.toList());
                tempIds.addAll(collect);
            }
            List<String> toIds = csWorkOrderReceiveEmailRecordDAO.queryCsWorkOrderIdsByTo(request.getReceiveEmail());
            if (!CollectionUtils.isEmpty(toIds)) {
                tempIds.addAll(toIds);
            }
            if (CollectionUtils.isEmpty(tempIds)) {
                return true;
            }
            ids.addAll(tempIds);
        }
        return false;
    }

    private void buildCoopCondition(CSWorkOrderPageListRequest request, Set<String> ids) {
        if (Objects.isNull(request.getCooperateWorkOrderCreateTimeBegin()) || Objects.isNull(request.getCooperateWorkOrderCreateTimeEnd())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR, "请输入协同工单创建时间范围");
        }
//        List<CooperationWorkOrderDO> cooperationWorkOrderDOS = cooperationWorkOrderDAO.queryByCooperationDomainWithTimeRange(request.getCooperationDomain(),
//                request.getCooperateWorkOrderCreateTimeBegin(), request.getCooperateWorkOrderCreateTimeEnd());
//        if (!CollectionUtils.isEmpty(cooperationWorkOrderDOS)) {
//            ids.addAll(cooperationWorkOrderDOS.stream().map(CooperationWorkOrderDO::getSourceOrderId).collect(Collectors.toSet()));
//        }

//        List<CoopWorkOrderDO> coopWorkOrderDOS = coopWorkOrderDAO.queryBySoAndCoDepartIdWithTimeRange(DepartmentConstant.CRM,
//                request.getCooperationDomain().name(), request.getCooperateWorkOrderCreateTimeBegin(), request.getCooperateWorkOrderCreateTimeEnd());
//        if (!CollectionUtils.isEmpty(coopWorkOrderDOS)) {
//            ids.addAll(coopWorkOrderDOS.stream().map(CoopWorkOrderDO::getSoWorkOrderId).collect(Collectors.toSet()));
//        }
        // 兼容云雀的协同单
        BizType bizType = COOPERATION_DOMAIN_BIZ_TYPE_MAP.get(request.getCooperationDomain());
        if (Objects.nonNull(bizType)) {
            List<String> collaborationIds = skylarkManager.queryCollaborationIds(BizType.CUSTOMER_SERVICE, bizType, request.getCooperateWorkOrderCreateTimeBegin(), request.getCooperateWorkOrderCreateTimeEnd(), coop_workoder_query_limit);
            if (!CollectionUtils.isEmpty(collaborationIds)) {
                List<CSWorkOrderDO> csWorkOrderDOS = csWorkOrderDAO.queryCSWorkOrderBySkylarkCollaborationIds(Sets.newHashSet(collaborationIds));
                ids.addAll(csWorkOrderDOS.stream().map(CSWorkOrderDO::getWorkorderId).collect(Collectors.toSet()));
            }
        }
        if (ids.size() > coop_workoder_query_limit) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR, "查询的工单数量超过限制,请缩短查询时间范围");
        }
    }

    @Override
    public CSWorkOrderVO queryOneWorkOrder(String workorderId) {
        CSWorkOrderDO csWorkOrderDO = findWorkOrderOne(workorderId);
        CSWorkOrderVO csWorkOrderVO = toWorkOrderVO(csWorkOrderDO);

        csWorkOrderVO.setHistoryPreAuditWorkOrderVOList(getHistoryPreAuditWorkOrders(csWorkOrderDO));
//        csWorkOrderVO.setPartnerBindVO(getPreAuditWorkOrderPartnerInfo(csWorkOrderDO));
//        csWorkOrderVO.setBusinessBindVO(getExpeditedWorkOrderBusinessInfo(csWorkOrderDO));
        return csWorkOrderVO;
    }

    @Override
    public boolean allowedEditFirmInfo(String workorderId) {
        if (StringUtils.isEmpty(workorderId)) {
            return false;
        }
        CSWorkOrderDO csWorkOrderDO = findWorkOrderOne(workorderId);
        if (Objects.isNull(csWorkOrderDO)) {
            return false;
        }
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            return false;
        }
        return allowedEditFirmInfoConfigId.contains(csWorkOrderDO.getWorkOrderConfigId());
    }

    @Override
    public CSWorkOrderVO queryWorkOrder(CSWorkOrderQueryRequest request) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByReferenceId(request.getReferenceId());
        return toWorkOrderVO(csWorkOrderDO);
    }

    @Override
    public List<CSWorkOrderVO> queryWorkOrderVOS(CSWorkOrdersQueryRequest request) {
        Query query = new Query();
        if (!Strings.isNullOrEmpty(request.getUserId())) {
            query.addCriteria(Criteria.where(CSWorkOrderDO.USER_ID).is(request.getUserId()));
        }
        if (!Strings.isNullOrEmpty(request.getFirmId())) {
            query.addCriteria(Criteria.where(CSWorkOrderDO.FIRM_ID).is(request.getFirmId()));
        }
        if (!Strings.isNullOrEmpty(request.getWorkOrderConfigId())) {
            query.addCriteria(Criteria.where(CSWorkOrderDO.WORK_ORDER_CONFIG_ID).is(request.getWorkOrderConfigId()));
        }
        return csWorkOrderDAO.find(query).stream().map(this::toWorkOrderVO).collect(Collectors.toList());
    }

    @Override
    public FacadeResponse<PageSerializable<CSWorkOrderVO>> bossAccountWorkOrderList(CSWorkOrderPageListRequest request) {
        FacadeResponse facadeResponse = validate(request);
        if (!facadeResponse.isSucceeded()) {
            return facadeResponse;
        }

        Criteria criteria = new Criteria();

        //工单所属领域为空,直接按照workOrderType和businessType查询工单配置
        //工单所属领域不为空查询对应领域工单
        //预审工单:workOrderType = 客户审核 and businessType = 客户成功
        //客服工单:workOrderType != 客户审核 or businessType != 客户成功
        Query configQuery = new Query();
        buildSomePartQuery(request, configQuery);
        Set<String> stringSet = null;
        if (null != request.getWorkOrderDomain() || request.getBusinessType() != null || !StringUtils.isEmpty(request.getWorkorderType())) {
            stringSet = csWorkOrderConfigDAO.find(configQuery).stream().map(CSWorkOrderConfigDO::getConfigId).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(stringSet)) {
                PageSerializable<CSWorkOrderVO> pageSerializable = new PageSerializable<>();
                pageSerializable.setTotal(0);
                pageSerializable.setList(new LinkedList<>());
                return getFacadeResponseWithValue(pageSerializable);
            }
        }
        //除销售工单外的所有工单配置id
        Set<String> configIdsByBusinessType = csWorkOrderConfigDAO.getConfigIdsByBusinessType(BusinessType.MARKETING);
        if (CollectionUtils.isEmpty(configIdsByBusinessType)) {
            return getFacadeResponseWithValue(new PageSerializable<>());
        }
        if (!CollectionUtils.isEmpty(stringSet)) {
            stringSet = stringSet.stream().filter(configIdsByBusinessType::contains).collect(Collectors.toSet());
        } else {
            //过滤销售工单的数据
            stringSet = configIdsByBusinessType;
        }
        if (!CollectionUtils.isEmpty(stringSet)) {
            criteria.and("workOrderConfigId").in(stringSet);
        }

        if (!Strings.isNullOrEmpty(request.getPrincipalUserId())) {
            criteria.and("principalUserId").is(request.getPrincipalUserId());
        }

        Criteria criteria1;
        Criteria criteria2;
        if (!Strings.isNullOrEmpty(request.getFirmId()) && !Strings.isNullOrEmpty(request.getUserId())) {
            criteria1 = Criteria.where("firmId").is(request.getFirmId());
            criteria2 = Criteria.where("userId").is(request.getUserId());
            criteria.andOperator(new Criteria().orOperator(criteria1, criteria2));
        } else if (!Strings.isNullOrEmpty(request.getFirmId()) && Strings.isNullOrEmpty(request.getUserId())) {
            criteria1 = Criteria.where("firmId").is(request.getFirmId());
            criteria.andOperator(criteria1);
        } else if (!Strings.isNullOrEmpty(request.getUserId()) && Strings.isNullOrEmpty(request.getFirmId())) {
            criteria2 = Criteria.where("userId").is(request.getUserId());
            criteria.andOperator(criteria2);
        } else {
            FacadeResponse<PageSerializable<CSWorkOrderVO>> response = new FacadeResponse<>();
            response.fillWithUnSuccess(AttaResponseCode.ILLEGAL_ARGUMENT, "firmId or userId is not null");
            return response;
        }

        if (request.getStatus() != null) {
            criteria.and("status").is(request.getStatus().name());
        }
        if (request.getBeginCreatedTime() != null || request.getEndCreatedTime() != null) {
            Criteria creationDateCriteria = criteria.and("creationDate");
            if (request.getBeginCreatedTime() != null) {
                creationDateCriteria.gte(request.getBeginCreatedTime());
            }
            if (request.getEndCreatedTime() != null) {
                creationDateCriteria.lt(request.getEndCreatedTime());
            }
        }
        Query query = new Query().addCriteria(criteria).with(Sort.by(Sort.Direction.DESC, CSWorkOrderDO.CREATION_DATE));

        Page<CSWorkOrderDO> CSWorkOrderDOS = this.csWorkOrderDAO.findPage(query, request.getPage(), request.getSize());
        List<CSWorkOrderVO> workOrderVOS = new LinkedList<>();
        if (!CollectionUtils.isEmpty(CSWorkOrderDOS)) {
            for (CSWorkOrderDO workOrderDO : CSWorkOrderDOS) {
                FacadeResponse<CSWorkOrderVO> facadeResponse1 = toWorkOrderVOList(workOrderDO);
                if (!facadeResponse1.isSucceeded()) {
                    return getFacadeResponseWithFailed(facadeResponse1.getResponseCode());
                }
                workOrderVOS.add(facadeResponse1.getValue());
            }
        }

        PageSerializable<CSWorkOrderVO> pageSerializable = new PageSerializable<>();
        pageSerializable.setTotal(CSWorkOrderDOS.getTotal());
        pageSerializable.setList(workOrderVOS);
        return getFacadeResponseWithValue(pageSerializable);
    }

    private CSWorkOrderDO findWorkOrderByReferenceId(String referenceId) {
        Criteria criteria = new Criteria();
        criteria.and("referenceId").is(referenceId);
        return csWorkOrderDAO.findOne(new Query().addCriteria(criteria));
    }

    @Override
    public FacadeResponse autoComplete(CSWorkOrderAutoCompleteRequest request) {
        FacadeResponse<CSWorkOrderConfigDO> response = validateBaseCsWorkOrder(request);
        if (!response.isSucceeded()) {
            return response;
        }
        CSWorkOrderConfigDO csWorkOrderConfigDO = response.getValue();

        CSWorkOrderDO exist = findWorkOrderByReferenceId(request.getReferenceId());
        if (exist != null) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR, "工单已存在");
        }
        String firmName = request.getFirmName();
        if (Strings.isNullOrEmpty(firmName)) {
            FacadeResponse<GetFirmNameInfoResponse> firmNameResponse = firmProfileClient.getFirmNameInfo(request.getFirmId());
            if (firmNameResponse.isSucceeded() && firmNameResponse.getValue() != null) {
                firmName = firmNameResponse.getValue().getFirmNameZh();
            }
        }
        CSWorkOrderDO csWorkOrderDO = createdCSWorkOrderDO(request.getReferenceId(), csWorkOrderConfigDO.getConfigId(),
                request.getMid(), request.getFirmId(), firmName, request.getUserId(), "system", request.getPhoneAreaCode(), request.getNewPhoneAreaCode());
        csWorkOrderDO.setStatus(CSWorkOrderStatus.Completed);
        csWorkOrderDO.setPhone(request.getPhone());
        csWorkOrderDO.setVideoUrl(request.getVideoUrl());
        csWorkOrderDO.setCompletedTime(new Date());
        csWorkOrderDO.setMid(request.getMid());
        csWorkOrderDO.setIdNumber(request.getIdNumber());
        csWorkOrderDO.setDirectorName(request.getDirectorName());


        if (csWorkOrderConfigDO.getBusinessType() == BusinessType.ACCOUNT_FUNCTION
                && WorkOrderType.ACCOUNT_SECURITY.getDesc().equals(csWorkOrderConfigDO.getWorkorderType())
                && ProblemType.CHANGE_PHONE_NUMBER_AUTO.getDesc().equals(csWorkOrderConfigDO.getProblemType())) {
            if (request.getNewPhone() == null || (!phonePattern.matcher(request.getNewPhone()).matches() &&
                    !HKPhonePattern.matcher(request.getNewPhone()).matches())) {
                return getFacadeResponseWithFailed(AttaResponseCode.ILLEGAL_ARGUMENT, "请输入正确的新手机号");
            }
            csWorkOrderDO.setNewPhone(request.getNewPhone());
        }
        CSWorkOrderCommRecordDO commRecordDO = createCommRecordDO(csWorkOrderDO.getWorkorderId()
                , csWorkOrderDO.getPrincipalUserId()
                , "人脸核身相似度:" + request.getCommRecord()
                , null
                , null);
        //插入工单
        boolean isOk = insertWO(csWorkOrderDO,
                commRecordDO,
                createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), csWorkOrderDO.getPrincipalUserId(), "自动结案工单"));
        if (!isOk) {
            return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
        }
        return getFacadeResponseWithSuccess();
    }

    @Override
    public FacadeResponse handleCustomerSubmitWorkOrder(CustomerSubmitWorkOrderRequest request) {
        FacadeResponse<CSWorkOrderConfigDO> validateResponse = validateBaseCsWorkOrder(request);
        if (!validateResponse.isSucceeded()) {
            return validateResponse;
        }
        CSWorkOrderDO existReferenceId = findWorkOrderByReferenceId(request.getReferenceId());
        if (existReferenceId != null) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR, "工单已存在");
        }
        CSWorkOrderConfigDO csWorkOrderConfigDO = validateResponse.getValue();
        CSWorkOrderDO csWorkOrderDO = createdCSWorkOrderDO(request.getReferenceId(), csWorkOrderConfigDO.getConfigId(),
                request.getMid(), request.getFirmId(), request.getFirmName(), request.getUserId(), "system", request.getPhoneAreaCode(), request.getNewPhoneAreaCode());
        csWorkOrderDO.setStatus(CSWorkOrderStatus.Checking);
        csWorkOrderDO.setPhone(request.getPhone());
        csWorkOrderDO.setIdNumber(request.getIdNumber());
        csWorkOrderDO.setDirectorName(request.getDirectorName());
        csWorkOrderDO.setSource(WorkOrderSource.USER);

        if (csWorkOrderConfigDO.getBusinessType() == BusinessType.ACCOUNT_FUNCTION
                && WorkOrderType.ACCOUNT_SECURITY.getDesc().equals(csWorkOrderConfigDO.getWorkorderType())
                && ProblemType.CHANGE_PHONE_NUMBER.getDesc().equals(csWorkOrderConfigDO.getProblemType())) {
            FacadeResponse phoneAvailable = checkPhoneAvailable(request.getPhoneAreaCode(), request.getPhone(), request.getNewPhoneAreaCode(), request.getNewPhone());
            if (!phoneAvailable.isSucceeded()) {
                return phoneAvailable;
            }

            csWorkOrderDO.setNewPhone(request.getNewPhone());
        }
        CSWorkOrderDO exist = checkSubsistingWorkOrder(csWorkOrderConfigDO.getConfigId(), request.getUserId());
        if (exist != null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "已提交审核,无需重复提交。");
        }
        String key = "insertWorkOrderKey:" + request.getUserId() + csWorkOrderConfigDO.getConfigId();
        FacadeResponse execute = redissonLock.execute(key, () -> {
            //double check
            CSWorkOrderDO exist2 = checkSubsistingWorkOrder(csWorkOrderConfigDO.getConfigId(), request.getUserId());
            if (exist2 != null) {
                return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "已提交审核,无需重复提交。");
            }
            CSWorkOrderCommRecordDO commRecordDO = createCommRecordDO(csWorkOrderDO.getWorkorderId()
                    , "system"
                    , request.getCommRecord()
                    , request.getAttachments()
                    , null);
            boolean isOk = insertWO(csWorkOrderDO,
                    commRecordDO,
                    createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), csWorkOrderDO.getPrincipalUserId(), "钉钉审核工单"));
            if (!isOk) {
                return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
            }
            return getFacadeResponseWithSuccess();
        });
        if (!execute.isSucceeded()) {
            return execute;
        }

        sendApproval(csWorkOrderDO.getWorkorderId(), "customerWorkOrder", submitBossUserId, submitBossUserName);
        return getFacadeResponseWithSuccess();
    }

    @Override
    public FacadeResponse<Void> review(WorkOrderReviewRequest request) {
        FacadeResponse validate = validate(request);
        if (!validate.isSucceeded()) {
            return validate;
        }
        CSWorkOrderDO csWorkOrderDO = findWorkOrderOne(request.getWorkorderId());
        if (csWorkOrderDO == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "工单不存在");
        }
        if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.Completed) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "工单已结案");
        }
        CSWorkOrderConfigDO csWorkOrderConfigDO = findConfigOne(request.getWorkorderTypeId());
        FacadeResponse facadeResponse = checkCsWorkOrderConfigAvailable(csWorkOrderConfigDO);
        if (!facadeResponse.isSucceeded()) {
            return facadeResponse;
        }
        csWorkOrderDO.setCurrentDealUserId(request.getCurrentDealUserId());
        return doHandleWorkOrderNotification(csWorkOrderDO, csWorkOrderConfigDO, request.getStatus());
    }

    @Override
    public FacadeResponse<Void> submitReview(CSWorkOrderSubmitReviewRequest request) {
        CSWorkOrderConfigDO csWorkOrderConfigDO = findConfigOne(request.getWorkorderTypeId());
        if (csWorkOrderConfigDO == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "工单配置不存在");
        }
        FacadeResponse facadeResponse = checkCsWorkOrderConfigAvailable(csWorkOrderConfigDO);
        if (!facadeResponse.isSucceeded()) {
            return facadeResponse;
        }

        CSWorkOrderDO csWorkOrderDO = WorkOrderConverter.convertFromSubmitReviewRequest(request);
        csWorkOrderDO.setStatus(CSWorkOrderStatus.Checking);
        csWorkOrderDO.setWorkorderId(getWorkOrderId());
        csWorkOrderDO.setNewCollaboration(CsOrderConstants.newCollaboration);

        WorkOrderConverter.setBasicFirmInfo(csWorkOrderDO, request.getUserId(), request.getFirmId(), firmClient, userClient);
        WorkOrderConverter.setCSOrderDOFromOrderConfig(csWorkOrderDO, request.getWorkorderTypeId(), cSWorkOrderConfigService);

        // 关联问卷
        relateQuestionnaire(csWorkOrderDO.getWorkorderId(), request.getWorkorderTypeId(), request.getSceneConfigId());

        //校验身份证
        if (request.getIdNumber() != null) {
            if (!IDUtils.isIDNumber(request.getIdNumber())) {
                csWorkOrderDO.setIdType(IDType.passport.name());
            } else {
                csWorkOrderDO.setIdType(IDType.identity.name());
            }
        }
        if (CHANGE_USER_MASTER_PHONE.equals(csWorkOrderConfigDO.getProblemType())) {
            if (Strings.isNullOrEmpty(csWorkOrderDO.getUserId())) {
                return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "非 XT 商户,修改手机号失败");
            }
            FacadeResponse phoneAvailable = checkPhoneAvailable(csWorkOrderDO.getPhoneAreaCode(), csWorkOrderDO.getPhone(), request.getNewPhoneAreaCode(), request.getNewPhone());
            if (!phoneAvailable.isSucceeded()) {
                return phoneAvailable;
            }
        }
        //解绑x盾
        if (UN_BIND_X.equals(csWorkOrderConfigDO.getProblemType())) {
            if (Strings.isNullOrEmpty(request.getUserId())) {
                return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "未绑定X盾");
            }
            FacadeResponse<Boolean> XShieldResponse = checkXshieldAvailable(csWorkOrderDO.getUserId());
            if (!XShieldResponse.isSucceeded() || XShieldResponse.getValue() == null || !XShieldResponse.getValue()) {
                return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "未绑定X盾");
            }
        }
        CSWorkOrderDO exist = checkSubsistingWorkOrder(csWorkOrderConfigDO.getConfigId(), csWorkOrderDO.getUserId());
        if (exist != null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "已提交审核,无需重复提交。");
        }

        String key = "insertWorkOrderKey:" + csWorkOrderDO.getUserId() + csWorkOrderConfigDO.getConfigId();
        FacadeResponse execute = redissonLock.execute(key, () -> {
            //double check
            CSWorkOrderDO exist2 = checkSubsistingWorkOrder(csWorkOrderConfigDO.getConfigId(), csWorkOrderDO.getUserId());
            if (exist2 != null) {
                return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "已提交审核,无需重复提交。");
            }
            CSWorkOrderCommRecordDO commRecordDO = createCommRecordDO(csWorkOrderDO.getWorkorderId()
                    , request.getCurrentDealUserId()
                    , request.getCommRecord()
                    , request.getAttachments()
                    , request.getCallCenterIds());
            boolean isOk = insertWO(csWorkOrderDO
                    , commRecordDO
                    , createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), csWorkOrderDO.getPrincipalUserId(), "钉钉审核工单"));
            if (!isOk) {
                return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
            }
            return getFacadeResponseWithSuccess();
        });
        if (!execute.isSucceeded()) {
            return execute;
        }

        //钉钉审核
        sendApproval(csWorkOrderDO.getWorkorderId(), "customerWorkOrder", request.getCurrentDealUserId(), request.getCurrentDealUserName());
        return getFacadeResponseWithSuccess();
    }

    @Override
    public FacadeResponse<Void> doHandleWorkOrderNotification(CSWorkOrderDO csWorkOrderDO, CSWorkOrderConfigDO csWorkOrderConfigDO, CrmApprovalStatus crmApprovalStatus) {
        if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.Completed) {
            log.warn("work order status is completed, work order id is {}", csWorkOrderDO.getWorkorderId());
            return getFacadeResponseWithSuccess();
        }
        CSWorkOrderOpLogDO opLogDO = new CSWorkOrderOpLogDO();
        opLogDO.setOpLogId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_OP_LOG));
        opLogDO.setOperatorUserId(csWorkOrderDO.getSource() == WorkOrderSource.BOSS ? csWorkOrderDO.getCurrentDealUserId() : "system");
        opLogDO.setOpTime(new Date());
        opLogDO.setStatus(CSWorkOrderStatus.Completed.name());
        opLogDO.setSuggestion(crmApprovalStatus == CrmApprovalStatus.AGREED ? "审核通过" : "审核不通过");
        opLogDO.setWorkorderId(csWorkOrderDO.getWorkorderId());

        csWorkOrderDO.setCompletedTime(new Date());
        csWorkOrderDO.setStatus(CSWorkOrderStatus.Completed);
        try {
            sendNotification(csWorkOrderDO, csWorkOrderConfigDO, crmApprovalStatus);
        } catch (Exception e) {
            log.error("send notification error,csWorkOrderDO workOrderId is {},{}", csWorkOrderDO.getWorkorderId(), e);
            return getFacadeResponseWithFailed(AttaResponseCode.FAIL, "审核失败");
        }
        return mongoTransactionTemplate.execute(status -> {
            if (csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) == null) {
                status.setRollbackOnly();
                return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
            }
            if (csWorkOrderOpLogDAO.insert(opLogDO) == null) {
                status.setRollbackOnly();
                return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
            }
            return getFacadeResponseWithSuccess();
        });
    }

    @Override
    public boolean isPreAuditWorkOrder(String workOrderId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
        if (csWorkOrderDO == null) {
            log.warn("csWorkOrderDO not found workOrderId:{}", workOrderId);
            return true;
        }
        return isPreAuditWorkOrder(csWorkOrderDO);
    }


    /**
     * 是否预审工单(注意:不包含【理财业务预审】【大陆企业股东结汇预审】)
     *
     * @param workOrderDO
     * @return
     */
    @Override
    public boolean isPreAuditWorkOrderV2(CSWorkOrderDO workOrderDO) {
        CSExceptionAssert.isTrue(StringUtils.isNotBlank(workOrderDO.getWorkOrderConfigId())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "workOrderConfigId is empty. workOrderId is: " + workOrderDO.getWorkorderId());

        CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(workOrderDO.getWorkOrderConfigId());

        return configDO != null && (WorkOrderConfigType.CertificationPreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.UpdateCertificationPreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.CustomerAnnualPreAudit.name().equals(configDO.getConfigType()));
    }

    @Override
    public void workOrderAutoCancelled() {
        //获取当前日期N天前的客服工单,状态为"新建"
        Date date = DateUtils.addDays(new Date(), -autoCancelledDays);
        Criteria criteria = Criteria.where("creationDate").lt(date).and("status").is(CSWorkOrderStatus.Created);

        Query query = new Query();
        query.addCriteria(criteria);

        int size = 500;
        query.limit(size);

        while (true) {
            List<CSWorkOrderDO> workOrderDOList = csWorkOrderDAO.find(query);
            if (CollectionUtils.isEmpty(workOrderDOList)) {
                break;
            }
            workOrderDOList.forEach(w -> {
                w.setStatus(CSWorkOrderStatus.Cancelled);
                if (ExpediteType.ORDINARY_EXPEDITE.equals(w.getExpediteType())) {
                    cancelledExpediteWorkOrder(w);
                } else {
                    this.updateWO(w, createdOpLogDO(w.getWorkorderId(), w.getStatus(), "system", "无人认领,撤销工单"));
                }
            });
        }
    }

    @Override
    public SpiServiceTicketOperationVO customerCancelWorkOrder(String userId, String serviceBizId, String reason, String language) {
        //TODO 迁移变更:这里不再支持预审认证撤销操作
        return SpiServiceTicketOperationVO.builder().globalId(serviceBizId).succeed(false).description("不支持的预审操作").build();
    }

    @Override
    public FacadeResponse<Boolean> checkExistCSWorkOrder(CSWorkOrderBaseRequest request) {
        FacadeResponse validateResponse = validate(request);
        if (!validateResponse.isSucceeded()) {
            return validateResponse;
        }
        FacadeResponse<Boolean> response = new FacadeResponse<>();
        response.setSucceeded(true);
        response.setValue(Boolean.TRUE);
        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.findOne(MongoQueryFactory.create(request));
        if (csWorkOrderConfigDO == null || csWorkOrderConfigDO.getConfigId() == null) {
            response.setSucceeded(false);
            response.setResponseCode(AttaResponseCode.VALIDATION_ERROR);
            response.setResponseMsg("work config is not exist");
            return response;
        }
        CSWorkOrderDO exist = checkSubsistingWorkOrder(csWorkOrderConfigDO.getConfigId(), request.getUserId());
        if (exist == null) {
            response.setValue(Boolean.FALSE);
        }
        return response;
    }

    @Override
    public FacadeResponse<CSWorkorderConfigVO> queryConfigByWorkOrderId(String workorderId) {
        FacadeResponse<CSWorkorderConfigVO> response = new FacadeResponse<>();
        response.setSucceeded(true);
        //查询工单
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where("workorderId").is(workorderId)));
        if (csWorkOrderDO == null || csWorkOrderDO.getWorkOrderConfigId() == null) {
            log.error("find workOrder failed, workOrder id is {},or config id is null", workorderId);
            response.setSucceeded(false);
            return response;
        }
        CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.findOne(new Query().addCriteria(Criteria.where("configId").is(csWorkOrderDO.getWorkOrderConfigId())));
        if (configDO == null) {
            log.error("find WorkOrderConfig error ,config id is {}", csWorkOrderDO.getWorkOrderConfigId());
            response.setSucceeded(false);
            return response;
        }
        response.setValue(convertConfigDOToVo(configDO));
        return response;
    }

    @Override
    public CSWorkOrderVO queryByReferenceId(String referenceId) {
        Assert.isTrue(StringUtils.isNotEmpty(referenceId), "referenceId should not be empty for queryByReferenceId");
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByReferenceId(referenceId);
        return toWorkOrderVO(csWorkOrderDO);
    }

    @Override
    public CSWorkOrderVO queryBySkylarkCollaborationId(String collaborationId) {
        Assert.isTrue(StringUtils.isNotEmpty(collaborationId), "collaborationId should not be empty for queryBySkylarkCollaborationId");
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderBySkylarkCollaborationId(collaborationId);
        return WorkOrderConverter.toWorkOrderVO(csWorkOrderDO);
    }

    @Override
    public boolean createChangePrincipalUserOrder(String customerId, String principalUserId) {
        //TODO 迁移变更:无上游调用,先置空
        return false;
    }

    @Override
    public boolean changeWorkOrderPrincipalUser(String userId, String fromPrincipalUserId, String toPrincipalUserId) {
        User principalUser = bossUserClient.queryByBossUserId(toPrincipalUserId);
        if (principalUser == null) {
            log.error("无法获取被转移人信息,工单转移失败:{}", toPrincipalUserId);
            return false;
        }
        List<String> configIds = csWorkOrderConfigDAO.find(new Query().addCriteria(Criteria.where("businessType").is(BusinessType.CUSTOMER_SUCCESS.name()))).stream().map(CSWorkOrderConfigDO::getConfigId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(configIds)) {
            return true;
        }
        Criteria criteria = Criteria.where("userId").is(userId).and("workOrderConfigId").in(configIds);
        if (StringUtils.isBlank(fromPrincipalUserId)) {
            criteria.and("principalUserId").in(Sets.newHashSet(null, "")).and("status").is(CSWorkOrderStatus.Created);
        } else {
            criteria.and("principalUserId").is(fromPrincipalUserId).and("status").in(processCSWorkOrderStatus);
        }
        List<CSWorkOrderDO> workOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(criteria));
        if (CollectionUtils.isEmpty(workOrderDOList)) {
            return true;
        }
        String suggestion = String.format("转移负责人至【%s】,分配理由:%s", principalUser.getNick(), "客户负责人转移,关联工单同步转移");
        List<CSWorkOrderOpLogDO> opLogDOS = new LinkedList<>();
        for (CSWorkOrderDO workOrderDO : workOrderDOList) {
            if (StringUtils.isEmpty(workOrderDO.getBeforeUpgradePrincipalId())) {
                workOrderDO.setStatus(CSWorkOrderStatus.Processing);
            } else {
                workOrderDO.setStatus(CSWorkOrderStatus.UpgradeProcessing);
            }
            workOrderDO.setCurrentDealUserId(toPrincipalUserId);
            workOrderDO.setPrincipalUserId(toPrincipalUserId);
            opLogDOS.add(createdOpLogDO(workOrderDO.getWorkorderId(), workOrderDO.getStatus(), workOrderDO.getCurrentDealUserId(), suggestion));
        }

        return updateWOS(workOrderDOList, opLogDOS).isSucceeded();
    }

    @Override
    public boolean changeWorkOrderNonePrincipalUser(String userId, String fromPrincipalUserId) {
        User principalUser = bossUserClient.queryByBossUserIdIgnoreStatus(fromPrincipalUserId);
        if (principalUser == null) {
            log.error("无法获取转移人信息,工单转移失败:{}", fromPrincipalUserId);
            return false;
        }
        List<String> configIds = csWorkOrderConfigDAO.find(new Query().addCriteria(Criteria.where("businessType").is(BusinessType.CUSTOMER_SUCCESS.name()))).stream().map(CSWorkOrderConfigDO::getConfigId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(configIds)) {
            return true;
        }

        Criteria criteria = Criteria.where("userId").is(userId).and("workOrderConfigId").in(configIds);
        criteria.and("principalUserId").is(fromPrincipalUserId).and("status").in(processCSWorkOrderStatus);
        List<CSWorkOrderDO> workOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(criteria));
        if (CollectionUtils.isEmpty(workOrderDOList)) {
            return true;
        }

        String suggestion;
        List<CSWorkOrderOpLogDO> opLogDOS = new LinkedList<>();
        for (CSWorkOrderDO workOrderDO : workOrderDOList) {

            workOrderDO.setStatus(CSWorkOrderStatus.Closed);
            workOrderDO.setCurrentDealUserId("");
            workOrderDO.setPrincipalUserId("");

            suggestion = String.format("从负责人【%s】转移至无负责人,分配理由:%s", principalUser.getNick(), "客户负责人转移,关联工单同步转移");
            opLogDOS.add(createdOpLogDO(workOrderDO.getWorkorderId(), workOrderDO.getStatus(), "system", suggestion));
        }

        return updateWOS(workOrderDOList, opLogDOS).isSucceeded();
    }

    @Override
    public void avgAssign(CSWorkOrderAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void weightAssign(CSWorkOrderWeightAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void directAssign(CSWorkOrderDirectAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void specificAssign(CSWorkOrderGroupAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void avgAssignForConfigId(CSWorkOrderAssignCommonRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public int avgAssignForWorkOrderIds(CSWorkOrderAssignReq request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
        return 0;
    }

    @Override
    public int avgAssignForReferenceIds(CSWorkOrderAssignByReferenceIdReq request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
        return 0;
    }

    @Override
    public int avgAssignForExecutorId(CSWorkOrderAssignByExecutorId request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
        return 0;
    }

    @Override
    public void closeCSWorkOrderToDate(CSWorkOrderAssignCommonRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public String autoCreate(CSWorkOrderAutoCreateRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
        return null;
    }

    @Override
    public void batchWeightAssign(CSWorkOrderBatchWeightAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void batchSpecificAssign(CSWorkOrderGroupBatchAssignRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void specificAssignForConfigId(CSWorkOrderAssignCommonRequest request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void specificAssignForReferenceIds(CSWorkOrderAssignByReferenceIdReq request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void specificAssignForExecutorId(CSWorkOrderAssignByExecutorId request) {
        //TODO 迁移变更:营销域逻辑已废弃,先置空
    }

    @Override
    public void preAuditSkip(PreAuditSkipRequest request) {
        Criteria criteria = Criteria.where("workorderId").in(request.getWorkOrderIds());
        List<CSWorkOrderDO> workOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(criteria));
        if (CollectionUtils.isEmpty(workOrderDOList)) {
            return;
        }
        List<CSWorkOrderStatus> skipStatus = Arrays.asList(CSWorkOrderStatus.Created, CSWorkOrderStatus.Processing, CSWorkOrderStatus.UpgradeProcessing);
        List<CSWorkOrderOpLogDO> opLogDOS = Lists.newArrayList();
        List<ActionDO> actionDOS = Lists.newArrayList();
        for (CSWorkOrderDO workOrder : workOrderDOList) {
            if (!isPreAuditWorkOrder(workOrder)) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "只能跳过预审工单");
            }
            //大陆预审不让跳过
            if (isMainLandFxPreAudit(workOrder)) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "大陆预审工单不能跳过");
            }
            if (!skipStatus.contains(workOrder.getStatus())) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "只能跳过新建、处理中的预审工单");
            }

            workOrder.setStatus(CSWorkOrderStatus.Completed);
            workOrder.setCompletedTime(new Date());
            workOrder.setCurrentDealUserId(request.getCurrentDealUserId());
            opLogDOS.add(createdOpLogDO(workOrder.getWorkorderId(), workOrder.getStatus(), workOrder.getCurrentDealUserId(), "跳过预审工单"));

            //预审核结果
            Query query = new Query();
            query.addCriteria(Criteria.where(ActionDO.SCENE_ID).is(workOrder.getWorkorderId()).and("tagKey").is("preAudit"));
            ActionDO actionDO = actionDAO.findOne(query);
            if (null == actionDO) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "未找到预审工单的action");
            }
            if (null == actionDO.getActionResults()) {
                actionDO.setActionResults(Maps.newHashMap());
            }
            Map<String, Object> actionResults = actionDO.getActionResults();
            actionResults.put(PRE_AUDIT_RESULT, "Skip");
            actionResults.put("skipPreAudit", "yes");
            actionDOS.add(actionDO);
        }

        Boolean isOk = mongoTransactionTemplate.execute(status -> {
            try {
                for (CSWorkOrderDO workOrderDO : workOrderDOList) {
                    if (csWorkOrderDAO.updateByIdSelective(workOrderDO) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                    }
                    if (null != workOrderDO.getExpediteType()) {
                        workOrderInstructionDAO.insertInstruction(workOrderDO.getWorkorderId(), WorkOrderInstructionType.ExpediteWorkOrder);
                    }
                    workOrderInstructionDAO.insertInstruction(workOrderDO.getWorkorderId(), WorkOrderInstructionType.CloseTag);
                    workOrderInstructionDAO.insertInstruction(workOrderDO.getWorkorderId(), WorkOrderInstructionType.PreAudit);
                }
                if (csWorkOrderOpLogDAO.batchInsert(opLogDOS).size() != opLogDOS.size()) {
                    throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert op log fail.");
                }
                for (ActionDO actionDO : actionDOS) {
                    if (actionDAO.updateByIdSelective(actionDO) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                    }
                }
            } catch (Exception e) {
                log.warn(e.getMessage(), e);
                status.setRollbackOnly();
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        });
        if (!Boolean.TRUE.equals(isOk)) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "跳过预审工单失败");
        }
        //批量跳过负责人,把原工单负责人加入到队列中
        try {
            workOrderDOList.forEach((workOrderDO) -> enterQueue(workOrderDO.getPrincipalUserId()));
        } catch (Exception e) {
            log.error("批量跳过负责人后,把用户移出自动分配队列异常", e);
        }

        CompletableFuture.runAsync(() -> workOrderDOList.forEach((workOrderDO) -> doInstruction(workOrderDO.getWorkorderId())), commonExecutor);
    }

    private boolean isMainLandFxPreAudit(CSWorkOrderDO workOrder) {
        if (StringUtils.isEmpty(workOrder.getWorkOrderConfigId())) {
            return false;
        }
        CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(workOrder.getWorkOrderConfigId());
        return WorkOrderConfigType.MainLandFxPreAudit.name().equals(configDO.getConfigType());
    }

    @Override
    public void preAuditNotify(PreAuditNotifyRequest request) {
        if (CollectionUtils.isEmpty(request.getWorkOrderIds())) {
            return;
        }
        request.getWorkOrderIds().forEach(workOrderId -> {
            CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
            if (csWorkOrderDO != null) {
                try {
                    workorderMessageProducer.sendPreAuditStatusEvent(csWorkOrderDO);
                } catch (Exception e) {
                    log.error("send PreAudit failed. e:", e);
                }
            }
        });
    }

    @Override
    public void relateQuestionnaire(String workOrderId, String workOrderConfigId, String sceneConfigId) {
        if (StringUtils.isAllBlank(workOrderConfigId, sceneConfigId) || StringUtils.isEmpty(workOrderId)) {
            log.warn("workOrderId or workOrderConfigId should not be empty. workOrderId: {},workOrderConfigId: {}.", workOrderId, workOrderConfigId);
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "");
        }
        QuestionnaireRelationQueryRequest relationQueryRequest = new QuestionnaireRelationQueryRequest();
        relationQueryRequest.setBizId(StringUtils.isNotBlank(sceneConfigId) ? sceneConfigId : workOrderConfigId);
        List<QuestionnaireRelationVO> relationVOS = questionnaireManager.queryQuestionnaireRelations(relationQueryRequest);
        if (CollectionUtils.isEmpty(relationVOS)) {
            return;
        }
        relationVOS.forEach(relationVO -> {
            com.atta.infra.crm.form.api.model.request.QuestionnaireRelateRequest relateRequest = new com.atta.infra.crm.form.api.model.request.QuestionnaireRelateRequest();
            relateRequest.setType(relationVO.getQuestionnaireType());
            relateRequest.setDomain(QuestionnaireRelationDomain.CUSTOMER_SERCICE);
            relateRequest.setScene(QuestionnaireRelationScene.CS_WORK_ORDER);
            relateRequest.setFormId(relationVO.getFormId());
            relateRequest.setFormVersion(relationVO.getFormVersion());
            relateRequest.setBizId(workOrderId);
            questionnaireManager.relateQuestionnaire(relateRequest);
        });
    }

    @Override
    public String queryPhoneByWorkOrderId(String workOrderId) {
        CSExceptionAssert.isTrue(StringUtils.isNoneEmpty(workOrderId)
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "workOrderId should not be empty");
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
        CSExceptionAssert.isTrue(csWorkOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "csWorkOrderDO is not exist.workOrderId is: " + workOrderId);
        return csWorkOrderDO.getPhone();
    }

    @Override
    public void autoClose() {
        log.info("start to auto close workOrder ");
        Integer count = 0;
        Criteria criteria = Criteria.where(CSWorkOrderDO.WORK_ORDER_END_TIME).lt(new Date()).and(CSWorkOrderDO.STATUS).nin(CSWorkOrderStatus.finalCSWorkOrderStatus);
        CloseableIterator<CSWorkOrderDO> iterator = csWorkOrderDAO.stream(new Query(criteria));
        while (iterator.hasNext()) {
            CSWorkOrderDO workOrderDO = iterator.next();
            log.info("auto close customer service work order : {}", workOrderDO.getWorkorderId());

            CSWorkOrderCompleteRequest completeRequest = new CSWorkOrderCompleteRequest();
            completeRequest.setWorkorderId(workOrderDO.getWorkorderId());
            completeRequest.setOperatorUserId(SystemConst.SYSTEM_USER_ID);
            completeRequest.setStatus(CSWorkOrderStatus.Closed);
            try {
                workOrderClose(completeRequest.getWorkorderId(), completeRequest.getOperatorUserId(), completeRequest.getStatus(), "结案工单");
                count++;
            } catch (Exception e) {
                log.error("auto close work order {} error:", workOrderDO.getWorkorderId(), e);
            }
        }
        log.info("auto close work order end ,count:{}", count);
    }

    @Override
    public String workOrderClose(String workOrderId, String operatorUserId, CSWorkOrderStatus status, String suggestion) {
        CSWorkOrderDO workOrderDO = validateWorkOrderClose(workOrderId, status);
        if (null == workOrderDO) {
            return workOrderId;
        }
        CSWorkOrderStatus oldStatus = workOrderDO.getStatus();
        ActionDO actionDO = actionDAO.getLastUpdateActionBySceneId(workOrderDO.getWorkorderId());
        //是预审工单,且预审结果没选,不能提交工单
        validatePreAudit(actionDO);

        workOrderDO.setStatus(status);
        // 设置关单类型
        workOrderDO.setCloseType(SystemConst.SYSTEM_USER_ID.equalsIgnoreCase(operatorUserId)?
                CSWorkOrderEmailCloseType.OTHER.getCode() : CSWorkOrderEmailCloseType.MANUAL_CLOSE.getCode());
        workOrderDO.setCompletedTime(new Date());
        workOrderDO.setCurrentDealUserId(operatorUserId);
        if (CollectionUtils.isEmpty(workOrderDO.getHistoryDealUsers())) {
            workOrderDO.setHistoryDealUsers(new HashSet<>());
        }
        workOrderDO.getHistoryDealUsers().add(operatorUserId);
        CSWorkOrderOpLogDO opLogDO = createdOpLogDO(workOrderDO.getWorkorderId()
                , workOrderDO.getStatus()
                , operatorUserId
                , suggestion);

        boolean isGeneratedByTag = isGeneratedByTag(workOrderDO.getWorkOrderConfigId());

        boolean isOk = transactionUtil.executeMongo(() -> {
            CSExceptionAssert.isTrue(csWorkOrderOpLogDAO.insert(opLogDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert opLogDO failed.");

            CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(workOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "update workOrderDO failed.");

            addWorkOrderInstructions(workOrderDO, actionDO, isGeneratedByTag);
        });

        CSExceptionAssert.isTrue(isOk
                , CSOrderBizErrorCode.SYSTEM_ERROR
                , "complete work order failed");
        CompletableFuture.runAsync(() -> doInstruction(workOrderDO.getWorkorderId()), commonExecutor);
        //TODO 迁移变更:预审驳回发送消息 废弃
//        sendPreAuditNoPassMessage(actionDO, workOrderDO);
        //发送工单结案消息
        sendWorkOrderCompleteMessage(workOrderDO);
        if (!Objects.isNull(workOrderDO.getSkylarkCollaborationId())) {
            //云雀平台新协同工单
            sendSkylarkCollaborationMessage(workOrderDO, workOrderDO.getSkylarkCollaborationId(), WorkOrderOptType.COMPLETE);
        }
//        else {
//            sendCooperationMessageNew(workOrderDO, WorkOrderOptType.COMPLETE);
//        }
        //工单完成后给代办发送消息
        sendToDoTaskMessage(workOrderDO);
        //sla 上报
        slaEscalationService.escalationCompleteWorkOrderSla(workOrderDO);
        return workOrderDO.getWorkorderId();
    }

    private CSWorkOrderDO validateWorkOrderClose(String workOrderId, CSWorkOrderStatus status) {
        CSExceptionAssert.isTrue(finalCSWorkOrderStatus.contains(status)
                , CSOrderBizErrorCode.VALIDATION_ERROR, "status should be final status.");

        CSWorkOrderDO workOrderDO = findWorkOrderOne(workOrderId);
        CSExceptionAssert.isTrue(workOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "workOrder is not exist, WorkOrderId is: " + workOrderId);
        if (finalCSWorkOrderStatus.contains(workOrderDO.getStatus())) {
            return null;
        }
        return workOrderDO;
    }

    /**
     * 校验预审
     */
    private void validatePreAudit(ActionDO actionDO) {
        if (actionDO != null && (FIRST_AUDIT_TOW.equals(actionDO.getTagValue()) || UPDATE_AUDIT.equals(actionDO.getTagValue()))
                && (actionDO.getActionResults() == null || !Sets.newHashSet(PASS_RESULT, NO_PASS_RESULT).contains(String.valueOf(actionDO.getActionResults().get(PRE_AUDIT_RESULT))))
        ) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "预审工单审核结果未保存");
        }
    }

    /**
     * 给代办发送消息
     */
    private void sendToDoTaskMessage(CSWorkOrderDO workOrderDO) {
        TodoTaskEvent todoTaskEvent = new TodoTaskEvent();
        convertWorkOrderDO2TodoTaskEvent(workOrderDO, todoTaskEvent);
        if (StringUtils.isNotEmpty(todoTaskEvent.getPrincipalUserId())) {
            imqProvider.push(TodoTaskEvent.QUEUE_NAME, todoTaskEvent, QueueType.TOPIC);
        }
    }

    @Override
    public String close(CSWorkOrderCloseRequest request) {
        try {
            workOrderClose(request.getWorkOrderId(), request.getOperatorUserId(), CSWorkOrderStatus.Closed, request.getSuggestion());
        } catch (Exception e) {
            log.error("close work order {} fail: ", request.getWorkOrderId(), e);
        }
        return request.getWorkOrderId();
    }

    @Override
    public void onCSWOOpen(CSWOOpenRequest request) {
        //TODO czs 需要迁移吗?
    }

    @Override
    public void onPreAuditWOOpen(CustomerTagChangeEvent event) {
        ValidateUtil.assertValid(event);

        String workOrderConfigId = findWorkOrderConfigId(event);
        if (Strings.isNullOrEmpty(workOrderConfigId)) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR, "workOrderTypeId  can not be null.");
        }
        if (Strings.isNullOrEmpty(event.getEventId())) {
            log.error("pre audit evenId can't be empty!");
            return;
        }
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByReferenceId(event.getEventId());
        if (csWorkOrderDO != null) {
            return;
        }

        csWorkOrderDO = buildWorkOrderDO(event, workOrderConfigId);
        // 关联问卷

        relateQuestionnaire(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getWorkOrderConfigId(), csWorkOrderDO.getSceneConfigId());

        ActionDO actionDO = buildActionDO(event, csWorkOrderDO.getWorkorderId());
        List<CSWorkOrderOpLogDO> opLogDOS = new LinkedList<>();

        CSWorkOrdersQueryRequest csWorkOrdersQueryRequest = new CSWorkOrdersQueryRequest();
        csWorkOrdersQueryRequest.setUserId(event.getUserId());
        csWorkOrdersQueryRequest.setWorkOrderConfigId(workOrderConfigId);
        List<CSWorkOrderVO> csWorkOrderVOList = queryWorkOrderVOS(csWorkOrdersQueryRequest);

        CSWorkOrderVO lastWorkOrderVO = csWorkOrderVOList.stream().filter(workOrderVO -> CSWorkOrderStatus.Completed.equals(workOrderVO.getStatus()))
                .max(Comparator.comparing(CSWorkOrderVO::getCreatedTime)).orElse(null);
        //上次预审结果是否通过
        boolean isPassedInLastPreAudit = true;
        ActionDO lastActionDO = null;

        if (lastWorkOrderVO != null) {
            lastActionDO = actionDAO.findOne(new Query(Criteria.where(ActionDO.SCENE_ID).is(lastWorkOrderVO.getWorkorderId())));
            if (lastActionDO != null && !CollectionUtils.isEmpty(lastActionDO.getActionResults()) && PASS_RESULT.equals(lastActionDO.getActionResults().get(PRE_AUDIT_RESULT))) {
            } else {
                isPassedInLastPreAudit = false;
            }
        } else {
            isPassedInLastPreAudit = false;
        }
        CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.findOne(new Query().addCriteria(Criteria.where("configId").is(workOrderConfigId)));
        if (isPassedInLastPreAudit && null != configDO && (FIRST_AUDIT.equals(configDO.getProblemType()) || WEALTH_MANAGEMENT_AUDIT.equals(configDO.getProblemType()))) {
            csWorkOrderDO.setPrincipalUserId("system");
            csWorkOrderDO.setCurrentDealUserId("system");
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Completed);
            csWorkOrderDO.setCompletedTime(new Date());

            actionDO.setActionResults(lastActionDO.getActionResults());

            opLogDOS.add(buildOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "结案工单"));
        } else {
//            RelationshipVO principalVO = findPrincipalUserId(event.getDomain(), event.getUserId(), workOrderConfigId);
//            csWorkOrderDO.setPrincipalUserId(principalVO == null ? null : principalVO.getPrincipalUserId());
            csWorkOrderDO.setCurrentDealUserId(csWorkOrderDO.getPrincipalUserId());

            if (Strings.isNullOrEmpty(csWorkOrderDO.getPrincipalUserId())) {
                csWorkOrderDO.setStatus(CSWorkOrderStatus.Created);
            } else {
                csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
            }

            //保留上次失败的审核结果、建议、备注
            if (null != lastActionDO && !CollectionUtils.isEmpty(lastActionDO.getActionResults())) {
                Map<String, Object> lastActionResults = lastActionDO.getActionResults();
                Map<String, Object> lastAuditResults = new HashMap<>(4);
                lastAuditResults.put(PRE_AUDIT_RESULT, lastActionResults.get(PRE_AUDIT_RESULT));
                lastAuditResults.put("preAuditSuggestion", lastActionResults.get("preAuditSuggestion"));
                lastAuditResults.put("preAuditRemark", lastActionResults.get("preAuditRemark"));
                lastAuditResults.put("version", preWorkOrderFormVersion);
                actionDO.setActionResults(lastAuditResults);
            }

            opLogDOS.add(buildOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "创建工单"));
//            if (!Strings.isNullOrEmpty(csWorkOrderDO.getPrincipalUserId())) {
//                String principalUserName = principalVO == null ? "" : principalVO.getPrincipalUserName();
//                opLogDOS.add(buildOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "自动分配处理人【" + principalUserName + "】"));
//            }
        }

        if (csWorkOrderDAO.insert(csWorkOrderDO) == null) {
            log.warn("insert fail.event >>> {}", event);
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert fail.");
        }
        if (actionDAO.insert(actionDO) == null) {
            log.warn("insert fail.event >>> {}", event);
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert fail.");
        }
        if (csWorkOrderOpLogDAO.batchInsert(opLogDOS).size() != opLogDOS.size()) {
            log.warn("insert opLogs fail.event >>> {}", event);
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert fail.");
        }

        workorderMessageProducer.sendPreAuditStatusEvent(csWorkOrderDO);
    }

    private String findWorkOrderConfigId(CustomerTagChangeEvent event) {
        WorkOrderAndTagRelationVO relationVO = workOrderConfigAndTagRelationService.getTagAndOrderConfigRelationByTag(event.getDomain(), event.getKey(), event.getValue());
        if (relationVO == null) {
            return null;
        } else {
            return relationVO.getWorkOrderConfigId();
        }
    }

    private CSWorkOrderDO buildWorkOrderDO(CustomerTagChangeEvent event, String workOrderTypeId) {
        CSWorkOrderDO workOrderDO = new CSWorkOrderDO();
        workOrderDO.setWorkorderId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER));
        workOrderDO.setWorkOrderConfigId(workOrderTypeId);
        workOrderDO.setLeadsId(event.getLeadsId());
        workOrderDO.setNewCollaboration(true);
        workOrderDO.setServiceBizId(event.getServiceBizId());

        //设置leads数据
        if (CrmDomain.leads.name().equals(event.getDomain())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "不支持leads场景");
        }

        if (event.isMainLand()) {
            workOrderDO.setFirmId(event.getFirmId());
            workOrderDO.setGrantorId(event.getGrantorId());
            workOrderDO.setBeGrantorId(event.getBeGrantorId());
        }

        WorkOrderConverter.setBasicFirmInfo(workOrderDO, event.getUserId(), null, firmClient, userClient);

        WorkOrderConverter.setCSOrderDOFromOrderConfig(workOrderDO, workOrderTypeId, cSWorkOrderConfigService);
        workOrderDO.setReferenceId(event.getEventId());

        return workOrderDO;
    }

    private ActionDO buildActionDO(CustomerTagChangeEvent event, String workOrderId) {
        ActionDO actionDO = new ActionDO();
        actionDO.setActionId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.MKT_ACTION));
        actionDO.setTagEntityId(event.getUserId());
        actionDO.setTagDomain(event.getDomain());
        actionDO.setTagKey(event.getKey());
        actionDO.setTagValue(event.getValue());
        actionDO.setSceneId(workOrderId);
        actionDO.setScene(ActionScene.CS_WORK_ORDER);
        actionDO.setCreationDate(new Date());
        actionDO.setLastUpdateDate(new Date());
        return actionDO;
    }

    @Override
    public void onAuditStatusChange(AuditStatusChangeEvent event) {
        if (Strings.isNullOrEmpty(event.getEventId())) {
            log.error("eventId can't be null!");
            return;
        }
        if (event.getAuditType() == null || !BossConstants.AuditType.FirmOnboarding.equals(event.getAuditType())) {
            log.warn("AuditType can't be " + (event.getAuditType() == null ? "null" : event.getAuditType().name()));
            return;
        }

        if (event.getAuditStatus() == null) {
            log.error("auditStatus can't be null!");
            return;
        } else {
            if (!Sets.newHashSet(
                    BossConstants.AuditStatus.Completed
                    , BossConstants.AuditStatus.Cancelled
                    , BossConstants.AuditStatus.Rescinded
                    , BossConstants.AuditStatus.Stopped
                    , BossConstants.AuditStatus.Processing).contains(event.getAuditStatus())) {
                log.warn("auditStatus can't be " + event.getAuditStatus().name());
                return;
            }
        }

        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByReferenceId(event.getEventId());

        if (csWorkOrderDO == null) {
            log.warn("CSWorkOrderDO can't be null!");
            return;
        }

        //客服工单是否需要撤销
        //撤销条件:
        //1、风控工单不为被领取状态,并且预审工单不为结案和撤销状态
        //2、风控工单为领取状态,并且预审工单为新建状态
        boolean shouldBeCancelled = !BossConstants.AuditStatus.Processing.equals(event.getAuditStatus()) && csWorkOrderDO.getStatus() != CSWorkOrderStatus.Completed && csWorkOrderDO.getStatus() != CSWorkOrderStatus.Cancelled
                || BossConstants.AuditStatus.Processing.equals(event.getAuditStatus()) && CSWorkOrderStatus.Created.equals(csWorkOrderDO.getStatus());

        if (shouldBeCancelled) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Cancelled);
            CSWorkOrderOpLogDO csWorkOrderOpLogDO = buildOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "首次认证预审撤销工单");

            //需同步撤销的加急工单
            List<CSWorkOrderDO> expediteWorkOrders = org.assertj.core.util.Lists.newArrayList();
            if (ExpediteType.ORDINARY_EXPEDITE.equals(csWorkOrderDO.getExpediteType())) {
                Criteria expediteConfigCriteria = Criteria.where("businessType").is(BusinessType.CUSTOMER_CONSULTATION)
                        .and("workorderType").is("其他")
                        .and("problemType").is("加急")
                        .and("status").is(CSWorkOrderConfigStatus.Enabled);
                CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.findOne(new Query().addCriteria(expediteConfigCriteria));
                if (null == csWorkOrderConfigDO) {
                    throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.NOT_SUPPORT, "工单自动撤销失败, 未找到加急工单类型");
                }
                Criteria expediteWorkOrderCriteria = Criteria.where("workOrderConfigId").is(csWorkOrderConfigDO.getConfigId())
                        .and("relatedWorkOrderId").is(csWorkOrderDO.getWorkorderId())
                        .and("relatedWorkOrderDomain").is(WorkOrderDomain.CS_WORK_ORDER)
                        .and("status").nin(finalCSWorkOrderStatus);
                expediteWorkOrders.addAll(csWorkOrderDAO.find(new Query().addCriteria(expediteWorkOrderCriteria)));
            }

            Boolean isOk = mongoTransactionTemplate.execute(status -> {

                try {
                    if (csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                    }

                    if (csWorkOrderOpLogDAO.insert(csWorkOrderOpLogDO) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert oplog fail.");
                    }

                    //撤销加急工单
                    if (!CollectionUtils.isEmpty(expediteWorkOrders)) {
                        for (CSWorkOrderDO expediteWorkOrder : expediteWorkOrders) {
                            expediteWorkOrder.setStatus(CSWorkOrderStatus.Cancelled);
                            CSWorkOrderOpLogDO expediteOpLogDO = buildOpLogDO(expediteWorkOrder.getWorkorderId(), expediteWorkOrder.getStatus(), "system", "同步撤销加急工单");
                            if (csWorkOrderDAO.updateByIdSelective(expediteWorkOrder) == null) {
                                throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                            }
                            if (csWorkOrderOpLogDAO.insert(expediteOpLogDO) == null) {
                                throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert oplog fail.");
                            }
                        }
                    }

                    workorderMessageProducer.closeCustomerTag(csWorkOrderDO);
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);
                    status.setRollbackOnly();
                    return Boolean.FALSE;
                }
                return Boolean.TRUE;
            });

            if (Boolean.FALSE.equals(isOk)) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.NOT_SUPPORT, "update fail.");
            }
        }

        workorderMessageProducer.sendPreAuditStatusEvent(csWorkOrderDO);
    }

    @Override
    public void onExpediteWorkOrderComplete(ExpediteWorkOrderCompleteEvent event) {
        if (Strings.isNullOrEmpty(event.getEventId())) {
            log.warn("eventId can't be null!");
            return;
        }
        if (null == event.getDomain()) {
            log.warn("domain can't be null!");
            return;
        }

        if (!WorkOrderDomain.CS_WORK_ORDER.equals(event.getDomain()) && !WorkOrderDomain.RISK_AUDIT_WORK_ORDER.equals(event.getDomain())) {
            log.warn("only cs work order and risk audit work order can use this event");
            return;
        }

        if (WorkOrderDomain.CS_WORK_ORDER.equals(event.getDomain())) {
            CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where("workorderId").is(event.getEventId())));
            if (null == csWorkOrderDO) {
                log.warn("cs work order can't be null!");
                return;
            }
            if (!isPreAuditWorkOrder(csWorkOrderDO)) {
                log.warn("only pre audit cs work order can use this event");
                return;
            }
            if (!CSWorkOrderStatus.Completed.equals(csWorkOrderDO.getStatus())) {
                log.warn("only completed cs work order can use this event");
                return;
            }
        }

        if (WorkOrderDomain.RISK_AUDIT_WORK_ORDER.equals(event.getDomain())) {
            AuditRequestVO auditRequestVO = riskWorkOrderClient.listAuditingByRequestId(event.getEventId());
            if (null == auditRequestVO) {
                log.warn("audit request can't be null!");
                return;
            }
            if (!StringUtils.equals(BossConstants.AuditStatus.Completed.name(), auditRequestVO.getStatus().name())) {
                log.warn("only completed risk audit work order can use this event");
                return;
            }
        }

        Criteria expediteCriteria = Criteria.where("businessType").is(BusinessType.CUSTOMER_CONSULTATION)
                .and("workorderType").is("其他")
                .and("problemType").is("加急")
                .and("status").is(CSWorkOrderConfigStatus.Enabled);
        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.findOne(new Query().addCriteria(expediteCriteria));
        if (null == csWorkOrderConfigDO) {
            log.warn("can not find expedite work order config!");
            return;
        }

        Criteria criteria = Criteria.where("workOrderConfigId").is(csWorkOrderConfigDO.getConfigId())
                .and("relatedWorkOrderId").is(event.getEventId())
                .and("relatedWorkOrderDomain").is(event.getDomain())
                .and("status").nin(finalCSWorkOrderStatus);
        List<CSWorkOrderDO> csWorkOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(criteria));

        for (CSWorkOrderDO csWorkOrderDO : csWorkOrderDOList) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Closed);
            CSWorkOrderOpLogDO csWorkOrderOpLogDO = buildOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "加急工单同步关闭");

            Boolean isOk = mongoTransactionTemplate.execute(status -> {
                try {
                    if (csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) == null) {
                        throw new AttaServiceException(SystemErrorCode.UpdateError, BizErrorCode.SystemError, "update fail.");
                    }

                    if (csWorkOrderOpLogDAO.insert(csWorkOrderOpLogDO) == null) {
                        throw new AttaServiceException(SystemErrorCode.InsertError, BizErrorCode.SystemError, "insert oplog fail.");
                    }
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);
                    status.setRollbackOnly();
                    return Boolean.FALSE;
                }
                return Boolean.TRUE;
            });
            if (!Boolean.TRUE.equals(isOk)) {
                log.error("Close expedite work order fail.WorkOrderId:{}", csWorkOrderDO.getWorkorderId());
            }
        }
    }

    @Override
    public FacadeResponse<CSWorkOrderVO> automaticAllocation(CSWorkOrderAutomaticAllocationRequest request) {
        FacadeResponse facadeResponse = validate(request);
        if (!facadeResponse.isSucceeded()) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR);
        }

        CSWorkOrderDO workOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());

        if (workOrderDO == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR);
        }

        if (!CSWorkOrderStatus.Created.equals(workOrderDO.getStatus())) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR);
        }

        if (!Strings.isNullOrEmpty(workOrderDO.getPrincipalUserId()) || !Strings.isNullOrEmpty(workOrderDO.getCurrentDealUserId())) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR);
        }

        OrderAssignReq assignReq = OrderAssignReq.builder()
                .workOrderId(workOrderDO.getWorkorderId())
                .operatorUserId(SystemConst.SYSTEM_USER_ID)
                .reason("系统分配")
                .toPrincipalUserId(request.getUserId())
                .build();
        try {
            transferPrincipal(assignReq);
        } catch (Exception e) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR);
        }
        return getFacadeResponseWithValue(toWorkOrderVO(workOrderDO));
    }

    @Override
    public Boolean orderSuspend(CSWorkOrderAwakeRequest request) {
        if (0 >= maxSuspendOrderCount) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "不允许暂停工单");
        }
        Assert.isTrue(checkAwakeRequest(request), "work order suspend fail,request is error");
        //校验当前用户名下暂停状态的工单是否达到三个
        Query query = new Query();
        query.addCriteria(Criteria.where("principalUserId").is(request.getUserId()));
        query.addCriteria(Criteria.where(CSWorkOrderDO.STATUS).is(CSWorkOrderStatus.Suspend));
        List<CSWorkOrderDO> workOrderDOS = csWorkOrderDAO.find(query);
        if (!CollectionUtils.isEmpty(workOrderDOS) && workOrderDOS.size() >= maxSuspendOrderCount) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "暂停工单失败,您名下已经存在" + maxSuspendOrderCount + "条暂停中的工单");
        }

        //更新工单状态为暂停
        List<CSWorkOrderOpLogDO> opLogDOS = new LinkedList<>();
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where("workorderId").is(request.getOrderId())));
        if (null == csWorkOrderDO || null == csWorkOrderDO.getStatus() || !processCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "can not find out work order by orderId or the order is not processing");
        }
        csWorkOrderDO.setStatus(CSWorkOrderStatus.Suspend);
        List<CSWorkOrderDO> workOrders = new ArrayList<>();
        workOrders.add(csWorkOrderDO);
        opLogDOS.add(createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), csWorkOrderDO.getCurrentDealUserId(), "暂停工单"));
        FacadeResponse updateResponse = updateWOS(workOrders, opLogDOS);
        return null != updateResponse && updateResponse.isSucceeded();
    }

    @Override
    public Boolean orderAwake(CSWorkOrderAwakeRequest request) {
        Assert.isTrue(checkAwakeRequest(request), "work order awake fail,request is error");
        //当前工单是否是暂停中,更新工单状态为处理中
        List<CSWorkOrderOpLogDO> opLogDOS = new LinkedList<>();
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where("workorderId").is(request.getOrderId())));
        if (null == csWorkOrderDO || null == csWorkOrderDO.getStatus() || !CSWorkOrderStatus.Suspend.equals(csWorkOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "can not find out work order by orderId or the order is not suspended");
        }
        // 说明是升级工单
        if (!StringUtils.isEmpty(csWorkOrderDO.getBeforeUpgradePrincipalId())) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.UpgradeProcessing);
        } else {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
        }

        List<CSWorkOrderDO> workOrders = new ArrayList<>();
        workOrders.add(csWorkOrderDO);
        opLogDOS.add(createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), csWorkOrderDO.getCurrentDealUserId(), "唤醒工单"));
        FacadeResponse updateResponse = updateWOS(workOrders, opLogDOS);
        //唤醒工单后需要把当前工单负责人移出队列
        removeUser(request.getUserId());
        return null != updateResponse && updateResponse.isSucceeded();
    }

    private boolean checkAwakeRequest(CSWorkOrderAwakeRequest request) {
        if (null == request) {
            return false;
        }
        if (StringUtils.isEmpty(request.getOrderId())) {
            return false;
        }
        if (StringUtils.isEmpty(request.getUserId())) {
            return false;
        }
        return null != request.getDomain();
    }


    @Override
    public String getSurveyQuestionnaireId() {
        return questionnaireId;
    }

    @Override
    public void upgradeWorkOrder(WorkOrderUpgradeReq workOrderUpgradeReq) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderUpgradeReq.getWorkOrderId());
        CSExceptionAssert.isTrue(csWorkOrderDO != null, CSOrderBizErrorCode.VALIDATION_ERROR, "工单不存在");
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InternalError, CSOrderBizErrorCode.VALIDATION_ERROR, "工单已关闭,不允许升级");
        }
        CSExceptionAssert.isTrue(!StringUtils.isEmpty(csWorkOrderDO.getPrincipalUserId())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "非处理中工单,不能升级");
        if (StringUtils.isEmpty(primaryGroup)) {
            log.error("primaryGroup is empty");
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "非一线人员,不能升级工单");
        }
        if(!OrderLevel.OL2.equals(csWorkOrderDO.getOrderLevel())){
            BossUserInGroupReq bossUserInGroupReq = BossUserInGroupReq.builder()
                    .bossUserId(workOrderUpgradeReq.getOperatorUserId())
                    .groupIds(Sets.newHashSet(primaryGroup))
                    .build();
            FacadeResponse<Boolean> facadeResponse = bossUserGroupClient.bossUserInSpecifiedGroup(bossUserInGroupReq);
            CSExceptionAssert.isTrue(facadeResponse.isSucceeded()
                    , CSOrderBizErrorCode.SYSTEM_ERROR, "系统内部错误,请重试");
            CSExceptionAssert.isTrue(Boolean.TRUE.equals(facadeResponse.getValue())
                    , CSOrderBizErrorCode.SYSTEM_ERROR, "非一线人员,不能升级工单");
        }
        String beforeUpgradePrincipalId = csWorkOrderDO.getPrincipalUserId();
        WorkOrderUpdateReq workOrderUpdateReq = WorkOrderUpdateReq.builder()
                .workOrderId(csWorkOrderDO.getWorkorderId())
                .principalUserId("")
                .beforeUpdatePrincipalUserId(StringUtils.isBlank(csWorkOrderDO.getBeforeUpgradePrincipalId()) ? beforeUpgradePrincipalId : "")
                .operatorUserId(workOrderUpgradeReq.getOperatorUserId())
                .status(CSWorkOrderStatus.UpgradePending)
                .orderLevel(OrderLevel.OL2)
                .upgradeSource(workOrderUpgradeReq.getUpgradeSource())
                .upgradeFlag(true)
                .build();
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        OpLogAddRequest request = OpLogAddRequest.builder()
                .workOrderId(workOrderUpgradeReq.getWorkOrderId())
                .suggestion("升级工单")
                .status(workOrderUpdateReq.getStatus())
                .operatorUserId(workOrderUpgradeReq.getOperatorUserId())
                .build();
        csWorkOrderLogService.insertOpLog(commanders, request);
        upgradeOrBackWorkOrder(commanders, workOrderUpdateReq, true);
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
        } catch (Exception e) {
            log.error("upgradeWorkOrder failed. e is:", e);
        }
//        移除原来的升级自动分配
//        CompletableFuture.runAsync(() -> {
//            try {
//                SecondGroupOrderAssignJob.assignOL2WorkOrder(csWorkOrderDO.getWorkorderId());
//            } catch (Exception e) {
//                log.error("assignOL2WorkOrder failed. e is: ", e);
//            }
//        });
        //上报工单升级 sla
        slaEscalationService.escalationUpgradeWorkOrderSla(csWorkOrderDO);
        consultProgressPushService.upgradeWorkOrder(csWorkOrderDO);
    }

    @Override
    public void backWorkOrder(WorkOrderBackReq req) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(req.getWorkOrderId());
        CSExceptionAssert.isTrue(csWorkOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR, "工单不存在");
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.InternalError
                    , CSOrderBizErrorCode.VALIDATION_ERROR, "工单已关闭,不允许退回");
        }
        if (csWorkOrderDO.getStatus() != CSWorkOrderStatus.UpgradeProcessing || StringUtils.isEmpty(csWorkOrderDO.getBeforeUpgradePrincipalId())) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR
                    , "不是升级工单,不能退回");
        }

        CSExceptionAssert.isTrue(!StringUtils.isEmpty(csWorkOrderDO.getPrincipalUserId())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "工单无负责人,不能退回");
        if (!req.getOperatorUserId().equals(csWorkOrderDO.getPrincipalUserId())) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR
                    , "非工单负责人,不能退回工单");
        }

        WorkOrderUpdateReq workOrderUpdateReq = WorkOrderUpdateReq.builder()
                .workOrderId(csWorkOrderDO.getWorkorderId())
                .beforeUpdatePrincipalUserId(handleUpdatePrincipalUserId(csWorkOrderDO))
                .operatorUserId(req.getOperatorUserId())
                .status(CSWorkOrderStatus.Processing)
                .principalUserId(csWorkOrderDO.getBeforeUpgradePrincipalId())
                .orderLevel(OrderLevel.OL1)
                .build();
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        OpLogAddRequest request = OpLogAddRequest.builder()
                .workOrderId(req.getWorkOrderId())
                .suggestion("工单退回")
                .status(CSWorkOrderStatus.Processing)
                .operatorUserId(req.getOperatorUserId())
                .build();
        csWorkOrderLogService.insertOpLog(commanders, request);
        upgradeOrBackWorkOrder(commanders, workOrderUpdateReq, false);
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
        } catch (Exception e) {
            log.error("upgradeWorkOrder failed. e is:", e);
            return;
        }
        CompletableFuture.runAsync(() -> {
            try {
                notify(csWorkOrderDO.getBeforeUpgradePrincipalId(), csWorkOrderDO.getWorkorderId());
            } catch (Exception e) {
                log.error("notify failed. e is: ", e);
            }
        }, commonExecutor);
        slaEscalationService.escalationBackWorkOrderSla(csWorkOrderDO, csWorkOrderDO.getBeforeUpgradePrincipalId(), req.getOperatorUserId());
    }

    private String handleUpdatePrincipalUserId(CSWorkOrderDO csWorkOrderDO) {
        // 只有回退到最后一层级的时候才置空
        if (CollectionUtils.isEmpty(csWorkOrderDO.getUpgradeLabelList()) || csWorkOrderDO.getUpgradeLabelList().size() == LABEL_LIST_SIZE_ONE) {
            return "";
        }
        return csWorkOrderDO.getBeforeUpgradePrincipalId();

    }

    /**
     * 查询客服意见反馈工单附件
     *
     * @param workOrderId 工单id
     * @return voList 对于2022/03/11版本的客服意见反馈工单,listSize应当为1
     * @throws CustomerServiceException case1:工单不存在  ;case2:通过formAnswer查询formAnswer不存在
     */
    @Override
    public List<CSWorkOrderFeedbackAttachmentVO> queryWorkOrderFeedbackAttachment(String workOrderId) {
        //查询工单,获取businessInfo.bizId,bizId即crm_form_answer_whole.formAnswerId
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
        if (csWorkOrderDO == null) {
//            log.error("csWorkOrder does not exist,workOrderId for query:{}", workOrderId);
//            throw new MktServiceException(RecordNotFoundError, NOT_FOUND, "客服工单不存在");
            return Lists.newArrayList();
        }
        List<BusinessInfoDTO> businessInfos = csWorkOrderDO.getBusinessInfos();
        List<CSWorkOrderFeedbackAttachmentVO> attachmentsList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(businessInfos)) {
            for (BusinessInfoDTO businessInfo : businessInfos) {
                if (Objects.equals(businessInfo.getType(), BusinessInfoType.FeedbackWorkOrderAttachment)) {
                    FacadeResponse<FormAnswerVO> rsp = formServAnswerClient.getByFormAnswerId(businessInfo.getBizId());
                    if (rsp == null || !rsp.isSucceeded() || rsp.getValue() == null || CollectionUtils.isEmpty(rsp.getValue().getAnswers())) {
                        log.error("formAnswer does not exist,can not query attachments,workOrderId:{};formAnswerId:{}", workOrderId, businessInfo.getBizId());
                        throw new CustomerServiceException(CSOrderSystemErrorCode.RecordNotFoundError, CSOrderBizErrorCode.NOT_FOUND, "查询失败,附件不存在");
                    }
                    CSWorkOrderFeedbackAttachmentVO vo = toCSWorkOrderFeedbackAttachmentVO(rsp.getValue());
                    if (vo != null) {
                        attachmentsList.add(vo);
                    }
                }
            }
        }
        return attachmentsList;
    }

    @Override
    public RecordTemplateVO queryRecordTemplateDetail(String recordTemplateId) {
        RecordTemplateDO recordTemplateDO = csWorkOrderRecordTemplateDAO.findByTemplateId(recordTemplateId);
        if (recordTemplateDO != null) {
            RecordTemplateVO recordTemplateVO = WorkOrderConverter.toRecordTemplateVO(recordTemplateDO);
            if (StringUtils.isNotEmpty(recordTemplateDO.getFormId())) {
                FacadeResponse<FormVO> facadeResponse = formServClient.queryDetails(recordTemplateDO.getFormId());
                if (facadeResponse.isSucceeded() && null != facadeResponse.getValue()) {
                    recordTemplateVO.setFormVO(facadeResponse.getValue());
                }
            }
            return recordTemplateVO;
        }
        return null;
    }

    @Override
    public List<ConsultingOrderVO> queryConsultingOrder(ConsultingOrderRequest request) {
        return queryPageConsultingOrder(request).getList();
    }

    @Override
    public AppointmentResultVO createAppointment(AppointmentCreateRequest request) {
        if (StringUtils.isNotEmpty(request.getRemark())) {
            CSExceptionAssert.isTrue(request.getRemark().length() <= remarkLength
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "留言不能超过 " + remarkLength + "字");
        }
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());

        AppointmentResultVO resultVO = isAllowed(csWorkOrderDO);
        if (!resultVO.getIsSuccess()) {
            return resultVO;
        }
        List<DataPersistenceCommander> commanders = new ArrayList<>();

        // 新增处理记录
        String content = StringUtils.isEmpty(request.getRemark()) ? "客户预约回电。" : "客户预约回电,留言:" + request.getRemark();
        ComRecordAddRequest addRequest = ComRecordAddRequest.builder()
                .workOrderId(csWorkOrderDO.getWorkorderId())
                .content(content)
                .operatorUserId(request.getOperatorUserId())
                .operatorType(request.getOperatorType())
                .build();
        String recordId = commRecordService.insertCommRecord(commanders, addRequest);

        // 新增操作日志
        OpLogAddRequest opLogAddRequest = OpLogAddRequest.builder()
                .workOrderId(request.getWorkOrderId())
                .commRecordId(recordId)
                .operatorUserId(request.getOperatorUserId())
                .status(csWorkOrderDO.getStatus())
                .suggestion(content)
                .operatorType(request.getOperatorType())
                .build();
        csWorkOrderLogService.insertOpLog(commanders, opLogAddRequest);

        InstructionAddRequest instructionAddRequest = InstructionAddRequest.builder()
                .associatedReferenceId(recordId)
                .type(WorkOrderInstructionType.CustomerAppointment)
                .referenceId(request.getWorkOrderId())
                .build();
        cSWorkOrderInstructionService.addInstructions(commanders, instructionAddRequest);

        transactionLazyExecutor.executeCommanders(commanders);

        CompletableFuture.runAsync(() -> {
            try {
                cSWorkOrderInstructionService.doUnSuccessStatusInstruction(request.getWorkOrderId());
            } catch (Exception e) {
                log.error("doUnSuccessStatusInstruction failed. e is: ", e);
            }
        }, commonExecutor);
        resultVO.setIsSuccess(true);

        //上报sla数据
        slaEscalationService.escalationUserOpSla(csWorkOrderDO);
        consultProgressPushService.userOpSubmitAppointment(csWorkOrderDO, request.getRemark());
        return resultVO;
    }

    @Override
    public List<CSWorkOrderResultVO> queryByFirmIdAndWorkOrderType(WorkOrderQueryRequest request) {
        String configId = cSWorkOrderConfigService.getConfigIdByConfigType(request.getWorkOrderType());
        Assert.isTrue(StringUtils.isNotEmpty(configId), "workOrderType: " + request.getWorkOrderType() + " is valid");

        List<CSWorkOrderDO> workOrderDOS = csWorkOrderDAO.queryByFirmIdAndWorkOrderType(request.getFirmId(), configId);
        if (CollectionUtils.isEmpty(workOrderDOS)) {
            return new ArrayList<>();
        }
        return workOrderDOS.stream().map(this::resultConvert).collect(Collectors.toList());
    }

    @Override
    public List<ConsultingTypeVO> getConsultingType() {
        List<ConsultingTypeVO> consultingTypesVOS = new ArrayList<>();
        if (consultingTypeMap == null) {
            return consultingTypesVOS;
        }

        consultingTypeMap.forEach((key, value) -> {
            ConsultingTypeVO consultingTypesVO = new ConsultingTypeVO();
            consultingTypesVO.setConsultingType(key);
            consultingTypesVO.setConsultingTypeDesc(value);
            consultingTypesVOS.add(consultingTypesVO);
        });
        return consultingTypesVOS;
    }

    @Override
    public CSWorkOrderVO queryOneWorkOrderByAssignId(String assignId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where(CSWorkOrderDO.ASSIGN_ID).is(assignId)));
        return Optional.ofNullable(csWorkOrderDO).map(order -> {
            CSWorkOrderVO csWorkOrderVO = toWorkOrderVO(order);
            csWorkOrderVO.setHistoryPreAuditWorkOrderVOList(getHistoryPreAuditWorkOrders(order));
//            csWorkOrderVO.setPartnerBindVO(getPreAuditWorkOrderPartnerInfo(order));
//            csWorkOrderVO.setBusinessBindVO(getExpeditedWorkOrderBusinessInfo(order));
            return csWorkOrderVO;
        }).orElse(null);
    }

    private CSWorkOrderVO queryOneWorkOrderByCallCenterId(String callCenterId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where(CSWorkOrderDO.CALL_CENTER_ID).is(callCenterId)));
        return Optional.ofNullable(csWorkOrderDO).map(order -> {
            CSWorkOrderVO csWorkOrderVO = toWorkOrderVO(order);
            csWorkOrderVO.setHistoryPreAuditWorkOrderVOList(getHistoryPreAuditWorkOrders(order));
//            csWorkOrderVO.setPartnerBindVO(getPreAuditWorkOrderPartnerInfo(order));
//            csWorkOrderVO.setBusinessBindVO(getExpeditedWorkOrderBusinessInfo(order));
            return csWorkOrderVO;
        }).orElse(null);
    }


    @Override
    public CSWorkOrderVO queryLastOne(QueryLastWorkOrderRequest request) {
        ConfigQueryByTypeRequest typeRequest = ConfigQueryByTypeRequest.builder()
                .businessType(request.getBusinessType())
                .workOrderType(request.getWorkorderType())
                .problemType(request.getProblemType())
                .build();

        CSWorkorderConfigVO csWorkorderConfigVO = cSWorkOrderConfigService.getWorkOrderConfigVO(typeRequest);

        if (null == csWorkorderConfigVO) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "can not find work order config by request");
        }

        Query query = new Query();
        Criteria criteria = Criteria.where(CSWorkOrderDO.FIRM_ID).is(request.getFirmId())
                .and(CSWorkOrderDO.WORK_ORDER_CONFIG_ID).is(csWorkorderConfigVO.getTypeId());
        query.addCriteria(criteria);
        query.with(Sort.by(Sort.Direction.DESC, CSWorkOrderDO.CREATION_DATE)).limit(1);

        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(query);
        CSWorkOrderVO csWorkOrderVO = toWorkOrderVO(csWorkOrderDO);
        if (Objects.isNull(csWorkOrderVO)) {
            return null;
        }

        csWorkOrderVO.setHistoryPreAuditWorkOrderVOList(getHistoryPreAuditWorkOrders(csWorkOrderDO));
//        csWorkOrderVO.setPartnerBindVO(getPreAuditWorkOrderPartnerInfo(csWorkOrderDO));
//        csWorkOrderVO.setBusinessBindVO(getExpeditedWorkOrderBusinessInfo(csWorkOrderDO));
        return csWorkOrderVO;
    }

    @Override
    public void csWorkOrderRelateFirmInfo(String csWorkOrderId, String firmId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(csWorkOrderId);
        if (Objects.isNull(csWorkOrderDO)) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR,"工单不存在");
        }
        if (CSWorkOrderStatus.endStatusList().contains(csWorkOrderDO.getStatus())) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR, "工单当前状态不允许关联");
        }
        if (!csWorkOrderDO.isCanRelateFirmInfo()) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.BadRequest, CSOrderBizErrorCode.SYSTEM_ERROR, "该工单不支持关联企业信息");
        }
        WorkOrderConverter.setBasicFirmInfo(csWorkOrderDO, null, firmId, firmClient, userClient);
        csWorkOrderDO.setCanRelateFirmInfo(false);
        Assert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null, "工单关联企业信息失败");
    }

    @Override
    public PageSerializable<CSWorkOrderVO> querySaleSessionWorkOrderPageList(CSSaleWorkOrderSessionRequest request) {
        Page<CSWorkOrderDO> page = csWorkOrderDAO.querySaleSessionWorkOrderPageList(request.getBossUserId(), request.getMixId(), request.getMixName(), request.getMixIdAndName(),
                request.getFromDate(), request.getPage(), request.getSize());
        PageSerializable<CSWorkOrderVO> result = new PageSerializable<>();
        result.setTotal(page.getTotal());
        result.setList(Lists.newArrayList());
        if (!CollectionUtils.isEmpty(page.getResult())) {
            result.setList(page.getResult().stream().map(this::toWorkOrderVO).collect(Collectors.toList()));
        }
        return result;
    }

    @Override
    public void workOrderBindSaleInfo(BindSaleInfoRequest request) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());
        Assert.notNull(csWorkOrderDO, "工单不存在");
        if (StringUtils.isNotBlank(csWorkOrderDO.getSaleBossUserId())) {
            return;
        }
        User user = bossUserClient.queryByBossUserId(request.getSaleBossUserId());
        Assert.notNull(user, "销售不存在");
        csWorkOrderDO.setSaleBossUserId(user.getBossUserId());
        csWorkOrderDO.setSaleUserName(user.getUserName());
        Assert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null, "工单绑定销售信息失败");
        consultProgressPushService.sendSaleIdChange(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getSaleBossUserId());
    }

    @Override
    public CSWorkOrderVO queryOneWorkOrderByAssignIdAndUseScene(String assignId, String useScene) {
        switch (useScene) {
            case "IM":
                return queryOneWorkOrderByAssignId(assignId);
            case "CALL_CENTER":
                return queryOneWorkOrderByCallCenterId(assignId);
            default:
                return null;
        }
    }

    @Override
    public PageSerializable<ConsultingOrderVO> queryPageConsultingOrder(ConsultingOrderRequest request) {
        List<CSWorkOrderDO> csWorkOrderDOS = csWorkOrderDAO.queryConsultingOrder(request, consultWorkOrderLimit, preDayLimit);
        if (CollectionUtils.isEmpty(csWorkOrderDOS)) {
            return new PageSerializable<>();
        }
        // 先按创建时间降序排
        csWorkOrderDOS = csWorkOrderDOS.stream().sorted(Comparator.comparing(CSWorkOrderDO::getCreationDate).reversed()).collect(Collectors.toList());

        List<CSWorkOrderDO> processingDOS = csWorkOrderDOS.stream()
                .filter((csWorkOrderDO) -> processCSWorkOrderStatus.contains(csWorkOrderDO.getStatus()))
                .collect(Collectors.toList());
        List<CSWorkOrderDO> notProcessingDOS = csWorkOrderDOS.stream()
                .filter((csWorkOrderDO) -> !processCSWorkOrderStatus.contains(csWorkOrderDO.getStatus()))
                .collect(Collectors.toList());
        List<CSWorkOrderDO> finalDOS = new ArrayList<>();
        finalDOS.addAll(processingDOS);
        finalDOS.addAll(notProcessingDOS);
        int startIndex = (request.getPage() - 1) * request.getSize();
        int endIndex = (request.getPage()) * request.getSize();
        if (endIndex > finalDOS.size()) {
            endIndex = finalDOS.size();
        }
        List<CSWorkOrderDO> allowedDOS = finalDOS.subList(startIndex, endIndex);
        if (CollectionUtils.isEmpty(allowedDOS)) {
            return new PageSerializable<>();
        }
        List<ConsultingOrderVO> allowedVOS = new ArrayList<>();
        for (CSWorkOrderDO csWorkOrderDO : allowedDOS) {
            ConsultingOrderVO orderVO = WorkOrderConverter.workOrderDO2ConsultingVO(csWorkOrderDO, consultingTypeMap);
            AppointmentResultVO resultVO = isAllowed(csWorkOrderDO);
            orderVO.setAllowedAppointment(resultVO.getIsSuccess());
            if (StringUtils.isEmpty(csWorkOrderDO.getPrincipalUserId())) {
                orderVO.setAllowedAppointment(false);
            }
            allowedVOS.add(orderVO);
        }
        PageSerializable<ConsultingOrderVO> result = new PageSerializable<>();
        result.setList(allowedVOS);
        result.setTotal(csWorkOrderDOS.size());
        return result;
    }

    @Override
    public Boolean existWorkOrderByAssIngId(String assignId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(new Query().addCriteria(Criteria.where(CSWorkOrderDO.ASSIGN_ID).is(assignId)));
        return Objects.nonNull(csWorkOrderDO);
    }

    private void currentWorkOrderConfigIsUsable(String workorderTypeId, String operatorUserId) {
        CSWorkorderConfigVO configVO = cSWorkOrderConfigService.getValidConfigVOByConfigId(workorderTypeId);
        CSExceptionAssert.isTrue(configVO != null, CSOrderBizErrorCode.VALIDATION_ERROR, "当前工单类型不存在或不可用");
        if (configVO.getCreateType() == CreateType.Auto && !SystemConst.SYSTEM_USER_ID.equals(operatorUserId)) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "当前工单类型不可手动创建");
        }
        if (configVO.getBusinessType() == BusinessType.CUSTOMER_MANAGEMENT_SERVICE) {
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "不支持创建" + BusinessType.CUSTOMER_MANAGEMENT_SERVICE.getDesc() + "类型工单");
        }
    }

    private void checkWorkOrderTrackWay(CSWorkOrderCreateRequest request) {
        if (Objects.isNull(request)) {
            throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, "参数不能为空");
        }
        if (Objects.equals(WorkOrderTrackWay.EMAIL.name(), request.getWorkOrderTrackWay())) {
            CSExceptionAssert.isTrue(Objects.nonNull(request.getCsWorkOrderEmailRequest())
                    , CSOrderBizErrorCode.VALIDATION_ERROR
                    , "邮件信息为空");
            ValidateResult validate = ValidateUtil.validate(request.getCsWorkOrderEmailRequest());
            if (!validate.isPassed()) {
                throw new CustomerServiceException(CSOrderSystemErrorCode.ValidationError, CSOrderBizErrorCode.ILLEGAL_ARGUMENT, validate.getReason());
            }
        }
    }

    private String getWorkOrderId() {
        return AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER);
    }

    private CSWorkOrderDO insertWorkOrder(WorkOrderAddRequest request) {
        CSWorkOrderDO csWorkOrderDO = CSWorkOrderDO.builder()
                .workorderId(getWorkOrderId())
                .workOrderConfigId(request.getWorkOrderConfigId())
                .currentDealUserId(request.getCurrentDealUserId())
                .firmId(request.getFirmId())
                .firmName(request.getFirmName())
                .email(request.getEmail())
                .source(request.getSource())
                .userId(request.getUserId())
                .feedbackTime(request.getFeedbackTime())
                .generatedReason(request.getGeneratedReason())
                .workorderEndTime(request.getWorkOrderEndTime())
                .invitationCode(request.getInvitationCode())
                .phone(request.getPhone())
                .lastFollowedTime(request.getLastFollowedTime())
                .principalUserId(request.getPrincipalUserId())
                .referenceId(request.getReferenceId())
                .orderLevel(request.getOrderLevel())
                .vaAccount(request.getVaAccount())
                .currency(request.getCurrency())
                .amount(request.getAmount())
                .serviceProvider(request.getServiceProvider())
                .canRelateFirmInfo(false)
                .newCollaboration(CsOrderConstants.newCollaboration)
                .build();
        if (StringUtils.isNotBlank(request.getPrincipalUserId())) {
            csWorkOrderDO.setAllocateTime(new Date());
        }
        // 如果是人工新建的工单
        if (!SystemConst.SYSTEM_USER_ID.equals(request.getCreateUserId())) {
            csWorkOrderDO.setLastFollowedTime(new Date());
        }
        if (!CollectionUtils.isEmpty(request.getBusinessInfoVOList())) {
            csWorkOrderDO.setBusinessInfos(request.getBusinessInfoVOList().stream()
                    .map(WorkOrderConverter::businessInfoVO2DO).collect(Collectors.toList()));
        }
        if (csWorkOrderDO.getHistoryDealUsers() == null || CollectionUtils.isEmpty(csWorkOrderDO.getHistoryDealUsers())) {
            if (!StringUtils.isEmpty(request.getCurrentDealUserId())) {
                csWorkOrderDO.setHistoryDealUsers(Sets.newHashSet(request.getCurrentDealUserId()));
            }
        } else {
            csWorkOrderDO.getHistoryDealUsers().add(request.getCurrentDealUserId());
        }
        WorkOrderConverter.setCSOrderDOFromOrderConfig(csWorkOrderDO, request.getWorkOrderConfigId(), cSWorkOrderConfigService);
        WorkOrderConverter.setBasicFirmInfo(csWorkOrderDO, request.getUserId(), request.getFirmId(), firmClient, userClient);
        setOrderLevel(csWorkOrderDO, request.getPrincipalUserId());
        csWorkOrderDO.setCreatedBy(request.getCreateUserId());

        if (StringUtils.isEmpty(request.getPrincipalUserId()) || SystemConst.SYSTEM_USER_ID.equals(request.getPrincipalUserId())) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Created);
        } else {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
        }
        return csWorkOrderDO;
    }

    private CSWorkOrderDO insertWorkOrder(List<DataPersistenceCommander> commanders, WorkOrderAddRequest request) {
        CSWorkOrderDO csWorkOrderDO = CSWorkOrderDO.builder()
                .workorderId(getWorkOrderId())
                .workOrderConfigId(request.getWorkOrderConfigId())
                .sceneConfigId(request.getSceneConfigId())
                .problemScene(request.getProblemScene())
                .currentDealUserId(request.getCurrentDealUserId())
                .firmId(request.getFirmId())
                .firmName(request.getFirmName())
                .email(request.getEmail())
                .source(request.getSource())
                .userId(request.getUserId())
                .feedbackTime(request.getFeedbackTime())
                .generatedReason(request.getGeneratedReason())
                .workorderEndTime(request.getWorkOrderEndTime())
                .invitationCode(request.getInvitationCode())
                .phone(request.getPhone())
                .lastFollowedTime(request.getLastFollowedTime())
                .principalUserId(request.getPrincipalUserId())
                .referenceId(request.getReferenceId())
                .orderLevel(request.getOrderLevel())
                .vaAccount(request.getVaAccount())
                .currency(request.getCurrency())
                .amount(request.getAmount())
                .serviceProvider(request.getServiceProvider())
                .canRelateFirmInfo(false)
                .phoneAreaCode(request.getPhoneAreaCode())
                .newPhoneAreaCode(request.getNewPhoneAreaCode())
                .saleBossUserId(request.getSaleBossUserId())
                .saleUserName(request.getSaleBossUserName())
                .site(request.getSite())
                .serviceObject(request.getServiceObject())
                .callCenterId(request.getCallCenterId())
                .newCollaboration(CsOrderConstants.newCollaboration)
                .skylarkCollaborationId(request.getSkylarkCollaborationId())
                .accountType(request.getAccountType())
                .userAccountId(request.getUserAccountId())
                .accountName(request.getAccountName())
                .customerShowSwitch(StringUtils.isEmpty(request.getCustomerShowSwitch()) ? CsWoCustomerShowSwitchEnum.CLOSE.name() : request.getCustomerShowSwitch())
                .receiptTradeId(request.getReceiptTradeId())
                .majorClient(false)
                .consultTopic(request.getConsultTopic())
                .build();
        if (StringUtils.isNotBlank(request.getPrincipalUserId())) {
            csWorkOrderDO.setAllocateTime(new Date());
        }
        if (StringUtils.isNotEmpty(request.getAssignId())) {
            csWorkOrderDO.setAssignId(request.getAssignId());
        }
        // 如果是人工新建的工单
        if (!SystemConst.SYSTEM_USER_ID.equals(request.getCreateUserId())) {
            csWorkOrderDO.setLastFollowedTime(new Date());
        }
        if (!CollectionUtils.isEmpty(request.getBusinessInfoVOList())) {
            csWorkOrderDO.setBusinessInfos(request.getBusinessInfoVOList().stream()
                    .map(WorkOrderConverter::businessInfoVO2DO).collect(Collectors.toList()));
        }
        if (csWorkOrderDO.getHistoryDealUsers() == null || CollectionUtils.isEmpty(csWorkOrderDO.getHistoryDealUsers())) {
            if (!StringUtils.isEmpty(request.getCurrentDealUserId())) {
                csWorkOrderDO.setHistoryDealUsers(Sets.newHashSet(request.getCurrentDealUserId()));
            }
        } else {
            csWorkOrderDO.getHistoryDealUsers().add(request.getCurrentDealUserId());
        }
        WorkOrderConverter.setCSOrderDOFromOrderConfig(csWorkOrderDO, request.getWorkOrderConfigId(), cSWorkOrderConfigService);
        WorkOrderConverter.setBasicFirmInfo(csWorkOrderDO, request.getUserId(), request.getFirmId(), firmClient, userClient);
        setOrderLevel(csWorkOrderDO, request.getPrincipalUserId());
        csWorkOrderDO.setCreatedBy(request.getCreateUserId());

        // 打标升级标签
        allocateHandleService.setUpgradeLabelBySource(request, csWorkOrderDO);

        if (StringUtils.isNotBlank(csWorkOrderDO.getUpgradeLabel()) && StringUtils.isEmpty(request.getPrincipalUserId())) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.UpgradePending);
        } else if (StringUtils.isEmpty(request.getPrincipalUserId()) || SystemConst.SYSTEM_USER_ID.equals(request.getPrincipalUserId())) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Created);
        } else if (CSWorkOrderUpgradeSource.SENIOR_CREATE_UPGRADE.name().equals(request.getUpgradeSource())) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.UpgradeProcessing);
        } else {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
        }
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderDAO.insert(csWorkOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert workOrderDO failed.");
        });
        return csWorkOrderDO;

    }

    private void setOrderLevel(CSWorkOrderDO csWorkOrderDO, String principalUserId) {

        if (StringUtils.isEmpty(principalUserId)) {
            return;
        }
        BossUserInGroupReq bossUserInSecondGroupReq = BossUserInGroupReq.builder()
                .groupIds(Sets.newHashSet(csOrderConfig.secondGroup))
                .bossUserId(principalUserId)
                .build();
        FacadeResponse<Boolean> secondGroupFacadeResponse = bossUserGroupClient.bossUserInSpecifiedGroup(bossUserInSecondGroupReq);
        CSExceptionAssert.isTrue(secondGroupFacadeResponse.isSucceeded(), CSOrderBizErrorCode.SYSTEM_ERROR, "系统内部错误");
        if (Boolean.TRUE.equals(secondGroupFacadeResponse.getValue())) {
            csWorkOrderDO.setOrderLevel(OrderLevel.OL2);
            return;
        }

        BossUserInGroupReq bossUserInPrimaryGroupReq = BossUserInGroupReq.builder()
                .groupIds(Sets.newHashSet(csOrderConfig.primaryGroup))
                .bossUserId(principalUserId)
                .build();
        FacadeResponse<Boolean> facadeResponse = bossUserGroupClient.bossUserInSpecifiedGroup(bossUserInPrimaryGroupReq);
        CSExceptionAssert.isTrue(facadeResponse.isSucceeded(), CSOrderBizErrorCode.SYSTEM_ERROR, "系统内部错误");
        if (Boolean.TRUE.equals(facadeResponse.getValue())) {
            csWorkOrderDO.setOrderLevel(OrderLevel.OL1);
        }
    }

    private OpLogAddRequest buildOpLogAddRequest(String workOrderId, CSWorkOrderStatus status, String operatorUserId, String suggestion) {

        OpLogAddRequest opLogRequest = OpLogAddRequest.builder()
                .workOrderId(workOrderId)
                .operatorUserId(operatorUserId)
                .status(status)
                .suggestion(suggestion)
                .build();

        return opLogRequest;
    }

    private boolean sendWorkOrderEmail(CSWorkOrderEmailRequest request, String bizId, String workOrderConfigId) {
        if (request == null) {
            return false;
        }
        String emailAcctId = emailWorkOrderEmailAccountMap.get(request.getSender());
        Assert.isTrue(StringUtils.isNotBlank(emailAcctId), "邮件账号不存在");
        String content = buryingPointContent(request.getContent(), bizId);
        EmailSendReq emailSendReq = EmailSendReq.builder()
                .emailAcctId(emailAcctId)
                .contentTemplate(DEFAULT_MESSAGE_TEMPLATE)
                .separate(request.isSeparate())
                .subject(request.getSubject())
                .replyTo(Optional.ofNullable(request.getReplyTo()).map(String::trim).orElse(""))
                .to(request.getTo())
                .cc(request.getCc())
                .bcc(request.getBcc())
                .params(
                        ImmutableMap.of("content", content)
                )
                .sendRequestFrom(EmailSendRequestFrom.USER.name())
                .attachments(Lists.newArrayList())
                .build();
        if (!CollectionUtils.isEmpty(request.getAttachmentRequests())) {
            emailSendReq.setAttachments(request.getAttachmentRequests().stream().map(item -> {
                EmailSendReq.Attachment attachment = new EmailSendReq.Attachment();
                attachment.setDisposition(item.getDisposition());
                attachment.setFilename(item.getFilename());
                attachment.setStorageUrl(item.getStorageUrl());
                attachment.setOriginFileId(item.getOriginFileId());
                attachment.setOriginParentFileId(item.getOriginParentFileId());
                return attachment;
            }).collect(Collectors.toList()));
        }
        FacadeResponse<Void> response = emailSendClient.send(emailSendReq);
        return response.isSucceeded();
    }

    private String buryingPointContent(String content, String bizId) {
        content = String.format("<div id=\"csWorkOderBizId\" style=\"display: none\" data-csWorkOderBizId=\"%s\"></div>%s", bizId, content);
        return "<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "</head>\n" +
                "<body>\n" +
                "  <div id=\"content\">" +
                content +
                "  </div>\n" +
                "</body>\n" +
                "</html>";
    }

    public void convertWorkOrderAddRequest2TodoTaskEvent(WorkOrderAddRequest workOrderAddRequest, TodoTaskEvent todoTaskEvent) {
        todoTaskEvent.setPrincipalUserId(workOrderAddRequest.getPrincipalUserId());
        todoTaskEvent.setReferenceDomain(TodoTaskReferenceDomain.WORK_ORDER);
        todoTaskEvent.setReferenceType(workOrderAddRequest.getWorkOrderConfigId());
        todoTaskEvent.setTodoTaskStatus(TodoTaskStatus.NEW);
        todoTaskEvent.setFirmName(workOrderAddRequest.getFirmName());
        todoTaskEvent.setFirmId(workOrderAddRequest.getFirmId());
        todoTaskEvent.setContactName(workOrderAddRequest.getUserId());
        todoTaskEvent.setStartTime(new Date());
    }

    public void createWorkOrderIntoEventCenter(String csWorkOrderId, WorkOrderSource source, String workOrderConfigId) {
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(csWorkOrderId);
        if (csWorkOrderDO == null) {
            return;
        }
        String userId = csWorkOrderDO.getUserId();
        String firmId = csWorkOrderDO.getFirmId();
        if (!openEventCenterEnabled || StringUtils.isBlank(workOrderConfigId) || (StringUtils.isBlank(userId) && StringUtils.isBlank(firmId))) {
            return;
        }
        CSWorkorderConfigVO configVO = cSWorkOrderConfigService.getValidConfigVOByConfigId(workOrderConfigId);
        if (configVO == null) {
            return;
        }
        EventTarget target = null;
        String targetId = "";
        if (StringUtils.isNotBlank(firmId)) {
            target = EventTarget.FIRM;
            targetId = firmId;
        }
        if (StringUtils.isNotBlank(userId)) {
            target = EventTarget.USER;
            targetId = userId;
        }
        EventRecord eventRecord = EventRecord
                .builder()
                .target(target)
                .targetId(targetId)
                .event("create_csWorkOrder")
                .bizId(csWorkOrderId)
                .bizTime(new Date())
                .eventId(csWorkOrderId)
                .product("crm")
                .withProperty("business_type", configVO.getBusinessType())
                .withProperty("work_order_type", configVO.getWorkorderType())
                .withProperty("problem_type", configVO.getProblemType())
                .withProperty("work_order_config_id", workOrderConfigId)
                .withProperty("work_order_id", csWorkOrderId)
                .withProperty("service_order_create_time", new Date())
                .withProperty("receipt_target", StringUtils.isNotBlank(csWorkOrderDO.getSaleBossUserId()) ? "sale" : "customer")
                .build();
        if (Objects.nonNull(source)) {
            eventRecord.getPropertiesMap().put("service_order_channel", source);
        }
        eventProducer.asyncPost(eventRecord, (metadata, exception) -> {
            if (exception != null) {
                log.error(exception.getMessage());
            }
        });
    }

    private FacadeResponse<CSWorkOrderVO> toWorkOrderVOList(CSWorkOrderDO workOrderDO) {
        if (workOrderDO == null) {
            return null;
        }
        CSWorkOrderVO csWorkOrderVO = WorkOrderConverter.toWorkOrderVO(workOrderDO);

        //TODO 迁移变更:有云雀协同单Id就是新协同UI展示---此处删除了五方协同灰度逻辑
        csWorkOrderVO.setNewCollaboration(true);

        if (StringUtils.isEmpty(csWorkOrderVO.getFirmId())) {
            String userLoginMobile = getUserLoginMobile(csWorkOrderVO.getUserId());
            if (null != userLoginMobile) {
                csWorkOrderVO.setPhone(userLoginMobile);
            }
        }
        converVideoUrl(csWorkOrderVO);
        if (!Strings.isNullOrEmpty(workOrderDO.getPrincipalUserId())) {
            User bossPrincipalUser = bossUserClient.queryByBossUserId(workOrderDO.getPrincipalUserId());

            if (bossPrincipalUser != null) {
                csWorkOrderVO.setPrincipalUserId(bossPrincipalUser.getBossUserId());
                csWorkOrderVO.setPrincipalUserName(bossPrincipalUser.getUserName());
            }
        }
        if (!Strings.isNullOrEmpty(workOrderDO.getCurrentDealUserId())) {
            User bossCurrentDealUser = bossUserClient.queryByBossUserId(workOrderDO.getCurrentDealUserId());

            if (bossCurrentDealUser != null) {
                csWorkOrderVO.setCurrentDealUserId(bossCurrentDealUser.getBossUserId());
                csWorkOrderVO.setCurrentDealUserName(bossCurrentDealUser.getUserName());
            }
        }

        CSWorkOrderConfigDO workOrderTypeDO = findConfigOne(workOrderDO.getWorkOrderConfigId());
        if (workOrderTypeDO != null) {
            csWorkOrderVO.setWorkorderTypeVO(WorkOrderConverter.toWorkOrderTypeVO(workOrderTypeDO));
        }

        CSWorkOrderCommRecordQueryRequest request = new CSWorkOrderCommRecordQueryRequest();
        request.setWorkorderId(workOrderDO.getWorkorderId());
        request.setSize(1);
        request.setPage(1);
        Page<CSWorkOrderCommRecordDO> commRecordDOS = csWorkOrderCommRecordDAO.findPage(
                new Query().addCriteria(Criteria.where("workorderId").is(request.getWorkorderId())).with(Sort.by(Sort.Direction.DESC, CSWorkOrderCommRecordDO.CREATION_DATE)),
                request.getPage(),
                request.getSize());
        Set<String> bossUserIds = commRecordDOS.stream().map(CSWorkOrderCommRecordDO::getOperator).filter(operator -> !StringUtils.isEmpty(operator)).collect(Collectors.toSet());
        List<User> users = bossUserClient.queryByBossUserIds(bossUserIds);
        Map<String, String> userIdAndUserNameMap = new HashMap<>();
        for (User user : users) {
            userIdAndUserNameMap.put(user.getBossUserId(), user.getUserName());
        }
        List<CSWorkorderCommRecordVO> commRecordVOS = commRecordDOS
                .stream().map(commRecordDO -> {
                    CSWorkorderCommRecordVO commRecordVO = WorkOrderConverter.toCommRecordVO(commRecordDO);
                    if (!StringUtils.isEmpty(commRecordDO.getOperator())) {
                        commRecordVO.setOperator(userIdAndUserNameMap.get(commRecordDO.getOperator()));
                    }
                    return commRecordVO;
                })
                .collect(Collectors.toList());


        //预审核结果
        ActionDO actionDO = actionDAO.findOneBySceneId(workOrderDO.getWorkorderId());
        if (actionDO != null && !CollectionUtils.isEmpty(actionDO.getActionResults())) {
            Object preAuditResult = actionDO.getActionResults().get(PRE_AUDIT_RESULT);
            Object preAuditSuggestion = actionDO.getActionResults().get("preAuditSuggestion");
            Object preAuditRemark = actionDO.getActionResults().get("preAuditRemark");
            csWorkOrderVO.setPreAuditResult(null == preAuditResult ? null : String.valueOf(preAuditResult));
            csWorkOrderVO.setPreAuditSuggestion(null == preAuditSuggestion ? null : String.valueOf(preAuditSuggestion));
            csWorkOrderVO.setPreAuditRemark(null == preAuditRemark ? null : String.valueOf(preAuditRemark));
        }

        // 查询协同发起人
        // 风控
//        CooperationWorkOrderDO cooperationWorkOrderDO = cooperationWorkOrderDAO.queryByCooperationOrderId(workOrderDO.getWorkorderId());
//        if (!ObjectUtils.isEmpty(cooperationWorkOrderDO)) {
//            csWorkOrderVO.setCooSponsor(cooperationWorkOrderDO.getCreatorName());
//            csWorkOrderVO.setSource(WorkOrderSource.RISK);
//        } else {
//            //
//            CoopWorkOrderDO coopWorkOrderDO = coopWorkOrderDAO.queryByCoWorkOrderId(workOrderDO.getWorkorderId());
//            if (!ObjectUtils.isEmpty(coopWorkOrderDO)) {
//                csWorkOrderVO.setCooSponsor(coopWorkOrderDO.getSoCreatorName());
//                if (Objects.equals(coopWorkOrderDO.getSoDepartmentId(), DepartmentConstant.CUSTOMER_ASSET)) {
//                    csWorkOrderVO.setSource(WorkOrderSource.CUSTOMER_ASSET);
//                }
//            }
//        }
        if (CollectionUtils.isEmpty(commRecordVOS) && !emailCsWorkOrderConfigIds.contains(workOrderDO.getWorkOrderConfigId())) {
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        csWorkOrderVO.setCsWorkorderCommRecordVOS(commRecordVOS);
//        if (!Objects.equals(workOrderDO.getWorkOrderConfigId(), emailCsWorkOrderConfigId)) {
//            return getFacadeResponseWithValue(csWorkOrderVO);
//        }

        CSWorkOrderReceiveEmailRecordDO receiveEmailRecordDO = csWorkOrderReceiveEmailRecordDAO.
                queryLastByWorkOrderId(workOrderDO.getWorkorderId());
        if (Objects.isNull(receiveEmailRecordDO)) {
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        csWorkOrderVO.setEmailType(receiveEmailRecordDO.getType());
        if (CollectionUtils.isEmpty(commRecordVOS)) {
            CSWorkorderCommRecordVO csWorkorderCommRecordVO = new CSWorkorderCommRecordVO();
            csWorkorderCommRecordVO.setContent(receiveEmailRecordDO.getSubject());
            csWorkorderCommRecordVO.setCreatedTime(receiveEmailRecordDO.getCreationDate());
            commRecordVOS.add(csWorkorderCommRecordVO);
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        CSWorkorderCommRecordVO csWorkorderCommRecordVO = commRecordVOS.get(0);
        if (Objects.nonNull(receiveEmailRecordDO.getCreationDate()) &&
                Objects.nonNull(csWorkorderCommRecordVO.getCreatedTime()) &&
                receiveEmailRecordDO.getCreationDate().after(csWorkorderCommRecordVO.getCreatedTime())) {
            csWorkorderCommRecordVO.setContent(receiveEmailRecordDO.getSubject());
            csWorkorderCommRecordVO.setCreatedTime(receiveEmailRecordDO.getCreationDate());
        }
        return getFacadeResponseWithValue(csWorkOrderVO);
    }

    /**
     * 获取用户登录手机号
     *
     * @param userId
     * @return
     */
    private String getUserLoginMobile(String userId) {
        if (StringUtils.isBlank(userId)) {
            return null;
        }
        FacadeResponse<UserAccountVO> response = userClient.queryUserAccountDetail(userId);
        if (!response.isSucceeded() || null == response.getValue()) {
            return null;
        }
        return response.getValue().getMobile();
    }

    private void converVideoUrl(CSWorkOrderVO workorderVO) {
        if (workorderVO.getVideoUrl() != null) {
            String url = workorderVO.getVideoUrl();
            try {
                InputStream fileFromOSS = fileStorageUtils.getFileFromOSS(url);
                int len;
                byte[] bytes = new byte[1024 * 8];
                StringBuilder s = new StringBuilder();
                while ((len = fileFromOSS.read(bytes)) != -1) {
                    s.append(new String(bytes, 0, len, StandardCharsets.UTF_8));
                }
                workorderVO.setVideoUrl(s.toString());
            } catch (Exception e) {
                log.error("get video base64 error ,url is {}, error info {}", url, e);
            }
        }
    }

    private CSWorkOrderConfigDO findConfigOne(String configId) {
        return csWorkOrderConfigDAO.findOne(new Query().addCriteria(Criteria.where("configId").is(configId)));
    }

    private CSWorkOrderDO findWorkOrderOne(String workOrderId) {
        return csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(workOrderId);
    }

    private static <T> FacadeResponse<T> getFacadeResponseWithValue(T value) {
        FacadeResponse<T> response = getFacadeResponseWithSuccess();
        response.setValue(value);
        return response;
    }

    private static <T> FacadeResponse<T> getFacadeResponseWithSuccess() {
        FacadeResponse<T> response = new FacadeResponse<>();
        response.fillWithSuccess();
        return response;
    }

    private static <T> FacadeResponse<T> getFacadeResponseWithFailed(AttaResponseCode code) {
        return getFacadeResponseWithFailed(code, null);
    }

    private static <T> FacadeResponse<T> getFacadeResponseWithFailed(AttaResponseCode code, String message) {
        FacadeResponse<T> facadeResponse = new FacadeResponse<>();
        facadeResponse.fillWithUnSuccess(code);
        facadeResponse.setResponseMsg(message);
        return facadeResponse;
    }

    private String transferPrincipal(OrderAssignReq request) {
        request.validate();
        List<DataPersistenceCommander> commanders = new ArrayList<>();
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());
        CSExceptionAssert.isTrue(csWorkOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "csWorkOrderDO should not be empty. workOrderId is: " + request.getWorkOrderId());
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            log.warn("csWorkOrderDO already completed. workOrderId is: {}", request.getWorkOrderId());
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "工单已结案,不能转移负责人");
        }
        boolean statusIsChange = false;

        if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.Pending || csWorkOrderDO.getStatus() == CSWorkOrderStatus.Created) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
            setOrderLevel(csWorkOrderDO, request.getToPrincipalUserId());
            statusIsChange = true;
        } else if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.UpgradePending) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.UpgradeProcessing);
            statusIsChange = true;
        }
        csWorkOrderDO.setPrincipalUserId(request.getToPrincipalUserId());
        csWorkOrderDO.setCurrentDealUserId(request.getOperatorUserId());

        User user = bossUserClient.queryByBossUserId(request.getToPrincipalUserId());
        String suggestion = StringUtils.isEmpty(request.getReason()) ? String.format("转移至负责人【%s】", user.getUserName()) : request.getReason();
        OpLogAddRequest logAddRequest = OpLogAddRequest.builder()
                .workOrderId(request.getWorkOrderId())
                .status(csWorkOrderDO.getStatus())
                .operatorUserId(request.getOperatorUserId())
                .suggestion(suggestion)
                .build();
        csWorkOrderLogService.insertOpLog(commanders, logAddRequest);
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR, "更新失败");
        });
        try {
            transactionLazyExecutor.executeWithRequire(commanders);
        } catch (Exception e) {
            log.error("transfer principal failed. e is: ", e);
            CSExceptionAssert.fail(CSOrderBizErrorCode.SYSTEM_ERROR, "转移负责人失败");
        }
        //如果是协同工单需要发送消息
        if (!Objects.isNull(csWorkOrderDO.getSkylarkCollaborationId())) {
            //云雀平台新协同工单
            //先通知工单变更处理人
            skylarkManager.notifyChangePrincipal(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getSkylarkCollaborationId(), user.getBossUserId(), user.getUserName());
            sendSkylarkCollaborationMessage(csWorkOrderDO, csWorkOrderDO.getSkylarkCollaborationId(), WorkOrderOptType.TRANSFER);
        }
        //转移负责人后把此时工单负责人移出队列
        removeUser(request.getToPrincipalUserId());

        slaEscalationService.escalationTransferPrincipalPrincipalWorkOrderSla(csWorkOrderDO, statusIsChange);
        return csWorkOrderDO.getWorkorderId();
    }

    private String transferPrincipal(List<DataPersistenceCommander> commanders, OrderAssignReq request) {
        request.validate();
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());
        CSExceptionAssert.isTrue(csWorkOrderDO != null
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "csWorkOrderDO should not be empty. workOrderId is: " + request.getWorkOrderId());
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            log.warn("csWorkOrderDO already completed. workOrderId is: {}", request.getWorkOrderId());
            CSExceptionAssert.fail(CSOrderBizErrorCode.VALIDATION_ERROR, "工单已结案,不能转移负责人");
        }

        boolean statusIsChange = false;
        if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.Pending || csWorkOrderDO.getStatus() == CSWorkOrderStatus.Created) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.Processing);
            statusIsChange = true;
            setOrderLevel(csWorkOrderDO, request.getToPrincipalUserId());
        } else if (csWorkOrderDO.getStatus() == CSWorkOrderStatus.UpgradePending) {
            csWorkOrderDO.setStatus(CSWorkOrderStatus.UpgradeProcessing);
            statusIsChange = true;
        }
        csWorkOrderDO.setPrincipalUserId(request.getToPrincipalUserId());
        csWorkOrderDO.setAllocateTime(new Date());
        csWorkOrderDO.setCurrentDealUserId(request.getOperatorUserId());
        User user = bossUserClient.queryByBossUserId(request.getToPrincipalUserId());
        String suggestion = StringUtils.isEmpty(request.getReason()) ? String.format("转移至负责人【%s】", user.getUserName()) : request.getReason();
        OpLogAddRequest logAddRequest = OpLogAddRequest.builder()
                .workOrderId(request.getWorkOrderId())
                .status(csWorkOrderDO.getStatus())
                .operatorUserId(request.getOperatorUserId())
                .suggestion(suggestion)
                .build();
        csWorkOrderLogService.insertOpLog(commanders, logAddRequest);
        // 记录分配负责人链路
        allocateHandleService.recordAllocationChain(csWorkOrderDO);
        final boolean finalStatusIsChange = statusIsChange;
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR, "更新失败");
            slaEscalationService.escalationTransferPrincipalPrincipalWorkOrderSla(csWorkOrderDO, finalStatusIsChange);
        });
        TodoTaskEvent todoTaskEvent = new TodoTaskEvent();
        convertWorkOrderDO2TodoTaskEvent(csWorkOrderDO, todoTaskEvent);
        //转移负责人后给代办发送消息
        if (StringUtils.isNotEmpty(todoTaskEvent.getPrincipalUserId())) {
            imqProvider.push(TodoTaskEvent.QUEUE_NAME, todoTaskEvent, QueueType.TOPIC);
        }
        //如果是协同工单需要发送消息
        //云雀平台新协同工单
        if (!Objects.isNull(csWorkOrderDO.getSkylarkCollaborationId())) {
            //先通知工单变更处理人
            skylarkManager.notifyChangePrincipal(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getSkylarkCollaborationId(), user.getBossUserId(), user.getUserName());
            sendSkylarkCollaborationMessage(csWorkOrderDO, csWorkOrderDO.getSkylarkCollaborationId(), WorkOrderOptType.TRANSFER);
        }
//        else {
//            sendCooperationMessage(csWorkOrderDO);
//            sendCooperationMessageNew(csWorkOrderDO, WorkOrderOptType.TRANSFER);
//        }

        //转移负责人后把此时工单负责人移出队列
        removeUser(request.getToPrincipalUserId());

        //插入同步云雀服务中心逻辑
//        authenticationNodeProducer.sendPrincipalChangeEvent(csWorkOrderDO);

        return csWorkOrderDO.getWorkorderId();
    }

    public void convertWorkOrderDO2TodoTaskEvent(CSWorkOrderDO csWorkOrderDO, TodoTaskEvent todoTaskEvent) {
        todoTaskEvent.setPrincipalUserId(csWorkOrderDO.getPrincipalUserId());
        todoTaskEvent.setReferenceDomain(TodoTaskReferenceDomain.WORK_ORDER);
        todoTaskEvent.setReferenceType(csWorkOrderDO.getWorkOrderConfigId());
        todoTaskEvent.setReferenceId(csWorkOrderDO.getWorkorderId());
        todoTaskEvent.setActionId(csWorkOrderDO.getWorkorderId());
        todoTaskEvent.setFirmId(csWorkOrderDO.getFirmId());
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            todoTaskEvent.setTodoTaskStatus(TodoTaskStatus.FINISHED);
        } else {
            todoTaskEvent.setTodoTaskStatus(TodoTaskStatus.NEW);
        }
        todoTaskEvent.setFirmName(csWorkOrderDO.getFirmName());
        todoTaskEvent.setContactName(csWorkOrderDO.getUserId());
        todoTaskEvent.setStartTime(new Date());
    }

    /**
     * 新的协同类型工单均通过云雀交互,由此方法发送工单通知
     *
     * @param csWorkOrderDO
     * @param collaborationId
     * @param workOrderOptType
     */
    private void sendSkylarkCollaborationMessage(CSWorkOrderDO csWorkOrderDO, String collaborationId, String workOrderOptType) {
        if (Objects.isNull(csWorkOrderDO) || Objects.isNull(collaborationId)) {
            return;
        }
        CollaborationVO collaborationVO = skylarkManager.getCollaborationDetail(collaborationId);
        BizType initiator = BizType.valueOf(collaborationVO.getFrom().getBizType());
        Optional<CoopWorkOrderMessageHandle> choose = coopWorkOrderMessageHolder.chooseByBizType(initiator);
        if (!choose.isPresent()) {
            return;
        }
        CoopWorkOrderDO coopWorkOrderDO = new CoopWorkOrderDO();
        coopWorkOrderDO.setSoWorkOrderId(collaborationVO.getFrom().getBizOrderId());
        coopWorkOrderDO.setSoCreatorId(collaborationVO.getFrom().getOperator().getBossUserId());
        choose.get().sendMessage(csWorkOrderDO, coopWorkOrderDO, workOrderOptType);
    }

    public void judgeAndSendAgent(String recordId, String currentUserId, String principalUserId, String csWorkOrderId) {
        final int NORMAL_PRIORITY = 20;
        if (StringUtils.isBlank(principalUserId)
                || StringUtils.isBlank(currentUserId)
                || Objects.equals(currentUserId, principalUserId)) {
            return;
        }
        BossUserInGroupReq bossUserInGroupReq = BossUserInGroupReq.builder()
                .bossUserId(principalUserId)
                .groupIds(Sets.newHashSet(secondGroup))
                .build();
        FacadeResponse<Boolean> response = bossUserGroupClient.bossUserInSpecifiedGroup(bossUserInGroupReq);
        //如果 当前负责人不是资深客服组的 不创建钉钉代办
        if (!response.isSucceeded() || Objects.equals(response.getValue(), false)) {
            return;
        }
        User currentUser = bossUserClient.queryByBossUserId(currentUserId);
        User principalUser = bossUserClient.queryByBossUserId(principalUserId);
        if (currentUser == null || principalUser == null) {
            return;
        }
        if (StringUtils.isBlank(currentUser.getUnionId()) || StringUtils.isBlank(principalUser.getUnionId())) {
            return;
        }
        DingTalkTodoCreateRequest request = new DingTalkTodoCreateRequest();
        request.setSourceId(recordId);
        request.setSubject(TODO_TITLE);
        request.setCreatorId(currentUser.getUnionId());
        request.setDescription(String.format(TODO_DESC, workOrderUrlPre, csWorkOrderId));
        request.setDueTime(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
        request.setExecutorIds(Lists.newArrayList(principalUser.getUnionId()));
        request.setParticipantIds(Lists.newArrayList(principalUser.getUnionId()));
        request.setDetailUrl(null);
        request.setIsOnlyShowExecutor(true);
        request.setPriority(NORMAL_PRIORITY);
        DingTalkTodoNotifyConfigs dingTalkTodoNotifyConfigs = new DingTalkTodoNotifyConfigs();
        dingTalkTodoNotifyConfigs.setDingNotify(DING_NOTIFY_TYPE);
        request.setNotifyConfigs(dingTalkTodoNotifyConfigs);
        request.setAppName(APP_NAME);
        request.setRequestId(UuidUtil.getId());
        request.setRequestSource(REQUEST_SOURCE);
        gwDingTalkTodoClient.createDingTalkTodo(request);
    }

    public void judgeSaleSessionWorkOrderAndSendAgent(String recordId, String bossUserId, String principalUserId, String csWorkOrderId, String content) {
        final int NORMAL_PRIORITY = 20;
        if (StringUtils.isBlank(principalUserId)) {
            return;
        }
        User user = bossUserClient.queryByBossUserId(bossUserId);
        if (user == null) {
            return;
        }
        User principalUser = bossUserClient.queryByBossUserId(principalUserId);
        if (principalUser == null) {
            return;
        }
        DingTalkTodoCreateRequest request = new DingTalkTodoCreateRequest();
        request.setSourceId(recordId);
        request.setSubject(TODO_TITLE);
        request.setCreatorId(user.getUnionId());
        request.setDescription(String.format(SALE_SESSION_WORK_ORDER_TODO_DESC, user.getUserName(), content, csWorkOrderId));
        request.setDueTime(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
        request.setExecutorIds(Lists.newArrayList(principalUser.getUnionId()));
        request.setParticipantIds(Lists.newArrayList(principalUser.getUnionId()));
        request.setDetailUrl(null);
        request.setIsOnlyShowExecutor(true);
        request.setPriority(NORMAL_PRIORITY);
        DingTalkTodoNotifyConfigs dingTalkTodoNotifyConfigs = new DingTalkTodoNotifyConfigs();
        dingTalkTodoNotifyConfigs.setDingNotify(DING_NOTIFY_TYPE);
        request.setNotifyConfigs(dingTalkTodoNotifyConfigs);
        request.setAppName(APP_NAME);
        request.setRequestId(UuidUtil.getId());
        request.setRequestSource(REQUEST_SOURCE);
        gwDingTalkTodoClient.createDingTalkTodo(request);
    }

    public void insertRecordTemplateLog(List<DataPersistenceCommander> commanders, RecordTemplateDO recordTemplateDO, RecordTemplateCreateRequest request) {

        RecordTemplateOpLogDO recordTemplateOpLogDO = RecordTemplateOpLogDO.builder()
                .opLogId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_RECORD_TEMPLATE_LOG))
                .templateId(recordTemplateDO.getTemplateId())
                .opTime(new Date())
                .opType(RecordTemplateOpType.CREATE)
                .opUserId(request.getOperatorUserId())
                .opSuggestion(getOpSuggestion(recordTemplateDO, RecordTemplateOpType.CREATE)).build();
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderRecdTmplOpLogDAO.insert(recordTemplateOpLogDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert recordTemplateOpLogDO failed.");
        });
    }

    public void updateRecordTemplateLog(List<DataPersistenceCommander> commanders, RecordTemplateDO recordTemplateDO, RecordTemplateEditRequest request) {
        RecordTemplateOpLogDO recordTemplateOpLogDO = RecordTemplateOpLogDO.builder()
                .opLogId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_RECORD_TEMPLATE_LOG))
                .templateId(recordTemplateDO.getTemplateId())
                .opTime(new Date())
                .opType(RecordTemplateOpType.UPDATE)
                .opUserId(request.getOperatorUserId())
                .opSuggestion(getOpSuggestion(recordTemplateDO, RecordTemplateOpType.UPDATE)).build();
        commanders.add(() -> {
            CSExceptionAssert.isTrue(csWorkOrderRecdTmplOpLogDAO.insert(recordTemplateOpLogDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "update recordTemplateOpLogDO failed.");
        });
    }

    public String getOpSuggestion(RecordTemplateDO recordTemplateDO, RecordTemplateOpType type) {
        if (null == recordTemplateDO) {
            return "";
        }
        String formName = "";
        StringBuilder relatedGroupName = new StringBuilder();
        String recordTemplateName = recordTemplateDO.getTemplateName();
        String status = recordTemplateDO.getStatus().getDesc();
        if (StringUtils.isNotEmpty(recordTemplateDO.getFormId())) {
            FacadeResponse<FormVO> facadeResponse = formServClient.queryDetails(recordTemplateDO.getFormId());
            if (facadeResponse.isSucceeded() && null != facadeResponse.getValue()) {
                formName = facadeResponse.getValue().getFormName();
            }
        }
        if (!CollectionUtils.isEmpty(recordTemplateDO.getRelatedGroupId())) {
            for (String groupId : recordTemplateDO.getRelatedGroupId()) {
                BossUserGroupQueryRequest bossUserGroupQueryRequest = new BossUserGroupQueryRequest();
                bossUserGroupQueryRequest.setPage(1);
                bossUserGroupQueryRequest.setSize(1);
                bossUserGroupQueryRequest.setGroupId(groupId);
                FacadeResponse<PageSerializable<BossUserGroupVO>> facadeResponse = bossUserClient.listGroups(bossUserGroupQueryRequest);
                if (facadeResponse.isSucceeded() && !CollectionUtils.isEmpty(facadeResponse.getValue().getList())) {
                    relatedGroupName.append(facadeResponse.getValue().getList().get(0).getGroupName()).append(",");
                }
            }
            if (relatedGroupName.length() > 0) {
                relatedGroupName.deleteCharAt(relatedGroupName.length() - 1);
            }
        }
        String suggestion = "";
        if (type.getDesc().equals(RecordTemplateOpType.CREATE.getDesc())) {
            String createSuggestionTemplate = "名称【templateName】;关联表单【relatedForm】;关联用户组【relatedGroup】;状态【status】";
            suggestion = createSuggestionTemplate.replace("templateName", recordTemplateName)
                    .replace("relatedForm", formName)
                    .replace("relatedGroup", relatedGroupName)
                    .replace("status", status);
        } else {
            RecordTemplateDO recordTemplateDO1 = csWorkOrderRecordTemplateDAO.findByTemplateId(recordTemplateDO.getTemplateId());
            String formNameSrc = "";
            StringBuilder relatedGroupNameSrc = new StringBuilder();
            String recordTemplateNameSrc = recordTemplateDO1.getTemplateName();
            String statusSrc = recordTemplateDO1.getStatus().getDesc();
            if (StringUtils.isNotEmpty(recordTemplateDO1.getFormId())) {
                FacadeResponse<FormVO> facadeResponse = formServClient.queryDetails(recordTemplateDO1.getFormId());
                if (facadeResponse.isSucceeded() && null != facadeResponse.getValue()) {
                    formNameSrc = facadeResponse.getValue().getFormName();
                }
            }
            if (!CollectionUtils.isEmpty(recordTemplateDO1.getRelatedGroupId())) {
                for (String groupId : recordTemplateDO1.getRelatedGroupId()) {
                    BossUserGroupQueryRequest bossUserGroupQueryRequest = new BossUserGroupQueryRequest();
                    bossUserGroupQueryRequest.setPage(1);
                    bossUserGroupQueryRequest.setSize(1);
                    bossUserGroupQueryRequest.setGroupId(groupId);
                    FacadeResponse<PageSerializable<BossUserGroupVO>> facadeResponse = bossUserClient.listGroups(bossUserGroupQueryRequest);
                    if (facadeResponse.isSucceeded() && !CollectionUtils.isEmpty(facadeResponse.getValue().getList())) {
                        relatedGroupNameSrc.append(facadeResponse.getValue().getList().get(0).getGroupName()).append(",");
                    }
                }
                if (relatedGroupNameSrc.length() > 0) {
                    relatedGroupNameSrc.deleteCharAt(relatedGroupNameSrc.length() - 1);
                }
            }
            StringBuilder sb = new StringBuilder();
            if (!recordTemplateName.equals(recordTemplateNameSrc)) {
                sb.append("名称【").append(recordTemplateNameSrc).append("->").append(recordTemplateName).append(";】");
            }
            if (!formName.equals(formNameSrc)) {
                sb.append("关联表单【").append(formNameSrc).append("->").append(formName).append(";】");
            }
            if (!relatedGroupName.toString().equals(relatedGroupNameSrc.toString())) {
                sb.append("关联用户组【").append(relatedGroupNameSrc).append("->").append(relatedGroupName).append(";】");
            }
            if (!status.equals(statusSrc)) {
                sb.append("状态【").append(statusSrc).append("->").append(status).append(";】");
            }
            suggestion = sb.toString();
        }
        return suggestion;
    }

    private CSWorkOrderOpLogDO createdOpLogDO(String workOrderId, CSWorkOrderStatus status, String operatorUserId, String suggestion) {
        CSWorkOrderOpLogDO opLogDO = new CSWorkOrderOpLogDO();
        opLogDO.setOpLogId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_OP_LOG));
        opLogDO.setOperatorUserId(operatorUserId);
        if (StringUtils.isEmpty(operatorUserId)) {
            opLogDO.setOperatorUserId("system");
        }
        opLogDO.setOpTime(new Date());
        opLogDO.setStatus(status.name());
        opLogDO.setSuggestion(suggestion);
        opLogDO.setWorkorderId(workOrderId);

        return opLogDO;
    }

    private boolean updateWorkOrder(CSWorkOrderDO workOrderDO, CSWorkOrderOpLogDO opLogDO) {
        boolean isGeneratedByTag = isGeneratedByTag(workOrderDO.getWorkOrderConfigId());
        Boolean result = mongoTransactionTemplate.execute(status -> {
            try {
                CSWorkOrderOpLogDO insertRes = csWorkOrderOpLogDAO.insert(opLogDO);
                if (insertRes == null) {
                    status.setRollbackOnly();
                    return false;
                }
                CSWorkOrderDO updateRes = csWorkOrderDAO.updateByIdSelective(workOrderDO);
                if (updateRes == null) {
                    status.setRollbackOnly();
                    return false;
                }
                ActionDO actionDO = actionDAO.find(new Query(Criteria.where("sceneId").is(updateRes.getWorkorderId())))
                        .stream().max(Comparator.comparing(ActionDO::getLastUpdateDate)).orElse(null);
                if (actionDO == null) {
                    status.setRollbackOnly();
                    return false;
                }
                Map<String, Object> actionRes = new HashMap<>();
                actionRes.put(PRE_AUDIT_RESULT, "NoPass");
                actionRes.put("preAuditSuggestion", "BDM预审驳回");
                actionRes.put("skipPreAudit", "no");
                actionRes.put("specialPermit", "no");
                actionDO.setActionResults(actionRes);
                if (actionDAO.updateByIdSelective(actionDO) == null) {
                    log.warn("update action fail, actionId : {}", actionDO.getActionId());
                    throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.BUSINESS_DATA_NOT_EXISTS, "update action fail");
                }
                addWorkOrderInstructions(workOrderDO, actionDO, isGeneratedByTag);
            } catch (Exception e) {
                status.setRollbackOnly();
                return false;
            }
            return true;
        });
        return result != null && result;
    }

    /**
     * 判断该类型的工单是否为打标后生成
     */
    private boolean isGeneratedByTag(String workOrderConfigId) {
        CSExceptionAssert.isTrue(StringUtils.isNotEmpty(workOrderConfigId)
                , CSOrderBizErrorCode.VALIDATION_ERROR, "workOrderConfigId should not be empty");
        WorkOrderAndTagRelationVO relationVO = workOrderConfigAndTagRelationService.getTagAndOrderConfigRelationByConfigId(workOrderConfigId);
        return relationVO != null;
    }

    public void addWorkOrderInstructions(CSWorkOrderDO csWorkOrderDO, ActionDO actionDO, boolean isGeneratedByTag) {
//        if (actionDO != null && !CollectionUtils.isEmpty(actionDO.getActionResults()) && actionDO.getActionResults().containsValue(CsOrderConstants.LENGTHEN)) {
//            workOrderInstructionDAO.insertInstruction(csWorkOrderDO.getWorkorderId(), WorkOrderInstructionType.ExtendTagValidTime);
//        }
        if (isGeneratedByTag) {
            workOrderInstructionDAO.insertInstruction(csWorkOrderDO.getWorkorderId(), WorkOrderInstructionType.CloseTag);
        }
        if (actionDO != null && (FIRST_AUDIT_TOW.equals(actionDO.getTagValue()) || UPDATE_AUDIT.equals(actionDO.getTagValue()) ||
                FIRM_SHAREHOLDER_AUDIT.equals(actionDO.getTagValue())
                || WEALTH_MANAGEMENT_AUDIT_PRE.equals(actionDO.getTagValue()) || CUSTOMER_ANNUAL_AUDIT.equals(actionDO.getTagValue()))) {
            workOrderInstructionDAO.insertInstruction(csWorkOrderDO.getWorkorderId(), WorkOrderInstructionType.PreAudit);
        }
        if (isPreAuditWorkOrder(csWorkOrderDO) && null != csWorkOrderDO.getExpediteType()) {
            workOrderInstructionDAO.insertInstruction(csWorkOrderDO.getWorkorderId(), WorkOrderInstructionType.ExpediteWorkOrder);
        }
        // add track record 根据工单类型判断
//        CSWorkOrderConfigDO orderConfigDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(csWorkOrderDO.getWorkOrderConfigId());
//        String workOrderType = orderConfigDO.getWorkorderType();
//        if (Sets.newHashSet(SellerWorkOrderEnum.CUSTOMER_FIRST_VISIT.getDesc(), SellerWorkOrderEnum.CUSTOMER_SECOND_VISIT.getDesc(),
//                SellerWorkOrderEnum.CUSTOMER_THIRD_VISIT.getDesc()).contains(workOrderType)) {
//            workOrderInstructionDAO.insertInstruction(csWorkOrderDO.getWorkorderId(), WorkOrderInstructionType.customerTrackRecord);
//        }

    }

    /**
     * 判断是否是预审工单
     *
     * @param workOrderDO
     * @return
     */
    private boolean isPreAuditWorkOrder(CSWorkOrderDO workOrderDO) {
        CSExceptionAssert.isTrue(StringUtils.isNotBlank(workOrderDO.getWorkOrderConfigId())
                , CSOrderBizErrorCode.VALIDATION_ERROR
                , "workOrderConfigId is empty. workOrderId is: " + workOrderDO.getWorkorderId());

        CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(workOrderDO.getWorkOrderConfigId());

        return WorkOrderConfigType.CertificationPreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.UpdateCertificationPreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.FinancePreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.MainLandFxPreAudit.name().equals(configDO.getConfigType())
                || WorkOrderConfigType.CustomerAnnualPreAudit.name().equals(configDO.getConfigType());
    }

    private void doInstruction(String workOrderId) {
        try {
            cSWorkOrderInstructionService.doUnSuccessStatusInstruction(workOrderId);
        } catch (Exception e) {
            log.error("do process failed. e: ", e);
        }
    }

    //获取预审结果
    private String obtainPreAudit(String workOrderId) {
        Set<String> preSet = Sets.newHashSet(FIRST_AUDIT_TOW, UPDATE_AUDIT);
        ActionDO actionDO = actionDAO.getLastUpdateActionBySceneId(workOrderId);
        return Optional.ofNullable(actionDO)
                .filter(item -> (preSet.contains(actionDO.getTagValue()) && actionDO.getActionResults() != null))
                .map(item -> String.valueOf(actionDO.getActionResults().get(PRE_AUDIT_RESULT)))
                .orElse(null);
    }

    private boolean preAuditNeedCheck(String workOrderId) {
        return Objects.equals(obtainPreAudit(workOrderId), PASS_RESULT);
    }

    private FacadeResponse<CSWorkOrderVO> toWorkOrderVOListOpt(CSWorkOrderDO workOrderDO, List<User> bossUsers, List<ActionDO> actionDOs) {
        if (workOrderDO == null || CollectionUtils.isEmpty(bossUsers)) {
            return null;
        }
        CSWorkOrderVO csWorkOrderVO = WorkOrderConverter.toWorkOrderVO(workOrderDO);
        //有云雀协同单Id就是新协同UI展示
        csWorkOrderVO.setNewCollaboration(true);
        //TODO 迁移变更:检查工单是否存在老coop协同,存在老ui协同逻辑
//        else if (existOldCoop(workOrderDO.getWorkorderId())) {
//            csWorkOrderVO.setNewCollaboration(false);
//        } else {
//            csWorkOrderVO.setNewCollaboration(true);
//        }
//        //灰度开关
//        if (grayscaleDataSwitch) {
//            csWorkOrderVO.setNewCollaboration(false);
//        }
        if (StringUtils.isEmpty(csWorkOrderVO.getFirmId())) {
            String userLoginMobile = getUserLoginMobile(csWorkOrderVO.getFirmId());
            if (null != userLoginMobile) {
                csWorkOrderVO.setPhone(userLoginMobile);
            }
        }
        converVideoUrl(csWorkOrderVO);
        if (!Strings.isNullOrEmpty(workOrderDO.getPrincipalUserId())) {
            Optional<User> bossPrincipalUser = bossUsers.stream().filter(t -> t.getBossUserId().equals(workOrderDO.getPrincipalUserId())).findFirst();
            if (bossPrincipalUser.isPresent()) {
                csWorkOrderVO.setPrincipalUserId(bossPrincipalUser.get().getBossUserId());
                csWorkOrderVO.setPrincipalUserName(bossPrincipalUser.get().getUserName());
            }
        }
        if (!Strings.isNullOrEmpty(workOrderDO.getCurrentDealUserId())) {
            Optional<User> bossCurrentDealUser = bossUsers.stream().filter(t -> t.getBossUserId().equals(workOrderDO.getCurrentDealUserId())).findFirst();
            if (bossCurrentDealUser.isPresent()) {
                csWorkOrderVO.setCurrentDealUserId(bossCurrentDealUser.get().getBossUserId());
                csWorkOrderVO.setCurrentDealUserName(bossCurrentDealUser.get().getUserName());
            }
        }

        CSWorkOrderConfigDO workOrderTypeDO = findConfigOne(workOrderDO.getWorkOrderConfigId());
        if (workOrderTypeDO != null) {
            csWorkOrderVO.setWorkorderTypeVO(WorkOrderConverter.toWorkOrderTypeVO(workOrderTypeDO));
        }

        CSWorkOrderCommRecordQueryRequest request = new CSWorkOrderCommRecordQueryRequest();
        request.setWorkorderId(workOrderDO.getWorkorderId());
        request.setSize(1);
        request.setPage(1);
        Page<CSWorkOrderCommRecordDO> commRecordDOS = csWorkOrderCommRecordDAO.findPage(
                new Query().addCriteria(Criteria.where("workorderId").is(request.getWorkorderId())).with(Sort.by(Sort.Direction.DESC, CSWorkOrderCommRecordDO.CREATION_DATE)),
                request.getPage(),
                request.getSize());
        Set<String> bossUserIds = commRecordDOS.stream().map(CSWorkOrderCommRecordDO::getOperator).filter(operator -> !StringUtils.isEmpty(operator)).collect(Collectors.toSet());
        List<User> users = bossUsers.stream().filter(t -> bossUserIds.contains(t.getBossUserId())).collect(Collectors.toList());

        Map<String, String> userIdAndUserNameMap = users.stream().collect(Collectors.toMap(User::getBossUserId, User::getUserName, (k, v) -> k));
        List<CSWorkorderCommRecordVO> commRecordVOS = commRecordDOS
                .stream().map(commRecordDO -> {
                    CSWorkorderCommRecordVO commRecordVO = WorkOrderConverter.toCommRecordVO(commRecordDO);
                    if (!StringUtils.isEmpty(commRecordDO.getOperator())) {
                        commRecordVO.setOperator(userIdAndUserNameMap.get(commRecordDO.getOperator()));
                    }
                    return commRecordVO;
                })
                .collect(Collectors.toList());

        //预审核结果
        Optional<ActionDO> actionDO = actionDOs.stream().filter(t -> t.getSceneId().equals(workOrderDO.getWorkorderId())).findFirst();
        if (actionDO.isPresent() && !CollectionUtils.isEmpty(actionDO.get().getActionResults())) {
            Object preAuditResult = actionDO.get().getActionResults().get(PRE_AUDIT_RESULT);
            Object preAuditSuggestion = actionDO.get().getActionResults().get("preAuditSuggestion");
            Object preAuditRemark = actionDO.get().getActionResults().get("preAuditRemark");
            csWorkOrderVO.setPreAuditResult(null == preAuditResult ? null : String.valueOf(preAuditResult));
            csWorkOrderVO.setPreAuditSuggestion(null == preAuditSuggestion ? null : String.valueOf(preAuditSuggestion));
            csWorkOrderVO.setPreAuditRemark(null == preAuditRemark ? null : String.valueOf(preAuditRemark));
        }
        // 查询协同发起人
        // 风控
//        Optional<CooperationWorkOrderDO> cooperationWorkOrderDO = cooperationWorkOrderDOs.stream().filter(t -> t.getCooperationOrderId().equals(workOrderDO.getWorkorderId())).findFirst();
//        if (cooperationWorkOrderDO.isPresent()) {
//            csWorkOrderVO.setCooSponsor(cooperationWorkOrderDO.get().getCreatorName());
//            csWorkOrderVO.setSource(WorkOrderSource.RISK);
//        } else {
//            //
//            CoopWorkOrderDO coopWorkOrderDO = coopWorkOrderDAO.queryByCoWorkOrderId(workOrderDO.getWorkorderId());
//            if (!ObjectUtils.isEmpty(coopWorkOrderDO)) {
//                csWorkOrderVO.setCooSponsor(coopWorkOrderDO.getSoCreatorName());
//                if (Objects.equals(coopWorkOrderDO.getSoDepartmentId(), DepartmentConstant.CUSTOMER_ASSET)) {
//                    csWorkOrderVO.setSource(WorkOrderSource.CUSTOMER_ASSET);
//                }
//            }
//        }
        if (CollectionUtils.isEmpty(commRecordVOS) && !emailCsWorkOrderConfigIds.contains(workOrderDO.getWorkOrderConfigId())) {
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        csWorkOrderVO.setCsWorkorderCommRecordVOS(commRecordVOS);

        CSWorkOrderReceiveEmailRecordDO receiveEmailRecordDO = csWorkOrderReceiveEmailRecordDAO.
                queryLastByWorkOrderId(workOrderDO.getWorkorderId());
        if (Objects.isNull(receiveEmailRecordDO)) {
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        csWorkOrderVO.setEmailType(receiveEmailRecordDO.getType());
        if (CollectionUtils.isEmpty(commRecordVOS)) {
            CSWorkorderCommRecordVO csWorkorderCommRecordVO = new CSWorkorderCommRecordVO();
            csWorkorderCommRecordVO.setContent(receiveEmailRecordDO.getSubject());
            csWorkorderCommRecordVO.setCreatedTime(receiveEmailRecordDO.getCreationDate());
            commRecordVOS.add(csWorkorderCommRecordVO);
            return getFacadeResponseWithValue(csWorkOrderVO);
        }
        CSWorkorderCommRecordVO csWorkorderCommRecordVO = commRecordVOS.get(0);
        if (Objects.nonNull(receiveEmailRecordDO.getCreationDate()) &&
                Objects.nonNull(csWorkorderCommRecordVO.getCreatedTime()) &&
                receiveEmailRecordDO.getCreationDate().after(csWorkorderCommRecordVO.getCreatedTime())) {
            csWorkorderCommRecordVO.setContent(receiveEmailRecordDO.getSubject());
            csWorkorderCommRecordVO.setCreatedTime(receiveEmailRecordDO.getCreationDate());
        }
        return getFacadeResponseWithValue(csWorkOrderVO);
    }

    private CSWorkOrderVO toWorkOrderVO(CSWorkOrderDO workOrderDO) {
        if (workOrderDO == null) {
            return null;
        }
        CSWorkOrderVO workOrderVO = WorkOrderConverter.toWorkOrderVO(workOrderDO);
        //有云雀协同单Id就是新协同UI展示
        workOrderVO.setNewCollaboration(true);
        //TODO 迁移变更:检查工单是否存在老coop协同,存在老ui协同逻辑
//        else if (existOldCoop(workOrderDO.getWorkorderId())) {
//            workOrderVO.setNewCollaboration(false);
//        } else {
//            workOrderVO.setNewCollaboration(true);
//        }
//        //灰度开关
//        if (grayscaleDataSwitch) {
//            workOrderVO.setNewCollaboration(false);
//        }
        String userLoginMobile = getUserLoginMobile(workOrderVO.getUserId());
        if (null != userLoginMobile) {
            workOrderVO.setPhone(userLoginMobile);
        }
        converVideoUrl(workOrderVO);
        if (Objects.equals(workOrderDO.getAccountType(), FundAccountTypeEnum.PERSONAL.name())
                && StringUtils.isNotBlank(workOrderDO.getUserAccountId())) {
            FacadeResponse<FundAccountVO> accountRes = fundAccountClient.queryAccountByAccountId(workOrderDO.getUserAccountId());
            if (accountRes.isSucceeded() && accountRes.getValue() != null) {
                workOrderVO.setAccountEnglishName(accountRes.getValue().getAccountEnglishName());
            }
        }
        if (!Strings.isNullOrEmpty(workOrderDO.getPrincipalUserId())) {
            User bossPrincipalUser = bossUserClient.queryByBossUserId(workOrderDO.getPrincipalUserId());

            if (bossPrincipalUser != null) {
                workOrderVO.setPrincipalUserId(bossPrincipalUser.getBossUserId());
                workOrderVO.setPrincipalUserName(bossPrincipalUser.getUserName());
            }
        }

        if (!Strings.isNullOrEmpty(workOrderDO.getCurrentDealUserId())) {
            User bossCurrentDealUser = bossUserClient.queryByBossUserId(workOrderDO.getCurrentDealUserId());

            if (bossCurrentDealUser != null) {
                workOrderVO.setCurrentDealUserId(bossCurrentDealUser.getBossUserId());
                workOrderVO.setCurrentDealUserName(bossCurrentDealUser.getUserName());
            }
        }

        //TODO 迁移变更:删除了opLogVOS和commRecordVOS的循环获取
//        List<CSWorkorderOpLogVO> opLogVOS = csWorkOrderOpLogDAO
//                .find(new Query().addCriteria(Criteria.where("workorderId").is(workOrderDO.getWorkorderId()))).stream().map(opLogDO -> {
//                    CSWorkorderOpLogVO opLogVO = WorkOrderConverter.toOpLogVO(opLogDO);
//
//                    if (SYSTEM.equals(opLogVO.getOperatorUserId())) {
//                        opLogVO.setOperatorUserName(SYSTEM);
//                    } else {
//                        User bossUser = bossUserClient.queryByBossUserId(opLogVO.getOperatorUserId());
//                        if (bossUser != null) {
//                            opLogVO.setOperatorUserName(bossUser.getUserName());
//                        }
//                    }
//                    return opLogVO;
//                }).collect(Collectors.toList());
//
//        workOrderVO.setWorkorderOpLogVOS(opLogVOS);
        List<CSWorkorderCommRecordVO> commRecordVOS = commRecordService.getCommRecordsByWorkOrderId(workOrderVO.getWorkorderId());
        workOrderVO.setCsWorkorderCommRecordVOS(commRecordVOS);

        CSWorkOrderConfigDO workOrderTypeDO = findConfigOne(workOrderDO.getWorkOrderConfigId());
        if (workOrderTypeDO != null) {
            workOrderVO.setWorkorderTypeVO(WorkOrderConverter.toWorkOrderTypeVO(workOrderTypeDO));
        }

        try {
            workOrderVO.setAttachmentVOList(attachmentFacade.getFileByReferenceId(workOrderDO.getWorkorderId()));
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.queryByWOConfigIdIgnoreStatus(workOrderDO.getWorkOrderConfigId());
        workOrderVO.setProblemType(Optional.ofNullable(csWorkOrderConfigDO).map(CSWorkOrderConfigDO::getProblemType).orElse(null));
        //客服工作台
        workOrderVO.setCurrTodoType(workOrderDO.getCurrTodoType());
        workOrderVO.setCurrProcessType(workOrderDO.getCurrProcessType());
        workOrderVO.setCurrProcessDeadline(workOrderDO.getCurrProcessDeadline());
        workOrderVO.setCurrProcessDeadlinePre(workOrderDO.getCurrProcessDeadlinePre());
        //咨询进度
        workOrderVO.setCustomerShowSwitch(workOrderDO.getCustomerShowSwitch());
        workOrderVO.setReceiptTradeId(workOrderDO.getReceiptTradeId());
        //预约时间从跟进记录里获取
        resetFeedbackTime(workOrderVO);
        workOrderVO.setUpgradeSource(workOrderDO.getUpgradeSource());
        return workOrderVO;
    }

    private void resetFeedbackTime(CSWorkOrderVO workOrderVO) {
        workOrderVO.setFeedbackTime(cswoSlaEventService.getSlaFeedbackTime(workOrderVO.getWorkorderId()));
    }

    /**
     * 获取预审工单对应相同客户的历史预审工单列表
     *
     * @param workOrderDO
     * @return
     */
    private List<CSWorkOrderVO> getHistoryPreAuditWorkOrders(CSWorkOrderDO workOrderDO) {
        if (!isPreAuditWorkOrder(workOrderDO)) {
            return null;
        }
        //获取同一客户的预审工单
        //根据最后修改时间排序
        List<CSWorkOrderDO> workOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(Criteria.where("workOrderConfigId").in(getPreAuditConfigIds()).and("userId").is(workOrderDO.getUserId())))
                .stream()
                .sorted(Comparator.comparing(CSWorkOrderDO::getLastUpdateDate, Comparator.nullsLast(Date::compareTo)))
                .collect(Collectors.toList());

        // 判断是否为客户成功-客户审核类型的工单
        if (customerAuditConfigId.contains(workOrderDO.getWorkOrderConfigId())) {
            List<CSWorkOrderDO> operatingWorkOrderDOList = csWorkOrderDAO.find(new Query().addCriteria(Criteria.where(CSWorkOrderDO.WORK_ORDER_CONFIG_ID).in(preAuditConfigIds).and(CSWorkOrderDO.FIRM_ID).is(workOrderDO.getFirmId())))
                    .stream()
                    .sorted(Comparator.comparing(CSWorkOrderDO::getLastUpdateDate, Comparator.nullsLast(Date::compareTo)))
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(operatingWorkOrderDOList)) {
                workOrderDOList.addAll(operatingWorkOrderDOList);
            }
        }


        if (CollectionUtils.isEmpty(workOrderDOList)) {
            return null;
        }

        List<CSWorkOrderVO> csWorkOrderVOList = Lists.newArrayList();
        for (CSWorkOrderDO csWorkOrderDO : workOrderDOList) {
            FacadeResponse<CSWorkOrderVO> response = toWorkOrderVOList(csWorkOrderDO);
            if (response.isSucceeded()) {
                csWorkOrderVOList.add(response.getValue());
            }
        }
        return csWorkOrderVOList;
    }

    /**
     * 查询预审工单配置id集合
     *
     * @return
     */
    private Set<String> getPreAuditConfigIds() {
        Query query = new Query();
        query.addCriteria(Criteria.where(CSWorkOrderConfigDO.BUSINESS_TYPE).is(BusinessType.CUSTOMER_SUCCESS)
                .and(CSWorkOrderConfigDO.WORK_ORDER_TYPE).is("客户审核")
                .and(CSWorkOrderConfigDO.PROBLEM_TYPE).in("首次认证预审核", "更新认证预审核", "理财业务预审核", "客户年审预审核"));
        List<CSWorkOrderConfigDO> configs = csWorkOrderConfigDAO.find(query);
        return CollectionUtils.isEmpty(configs) ? Sets.newHashSet() : configs.stream().map(CSWorkOrderConfigDO::getConfigId).collect(Collectors.toSet());
    }

    private FacadeResponse validate(Object object) {
        ValidateResult validateResult = ValidateUtil.validate(object);

        if (!validateResult.isPassed()) {
            return getFacadeResponseWithFailed(AttaResponseCode.VALIDATION_ERROR, validateResult.getField() + validateResult.getReason());
        } else {
            return getFacadeResponseWithSuccess();
        }
    }

    private void buildSomePartQuery(CSWorkOrderPageListRequest request, Query configQuery) {
        if (null != request.getWorkOrderDomain()) {
            if (WorkOrderDomain.CS_WORK_ORDER.equals(request.getWorkOrderDomain())) {
                Criteria configCriteria = new Criteria();
                if (request.getBusinessType() == null) {
                    Criteria orCriteria1 = Criteria.where(CSWorkOrderConfigDO.BUSINESS_TYPE).ne(BusinessType.CUSTOMER_SUCCESS);
                    Criteria orCriteria2 = Criteria.where(CSWorkOrderConfigDO.WORK_ORDER_TYPE).ne("客户审核").and(CSWorkOrderConfigDO.BUSINESS_TYPE).is(BusinessType.CUSTOMER_SUCCESS);
                    configCriteria.orOperator(orCriteria1, orCriteria2);
                } else if (request.getBusinessType() == BusinessType.CUSTOMER_SUCCESS) {
                    configCriteria = Criteria.where(CSWorkOrderConfigDO.BUSINESS_TYPE).is(BusinessType.CUSTOMER_SUCCESS)
                            .and(CSWorkOrderConfigDO.WORK_ORDER_TYPE).ne("客户审核");
                } else {
                    configCriteria = Criteria.where(CSWorkOrderConfigDO.BUSINESS_TYPE).is(request.getBusinessType());
                    if (StringUtils.isNotBlank(request.getWorkorderType())) {
                        configQuery.addCriteria(Criteria.where(CSWorkOrderConfigDO.WORK_ORDER_TYPE).is(request.getWorkorderType()));
                    }
                }
                configQuery.addCriteria(configCriteria);
            }
            if (WorkOrderDomain.PRE_CS_WORK_ORDER.equals(request.getWorkOrderDomain())) {
                configQuery.addCriteria(Criteria.where(CSWorkOrderConfigDO.BUSINESS_TYPE).is(BusinessType.CUSTOMER_SUCCESS));
                configQuery.addCriteria(Criteria.where(CSWorkOrderConfigDO.WORK_ORDER_TYPE).is("客户审核"));
            }
        } else if (request.getBusinessType() != null || !StringUtils.isEmpty(request.getWorkorderType())) {
            if (request.getBusinessType() != null) {
                configQuery.addCriteria(Criteria.where("businessType").is(request.getBusinessType().name()));
            }
            if (!StringUtils.isEmpty(request.getWorkorderType())) {
                configQuery.addCriteria(Criteria.where(CSWorkOrderConfigDO.WORK_ORDER_TYPE).is(request.getWorkorderType()));
            }
        }
    }

    private FacadeResponse<CSWorkOrderConfigDO> validateBaseCsWorkOrder(CSWorkOrderBaseRequest request) {
        FacadeResponse validate = validate(request);
        if (!validate.isSucceeded()) {
            return validate;
        }
        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.findOne(MongoQueryFactory.create(request));
        if (csWorkOrderConfigDO == null || csWorkOrderConfigDO.getConfigId() == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.WORK_ORDER_NOT_FUND, "未知工单配置");
        }
        FacadeResponse<CSWorkOrderConfigDO> success = new FacadeResponse<>();
        success.setSucceeded(true);
        success.setValue(csWorkOrderConfigDO);
        return success;
    }

    private CSWorkOrderDO createdCSWorkOrderDO(String referenceId, String configId, String mid, String firmId, String firmName, String userId, String principalUserId, String phoneAreaCode, String newPhoneAreaCode) {
        CSWorkOrderDO csWorkOrderDO = new CSWorkOrderDO();
        csWorkOrderDO.setWorkorderId(getWorkOrderId());
        csWorkOrderDO.setReferenceId(referenceId);
        csWorkOrderDO.setWorkOrderConfigId(configId);
        csWorkOrderDO.setMid(mid);
        csWorkOrderDO.setFirmId(firmId);
        csWorkOrderDO.setFirmName(firmName);
        csWorkOrderDO.setUserId(userId);
        csWorkOrderDO.setPrincipalUserId(principalUserId);
        csWorkOrderDO.setNewCollaboration(CsOrderConstants.newCollaboration);
        if (StringUtils.isNotEmpty(phoneAreaCode)) {
            csWorkOrderDO.setPhoneAreaCode(phoneAreaCode);
        }

        if (StringUtils.isNotEmpty(newPhoneAreaCode)) {
            csWorkOrderDO.setNewPhoneAreaCode(newPhoneAreaCode);
        }

        return csWorkOrderDO;
    }

    private CSWorkOrderCommRecordDO createCommRecordDO(String workOrderId
            , String userId
            , String content
            , List<AttachmentVO> attachments
            , Set<String> callCenterIds) {
        if (Strings.isNullOrEmpty(content) && CollectionUtils.isEmpty(attachments)) {
            return null;
        }
        CSWorkOrderCommRecordDO commRecordDo = new CSWorkOrderCommRecordDO();
        commRecordDo.setRecordId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_COMM_RECORD));
        commRecordDo.setOperator(userId);
        commRecordDo.setWorkorderId(workOrderId);
        commRecordDo.setContent(content);
        commRecordDo.setCallCenterIds(callCenterIds);

        if (!CollectionUtils.isEmpty(attachments)) {
            attachments.forEach(attachmentVO -> attachmentVO.setReferenceId(commRecordDo.getRecordId()));
            try {
                Assert.isTrue(attachmentFacade.submitAddedAttachment(attachments), "insert attachment failed.");
            } catch (Exception e) {
                log.error("insert attachment failed.e: ", e);
            }
        }

        return commRecordDo;
    }

    private boolean insertWO(CSWorkOrderDO workOrderDO,
                             CSWorkOrderCommRecordDO commRecordDo,
                             CSWorkOrderOpLogDO opLogDO) {

        boolean isOk = transactionUtil.executeMongo(() -> {
            CSExceptionAssert.isTrue(csWorkOrderOpLogDAO.insert(opLogDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert opLogDO failed.");

            CSExceptionAssert.isTrue(csWorkOrderDAO.insert(workOrderDO) != null
                    , CSOrderBizErrorCode.SYSTEM_ERROR
                    , "insert workOrderDO failed.");


            if (commRecordDo != null) {
                CSExceptionAssert.isTrue(csWorkOrderCommRecordDAO.insert(commRecordDo) != null
                        , CSOrderBizErrorCode.SYSTEM_ERROR
                        , "insert commRecordDo failed.");
            }
        });

        if (isOk) {
            workorderMessageProducer.sendPreAuditStatusEvent(workOrderDO);
            if (finalCSWorkOrderStatus.contains(workOrderDO.getStatus())) {
                try {
                    workorderMessageProducer.closeCustomerTag(workOrderDO);
                } catch (Exception e) {
                    log.error("close customer Tag failed. e: ", e);
                }
            }
        }

        return isOk;
    }

    @Autowired
    private UserSecureInfoClient userSecureInfoClient;
    @Autowired
    private ApprovalClient approvalClient;

    public FacadeResponse checkPhoneAvailable(String phoneAreaCode, String phone, String newPhoneAreaCode,String newPhone) {
        if (phone == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "该商户未绑定手机号, 修改手机号失败");
        }
        if(StringUtils.isEmpty(phoneAreaCode)){
            phoneAreaCode = DEFAULT_PHONE_AREA_CODE;
        }
        if(StringUtils.isEmpty(newPhoneAreaCode)){
            newPhoneAreaCode = DEFAULT_PHONE_AREA_CODE;
        }
        if (newPhone == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "请输入正确的新手机号");
        }
        if (phoneAreaCode.equalsIgnoreCase(newPhoneAreaCode) && newPhone.equalsIgnoreCase(phone)) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "请输入正确的新手机号");
        }
        //校验老手机号 是否在使用
        PhoneNumberVO phoneNumberVO = new PhoneNumberVO();
        phoneNumberVO.setDialingCode(phoneAreaCode);
        phoneNumberVO.setNumber(phone);
        FacadeResponse<UsernameInfo> oldUsernameInfo = userSecureInfoClient.loadUsernameInfo(phoneNumberVO);
        if (!oldUsernameInfo.isSucceeded()) {
            log.error("remote get userInfo by phone error,{}", oldUsernameInfo.getResponseMsg());
            return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
        }
        if (oldUsernameInfo.getValue() == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "该商户绑定手机号不存在");
        }
        //校验新手机号是否存在
        PhoneNumberVO newPhoneNumberVO = new PhoneNumberVO();
        newPhoneNumberVO.setDialingCode(newPhoneAreaCode);
        newPhoneNumberVO.setNumber(newPhone);
        FacadeResponse<UsernameInfo> loadUsernameInfo = userSecureInfoClient.loadUsernameInfo(newPhoneNumberVO);
        if (!loadUsernameInfo.isSucceeded()) {
            log.error("remote get userInfo by phone error,{}", loadUsernameInfo.getResponseMsg());
            return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
        }
        if (loadUsernameInfo.getValue() != null) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "该手机号已被占用");
        }
        return getFacadeResponseWithSuccess();
    }

    private CSWorkOrderDO checkSubsistingWorkOrder(String configId, String userId) {
        Criteria criteria = new Criteria();
        criteria.and("status").nin(Lists.newArrayList("Completed", "Cancelled"));
        criteria.and("workOrderConfigId").is(configId);
        criteria.and("userId").is(userId);
        return csWorkOrderDAO.findOne(new Query().addCriteria(criteria));
    }

    private void sendApproval(String bizId, String bizType, String currentDealUserId, String currentDealUserName) {
        ApprovalRequestCreateRequest approvalRequest = new ApprovalRequestCreateRequest();
        approvalRequest.setBizId(bizId);
        approvalRequest.setBizType(bizType);
        approvalRequest.setCreatorUserId(currentDealUserId);
        approvalRequest.setCreatorUserName(currentDealUserName);
        CompletableFuture.runAsync(() -> {
            try {
                FacadeResponse approvalResponse = approvalClient.createApprovalRequest(approvalRequest);
                if (!approvalResponse.isSucceeded()) {
                    log.error("Submit Ding Ding error ,bizId is {}, bizType is {}", bizId, bizType);
                }
            } catch (Exception e) {
                log.error("createApprovalRequest failed. bizId is {}, bizType is {}, e is: ", bizId, bizType, e);
            }
        }, commonExecutor);
    }


    private FacadeResponse checkCsWorkOrderConfigAvailable(CSWorkOrderConfigDO csWorkOrderConfigDO) {
        if (csWorkOrderConfigDO == null) {
            return getFacadeResponseWithFailed(AttaResponseCode.ILLEGAL_ARGUMENT, "工单配置错误");
        }
        if (csWorkOrderConfigDO.getBusinessType() != BusinessType.ACCOUNT_FUNCTION) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "暂不支持该业务类型");
        }
        if (!ACCOUNT_SECURITY.equals(csWorkOrderConfigDO.getWorkorderType())) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "暂不支持该工单类型");
        }
        if (!CHANGE_USER_MASTER_PHONE.equals(csWorkOrderConfigDO.getProblemType()) && !UN_BIND_X.equals(csWorkOrderConfigDO.getProblemType())) {
            return getFacadeResponseWithFailed(AttaResponseCode.BIZ_ERROR, "暂不支持该问题类型");
        }
        return getFacadeResponseWithSuccess();
    }

    private FacadeResponse<Boolean> checkXshieldAvailable(String userId) {
        FacadeResponse<Boolean> response = userSecretClient.hasActiveSecret(userId, SecretType.OTP.name());
        if (!response.isSucceeded()) {
            log.error("remote get userInfo error,{}", response.getResponseMsg());
            return getFacadeResponseWithFailed(AttaResponseCode.SYSTEM_ERROR);
        }
        return response;
    }

    @Autowired
    private NotificationService notificationService;

    public void sendNotification(CSWorkOrderDO csWorkOrderDO, CSWorkOrderConfigDO csWorkOrderConfigDO, CrmApprovalStatus approvalStatus) throws Exception {
        NotificationEvent notificationEvent = new NotificationEvent();
        notificationEvent.setEventId(csWorkOrderDO.getWorkorderId());
        notificationEvent.setEventTopic("WorkOrderCheckResult");
        notificationEvent.setTags(Lists.newArrayList("WorkOrderCheckAction"));
        CSWorkOrderNotifyResultRequest request = new CSWorkOrderNotifyResultRequest();
        request.setRequestId(csWorkOrderDO.getReferenceId());
        request.setBusinessType(csWorkOrderConfigDO.getBusinessType());
        request.setWorkOrderType(WorkOrderType.convertWorkOrderType(csWorkOrderConfigDO.getWorkorderType()));
        request.setProblemType(ProblemType.convertProblemType(csWorkOrderConfigDO.getProblemType()));
        request.setWorkOrderSource(csWorkOrderDO.getSource());
        request.setStatus(approvalStatus);
        request.setUserId(csWorkOrderDO.getUserId());
        request.setPhone(csWorkOrderDO.getPhone());
        request.setNewPhone(csWorkOrderDO.getNewPhone());
        request.setPhoneAreaCode(csWorkOrderDO.getPhoneAreaCode());
        request.setNewPhoneAreaCode(csWorkOrderDO.getNewPhoneAreaCode());
        notificationEvent.setMessageBody(request);
        notificationService.notify(notificationEvent);
    }

    /**
     * 工单撤销
     * 同步撤销加急工单
     */
    private void cancelledExpediteWorkOrder(CSWorkOrderDO csWorkOrderDO) {
        Criteria expediteConfigCriteria = Criteria.where("businessType").is(BusinessType.CUSTOMER_CONSULTATION)
                .and("workorderType").is("其他")
                .and("problemType").is("加急")
                .and("status").is(CSWorkOrderConfigStatus.Enabled);
        CSWorkOrderConfigDO csWorkOrderConfigDO = csWorkOrderConfigDAO.findOne(new Query().addCriteria(expediteConfigCriteria));
        if (null == csWorkOrderConfigDO) {
            log.error("工单自动撤销失败, 未找到加急工单类型");
            return;
        }

        Criteria expediteWorkOrderCriteria = Criteria.where("workOrderConfigId").is(csWorkOrderConfigDO.getConfigId())
                .and("relatedWorkOrderId").is(csWorkOrderDO.getWorkorderId())
                .and("relatedWorkOrderDomain").is(WorkOrderDomain.CS_WORK_ORDER)
                .and("status").nin(finalCSWorkOrderStatus);
        List<CSWorkOrderDO> expediteWorkOrders = csWorkOrderDAO.find(new Query().addCriteria(expediteWorkOrderCriteria));

        csWorkOrderDO.setStatus(CSWorkOrderStatus.Cancelled);
        CSWorkOrderOpLogDO csWorkOrderOpLogDO = createdOpLogDO(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getStatus(), "system", "无人认领,撤销工单");

        Boolean isOk = mongoTransactionTemplate.execute(status -> {
            try {
                if (csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) == null) {
                    throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                }

                if (csWorkOrderOpLogDAO.insert(csWorkOrderOpLogDO) == null) {
                    throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert oplog fail.");
                }

                for (CSWorkOrderDO expediteWorkOrder : expediteWorkOrders) {
                    expediteWorkOrder.setStatus(CSWorkOrderStatus.Cancelled);
                    CSWorkOrderOpLogDO expediteOpLogDO = createdOpLogDO(expediteWorkOrder.getWorkorderId(), expediteWorkOrder.getStatus(), "system", "同步撤销加急工单");
                    if (csWorkOrderDAO.updateByIdSelective(expediteWorkOrder) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.UpdateError, CSOrderBizErrorCode.SYSTEM_ERROR, "update fail.");
                    }
                    if (csWorkOrderOpLogDAO.insert(expediteOpLogDO) == null) {
                        throw new CustomerServiceException(CSOrderSystemErrorCode.InsertError, CSOrderBizErrorCode.SYSTEM_ERROR, "insert oplog fail.");
                    }
                }
            } catch (Exception e) {
                log.warn(e.getMessage(), e);
                status.setRollbackOnly();
                return Boolean.FALSE;
            }
            return Boolean.TRUE;
        });

        if (!Boolean.TRUE.equals(isOk)) {
            log.error("工单自动撤销失败, csWorkOrderDO: {}", csWorkOrderDO);
        }
    }

    private FacadeResponse updateWO(CSWorkOrderDO workOrderDO,
                                    CSWorkOrderOpLogDO opLogDO) {

        FacadeResponse facadeResponse = mongoTransactionTemplate.execute(status -> {
            if (csWorkOrderOpLogDAO.insert(opLogDO) == null) {
                status.setRollbackOnly();
                return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
            }

            if (workOrderDO != null) {
                if (csWorkOrderDAO.updateByIdSelective(workOrderDO) == null) {
                    status.setRollbackOnly();
                    return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
                }
            }

            return getFacadeResponseWithSuccess();
        });

        if (facadeResponse != null && facadeResponse.isSucceeded()) {
            workorderMessageProducer.sendPreAuditStatusEvent(workOrderDO);
            if (finalCSWorkOrderStatus.contains(workOrderDO.getStatus())) {
                try {
                    workorderMessageProducer.closeCustomerTag(workOrderDO);
                } catch (Exception e) {
                    log.error("close customer Tag failed. e: ", e);
                }
            }
        }

        return facadeResponse;
    }

    private static CSWorkorderConfigVO convertConfigDOToVo(CSWorkOrderConfigDO csWorkOrderConfigDO) {
        CSWorkorderConfigVO vo = new CSWorkorderConfigVO();
        vo.setCreatedTime(csWorkOrderConfigDO.getCreationDate());
        vo.setProblemType(csWorkOrderConfigDO.getProblemType());
        vo.setBusinessType(csWorkOrderConfigDO.getBusinessType());
        vo.setWorkorderType(csWorkOrderConfigDO.getWorkorderType());
        vo.setGroupId(csWorkOrderConfigDO.getGroupId());
        vo.setStatus(csWorkOrderConfigDO.getStatus());
        vo.setOpLog(csWorkOrderConfigDO.getOpLog());
        vo.setExtra(csWorkOrderConfigDO.getExtra());
        vo.setMainType(csWorkOrderConfigDO.getMainType());
        return vo;
    }

    private FacadeResponse updateWOS(List<CSWorkOrderDO> workorderDOS, List<CSWorkOrderOpLogDO> opLogDOS) {

        FacadeResponse facadeResponse = mongoTransactionTemplate.execute(status -> {

            if (csWorkOrderOpLogDAO.batchInsert(opLogDOS).size() != opLogDOS.size()) {
                status.setRollbackOnly();
                return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
            }

            for (CSWorkOrderDO workOrderDO : workorderDOS) {
                if (csWorkOrderDAO.updateByIdSelective(workOrderDO) == null) {
                    status.setRollbackOnly();
                    return getFacadeResponseWithFailed(AttaResponseCode.UNKNOWN_DATA_ACCESS_ERROR);
                }
            }

            return getFacadeResponseWithSuccess();
        });

        assert facadeResponse != null;
        if (facadeResponse.isSucceeded()) {
            workorderDOS.forEach(csWorkOrderDO -> workorderMessageProducer.sendPreAuditStatusEvent(csWorkOrderDO));
        }

        return facadeResponse;
    }

    //转移负责之后,把原工单负责人加入到自动分配用户队列中
    private void enterQueue(String userId) {
        try {
            if (!StringUtils.isEmpty(userId) && !userId.equals(SystemConst.SYSTEM_USER_ID)) {
                WorkOrderDistributionRequest request = new WorkOrderDistributionRequest();
                request.setUserId(userId);
                User user = bossUserClient.queryByBossUserId(userId);
                request.setUserName(null != user ? user.getUserName() : "");
                csWorkOrderAutomaticAllocationService.distribution(request);
            }
        } catch (Exception e) {
            log.warn("【user queue】user enter queue :{} fail", userId);
        }
    }

    //把用户从自动分配用户队列中移出
    private void removeUser(String userId) {
        try {
            if (!StringUtils.isEmpty(userId)) {
                csWorkOrderUserService.remove(userId);
            }
        } catch (Exception e) {
            log.warn("【user queue】remove user :{} fail", userId);
        }
    }

    private void sendWorkOrderCompleteMessage(CSWorkOrderDO csWorkOrderDO) {
        //如果是技术工单需要发送通知
        try {
            if (!isItWorkOrder(csWorkOrderDO.getWorkOrderConfigId())) {
                return;
            }
            //1,给创建人发送消息
            messageManager.sendITCloseMessage(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getCreatedBy(), WorkOrderOptType.IT_CLOSE);
        } catch (Exception e) {
            log.error("【技术工单】发送消息失败", e);
        }
    }

    private boolean isItWorkOrder(String workOrderConfigId) {
        try {
            CSWorkorderConfigVO configVO = cSWorkOrderConfigService.getValidConfigVOByConfigId(workOrderConfigId);
            if (null == configVO) {
                log.warn("根据工单类型ID:{},为查询到工单类型", workOrderConfigId);
                return false;
            }
            if (null == workOrderConfig || CollectionUtils.isEmpty(workOrderConfig.getInfo()) || null == workOrderConfig.getInfo().get(WorkOrderConfig.ConfigKey.it)) {
                log.warn("没有找到it工单配置");
                return false;
            }
            WorkOrderConfig.configInfo configInfo = workOrderConfig.getInfo().get(WorkOrderConfig.ConfigKey.it);
            if (configVO.getBusinessType().equals(configInfo.getBusinessType()) &&
                    (StringUtils.isEmpty(configInfo.getWorkOrderType()) || configVO.getWorkorderType().equals(configInfo.getWorkOrderType())) &&
                    (StringUtils.isEmpty(configInfo.getProblemType()) || configVO.getProblemType().equals(configInfo.getWorkOrderType()))) {
                return true;
            }
        } catch (Exception e) {
            log.warn("【技术工单】当前工单是否是it工单异常", e);
        }
        return false;
    }

    /**
     * 构建操作日志
     *
     * @param workOrderId
     * @param status
     * @param operatorUserId
     * @param suggestion
     * @return
     */
    private CSWorkOrderOpLogDO buildOpLogDO(String workOrderId
            , CSWorkOrderStatus status
            , String operatorUserId
            , String suggestion) {

        CSWorkOrderOpLogDO opLogDO = new CSWorkOrderOpLogDO();
        opLogDO.setOpLogId(AttaIdUtils.getByProdCode(AttaIdUtils.ProdCode.CUSTOMER_SERVICE_WORKORDER_OP_LOG));
        opLogDO.setOperatorUserId(operatorUserId);
        opLogDO.setOpTime(new Date());
        opLogDO.setStatus(status.name());
        opLogDO.setSuggestion(suggestion);
        opLogDO.setWorkorderId(workOrderId);

        return opLogDO;
    }

    private void upgradeOrBackWorkOrder(List<DataPersistenceCommander> commanders, WorkOrderUpdateReq request, boolean showToCustomer) {
        request.validate();
        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.queryCSWorkOrderByWorkOrderId(request.getWorkOrderId());
        csWorkOrderDO.setCurrentDealUserId(request.getOperatorUserId());
        csWorkOrderDO.setBeforeUpgradePrincipalId(request.getBeforeUpdatePrincipalUserId());
        csWorkOrderDO.setPrincipalUserId(request.getPrincipalUserId());
        csWorkOrderDO.setOrderLevel(request.getOrderLevel());
        csWorkOrderDO.setStatus(request.getStatus());

        if (request.isUpgradeFlag()) {
            // 打标升级标签 舆情升级、普通升级、协同升级
            allocateHandleService.setUpgradeLabelByUpgrade(request, csWorkOrderDO);
            // 如果是协同升级,是否需要智能托管
            if (CSWorkOrderUpgradeSource.COLLABORATION_UPGRADE.name().equals(csWorkOrderDO.getUpgradeSource())) {
                hostingService.intelligentHostingStart(csWorkOrderDO);
            }
        } else {
            // 回退逻辑 回退升级标签
            allocateHandleService.backUpgradeLabel(request, csWorkOrderDO);
        }

        /* TODO 迁移变更:用户咨询类型的单子需要修改 showToCustomer字段 */
//        String workOrderConfigId = csWorkOrderDO.getWorkOrderConfigId();
//        if (StringUtils.isNotEmpty(workOrderConfigId)) {
//            CSWorkOrderConfigDO configDO = csWorkOrderConfigDAO.queryValidByConfigId(workOrderConfigId);
//            if (null != configDO && SHOW_TO_CUSTOMER.contains(configDO.getBusinessType())) {
//                csWorkOrderDO.setShowToCustomer(showToCustomer);
//            }
//        }
        commanders.add(() -> CSExceptionAssert.isTrue(csWorkOrderDAO.updateByIdSelective(csWorkOrderDO) != null
                , CSOrderBizErrorCode.SYSTEM_ERROR, "更新失败"));
        //云雀平台新协同工单通知工单变更处理人
        if (!Strings.isNullOrEmpty(csWorkOrderDO.getSkylarkCollaborationId()) && !Strings.isNullOrEmpty(request.getPrincipalUserId())) {
            User user = bossUserClient.queryByBossUserId(request.getPrincipalUserId());
            skylarkManager.notifyChangePrincipal(csWorkOrderDO.getWorkorderId(), csWorkOrderDO.getSkylarkCollaborationId(),
                    user.getBossUserId(), user.getUserName());

        }
    }

    /**
     * 发送钉钉消息给指定用户
     *
     * @param bossUserId
     * @param workOrderId
     */
    private void notify(String bossUserId, String workOrderId) {
        if (StringUtil.isEmpty(bossUserId)) {
            return;
        }
        User user = bossUserClient.getByBossUserId(bossUserId);
        if (null == user || StringUtil.isEmpty(user.getDingUserId())) {
            return;
        }
        MessageRequestVO messageRequestVO = new MessageRequestVO();
        messageRequestVO.setRequestId(UUID.randomUUID().toString());
        messageRequestVO.setTemplateType(MessageTemplateType.JIRA_NOTIFY.name());
        messageRequestVO.setTargetList(Lists.newArrayList(MessageTargetVO.builder("").dingUserId(user.getDingUserId()).build()));
        HashMap<String, String> msgContext = Maps.newHashMap();
        msgContext.put("message", "您的升级工单(" + workOrderId + ")已退回,请至BOSS后台查看处理");
        messageRequestVO.setParamValueMap(msgContext);
        ResponseEntity<Map<String, String>> resp = messageFacadeClient.sendMessage(messageRequestVO);
        log.info("send ding message response is :{}", resp);
        Assert.isTrue(resp != null && resp.getStatusCode() == HttpStatus.OK, "jira notify: notify failed");
    }

    private AppointmentResultVO isAllowed(CSWorkOrderDO csWorkOrderDO) {

        AppointmentResultVO resultVO = new AppointmentResultVO();
        resultVO.setIsSuccess(false);

        // 工单不存在
        if (csWorkOrderDO == null) {
            resultVO.setReason(AppointmentResultVO.FailedReason.OrderNotExist);
            return resultVO;
        }
        // 工单已结案
        if (finalCSWorkOrderStatus.contains(csWorkOrderDO.getStatus())) {
            resultVO.setReason(AppointmentResultVO.FailedReason.OrderCompleted);
            return resultVO;
        }

        CSWorkorderCommRecordVO commRecordVO = commRecordService.getNewestStaffRecordsByWorkOrderId(csWorkOrderDO.getWorkorderId());
        WorkOrderInstructionDO instructionDO = workOrderInstructionDAO.getLatestInstructionByReferenceId(csWorkOrderDO.getWorkorderId()
                , WorkOrderInstructionType.CustomerAppointment);

        if (Objects.nonNull(instructionDO) && Objects.equals(instructionDO.getStatus(), WorkOrderInstructionStatus.NEW)) {
            resultVO.setReason(AppointmentResultVO.FailedReason.AppointmentNotExpired);
            resultVO.setIsSuccess(false);
            return resultVO;
        }
        // 查询最新的待办是否过期
        QueryLatestTodoTaskRequest latestTaskRequest = new QueryLatestTodoTaskRequest();
        latestTaskRequest.setBusinessId(csWorkOrderDO.getWorkorderId());
        latestTaskRequest.setTodoTaskType(TodoTaskType.AppointmentCallBack.name());
        TodoTaskVO todoTaskVO = todoTaskManager.queryLatestTodoTask(latestTaskRequest);
        if (todoTaskVO != null && new Date().before(todoTaskVO.getEndTime())) { // 待办未过期的情况
            // 在未过期的时间内
            //1、没有新增沟通记录,不允许创建
            //2、最新的沟通记录,创建时间在待办创建时间之前的。不允许创建
            if (commRecordVO == null || commRecordVO.getCreatedTime().before(todoTaskVO.getCreationDate())) {
                resultVO.setReason(AppointmentResultVO.FailedReason.AppointmentNotExpired);
                resultVO.setIsSuccess(false);
                return resultVO;
            }
        }
        resultVO.setIsSuccess(true);
        return resultVO;
    }

    /**
     * 工单对象转换为工单结果对象
     */
    private CSWorkOrderResultVO resultConvert(CSWorkOrderDO csWorkOrderDO) {
        CSWorkOrderResultVO resultVO = new CSWorkOrderResultVO();
        resultVO.setFirmId(csWorkOrderDO.getFirmId());
        resultVO.setWorkOrderId(csWorkOrderDO.getWorkorderId());
        resultVO.setCompletedTime(csWorkOrderDO.getCompletedTime());
        resultVO.setStatus(csWorkOrderDO.getStatus().name());

        ActionDO actionDO = actionDAO.getLastUpdateActionBySceneId(csWorkOrderDO.getWorkorderId());
        if (null != actionDO && null != actionDO.getActionResults() && !actionDO.getActionResults().isEmpty()) {
            resultVO.setAuditResult(null != actionDO.getActionResults().get(PRE_AUDIT_RESULT_KEY) ? actionDO.getActionResults().get(PRE_AUDIT_RESULT_KEY).toString() : "");
            resultVO.setRejectReason(null != actionDO.getActionResults().get(PRE_AUDIT_SUGGESTION_KEY) ? actionDO.getActionResults().get(PRE_AUDIT_SUGGESTION_KEY).toString() : "");
        }
        return resultVO;
    }

    /**
     * @param from formAnswer  附件存放于answers中 文本附件对应TEXT_INPUT 其他附件对应UPLOAD
     * @return answers中为null的元素直接skip,UploadAnswer.valueList中为null的元素直接skip.to为空对象返回null
     */
    private CSWorkOrderFeedbackAttachmentVO toCSWorkOrderFeedbackAttachmentVO(FormAnswerVO from) {
        CSWorkOrderFeedbackAttachmentVO to = new CSWorkOrderFeedbackAttachmentVO();
        List<AttachmentVO> attachmentList = new ArrayList<>();
        for (FormAnswerVO.Answer answer : from.getAnswers()) {
            if (Objects.isNull(answer)) {
                continue;
            }
            //text_input存储文字反馈
            if (QuestionTypeEnum.TEXT_INPUT.equals(answer.getQuestionType())) {
                to.setContent((String) answer.getValue());
            }
            //upload中的valueList存储附件url
            if (QuestionTypeEnum.UPLOAD.equals(answer.getQuestionType())) {
                List<String> valueList = ((FormAnswerVO.UploadAnswer) answer).getValueList();
                if (!CollectionUtils.isEmpty(valueList)) {
                    for (String value : valueList) {
                        if (value == null) {
                            continue;
                        }
                        AttachmentVO attachmentVO = new AttachmentVO();
                        attachmentVO.setUrl(value);
                        attachmentList.add(attachmentVO);
                    }
                }
            }
        }
        to.setAttachmentVOS(attachmentList);
        return to.getContent() == null && CollectionUtils.isEmpty(to.getAttachmentVOS()) ? null : to;
    }



    @Override
    public CSWorkOrderVO queryLastOneByAssignFirm(String assignId, String firmId) {

        Query query = new Query();
        Criteria criteria = Criteria.where(CSWorkOrderDO.FIRM_ID).is(firmId)
                .and(CSWorkOrderDO.ASSIGN_ID).is(assignId);
        query.addCriteria(criteria);
        query.with(Sort.by(Sort.Direction.DESC, CSWorkOrderDO.CREATION_DATE)).limit(1);

        CSWorkOrderDO csWorkOrderDO = csWorkOrderDAO.findOne(query);
        CSWorkOrderVO csWorkOrderVO = toWorkOrderVO(csWorkOrderDO);
        if (Objects.isNull(csWorkOrderVO)) {
            return null;
        }

        csWorkOrderVO.setHistoryPreAuditWorkOrderVOList(getHistoryPreAuditWorkOrders(csWorkOrderDO));
        return csWorkOrderVO;
    }

    private void pushHostingComplete(CSWorkOrderCommRecordSaveRequest request) {
        HostingCompleteEvent event = new HostingCompleteEvent();
        if (request.isOptBySale()) {
            // 销售留言
            event.setCompleteType(CSWorkOrderHostingCompleteType.SALE_LEAVE_MESSAGE.name());
        } else if (CsWoButtonSource.EDIT_SUBMIT.name().equals(request.getButtonSource())) {
            // 工单跟进
            event.setRelateSession(!CollectionUtils.isEmpty(request.getSessionIds()) || !CollectionUtils.isEmpty(request.getCallCenterIds()));
            event.setOptDate(new Date());
            event.setCompleteType(CSWorkOrderHostingCompleteType.ADD_FOLLOW.name());
        } else {
            return;
        }
        event.setWorkorderId(request.getWorkorderId());
        event.setCommRecord(request.getCommRecord());
        csWorkOrderHostingEndProducer.send(event);
    }

}
相关推荐
潜心一志2 小时前
HALCON软件——数据结构
学习·计算机视觉
秦哈哈2 小时前
【HelloAgents】学习笔记(一)
学习·ai·agent
wuyk5552 小时前
从零吃透 MQTT 通信|第 10 章 阿里云 / 腾讯云 MQTT 设备完整对接实战,三元组、签名、设备上云调试
c语言·开发语言·stm32·学习·阿里云·云计算·腾讯云
fanged3 小时前
English at Work小结(TODO)
学习
X-future4263 小时前
OpenClaw源码学习
学习
kaixin_啊啊3 小时前
中国研究生数学建模竞赛(华为杯)学习笔记——三维瀑布图绘图代码
笔记·学习·数学建模
动词ing3 小时前
【学习笔记】数据结构(链表合并 双指针合并有序链表)
数据结构·笔记·学习
风之清扬3 小时前
Agent学习之四-初识Agent CLI
人工智能·科技·学习
传奇开心果编程3 小时前
【xilem0.4基础语法学与练】第45课 xilem_core
学习·rust·前端框架