package com.ktg.iscs.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ktg.common.core.domain.entity.SysUser;
import com.ktg.common.core.redis.RedisCache;
import com.ktg.common.core.text.Convert;
import com.ktg.common.utils.DateUtils;
import com.ktg.common.utils.bean.BeanUtils;
import com.ktg.iscs.domain.*;
import com.ktg.iscs.domain.dto.materialsLoan.AddLoanDTO;
import com.ktg.iscs.domain.dto.materialsLoan.MaterialsLoanPageDTO;
import com.ktg.iscs.domain.dto.materialsLoan.ReminderPageDTO;
import com.ktg.iscs.domain.dto.materialsLoan.ReturnLoanDTO;
import com.ktg.iscs.domain.vo.materialsLoan.MaterialsLoanPageVO;
import com.ktg.iscs.domain.vo.materialsLoan.ReminderPageVO;
import com.ktg.iscs.domain.vo.materialsType.MaterialsTypePageVO;
import com.ktg.iscs.mapper.IsMaterialsLoanMapper;
import com.ktg.iscs.service.*;
import com.ktg.system.service.ISysUserService;
import com.ktg.system.service.NotifySendService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 物资借出Service业务层处理
 *
 * @author cgj
 * @date 2024-11-08
 */
@Service
public class IsMaterialsLoanServiceImpl extends ServiceImpl<IsMaterialsLoanMapper, IsMaterialsLoan> implements IIsMaterialsLoanService {
    @Autowired
    private IsMaterialsLoanMapper isMaterialsLoanMapper;
    @Autowired
    private IIsMaterialsService isMaterialsService;
    @Autowired
    private IIsMaterialsTypeService isMaterialsTypeService;
    @Resource
    private NotifySendService notifySendService;
    @Autowired
    private IIsMaterialsReminderService iIsMaterialsReminderService;
    @Autowired
    private IIsMailTemplateService iIsMailTemplateService;
    @Autowired
    private IIsMailNotifyConfigService isMailNotifyConfigService;
    @Autowired
    private IIsMailSendTaskItemService iIsMailSendTaskItemService;
    @Autowired
    private IIsMaterialsCabinetService iIsMaterialsCabinetService;
    @Autowired
    private IIsMaterialsLoanExceptionService isMaterialsLoanExceptionService;
    @Resource
    private ISysUserService iSysUserService;
    @Resource
    private IIsSystemAttributeService isSystemAttributeService;
    @Autowired
    private RedisCache redisCache;
    // @Value("${mail.reminder}")
    // private String reminder;
    // @Value("${mail.alarm}")
    // private String alarm;
    private final ConcurrentHashMap<String, Long> messages = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);


    private static final String MATERIALS_LOAN = "materials_loan:";
    private static final String REMINDER = "reminder";

    private static final String ALARM = "alarm";


    /*@PostConstruct
    public void init() {
        // 在这里编写你的初始化代码
        System.out.println("项目启动时执行一次的方法");
        // 取出
        List<String> keys = new ArrayList<>(redisCache.keys("materials_loan:*"));
        for (String key : keys) {
            // 获取基本信息
            Long materialsLoanId = redisCache.getCacheObject(key);
            // 获取过期时间
            Long delayInSeconds = redisCache.getTtl(key);
            if (key.contains(REMINDER)) {
                // 如果是提醒
                reminderMessage(materialsLoanId, delayInSeconds);
            } else {
                // 如果是告警
                alarmMessage(materialsLoanId, delayInSeconds);
            }
        }
    }*/

    public void reminderMessage(long materialsLoanId, long delayInSeconds) {
        String messageId = REMINDER + materialsLoanId;
        messages.put(messageId, materialsLoanId);
        scheduler.schedule(() -> {
            sendReminderMessage(messageId);
            messages.remove(messageId);
        }, delayInSeconds, TimeUnit.SECONDS);
        // 开始缓存，避免重启项目无法持久化
        redisCache.setCacheObject(MATERIALS_LOAN + messageId, materialsLoanId, (int) delayInSeconds, TimeUnit.SECONDS);
    }

    /**
     * 发送归还提醒
     *
     * @param messageId
     */
    @Transactional
    public void sendReminderMessage(String messageId) {
        Long materialsLoanId = messages.get(messageId);
        if (materialsLoanId != null) {
            // 发送消息的逻辑
            System.out.println("开始发送消息提醒，借出数据id: " + materialsLoanId);
            // 1.开始查看借出数据
            IsMaterialsLoan materialsLoan = getById(materialsLoanId);
            if (materialsLoan != null) {
                // 1.1数据存在开始判断消息是否需要发送
                // if (materialsLoan.getRestitutionUserId() == null
                //         && materialsLoan.getActualRestitutionTime() == null
                //         && materialsLoan.getRestitutionToId() == null) {
                if (!"1".equals(materialsLoan.getStatus())) {
                    // 没有归还则需要开始新增提醒消息
                    IsMaterials materials = isMaterialsService.getById(materialsLoan.getMaterialsId());
                    Map<String, Object> templateParams = new HashMap<>();
                    templateParams.put("借出时间", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(materialsLoan.getLoanTime()));
                    templateParams.put("物资", materials.getMaterialsName());
                    templateParams.put("归还时间", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(materialsLoan.getRestitutionTime()));
                    notifySendService.sendSingleNotifyToMember(materialsLoan.getLoanUserId(),
                            "REMINDER_RETURN", templateParams);

                    // 存储到is_materials_reminder
                    IsMaterialsReminder isMaterialsReminder = new IsMaterialsReminder();
                    isMaterialsReminder.setMaterialsLoanId(materialsLoanId);
                    isMaterialsReminder.setReminderType(0);
                    isMaterialsReminder.setReadFlag(0);
                    iIsMaterialsReminderService.save(isMaterialsReminder);
                }
            }
        }
    }

    public void alarmMessage(long materialsLoanId, long delayInSeconds) {
        String messageId = ALARM + materialsLoanId;
        messages.put(messageId, materialsLoanId);
        scheduler.schedule(() -> {
            sendAlarmMessage(messageId);
            messages.remove(messageId);
        }, delayInSeconds, TimeUnit.SECONDS);
        // 开始缓存，避免重启项目无法持久化
        redisCache.setCacheObject(MATERIALS_LOAN + messageId, materialsLoanId, (int) delayInSeconds, TimeUnit.SECONDS);
    }

    /**
     * 发送归还报警
     *
     * @param messageId
     */
    @Transactional
    public void sendAlarmMessage(String messageId) {
        Long materialsLoanId = messages.get(messageId);
        if (materialsLoanId != null) {
            // 发送消息的逻辑
            System.out.println("开始发送消息告警，借出数据id: " + materialsLoanId);
            // 1.开始查看借出数据
            IsMaterialsLoan materialsLoan = getById(materialsLoanId);
            if (materialsLoan != null) {
                // 1.1数据存在开始判断消息是否需要发送
                // if (materialsLoan.getRestitutionUserId() == null
                //         && materialsLoan.getActualRestitutionTime() == null
                //         && materialsLoan.getRestitutionToId() == null) {

                if (!"1".equals(materialsLoan.getStatus())) {
                    // 没有归还则需要开始新增提醒消息
                    IsMaterials materials = isMaterialsService.getById(materialsLoan.getMaterialsId());
                    Map<String, Object> templateParams = new HashMap<>();
                    templateParams.put("借出时间", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(materialsLoan.getLoanTime()));
                    templateParams.put("物资", materials.getMaterialsName());
                    templateParams.put("归还时间", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(materialsLoan.getRestitutionTime()));
                    notifySendService.sendSingleNotifyToMember(materialsLoan.getLoanUserId(),
                            "ALARM_RETURN", templateParams);

                    // 存储到is_materials_reminder
                    IsMaterialsReminder isMaterialsReminder = new IsMaterialsReminder();
                    isMaterialsReminder.setMaterialsLoanId(materialsLoanId);
                    isMaterialsReminder.setReminderType(1);
                    isMaterialsReminder.setReadFlag(0);
                    iIsMaterialsReminderService.save(isMaterialsReminder);
                    // 更新借出记录为-超时未归还
                    update(Wrappers.<IsMaterialsLoan>lambdaUpdate()
                            .eq(IsMaterialsLoan::getMaterialsLoanId, materialsLoanId)
                            .set(IsMaterialsLoan::getStatus, 2));
                    System.out.println("发送消息告警成功，借出数据id: " + materialsLoanId);
                }
            }
        }
    }


    /**
     * 查询物资借出
     *
     * @param materialsLoanId 物资借出主键
     * @return 物资借出
     */
    @Override
    public IsMaterialsLoan selectIsMaterialsLoanByMaterialsLoanId(Long materialsLoanId) {
        return isMaterialsLoanMapper.selectIsMaterialsLoanByMaterialsLoanId(materialsLoanId);
    }

    /**
     * 查询物资借出列表
     *
     * @param isMaterialsLoan 物资借出
     * @return 物资借出
     */
    @Override
    public List<IsMaterialsLoan> selectIsMaterialsLoanList(IsMaterialsLoan isMaterialsLoan) {
        return isMaterialsLoanMapper.selectIsMaterialsLoanList(isMaterialsLoan);
    }

    /**
     * 新增物资借出
     *
     * @param dto 物资借出
     * @return 结果
     */
    /*@Transactional
    @Override
    public int insertIsMaterialsLoan(AddLoanDTO dto) {
        Date nowDate = DateUtils.getNowDate();
        IsMaterialsLoan isMaterialsLoan = BeanUtils.toBean(dto, IsMaterialsLoan.class);
        isMaterialsLoan.setLoanTime(nowDate);
        isMaterialsLoan.setCreateTime(nowDate);
        // 存储提醒倒计时和告警倒计时
        int re = 0;
        int al = 0;
        // 1.物资信息
        IsMaterials materials = isMaterialsService.getById(isMaterialsLoan.getMaterialsId());
        MaterialsTypePageVO materialsTypePageVO = new MaterialsTypePageVO();
        // 1.2开始读取类型和规则，然后计算归还时间，提醒时间
        if (materials != null) {
            // 1.2.1填充字段
            isMaterialsLoan.setLoanFromId(materials.getMaterialsCabinetId());
            materialsTypePageVO = isMaterialsTypeService.selectIsMaterialsTypeByMaterialsTypeId(materials.getMaterialsTypeId());
            if (materialsTypePageVO != null) {
                // 1.2.1.1填充字段
                isMaterialsLoan.setRestitutionRequired(materialsTypePageVO.getRestitutionRequired());
                if (materialsTypePageVO.getLoanDuration() != null) {
                    // 1.2.2计算应该归还时间
                    Date restitutionTime = formatDateTime(nowDate, materialsTypePageVO.getLoanDuration());
                    isMaterialsLoan.setRestitutionTime(restitutionTime);
                    if (materialsTypePageVO.getReminderTime() != null && materialsTypePageVO.getReminderTime() > 0) {
                        // 1.2.3计算提醒时间
                        re = materialsTypePageVO.getLoanDuration() - materialsTypePageVO.getReminderTime();
                        Date reminderTime = formatDateTime(nowDate, materialsTypePageVO.getLoanDuration() - materialsTypePageVO.getReminderTime());
                        isMaterialsLoan.setReminderTime(reminderTime);
                    }
                    if (materialsTypePageVO.getTimeoutAlarm() != null && materialsTypePageVO.getTimeoutAlarm() > 0) {
                        // 1.2.4计算告警时间
                        al = materialsTypePageVO.getLoanDuration() + materialsTypePageVO.getTimeoutAlarm();
                        Date timeoutAlarm = formatDateTime(nowDate, materialsTypePageVO.getLoanDuration() + materialsTypePageVO.getTimeoutAlarm());
                        isMaterialsLoan.setTimeoutAlarm(timeoutAlarm);
                    }
                }
            }
        }
        int i = isMaterialsLoanMapper.insertIsMaterialsLoan(isMaterialsLoan);
        if (materialsTypePageVO != null && materialsTypePageVO.getRestitutionRequired().equals(1) ) {
            // 开始处理消息
            // 1.3.1处理归还提醒
            if (materials != null && isMaterialsLoan.getReminderTime() != null) {
                System.out.println("设置借用提醒消息");
                reminderMessage(isMaterialsLoan.getMaterialsLoanId(), re);
            }
            // 1.3.2处理归还告警
            if (materials != null && isMaterialsLoan.getTimeoutAlarm() != null) {
                System.out.println("设置借用告警消息");
                alarmMessage(isMaterialsLoan.getMaterialsLoanId(), al);
            }
        }

        // 2.开始更新物资信息为借出状态
        LambdaUpdateWrapper<IsMaterials> set = Wrappers.<IsMaterials>lambdaUpdate()
                .eq(IsMaterials::getMaterialsId, isMaterialsLoan.getMaterialsId())
                .set(IsMaterials::getLoanState, 0);
        if (materials != null && materials.getAvailableTimes() != null) {
            set.set(IsMaterials::getLoanState, materials.getAvailableTimes() - 1);
        }
        return i;
    }*/
    @Transactional
    @Override
    public int insertIsMaterialsLoan(AddLoanDTO dto) throws Exception {
        try {
            // 检测如果已经有一条借出，则提示异常
            /*List<IsMaterialsLoan> loanList = list(Wrappers.<IsMaterialsLoan>lambdaQuery()
                    .eq(IsMaterialsLoan::getMaterialsId, dto.getMaterialsId())
                    .ne(IsMaterialsLoan::getStatus, "1"));*/
            // Assert.isTrue(loanList.isEmpty(), "该物资存在多条借出记录！");
            Date nowDate = DateUtils.getNowDate();
            IsMaterialsLoan isMaterialsLoan = BeanUtils.toBean(dto, IsMaterialsLoan.class);
            isMaterialsLoan.setLoanTime(nowDate);
            // 存储提醒倒计时和告警倒计时
            Date re = null;
            Date al = null;
            // 1.物资信息
            IsMaterials materials = isMaterialsService.getById(isMaterialsLoan.getMaterialsId());
            MaterialsTypePageVO materialsTypePageVO = new MaterialsTypePageVO();
            // 1.1处理更新错放异常
            isMaterialsLoanExceptionService.update(Wrappers.<IsMaterialsLoanException>lambdaUpdate()
                    .eq(IsMaterialsLoanException::getMaterialsRfid, dto.getMaterialsRfid())
                    .set(IsMaterialsLoanException::getStatus, "1")
                    .set(IsMaterialsLoanException::getHandleTime, nowDate));
            // 1.1.1同时处理物资的借出状态
            isMaterialsService.update(Wrappers.<IsMaterials>lambdaUpdate()
                    .eq(IsMaterials::getMaterialsId, dto.getMaterialsId())
                    .eq(IsMaterials::getStatus, "3")
                    .set(IsMaterials::getStatus, "0"));
            // 如果没有绑定柜子，直接结束，因为不用生成借出记录
            if (materials.getMaterialsCabinetId() == null || materials.getMaterialsCabinetId() == 0) {
                return 1;
            }

            // 1.2借用到期规则
            IsMailNotifyConfig reminderConf = isMailNotifyConfigService.getOne(Wrappers.<IsMailNotifyConfig>lambdaQuery()
                    .eq(IsMailNotifyConfig::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_expiration_reminder").getSysAttrValue()));
            IsMailTemplate reminderTemp = iIsMailTemplateService.getOne(Wrappers.<IsMailTemplate>lambdaQuery()
                    .eq(IsMailTemplate::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_expiration_reminder").getSysAttrValue()));
            // 1.3逾期时间规则
            IsMailNotifyConfig alarmConf = isMailNotifyConfigService.getOne(Wrappers.<IsMailNotifyConfig>lambdaQuery()
                    .eq(IsMailNotifyConfig::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_overdue_alarm").getSysAttrValue()));
            IsMailTemplate alarmTemp = iIsMailTemplateService.getOne(Wrappers.<IsMailTemplate>lambdaQuery()
                    .eq(IsMailTemplate::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_overdue_alarm").getSysAttrValue()));
            // 1.4开始读取类型和规则，然后计算归还时间，提醒时间
            if (materials != null) {
                // 1.4.1填充字段
                materialsTypePageVO = isMaterialsTypeService.selectIsMaterialsTypeByMaterialsTypeId(materials.getMaterialsTypeId());
                isMaterialsLoan.setLoanFromId(materials.getMaterialsCabinetId());
                if (materialsTypePageVO != null) {
                    // 1.5填充字段
                    isMaterialsLoan.setRestitutionRequired(materialsTypePageVO.getRestitutionRequired());
                    // 1.6如果需要归还，则开始计算时间
                    if (materialsTypePageVO.getRestitutionRequired() != null && materialsTypePageVO.getRestitutionRequired().equals(1)) {
                        // 1.6.1如果借用到期有数据，则开始计算
                        if (reminderTemp != null
                                && reminderConf != null
                                && "1".equals(reminderConf.getStatus())
                                && reminderConf.getReminderTime() != null
                                && reminderConf.getReminderTime() > 0) {
                            // 计算提醒时间
                            re = DateUtils.getNewDateTime(nowDate, reminderConf.getReminderTime());
                            isMaterialsLoan.setReminderTime(re);
                            isMaterialsLoan.setRestitutionTime(re);
                        }
                        // 1.6.2逾期时间规则
                        if (alarmTemp != null
                                && alarmConf != null
                                && "1".equals(alarmConf.getStatus())
                                && alarmConf.getReminderTime() != null
                                && alarmConf.getReminderTime() > 0) {
                            // 计算告警时间
                            al = DateUtils.getNewDateTime(nowDate, alarmConf.getReminderTime());
                            isMaterialsLoan.setTimeoutAlarm(al);
                        }
                    }
                }
            }
            int i = isMaterialsLoanMapper.insertIsMaterialsLoan(isMaterialsLoan);
            // 2.邮件的发送
            if (materialsTypePageVO != null && materialsTypePageVO.getRestitutionRequired().equals(1) && materials != null) {
                // 2.1获取收件人
                SysUser user = iSysUserService.getById(isMaterialsLoan.getLoanUserId());
                Assert.isFalse(user == null, "当前用户信息不存在!");
                IsMaterialsCabinet cabinet = iIsMaterialsCabinetService.getById(materials.getMaterialsCabinetId());
                // 2.2整理可配置的字段
                // [收件人],[物资名称],[借用时间],[到期时间],[告警时间],[归还地址],[邮件发送日期]
                HashMap<String, String> map = new HashMap<>();
                map.put("[收件人]", user.getNickName());
                map.put("[物资名称]", materials.getMaterialsName());
                map.put("[借用时间]", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, nowDate));
                map.put("[到期时间]", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, re));
                map.put("[告警时间]", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, al));
                map.put("[归还地址]", cabinet != null ? cabinet.getCabinetName() : "");
                map.put("[发件人]", isSystemAttributeService.getIsSystemAttributeByKey("info.mail.name").getSysAttrValue());
                map.put("[公司名称]", isSystemAttributeService.getIsSystemAttributeByKey("info.mail.company").getSysAttrValue());
                // 2.3处理归还提醒
                if (isMaterialsLoan.getReminderTime() != null && reminderConf != null && "1".equals(reminderConf.getStatus())) {
                    System.out.println("设置借用提醒邮件");
                    // 邮件实体
                    IsMailSendTaskItem taskItem = new IsMailSendTaskItem();
                    taskItem.setSendToUserId(dto.getLoanUserId());
                    taskItem.setEmailTemplateId(reminderTemp.getTemplateId());
                    taskItem.setScheduledSendTime(re);
                    taskItem.setBelongId(isMaterialsLoan.getMaterialsLoanId());
                    taskItem.setSendStatus(0);
                    taskItem.setIsCancelled(0);
                    iIsMailSendTaskItemService.insertIsMailSendTaskItem(taskItem, map);
                }
                // 2.4处理归还告警
                if (isMaterialsLoan.getTimeoutAlarm() != null && alarmConf != null && "1".equals(alarmConf.getStatus())) {
                    System.out.println("设置借用告警邮件");
                    // 邮件实体
                    IsMailSendTaskItem taskItem = new IsMailSendTaskItem();
                    taskItem.setSendToUserId(dto.getLoanUserId());
                    taskItem.setEmailTemplateId(alarmTemp.getTemplateId());
                    taskItem.setScheduledSendTime(al);
                    taskItem.setBelongId(isMaterialsLoan.getMaterialsLoanId());
                    taskItem.setSendStatus(0);
                    taskItem.setIsCancelled(0);
                    iIsMailSendTaskItemService.insertIsMailSendTaskItem(taskItem, map);
                }
            }

            // 2.开始更新物资信息为借出状态
            LambdaUpdateWrapper<IsMaterials> set = Wrappers.<IsMaterials>lambdaUpdate()
                    .eq(IsMaterials::getMaterialsId, isMaterialsLoan.getMaterialsId())
                    .set(IsMaterials::getLoanState, 0);
            if (materials != null && materials.getAvailableTimes() != null) {
                set.set(IsMaterials::getLoanState, materials.getAvailableTimes() - 1);
            }
        } catch (Exception e) {
            throw new Exception(e);
        }
        return 1;
    }

    // 格式化日期时间的方法
    private static Date formatDateTime(Date currentDate, Integer secondsToAdd) {
        // 获取当前时间
        System.out.println("当前时间: " + currentDate);
        // 将Date转换为Instant
        Instant instant = currentDate.toInstant();
        // 根据设定的秒数增加时间
        Instant newInstant = instant.plusSeconds(secondsToAdd);
        // 将Instant转换回Date
        Date newDate = Date.from(newInstant);
        System.out.println("增加时间后: " + newDate);
        return newDate;
    }

    /**
     * 修改物资借出
     *
     * @param isMaterialsLoan 物资借出
     * @return 结果
     */
    @Override
    public int updateIsMaterialsLoan(IsMaterialsLoan isMaterialsLoan) {
        isMaterialsLoan.setUpdateTime(DateUtils.getNowDate());
        return isMaterialsLoanMapper.updateIsMaterialsLoan(isMaterialsLoan);
    }

    /**
     * 批量删除物资借出
     *
     * @param materialsLoanIds 需要删除的物资借出主键
     * @return 结果
     */
    @Override
    public int deleteIsMaterialsLoanByMaterialsLoanIds(String materialsLoanIds) {
        Assert.notBlank(materialsLoanIds, "请选择需要删除的数据!");
        Long[] longIds = Convert.toLongArray(materialsLoanIds);
        return isMaterialsLoanMapper.deleteIsMaterialsLoanByMaterialsLoanIds(longIds);
    }

    /**
     * 删除物资借出信息
     *
     * @param materialsLoanId 物资借出主键
     * @return 结果
     */
    @Override
    public int deleteIsMaterialsLoanByMaterialsLoanId(Long materialsLoanId) {
        return isMaterialsLoanMapper.deleteIsMaterialsLoanByMaterialsLoanId(materialsLoanId);
    }

    @Override
    public Page<MaterialsLoanPageVO> getIsMaterialsLoanPage(Page<IsMaterialsLoan> page, MaterialsLoanPageDTO dto) {
        return isMaterialsLoanMapper.getIsMaterialsLoanPage(page, dto);
    }

    @Transactional
    @Override
    public Boolean returnIsMaterialsLoan(ReturnLoanDTO dto) {
        // try {
        Assert.notNull(dto.getMaterialsId(), "物资id不能为空！");
        Assert.notNull(dto.getRestitutionUserId(), "归还人id不能为空！");
        Assert.notNull(dto.getRestitutionToId(), "归还柜id不能为空！");
        // 1.查询物资信息
        IsMaterials materials = isMaterialsService.getById(dto.getMaterialsId());
        // 0.检查借出数据
        List<IsMaterialsLoan> loanList = list(Wrappers.<IsMaterialsLoan>lambdaQuery()
                .eq(IsMaterialsLoan::getMaterialsId, dto.getMaterialsId())
                .isNull(IsMaterialsLoan::getRestitutionUserId)
                .isNull(IsMaterialsLoan::getRestitutionToId)
        );
        // Assert.isFalse(loanList.isEmpty(), "尚未找到该物资的借出信息!");
        // 如果没有借取信息 直接存储归还异常
        if (loanList.isEmpty() || materials.getMaterialsCabinetId() == null || materials.getMaterialsCabinetId() == 0) {
            IsMaterialsLoanException loanException = new IsMaterialsLoanException();
            loanException.setMaterialsId(dto.getMaterialsId());
            loanException.setMaterialsRfid(materials.getMaterialsRfid());
            loanException.setRestitutionUserId(dto.getRestitutionUserId());
            loanException.setRestitutionToId(dto.getRestitutionToId());
            loanException.setOccurTime(new Date());
            isMaterialsLoanExceptionService.save(loanException);
            return true;
        }
        // Assert.isFalse(loanList.size() > 1, "该物资的借出信息出现多条!");

        // 1.1 如果不是归还到原先的柜子，则不能更新归还记录，存储归还异常等待处理，并且更新物资状态异常
        if (!materials.getMaterialsCabinetId().equals(dto.getRestitutionToId())) {
            IsMaterialsLoanException loanException = new IsMaterialsLoanException();
            loanException.setMaterialsId(dto.getMaterialsId());
            loanException.setMaterialsLoanId(loanList.get(0).getMaterialsLoanId());
            loanException.setLoanFromId(materials.getMaterialsCabinetId());
            // loanException.setLoanTime(loanList.get(0).getLoanTime());
            loanException.setRestitutionUserId(dto.getRestitutionUserId());
            loanException.setRestitutionToId(dto.getRestitutionToId());
            // loanException.setActualRestitutionTime(new Date());
            loanException.setOccurTime(new Date());
            isMaterialsLoanExceptionService.save(loanException);
            isMaterialsService.update(Wrappers.<IsMaterials>lambdaUpdate()
                    .eq(IsMaterials::getMaterialsId, materials.getMaterialsId())
                    .set(IsMaterials::getLoanState, "0")
                    .set(IsMaterials::getStatus, "3"));
            return true;
        }
        // 2.开始更新loan表数据
        boolean update = update(Wrappers.<IsMaterialsLoan>lambdaUpdate()
                // .eq(IsMaterialsLoan::getMaterialsLoanId, loanList.get(0).getMaterialsLoanId())
                .eq(IsMaterialsLoan::getMaterialsId, dto.getMaterialsId())
                .set(IsMaterialsLoan::getRestitutionUserId, dto.getRestitutionUserId())
                .set(IsMaterialsLoan::getRestitutionToId, dto.getRestitutionToId())
                .set(IsMaterialsLoan::getStatus, 1)
                .set(IsMaterialsLoan::getActualRestitutionTime, new Date()));
        // 3.开始更新物资信息
        /*isMaterialsService.update(Wrappers.<IsMaterials>lambdaUpdate()
                .eq(IsMaterials::getMaterialsId, dto.getMaterialsId())
                .set(IsMaterials::getLoanState, 1));*/
        // 3.1 检查当前物资是否有未处理的错换柜子异常，如果有，现在已经还对了，默认处理掉异常信息
        List<IsMaterialsLoanException> exList = isMaterialsLoanExceptionService.list(Wrappers.<IsMaterialsLoanException>lambdaQuery()
                .eq(IsMaterialsLoanException::getMaterialsId, dto.getMaterialsId())
                .eq(IsMaterialsLoanException::getStatus, "0"));
        if (!exList.isEmpty()) {
            isMaterialsLoanExceptionService.update(Wrappers.<IsMaterialsLoanException>lambdaUpdate()
                    .eq(IsMaterialsLoanException::getMaterialsId, dto.getMaterialsId())
                    .eq(IsMaterialsLoanException::getStatus, "0")
                    .set(IsMaterialsLoanException::getStatus, "1")
            );
        }
        // 5.检查邮件里面的数据发送了没有，没有发送就取消掉
        // 6.借用到期模板
        IsMailTemplate reminderTemp = iIsMailTemplateService.getOne(Wrappers.<IsMailTemplate>lambdaQuery()
                .eq(IsMailTemplate::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_expiration_reminder").getSysAttrValue()));
        // 6.1取消未发送的邮件的发送
        IsMailTemplate alarmTemp = iIsMailTemplateService.getOne(Wrappers.<IsMailTemplate>lambdaQuery()
                .eq(IsMailTemplate::getTemplateCode, isSystemAttributeService.getIsSystemAttributeByKey("sys.template.material_overdue_alarm").getSysAttrValue()));
        if (reminderTemp != null) {
            iIsMailSendTaskItemService.update(Wrappers.<IsMailSendTaskItem>lambdaUpdate()
                    .eq(IsMailSendTaskItem::getBelongId, loanList.get(0).getMaterialsLoanId())
                    .eq(IsMailSendTaskItem::getEmailTemplateId, reminderTemp.getTemplateId())
                    .eq(IsMailSendTaskItem::getSendStatus, "0")
                    .set(IsMailSendTaskItem::getIsCancelled, "1")
                    .set(IsMailSendTaskItem::getCancelTime, new Date()));
        }
        if (alarmTemp != null) {
            iIsMailSendTaskItemService.update(Wrappers.<IsMailSendTaskItem>lambdaUpdate()
                    .eq(IsMailSendTaskItem::getBelongId, loanList.get(0).getMaterialsLoanId())
                    .eq(IsMailSendTaskItem::getEmailTemplateId, alarmTemp.getTemplateId())
                    .eq(IsMailSendTaskItem::getSendStatus, "0")
                    .set(IsMailSendTaskItem::getIsCancelled, "1")
                    .set(IsMailSendTaskItem::getCancelTime, new Date()));
        }
        // } catch (Exception e) {
        //     throw new Exception(e);
        // }
        return true;
    }

    @Override
    public Page<ReminderPageVO> getReturnReminderPage(Page<IsMaterialsLoan> page, ReminderPageDTO dto) {
        return isMaterialsLoanMapper.getReturnReminderPage(page, dto);
    }

    @Override
    public Page<ReminderPageVO> getExpiredReminderPage(Page<IsMaterialsLoan> page, ReminderPageDTO dto) {
        return isMaterialsLoanMapper.getExpiredReminderPage(page, dto);
    }

    @Override
    public List<Long> getLastLoanRecord(List<Long> materialsIdList) {
        return isMaterialsLoanMapper.getLastLoanRecord(materialsIdList);
    }

}
