package cn.superdesk.uniorder.service.accounting.score.customer.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.superdesk.uniorder.common.enums.accounting.AccountingOrderInvoiceApplyStatusEnum;
import cn.superdesk.uniorder.common.enums.accounting.AccountingOrderIsSendEmailEnum;
import cn.superdesk.uniorder.common.enums.accounting.AccountingOrderTypeDimEnum;
import cn.superdesk.uniorder.common.enums.accounting.customer.AccountingOrderCustomerAccountingStatusEnum;
import cn.superdesk.uniorder.common.enums.common.*;
import cn.superdesk.uniorder.common.enums.invoice.customer.InvoiceApplyCustomerInvoiceProgressingStatusEnum;
import cn.superdesk.uniorder.common.enums.invoice.customer.InvoiceApplyCustomerMailTickStatusEnum;
import cn.superdesk.uniorder.common.enums.order.UnifiedOrderCreditTypeDefaultEnum;
import cn.superdesk.uniorder.common.json.AnJsonUtils;
import cn.superdesk.uniorder.common.lamda.AnLamdaUtils;
import cn.superdesk.uniorder.common.numerical.AnBeanUtils;
import cn.superdesk.uniorder.common.numerical.AnIntegerUtils;
import cn.superdesk.uniorder.common.numerical.AnStringUtils;
import cn.superdesk.uniorder.common.rest.response.R;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.dao.convert.DtoSugarUpUtils;
import cn.superdesk.uniorder.dao.entity.accounting.score.customer.AccountingScoreCustomer;
import cn.superdesk.uniorder.dao.entity.accounting.score.customer.AccountingScoreCustomerExample;
import cn.superdesk.uniorder.dao.entity.accounting.score.customer.AccountingScoreCustomerXCredit;
import cn.superdesk.uniorder.dao.entity.accounting.score.customer.AccountingScoreCustomerXCreditExample;
import cn.superdesk.uniorder.dao.entity.invoice.common.InvoiceBankInfo;
import cn.superdesk.uniorder.dao.entity.sys.SysUser;
import cn.superdesk.uniorder.dao.mapper.accounting.score.customer.AccountingScoreCustomerMapper;
import cn.superdesk.uniorder.dao.mapper.accounting.score.customer.AccountingScoreCustomerMapperEx;
import cn.superdesk.uniorder.dao.mapper.accounting.score.customer.AccountingScoreCustomerXCreditMapper;
import cn.superdesk.uniorder.dao.mapper.accounting.score.customer.AccountingScoreCustomerXCreditMapperEx;
import cn.superdesk.uniorder.dao.mapper.base.UnifiedOrderCreditMapper;
import cn.superdesk.uniorder.dto.param.accounting.score.customer.AccountingOrderScoreCustomerNotYetDimCreditScene3QueryCriteria;
import cn.superdesk.uniorder.dto.param.accounting.score.customer.AccountingOrderScoreCustomerOpDetailQueryCriteria;
import cn.superdesk.uniorder.dto.param.accounting.score.customer.AccountingOrderScoreCustomerQueryCriteria;
import cn.superdesk.uniorder.dto.param.invoice.customer.InvoiceApplyCustomerQueryCriteriaV2_1;
import cn.superdesk.uniorder.dto.vo.accounting.score.customer.AccountingOrderScoreCustomerDetailVo;
import cn.superdesk.uniorder.dto.vo.accounting.score.customer.AccountingOrderScoreCustomerHeadVo;
import cn.superdesk.uniorder.dto.vo.accounting.score.customer.AccountingOrderScoreCustomerListItemVo;
import cn.superdesk.uniorder.dto.vo.invoice.common.InvoiceBankInfoVo;
import cn.superdesk.uniorder.dto.vo.invoice.customer.InvoiceApplyCustomerPageVo;
import cn.superdesk.uniorder.dto.vo.invoice.customer.InvoiceApplyDetailDimCreditItemVo;
import cn.superdesk.uniorder.dto.vo.order.CreditTypeItemVo;
import cn.superdesk.uniorder.executor.scheduler.kafka._thirdpart.qchjc.QchjcStatementKafkaEventPublisher;
import cn.superdesk.uniorder.service.accounting.score.customer.AccountingScoreCustomerService;
import cn.superdesk.uniorder.service.accounting.score.customer.strategy.AccountingScoreCustomerStrategy;
import cn.superdesk.uniorder.service.accounting.score.customer.strategy.AccountingScoreCustomerStrategyContext;
import cn.superdesk.uniorder.service.order.base.ScoreOrderService;
import cn.superdesk.uniorder.service.order.base.UnifiedOrderCreditService;
import cn.superdesk.uniorder.service.invoice.common.InvoiceBankInfoService;
import cn.superdesk.uniorder.service.invoice.customer.InvoiceApplyCustomerService;
import cn.superdesk.uniorder.service.invoice.customer.InvoiceApplyXGoodsService;
import cn.superdesk.uniorder.service.setting.UnifiedSnConfigService;
import cn.superdesk.uniorder.service.sys.SysUserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;


/**
 * @Description: Java类作用描述
 * @Author: linjinyu
 * @CreateDate: 6/28/20 1:03 PM
 * @UpdateUser: linjinyu
 * @UpdateDate: 6/28/20 1:03 PM
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Slf4j
@Service
public class AccountingScoreCustomerServiceImpl implements AccountingScoreCustomerService {

    @Autowired
    private AccountingScoreCustomerMapper   accountingScoreCustomerMapper;
    @Autowired
    private AccountingScoreCustomerMapperEx accountingScoreCustomerMapperEx;

    @Autowired
    private AccountingScoreCustomerXCreditMapper   accountingScoreCustomerXCreditMapper;
    @Autowired
    private AccountingScoreCustomerXCreditMapperEx accountingScoreCustomerXCreditMapperEx;

    @Autowired
    private UnifiedOrderCreditMapper  unifiedOrderCreditMapper;
    @Autowired
    private UnifiedOrderCreditService unifiedOrderCreditService;

    @Autowired
    private InvoiceBankInfoService invoiceBankInfoService;

    @Autowired
    private ScoreOrderService scoreOrderService;

//	@Autowired
//	private SysConfigService sysConfigService;

    @Autowired
    private UnifiedSnConfigService unifiedSnConfigService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private InvoiceApplyCustomerService invoiceApplyCustomerService;

    @Autowired
    private InvoiceApplyXGoodsService invoiceApplyXGoodsService;

    @Autowired
    private AccountingScoreCustomerStrategyContext accountingScoreCustomerStrategyContext;

    /**************************************** superdesk-uniorder-executor start ****************************************/

    @Autowired
    private QchjcStatementKafkaEventPublisher qchjcStatementEventPublisher;

    /**************************************** superdesk-uniorder-executor   end ****************************************/


    @Override
    public int insertSelective(AccountingScoreCustomer scoreAccountingCustomer) {
        return accountingScoreCustomerMapper.insertSelective(scoreAccountingCustomer);
    }

    @Override
    public int updateByPrimaryKeySelective(AccountingScoreCustomer scoreAccountingCustomer) {

        return accountingScoreCustomerMapper.updateByPrimaryKeySelective(scoreAccountingCustomer);
    }

    public int opUpdateByPrimaryKeySelective(AccountingScoreCustomer scoreAccountingCustomer, LoginSysUser loginSysUser) {

        return accountingScoreCustomerMapper.updateByPrimaryKeySelective(scoreAccountingCustomer);

    }


    @Override
    public void deleteObj(Long id) {
        accountingScoreCustomerMapper.deleteByPrimaryKey(id);
    }


    @Override
    public AccountingScoreCustomer findById(Long id) {
        return accountingScoreCustomerMapper.selectByPrimaryKey(id);
    }

    @Deprecated
    @Override
    public PageInfo<AccountingScoreCustomer> findByPaging(Map<String, Object> params, Integer pageNo, Integer pageSize) {
        return null;
    }

    @Override
    public List<AccountingScoreCustomer> findByIds(List<Long> accountingOrderIds) {

        AccountingScoreCustomerExample example = new AccountingScoreCustomerExample();
        example.createCriteria()
                .andIdIn(accountingOrderIds);

        List<AccountingScoreCustomer> resultList = accountingScoreCustomerMapper.selectByExample(example);

        return resultList;

    }

    public Map<Long, AccountingScoreCustomer> convertAccountingScoreCustomerList2MapAccountingOrderIdEntity(List<Long> accountingOrderIds) {

        Map<Long, AccountingScoreCustomer> resultMap = new HashMap<>();

        List<AccountingScoreCustomer> resultList = findByIds(accountingOrderIds);

        resultMap = resultList.stream().collect(Collectors.toMap(AccountingScoreCustomer::getId, v -> v, (k, v) -> v));

        return resultMap;
    }


    public int opResetNeedReviseMark(Long accountingOrderId) {

        int ret = 0;

        AccountingScoreCustomer forUpdate = new AccountingScoreCustomer();

        forUpdate.setId(accountingOrderId);
        forUpdate.setNeedReviseMark(CommonNeedReviseMarkEnum.正常.getKey());

        ret = accountingScoreCustomerMapper.updateByPrimaryKeySelective(forUpdate);

        return ret;
    }

    @Override
    public List<AccountingScoreCustomer> findAll(Map<String, Object> queryMap) {
        return null;
    }


    public AccountingScoreCustomer findByAccountingOrderSn(String accountingOrderSn) {

        AccountingScoreCustomerExample example = new AccountingScoreCustomerExample();
        example.createCriteria()
                .andAccountingOrderSnEqualTo(accountingOrderSn);

        List<AccountingScoreCustomer> result = accountingScoreCustomerMapper.selectByExample(example);

        if (CollUtil.isNotEmpty(result)) {
            return result.get(0);
        } else {
            return null;
        }

    }

    /****************************************** 积分结算 对账单 start ******************************************/

    /**
     * 积分结算2.1 整改 mark by linjinyu at 2022022
     *
     * @param queryCriteria
     * @return
     */
    @Override
    public List<Long> getAccountingXCreditIdsByQueryCriteria(AccountingOrderScoreCustomerOpDetailQueryCriteria queryCriteria) {

        AccountingScoreCustomer search = accountingScoreCustomerMapper.selectByPrimaryKey(queryCriteria.getAccountingOrderId());

        AccountingScoreCustomerXCreditExample          example  = new AccountingScoreCustomerXCreditExample();
        AccountingScoreCustomerXCreditExample.Criteria criteria = example.createCriteria();

        criteria.andAccountingOrderSnEqualTo(search.getAccountingOrderSn());

        if (ObjectUtil.isNotNull(queryCriteria.getCreditAccountingState()) && queryCriteria.getCreditAccountingState().compareTo(0) != 0) {
            criteria.andAccountingStateEqualTo(queryCriteria.getCreditAccountingState());
        }

        List<AccountingScoreCustomerXCredit> tempResult = accountingScoreCustomerXCreditMapper.selectByExample(example);

        log.info("getAccountingXCreditIds \n{}", JSONUtil.toJsonStr(tempResult));
        log.info("getAccountingXCreditIds \n{}", JSONUtil.toJsonStr(tempResult
                .stream()
                .map(AccountingScoreCustomerXCredit::getUnifiedOrderCreditId)
                .collect(Collectors.toList())));

        return tempResult
                .stream()
                .map(AccountingScoreCustomerXCredit::getUnifiedOrderCreditId)
                .collect(Collectors.toList());
    }


    @Override
    public R<List<AccountingOrderScoreCustomerListItemVo>> pageQueryAccountingOrderList(AccountingOrderScoreCustomerQueryCriteria queryCriteria, LoginSysUser loginSysUser) {


        PageHelper.startPage(queryCriteria.getPageNumber(), queryCriteria.getPageSize());
//        List<AccountingOrderScoreCustomerListItemVo>     resultList = accountingScoreCustomerMapperEx.getAccountingOrderList(queryCriteria);
        List<AccountingOrderScoreCustomerListItemVo>     resultList = getAccountingOrderList(queryCriteria, loginSysUser);
        PageInfo<AccountingOrderScoreCustomerListItemVo> page       = new PageInfo<AccountingOrderScoreCustomerListItemVo>(resultList);
        PageHelper.clearPage();

//        // 附加信息
//        additionalInfoGetAccountingList(resultList);
//
//        // sugarUp
//        DtoSugarUpUtils.sugarUpAccountingOrderScoreCustomerVoList(resultList);

        /**************************************** 补丁程序 start ****************************************/

        /**
         * mark by linjinyu at 2022-05-139 06:56:45 脱钩 转 xxl job 发起

         // 修正 v2.1 统计信息
         patchNeedReviseMarkStatistics(resultList, loginSysUser);

         // 修正 v2.1 申请状态 fix invoiceStatus
         //	patchReviseInvoiceStatus(resultList, loginSysUser);

         // 修正 v2.2 寄票业务 逻辑
         patchNeedReviseMarkMailTickStatus(resultList, loginSysUser);

         // 修正 v2.3 serviceNo sourceFrom
         patchNeedReviseMarkServiceNoSourceFrom(resultList, loginSysUser);
         */

        /**************************************** 补丁程序   end ****************************************/


        return new R<List<AccountingOrderScoreCustomerListItemVo>>(page.getPageNum(), page.getPageSize(), (int) page.getTotal(), resultList);
    }

    @Override
    public List<AccountingOrderScoreCustomerListItemVo> getAccountingOrderList(AccountingOrderScoreCustomerQueryCriteria queryCriteria, LoginSysUser loginSysUser) {

        List<AccountingOrderScoreCustomerListItemVo> resultList = new ArrayList<>();

        resultList = accountingScoreCustomerMapperEx.getAccountingOrderList(queryCriteria);

        // 附加信息
        additionalInfoGetAccountingList(resultList);

        // sugarUp
        DtoSugarUpUtils.sugarUpAccountingOrderScoreCustomerVoList(resultList);

        /**************************************** 补丁程序 start ****************************************/

        /**
         * mark by linjinyu at 2022-05-19 06:56:45 脱钩 转 xxl job 发起

         // 修正 v2.1 统计信息
         patchNeedReviseMarkStatistics(resultList, loginSysUser);

         // 修正 v2.1 申请状态 fix invoiceStatus
         //	patchReviseInvoiceStatus(resultList, loginSysUser);

         // 修正 v2.2 寄票业务 逻辑
         patchNeedReviseMarkMailTickStatus(resultList, loginSysUser);

         // 修正 v2.3 serviceNo sourceFrom
         patchNeedReviseMarkServiceNoSourceFrom(resultList, loginSysUser);
         */

        /**************************************** 补丁程序   end ****************************************/


        return resultList;
    }

    /**
     * 修正统计信息
     *
     * @param resultList
     * @param loginSysUser
     */
    public void patchNeedReviseMarkStatistics(List<AccountingOrderScoreCustomerListItemVo> resultList, LoginSysUser loginSysUser) {

        log.info("进入 patchNeedReviseMarkStatistics");

        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        resultList.forEach(vo -> {
            if (ObjectUtil.isNotNull(vo.getNeedReviseMark())
                    && vo.getNeedReviseMark().compareTo(CommonNeedReviseMarkEnum.修正.getKey()) == 0) {

                List<Long> idsCredit = getAccountingXCreditIdsByQueryCriteria(
                        AccountingOrderScoreCustomerOpDetailQueryCriteria
                                .builder()
                                .accountingOrderId(vo.getId())
                                .build()
                );

                AccountingScoreCustomer accountingScoreCustomer = new AccountingScoreCustomer();
                List<InvoiceApplyDetailDimCreditItemVo> invoiceApplyDetailDimCreditItemVoList = scoreOrderService.getScoreOrderReconciliationNotYetScene3(
                        AccountingOrderScoreCustomerNotYetDimCreditScene3QueryCriteria
                                .builder()
                                .idsCredit(idsCredit)
                                .build(),
                        loginSysUser
                );

                // 重新统计
                statisticsScoreAccountingCustomerByCredit(accountingScoreCustomer, invoiceApplyDetailDimCreditItemVoList);

//                accountingScoreCustomer.setId(vo.getId());
//                accountingScoreCustomer.setNeedReviseMark(CommonNeedReviseMarkEnum.正常.getKey());
//
//                updateByPrimaryKeySelective(accountingScoreCustomer);
                int retReset = opResetNeedReviseMark(vo.getId());
            }
        });

    }

    /**
     * 修正申请状态 fix invoiceStatus
     *
     * @param resultList
     * @param loginSysUser
     */
    public void patchNeedReviseMarkInvoiceStatus(List<AccountingOrderScoreCustomerListItemVo> resultList, LoginSysUser loginSysUser) {

        log.info("进入 patchNeedReviseMarkInvoiceStatus");

        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        resultList.forEach(vo -> {
            if (ObjectUtil.isNotNull(vo.getNeedReviseMark())
                    && vo.getNeedReviseMark().compareTo(CommonNeedReviseMarkEnum.修数据_20220518.getKey()) == 0) {

                invoiceApplyXGoodsService.checkInvoiceStatusByAccountingOrderIdAndUpdateAccountingEntityFocusScore(
                        vo.getId(),
                        CommonBeRelatedEnum.失联,
                        loginSysUser
                );

//                AccountingScoreCustomer accountingScoreCustomer = new AccountingScoreCustomer();
//                accountingScoreCustomer.setId(vo.getId());
//                accountingScoreCustomer.setNeedReviseMark(CommonNeedReviseMarkEnum.正常.getKey());
//
//                updateByPrimaryKeySelective(accountingScoreCustomer);

                int retReset = opResetNeedReviseMark(vo.getId());

            }


        });

    }

    /**
     * 【2. 积分结算-2.3 发票管理-2.3.4 线上历史数据处理-已整理-20220425-后端待处理-20220427】
     * https://www.tapd.cn/33003868/prong/stories/view/1133003868001004189
     *
     * @param resultList
     * @param loginSysUser
     */
    public void patchNeedReviseMarkMailTickStatus(List<AccountingOrderScoreCustomerListItemVo> resultList, LoginSysUser loginSysUser) {

        log.info("进入 patchNeedReviseMarkMailTickStatus");

        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        resultList.forEach(vo -> {

            if (ObjectUtil.isNotNull(vo.getNeedReviseMark())
                    && vo.getNeedReviseMark().compareTo(CommonNeedReviseMarkEnum.修数据_20220505.getKey()) == 0) {

                /** mark by linjinyu at 20220505
                 2.3.4 线上历史数据处理
                 原积分结算开票申请下的“快递公司&快递单号”和“发票文件”数据需合并后挂靠到该开票申请所属的对账单下，不再挂靠在开票申请下。
                 合并后处理方式：
                 ①对账单内的快递公司只取第一个开票申请的快递公司，其他快递公司无需保留；
                 ②对账单内按快递单号的内容需去重，去重后挂靠到对账单下；
                 ③对账单内按发票文件的名称内容去重，去重后挂靠到对账单下；
                 */

                Integer[] invoiceProgressingStatusContains = {
                        InvoiceApplyCustomerInvoiceProgressingStatusEnum.待寄票.getKey(),
                        InvoiceApplyCustomerInvoiceProgressingStatusEnum.待回款.getKey(),
                        InvoiceApplyCustomerInvoiceProgressingStatusEnum.已完成.getKey()
                };

                Integer[] invoiceMailTickContains = {
                        InvoiceApplyCustomerMailTickStatusEnum.已寄票.getKey()
                };

                List<InvoiceApplyCustomerPageVo> applyCustomerPageVoList = invoiceApplyCustomerService.queryInvoiceApplyListFocusScoreV2_1(
                        InvoiceApplyCustomerQueryCriteriaV2_1
                                .builder()

                                .accountingOrderId(vo.getId())
                                .accountingOrderSn(vo.getAccountingOrderSn())

                                .invoiceProgressingStatusSelected(Arrays.asList(invoiceProgressingStatusContains))
                                .mailTickStatusSelected(Arrays.asList(invoiceMailTickContains))

                                .build()
                );


                AccountingScoreCustomer accountingScoreCustomer = new AccountingScoreCustomer();
                accountingScoreCustomer.setId(vo.getId());

                /**************************************** 逻辑 start ****************************************/

                log.info("\npatchReviseMailTickStatus\n{}", JSONUtil.toJsonPrettyStr(applyCustomerPageVoList));

                if (CollUtil.isNotEmpty(applyCustomerPageVoList)) {

                    Long   expressCompanyId;
                    String expressNo;
                    String attachmentUploadFilesUids;
                    String attachmentUploadFilesUidsDistinct;

                    List<Long>   expressCompanyIdList          = applyCustomerPageVoList.stream().map(o -> o.getExpressCompanyId()).distinct().collect(Collectors.toList());
                    List<String> expressNoList                 = applyCustomerPageVoList.stream().map(o -> o.getExpressNo()).distinct().collect(Collectors.toList());
                    List<String> attachmentUploadFilesUidsList = applyCustomerPageVoList.stream().map(o -> o.getAttachmentUploadFilesUids()).collect(Collectors.toList());


                    expressCompanyId = expressCompanyIdList.get(0);
                    expressNo = expressNoList.get(0);
                    attachmentUploadFilesUids = AnStringUtils.stringListConvertToString(attachmentUploadFilesUidsList);
                    attachmentUploadFilesUidsDistinct = AnStringUtils.stringArrayConvertToStringList(attachmentUploadFilesUids, ",").stream().distinct().collect(Collectors.joining());

                    accountingScoreCustomer.setExpressCompanyId(expressCompanyId);
                    accountingScoreCustomer.setExpressNo(expressNo);
                    accountingScoreCustomer.setAttachmentUploadFilesUids(attachmentUploadFilesUidsDistinct);

                }

                /**************************************** 逻辑   end ****************************************/

                updateByPrimaryKeySelective(accountingScoreCustomer);

                int retReset = opResetNeedReviseMark(vo.getId());

            }
        });

    }

    /**
     * 【5. 积分结算 5.7 线上历史数据处理_积分结算 5.7.2 发票申请管理-20220513-初步整理】
     * https://www.tapd.cn/33003868/prong/stories/view/1133003868001004351
     * 【5. 积分结算 5.7 线上历史数据处理_积分结算 5.7.3 金税盘发票-20220513-初步整理】
     * https://www.tapd.cn/33003868/prong/stories/view/1133003868001004352
     *
     * @param resultList
     * @param loginSysUser
     */
    public void patchNeedReviseMarkServiceNoSourceFrom(List<AccountingOrderScoreCustomerListItemVo> resultList, LoginSysUser loginSysUser) {

        log.info("进入 patchReviseServiceNoSourceFrom");

        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        for (AccountingOrderScoreCustomerListItemVo vo : resultList) {


            if (ObjectUtil.isNotNull(vo.getNeedReviseMark())
                    && vo.getNeedReviseMark().compareTo(CommonNeedReviseMarkEnum.修数据_20220519.getKey()) == 0) {


                log.info("{}", JSONUtil.toJsonStr(vo));

                Long                       accountingOrderId          = vo.getId();
                String                     accountingOrderSn          = vo.getAccountingOrderSn();
                AccountingOrderTypeDimEnum accountingOrderTypeDimEnum = AccountingOrderTypeDimEnum.积分订单;


                String serviceNo  = vo.getServiceNo();
                String sourceFrom = vo.getSourceFrom();

                // 目的
                // 1. 首先是传导

                /**************************************** 验证 start ****************************************/

                if (StrUtil.isBlank(sourceFrom)) {
                    continue;
                }

                /**************************************** 验证 start ****************************************/

                int retFix = invoiceApplyCustomerService.pathFixFieldServiceNoSourceFrom(accountingOrderSn, accountingOrderTypeDimEnum, serviceNo, sourceFrom, loginSysUser);

                int retReset = opResetNeedReviseMark(accountingOrderId);

            } else {
                log.info("{} {}", vo.getNeedReviseMark(), JSONUtil.toJsonStr(vo));
            }
        }

    }

    // 附加信息
    public void additionalInfoGetAccountingList(List<AccountingOrderScoreCustomerListItemVo> result) {

        if (CollUtil.isEmpty(result)) {
            return;
        }

        List<Long> sysUserIds = new ArrayList<>();

        List<Long> createUserIds = result.stream().filter(o -> o.getCreateUserId() != null).filter(AnLamdaUtils.distinctByKey(o -> o.getCreateUserId())).map(o -> o.getCreateUserId()).collect(Collectors.toList());
        List<Long> updateUserIds = result.stream().filter(o -> o.getUpdateUserId() != null).filter(AnLamdaUtils.distinctByKey(o -> o.getUpdateUserId())).map(o -> o.getUpdateUserId()).collect(Collectors.toList());

        sysUserIds.addAll(createUserIds);
        sysUserIds.addAll(updateUserIds);

        sysUserIds = sysUserIds.stream().distinct().collect(Collectors.toList());

        log.info("\n{}", JSONUtil.toJsonStr(sysUserIds));

        Map<Long, SysUser> sysUserMap = sysUserService.convertSysUserIds2MapSysUserId2Entity(sysUserIds);

        for (AccountingOrderScoreCustomerListItemVo vo : result) {

            vo.setAccountingStatusName(AccountingOrderCustomerAccountingStatusEnum.getDesc(vo.getAccountingStatus()));
            vo.setInvoiceStatusName(AccountingOrderInvoiceApplyStatusEnum.getDesc(vo.getInvoiceStatus()));
            vo.setSendEmailStatusName(AccountingOrderIsSendEmailEnum.getDesc(vo.getIsSendEmail()));

            /**************************************** mark by linjinyu at 20220516 v2.3 start ****************************************/

            // 补 订单类型-订单来源-引用enum CommonServiceNoEnum
            if (ObjectUtil.isNotNull(vo.getServiceNo())) {
                vo.setServiceNoName(CommonServiceNoEnum.getByServiceNo(vo.getServiceNo()).getServiceName());
            } else {
                vo.setServiceNoName(CommonServiceNoEnum.UNKNOW.getServiceName());
            }
            // 补 操作来源-引用enum CommonSourceFromEnum
            if (ObjectUtil.isNotNull(vo.getSourceFrom())) {
                vo.setSourceFromName(CommonSourceFromEnum.getBySourceFromCode(vo.getSourceFrom()).getSourceFromName());
            } else {
                vo.setSourceFromName(CommonSourceFromEnum.UNKNOW.getSourceFromName());
            }

            /**************************************** mark by linjinyu at 20220516 v2.3 start ****************************************/

            // 修人
            if (sysUserMap.get(vo.getCreateUserId()) != null) {
                vo.setCreateUserName(sysUserMap.get(vo.getCreateUserId()).getFullname());
            } else if (vo.getCreateUserId().equals(1L)) {
                vo.setCreateUserName("系统");
            }

            if (sysUserMap.get(vo.getUpdateUserId()) != null) {
                vo.setUpdateUserName(sysUserMap.get(vo.getUpdateUserId()).getFullname());
            } else if (vo.getCreateUserId().equals(1L)) {
                vo.setUpdateUserName("系统");
            }

            // 修积分类型
            if (StrUtil.isNotBlank(vo.getCreditTypeStruct())) {
                vo.setCreditTypeList(AnJsonUtils.toList(vo.getCreditTypeStruct(), CreditTypeItemVo.class));
            }

        }

        // 这个不知道是什么需求的,这样查,会超级慢
//			for (int i = 0; i < viewUnifiedOrderGoodsList.size(); i++) {
//
//				ViewUnifiedOrderGoods item = viewUnifiedOrderGoodsList.get(i);
//
//				item.setGoodsAmount(new BigDecimal(item.getPrice().doubleValue() * item.getQuantity()));
//
//				ViewUnifiedOrderGoodsUtils.setInvStateName(item);
//				ViewUnifiedOrderGoodsUtils.setPayTypeName(item);
//
//				// 临时 查找发票信息
//
//				Map<String, Object> invQuery = new HashMap<>();
//				invQuery.put("sn", item.getOrderSn());
//				List<InvoiceApply> applies = invoiceApplyDubboConsumerService.findInvoiceBySn(invQuery);
//
//				if (applies != null && applies.size() == 1) {
//					item.setInvNumber(applies.get(0).getInvoiceNumber());
//					item.setInvSn(applies.get(0).getInvoiceNo());
//				}
//			}
    }


    /**
     * @param queryCriteria
     * @return
     */
    @Override
    public AccountingOrderScoreCustomerHeadVo getAccountingOrderListHeadSum(AccountingOrderScoreCustomerQueryCriteria queryCriteria) {

        return accountingScoreCustomerMapperEx.getAccountingOrderListHeadSum(queryCriteria);

    }

    /****************************************** 积分结算 对账单   end ******************************************/


    /****************************************** 积分结算 对账单 详情 start ******************************************/


    @Override
    public AccountingOrderScoreCustomerListItemVo getAccountingOrderScoreCustomerVoByAccountingOrderId(AccountingOrderScoreCustomerOpDetailQueryCriteria queryCriteria, LoginSysUser loginSysUser) {

        List<AccountingOrderScoreCustomerListItemVo> setAccountingObj = getAccountingOrderList(
                AccountingOrderScoreCustomerQueryCriteria
                        .builder()
                        .accountingOrderIds(Arrays.asList(queryCriteria.getAccountingOrderId()))
                        .build(),
                loginSysUser
        );

        if (CollUtil.isNotEmpty(setAccountingObj)) {
            return setAccountingObj.get(0);
        } else {
            return null;
        }
    }


    @Override
    public AccountingOrderScoreCustomerDetailVo commonStatementOfAccountDetailDimCredit(AccountingOrderScoreCustomerOpDetailQueryCriteria queryCriteria, LoginSysUser loginSysUser) {


        // 修正 申请状态 fix invoiceStatus
        invoiceApplyXGoodsService.checkInvoiceStatusByAccountingOrderIdAndUpdateAccountingEntityFocusScore(queryCriteria.getAccountingOrderId(), CommonBeRelatedEnum.失联, loginSysUser);

        log.info("commonStatementOfAccountDetailDimCredit ->\n{}\n{}", JSONUtil.toJsonStr(queryCriteria), JSONUtil.toJsonStr(loginSysUser));

        AccountingOrderScoreCustomerDetailVo accountingScoreCustomerDetailVo = new AccountingOrderScoreCustomerDetailVo();

        /****************************************** 得到 对账单 主体 start ******************************************/

            AccountingOrderScoreCustomerListItemVo accountingOrderScoreCustomerVo = getAccountingOrderScoreCustomerVoByAccountingOrderId(queryCriteria, loginSysUser);

        if (ObjectUtil.isNull(accountingOrderScoreCustomerVo)) {
            return accountingScoreCustomerDetailVo;
        }


        // sugarUp
        DtoSugarUpUtils.sugarUpAccountingOrderScoreCustomerVo(accountingOrderScoreCustomerVo);

        accountingScoreCustomerDetailVo.setAccounting(accountingOrderScoreCustomerVo);

        /****************************************** 得到 对账单 主体   end ******************************************/

        /****************************************** 得到 对账单 X 积分流水 start ******************************************/

        // 关键点 这个判定 是否包含 不对账
        List<Long> idsCredit = getAccountingXCreditIdsByQueryCriteria(queryCriteria);

        List<InvoiceApplyDetailDimCreditItemVo> setCreditListObj = scoreOrderService.getScoreOrderReconciliationNotYetScene3(
                AccountingOrderScoreCustomerNotYetDimCreditScene3QueryCriteria
                        .builder()
                        .idsCredit(idsCredit)
                        .build(),
                loginSysUser
        );

        accountingScoreCustomerDetailVo.setCreditList(setCreditListObj);

        /****************************************** 得到 对账单 X 积分流水   end ******************************************/

        /****************************************** 得到 对账单 X 销方信息 收款方信息 start ******************************************/

        InvoiceBankInfoVo invoiceBankInfoVo = null;
        if (StrUtil.isNotBlank(accountingOrderScoreCustomerVo.getInvoiceBankInfoSnapshot())) {
            invoiceBankInfoVo = AnJsonUtils.toObject(accountingOrderScoreCustomerVo.getInvoiceBankInfoSnapshot(), InvoiceBankInfoVo.class);
        } else {
            InvoiceBankInfo invoiceBankInfo = invoiceBankInfoService.getInvoiceBankInfoByIdOrNullByDefault(accountingOrderScoreCustomerVo.getBankInfoId());
            invoiceBankInfoVo = AnBeanUtils.copy(invoiceBankInfo, InvoiceBankInfoVo.class);
        }
        accountingScoreCustomerDetailVo.setBankInfo(invoiceBankInfoVo);

        /****************************************** 得到 对账单 X 销方信息 收款方信息   end ******************************************/

        return accountingScoreCustomerDetailVo;
    }

    /****************************************** 积分结算 对账单 详情   end ******************************************/


    /****************************************** 积分结算 对账单 操作 start ******************************************/


    /**
     * 效率优化 mark by linjinyu at 20211203
     *
     * @param accountingScoreCustomer
     * @param scoreAccountingCustomerXCreditList
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountingScoreCustomer createScoreAccountingCustomer(
            AccountingScoreCustomer accountingScoreCustomer,
            List<AccountingScoreCustomerXCredit> scoreAccountingCustomerXCreditList) {

        if (StringUtils.isBlank(accountingScoreCustomer.getAccountingOrderSn())) {
            accountingScoreCustomer.setAccountingOrderSn(unifiedSnConfigService.getCustomerStatementOfAccountSnBySnowflake(""));
        }

        accountingScoreCustomerMapper.insert(accountingScoreCustomer);

        // 慢的原因 已优化 mark by linjinyu at 20211203
        for (AccountingScoreCustomerXCredit x : scoreAccountingCustomerXCreditList) {

            x.setAccountingOrderId(accountingScoreCustomer.getId());
            x.setAccountingOrderSn(accountingScoreCustomer.getAccountingOrderSn());
//			accountingScoreCustomerXCreditMapper.insert(x);

        }

        log.info("AccountingScoreCustomerXCredit \n{}", JSONUtil.toJsonStr(scoreAccountingCustomerXCreditList));

        // 4401
        int ret = accountingScoreCustomerXCreditMapperEx.insertBatch(scoreAccountingCustomerXCreditList);

        log.info("AccountingScoreCustomerXCredit.insertBatch ret = {}", ret);

        List<Long> processUnifiedOrderCreditIds = scoreAccountingCustomerXCreditList.stream()
                .map(AccountingScoreCustomerXCredit::getUnifiedOrderCreditId)
                .collect(Collectors.toList());

        unifiedOrderCreditService.updateFieldAccountingStateCustomerByUnifiedOrderCreditIds(processUnifiedOrderCreditIds, CommonAccountingStateEnum.已对账);

        return accountingScoreCustomer;
    }


    private static String fetchGroupKeyByCreditTypecreditName(InvoiceApplyDetailDimCreditItemVo e) {
        return StrUtil.format("{}^_^{}", e.getCreditType(), e.getCreditName());
    }

    @Override
    public void statisticsScoreAccountingCustomerByCredit(AccountingScoreCustomer accountingScoreCustomer, List<InvoiceApplyDetailDimCreditItemVo> invoiceApplyDetailDimCreditItemVoList) {

        /****************************************** 统计积分批次 start ******************************************/

        List<String> batchsList = invoiceApplyDetailDimCreditItemVoList.stream()
                // 是要去掉 这个会导致一个订单多个积分批次只统计一个的问题
//				.filter(AnLamdaUtils.distinctByKey(o -> o.getUnifiedOrderId()))
                .map(r -> r.getBatchs())
                .distinct()
                .collect(Collectors.toList());

        accountingScoreCustomer.setBatchs(AnStringUtils.stringListConvertToString(batchsList));

        /****************************************** 统计积分批次   end ******************************************/

        /****************************************** 统计积分类型 start ******************************************/

        // 结构体形态
        Map<String, List<InvoiceApplyDetailDimCreditItemVo>> creditTypeMap = invoiceApplyDetailDimCreditItemVoList
                .parallelStream()
                .collect(Collectors.groupingBy(AccountingScoreCustomerServiceImpl::fetchGroupKeyByCreditTypecreditName, Collectors.toList()));

        List<CreditTypeItemVo> creditTypeItemVoList = new ArrayList<>();
        List<Integer>          creditTypeKeyList    = new ArrayList<>();
        List<String>           creditNameValueList  = new ArrayList<>();

        for (Map.Entry<String, List<InvoiceApplyDetailDimCreditItemVo>> entry : creditTypeMap.entrySet()) {
            String[] keyItem = StrUtil.split(entry.getKey(), "^_^");
            creditTypeItemVoList.add(CreditTypeItemVo.builder()
                    .creditType(AnIntegerUtils.getInteger(keyItem[0]))
                    .creditName(AnStringUtils.getString(keyItem[1]))
                    .build());

            creditTypeKeyList.add(AnIntegerUtils.getInteger(keyItem[0]));
            creditNameValueList.add(AnStringUtils.getString(keyItem[1]));
        }

        accountingScoreCustomer.setCreditTypeStruct(JSONUtil.toJsonStr(creditTypeItemVoList));
        accountingScoreCustomer.setCreditType(AnStringUtils.integerListConvertToString(creditTypeKeyList));
        accountingScoreCustomer.setCreditName(AnStringUtils.stringListConvertToString(creditNameValueList));

        /****************************************** 统计积分类型 start ******************************************/

        /****************************************** 统计各项数量 start ******************************************/

        Integer quantityOrder       = 0;
        Integer quantityOrderGoods  = 0;
        Integer quantityOrderCredit = 0;

        quantityOrder = invoiceApplyDetailDimCreditItemVoList.stream().filter(AnLamdaUtils.distinctByKey(o -> o.getUnifiedOrderId())).collect(Collectors.toList()).size();
        quantityOrderGoods = invoiceApplyDetailDimCreditItemVoList.stream().filter(AnLamdaUtils.distinctByKey(o -> o.getUnifiedOrderGoodsId())).collect(Collectors.toList()).size();
        quantityOrderCredit = invoiceApplyDetailDimCreditItemVoList.size();

        accountingScoreCustomer.setQuantityOrder(quantityOrder);
        accountingScoreCustomer.setQuantityOrderGoods(quantityOrderGoods);
        accountingScoreCustomer.setQuantityOrderCredit(quantityOrderCredit);

        /****************************************** 统计各项数量   end ******************************************/

        /****************************************** 统计各项金额 start ******************************************/

        // 对账单-订单金额 - 积分结算2.1 新加 取该对账单中所有流水的结算金额总和 等于 结算金额+微信金额 也称之为统计金额
        BigDecimal statisticsTotalAmount = BigDecimal.ZERO;
        // 对账单-结算金额 - 之前的 - 取该对账单中所有积分类型为非`微信支付`的结算金额总和,目前创建对账单成功时服务端默认`微信支付`流水不参与结算,其他积分类型的流水参与结算,未来由业务操作人员选择结算积分类型来计算
        BigDecimal accountingAmount = BigDecimal.ZERO;
        // 对账单-微信金额 - 积分结算2.1 新加 取该对账单中所有积分类型为`微信支付`的结算金额总和 也称之为不对账金额 不结算金额
        BigDecimal unAccountingAmount = BigDecimal.ZERO;

        List<Predicate<InvoiceApplyDetailDimCreditItemVo>> predicatesByAccounting = new ArrayList<>();
        predicatesByAccounting.add(
                r -> null != r.getCreditType()
                        && r.getCreditType().compareTo(UnifiedOrderCreditTypeDefaultEnum.微信支付.getKey()) != 0
        );

        List<Predicate<InvoiceApplyDetailDimCreditItemVo>> predicatesByUnAccounting = new ArrayList<>();
        predicatesByUnAccounting.add(
                r -> null != r.getCreditType()
                        && r.getCreditType().compareTo(UnifiedOrderCreditTypeDefaultEnum.微信支付.getKey()) == 0
        );

        // 如果本来数据 是没这个流水Id 是找不出来的
        List<InvoiceApplyDetailDimCreditItemVo> findByAccountingCreditList   = AnLamdaUtils.filters(invoiceApplyDetailDimCreditItemVoList, predicatesByAccounting);
        List<InvoiceApplyDetailDimCreditItemVo> findByUnAccountingCreditList = AnLamdaUtils.filters(invoiceApplyDetailDimCreditItemVoList, predicatesByUnAccounting);

        statisticsTotalAmount = invoiceApplyDetailDimCreditItemVoList
                .stream()
                .map(item -> item.getBalance() == null ? BigDecimal.ZERO : item.getBalance())
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        if (CollUtil.isNotEmpty(findByAccountingCreditList)) {
            accountingAmount = findByAccountingCreditList
                    .stream()
                    .map(item -> item.getBalance() == null ? BigDecimal.ZERO : item.getBalance())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        if (CollUtil.isNotEmpty(findByUnAccountingCreditList)) {
            unAccountingAmount = findByUnAccountingCreditList
                    .stream()
                    .map(item -> item.getBalance() == null ? BigDecimal.ZERO : item.getBalance())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        accountingScoreCustomer.setAccountingAmount(accountingAmount);
        accountingScoreCustomer.setUnAccountingAmount(unAccountingAmount);
        accountingScoreCustomer.setStatisticsTotalAmount(statisticsTotalAmount);

        /****************************************** 统计各项金额   end ******************************************/

    }

    /**
     * 拒绝对账 重新对账 取消对账
     *
     * @param accountingOrderId
     * @param accountingStatus
     * @param loginSysUser
     * @return
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int opCancelAccounting(Long accountingOrderId,
            /* 对账单状态:0-未确认;1-已确认;-1-已取消; */Integer accountingStatus,
                                  LoginSysUser loginSysUser) {

        DateTime now  = DateUtil.date();
        int      ret  = 0;
        int      ret1 = 0;
        int      ret2 = 0;

        // 查出 对账单
        AccountingScoreCustomer existDB = accountingScoreCustomerMapper.selectByPrimaryKey(accountingOrderId);
        //获取账单来源对应的策略
        AccountingScoreCustomerStrategy instance = accountingScoreCustomerStrategyContext.getInstance(existDB.getSourceFrom());
        //交给策略处理
        return instance.opCancelAccounting(existDB, accountingStatus, loginSysUser);
    }

    /**
     * 删除对账单
     *
     * @param accountingOrderId
     * @param loginSysUser
     * @return
     */
    public int opDeleteAccounting(Long accountingOrderId, LoginSysUser loginSysUser) {

        int ret  = 0;
        int ret1 = 0;
        int ret2 = 0;

        // 查出 对账单
        AccountingScoreCustomer existDB = accountingScoreCustomerMapper.selectByPrimaryKey(accountingOrderId);

        if (ObjectUtil.isNull(existDB)) {
            return 0;
        }

        // 查出 对账单 与 积分流水 关联
        AccountingScoreCustomerXCreditExample xExample = new AccountingScoreCustomerXCreditExample();
        xExample.createCriteria()
                .andAccountingOrderSnEqualTo(existDB.getAccountingOrderSn());

        List<AccountingScoreCustomerXCredit> x = accountingScoreCustomerXCreditMapper.selectByExample(xExample);

        log.info("查出 对账单 与 积分流水 关联,\n {}", AnJsonUtils.toPrettyJson(x));

        List<Long> xIds = x.stream()
                .map(AccountingScoreCustomerXCredit::getId)
                .collect(Collectors.toList());

        // 删除关联
        if (CollUtil.isNotEmpty(xIds)) {
            // 删除 x
            AccountingScoreCustomerXCreditExample xDeleteExample = new AccountingScoreCustomerXCreditExample();
            xDeleteExample.createCriteria()
                    .andIdIn(xIds);
            ret1 = accountingScoreCustomerXCreditMapper.deleteByExample(xDeleteExample);
        }

        // 删除主体
        // accountingScoreCustomerMapper.deleteByPrimaryKey(id);
        // TODO 改为软删除
        AccountingScoreCustomer forSoftDelete = new AccountingScoreCustomer();

        forSoftDelete.setId(accountingOrderId);
        forSoftDelete.setDeleted(CommonSoftDeleteEnum.删除.getKey());

        forSoftDelete.setUpdateTime(DateUtil.date());
        forSoftDelete.setUpdateUserId(loginSysUser.getSysUserId());

        ret2 = accountingScoreCustomerMapper.updateByPrimaryKeySelective(forSoftDelete);

        if (ret1 > 0 && ret2 > 0) {
            ret = 1;
        }
        return ret;
    }


    /****************************************** 积分结算 对账单 操作   end ******************************************/


}
