package com.dayou.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dayou.common.BaseEntity;
import com.dayou.configuration.DfsConfig;
import com.dayou.dto.*;
import com.dayou.entity.*;
import com.dayou.enums.CertificateEnum;
import com.dayou.enums.MainBusinessEnum;
import com.dayou.enums.ProductionEnum;
import com.dayou.enums.ReportStatus;
import com.dayou.exception.ErrorCode;
import com.dayou.exception.file.InvalidExtensionException;
import com.dayou.mapper.PersonalCalculateMapper;
import com.dayou.mapper.PersonalProductionMapper;
import com.dayou.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dayou.utils.*;
import com.dayou.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static com.dayou.common.Constants.*;
import static com.dayou.enums.CertificateEnum.*;
import static com.dayou.enums.ProductionEnum.*;

/**
 * <p>
 * 个贷产品 服务实现类
 * </p>
 *
 * @author wucl
 * @since 2024-03-18
 */
@DS("mbs")
@Service
public class PersonalProductionServiceImpl extends ServiceImpl<PersonalProductionMapper, PersonalProduction> implements IPersonalProductionService , ResourceLoaderAware {


    @Autowired
    private IPersonalService personalService;

    @Autowired
    private IPersonalTargetService personalTargetService;

    @Autowired
    private PersonalCalculateMapper personalCalculateMapper;

    @Autowired
    private IPersonalExampleService personalExampleService;

    private ResourceLoader resourceLoader;

    @Autowired
    private DfsConfig dfsConfig;

    @Autowired
    private ICustomerCompanyService customerCompanyService;

    @Autowired
    private PersonalProductionMapper personalProductionMapper;



    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    @SuppressWarnings("unchecked")
    public Page<PersonalProduction> selectPage(Page page,PersonalProduction personalProduction){
        return this.page(page, new QueryWrapper<PersonalProduction>(personalProduction));
    }


    @Override
    public PersonalProduction detail(Long id){
        return this.getById(id);
    }

    @Override
    public Boolean add(PersonalProduction personalProduction){
        return  this.save(personalProduction);
    }

    @Override
    public Boolean update(PersonalProduction personalProduction){
        return  this.updateById(personalProduction);
    }

    @Override
    public Boolean delete(Long id){
        //逻辑删除
        return this.removeById(id);
    }

    @Override
    public Boolean genFile(Long personalId, Integer tag)  {
        //获取提供的证件信息
        Personal personal = personalService.getOne(new LambdaQueryWrapper<Personal>().select(Personal::getCredentials).eq(BaseEntity::getId, personalId).eq(BaseEntity::getDeleted, Boolean.FALSE));
        String orderId = personalService.getOne(new LambdaQueryWrapper<Personal>().eq(BaseEntity::getId, personalId).select(Personal::getOrderId).eq(BaseEntity::getDeleted, Boolean.FALSE)).getOrderId();
        PersonalTarget target = personalTargetService.getOne(new LambdaQueryWrapper<PersonalTarget>().eq(PersonalTarget::getPersonalId, personalId).eq(BaseEntity::getDeleted, Boolean.FALSE));
        List<CertificateEnum> certificates = JSON.parseArray(personal.getCredentials(), CertificateEnum.class);
        CertificateEnum certificateEnum = determineCertificate(certificates);
        Map<String,Object> textMap = new HashMap();

        PersonalSellingAbilityVO sell = JSON.parseObject(target.getSellingAbility(), PersonalSellingAbilityVO.class);
        PersonalBackgroundInfo bg = JSON.parseObject(target.getBackgroundInfo(), PersonalBackgroundInfo.class);
        PersonalEntityInfoVO en = JSON.parseObject(target.getEntityInfo(), PersonalEntityInfoVO.class);
        Field[] sellFields = sell.getClass().getDeclaredFields();
        Field[] bgFields = bg.getClass().getDeclaredFields();
        Field[] enFields = en.getClass().getDeclaredFields();
        for (Field field :sellFields){
            textMap.put(field.getName(),ReflectUtils.getFieldValue(sell,field.getName()));
        }
        for (Field field :bgFields){
            textMap.put(field.getName(),ReflectUtils.getFieldValue(bg,field.getName()));
        }
        for (Field field :enFields){
            textMap.put(field.getName(),ReflectUtils.getFieldValue(en,field.getName()));
        }
        textMap.put("buildAcreage",en.getAcreage());
        textMap.put("orderId",orderId);
        textMap.put("nAcreage",target.getAcreage());
        textMap.put("nPrice",((BigDecimal)target.getPrice()).setScale(0, RoundingMode.HALF_UP));
        textMap.put("nAmount",target.getAmount());
        textMap.put("validateCode",target.getValidateCode());
        addEnvironment(bg,textMap);
        PersonalCalculate calculate = personalCalculateMapper.getByTargetId(target.getId());
        textMap.put("rightsType",calculate.getRightsType());
        if (tag == 1){
            NameUrlDTO statementPath = genStatement(orderId,target,textMap,certificateEnum);
            return createPro(target.getId(),statementPath,tag,true);
        }
        if (tag == 2){
            NameUrlDTO technicPath = genTechnic(orderId,target,textMap,calculate,certificateEnum);
            return createPro(target.getId(),technicPath,tag,true);
        }
        if (tag == 3){
            NameUrlDTO finalPath = genFinal(orderId,target,textMap,certificateEnum);
            return createPro(target.getId(),finalPath,tag,true);
        }

        return Boolean.FALSE;
    }



    @Override
    public Boolean genFile(Long personalId) {
        try {
            //获取提供的证件信息
            Personal personal = personalService.getOne(new LambdaQueryWrapper<Personal>().select(Personal::getCredentials).eq(BaseEntity::getId, personalId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            String orderId = personalService.getOne(new LambdaQueryWrapper<Personal>().eq(BaseEntity::getId, personalId).select(Personal::getOrderId).eq(BaseEntity::getDeleted, Boolean.FALSE)).getOrderId();
            PersonalTarget target = personalTargetService.getOne(new LambdaQueryWrapper<PersonalTarget>().eq(PersonalTarget::getPersonalId, personalId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            List<CertificateEnum> certificates = JSON.parseArray(personal.getCredentials(), CertificateEnum.class);
            CertificateEnum certificateEnum = determineCertificate(certificates);
            Map<String,Object> textMap = new HashMap();

            PersonalSellingAbilityVO sell = JSON.parseObject(target.getSellingAbility(), PersonalSellingAbilityVO.class);

            PersonalBackgroundInfo bg = JSON.parseObject(target.getBackgroundInfo(), PersonalBackgroundInfo.class);
            PersonalEntityInfoVO en = JSON.parseObject(target.getEntityInfo(), PersonalEntityInfoVO.class);
            Field[] sellFields = sell.getClass().getDeclaredFields();
            Field[] bgFields = bg.getClass().getDeclaredFields();
            Field[] enFields = en.getClass().getDeclaredFields();
            for (Field field :sellFields){
                textMap.put(field.getName(),ReflectUtils.getFieldValue(sell,field.getName()));
            }
            for (Field field :bgFields){
                textMap.put(field.getName(),ReflectUtils.getFieldValue(bg,field.getName()));
            }
            for (Field field :enFields){
                textMap.put(field.getName(),ReflectUtils.getFieldValue(en,field.getName()));
            }
            textMap.put("buildAcreage",en.getAcreage());
            textMap.put("orderId",orderId);
            textMap.put("nAcreage",target.getAcreage());
            textMap.put("nPrice",((BigDecimal)target.getPrice()).setScale(0, RoundingMode.HALF_UP));
            textMap.put("nAmount",target.getAmount());
            textMap.put("validateCode",target.getValidateCode());
            addEnvironment(bg,textMap);

            PersonalCalculate calculate = personalCalculateMapper.getByTargetId(target.getId());
            textMap.put("rightsType",calculate.getRightsType());

            NameUrlDTO statementPath = genStatement(orderId,target,textMap,certificateEnum);
            createPro(target.getId(),statementPath,1,true);
            NameUrlDTO technicPath = genTechnic(orderId,target,textMap,calculate,certificateEnum);
            createPro(target.getId(),technicPath,2,true);
            NameUrlDTO finalPath = genFinal(orderId,target,textMap,certificateEnum);
            createPro(target.getId(),finalPath,3,true);
            return Boolean.TRUE;
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<PersonalProductionDTO> updatePersonalProductionTime() {
        List<PersonalProductionDTO> list = personalProductionMapper.getProductionAfter2025();
        return list;
    }

    private CertificateEnum determineCertificate(List<CertificateEnum> certificates){
        if (CollectionUtil.isEmpty(certificates)){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"请完善不动产权证或房产证其中一种证件");
        }
        //优先不动产权证
        if (certificates.contains(IMMOVABLE_CERTIFICATE)){
            return IMMOVABLE_CERTIFICATE;
        }
        //房产证&&国土证
        if(certificates.contains(HOUSE_CERTIFICATE) && certificates.contains(LAND_CERTIFICATE)){
            return LAND_CERTIFICATE;
        }
        //房产证
        if (certificates.contains(HOUSE_CERTIFICATE)){
            return HOUSE_CERTIFICATE;
        }
        return null;
    }


    /**
     * 生成结果报告
     * @param orderId
     * @param target
     * @return
     */
    private NameUrlDTO genFinal(String orderId, PersonalTarget target, Map<String, Object> textMap,CertificateEnum certificateEnum) {
        addCertificateValue(certificateEnum,textMap,target);
        immRemark(textMap);
        addDebiInfo(textMap);
        addExamples(target.getId(),textMap);
        BigDecimal am3 = BigDecimal.valueOf((Double.valueOf(String.valueOf(textMap.get("am3")))));
        textMap.put("amTotalUp", am3.divide(new BigDecimal("10000"),2,BigDecimal.ROUND_HALF_UP));
        addIsPledge(textMap);
        addRationalUsing(textMap);
        isNoneShangCase(textMap);
        addIfCondition(textMap,certificateEnum);
        proofNotEnough(textMap,certificateEnum);
        isSameAddress(textMap,certificateEnum);
        isLimit(textMap,certificateEnum);
        specialHandle(textMap);
        sellOrAllot(textMap);
        attachment(textMap,target);
        return doCreateFile(certificateEnum.getTemplatePath().get(3),textMap,orderId,ProductionEnum.FINAL);
    }

    /**
     * 生成意见书
     * @param orderId
     * @param target
     * @return
     */
    private NameUrlDTO genStatement(String orderId, PersonalTarget target,Map<String,Object> textMap,CertificateEnum certificateEnum){
        addCertificateValue(certificateEnum,textMap,target);
        immRemark(textMap);
        addDebiInfo(textMap);
        addExamples(target.getId(),textMap);
        return doCreateFile(certificateEnum.getTemplatePath().get(1),textMap,orderId,ProductionEnum.STATEMENT);
    }


    /**
     * 生成技术报告
     * @param orderId
     * @param target
     * @return
     */
    private NameUrlDTO genTechnic(String orderId, PersonalTarget target, Map<String, Object> textMap,PersonalCalculate calculate,CertificateEnum certificateEnum) {
        addCertificateValue(certificateEnum,textMap,target);
        immRemark(textMap);
        textMap.put("rt","SELL".equals(calculate.getRightsType())?"出让":"划拨");
        addDebiInfo(textMap);
        addExamples(target.getId(),textMap);
        addIsAllot(calculate,textMap);
        BigDecimal am3 = BigDecimal.valueOf((Double.valueOf(String.valueOf(textMap.get("am3")))));
        textMap.put("amTotalUp", am3.divide(new BigDecimal("10000"),2,BigDecimal.ROUND_HALF_UP));
        addIsPledge(textMap);
        return doCreateFile(certificateEnum.getTemplatePath().get(2),textMap,orderId,ProductionEnum.TECHNIC);
    }


    /**
     * 收集证件属性值
     * @param certificateEnum
     * @param textMap
     * @param target
     */
    private void addCertificateValue(CertificateEnum certificateEnum,Map<String,Object> textMap,PersonalTarget target){
        landRight(textMap,certificateEnum);
        switch (certificateEnum){
            case IMMOVABLE_CERTIFICATE:
                BigDecimal tenThousand = new BigDecimal("10000");
                PersonalImmovableCertificateVO imm = JSON.parseObject(target.getImmovableCertificate(), PersonalImmovableCertificateVO.class);
                Field[] immFields = imm.getClass().getDeclaredFields();
                for (Field field :immFields){
                    textMap.put(field.getName(),ReflectUtils.getFieldValue(imm,field.getName()));
                }
                if (StrUtil.isNotEmpty(imm.getBailor1IdNo())){
                    textMap.put("bailor1IdNo","（公民身份号码："+imm.getBailor1IdNo()+")");
                }
                if (StrUtil.isNotEmpty(imm.getBailor2IdNo())){
                    textMap.put("bailor2IdNo","（公民身份号码："+imm.getBailor2IdNo()+")");
                }
                textMap.put("nAmount",target.getAmount().divide(tenThousand,2,BigDecimal.ROUND_HALF_UP));
                textMap.put("ensureDebiAmount",imm.getEnsureDebiAmount().divide(tenThousand,imm.getEnsureDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
                textMap.put("defaultDebiAmount",imm.getDefaultDebiAmount().divide(tenThousand,imm.getDefaultDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
                textMap.put("otherDebiAmount",imm.getOtherDebiAmount().divide(tenThousand,imm.getOtherDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
                BigDecimal totalAmount = calculateDebiAmount(imm.getEnsureDebiAmount(), imm.getDefaultDebiAmount(), imm.getOtherDebiAmount());
                textMap.put("totalDebiAmount", totalAmount.divide(tenThousand,totalAmount.compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
                textMap.put("nAmountUp", ChineseUtils.formatMoney(((BigDecimal)textMap.get("nAmount")).multiply(tenThousand)));
                addOtherInfo(textMap);
                break;
            case HOUSE_CERTIFICATE:
                addHouseCertificateValue(textMap,target);
                textMap.put("hasLandCer","");
                textMap.put("dz","");
                textMap.put("dazheng","");
                textMap.put("type", "");
                addHouseOtherInfo(textMap,HOUSE_CERTIFICATE);
                break;
            case LAND_CERTIFICATE:
                addHouseCertificateValue(textMap,target);
                PersonalLandCertificateVO land = JSON.parseObject(target.getLandCertificate(), PersonalLandCertificateVO.class);
                Field[] landFields = land.getClass().getDeclaredFields();
                for (Field field :landFields){
                    textMap.put(field.getName(),ReflectUtils.getFieldValue(land,field.getName()));
                }
                textMap.put("userRightType","SELL".equals(land.getUserRightType())?"出让":"划拨");
                textMap.put("hasLandCer","、《国有土地使用证》");
                textMap.put("dz","");
                textMap.put("dazheng","");
                // 分户还是大证
                String type = "根据委托人提供的估价对象《国有土地使用证》，估价对象土地权属登记状况如下：";
                if (land.getCertificateType().equals("大证")){
                    type = "根据委托人提供的所在宗地《国有土地使用证》（大证），估价对象所在宗地土地权属登记状况如下：";
                    textMap.put("dz", "估价对象系上述宗地上的部分房地产，尚未办理分户《国有土地使用证》，根据委托人提供的估价对象所在宗地《国有土地使用证》（大证）记载，本次评估设定估价对象占用（分摊）的土地使用权系出让住宅用地位假设前提。");
                    textMap.put("dazheng", "\n2．经委托人介绍，估价对象尚未办理分户《国有土地使用证》，根据委托人提供的估价对象所在宗地《国有土地使用证》（大证）记载，本次评估设定估价对象占用（分摊）的土地使用权系出让住宅用地。");
                }
                textMap.put("type", type);
                addHouseOtherInfo(textMap,LAND_CERTIFICATE);
                break;
        }

    }

    /**
     * 房地产证字段值
     * @param textMap
     * @param target
     */
    private void addHouseCertificateValue(Map<String,Object> textMap,PersonalTarget target){
        BigDecimal tenThousand = new BigDecimal("10000");
        PersonalHouseCertificateVO house = JSON.parseObject(target.getHouseCertificate(), PersonalHouseCertificateVO.class);
        Field[] houseFields = house.getClass().getDeclaredFields();
        for (Field field :houseFields){
            textMap.put(field.getName(),ReflectUtils.getFieldValue(house,field.getName()));
        }
        if (StrUtil.isNotEmpty(house.getBailor1IdNo())){
            textMap.put("bailor1IdNo","（公民身份号码："+house.getBailor1IdNo()+")");
        }
        if (StrUtil.isNotEmpty(house.getBailor2IdNo())){
            textMap.put("bailor2IdNo","（公民身份号码："+house.getBailor2IdNo()+")");
        }
        textMap.put("nAmount",target.getAmount().divide(tenThousand,2,BigDecimal.ROUND_HALF_UP));
        textMap.put("ensureDebiAmount",house.getEnsureDebiAmount().divide(tenThousand,house.getEnsureDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
        textMap.put("defaultDebiAmount",house.getDefaultDebiAmount().divide(tenThousand,house.getDefaultDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
        textMap.put("otherDebiAmount",house.getOtherDebiAmount().divide(tenThousand,house.getOtherDebiAmount().compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
        BigDecimal totalAmount = calculateDebiAmount(house.getEnsureDebiAmount(), house.getDefaultDebiAmount(), house.getOtherDebiAmount());
        textMap.put("totalDebiAmount", totalAmount.divide(tenThousand,totalAmount.compareTo(BigDecimal.ZERO)==0?0:2,BigDecimal.ROUND_HALF_UP));
        textMap.put("nAmountUp", ChineseUtils.formatMoney(((BigDecimal)textMap.get("nAmount")).multiply(tenThousand)));
        if (StrUtil.isNotEmpty(house.getHouseCertificateNo())){
            textMap.put("fileName","房屋所有权证号");
            textMap.put("houseCertificateNo",house.getHouseCertificateNo());
        }else{
            textMap.put("fileName","丘（地）号/业务件号");
            textMap.put("houseCertificateNo",house.getBusinessNo());
        }

    }
    @Override
    public Boolean upload(String filePath, MultipartFile file,Long targetId, Integer tag) {
        try {
            NameUrlDTO nameUrlDTO = new NameUrlDTO();
            String originalFilename = file.getOriginalFilename();
            String upload = FileUploadUtils.upload(filePath, file, originalFilename);
            String url = dfsConfig.getDomain() + upload;
            nameUrlDTO.setName(originalFilename);
            nameUrlDTO.setUrl(url);
            return createPro(targetId,nameUrlDTO,tag,false);
        } catch (InvalidExtensionException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Boolean removeFile(Long id) {
        return this.update(new LambdaUpdateWrapper<PersonalProduction>().set(PersonalProduction::getFilePath,null).eq(BaseEntity::getId,id));
    }

    @Override
    public Boolean checkPass(TaskRecordDTO<PersonalProduction> taskRecordDTO) {
        PersonalProduction production = taskRecordDTO.getTaskData();
        Long targetId = production.getTargetId();
        String type = production.getProduction();
        if (STATEMENT.name().equals(type)){
            PersonalProduction pp = this.getOne(new LambdaQueryWrapper<PersonalProduction>().eq(PersonalProduction::getProduction, type)
                    .eq(PersonalProduction::getTargetId, targetId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            if (pp==null){
                    createPro(targetId,null,1,false);
            }
            return this.update(new LambdaUpdateWrapper<PersonalProduction>().eq(PersonalProduction::getProduction,type)
                        .eq(PersonalProduction::getTargetId,targetId).set(PersonalProduction::getCheckState, ReportStatus.已审核));

        }
        else if(LETTER.name().equals(type)){
            PersonalProduction pp = this.getOne(new LambdaQueryWrapper<PersonalProduction>().eq(PersonalProduction::getProduction, type)
                    .eq(PersonalProduction::getTargetId, targetId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            if (pp==null){
                createPro(targetId,null,4,false);
            }
            return this.update(new LambdaUpdateWrapper<PersonalProduction>().eq(PersonalProduction::getProduction,type)
                    .eq(PersonalProduction::getTargetId,targetId).set(PersonalProduction::getCheckState, ReportStatus.已审核));
        }
        else{
            PersonalProduction pp1 = this.getOne(new LambdaQueryWrapper<PersonalProduction>().eq(PersonalProduction::getProduction, FINAL.name())
                    .eq(PersonalProduction::getTargetId, targetId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            PersonalProduction pp2 = this.getOne(new LambdaQueryWrapper<PersonalProduction>().eq(PersonalProduction::getProduction, TECHNIC.name())
                    .eq(PersonalProduction::getTargetId, targetId).eq(BaseEntity::getDeleted, Boolean.FALSE));
            if (pp1==null){
                createPro(targetId,null,3,false);
            }
            if (pp2==null){
                createPro(targetId,null,2,false);
            }
            return this.update(new LambdaUpdateWrapper<PersonalProduction>().ne(PersonalProduction::getProduction,STATEMENT.name())
                    .eq(PersonalProduction::getTargetId,targetId).set(PersonalProduction::getCheckState, ReportStatus.已审核));
        }
    }

    @Override
    public Boolean repertoryIn(TaskRecordDTO<PersonalProduction> taskRecordDTO) {
        PersonalProduction taskData = taskRecordDTO.getTaskData();
        List<PersonalProduction> productions;
        if (taskData.getProduction().equals(STATEMENT.name())){
            productions = this.list(new LambdaQueryWrapper<PersonalProduction>()
                    .eq(PersonalProduction::getTargetId,taskData.getTargetId()).eq(PersonalProduction::getProduction,taskData.getProduction())
                    .eq(BaseEntity::getDeleted, Boolean.FALSE));
        }else {
            productions = this.list(new LambdaQueryWrapper<PersonalProduction>()
                    .eq(PersonalProduction::getTargetId,taskData.getTargetId()).ne(PersonalProduction::getProduction,STATEMENT.name())
                    .eq(BaseEntity::getDeleted, Boolean.FALSE));
        }

        if (CollectionUtil.isEmpty(productions)){
            ErrorCode.throwBusinessException(ErrorCode.CUSTOM_ERROR,"产品不存在，入库失败。");
        }
        List<Long> productionIds = productions.stream().map(BaseEntity::getId).collect(Collectors.toList());
        return this.update(new LambdaUpdateWrapper<PersonalProduction>().set(PersonalProduction::getRepertoryState,Boolean.FALSE)
                .set(PersonalProduction::getRepertoryInTime,new Date()).in(BaseEntity::getId,productionIds));
    }

    @Override
    public Boolean repertoryOut(TaskRecordDTO<PersonalProduction> taskRecordDTO) {
        PersonalProduction taskData = taskRecordDTO.getTaskData();
        List<PersonalProduction> productions;
        Long targetId = taskData.getTargetId();
        if (taskData.getProduction().equals(STATEMENT.name())){
            productions = this.list(new LambdaQueryWrapper<PersonalProduction>()
                    .eq(PersonalProduction::getTargetId, targetId).eq(PersonalProduction::getProduction,taskData.getProduction())
                    .eq(BaseEntity::getDeleted, Boolean.FALSE));
        }else {
            productions = this.list(new LambdaQueryWrapper<PersonalProduction>()
                    .eq(PersonalProduction::getTargetId, targetId).ne(PersonalProduction::getProduction,STATEMENT.name())
                    .eq(BaseEntity::getDeleted, Boolean.FALSE));
        }

        if (productions.size()==1 && productions.get(0).getProduction().equals(STATEMENT.name())){
            //价值意见书出库无需申请审核
            return this.update(new LambdaUpdateWrapper<PersonalProduction>().set(PersonalProduction::getRepertoryState, Boolean.TRUE).set(PersonalProduction::getRepertoryOutTime,new Date())
                    .eq(BaseEntity::getId, productions.get(0).getId()));
        }else{
            //非意见书 需做出库前校验
            if (!customerCompanyService.avoidWareHouseApplyByTargetId(targetId)){
                //验证是否有出库申请并且审核通过

            }
            List<Long> productionIds = productions.stream().map(BaseEntity::getId).collect(Collectors.toList());
            return this.update(new LambdaUpdateWrapper<PersonalProduction>().set(PersonalProduction::getRepertoryState,Boolean.TRUE)
                    .set(PersonalProduction::getRepertoryOutTime,new Date()).in(BaseEntity::getId,productionIds));
        }
    }

    @Override
    public OrderProductionsBag productionBag(Long id) {
        //获取产品信息
        List<ProductionDTO> productions = personalProductionMapper.getProductions(id);
        if (CollectionUtil.isNotEmpty(productions)){
            OrderProductionsBag orderProductionsBag = new OrderProductionsBag();
            orderProductionsBag.setBusinessId(id);
            orderProductionsBag.setBusinessType(MainBusinessEnum.PERSONAL_BUSINESS.name());
            orderProductionsBag.setProductions(productions);
            return orderProductionsBag;
        }
        return null;
    }



    /**
     * 创建文档
     * @param originFilePath
     * @param textMap
     * @param orderId
     * @param productionEnum
     * @return
     */
    private NameUrlDTO doCreateFile(String originFilePath, Map<String,Object> textMap, String orderId,ProductionEnum productionEnum){
        try {
            Resource resource = resourceLoader.getResource(originFilePath);
            InputStream in = resource.getInputStream();
            XWPFDocument document = new XWPFDocument(in);
            POIWordUtil.replaceWord(document,textMap);
            String baseDir = dfsConfig.getPath();
            String fileName = orderId + productionEnum.getMsg()+ DOCX;
            String dateIndexFileName = FileUploadUtils.createDateIndexFileName(fileName);
            File absoluteFile = FileUploadUtils.getAbsoluteFile(baseDir, dateIndexFileName);
            FileOutputStream out = new FileOutputStream(absoluteFile);
            document.write(out);
            out.close();
            in.close();
            NameUrlDTO nameUrl = new NameUrlDTO();
            nameUrl.setName(fileName);
            nameUrl.setUrl(dfsConfig.getDomain() + dateIndexFileName);
            return nameUrl;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新DB数据
     * @param targetId
     * @param nameUrlDTO
     * @param tag
     * @return
     */
    private Boolean createPro(Long targetId,NameUrlDTO nameUrlDTO,Integer tag,Boolean isOnline){
        PersonalProduction pp = null;
        String productionType = null;
        String productionTypeName = null;
        PersonalTarget target = personalTargetService.getOne(new LambdaQueryWrapper<PersonalTarget>().eq(PersonalTarget::getId,targetId).select(PersonalTarget::getProductions,BaseEntity::getId));
        if (tag ==1){
            productionType = ProductionEnum.STATEMENT.name();
            productionTypeName = ProductionEnum.STATEMENT.getMsg();
        }
        else if (tag ==2){
            productionType = ProductionEnum.TECHNIC.name();
            productionTypeName = ProductionEnum.TECHNIC.getMsg();
        }
        else if (tag ==3){
            productionType = ProductionEnum.FINAL.name();
            productionTypeName = ProductionEnum.FINAL.getMsg();
        }else if (tag ==4){
            productionType = ProductionEnum.LETTER.name();
            productionTypeName = ProductionEnum.LETTER.getMsg();
        }
        else {
            return Boolean.FALSE;
        }
        pp = this.getOne(new LambdaQueryWrapper<PersonalProduction>().eq(PersonalProduction::getTargetId, targetId).eq(PersonalProduction::getProduction, productionType).eq(BaseEntity::getDeleted, Boolean.FALSE));
        if (pp==null){
            PersonalProduction personalProduction = new PersonalProduction();
            personalProduction.setTargetId(targetId);
            if (nameUrlDTO!=null){
                personalProduction.setFilePath(JSON.toJSONString(nameUrlDTO));
            }
            personalProduction.setProduction(productionType);
            personalProduction.setUserId(LoginContext.getCurrentUserId());
            personalProduction.setIsOnline(isOnline);
            this.add(personalProduction);
        }else {
            if (nameUrlDTO!=null){
                pp.setFilePath(JSON.toJSONString(nameUrlDTO));
            }
            pp.setModified(DateUtils.getNowDate());
            this.update(pp);
        }

        if (target!=null){
            String productions = target.getProductions();
            if (StrUtil.isBlank(productions)){
                productions = productionTypeName;
            }else {
                productions += ","+productionTypeName;
            }
            personalTargetService.update(new LambdaUpdateWrapper<PersonalTarget>()
                    .set(PersonalTarget::getProductions, productions).eq(BaseEntity::getId,targetId));
        }
        return Boolean.TRUE;
    }


    /**
     * 收集可比实例数据
     * @param targetId
     * @param map
     */
    private void addExamples(Long targetId,Map<String,Object> map){
        List<PersonalExample> examples = personalExampleService.list(new LambdaQueryWrapper<PersonalExample>()
                .select(PersonalExample::getLocation, PersonalExample::getAtFloor, PersonalExample::getDecoration,
                        PersonalExample::getAcreage, PersonalExample::getPrice,PersonalExample::getMarketInfo,PersonalExample::getOrientation,
                        PersonalExample::getHouseType,PersonalExample::getOtherInfo,PersonalExample::getFixPrice,PersonalExample::getAmount)
                .eq(PersonalExample::getTargetId, targetId).eq(BaseEntity::getDeleted, Boolean.FALSE));
        for (int i=0;i<examples.size();i++){
            ItemData price = JSON.parseObject(examples.get(i).getPrice(), ItemData.class);
            ItemData location = JSON.parseObject(examples.get(i).getLocation(), ItemData.class);
            ItemData decoration = JSON.parseObject(examples.get(i).getDecoration(), ItemData.class);
            ItemData atFloor = JSON.parseObject(examples.get(i).getAtFloor(), ItemData.class);
            ItemData acreage = JSON.parseObject(examples.get(i).getAcreage(), ItemData.class);
            ItemData marketInfo = JSON.parseObject(examples.get(i).getMarketInfo(), ItemData.class);
            ItemData orientation = JSON.parseObject(examples.get(i).getOrientation(), ItemData.class);
            ItemData houseType = JSON.parseObject(examples.get(i).getHouseType(), ItemData.class);
            ItemData otherInfo = JSON.parseObject(examples.get(i).getOtherInfo(), ItemData.class);
            ItemData fixPrice = JSON.parseObject(examples.get(i).getFixPrice(), ItemData.class);
            ItemData amount = JSON.parseObject(examples.get(i).getAmount(), ItemData.class);
            map.put("lo"+i,location.getValueA());
            map.put("pr"+i,price.getValueA());
            map.put("de"+i,decoration.getValueA());
            map.put("at"+i,atFloor.getValueA());
            map.put("ac"+i,acreage.getValueA());
            map.put("mr"+i,DateUtils.tzTime2ChineseYM(marketInfo.getValueA()));
            map.put("or"+i,orientation.getValueA());
            map.put("aa"+i,atFloor.getValueA()+"/"+atFloor.getValueB()+"层");
            map.put("ho"+i,houseType.getValueA());
            map.put("ot"+i,otherInfo.getValueA());
            map.put("orw"+i,orientation.getWeight());
            map.put("atw"+i,atFloor.getWeight());
            map.put("acw"+i,acreage.getWeight());
            map.put("dew"+i,decoration.getWeight());
            map.put("otw"+i,otherInfo.getWeight());
            map.put("how"+i,otherInfo.getWeight());
            map.put("px"+i,fixPrice.getValueA());
            map.put("am"+i,amount.getValueA());
            map.put("mrw"+i,marketInfo.getWeight());
        }
    }

    private BigDecimal calculateDebiAmount(BigDecimal ensureDebiAmount,BigDecimal defaultDebiAmount,BigDecimal otherDebiAmount){
        return ensureDebiAmount.add(defaultDebiAmount).add(otherDebiAmount);
    }

    /**
     * 备注
     * @param textMap
     */
    private void immRemark(Map<String,Object> textMap){
        String rightType = String.valueOf(textMap.get("rightsType"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String otherPledgePerson = String.valueOf(textMap.get("otherPledgePerson"));
        StringBuffer sb = new StringBuffer();
        sb.append("备注：").append("\r\n");
        sb.append("1. 币种：人民币；评估单价按建筑面积计。 ").append("\r\n");
        sb.append("2. 估价结果单价取整至元位，总价取整至百元位。").append("\r\n");
        int index = 3;
        if (StrUtil.equals("ALLOT",rightType)) {
            sb.append(String.format("%s. 估价对象占用（分摊）土地使用权为划拨用地，本报告估价结果已扣除估价对象划拨土地使用权应缴纳的土地出让金或者相当于土地出让金的价款。\r\n", index++));
        }
        // 是否设置抵押权

        if (isPledge.contains("同一抵押权人")) {
            sb.append(String.format("%s. 根据委托人提供的相关资料，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象%s。根据《房地产估价规范》（GB/T50291-2015）相关规定，本抵押价值估价结果未扣减续贷对应的已抵押担保的债权数额，请报告使用人予以特别关注。",
                    "\n"+index++, pledgePerson,textMap.get("totalDebiAmount"), isPledge));
        } else if (isPledge.contains("新抵押权人")) {
            sb.append(String.format("%s. 根据委托人提供的相关资料，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象向%s申请抵押贷款。权利人承诺：在新的抵押权设立前，将清偿估价对象已抵押担保的所有债权，注销原已设立的抵押权，故本抵押价值估价结果未扣减已抵押担保的债权数额，请报告使用人予以特别关注。",
                    "\n"+index++, pledgePerson,textMap.get("totalDebiAmount"),otherPledgePerson)
            );
        }
        textMap.put("remark", sb.toString());
    }

    /**
     * 环境描述
     * @param bg
     * @param map
     */
    private void addEnvironment(PersonalBackgroundInfo bg, Map<String, Object> map) {
        // 地理位置
        String area = String.format("估价对象位于%s，%s朝向，小区%s。",
                bg.getLocation(), bg.getAspect(), bg.getRoundTouch());
        map.put("areaInfo", area);
        // 交通情况
        String traffic = String.format("估价对象所处区域有%s、%s、%s等主次干道纵横交错，道路通达度%s，有%s等多路公交车途经此地，并就近设有车站，"
                        +(StrUtil.isBlank(bg.getSubway())?"":bg.getSubway()+"，")+"交通便捷度%s。",
                bg.getRoad1(), bg.getRoad2(), bg.getRoad3(), bg.getRoadSmooth(), bg.getPubTraffic(), bg.getBgTraffic());
        map.put("traffic", traffic);
        // 公共配套
        String common = String.format("估价对象周边布局有%s、%s、%s、%s等公共配套，区域公共配套设施齐全。",
                bg.getBusinessPoint(), bg.getFinancePoint(), bg.getEduEquipment(), bg.getMedicalInfo()
        );
        map.put("common", common);
        // 周边环境
        String environment = String.format("估价对象周边有%s%s住宅小区，区域整体自然环境和景观%s。%s",
                StrUtil.isNotEmpty(bg.getCityParks()) ? bg.getCityParks() : "",
                (StrUtil.isNotEmpty(bg.getCityParks()) ? "，" : "") + bg.getAroundCommunities(), bg.getBgEnvir(), "");
        map.put("envir", environment);
        // 基础设施
        map.put("basz", bg.getBgBase());
        map.put("trafficInfo", String.format("有%s等多路公交线路通过，交通便捷度%s", map.get("pubTraffic"), bg.getBgTraffic()).replaceAll("null", ""));

    }

    /**
     * 权益状况描述
     */
    private void addDebiInfo(Map<String, Object> textMap){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String usingInfo = String.valueOf(textMap.get("usingInfo"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        // 其他权益状况
        String debiInfo = null;
        // 1未出租，未抵押
        if (!"出租".equals(usingInfo) && "否".equals(isPledge)) {
            debiInfo = "根据委托人提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象未设定抵押等他项权利，未出租。";
        }
        // 2已出租，未抵押
        if ("出租".equals(usingInfo) && "否".equals(isPledge)) {
            debiInfo = "根据委托人提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，未设定抵押等他项权利。";
        }
        // 3未出租，已抵押
        if (!"出租".equals(usingInfo) && !"否".equals(isPledge)) {
            debiInfo = String.format("根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象未出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。",
                    pledgePerson, textMap.get("totalDebiAmount"));
        }

        // 4已出租，已抵押
        if ("出租".equals(usingInfo) && !"否".equals(isPledge)) {
            debiInfo = String.format("根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。",
                    pledgePerson, textMap.get("totalDebiAmount"));
        }
        textMap.put("debiInfo", debiInfo);
    }

    /**
     * 不动产证 ：其他信息描述
     * @param textMap
     */
    private void addOtherInfo(Map<String,Object> textMap){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        Boolean isShare = (Boolean) textMap.get("isShare");
        String rightsType = String.valueOf(textMap.get("rightsType"));
        String usingInfo = String.valueOf(textMap.get("usingInfo"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String rightsToPurpose = (String) textMap.get("rightsToPurpose");
        String isShareDesc = "";
        int index = 2;
        if (isShare) {
            isShareDesc = (index++) + "、委托人提供的《不动产权证书》记载国有建设用地使用权面积为共用宗地面积，系国有出让城镇住宅用地，未记载估价对象分摊土地使用权面积，本次评估设定估价对象分摊的土地使用权系出让城镇住宅用地。";
        }
        isShareDesc += (index == 2 ? "" : "\t") + (index++) + "、本报告估价结果包括房屋（含附属设备设施）所有权及其占用（分摊）范围内的土地使用权的价值。该土地使用权和附属的设施设备若与房屋所有权分割处置，本估价结果无效。";
        if (rightsType.equals("SELL")) {
            // 出让金
            isShareDesc += "本报告估价结果含土地出让金。";
        } else {
            // 划拨
            isShareDesc += "本报告估价结果已扣除估价对象划拨土地使用权应缴纳的土地出让金或者相当于土地出让金的价款。";
        }

        // 1未出租，未抵押
        if (!"出租".equals(usingInfo) && "否".equals(isPledge)) {
            isShareDesc += "\r\n\t" + (index++) + "、根据委托人提供的“估价对象权利状况、法定优先受偿款调查说明及承诺书”、“房地产评估委托、确认书”书面承诺和确认，在价值时点，估价对象未设定抵押，无法定优先受偿权，本次评估以此为引用依据。";
        }
        // 2已出租，未抵押
        if ("出租".equals(usingInfo) && "否".equals(isPledge)) {
            isShareDesc += "\r\n\t" +  (index++) + "、根据委托人提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，未设定抵押等他项权利。";
        }
        // 3未出租，已抵押
        if (!"出租".equals(usingInfo) && !"否".equals(isPledge)) {
            String totalDebiAmount = String.format("、根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象未出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。"
                    , pledgePerson, textMap.get("totalDebiAmount"));
            isShareDesc += "\r\n\t" + (index++)+totalDebiAmount;
        }
        // 4已出租，已抵押
        if ("出租".equals(usingInfo) && !"否".equals(isPledge)) {
            String totalDebiAmount = String.format("、根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。"
                    , pledgePerson, textMap.get("totalDebiAmount"));
            isShareDesc +="\r\n\t" +(index++)+ totalDebiAmount;
        }

        String format = String.format("、经注册房地产估价师现场查勘，估价对象系%s，无与相邻房屋合并连通使用情形。", rightsToPurpose);
        isShareDesc += "\r\n\t" +(index++)+format;
        isShareDesc += "\r\n\t"+(index++)+"、经注册房地产估价师现场查勘，估价对象内、外部设计未进行明显改造。";
        textMap.put("isShareDesc", isShareDesc);
    }


    private void addHouseOtherInfo(Map<String,Object> textMap,CertificateEnum certificateEnum){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String rightsType = String.valueOf(textMap.get("rightsType"));
        String usingInfo = String.valueOf(textMap.get("usingInfo"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String rightsToPurpose = (String) textMap.get("rightsToPurpose");
        String isShareDesc = "";
        // 相关情况
        int index = 2;

        if (!certificateEnum.equals(LAND_CERTIFICATE)) {
            // 没有提供国土证
            // 要判断是出让还是划拨
            isShareDesc = ("\n" + index++) + "、委托人未提供估价对象《国有土地使用证》，注册房地产估价师虽进行了尽职调查，但难以查证和取得估价对象土地权属信息；经注册房地产估价师实地调查，" + textMap.get("landRight") + "。\r\n\t";
        } else {
            // 提供了国土证
            // 是否提供了大证
            if ( textMap.get("certificateType").equals("大证")) {
                isShareDesc = String.format("%s、经委托人介绍，估价对象尚未办理分户《国有土地使用证》，根据委托人提供的估价对象所在宗地《国有土地使用证》（大证）记载，本次评估设定估价对象占用（分摊）的土地使用权系%s住宅用地。\r\n\t",
                        "\n" + index++,textMap.get("rightsType").equals("ALLOT") ? "划拨" : "出让");
            }
        }
        isShareDesc += ("\n" +index++) + "、本报告估价结果包括房屋（含附属设备设施）所有权及其占用（分摊）范围内的土地使用权的价值。该土地使用权和附属的设施设备若与房屋所有权分割处置，本估价结果无效。";
        if (rightsType.equals("SELL")) {
            // 出让金
            isShareDesc += "本报告估价结果含土地出让金。\r\n\t";
        } else {
            // 划拨
            isShareDesc += "本报告估价结果已扣除估价对象划拨土地使用权应缴纳的土地出让金或者相当于土地出让金的价款。\r\n\t";
        }
        // 1未出租，未抵押
        if (!"出租".equals(usingInfo) && "否".equals(isPledge)) {
            isShareDesc += ("\n" + index++) + "、根据委托人提供的“估价对象权利状况、法定优先受偿款调查说明及承诺书”、“房地产评估委托、确认书”书面承诺和确认，在价值时点，估价对象未设定抵押，无法定优先受偿权，本次评估以此为引用依据。\r\n\t";
        }
        // 2已出租，未抵押
        if ("出租".equals(usingInfo) && "否".equals(isPledge)) {
            isShareDesc += ("\n" + index++) + "、根据委托人提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，未设定抵押等他项权利。\r\n\t";
        }
        // 3未出租，已抵押
        if (!"出租".equals(usingInfo) && !"否".equals(isPledge)) {
            isShareDesc += String.format("%s、根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象未出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。\r\n\t",
                    "\n" + index++,pledgePerson, textMap.get("totalDebiAmount"));
        }
        // 4已出租，已抵押
        if ("出租".equals(usingInfo) && !"否".equals(isPledge)) {
            isShareDesc += String.format("%s、根据委托人介绍和提供的“房地产评估委托、确认书”、“估价对象权利状况、法定优先受偿款调查说明及承诺书”，经注册房地产估价师实地查勘，至价值时点，估价对象已出租，估价对象已设置了一般抵押权尚未注销，抵押权人为%s，抵押权利价值为%s万元。\r\n\t",
                    "\n" + index++,pledgePerson, textMap.get("totalDebiAmount"));
        }
        isShareDesc += "\r\n\t";
        isShareDesc += String.format("%s、经注册房地产估价师现场查勘，估价对象系%s，无与相邻房屋合并连通使用情形。\r\n\t", index++,rightsToPurpose);
        isShareDesc += ("\n" + index++) + "、经注册房地产估价师现场查勘，估价对象内、外部设计未进行明显改造。";
        textMap.put("houseOtherInfo", isShareDesc);
    }
    private void addIsAllot(PersonalCalculate calculate,Map<String, Object> textMap) {
        String rightsType = calculate.getRightsType();
        Integer rollingLevel = calculate.getRollingLevel();

        // 是否划拨
        String isAllot = "";
        if (StrUtil.equals("ALLOT", rightsType)) {
            BigDecimal sellMoneyBase = calculate.getSellMoneyBase();
            BigDecimal sellMoney = calculate.getSellMoney();
            String hbTemplate = "以上求取的是估价对象在出让土地使用权下的市场价值，估价对象占用（分摊）土地使用权类型为 “划拨”，需扣除土地出让金。\r\n" +
                    "根据成国土资发（2016）150号《成都市国土资源局关于分户国有土地使用权划拨补办出让及转让有关问题处理意见的通知》，土地出让价款缴纳标准为：成都市中心城区享受优惠政策普通住房平均交易价格×房屋产权登记面积×1%%。 \r\n" +
                    "\t估价对象位于%s，根据《成都市城乡房产管理局关于2016年第二次公布中心城区享受优惠政策普通住房平均交易价格的通知》（成房发[2016]107号），住房平均交易价格为%s元/平方米，故估价对象需扣除土地出让金=%s×1%%=%s元/平方米，则划拨土地使用权下的市场价值=%s-%s=%s。";
            String circle = "";
            if (rollingLevel == 1) {
                circle = "二环内";
            } else if (rollingLevel == 2) {
                circle = "二环至三环";
            } else if (rollingLevel == 3) {
                circle = "三环外";
            }
            isAllot = String.format(hbTemplate,
                    circle,   // 环
                    sellMoneyBase == null ? "" : sellMoneyBase,  // 出让金基数（元）
                    sellMoneyBase == null ? "" : sellMoneyBase,
                    sellMoney == null ? "" : sellMoney,      // 实际出让金（元）
                    textMap.get("px3"),            // 修正后的价格
                    sellMoney == null ? "" : sellMoney,
                    textMap.get("pr3") == null ? "" :  textMap.get("pr3")
            );
        }
        textMap.put("isAllot", isAllot);
    }

    private void addIsPledge(Map<String, Object> textMap){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String otherPledgePerson = String.valueOf(textMap.get("otherPledgePerson"));
        String buildAcreage = String.valueOf(textMap.get("buildAcreage"));
        // 抵押价值计算
        String pledge = "";
        // 如果没有抵押
        if ("否".equals(isPledge)) {
            pledge = String.format("估价对象的抵押价值＝估价对象假定未设立法定优先受偿权利下的价值－估价师知悉的法定优先受偿款额，为：\r\n" +
                            "      总价（RMB）：%s-%s=%s（万元）[百元以下四舍五入]\r\n" +
                            "      单价（RMB）：%s元/㎡（按建筑面积计）", textMap.get("amTotalUp"), 0d,textMap.get("amTotalUp"),
                    textMap.get("px3")
            );
        } else if (isPledge.contains("同一抵押权人")) {
            pledge = String.format("估价对象的抵押价值＝估价对象假定未设立法定优先受偿权利下的价值－估价师知悉的法定优先受偿款额\r\n" +
                            "\t根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象向同一抵押权人申请续期贷款。根据《房地产估价规范》（GB/T50291-2015）相关规定，本抵押价值估价结果未扣减续贷对应的已抵押担保的债权数额，故，估价对象的抵押价值为：\r\n" +
                            "      总价（RMB）：%s-%s=%s（万元）[百元以下四舍五入]\r\n" +
                            "      单价（RMB）：%s元/㎡（按建筑面积计）",
                    pledgePerson,
                    textMap.get("totalDebiAmount"),
                    new BigDecimal((String) textMap.get("am3")).divide(new BigDecimal("10000"),0,BigDecimal.ROUND_HALF_UP),
                    0d,
                    textMap.get("amTotalUp"),
                    textMap.get("pr3")
            );
        } else if (isPledge.contains("新抵押权人")) {
            BigDecimal residue = ((BigDecimal) textMap.get("amTotalUp")).subtract((BigDecimal) textMap.get("totalDebiAmount"));
            pledge = String.format("估价对象的抵押价值＝估价对象假定未设立法定优先受偿权利下的价值－估价师知悉的法定优先受偿款额\r\n" +
                            "\t根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象向%s申请抵押贷款，在新的抵押权设立前，权利人将清偿估价对象已抵押担保的所有债权，注销原已设立的抵押权；故本抵押价值估价结果未扣减已抵押担保的债权数额，则，估价对象的抵押价值为：\r\n" +
                            "      总价（RMB）：%s-%s=%s（万元）[百元以下四舍五入]\r\n" +
                            "      单价（RMB）：%s元/㎡（按建筑面积计）",
                    pledgePerson,
                    textMap.get("totalDebiAmount"),
                    otherPledgePerson,
                    textMap.get("amTotalUp"),
                    textMap.get("totalDebiAmount"),
                    residue,
                    residue.multiply(new BigDecimal("10000")).divide(new BigDecimal(buildAcreage),0, BigDecimal.ROUND_HALF_UP));
        }
        textMap.put("isPledgeInfo", pledge);
    }

    /**
     * 合理使用价值评估 （结果报告）
     */
    private void addRationalUsing(Map<String, Object> textMap){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String rational = "";
        int index = 1;
        if (!"否".equals(isPledge)) {
            // 如果抵押，则显示
            rational = String.format("（%s）估价对象已设置有抵押权，截至价值时点抵押权尚未注销，因本估价报告结论是以再次设置抵押权之前，需要注销原已设置的抵押权为前提，提请报告使用者合理使用本估价结果。\r\n\t",
                    index++);
        }
        rational += String.format("（%s）本次评估中抵押价值仅为价值时点%s法定优先受偿权利下的抵押价值，并非抵押合同到期的抵押物价值或抵押物进行处分时的价值，在此提请报告使用者注意。\r\n\t",
                "\n" + index++, ((BigDecimal) textMap.get("otherDebiAmount")).compareTo(BigDecimal.ZERO)>0 ? "设立" : "未设立");
        rational += String.format("（%s）本报告估价结果为在价值时点房地产市场状况下的抵押价值，未考虑抵押期间及抵押权实现时的国家宏观经济政策的变化、市场状况变化、未来处置风险、房地产价值减损、抵押权实现时产生的相关税费等因素对房地产价值的影响，提请报告使用者注意。", index++);
        textMap.put("rational", rational);
    }

    private void isNoneShangCase(Map<String, Object> textMap){
        textMap.put("nongshang", ((Boolean)textMap.get("isNoneShangCase")) ? "" : "如有异议，请在十日内向我公司提出");
    }

    /**
     * 假设条件 （结果报告）
     * @param textMap
     */
    private void addIfCondition(Map<String, Object> textMap,CertificateEnum certificateEnum){
        String ifCond = "";
        int index = 6;
        if (certificateEnum.equals(IMMOVABLE_CERTIFICATE)){
            if (certificateEnum.equals(IMMOVABLE_CERTIFICATE) && (Boolean) textMap.get("haveOrigin")) {
                ifCond = ("\n" + index++) + "．我们已对委托人提供的估价对象《不动产权证书》原件进行了审慎核对，在无理由怀疑其原件合法性、真实性、准确性和完整性的情况下，假设委托人提供的上述原件合法、真实、准确、完整。\r\n\t";
            }
            ifCond += ("\n" + index++) + "．我们已对房屋安全、环境污染等影响估价对象价值的重大因素给予了关注，在无理由怀疑估价对象存在安全隐患且无相应的专业机构进行鉴定、检测的情况下，假设估价对象能正常安全使用。\r\n\t";
            if (certificateEnum.equals(IMMOVABLE_CERTIFICATE) && !(Boolean) textMap.get("isShare")) {
                ifCond +=("\n" + index++)+ "．由于受专业限制，我们未对估价对象房屋建筑面积及其分摊的土地使用权面积进行专业测量，本次估价假设估价对象房屋建筑面积及其分摊的土地使用权面积与《不动产权证书》记载面积一致。\r\n\t";
            } else {
                ifCond += ("\n" + index++) + "．由于受专业限制，我们未对估价对象房屋建筑面积及其共用宗地使用权面积进行专业测量，本次估价假设估价对象房屋建筑面积及其共用宗地的使用权面积与《不动产权证书》记载面积一致。\r\n\t";
            }
            ifCond += ("\n" + index++) + "．由于我们无法通过第三方进一步查证估价对象是否存在法定优先受偿权，或者查证委托人提供的情况是否与实际相符，本次估价假设权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”所述内容真实、完整、有效。\r\n\t";
            ifCond += ("\n" + index++) + "．估价对象为所在建筑物的部分，本次估价以估价对象建筑物区分所有权的共有部分的通行权及水、公共照明、消防等附属设施设备的使用权，能被权利人、实际占有人合法享有为前提进行估价。";
        }else if(certificateEnum.equals(LAND_CERTIFICATE) && textMap.get("certificateType").equals("分户")){
            ifCond += ("\n" + index++) + "．我们已对房屋安全、环境污染等影响估价对象价值的重大因素给予了关注，在无理由怀疑估价对象存在安全隐患且无相应的专业机构进行鉴定、检测的情况下，假设估价对象能正常安全使用。\r\n\t";
            ifCond += ("\n" + index++) + "．由于受专业限制，我们未对估价对象房屋建筑面积及其占用（分摊）的土地使用权面积进行专业测量，本次估价假设估价对象房屋建筑面积及其占用（分摊）的土地使用权面积与《房屋所有权证》和《国有土地使用证》记载面积一致。\r\n\t";
            ifCond += ("\n" + index++) + "．由于我们无法通过第三方进一步查证估价对象是否存在法定优先受偿权，或者查证委托人提供的情况是否与实际相符，本次估价假设权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”所述内容真实、完整、有效。\r\n\t";
            ifCond += ("\n" + index++) + "．估价对象为所在建筑物的部分，本次估价以估价对象建筑物区分所有权的共有部分的通行权及水、公共照明、消防等附属设施设备的使用权，能被权利人、实际占有人合法享有为前提进行估价。";
        }
        else{
            ifCond += ("\n" + index++) + "．我们已对房屋安全、环境污染等影响估价对象价值的重大因素给予了关注，在无理由怀疑估价对象存在安全隐患且无相应的专业机构进行鉴定、检测的情况下，假设估价对象能正常安全使用。\r\n\t";
            ifCond += ("\n" + index++) + "．由于受专业限制，我们未对估价对象房屋建筑面积进行专业测量，本次估价假设估价对象房屋建筑面积与《房屋所有权证》记载面积一致。\r\n\t";
            ifCond += ("\n" + index++) + "．由于我们无法通过第三方进一步查证估价对象是否存在法定优先受偿权，或者查证委托人提供的情况是否与实际相符，本次估价假设权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”所述内容真实、完整、有效。\r\n\t";
            ifCond += ("\n" + index++) + "．估价对象为所在建筑物的部分，本次估价以估价对象建筑物区分所有权的共有部分的通行权及水、公共照明、消防等附属设施设备的使用权，能被权利人、实际占有人合法享有为前提进行估价。";

        }
        textMap.put("ifCond", ifCond);
    }

    /**
     * 结果报告：依据不足假设
     * @param textMap
     */
    private void proofNotEnough(Map<String, Object> textMap,CertificateEnum certificateEnum){
        String proof = "";
        int index = 1;
        if (certificateEnum.equals(IMMOVABLE_CERTIFICATE)){

            if ((Boolean) textMap.get("isShare")) {
                proof += ("\n" + index++) + "．委托人提供的《不动产权证书》记载国有建设用地使用权面积为共用宗地面积，系国有出让城镇住宅用地，未记载估价对象分摊土地使用权面积，本次评估设定估价对象分摊的土地使用权系出让城镇住宅用地。";
            }
            if (!(Boolean) textMap.get("haveOrigin")) {
                // 未提供原件
                proof += (index > 1 ? "\r\n\t" : "") + (index++) + "．本次估价，委托人不能提供估价对象的《不动产权证书》原件，受《不动产登记暂行条例》对不动产登记资料查询资格的限制，注册房地产估价师无法在不动产登记机关查询估价对象登记信息，但通过“成都市房地产评估管理服务信息系统”对估价对象权属登记信息进行了查询，查询的不动产权属证书号、用途、坐落、建筑面积与委托人提供的权属证书复印件记载一致。委托人提供了由房屋所有权人（不动产权利人）出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，承诺提供的权属证书及与估价对象相关的法律文件复印件与原件一致。在难以取得估价对象的权属证书及与估价对象相关的法律文件原件的情况下，注册房地产估价师无法将委托人提供的评估资料复印件与原件进行核对，在无理由怀疑其合法性、真实性、准确性和完整性的情况下，根据委托人提供的（由房屋所有权人出具的）“估价对象权利状况、法定优先受偿款调查说明及承诺书”，本次评估设定委托人提供的估价对象权属证书及与估价对象相关的法律文件复印件与原件一致，并以上述评估资料复印件所载信息为评估依据。";
            }
            if (StringUtils.equals("出租", String.valueOf(textMap.get("usingInfo")))) {
                // 出租
                proof += (index > 1 ? "\r\n\t" : "") + (index++) + "．本公司估价人员实地查勘时，估价对象已出租，委托人不能提供估价对象租赁合同，注册房地产估价师虽进行了尽职调查，难以查证和取得估价对象租赁合同，本次评估设定估价对象租赁收入符合同一供需圈类似房地产的客观租金水平，未考虑租约对估价结果的影响。";
            }

        }
        else {
            if (!(Boolean) textMap.get("haveOrigin")) {
                // 未提供原件
                String hasGt = (certificateEnum.equals(LAND_CERTIFICATE)) ? "和《国有土地使用证》" : "";
                proof += ("\n" + index++) + "．本次估价，委托人不能提供估价对象的《房屋所有权证》" + hasGt + "原件，受《不动产登记暂行条例》对不动产登记资料查询资格的限制，注册房地产估价师无法在不动产登记机关查询估价对象登记信息，但通过“成都市房地产评估管理服务信息系统”对估价对象权属登记信息进行了查询，查询的不动产权属证书号、用途、坐落、建筑面积与委托人提供的权属证书复印件记载一致。委托人提供了由房屋所有权人（不动产权利人）出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，承诺提供的权属证书及与估价对象相关的法律文件复印件与原件一致。在难以取得估价对象的权属证书及与估价对象相关的法律文件原件的情况下，注册房地产估价师无法将委托人提供的评估资料复印件与原件进行核对，在无理由怀疑其合法性、真实性、准确性和完整性的情况下，根据委托人提供的（由房屋所有权人出具的）“估价对象权利状况、法定优先受偿款调查说明及承诺书”，本次评估设定委托人提供的估价对象权属证书及与估价对象相关的法律文件复印件与原件一致，并以上述评估资料复印件所载信息为评估依据。";
            }
            if (certificateEnum.equals(HOUSE_CERTIFICATE) || !"分户".equals(textMap.get("certificateType"))) {
                proof += (index > 1 ? "\r\n\t" : "") + (index++) + "．经委托人介绍，估价对象尚未办理分户《国有土地使用证》，注册房地产估价师虽进行了尽职调查，但难以查证和取得估价对象所在宗地土地权属信息；经注册房地产估价师实地调查，估价对象所在楼房为商品房，本次评估设定估价对象占用（分摊）的土地使用权系出让住宅用地。";
            }
            if (StringUtils.equals("出租", ((String) textMap.get("usingInfo")))){
                // 出租
                proof += (index > 1 ? "\r\n\t" : "") + (index++) + "．本公司估价人员实地查勘时，估价对象已出租，委托人不能提供估价对象租赁合同，注册房地产估价师虽进行了尽职调查，难以查证和取得估价对象租赁合同，本次评估设定估价对象租赁收入符合同一供需圈类似房地产的客观租金水平，未考虑租约对估价结果的影响。";
            }
        }
        textMap.put("proof", StrUtil.isNotEmpty(proof)? proof : "本次评估无依据不足假设。");
    }

    /**
     * 结果报告：不相一致
     * @param textMap
     */
    private void isSameAddress(Map<String, Object> textMap,CertificateEnum certificateEnum){
        String isSameAddress = String.valueOf(textMap.get("isSameAddress"));
        String sceneAddress = String.valueOf(textMap.get("sceneAddress"));
        String location = String.valueOf(textMap.get("location"));
        String addressProve = String.valueOf(textMap.get("addressProve"));
        // 结果报告：不相一致假设
        String isSame = "本报告无不相一致假设。";
        if (certificateEnum.equals(IMMOVABLE_CERTIFICATE)){
            if (!Boolean.valueOf(isSameAddress)) {
                // 地址不一致时显示
                isSame = String.format("委托人提供的估价对象《不动产权证书》记载地址为：%s，经委托人现场指界、确认，注册房地产估价师实地查勘估价对象地址为：%s，根据委托人提供的权属证书%s，本次估价以委托人提供的《不动产权证书》界定标的物与注册房地产估价师实地查勘标的物为同一估价对象为假设前提。",
                        location, sceneAddress, Boolean.valueOf(addressProve) ? "、地址证明" : "");
            }
        }else {
            if (!(Boolean) textMap.get("isSameAddress")){
                // 地址不一致时显示
                isSame = String.format("委托人提供的估价对象《房屋所有权证》记载地址为：%s，经委托人现场指界、确认，注册房地产估价师实地查勘估价对象地址为：%s，根据委托人提供的权属证书%s，本次估价以委托人提供的《房屋所有权证》界定标的物与注册房地产估价师实地查勘标的物为同一估价对象为假设前提。",
                        textMap.get("location"),textMap.get("sceneAddress"), ((Boolean) textMap.get("addressProve"))? "、地址证明" : "");
            }
        }
        textMap.put("isSame", isSame);
    }

    /**
     * 结果报告：限制条件
     * @param textMap
     */
    private void isLimit(Map<String, Object> textMap,CertificateEnum certificateEnum){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String otherPledgePerson = String.valueOf(textMap.get("otherPledgePerson"));
        String rightsType = String.valueOf(textMap.get("rightsType"));
        // 结果报告：限制条件
        String limit = "";
        int index = 2;
        if (isPledge.contains("否")) {
            limit += ("\n" + index++) + "．根据房屋所有权人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，至价值时点，估价对象未设定抵押权，无法定优先受偿权，本次估价以此为引用依据。我们未对上述事项进行调查确认，若“估价对象权利状况、法定优先受偿款调查说明及承诺书”说明及承诺事项不实，造成注册房地产估价师对法定优先受偿款的判断有误，本估价结果需作相应调整或重新进行估价。\r\n\t";
        }
        if (isPledge.contains("同一抵押权人")) {
            limit += String.format("%s．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象向同一抵押权人申请续期贷款。估价对象在原已设立的一般抵押权注销前，本估价结果不得用于本估价目的，否则，本估价结果无效。/T50291-2015）相关规定，本抵押价值估价结果未扣减续贷对应的已抵押担保的债权数额，请报告使用人予以特别关注。\r\n\t",
                    "\n" + index++, pledgePerson,
                    textMap.get("nAmount")
            );
        }
        if (isPledge.contains("新抵押权人")) {
            limit += String.format("%s. 根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，估价对象已设置了一般抵押权，抵押权人为%s，抵押权利价值为%s万元，截至价值时点尚未注销，委托人拟以估价对象向%s申请抵押贷款。估价对象在原已设立的一般抵押权注销前，本估价结果不得用于本估价目的，否则，本估价结果无效。\r\n\t",
                    "\n" + index++, pledgePerson,
                    textMap.get("nAmount"),
                    otherPledgePerson
            );
        }
        if (certificateEnum.equals(LAND_CERTIFICATE)){
            if (textMap.get("certificateType").equals("分户")) {
                limit += ("\n" + index++) + ". 估价对象的房屋建筑面积、土地使用权面积和房地产权属状况是根据估价委托人提供的《房屋所有权证》、《国有土地使用证》复印件所载内容和数据为本次评估依据，我们无法确保本报告估价对象的建筑面积、土地使用权面积和房地产权属状况与房地产权属登记机构“房屋登记薄”、“土地登记薄”中所记载的上述内容有无差异，故本报告不能作为确定估价对象确权的依据。\r\n\t";
            } else {
                limit += ("\n" + index++) + ". 估价对象的房屋建筑面积、房地产权属状况是根据估价委托人提供的《房屋所有权证》复印件所载内容和数据为本次评估依据，我们无法确保本报告估价对象的建筑面积和房地产权属状况与房地产权属登记机构“房屋登记薄”中所记载的上述内容有无差异，故本报告不能作为确定估价对象确权的依据。\r\n\t";
            }
        }
        if (certificateEnum.equals(HOUSE_CERTIFICATE)){
            limit += ("\n" + index++) + "．本报告估价结果包括房屋（含附属设备设施）所有权及其占用（分摊）范围内的土地使用权的价值。该土地使用权和附属的设施设备若与房屋所有权分割处置，本估价结果无效。\r\n\t";

        }
        if (certificateEnum.equals(IMMOVABLE_CERTIFICATE)){
            limit += ("\n" + index++) + "．估价对象的房屋建筑面积、土地使用权面积和房地产权属状况是根据估价委托人提供的《不动产权证书》复印件所载内容和数据为本次评估依据，我们无法确保本报告估价对象的建筑面积、共用（分摊）土地使用权面积和房地产权属状况与不动产登记机构“不动产登记薄”中所记载的上述内容有无差异，故本报告不能作为确定估价对象确权的依据。\r\n\t";
            limit += ("\n" + index++) + "．本报告估价结果包括房屋（含附属设备设施）所有权及其占用（分摊）范围内的土地使用权的价值。该土地使用权和附属的设施设备若与房屋所有权分割处置，本估价结果无效。";
        }

        if ("ALLOT".equals(rightsType)) {
            limit += "本报告估价结果已扣除估价对象划拨土地使用权应缴纳的土地出让金或者相当于土地出让金的价款。\r\n\t";
        } else {
            limit += "本报告估价结果含土地出让金。\r\n\t";
        }
        limit += ("\n" + index++) + "．本估价报告书由“致估价委托人函”、“房地产估价师声明”、“估价的假设和限制条件及使用报告说明”、“估价结果报告”和“附件”构成完整的估价报告，本估价报告必须完整使用方为有效，对仅使用本估价报告中的部分内容，所导致可能发生的损失，本估价机构不承担责任。\r\n\t";
        limit += ("\n" + index++) + "．本估价报告使用期限自估价报告出具之日起为壹年。";
        if (!((Boolean)textMap.get("isNoneShangCase")) ) {
            limit += "若报告使用期限内，国家和地方政策、房地产市场或估价对象状况发生重大变化、本报告假设前提变化等，估价结果需做相应调整或委托估价机构重新估价。";
        }
        limit += ("\r\n\t" + index++) + "．本报告未经本公司同意，不得向委托人、抵押权人和抵押登记机关以外的人员或单位提供，并不得公开发表。\r\n\t";
        limit += String.format("%s．本估价报告全部文件必须配套使用，并经注册房地产估价师和本公司盖章后生效。","\n" + index++);
        textMap.put("limit", limit);
    }

    /**
     * 结果报告：特殊处理
     * @param textMap
     */
    private void specialHandle(Map<String, Object> textMap){
        String isPledge = String.valueOf(textMap.get("isPledge"));
        String pledgePerson = String.valueOf(textMap.get("pledgePerson"));
        String otherPledgePerson = String.valueOf(textMap.get("otherPledgePerson"));
        String usingInfo = String.valueOf(textMap.get("usingInfo"));
        String special = "";
        int index = 2;
        if ("否".equals(isPledge) || StringUtils.isEmpty(isPledge)) {
            special += ("\n" + index++) + "．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”说明及承诺：至价值时点，估价对象不存在法定优先受偿款，本次估价以此为引用依据，本报告设定估价对象在价值时点法定优先受偿款为“0”。上述事项我们未作进一步调查核实，请报告使用人予以特别关注。\r\n\t";
        }
        if (isPledge.contains("同一抵押权人")) {
            special += String.format("%s．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，估价对象已设置了一般抵押权，抵押权人为%s，截至价值时点尚未注销，委托人拟以估价对象向同一抵押权人申请续期贷款。根据《房地产估价规范》（GB/T50291-2015）相关规定，本抵押价值估价结果未扣减续贷对应的已抵押担保的债权数额；在使用本估价报告结论前，应注销估价对象已设置的抵押权，提请报告使用人予以特别关注。\r\n\t",
                    "\n" + index++, pledgePerson);
        }
        if (isPledge.contains("新抵押权人")) {
            special += String.format("%s．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”说明及承诺：估价对象已设置了一般抵押权，抵押权人为%s，截至价值时点尚未注销，委托人拟以估价对象向%s申请抵押贷款，在新的抵押权设立前，权利人将清偿估价对象已抵押担保的所有债权，注销原已设立的抵押权；故本抵押价值估价结果未扣减已抵押担保的债权数额；在使用本估价报告结论前，应注销估价对象已设置的抵押权，提请报告使用人予以特别关注。\r\n\t",
                    "\n" + index++, pledgePerson, otherPledgePerson);
        }

        if (StringUtils.isEmpty(usingInfo) ||  "空置".equals(usingInfo) || "自用".equals(usingInfo)) {
            special += String.format("%s．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，至价值时点，估价对象系%s房地产，未出租，本次估价以此为引用依据。上述事项我们未作进一步调查核实，请报告使用人予以特别关注。\r\n\t",
                    "\n" + index++, StrUtil.isEmpty(usingInfo)?"____":usingInfo);
        }
        if ( "出租".equals(usingInfo) || "已出租".equals(usingInfo)) {
            special += String.format("%s．根据权利人出具的“估价对象权利状况、法定优先受偿款调查说明及承诺书”，至价值时点，估价对象已出租，本次估价以此为引用依据。上述事项我们未作进一步调查核实，请报告使用人予以特别关注。\r\n\t",
                    "\n" + index++);
        }
        textMap.put("special", special);
    }

    /**
     *结果报告：划拨还是出让
     * @param textMap
     */
    private void sellOrAllot(Map<String, Object> textMap){
        String rightsType = String.valueOf(textMap.get("rightsType"));
        String hbStr = "";
        if ("SELL".equals(rightsType)) {
            // 出让金
            hbStr = "本报告估价结果含土地出让金。";
        } else {
            // 划拨
            hbStr = "本报告估价结果已扣除估价对象划拨土地使用权应缴纳的土地出让金或者相当于土地出让金的价款。";
        }
        textMap.put("hbStr", hbStr);
    }

    /**
     * 结果报告：资料附件
     * @param textMap
     */
    private void attachment(Map<String, Object> textMap, PersonalTarget target){
        String attach = "";
        int index = 4;
        PersonalLandCertificateVO land = JSON.parseObject(target.getLandCertificate(), PersonalLandCertificateVO.class);
        if (land != null) {
            attach += ("\n" + index++) + "．《国有土地使用证》（复印件）；\r\n\t";
        }
        if (StrUtil.isNotEmpty((String)textMap.get("bailor1IdNo")) || StrUtil.isNotEmpty((String)textMap.get("bailor2IdNo"))){
            attach += ("\n" + index++) + "．委托人身份证（复印件）；\r\n\t";
        }
        attach += index + "．其他有关资料。";
        textMap.put("attach", attach);
    }


    /**
     * 土地权益
     */
    private void landRight(Map<String, Object> textMap,CertificateEnum certificateEnum){
        // 土地权益:判断是否提供了国土证，如果没有提供国土证。则需要判断是否是划拨
        if (!certificateEnum.equals(LAND_CERTIFICATE)) {
            String landRight = (textMap.get("rightsType")).equals("ALLOT") ? "估价对象所占用的土地为划拨，本次评估设定估价对象占用的土地使用权系划拨用地" : "估价对象所在楼房为商品房，本次评估设定估价对象占用的土地使用权系出让用地";
            textMap.put("landRight", landRight);
        }
    }
}
