package com.ym.mec.biz.service.impl;

import static com.ym.mec.biz.dal.enums.MessageTypeEnum.EMAIL_TENANT_EXPIRATION_REMINDERS;
import static com.ym.mec.biz.dal.enums.MessageTypeEnum.EMAIL_TENANT_RENEWAL_SUCCESSFUL;
import static com.ym.mec.biz.dal.enums.MessageTypeEnum.SMS_TENANT_EXPIRATION_REMINDERS;
import static com.ym.mec.biz.dal.enums.MessageTypeEnum.SMS_TENANT_RENEWAL_SUCCESSFUL;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysRole;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.auth.api.enums.SysUserType;
import com.ym.mec.biz.dal.dao.OrganizationDao;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dao.TenantInfoDao;
import com.ym.mec.biz.dal.dto.TenantConfigDto;
import com.ym.mec.biz.dal.dto.TenantInfoDto;
import com.ym.mec.biz.dal.dto.TenantProductInfoDto;
import com.ym.mec.biz.dal.entity.Employee;
import com.ym.mec.biz.dal.entity.Organization;
import com.ym.mec.biz.dal.entity.PlatformServe;
import com.ym.mec.biz.dal.entity.PlatformServeDetail;
import com.ym.mec.biz.dal.entity.SysArea;
import com.ym.mec.biz.dal.entity.SysConfig;
import com.ym.mec.biz.dal.entity.SysUserTenant;
import com.ym.mec.biz.dal.entity.SysUserTsign;
import com.ym.mec.biz.dal.entity.TenantAssetsInfo;
import com.ym.mec.biz.dal.entity.TenantConfig;
import com.ym.mec.biz.dal.entity.TenantContractRecord;
import com.ym.mec.biz.dal.entity.TenantContractRecord.TenantContractRecordEnum;
import com.ym.mec.biz.dal.entity.TenantInfo;
import com.ym.mec.biz.dal.entity.TenantOrderRecord;
import com.ym.mec.biz.dal.entity.TenantPreJoin;
import com.ym.mec.biz.dal.entity.TenantProductInfo;
import com.ym.mec.biz.dal.entity.TenantProductSumm;
import com.ym.mec.biz.dal.enums.GradeTypeEnum;
import com.ym.mec.biz.dal.enums.JobNatureEnum;
import com.ym.mec.biz.dal.enums.PaymentChannelEnum;
import com.ym.mec.biz.dal.enums.TenantOrderRecordEnum;
import com.ym.mec.biz.dal.enums.YesOrNoEnum;
import com.ym.mec.biz.dal.vo.PlatformServePageVo;
import com.ym.mec.biz.dal.vo.SysUserTenantVo;
import com.ym.mec.biz.dal.vo.TenantInfoPageVo;
import com.ym.mec.biz.service.ContractService;
import com.ym.mec.biz.service.EmployeeService;
import com.ym.mec.biz.service.PayService;
import com.ym.mec.biz.service.PlatformProductService;
import com.ym.mec.biz.service.PlatformServeDetailService;
import com.ym.mec.biz.service.PlatformServeService;
import com.ym.mec.biz.service.StudentService;
import com.ym.mec.biz.service.SysAreaService;
import com.ym.mec.biz.service.SysConfigService;
import com.ym.mec.biz.service.SysMessageService;
import com.ym.mec.biz.service.SysUserTenantService;
import com.ym.mec.biz.service.SysUserTsignService;
import com.ym.mec.biz.service.TenantAssetsInfoService;
import com.ym.mec.biz.service.TenantConfigService;
import com.ym.mec.biz.service.TenantContractRecordService;
import com.ym.mec.biz.service.TenantContractTemplateService;
import com.ym.mec.biz.service.TenantInfoService;
import com.ym.mec.biz.service.TenantOrderRecordService;
import com.ym.mec.biz.service.TenantPreJoinService;
import com.ym.mec.biz.service.TenantProductInfoService;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.page.PageUtil;
import com.ym.mec.common.page.WrapperUtil;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.tenant.TenantContextHolder;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.thirdparty.storage.StoragePluginContext;
import com.ym.mec.thirdparty.storage.provider.KS3StoragePlugin;
import com.ym.mec.thirdparty.yqpay.DateUtils;
import com.ym.mec.util.date.DateUtil;
import com.ym.mec.util.freemarker.FreemarkerTemplateEngine;
import com.ym.mec.util.pdf.PDFUtil;

@Service
public class TenantInfoServiceImpl extends ServiceImpl<TenantInfoDao, TenantInfo> implements TenantInfoService {
    private static final Logger log = LoggerFactory.getLogger(TenantInfoServiceImpl.class);

    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private TenantProductInfoService tenantProductInfoService;
    @Autowired
    private PlatformProductService platformProductService;
    @Autowired
    private PlatformServeService platformServeService;
    @Autowired
    private PlatformServeDetailService platformServeDetailService;
    @Autowired
    private TenantConfigService tenantConfigService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private SysUserTsignService sysUserTsignService;
    @Autowired
    private OrganizationDao organizationDao;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private PayService payService;
    @Autowired
    private IdGeneratorService idGenerator;
    @Autowired
    private TenantOrderRecordService tenantOrderRecordService;
    @Autowired
    private TenantAssetsInfoService assetsInfoService;
    @Autowired
    private StudentService studentService;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    @Lazy
    private ContractService contractService;
    @Autowired
    private SysAreaService sysAreaService;
    @Autowired
    private StoragePluginContext storagePluginContext;
    @Autowired
    private TenantPreJoinService tenantPreJoinService;
    @Autowired
    private TenantContractRecordService tenantContractRecordService;
    @Autowired
    private SysUserTenantService sysUserTenantService;
    @Autowired
    private TenantContractTemplateService tenantContractTemplateService;

    @Value("${contract.baseDir:/var/pdf}")
    private String contractBaseDir;
    //乐小雅协议名称
    public static final String lexiaoyaContract = "lexiaoyaContract";
    //云教室每分钟价格
    public static final String cloudAmount = "${cloudAmount}";
    //云教室人数
    public static final String cloudCount = "${cloudCount}";
    //协议中云教室价格动态的样式 默认一个人的价格
    public static final String defSpan = "<td style='text-align: center;'>" + cloudAmount + "/分钟/1V" + cloudCount + "</td>";
    //超过1人的则需要这样的样式
    public static final String rowspan = "<tr>" + defSpan + "</tr>";

    @Override
    public TenantInfoDao getDao() {
        return this.baseMapper;
    }

    /**
     * 新增机构
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addTenantInfo(TenantInfoDto dto) {
        //校验手机号是否唯一
        RBucket<Object> bucket = checkPhone(dto);
        //校验营业执照信息
        checkTsign(dto.getTsignCode());

        //添加机构基本信息
        Integer userId = getUserId();
        TenantInfo tenantInfo = new TenantInfo();
        BeanUtils.copyProperties(dto, tenantInfo);
        tenantInfo.setPayState(0);
        tenantInfo.setState(2);
        tenantInfo.setCreatedBy(userId);
        tenantInfo.setCreatedTime(new Date());
        try {
            baseMapper.insert(tenantInfo);
        } catch (DuplicateKeyException e) {
            throw new BizException("同一个手机号只能创建一个机构!");
        }

        //机构id
        Integer tenantId = tenantInfo.getId();
        //添加机构产品信息
        setIdByApply(tenantId, dto.getProductInfo(), dto.getProductInfo()::setTenantId,
                tenantProductInfoService::addTenantProduct);
        //新建机构是0元时，直接默认支付成功
        //2022年2月10日 根据需求进行变更，0元要走缴费流程，因为缴费之前要弹协议给客户看
//        if (dto.getProductInfo().getPayAmount().compareTo(BigDecimal.ZERO) == 0) {
//            //修机构相关的改付款时间
//            tenantOpen(tenantId);
//            String orderNo = idGenerator.generatorId("payment") + "";
//            //写入付款记录
//            createOrderRecord(tenantId, BigDecimal.ZERO, orderNo, TenantOrderRecordEnum.TENANT_OPEN, 1, null);
//        }
        //添加机构配置
        setIdByApply(tenantId, dto.getConfig(), dto.getConfig()::setTenantId, tenantConfigService::addConfig);
        //查询机构入驻表如果公司全称和联系电话相同则把入驻表修改为已入驻
        TenantPreJoin preJoin = tenantPreJoinService.getOne(new QueryWrapper<TenantPreJoin>()
                .eq("tsign_code_", dto.getTsignCode())
                .eq("state_", 0));
        if (Objects.nonNull(preJoin)) {
            preJoin.setState(1);
            tenantPreJoinService.updateById(preJoin);
        }
        //添加人员机构关联关系
        SysUserTenant userTenant = new SysUserTenant();
        userTenant.setUserId(userId);
        userTenant.setTenantId(tenantId);
        userTenant.setCreatedTime(new Date());
        sysUserTenantService.save(userTenant);
        //释放
        bucket.delete();
    }

    /**
     * 修改机构信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTenantInfo(TenantInfoDto dto) {
        //查询机构信息
        TenantInfo tenantInfo = Optional.ofNullable(dto)
                .map(TenantInfoDto::getId)
                .map(this::getById)
                .orElseThrow(() -> new BizException("未找到该机构信息,机构信息不能为空!"));

        Integer tenantId = tenantInfo.getId();

        //是否修改了机构名称或机构编码
        if (!StringUtils.equals(dto.getTsignName(), tenantInfo.getTsignName()) || !StringUtils.equals(dto.getTsignCode(), tenantInfo.getTsignCode())) {
            //升级机构协议版本号
            tenantContractTemplateService.upgradeContractVersion(tenantId);
            //添加签章信息
            contractService.addTsign(tenantInfo.getUserId(), dto.getTsignCode(), dto.getTsignName(), tenantId);
        }

        //机构状态 1已缴费，并且 机构注册的手机号与本次修改后的手机号不同，就证明本次修改了手机号 则需要修改机构的账号信息
        if (1 == tenantInfo.getPayState() && !Objects.equals(tenantInfo.getPhone(), dto.getPhone())) {
            //校验修改后的手机号是否是唯一
            RBucket<Object> bucket = checkPhone(dto);
            //
            if (Objects.nonNull(tenantInfo.getUserId())) {
                //
                SysUser tenantUser = sysUserFeignService.queryUserById(tenantInfo.getUserId());
                tenantUser.setPhone(dto.getPhone());
                sysUserFeignService.updateSysUser(tenantUser);
            }
            //释放锁
            bucket.delete();
        }

        //机构状态 0未缴费
        if (0 == tenantInfo.getPayState()) {
            //校验营业执照信息
            checkTsign(dto.getTsignCode());
            //修改产品
            if (Objects.nonNull(dto.getProductInfo())) {
                setIdByApply(tenantInfo.getId(), dto.getProductInfo(), dto.getProductInfo()::setTenantId,
                        tenantProductInfoService::updateTenantProduct);
            }
        }

        Date now = new Date();
        BeanUtils.copyProperties(dto, tenantInfo);
        tenantInfo.setUpdatedBy(getUserId());
        tenantInfo.setUpdatedTime(now);
        try {
            this.updateById(tenantInfo);
        } catch (DuplicateKeyException e) {
            throw new BizException("同一个手机号只能创建一个机构!");
        }

        //修改机构配置
        if (Objects.nonNull(dto.getConfig())) {
            setIdByApply(tenantInfo.getId(), dto.getConfig(), dto.getConfig()::setTenantId, tenantConfigService::updateConfig);
        }
    }

    /**
     * 查询单个机构详情
     *
     * @param tenantId 机构id
     */
    @Override
    public TenantInfoDto queryTenantInfo(Integer tenantId) {
        //查询机构基础信息
        TenantInfo tenantInfo = Optional.ofNullable(tenantId)
                .map(baseMapper::selectById)
                .orElseThrow(() -> new BizException("未查询到机构信息[" + "tenantId" + "]"));
        TenantInfoDto tenantInfoDto = new TenantInfoDto();
        BeanUtils.copyProperties(tenantInfo, tenantInfoDto);
        //机构产品信息
        TenantProductInfo productInfo = tenantProductInfoService.getOne(new QueryWrapper<TenantProductInfo>()
                .eq("tenant_id_", tenantId).eq("using_", 0));
        setTenantInfo(productInfo, new TenantProductInfoDto(), tenantInfoDto::setProductInfo);
        //机构配置信息
        TenantConfig tenantConfig = tenantConfigService.getOne(new QueryWrapper<TenantConfig>()
                .eq("tenant_id_", tenantId));
        setTenantInfo(tenantConfig, new TenantConfigDto(), tenantInfoDto::setConfig);
        //获取服务信息
        PlatformServe platformServe = platformServeService.getOne(new QueryWrapper<PlatformServe>()
                .eq("id_", productInfo.getServeId()));
        Optional.ofNullable(platformServe)
                .map(PlatformServe::getName)
                .ifPresent(tenantInfoDto::setPlatformServeName);
        //获取服务详情
        PlatformServeDetail serveDetail = platformServeDetailService.getOne(new QueryWrapper<PlatformServeDetail>()
                .eq("id_", productInfo.getServeDetailId()));
        Optional.ofNullable(serveDetail)
                .map(PlatformServeDetail::getStudentUpLimit)
                .ifPresent(tenantInfoDto::setStudentUpLimit);
        return tenantInfoDto;
    }

    /**
     * 订单购买信息查询
     * 为什么要这个功能？
     * 防重复支付，若该机构开通时已经支付还会主动查询第三方付款状态
     *
     * @param tenantId 机构id
     */
    @Override
    public TenantInfoDto queryTenantInfoCheck(Integer tenantId) {
        if (Objects.isNull(tenantId)) {
            throw new BizException("请传入机构id");
        }
        //查询开通 续费 正在支付的记录
        List<TenantOrderRecord> record = tenantOrderRecordService.list(new WrapperUtil<TenantOrderRecord>().queryWrapper()
                .eq("tenant_id_", tenantId)
                .isNotNull("trans_no_")
                .and(wrapper -> wrapper.eq("order_type_", "TENANT_OPEN")
                        .or().eq("order_type_", "TENANT_RENEW"))
                .eq("payment_channel_", "ADAPAY")
                .eq("order_state_", 0));
        //存在，就去第三方再次校验
        if (CollectionUtils.isNotEmpty(record)) {
            record.forEach(r -> tenantOrderRecordService.checkTenantOrder(r));
        }
        return queryTenantInfo(tenantId);
    }

    /**
     * 启用/停用
     * 第一次启用默认开通账号
     *
     * @param id    机构id
     * @param state 机构状态1启动 2停用
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, Object> opsTenantState(Integer id, Integer state) {
        if (Objects.nonNull(state) && state == 1 || state == 2) {
            TenantInfo tenantInfo = baseMapper.selectById(id);
            if (Objects.isNull(tenantInfo)) {
                throw new BizException("该机构数据异常! ID : [" + id + "]");
            }
            Integer userId = getUserId();
            tenantInfo.setUpdatedBy(userId);
            tenantInfo.setUpdatedTime(new Date());
            tenantInfo.setState(state);
            String pw = null;
            //状态= 开通
            if (state == 1) {
                if (tenantInfo.getPayState() == 0) {
                    throw new BizException("机构未缴费无法开通!");
                }
                //已支付完成 并且 没有创建用户信息
                if (tenantInfo.getPayState() == 1 && Objects.isNull(tenantInfo.getUserId())) {
                    //初次开通 返回密码
                    pw = checkFirstOpen(tenantInfo);
                }
            }
            baseMapper.updateById(tenantInfo);
            Map<String, Object> result = new HashMap<>();
            result.put("pw", pw);
            result.put("tenantInfo", tenantInfo);
            return result;
        }
        throw new BizException("传入机构状态参数异常!");
    }

    /**
     * 第一次开通服务
     *
     * @param tenantInfo 机构信息
     * @return 初次开通机构返回机构密码
     */
    private String checkFirstOpen(TenantInfo tenantInfo) {
        Integer tenantId = tenantInfo.getId();
        SysUser user = sysUserFeignService.queryUserByMobile(tenantInfo.getPhone());
        if (Objects.nonNull(user)) {
            if (user.getUserType().contains(SysUserType.SYSTEM.getCode())) {
                return null;
            }
        }

        //防止重复点击 加锁
        String key = "Tenant_First_Open:" + tenantId;
        RBucket<Object> bucket = redissonClient.getBucket(key);
        //原子操作 抢锁成功为true
        if (!bucket.trySet(tenantId, 3L, TimeUnit.SECONDS)) {
            throw new BizException("机构正在开通中请勿频繁操作");
        }
        //获取产品信息得到服务id
        TenantProductInfo productInfo = tenantProductInfoService.getOne(new WrapperUtil<TenantProductInfo>()
                .hasEq("tenant_id_", tenantId).queryWrapper().eq("using_", 0));
        if (Objects.isNull(productInfo)) {
            throw new BizException("开通账号信息异常!未查询到购买的产品信息!");
        }
        //根据服务id查询对应的菜单值(该值是多个id用逗号分割的)
        List<String> menuIdList = platformProductService.queryMenuIdList(productInfo.getServeId());
        if (CollectionUtils.isEmpty(menuIdList)) {
            throw new BizException("开通账号信息异常!未查询到购买的产品信息!!");
        }
        //拆分菜单获取菜单ID
        List<Integer> collectMenuId = getMenuId(menuIdList);
        //创建一个和机构同名称的“分部”
        Integer orgId = createOrg(tenantInfo);
        //建立角色
        Integer roleId = createRole(tenantInfo);
        //密码规则 gym + 随机6位数字
        String pwStr = "gym" + (int) ((Math.random() * 9 + 1) * 100000);
        // 创建账号、用户信息、用户和角色关系
        createUser(tenantInfo, orgId, Lists.newArrayList(roleId), new BCryptPasswordEncoder().encode(pwStr));
        //建立角色和菜单关系数据
        Lists.partition(collectMenuId, 50)
                .forEach(idList -> employeeService.batchInsertRoleMenu(roleId, idList, tenantId));
        //添加签章信息
        contractService.addTsign(tenantInfo.getUserId(), tenantInfo.getTsignCode(), tenantInfo.getTsignName(), tenantId);

        Date now = new Date();

        //创建资产信息
        TenantAssetsInfo assetsInfo = new TenantAssetsInfo();
        assetsInfo.setTenantId(tenantId);
        assetsInfo.setBalance(BigDecimal.ZERO);
        assetsInfo.setFrozenAmount(BigDecimal.ZERO);
        assetsInfo.setCreatedTime(now);
        assetsInfoService.save(assetsInfo);
        //修改有效期
        Date expiryDate = getExpiryDate(productInfo.getExpiryCount(), productInfo.getExpiryUnit(), now);
        productInfo.setExpiryDate(expiryDate);
        tenantProductInfoService.updateById(productInfo);
        //关联精彩活动等数据
        baseMapper.openInitNewsInformation(tenantId, orgId.toString());
        //释放锁
        bucket.delete();

        return pwStr;
    }

    //计算过期时间
    private Date getExpiryDate(Integer expiryCount, String expiryUnit, Date date) {
        Date expiryDate;
        if (TenantProductInfo.MONTH.equals(expiryUnit)) {
            expiryDate = DateUtils.addMonths(date, expiryCount);
        } else if (TenantProductInfo.YEAR.equals(expiryUnit)) {
            expiryDate = DateUtils.addYears(date, expiryCount);
        } else {
            throw new BizException("产品信息异常!开通失败!");
        }
        return expiryDate;
    }

    //拆分菜单获取菜单ID
    private List<Integer> getMenuId(List<String> menuIdList) {
        return menuIdList.stream()
                .map(ids -> ids.split(","))
                .flatMap(Arrays::stream)
                .distinct()
                .map(Integer::parseInt)
                .collect(Collectors.toList());
    }

    //创建角色
    private Integer createRole(TenantInfo tenantInfo) {
        SysRole sysRole = new SysRole();
        sysRole.setRoleName("管理员");
        sysRole.setRoleDesc("管理员");
        sysRole.setCreateTime(new Date());
        sysRole.setDelFlag("0");
        sysRole.setTenantId(tenantInfo.getId());
        employeeService.insertSysRole(sysRole);
        return sysRole.getId();
    }

    //创建账号、用户信息、用户和角色关系 返回密码
    private void createUser(TenantInfo tenantInfo, Integer orgId, List<Integer> roles, String pw) {
        Employee e = new Employee();
        e.setTenantId(tenantInfo.getId());
        e.setPhone(tenantInfo.getPhone());
        e.setRealName(tenantInfo.getContacts());
        e.setJobNature(JobNatureEnum.FULL_TIME);
        e.setEntryDate(LocalDate.now().toDate());
        e.setRoles(roles);
        e.setRoleIds(roles);
        e.setUserType(SysUserType.SYSTEM.getCode());
        e.setOrganIdList(String.valueOf(orgId));
        e.setPassword(pw);
        e.setTenantIds(Lists.newArrayList(tenantInfo.getId()));
        e.setCreateTenantUserId(tenantInfo.getCreatedBy());
        try {
            log.info("createUser >>>> {}", e);
            employeeService.add(e);
            //将创建的userId反写到机构表
            tenantInfo.setUserId(e.getId());
        } catch (Exception ex) {
            throw new BizException("开通账号信息异常!");
        }
    }

    //创建一个和机构同名称的“分部”
    private Integer createOrg(TenantInfo tenantInfo) {
        Organization organization = new Organization();
        organization.setName(tenantInfo.getName());
        organization.setDelFlag(YesOrNoEnum.NO);
        organization.setGradeType(GradeTypeEnum.SIX_PLUS);
        organization.setCreateTime(new Date());
        organization.setTenantId(tenantInfo.getId());
        organization.setAreaId(tenantInfo.getAreaId());
        organizationDao.insert(organization);
        return organization.getId();
    }

    /**
     * 分页查询
     *
     * @param param 查询条件
     * @return
     */
    @Override
    public PageInfo<TenantInfoPageVo> queryPage(Map<String, Object> param) {
        //获取当前登录用户
        SysUser user = getUser();
        if (Objects.nonNull(user.getIsSuperAdmin()) && user.getTenantId() == -1) {
            //不是超级管理员 但是 是平台账号，则需要查询自己关联的机构有哪些
            if (!user.getIsSuperAdmin()) {
                List<SysUserTenantVo> sysUserTenantVos = sysUserTenantService.queryUserTenant(user.getId());
                if (CollectionUtils.isNotEmpty(sysUserTenantVos)) {
                    List<Integer> tenantIds = sysUserTenantVos.stream().map(SysUserTenantVo::getTenantId).collect(Collectors.toList());
                    param.put("tenantIds", tenantIds);
                } else {
                    throw new BizException("您的账号暂无机构权限，请联系平台管理员配置");
                }
            }
        }
        Page<TenantInfoPageVo> pageInfo = PageUtil.getPageInfo(param);
        pageInfo.setAsc("a.id_");
        return PageUtil.pageInfo(baseMapper.queryPage(pageInfo, param));
    }

    @Override
    public TenantInfo get(Integer tenantId) {
        return baseMapper.selectById(tenantId);
    }

    /**
     * 预览协议
     *
     * @param tenantId 机构id
     * @param en       协议类型 开通还是续费
     * @param val      续费的时间长度
     */
    @Override
    public String getContract(Integer tenantId, TenantContractRecordEnum en, Integer val) {
        List<TenantInfoPageVo> tenantInfoList = baseMapper.queryPage(new HashMap<String, Object>() {{
            put("tenantId", tenantId);
        }});
        //查询协议中的信息
        TenantInfoPageVo tenantInfo = Optional.ofNullable(tenantInfoList)
                .filter(CollectionUtils::isNotEmpty)
                .map(a -> a.get(0))
                .orElseThrow(() -> new BizException("未查询到机构信息"));
        Date now = new Date();
        Date startDate = now;
        Date expiryDate;
        //如果本次协议是开通服务则直接取开通时的价格并在现在的时间计算到期时间
        if (TenantContractRecordEnum.OPEN.equals(en)) {
            val = tenantInfo.getExpiryCount();
            tenantInfo.setContractPrice(tenantInfo.getOpenPrice());
            expiryDate = getExpiryDate(val, tenantInfo.getExpiryUnit(), now);
        } else {
            //修改续费时间
            if (tenantInfo.getExpireDate().compareTo(now) > 0) {
                //在上一个到期时间节点上做增加时间的操作
                expiryDate = getExpiryDate(val, tenantInfo.getExpiryUnit(), tenantInfo.getExpireDate());
                //协议开始时间为上一个到期时间节点+1天
                startDate = DateUtils.addDays(tenantInfo.getExpireDate(), 1);
            } else {
                //如果上一个时间已经到期则 取现在时间
                expiryDate = getExpiryDate(val, tenantInfo.getExpiryUnit(), now);
            }
            //续费时 取合同价
            BigDecimal contractAmount = tenantInfo.getContractPrice().multiply(new BigDecimal(val), new MathContext(1, RoundingMode.HALF_UP));
            tenantInfo.setContractPrice(contractAmount);
            tenantInfo.setExpiryCount(val);
        }
        //获取协议变量参数
        Map<String, Object> param = getContractParam(tenantInfo);
        //设置协议服务开始的时间和到期时间
        Function<Date, String> getStrDate = (date) -> DateUtils.formatDate(date, "yyyy年MM月dd日");
        //协议中服务开始时间
        param.put("startDate", getStrDate.apply(startDate));
        //协议中服务过期时间
        param.put("expireDate", getStrDate.apply(expiryDate));
        //当前时间
        param.put("nowDate", getStrDate.apply(now));
        //生成模版
        FreemarkerTemplateEngine templateEngine = FreemarkerTemplateEngine.getInstance();
        templateEngine.setClassForTemplateLoading(TenantInfoServiceImpl.class, "/config/contracts/");
        //文件地址验证
        String srcPath = contractBaseDir + "/" + lexiaoyaContract + "/" + tenantId + "-" + tenantInfo.getServeName() + DateUtils.formatDate(new Date(), "yyyyMMddHHmmss") + ".html";
        File srcFile = new File(srcPath);
        if (!srcFile.getParentFile().exists()) {
            srcFile.getParentFile().mkdirs();
        }
        //读取模版并替换模版内容
        templateEngine.render(param, lexiaoyaContract + ".ftl", srcPath);
        try {
            return FileUtils.readFileToString(srcFile, Charset.defaultCharset());
        } catch (IOException e) {
            log.error("读取产品协议出错", e.getCause());
            throw new BizException("读取产品协议出错");
        } finally {
            //删除文件
            FileUtils.deleteQuietly(srcFile);
        }
    }

    /**
     * 获取协议变量参数
     */
    private Map<String, Object> getContractParam(TenantInfoPageVo tenantInfo) {
        //将数据转换为Map
        Map<String, Object> param = WrapperUtil.toMap(tenantInfo);
        //购买服务的时长单位
        param.put("expiryUnit", TenantProductInfo.MONTH.equals(tenantInfo.getExpiryUnit()) ? "个月" : "年");
        //乙方公章
        param.put("officialSealB", "https://daya.ks3-cn-beijing.ksyun.com/202202/Sx6rzWm.png");
        //获取产品名称
        opsContractData(tenantInfo.getServeId(), platformServeService::queryProductNameById,
                "未查询到产品信息！",
                param::put, "productName", PlatformServePageVo::getProductName);
        //获取城市
        SysArea city = opsContractData(tenantInfo.getAreaId(), sysAreaService::get,
                "未查询到城市信息！",
                param::put, "city", SysArea::getName);
        //获取省份
        opsContractData(city.getId(), sysAreaService::getParentArea,
                "未查询到省份信息！",
                param::put, "province", SysArea::getName);
        //查询机构配置信息
        TenantConfig tenantConfig = opsContractData(tenantInfo.getId(), tenantConfigService::queryByTenantId,
                "未查询到机构配置信息！",
                param::put, "chargeRate", TenantConfig::getChargeRate);
        //甲方企业公章
        param.put("officialSealA", tenantConfig.getCorporateChops());
        //获取云教室规则  String人数 BigDecimal每分钟扣费标准
        Map<String, BigDecimal> rule = opsContractData(tenantConfig.getConfig(), tenantConfigService::getCloudRoomRule,
                "未查询到云教室的价格配置！",
                param::put, "rowspan", Map::size);

        AtomicReference<String> rowspanData = new AtomicReference<>("");
        //生成云教室扣费的样式
        rule.forEach((k, v) -> {
            //人数
            int count = Integer.parseInt(k) - 1;
            if (count == 1) {
                param.put("defRowspanData", replaceTemplate(defSpan, count, v));
            } else {
                rowspanData.set(rowspanData.get() + replaceTemplate(rowspan, count, v));
            }
        });
        param.put("rowspanData", rowspanData.get());
        return param;
    }

    private static <O, T, S> T opsContractData(O param, Function<O, T> fun, String msg,
                                               BiConsumer<String, Object> putC, String key, Function<T, S> val) {
        Optional<T> optional = Optional.ofNullable(fun.apply(param));
        optional.orElseThrow(() -> new BizException(msg));
        optional.ifPresent(a -> {
            S apply = val.apply(a);
            if (apply instanceof BigDecimal) {
                putC.accept(key, ((BigDecimal) apply).setScale(2, RoundingMode.HALF_UP).toString());
            } else {
                putC.accept(key, apply);
            }
        });
        return optional.get();
    }

    private String replaceTemplate(String tem, Integer count, BigDecimal v) {
        return tem.replace(cloudCount, count + "")
                .replace(cloudAmount, v.toString());
    }

    /**
     * 生成PDF协议上传到服务器并返回协议地址
     *
     * @param tenantId 机构id
     * @param en       协议类型 开通还是续费
     * @param val      续费的时间长度-续费才有值
     * @return 协议地址
     */
    private String getContractAndUpLoad(Integer tenantId, TenantContractRecordEnum en, Integer val) {
        String key = "getContractAndUpLoad:" + tenantId + ":" + en.getMsg();
        log.info("getContractAndUpLoad >>>>key:{}", key);
        RBucket<Object> bucket = redissonClient.getBucket(key);
        if (!bucket.trySet(tenantId, 15L, TimeUnit.SECONDS)) {
            return null;
        }
        log.info("getContractAndUpLoad >>>>ok  tenantId:{} en:{} val:{}", tenantId, en, val);
        Date now = new Date();
        //创建PDF本次缓存地址
        String srcPdfPath = contractBaseDir + lexiaoyaContract + DateUtils.formatDate(now, "yyyyMMddHHmmss") + "/" + tenantId + "_" + java.time.LocalDateTime.now().getSecond() + ".pdf";
        //生成文件
        File srcFile = new File(srcPdfPath);
        if (!srcFile.getParentFile().exists()) {
            srcFile.getParentFile().mkdirs();
        }
        //获取协议内容
        String contractHtml = getContract(tenantId, en, val);
        // 生成PDF格式协议
        try {
            PDFUtil.renderToPDFByData(TenantInfoServiceImpl.class.getResource("/").getFile(), contractHtml, srcPdfPath, "simsun.ttc");
        } catch (IOException e) {
            FileUtils.deleteQuietly(srcFile);
            throw new BizException("生成产品协议失败");
        }
        //上传到云服务器后获取上传地址
        String dateStrOss = DateUtils.formatDate(now, "yyyy/MM/dd");
        dateStrOss = "oa/" + dateStrOss + "/" + DateUtil.getHour(now);
        String pdfFilePath = storagePluginContext.uploadFile(KS3StoragePlugin.PLUGIN_NAME, dateStrOss, srcFile);
        //删除本次PDF生成的文件缓存
        FileUtils.deleteQuietly(srcFile);
        //删除缓存
        if (en.equals(TenantContractRecordEnum.RENEW)) {
            redissonClient.getBucket(key).delete();
        }
        return pdfFilePath;
    }

    /**
     * 机构开通缴费
     */
    public Map<String, Object> tenantOpenPay(Integer tenantId) throws Exception {
        SysConfig config = sysConfigService.findByParamName("platform_collection_organ");
        if (Objects.isNull(config)) {
            throw new BizException("未查询到机构收款分部信息！");
        }

        TenantOrderRecordEnum tenantEnum = TenantOrderRecordEnum.TENANT_OPEN;
        Map<String, Object> result = new HashMap<>();
        TenantProductInfo productInfo = getProductInfo(tenantId);
        //生成订单编号
        String orderNo = idGenerator.generatorId("payment") + "";
        int orderState = 0;

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        //消费大于0元则拉起支付
        if (productInfo.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
            result = payService.getPayToPlatformMap(tenantId,
                    PaymentChannelEnum.ADAPAY,
                    productInfo.getPayAmount(),
                    BigDecimal.ZERO,
                    orderNo,
                    baseApiUrl + "/api-web/tenantInfo/callback",
                    baseApiUrl + "/api-web/tenantInfo/paymentResult?orderNo=" + orderNo,
                    tenantEnum.getMsg(),
                    tenantEnum.getMsg()
            );
        } else {
            //已支付
            orderState = 1;
            tenantOpenSuccess(tenantId);
            Map<String, Object> payMap = new HashMap<>();
            payMap.put("orderNo", orderNo);
            result.put("payMap", payMap);
        }
        result.put("amount", productInfo.getPayAmount());
        result.put("orderNo", orderNo);
        createOrderRecord(tenantId, productInfo.getPayAmount(), orderNo, tenantEnum, orderState, WrapperUtil.toStr(result, "routingMerNos"));
        log.info("tenant pay >>>>> {} ", result);
        return result;
    }

    /**
     * 机构续费
     *
     * @param tenantId 机构id
     * @param val      购买周期
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> tenantRenewPay(Integer tenantId, Integer val) throws Exception {

        TenantOrderRecordEnum tenantEnum = TenantOrderRecordEnum.TENANT_RENEW;
        Map<String, Object> result = new HashMap<>();
        TenantProductInfo productInfo = getProductInfo(tenantId);
        //续费时 取合同价
        BigDecimal amount = productInfo.getContractPrice().multiply(new BigDecimal(val), new MathContext(1, RoundingMode.HALF_UP));
        //生成订单编号
        String orderNo = idGenerator.generatorId("payment") + "";
        int orderState = 0;

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        //消费大于0元则拉起支付
        if (amount.compareTo(BigDecimal.ZERO) > 0) {
            result = payService.getPayToPlatformMap(tenantId,
                    PaymentChannelEnum.ADAPAY,
                    amount,
                    BigDecimal.ZERO,
                    orderNo,
                    baseApiUrl + "/api-web/tenantInfo/callback",
                    baseApiUrl + "/api-web/tenantInfo/paymentResult?orderNo=" + orderNo,
                    tenantEnum.getMsg(),
                    tenantEnum.getMsg()
            );
            //操作续费信息，把续费周期存起来
            opsRenewInfo(tenantId).set(val, 1, TimeUnit.HOURS);
        } else {
            //续费成功
            renewSuccess(val, productInfo, amount);
            orderState = 1;
        }
        result.put("amount", amount);
        result.put("orderNo", orderNo);
        createOrderRecord(tenantId, amount, orderNo, tenantEnum, orderState, WrapperUtil.toStr(result, "routingMerNos"));
        log.info("tenant pay >>>>> {} ", result);
        return result;
    }

    //操作续费信息
    public RBucket<Integer> opsRenewInfo(Integer key) {
        return redissonClient.getBucket(TenantOrderRecordEnum.TENANT_RENEW.getCode() + ":" + key);
    }

    /**
     * 续费成功
     *
     * @param val         续费周期
     * @param productInfo 原产品信息
     * @param amount      续费总金额
     */
    public void renewSuccess(Integer val, TenantProductInfo productInfo, BigDecimal amount) {
        Integer tenantId = productInfo.getTenantId();
        RBucket<Object> bucket = redissonClient.getBucket("tenant_renew_success:" + tenantId);
        if (!bucket.trySet(tenantId, 1, TimeUnit.MINUTES)) {
            //防止重复修改数据
            return;
        }

        Date expiryDate;
        Date now = new Date();
        if (productInfo.getExpiryDate().compareTo(now) > 0) {
            //在上一个时间节点上做增加操作
            expiryDate = getExpiryDate(val, productInfo.getExpiryUnit(), productInfo.getExpiryDate());
        } else {
            //如果上一个时间已经到期则 取现在时间
            expiryDate = getExpiryDate(val, productInfo.getExpiryUnit(), now);
        }
        productInfo.setExpiryDate(expiryDate);
        productInfo.setPayAmount(productInfo.getPayAmount().add(amount));
        productInfo.setExpiryCount(productInfo.getExpiryCount() + val);

        //生成协议并上传得到地址
        TenantContractRecordEnum renew = TenantContractRecordEnum.RENEW;
        String contractPath = getContractAndUpLoad(tenantId, renew, val);
        if (StringUtils.isBlank(contractPath)) {
            return;
        }
        //写入协议记录
        tenantContractRecordService.insertContractRecord(tenantId, contractPath, renew);
        //更新产品信息
        tenantProductInfoService.updateById(productInfo);
        //发送邮件短信
        Map<String, Object> par = new HashMap<>();
        par.put("tenantId", tenantId);
        List<TenantInfoPageVo> tenantInfos = baseMapper.queryPage(par);
        if (CollectionUtils.isNotEmpty(tenantInfos)) {
            TenantInfoPageVo tenantInfo = tenantInfos.get(0);
            //邮件
            if (StringUtils.isNotBlank(tenantInfo.getEmail())) {
                //机构名称 服务名称 学员上限 有效期
                Object[] emailMsg = {tenantInfo.getName(), tenantInfo.getServeName(), tenantInfo.getStudentUpLimit(), DateUtils.formatDate(expiryDate, "yyyy年MM月dd日")};
                Map<Integer, String> sendPar = new HashMap<>();
                sendPar.put(tenantInfo.getUserId(), tenantInfo.getEmail());
                log.info("platformSendToAll>>> 续费 receiveUserId {} email {} objs {} sendPar {}", tenantInfo.getUserId(), tenantInfo.getEmail(), emailMsg, sendPar);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.EMAIL,
                        EMAIL_TENANT_RENEWAL_SUCCESSFUL,
                        sendPar, null, 0, null,
                        "SYSTEM", emailMsg);
            }
            //短信
            if (StringUtils.isNotBlank(tenantInfo.getPhone())) {
                //机构名称
                Object[] phoneMsg = {tenantInfo.getName()};
                Map<Integer, String> sendPar2 = new HashMap<>();
                sendPar2.put(tenantInfo.getUserId(), tenantInfo.getPhone());
                log.info("platformSendToAll>>> 续费 receiveUserId {} phone {} objs {} sendPar {}", tenantInfo.getUserId(), tenantInfo.getPhone(), phoneMsg, sendPar2);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.AWSMS,
                        SMS_TENANT_RENEWAL_SUCCESSFUL,
                        sendPar2, null, 0, null,
                        "SYSTEM", phoneMsg);
            }
        }

        //释放锁
        bucket.delete();
    }

    private TenantProductInfo getProductInfo(Integer tenantId) {
        TenantInfo tenantInfo = Optional.ofNullable(tenantId)
                .map(baseMapper::selectById)
                .orElseThrow(() -> new BizException("未查询到机构信息!"));
        String key = "Tenant_Pay:" + tenantId;
        RBucket<Object> bucket = redissonClient.getBucket(key);
        //原子操作 抢锁成功为true
        if (!bucket.trySet(tenantId, 10, TimeUnit.SECONDS)) {
            if (tenantInfo.getPayState() == 1) {
                throw new BizException("请勿频繁操作！");
            }
            throw new BizException("正在缴费中请稍后!");
        }
        //机构产品信息
        return tenantProductInfoService.getOne(new QueryWrapper<TenantProductInfo>()
                .eq("tenant_id_", tenantId).eq("using_", 0));
    }

    //生成订单
    @Override
    public TenantOrderRecord createOrderRecord(Integer tenantId, BigDecimal payAmount, String orderNo, TenantOrderRecordEnum
            tenantEnum, Integer orderState, String merNos) {
        TenantOrderRecord record = new TenantOrderRecord();
        record.setTenantId(tenantId);
        record.setOrderNo(orderNo);
        record.setOrderType(tenantEnum.getCode());
        record.setPaymentChannel(PaymentChannelEnum.ADAPAY.getCode());
        record.setExpectAmount(payAmount);
        record.setActualAmount(payAmount);
        record.setOrderState(orderState);
        record.setMerNos(merNos);
        LocalDateTime now = LocalDateTime.now();
        if (orderState == 1) {
            record.setPayDate(now.toLocalDate().toDate());
            record.setPayTime(now.toDate());
        }
        record.setCreatedTime(now.toDate());
        tenantOrderRecordService.save(record);
        return record;
    }

    /**
     * 校验手机号
     */
    private RBucket<Object> checkPhone(TenantInfoDto dto) {
        //防止重复点击 加锁
        String key = "Tenant_Check_Phone:" + dto.getPhone();
        RBucket<Object> bucket = redissonClient.getBucket(key);
        //原子操作 抢锁成功为true
        if (!bucket.trySet(dto.getPhone(), 1L, TimeUnit.MINUTES)) {
            throw new BizException("同一个手机号请勿频繁操作！");
        }
        SysUser sysUser = Optional.of(dto)
                .map(TenantInfoDto::getPhone)
                .map(sysUserFeignService::queryUserByMobile)
                .orElse(null);
        TenantInfo phone = this.getOne(new WrapperUtil<TenantInfo>().queryWrapper()
                .eq("phone_", dto.getPhone()));
        if (Objects.nonNull(phone)) {
            throwPhoneBizEx(bucket);
        }
        if (Objects.nonNull(sysUser)) {
            throwPhoneBizEx(bucket);
        }
        return bucket;
    }

    private void throwPhoneBizEx(RBucket<Object> bucket) {
        bucket.delete();
        throw new BizException("该手机号已被注册!");
    }

    /**
     * set机构Id 并执行一个操作
     *
     * @param tenantId  机构id
     * @param clazz     setId的类
     * @param setOption set构造函数
     * @param action    需要执行的操作
     */
    private <T> void setIdByApply(Integer tenantId, T clazz, Consumer<Integer> setOption, Consumer<T> action) {
        setOption.accept(tenantId);
        action.accept(clazz);
    }

    /**
     * 给机构各关联配置赋值
     *
     * @param source 原对象
     * @param target 转换的对象
     * @param action 执行动作
     */
    private <T> void setTenantInfo(Object source, T target, Consumer<T> action) {
        if (Objects.nonNull(source)) {
            BeanUtils.copyProperties(source, target);
            action.accept(target);
        }
    }

    private Integer getUserId() {
        //修改机构基础信息
        return Optional.ofNullable(getUser())
                .map(SysUser::getId)
                .orElseThrow(() -> new BizException("用户信息获取失败,请刷新页面或者重新登录!"));
    }

    private SysUser getUser() {
        //修改机构基础信息
        return Optional.ofNullable(sysUserFeignService.queryUserInfo())
                .orElseThrow(() -> new BizException("用户信息获取失败,请刷新页面或者重新登录!"));
    }

    private void checkTsign(String code) {
        //校验营业信息
        SysUserTsign sysUserTsign = sysUserTsignService.queryByCardNo(code);
        if (Objects.nonNull(sysUserTsign)) {
            throw new BizException("营业执照编号已被注册!");
        }
    }


    /**
     * 机构开通需修机构相关的改付款时间和状态
     *
     * @param tenantId 机构id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void tenantOpenSuccess(Integer tenantId) {
        //修改机构状态
        TenantInfo tenantInfo = new TenantInfo();
        tenantInfo.setId(tenantId);
        tenantInfo.setPayState(1);
        baseMapper.updateById(tenantInfo);

        Date now = new Date();
        //修改机构正在使用的产品的付款时间
        TenantProductInfo productInfo = new TenantProductInfo();
        productInfo.setPayDate(now);
        productInfo.setTenantId(tenantId);
        tenantProductInfoService.updateByTenantId(productInfo);
        //一个机构只能创建一个开通协议
        TenantContractRecord openRecord = tenantContractRecordService.getOne(new QueryWrapper<>(new TenantContractRecord())
                .eq("tenant_id_", tenantId)
                .eq("type_", TenantContractRecordEnum.OPEN.getType()));
        if (Objects.nonNull(openRecord)) {
            return;
        }
        //生成协议并上传得到地址
        TenantContractRecordEnum open = TenantContractRecordEnum.OPEN;
        String contractPath = getContractAndUpLoad(tenantId, open, null);
        if (StringUtils.isBlank(contractPath)) {
            return;
        }
        //写入协议记录
        tenantContractRecordService.insertContractRecord(tenantId, contractPath, open);
    }

    @Override
    public Map<String, Object> recharge(Integer tenantId, BigDecimal amount) throws Exception {
        SysConfig config = sysConfigService.findByParamName("platform_collection_organ");
        if (Objects.isNull(config)) {
            throw new BizException("未查询到机构收款分部信息！");
        }
        log.error("机构 " + tenantId.toString() + "充值");
        Optional.of(tenantId)
                .map(baseMapper::selectById)
                .orElseThrow(() -> new BizException("充值时,未查询到机构信息!"));
        String key = "Tenant_Pay:" + tenantId;
        RBucket<Object> bucket = redissonClient.getBucket(key);
        //原子操作 抢锁成功为true
        if (!bucket.trySet(tenantId, 10, TimeUnit.SECONDS)) {
            throw new BizException("正在缴费中请稍后!");
        }
        //生成订单编号
        String orderNo = idGenerator.generatorId("payment") + "";
        TenantOrderRecordEnum tenantEnum = TenantOrderRecordEnum.TENANT_RECHARGE;
        Map<String, Object> result = new HashMap<>();
        int orderState = 0;

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        
        //消费大于0元则拉起支付
        if (amount.compareTo(BigDecimal.ZERO) > 0) {
            result = payService.getPayToPlatformMap(tenantId,
                    PaymentChannelEnum.ADAPAY,
                    amount,
                    BigDecimal.ZERO,
                    orderNo,
                    baseApiUrl + "/api-web/tenantInfo/callback",
                    baseApiUrl + "/api-web/tenantInfo/paymentResult?orderNo=" + orderNo,
                    tenantEnum.getMsg(),
                    tenantEnum.getMsg()
            );
        } else {
            orderState = 1;
        }
        result.put("amount", amount);
        result.put("orderNo", orderNo);
        TenantOrderRecord orderRecord = createOrderRecord(tenantId, amount, orderNo, tenantEnum, orderState, WrapperUtil.toStr(result, "routingMerNos"));
        if (orderState == 1) {
            //0元支付成功后直接成功
            tenantOrderRecordService.rechargeSuccess(orderRecord);
        }
        log.info("tenant pay >>>>> {} ", result);
        return result;
    }

    @Override
    public Boolean rechargeCheck(String orderNo) throws Exception {
        TenantOrderRecord record = tenantOrderRecordService.getOne(new QueryWrapper<TenantOrderRecord>().eq("order_no_", orderNo));
        if (record.getId() == null) {
            throw new Exception("订单未找到");
        }
        return record.getOrderState() == 1;
    }

    @Override
    public TenantProductSumm queryTenantInfoProductSumm(Integer tenantId) {
        TenantProductSumm tps = baseMapper.queryTenantInfoProductSumm(tenantId);

        if (tps.getExpiryDateEnd() == null) {
            tps.setValidRemaining(0);
        } else {
            Date now = LocalDate.now().toDate();//不能直接new Date()的，不要用带时分秒的时间和不带时分秒的时间做对比！！！
            tps.setValidRemaining(DateUtil.daysBetween(now, tps.getExpiryDateEnd()));
        }
        tps.setSumBalance(tps.getBalance().add(tps.getFrozenAmount()));

        Map<String, Object> params = new HashMap<>();
        params.put("tenantId", tenantId);
        int studentCount = studentService.findCount(params);
        tps.setStudentNum(studentCount);
        return tps;
    }

    @Override
    public void saveTenantContextHolder(HttpServletRequest request) {

        final String TENANT_ID = "tenantId";
        String tenantId = request.getParameter(TENANT_ID);

        if (StringUtils.isBlank(tenantId)) {
            tenantId = request.getHeader(TENANT_ID);
        }

        if (StringUtils.isBlank(tenantId)) {
            SysUser sysUser = sysUserFeignService.queryUserInfo();
            if (sysUser != null && sysUser.getTenantId() != null) {
                tenantId = sysUser.getTenantId().toString();
            }
        }
        if (StringUtils.isNotBlank(tenantId)) {
            TenantContextHolder.setTenantId(Integer.parseInt(tenantId));
            if (Integer.parseInt(tenantId) != -1) {
                log.info("==> " + request.getRequestURL());
                TenantInfo tenantInfo = this.baseMapper.getOpenTenant(Integer.parseInt(tenantId));
                if (tenantInfo != null) {
                    request.setAttribute("datasourceId", tenantInfo.getDataSource());
                } else {
                    throw new BizException("机构服务已停用，请联系机构管理员");
                }
            }
        }
    }

    @Override
    public void updatePhone(String newPhone, String oldPhone) {
        baseMapper.updatePhone(newPhone, oldPhone);
    }


    /**
     * 监测机构状态
     */
    @Override
    public void checkTenantState() {
        //查询还差30天和1天过期的机构，并发送邮件和短信
        Integer[] days = {30, 1};
        Arrays.stream(days).forEach(this::checkAndSend);

        Date now = LocalDate.now().toDate();
        //查询过期并且没有停用的机构，将其停用
        Date maturity = DateUtils.addDays(now, -1);
        Map<String, Object> par = new HashMap<>();
        par.put("expiryDate", maturity);
        par.put("state", 1);
        List<TenantInfoPageVo> maturityTenant = baseMapper.queryPage(par);
        maturityTenant.forEach(t -> {
            TenantInfo tenantInfo = baseMapper.selectById(t.getId());
            tenantInfo.setState(2);
            tenantInfo.setUpdatedBy(-1);
            tenantInfo.setUpdatedTime(now);
            this.updateById(tenantInfo);
            log.info("checkTenantState stop >>>> tenantInfo {}", tenantInfo);
        });
    }

    private void checkAndSend(int i) {
        Date now = LocalDate.now().toDate();
        Date expiryDate = DateUtils.addDays(now, i);
        Map<String, Object> par = new HashMap<>();
        par.put("state", 1);
        par.put("expiryDate", expiryDate);
        List<TenantInfoPageVo> tenantList = baseMapper.queryPage(par);
        expiringSend(tenantList, DateUtils.formatDate(expiryDate, "yyyy年MM月dd日"), i);
    }

    //向即将到期的机构发送信息
    private void expiringSend(List<TenantInfoPageVo> infoList, String dateStr, int i) {
        infoList.forEach(t -> {
            //邮件
            if (StringUtils.isNotBlank(t.getEmail())) {
                //机构名称 服务名称 到期时间 学员数量
                Object[] emailMsg = {t.getName(), t.getServeName(), dateStr, t.getStudentUpLimit(), i};
                Map<Integer, String> sendPar = new HashMap<>();
                sendPar.put(t.getUserId(), t.getEmail());
                log.info("platformSendToAll>>> 即将到期 receiveUserId {} email {} objs {} sendPar {}", t.getUserId(), t.getEmail(), emailMsg, sendPar);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.EMAIL,
                        EMAIL_TENANT_EXPIRATION_REMINDERS,
                        sendPar, null, 0, null,
                        "SYSTEM", emailMsg);
            }
            //短信
            if (StringUtils.isNotBlank(t.getPhone())) {
                //机构名称 到期时间
                Object[] phoneMsg = {t.getName(), dateStr};
                Map<Integer, String> sendPar2 = new HashMap<>();
                sendPar2.put(t.getUserId(), t.getPhone());
                log.info("platformSendToAll>>> 即将到期 receiveUserId {} phone {} objs {} sendPar {}", t.getUserId(), t.getPhone(), phoneMsg, sendPar2);
                sysMessageService.batchSendMessage(-1, MessageSenderPluginContext.MessageSender.AWSMS,
                        SMS_TENANT_EXPIRATION_REMINDERS,
                        sendPar2, null, 0, null,
                        "SYSTEM", phoneMsg);
            }
        });
    }

    @Override
    public TenantInfo queryTenantInfoByOrgan(Integer organId) {
        return baseMapper.queryTenantInfoByOrgan(organId);
    }

}