package com.ruoyi.ss.device.service.impl;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.enums.LoginType;
import com.ruoyi.common.enums.UserType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.iot.domain.IotDeviceInfo;
import com.ruoyi.iot.domain.response.CommandResponse;
import com.ruoyi.iot.enums.IotHttpStatus;
import com.ruoyi.iot.service.IotService;
import com.ruoyi.ss.device.domain.Device;
import com.ruoyi.ss.device.domain.DeviceBO;
import com.ruoyi.ss.device.domain.DeviceCountVO;
import com.ruoyi.ss.device.domain.DeviceQuery;
import com.ruoyi.ss.device.domain.dto.DeviceBatchUpdateModelDTO;
import com.ruoyi.ss.device.domain.dto.DeviceRegisterDTO;
import com.ruoyi.ss.device.domain.dto.DeviceWifiDTO;
import com.ruoyi.ss.device.domain.enums.DeviceOnlineStatus;
import com.ruoyi.ss.device.domain.enums.DevicePowerStatus;
import com.ruoyi.ss.device.domain.enums.DeviceServiceMode;
import com.ruoyi.ss.device.domain.enums.DeviceStatus;
import com.ruoyi.ss.device.domain.vo.DeviceMacSnVO;
import com.ruoyi.ss.device.domain.vo.DeviceVO;
import com.ruoyi.ss.device.mapper.DeviceMapper;
import com.ruoyi.ss.device.service.DeviceAssembler;
import com.ruoyi.ss.device.service.DeviceService;
import com.ruoyi.ss.device.service.DeviceValidator;
import com.ruoyi.ss.deviceBindRecord.domain.enums.BindRecordType;
import com.ruoyi.ss.deviceBindRecord.domain.enums.BindRecordUserType;
import com.ruoyi.ss.deviceBindRecord.service.DeviceBindRecordService;
import com.ruoyi.ss.deviceSuit.service.DeviceSuitConverter;
import com.ruoyi.ss.deviceSuit.service.DeviceSuitService;
import com.ruoyi.ss.model.domain.SmModelVO;
import com.ruoyi.ss.model.domain.enums.ModelTag;
import com.ruoyi.ss.model.service.ModelService;
import com.ruoyi.ss.record.time.domain.RecordTime;
import com.ruoyi.ss.record.time.domain.enums.RecordTimeType;
import com.ruoyi.ss.record.time.service.IRecordTimeService;
import com.ruoyi.ss.record.time.service.RecordTimeConverter;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.service.StoreService;
import com.ruoyi.ss.storeStaff.service.StoreStaffValidator;
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.user.service.ISmUserService;
import com.ruoyi.ss.user.service.UserValidator;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 设备Service业务层处理
 *
 * @author 邱贞招
 * @date 2024-02-21
 */
@Service
@Slf4j
public class DeviceServiceImpl implements DeviceService
{
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IotService iotService;

    @Autowired
    private DeviceBindRecordService smDeviceBindRecordService;

    @Autowired
    private ScheduledExecutorService scheduledExecutorService;

    @Autowired
    private DeviceAssembler deviceAssembler;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private StoreService storeService;

    @Autowired
    private IRecordTimeService recordTimeService;

    @Autowired
    private RecordTimeConverter recordTimeConverter;

    @Autowired
    private ISmUserService userService;

    @Autowired
    private DeviceValidator deviceValidator;

    @Autowired
    private DeviceSuitConverter deviceSuitConverter;

    @Autowired
    private TransactionBillService transactionBillService;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private ModelService modelService;

    @Autowired
    private DeviceSuitService deviceSuitService;

    @Autowired
    private DeviceSuitConverter deviceConverter;

    @Autowired
    private UserValidator userValidator;

    @Autowired
    private StoreStaffValidator storeStaffValidator;

    /**
     * 查询设备
     *
     * @param deviceId 设备主键
     * @return 设备
     */
    @Override
    public DeviceVO selectById(Long deviceId)
    {
        return deviceMapper.selectSmDeviceByDeviceId(deviceId);
    }

    /**
     * 查询设备列表
     *
     * @param smDevice 设备
     * @return 设备
     */
    @Override
    public List<DeviceVO> selectSmDeviceList(DeviceQuery smDevice) {
        return deviceMapper.selectSmDeviceList(smDevice);
    }

    /**
     * 新增设备
     * @param data 设备
     * @return 结果
     */
    @Override
    public int insertSmDevice(Device data) {
        ServiceUtil.assertion(data == null, "参数错误，不允许为空");
        ServiceUtil.assertion(StringUtils.isBlank(data.getDeviceNo()), "参数错误，SN不允许为空");
        ServiceUtil.assertion(StringUtils.isBlank(data.getMac()), "参数错误，MAC不允许为空");

        String key = data.getDeviceNo();
        ServiceUtil.assertion(!redisLock.lock(RedisLockKey.ADD_DEVICE, key), "当前新增设备过于频繁，请稍后再试");
        try {

            SmModelVO model = modelService.selectSmModelByModelId(data.getModelId());
            ServiceUtil.assertion(model == null, "型号不存在");
            ServiceUtil.assertion(StringUtils.isBlank(model.getProductId()), "型号产品ID未配置，请联系管理员处理");

            data.setCreateTime(DateUtils.getNowDate());
            data.setStatus(DeviceStatus.NORMAL.getStatus());
            data.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());

            Integer result = transactionTemplate.execute(status -> {
                // 创建设备
                int insert = deviceMapper.insertSmDevice(data);
                ServiceUtil.assertion(insert != 1, "新增设备失败");

                // 后校验
                DeviceVO vo = this.selectById(data.getDeviceId());
                deviceAssembler.assembleBonusList(vo);
                deviceValidator.afterCheck(vo);

                // 创建OneNet设备1
                int code = iotService.create(data.getMac(), model.getProductId());
                ServiceUtil.assertion(!IotHttpStatus.SUCCESS.equalCode(code) && !IotHttpStatus.DEVICE_EXIST.equalCode(code), "MAC-1注册失败");
                // 创建OneNet设备2
                if (StringUtils.hasText(data.getMac2())) {
                    code = iotService.create(data.getMac2(), model.getProductId());
                    ServiceUtil.assertion(!IotHttpStatus.SUCCESS.equalCode(code) && !IotHttpStatus.DEVICE_EXIST.equalCode(code), "MAC-2注册失败");
                }

                return insert;
            });
            return result == null ? 0 : result;
        } finally {
            redisLock.unlock(RedisLockKey.ADD_DEVICE, key);
        }
    }

    /**
     * 修改设备
     *
     * @param data 设备
     * @return 结果
     */
    @Override
    public int updateSmDevice(DeviceBO data) {

        data.setUpdateTime(DateUtils.getNowDate());

        // 修改数据库
        Integer result = transactionTemplate.execute(status -> {
            int update = deviceMapper.updateSmDevice(data);
            ServiceUtil.assertion(update != 1, "更新设备失败");

            // 后校验
            DeviceVO vo = this.selectById(data.getDeviceId());
            deviceAssembler.assembleBonusList(vo);
            deviceValidator.afterCheck(vo);

            // 更新套餐关联
            if (data.getSuitIds() != null) {
                // 删除设备套餐关联
                deviceSuitService.deleteByDeviceId(data.getDeviceId());
                // 新增关联
                deviceSuitService.batchInsert(deviceSuitConverter.toPo(data));
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    /**
     * 批量删除设备
     *
     * @param deviceIds 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteSmDeviceByDeviceIds(Long[] deviceIds)
    {
        this.validatePreDelete(Arrays.asList(deviceIds));
        return deviceMapper.deleteSmDeviceByDeviceIds(deviceIds);
    }

    /**
     * 真正的删除校验
     */
    private void validatePreDelete(List<Long> deviceIds) {
        ServiceUtil.assertion(CollectionUtils.isEmpty(deviceIds), "设备不能为空");
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        dto.setDeleted(true);
        Map<Long, DeviceVO> deviceMap = selectMap(dto);

        for (Long deviceId : deviceIds) {
            DeviceVO device = deviceMap.get(deviceId);
            ServiceUtil.assertion(device == null, String.format("设备“%s”不存在，请刷新后重试", deviceId));
        }
    }

    /**
     * 逻辑删除校验
     */
    private void validatePreLogicDelete(List<Long> deviceIds) {
        ServiceUtil.assertion(CollectionUtils.isEmpty(deviceIds), "设备不能为空");

        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        dto.setDeleted(false);
        // 前台用户，判断是不是他的设备
        if (LoginType.FRONT.equals(SecurityUtils.getLoginType())) {
            dto.setUserId(SecurityUtils.getUserId());
        }
        Map<Long, DeviceVO> deviceMap = selectMap(dto);

        for (Long deviceId : deviceIds) {
            DeviceVO device = deviceMap.get(deviceId);
            ServiceUtil.assertion(device == null, String.format("设备“%s”不存在，请刷新后重试", deviceId));
        }

    }

    /**
     * 条件查询列表，并转为Map
     */
    private Map<Long, DeviceVO> selectMap(DeviceQuery dto) {
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(DeviceVO::getDeviceId, item -> item));
    }

    @Override
    public int selectCount(DeviceQuery dto) {
        return deviceMapper.selectCount(dto);
    }

    @Override
    public List<DeviceVO> selectSmDeviceByStoreId(Long storeId) {
        if (storeId == null) {
            return Collections.emptyList();
        }
        DeviceQuery dto = new DeviceQuery();
        dto.setStoreId(storeId);
        return deviceMapper.selectSmDeviceList(dto);
    }

    @Override
    public int register(DeviceRegisterDTO dto) {
        if (dto == null) {
            return 0;
        }

        DeviceBO device = deviceConverter.toBOByRegister(dto);
        DeviceVO old = selectByAnyMac(dto.getMac());

        if (old == null) {
            // 若设备MAC不存在，则新增
            return this.insertSmDevice(device);
        } else {
            // 若设备MAC存在，则修改
            device.setDeviceId(old.getDeviceId());
            return this.updateSmDevice(device);
        }
    }

    @Override
    public List<DeviceMacSnVO> selectMacSnList(DeviceQuery query) {
        return deviceMapper.selectMacSnList(query);
    }

    @Override
    public DeviceVO selectOne(DeviceQuery query) {
        return deviceMapper.selectOne(query);
    }

    @Override
    public List<DeviceVO> selectUsingDevice(Long userId) {
        // 查询正在使用中的订单
        List<TransactionBillVO> usingBills = transactionBillService.selectUsingBill(userId);
        List<Long> deviceIds = usingBills.stream().map(TransactionBillVO::getDeviceId).distinct().collect(Collectors.toList());
        // 查询设备列表
        return this.selectByIds(deviceIds);
    }

    /**
     * 开关设备
     */
    @Override
    public int switchDevice(Long deviceId, boolean open, String reason) {
        DeviceVO device = selectById(deviceId);
        return this.switchDevice(device, open ? DevicePowerStatus.ON : DevicePowerStatus.OFF, reason);
    }

    @Override
    public int batchUpdateModel(DeviceBatchUpdateModelDTO dto) {
        return deviceMapper.batchUpdateModel(dto);
    }

    @Override
    public int updateServiceRate(DeviceVO data) {
        return deviceMapper.updateServiceRate(data);
    }

    @Override
    public int renewalRentTime(Long deviceId, int time, TimeUnit timeUnit) {
        long seconds = timeUnit.toSeconds(time);
        return deviceMapper.renewalRentTime(deviceId, seconds);
    }

    @Override
    public void pullDeviceInfo(Long deviceId) {
        pullDeviceInfo(Collections.singletonList(deviceId));
    }

    @Override
    public int resetTime(DeviceVO device, boolean required, String reason) {
        Integer result = transactionTemplate.execute(status -> {
            // 归零时间，将过期时间设置为现在
            int update = this.resetUpdateDbTime(device.getDeviceId());
            ServiceUtil.assertion(update != 1, "修改设备时间失败");

            // 物联网设备归零
            try {
                CommandResponse res = iotService.setTime(device, 0L, reason);
                ServiceUtil.assertion(res == null, "设备归零失败，返回值为空");
                ServiceUtil.assertion(!res.isSuccess(), "设备归零失败，请检查设备是否在线或联系管理员");
            } catch (Exception e) {
                log.warn("设备归零失败:{}, {}, {}", device.getDeviceId(), device.getDeviceNo(), e.getMessage());
                if (required) {
                    throw e;
                }
            }

            return update;
        });

        // 归零记录
        if (result != null && result == 1 ) {
            this.resetRecord(device, RecordTimeType.TIME, reason);
        }

        return result == null ? 0 : result;
    }

    private int resetUpdateDbTime(Long deviceId) {
        Device form = new Device();
        form.setDeviceId(deviceId);
        form.setExpireTime(LocalDateTime.now());
        form.setStatus(DeviceStatus.NORMAL.getStatus());
        return deviceMapper.updateSmDevice(form);
    }

    @Override
    public int resetEle(DeviceVO device, boolean required, String reason) {
        if (device == null) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            // 更新剩余电量
            int update = this.resetUpdateDbEle(device.getDeviceId(), device.getTotalElectriQuantity());
            ServiceUtil.assertion(update != 1, "更新剩余电量失败");

            // 直接设置电量为0
            try {
                CommandResponse res = iotService.setEle(device, BigDecimal.ZERO, reason);
                ServiceUtil.assertion(res == null, "归零电量失败，返回值为空");
                ServiceUtil.assertion(!res.isSuccess(), "归零电量失败，请检查设备是否在线或联系管理员");
            } catch (Exception e) {
                if (required) {
                    throw e;
                }
            }

            return update;
        });

        // 归零记录
        if (result != null && result == 1 ) {
            this.resetRecord(device, RecordTimeType.ELE, reason);
        }

        return result == null ? 0 : result;
    }

    private void resetRecord(DeviceVO device, RecordTimeType type, String reason) {
        // 电量
        if (RecordTimeType.ELE.equals(type)) {
            int amount = device.getSurplusEle().negate().intValue();
            RecordTime record = recordTimeConverter.toRecordTime(device.getDeviceId(), amount, reason, RecordTimeType.ELE.getType());
            scheduledExecutorService.schedule(() -> {
                recordTimeService.insertRecordTime(record);
            }, 0, TimeUnit.SECONDS);
        }
        // 时长
        else if (RecordTimeType.TIME.equals(type)) {
            Duration duration = Duration.between(LocalDateTime.now(), device.getExpireTime());
            long seconds = duration.getSeconds() > 0 ? duration.getSeconds() : 0;
            RecordTime record = recordTimeConverter.toRecordTime(device.getDeviceId(), -seconds, reason, RecordTimeType.TIME.getType());
            scheduledExecutorService.schedule(() -> {
                recordTimeService.insertRecordTime(record);
            }, 0, TimeUnit.SECONDS);
        }
    }

    private int resetUpdateDbEle(Long deviceId, BigDecimal expireEle) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setSurplusEle(BigDecimal.ZERO);
        data.setExpireEle(expireEle);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int resetEle(Long deviceId, boolean required, String reason) {
        // 获取设备信息
        DeviceVO device = selectById(deviceId);
        this.pullDeviceInfo(device);
        return resetEle(device, required, reason);
    }

    @Override
    public boolean addEle(Long deviceId, BigDecimal ele, boolean withIot, String reason) {
        DeviceVO device = selectById(deviceId);
        return this.addEle(device, ele, withIot, reason);
    }

    @Override
    public boolean addEle(DeviceVO device, BigDecimal amount, boolean withIot, String reason) {
        if (device == null) {
            return false;
        }

        Integer result = transactionTemplate.execute(status -> {
            // 更新设备电量
            int update = deviceMapper.addEle(device.getDeviceId(), amount);
            ServiceUtil.assertion(update != 1, "更新电量失败");

            // 记录日志
            RecordTime record = recordTimeConverter.toRecordTime(device.getDeviceId(), amount.longValue(), reason, RecordTimeType.ELE.getType());
            int insertRecord = recordTimeService.insertRecordTime(record);
            ServiceUtil.assertion(insertRecord != 1, "记录电量变化失败");

            if (withIot) {
                // 若设备剩余电量小于0，则补偿电量充值
                CommandResponse res;
                if (device.getSurplusEle().compareTo(BigDecimal.ZERO) < 0) {
                    res = iotService.addEle(device, amount.subtract(device.getSurplusEle()), reason);
                } else {
                    res = iotService.addEle(device, amount, reason);
                }
                boolean iot = res.isSuccess();
                ServiceUtil.assertion(!iot, "为设备充值电量失败");
            }

            return update;
        });

        return result != null && result == 1;
    }

    @Override
    public int updateLastRecoverTime(Long deviceId, Long lastRecoverTime) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setLastRecoverTime(lastRecoverTime);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int setTime(DeviceVO device, LocalDateTime expireTime, boolean withIot, int tryCount, String reason) {
        if (device == null || StringUtils.isBlank(device.getMac()) || expireTime == null) {
            return 0;
        }

        Integer result = transactionTemplate.execute(status -> {
            int update = this.updateExpireTime(device.getDeviceId(), expireTime);
            ServiceUtil.assertion(update != 1, "更新数据失败");

            if (withIot) {
                Duration between = Duration.between(LocalDateTime.now(), expireTime);
                long seconds = between.getSeconds();
                if (seconds > 0) {
                    CommandResponse res = iotService.trySetTime(device, seconds, tryCount, reason);
                    if (!res.isSuccess()) {
                        log.error("操作设备设置时长{}失败：{}", device.getDeviceId(), res.getMsg());
                        throw new ServiceException(res.getMsg());
                    }
                }
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    private int updateExpireTime(Long deviceId, LocalDateTime expireTime) {
        Device data = new Device();
        data.setDeviceId(deviceId);
        data.setExpireTime(expireTime);
        return deviceMapper.updateSmDevice(data);
    }

    @Override
    public int setWifi(DeviceWifiDTO dto) {
        DeviceVO device = selectById(dto.getDeviceId());
        if (device == null) {
            return 0;
        }
        CommandResponse res = iotService.setWifi(device, dto.getWifiName(), dto.getWifiPwd(), dto.getReason());
        ServiceUtil.assertion(!res.isSuccess(), "设置WIFI失败：" + res.getMsg());

        return 1;
    }

    @Override
    public DeviceVO selectByAnyMac(String mac) {
        return deviceMapper.selectByAnyMac(mac);
    }

    @Override
    public void pullDeviceInfo(DeviceVO device) {
        this.pullDeviceInfoList(Collections.singletonList(device));
    }

    @Override
    public int unbindAgent(Long deviceId) {
        DeviceVO device = selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");
        ServiceUtil.assertion(device.getAgentId() == null, "设备没有代理商，无需解绑");

        Integer result = transactionTemplate.execute(status -> {
            // 解绑
            int unbind = deviceMapper.unbindAgent(deviceId);
            ServiceUtil.assertion(unbind != 1, "解绑失败，请刷新后重试");

            // 新增解绑记录
            smDeviceBindRecordService.record(device.getAgentId(), deviceId, BindRecordType.UNBIND, BindRecordUserType.AGENT);

            return unbind;
        });

        return result == null ? 0 : result;
    }

    @Override
    public String bindMch(Long deviceId, Long mchId) {
        DeviceVO device = this.selectById(deviceId);
        return this.bind(null, mchId, device);
    }

    @Override
    public BigDecimal selectMaxAgentServiceRate(DeviceQuery query) {
        query.setServiceMode(DeviceServiceMode.AGENT.getMode());
        return deviceMapper.selectMaxAgentServiceRate(query);
    }

    @Override
    public int bindAgent(Long deviceId, Long agentId, BigDecimal agentServiceRate) {
        if (deviceId == null || agentId == null || agentServiceRate == null) {
            return 0;
        }
        ServiceUtil.assertion(!userValidator.isAgent(agentId), "该用户不是代理商，无法绑定为设备代理商");

        Integer result = transactionTemplate.execute(status -> {
            // 绑定
            int bind = deviceMapper.bindAgent(deviceId, agentId, agentServiceRate);
            ServiceUtil.assertion(bind != 1, "绑定代理商失败，请刷新后重试");

            // 后校验
            DeviceVO vo = this.selectById(deviceId);
            deviceValidator.checkAgent(vo);
            deviceAssembler.assembleBonusList(vo);
            deviceValidator.checkBonus(vo);

            // 添加绑定记录
            int record = smDeviceBindRecordService.record(agentId, deviceId, BindRecordType.BIND, BindRecordUserType.AGENT);
            ServiceUtil.assertion(record != 1, "添加绑定记录失败");

            return bind;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int setEle(DeviceVO device, BigDecimal ele, int tryCount, String reason) {
        if (device == null || ele == null || tryCount <= 0) {
            log.error("设置电量失败：参数为空");
            return 0;
        }
        Integer result = transactionTemplate.execute(status -> {
            int update = this.updateSurplusEle(device.getDeviceId(), ele);
            ServiceUtil.assertion(update != 1, "更新设备数据失败");

            CommandResponse res = iotService.trySetEle(device, ele, tryCount, reason);
            if (res == null || !res.isSuccess()) {
                log.error("操作设备设置电量{}失败：{}", device.getDeviceId(), res);
                ServiceUtil.assertion(res == null, "充值电量失败");
                IotHttpStatus statusResult = IotHttpStatus.convertByCode(res.getCode());
                ServiceUtil.assertion(!res.isSuccess(), "充值电量失败：" + statusResult.getMsg());
            }

            return update;
        });

        return result == null ? 0 : result;
    }

    @Override
    public int setTime(DeviceVO device, long seconds, int tryCount, String reason) {
        return this.setTime(device, LocalDateTime.now().plusSeconds(seconds), true, tryCount, reason);
    }

    @Override
    public List<DeviceVO> selectStaffDeviceList(DeviceQuery query) {
        query.setInStaff(true);
        return deviceMapper.selectSmDeviceList(query);
    }

    private int updateSurplusEle(Long deviceId, BigDecimal ele) {
        if (deviceId == null || ele == null) {
            return 0;
        }
        Device data = new Device();
        data.setSurplusEle(ele);
        data.setDeviceId(deviceId);
        return deviceMapper.updateSmDevice(data);
    }


    @Override
    public boolean addTime(Long deviceId, long seconds, boolean withIot, String reason) {
        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        return addTime(device, seconds, withIot, reason);
    }

    @Override
    public boolean addTime(DeviceVO device, long seconds, boolean withIot, String reason) {
        ServiceUtil.assertion( seconds < 0, "增加的时长不允许小于0");
        ServiceUtil.assertion(device == null || device.getDeviceId() == null, "设备不存在");
        ServiceUtil.assertion(!StringUtils.hasText(device.getMac()), "设备MAC号为空");
        ServiceUtil.assertion(DeviceStatus.FIXING.getStatus().equals(device.getStatus()), "设备正在维修中，无法使用");

        Long deviceId = device.getDeviceId();
        LocalDateTime now = LocalDateTime.now();

        Boolean result = transactionTemplate.execute(status -> {
            // 更新数据库时长
            int updateCount = deviceMapper.addTime(deviceId, seconds);
            ServiceUtil.assertion(updateCount != 1, "增加时长失败，请刷新后重试");

            // 记录下操作日志
            RecordTime record = recordTimeConverter.toRecordTime(deviceId, seconds, reason, RecordTimeType.TIME.getType());
            int insertRecord = recordTimeService.insertRecordTime(record);
            ServiceUtil.assertion(insertRecord != 1, "记录时长变化日志失败");

            // 物联网设备增加时长
            if (withIot) {
                DeviceVO newDevice = selectById(deviceId);
                long betweenSeconds = Duration.between(now, newDevice.getExpireTime()).getSeconds();
                if (betweenSeconds > 0) {
                    CommandResponse rechargeResult = iotService.setTime(device, betweenSeconds, reason);
                    ServiceUtil.assertion(!rechargeResult.isSuccess(), "设备充值失败，请检查设备是否在线");
                }
            }
            return Boolean.TRUE;
        });

        // 是否成功
        boolean success = result != null && result;
        if (success) {
            // 拉取设备信息
            this.pullDeviceInfoAsync(Collections.singletonList(deviceId), 3, TimeUnit.SECONDS);
        }

        return success;
    }


//    private void freshStatus(Long deviceId) {
//        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
//        freshStatus(device);
//    }

    @Override
    public void freshStatus(DeviceVO device) {
        if (device == null || device.getExpireTime() == null) {
            return;
        }
        if (!DeviceStatus.USING.getStatus().equals(device.getStatus())) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        Duration between = Duration.between(now, device.getExpireTime());

        // 如果已过期，则调整设备为正常状态
        int i = deviceMapper.changeTimeoutStatus(device.getDeviceId());
        if (i == 1) {
            return;
        }

//        // 如果没有过期，则延迟至过期时间继续查询
//        if (between.getSeconds() > 0) {
//            scheduledExecutorService.schedule(() -> {
//                freshStatus(device.getDeviceId());
//            }, between.getSeconds() , TimeUnit.SECONDS);
//        }
    }

    /**
     * 设备心跳，用于检测是否在线
     */
    @Override
    public void deviceHeartBeat() {
        log.info("device heart beat running...");
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(new DeviceQuery());
        if (CollectionUtils.isEmpty(list)) {
            log.info("device list is empty");
            return;
        }
        LocalDateTime now = LocalDateTime.now();

        for (DeviceVO device : list) {
            if (StringUtils.hasText(device.getMac())) {
                String status = iotService.getOnlineStatus(device).getStatus();
//                log.info("device: {} {} online status is {}", device.getDeviceId(), device.getMac(), status);
                device.setOnlineStatus(status);
                if (DeviceOnlineStatus.ONLINE.getStatus().equals(status)) {
                    device.setLastOnlineTime(now);
                }
            } else {
                device.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());
                log.info("device {} mac is empty", device.getDeviceId());
            }
        }
        int i = deviceMapper.batchUpdateOnlineStatus(list);
        log.info("update count: {}", i);
    }


    private int changeStatus(Long deviceId, DeviceStatus status) {
        Device device = new Device();
        device.setDeviceId(deviceId);
        device.setStatus(status.getStatus());
        return deviceMapper.updateSmDevice(device);
    }

    @Override
    public int logicDel(List<Long> deviceIds) {
        // 校验
        this.validatePreLogicDelete(deviceIds);

        // 拉取最新数据
        this.pullDeviceInfo(deviceIds);
        // 查询设备列表
        List<DeviceVO> deviceList = this.selectByIds(deviceIds);

        Integer result = transactionTemplate.execute(status -> {
            // 关闭设备订单
            for (Long deviceId : deviceIds) {
                if (deviceId == null) {
                    continue;
                }

                DeviceVO device = deviceList.stream().filter(item -> Objects.equals(item.getDeviceId(), deviceId)).findFirst().orElse(null);
                if (device == null) {
                    continue;
                }

                // 关闭设备未结束的订单
                TransactionBillQuery query = new TransactionBillQuery();
                query.setType(TransactionBillType.RECHARGE.getType());
                query.setStatusList(TransactionBillStatus.canClose());
                query.setDeviceId(deviceId);
                query.setIsFinished(false);
                List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);
                int closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device, null);
                ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
            }

            int i = deviceMapper.logicDel(deviceIds);
            ServiceUtil.assertion(i != deviceIds.size(), "删除设备失败,请刷新后重试");

            return i;
        });

        return result == null ? 0 : result;
    }

    @Override
    public Map<Long, List<DeviceVO>> selectGroupByModelIds(List<Long> modelIds) {
        if (CollectionUtils.isEmpty(modelIds)) {
            return Collections.emptyMap();
        }
        DeviceQuery dto = new DeviceQuery();
        dto.setModelIds(modelIds);
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.groupingBy(DeviceVO::getModelId));
    }

    /**
     * 切换设备通电状态（通/断电）
     *
     * @param device 设备id
     * @param powerStatus 通电状态
     * @param reason
     */
    @Override
    public int switchDevice(DeviceVO device, DevicePowerStatus powerStatus, String reason) {
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");

        Integer result = transactionTemplate.execute(status -> {
            // 修改数据库通断电状态
            Device changePowerForm = new Device();
            changePowerForm.setDeviceId(device.getDeviceId());
            changePowerForm.setPowerStatus(powerStatus.getStatus());
            int update = deviceMapper.updateSmDevice(changePowerForm);

            // 操作设备
            if (update == 1) {
                if (DevicePowerStatus.ON.equals(powerStatus)) {
                    iotService.open(device, reason);
                } else if (DevicePowerStatus.OFF.equals(powerStatus)) {
                    iotService.close(device, reason);
                } else {
                    throw new ServiceException("不支持的操作");
                }
            }

            return update;
        });


        return result == null ? 0 : result;
    }

    /**
     * 拉取设备信息，并保存到数据库
     * @param deviceIds 设备id列表（为空则查询所有）
     */
    @Override
    @SneakyThrows
    public void pullDeviceInfo(List<Long> deviceIds) {
        // 查询设备列表
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(deviceIds);
        List<DeviceVO> list = deviceMapper.selectSmDeviceList(dto);
        this.pullDeviceInfoList(list);
    }

    @Override
    public void pullDeviceInfoList(List<DeviceVO> list) {
        if (CollectionUtils.isEmptyElement(list)) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();

        // 拼接正在使用中的订单数量
        deviceAssembler.assembleUsingBillCount(list);

        // 列表根据产品ID分组
        Map<String, List<DeviceVO>> group = list.stream().collect(Collectors.groupingBy(DeviceVO::getModelProductId));

        for (Map.Entry<String, List<DeviceVO>> entry : group.entrySet()) {

            List<IotDeviceInfo> iotList = iotService.getDeviceInfo(entry.getValue(), entry.getKey());

            for (DeviceVO device : list) {
                try {
                    if (StringUtils.isAnyBlank(device.getMac(), device.getModelProductId())) {
                        continue;
                    }
                    // 获取数据点信息
//                    IotDeviceInfo deviceInfo = iotService.getDeviceInfo(device);
                    IotDeviceInfo deviceInfo = iotList.stream()
                            .filter(item -> Objects.equals(device.getMac(), item.getMac()) || Objects.equals(device.getMac2(), item.getMac()))
                            .findFirst().orElse(null);

                    // 更新设备信息
                    device.setDeviceId(device.getDeviceId());
                    if (deviceInfo != null) {
                        device.setLastPullTime(deviceInfo.getAt());
                        device.setPowerStatus(deviceInfo.getS());
                        device.setRemainTime(deviceInfo.getTime());
                        device.setRealTimePower(deviceInfo.getP());
                        device.setVoltage(deviceInfo.getV());
                        device.setElectricity(deviceInfo.getA());
                        device.setVersion(deviceInfo.getVersion());
                        device.setVxs(deviceInfo.getVxs());

                        // 总用电量
                        if (deviceInfo.getW() != null) {
                            // 若有数据点中的值大于数据库中的值，则更新
                            if (deviceInfo.getW().compareTo(device.getTotalElectriQuantity()) >= 0) {
                                device.setTotalElectriQuantity(deviceInfo.getW());
                            }
                            // 若数据点小于数据库中的值，则发命令恢复物联网设备的总用电量
                            else {
                                scheduledExecutorService.schedule(() -> {
                                    String reason = "拉取设备信息时，恢复总用电量,数据库值:" + device.getTotalElectriQuantity() + "，OneNet值：" + deviceInfo.getW();
                                    iotService.setTotalEle(device, device.getTotalElectriQuantity().multiply(new BigDecimal(1000)), reason);
                                }, 0, TimeUnit.SECONDS);
                            }
                        }

                        // 是否有WIFI
                        if (ModelTag.hasTag(device.getModelTags(), ModelTag.WIFI)) {
                            device.setWifi(deviceInfo.getWifi());
                        }

                        // 是否有电量
                        if (ModelTag.hasTag(device.getModelTags(), ModelTag.ELE)) {
                            device.setSurplusEle(deviceInfo.getM());
                        } else {
                            device.setSurplusEle(BigDecimal.ZERO);
                        }
                    }

                    // 判断设备是否正在使用
                    // 设备过期时间 > 当前时间，则正在使用
//                boolean hasTime =  device.getExpireTime() != null && device.getExpireTime().isAfter(now);
//                // 若当前设备有电量，则正在使用
//                boolean hasEle = device.getSurplusEle() != null && device.getSurplusEle().compareTo(BigDecimal.ZERO) > 0;
                    // 若开关开启，则正在使用
                    boolean hasOpen = DevicePowerStatus.ON.getStatus().equals(device.getPowerStatus());
                    // 若设备有正在使用中的订单，则正在使用
                    boolean hasUsingBill = device.getUsingBillCount() != null && device.getUsingBillCount() > 0;
                    if (hasOpen || hasUsingBill) {
                        device.setStatus(DeviceStatus.USING.getStatus());
                    } else {
                        device.setStatus(DeviceStatus.NORMAL.getStatus());
                    }

                    // 是否在线
                    String onlineStatus1 = iotService.getOnlineStatus(device.getMac(), device.getProductId()).getStatus();
                    String onlineStatus2 = iotService.getOnlineStatus(device.getMac2(), device.getProductId()).getStatus();
                    device.setOnlineStatus1(onlineStatus1);
                    device.setOnlineStatus2(onlineStatus2);
                    if (DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus1) || DeviceOnlineStatus.ONLINE.getStatus().equals(onlineStatus2)) {
                        device.setOnlineStatus(DeviceOnlineStatus.ONLINE.getStatus());
                        device.setLastOnlineTime(now);
                    } else {
                        device.setOnlineStatus(DeviceOnlineStatus.OFFLINE.getStatus());
                    }

                    this.updateIotInfo(device);
                } catch (Exception e) {
                    log.error("更新设备信息失败，deviceId:{}, {}", device.getDeviceId(), e.getMessage());
                }
            }
        }
    }

    /**
     * 更新物联网信息
     */
    private int updateIotInfo(Device device) {
        Device data = new Device();
        data.setDeviceId(device.getDeviceId());
        data.setLastPullTime(device.getLastPullTime());
        data.setTotalElectriQuantity(device.getTotalElectriQuantity());
        data.setPowerStatus(device.getPowerStatus());
        data.setRemainTime(device.getRemainTime());
        data.setRealTimePower(device.getRealTimePower());
        data.setVoltage(device.getVoltage());
        data.setElectricity(device.getElectricity());
        data.setWifi(device.getWifi());
        data.setSurplusEle(device.getSurplusEle());
        data.setStatus(device.getStatus());
        data.setOnlineStatus(device.getOnlineStatus());
        data.setLastOnlineTime(device.getLastOnlineTime());
        return deviceMapper.updateSmDevice(device);
    }

    /**
     * 异步延迟执行拉取设备信息
     * @param deviceIds 设备id列表
     * @param delay 延迟时间
     * @param unit 时间单位
     */
    private void pullDeviceInfoAsync(List<Long> deviceIds, long delay, TimeUnit unit) {
        scheduledExecutorService.schedule(() -> {
            log.info("异步拉取设备信息 start");
            this.pullDeviceInfo(deviceIds);
            log.info("异步拉取设备信息 end");
        }, delay, unit);
    }



    @Override
    public String bindBySn(Long storeId, Long userId, String deviceNo) {
        DeviceVO device = selectByDeviceNo(deviceNo);
        return this.bind(storeId, userId, device);
    }

    @Override
    public String bindByMac(Long storeId, Long userId, String mac) {
        DeviceVO device = selectByMac(mac);
        return this.bind(storeId, userId, device);
    }

    private String bind(Long storeId, Long userId, DeviceVO device) {
        ServiceUtil.assertion(device == null, "设备未录入");
        // 如果绑定的用户是原来的用户，则直接返回MAC
        if (device.getUserId() != null) {
            if (Objects.equals(device.getUserId(), userId)) {
                return device.getMac();
            } else {
                throw new ServiceException("该设备已被绑定");
            }
        }
        // 如果绑定的是原来的店铺，则直接返回MAC
        if (device.getStoreId() != null) {
            if (Objects.equals(device.getStoreId(), storeId)) {
                return device.getMac();
            } else {
                throw new ServiceException("该设备已被绑定");
            }
        }

        ServiceUtil.assertion(device.getLimitRechargeTime() != null
                && device.getLimitRechargeTime().isAfter(LocalDateTime.now()),
                "设备无法绑定：" + device.getLimitRechargeReason()
        );

        // 查询用户
        SmUserVO user = userService.selectSimpleById(userId);
        ServiceUtil.assertion(user == null, "用户不存在");

        // 查询店铺
        if (storeId != null) {
            StoreVo store = storeService.selectSmStoreById(storeId);
            ServiceUtil.assertion(store == null, "店铺不存在");
            ServiceUtil.assertion(!Objects.equals(store.getUserId(), userId), "该店铺不属于该用户");
        }

        transactionTemplate.execute(status -> {
            // 绑定
            int updateCount = deviceMapper.bind(device.getDeviceId(), storeId, userId);
            ServiceUtil.assertion(updateCount != 1, "当前设备信息已变更，请刷新后重试");

            // 校验分成
            deviceValidator.checkBonus(device.getDeviceId());

            // 记录绑定记录
            int record = smDeviceBindRecordService.record(userId, device.getDeviceId(), BindRecordType.BIND, BindRecordUserType.MCH);
            ServiceUtil.assertion(record != 1, "添加绑定记录失败");

            // 用户设置为商户
            if (UserType.USER.getType().equals(user.getType())) {
                int changeType = userService.changeType(userId, UserType.MCH);
                ServiceUtil.assertion(changeType != 1, "修改用户类型失败");
            }
            return updateCount;
        });

        return device.getMac();
    }

    /**
     * 根据mac号查询数据
     * @param mac
     * @return
     */
    @Override
    public DeviceVO selectByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return null;
        }
        return deviceMapper.selectSimpleSmDeviceByMac(mac);
    }

    @Override
    public int resetWithBill(Long deviceId, boolean requiredIot, BigDecimal totalEle, String reason, boolean withTime, boolean withEle) {
        ServiceUtil.assertion(deviceId == null, "设备id不能为空");

        // 获取设备信息
        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");
        // 拉取最新设备信息
        pullDeviceInfo(device);

        Integer result = transactionTemplate.execute(status -> {
            // 关闭未完成订单
            this.closeNotFinishedBill(device, totalEle);

            // 修改设备信息
            if (withTime) {
                int timeUpdate = this.resetUpdateDbTime(deviceId);
                ServiceUtil.assertion(timeUpdate != 1, "修改设备时间失败");
            }

            if (withEle) {
                int eleUpdate = this.resetUpdateDbEle(deviceId, device.getTotalElectriQuantity());
                ServiceUtil.assertion(eleUpdate != 1, "修改设备电量失败");
            }

            // 发送命令
            if (withTime) {
                this.resetTime(device, requiredIot, reason);
            }

            if (withEle) {
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {
                    log.error("设备归零等待时间异常:{}", e.getMessage());
                    throw new ServiceException(e.getMessage());
                }

                this.resetEle(device, requiredIot, reason);
            }

            return 1;
        });

        if (result != null && result == 1) {
            this.resetRecord(device, RecordTimeType.TIME, reason);
            this.resetRecord(device, RecordTimeType.ELE, reason);
        }

        return result == null ? 0 : result;
    }

    /**
     * 尝试关闭该设备未结束的所有订单
     * @param device 设备
     * @param totalEle 总用电量
     */
    @Override
    public int closeNotFinishedBill(DeviceVO device, BigDecimal totalEle) {
        if (device == null || device.getDeviceId() == null) {
            return 0;
        }

        TransactionBillQuery query = new TransactionBillQuery();
        query.setStatusList(TransactionBillStatus.canClose());
        query.setDeviceId(device.getDeviceId());
        query.setIsFinished(false);   // 未结束的订单都会被关闭
        List<TransactionBillVO> billList = transactionBillService.selectSmTransactionBillList(query);

        int closeCount = 0;
        try {
            closeCount = transactionBillService.batchCloseBillByDevice(billList, false, device, totalEle);
            ServiceUtil.assertion(closeCount != billList.size(), "关闭订单失败:closeCount =" + closeCount);
        } catch (Exception e) {
            log.error("关闭订单失败: {}", e.getMessage());
        }
        return closeCount;
    }

    @Override
    public int initTotalEle(Long deviceId) {
        if (deviceId == null) {
            return 0;
        }
        // 拉取设备信息
        this.pullDeviceInfo(deviceId);
        return deviceMapper.initTotalEle(deviceId);
    }

    @Override
    public int updateVxs(Long deviceId, BigDecimal vxs, String reason) {
        if (deviceId == null || vxs == null) {
            return 0;
        }
        DeviceVO device = this.selectById(deviceId);
        ServiceUtil.assertion(device == null, "设备不存在");

        CommandResponse res = iotService.setVxs(device, vxs, reason);
        return res != null && res.isSuccess() ? 1 : 0;
    }

    /**
     * 设备是否已经被绑定
     *
     * @param mac
     * @return
     */
    @Override
    public int bindCodeByMac(String mac) {
        if (StringUtils.isBlank(mac)) {
            return Constants.DEVICE_NOT_FOUND;
        }
        DeviceVO device = deviceMapper.selectSimpleSmDeviceByMac(mac);
        return isBindCode(device);
    }

    private int isBindCode(DeviceVO device) {
        if (device == null) {
            return Constants.DEVICE_NOT_FOUND;
        }
        if (device.getUserId() == null) {
            return Constants.DEVICE_NOT_BIND;
        } else {
            return Constants.DEVICE_BINDED;
        }
    }

    /**
     * 根据SN码查询设备是否已经绑定
     *
     * @param deviceNo
     * @return
     */
    @Override
    public int bindCodeBySn(String deviceNo) {
        if (StringUtils.isBlank(deviceNo)) {
            return Constants.DEVICE_NOT_FOUND;
        }
        DeviceVO device = deviceMapper.selectSimpleSmDeviceBySn(deviceNo);
        return isBindCode(device);
    }

    /**
     * 更新设备服务费
     *
     * @param userId
     * @param serviceRate
     */
    @Override
    public int updateServiceRateByUserId(Long userId, BigDecimal serviceRate) {
        if (userId == null) {
            return 0;
        }
        List<StoreVo> list = storeService.selectByUserId(userId);
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        return deviceMapper.updateServiceRateByStores(list.stream().map(StoreVo::getStoreId).collect(Collectors.toList()), serviceRate);
    }

    @Override
    public int updateByIot(Device device) {
        device.setLastPullTime(DateUtils.getNowDate());
        return deviceMapper.updateSmDeviceByIot(device);
    }

    /**
     * ids查询列表
     *
     * @param ids
     * @param userId
     */
    @Override
    public List<DeviceVO> selectByIds(List<Long> ids, Long userId) {
        if (CollectionUtils.isEmptyElement(ids)) {
            return Collections.emptyList();
        }
        DeviceQuery dto = new DeviceQuery();
        dto.setDeviceIds(ids);
        dto.setUserId(userId);
        return selectSmDeviceList(dto);
    }

    /**
     * 查询各店铺的设备数量,并店铺
     *
     * @param storeIds 店铺id
     */
    @Override
    public Map<Long, Integer> selectCountMapByStoreIds(List<Long> storeIds) {
        if (CollectionUtils.isEmptyElement(storeIds)) {
            return Collections.emptyMap();
        }
        List<DeviceCountVO> list = deviceMapper.selectCountByStoreIds(storeIds);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(DeviceCountVO::getStoreId, DeviceCountVO::getCount));
    }

    /**
     * 条件查询数量，并店铺
     *
     * @param query 查询条件
     * @param keyMapper 键值的映射
     */
    @Override
    public <K> Map<K, Integer> selectCommonCountMap(DeviceQuery query, Function<? super DeviceCountVO, ? extends K> keyMapper ) {
        if (query == null) {
            return Collections.emptyMap();
        }
        List<DeviceCountVO> list = deviceMapper.selectCommonCount(query);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.toMap(keyMapper, DeviceCountVO::getCount));
    }

    /**
     * 获取设备，包含套餐列表
     *
     * @param deviceSn
     * @return
     */
    @Override
    public DeviceVO getDeviceWithSuitList(String deviceSn) {
        if (deviceSn == null) {
            return null;
        }
        DeviceVO device = deviceMapper.selectByDeviceNo(deviceSn);
        if (device != null) {
            deviceAssembler.assembleSuitList(Collections.singletonList(device));
        }
        return device;
    }

    /**
     * 根据设备编号查询
     *
     * @param deviceNo
     */
    @Override
    public DeviceVO selectByDeviceNo(String deviceNo) {
        return deviceMapper.selectByDeviceNo(deviceNo);
    }

    @Override
    @Transactional
    public int unbind(Long deviceId) {
        DeviceVO device = deviceMapper.selectSmDeviceByDeviceId(deviceId);
        ServiceUtil.assertion(device == null || device.getDeleted(), "设备不存在");
        // 判断是否有正在使用中的订单
        ServiceUtil.assertion(deviceValidator.hasUsingBill(device.getDeviceId()), "当前设备有正在使用中的订单，请关闭后重试");

        Integer result = transactionTemplate.execute(status -> {
            int updateCount = deviceMapper.unbindStore(deviceId);
            ServiceUtil.assertion(updateCount != 1, "当前设备信息已变更，请刷新后重试");

            // 删除套餐关联关系
            deviceSuitService.deleteByDeviceId(deviceId);

            // 新增解绑记录
            smDeviceBindRecordService.record(device.getUserId(), deviceId, BindRecordType.UNBIND, BindRecordUserType.MCH);

            return updateCount;
        });

        return result == null ? 0 : result;
    }

    /**
     * 设备绑定SN码，若其他设备已有该SN则直接替换
     *
     * @param deviceId 设备ID
     * @param sn       SN
     */
    @Override
    public int bindSn(Long deviceId, String sn) {
        if (deviceId == null || StringUtils.isBlank(sn)) {
            return 0;
        }
        return deviceMapper.bindSn(deviceId, sn);
    }
}
