package com.skr.mdm.api.app.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.github.pagehelper.PageHelper;
import com.skr.mdm.api.app.service.AppConfigService;
import com.skr.mdm.api.app.service.MdmAppStoreService;
import com.skr.mdm.bean.qo.FileChunkQO;
import com.skr.mdm.bean.qo.StrategyDeptQO;
import com.skr.mdm.bean.qo.StrategyUserQO;
import com.skr.mdm.bean.qo.app.*;
import com.skr.mdm.bean.vo.app.MdmApkInfoVO;
import com.skr.mdm.constant.MinIoMessageConstants;
import com.skr.mdm.dao.*;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.*;
import com.skr.mdm.enums.*;
import com.skr.mdm.exception.ServiceException;
import com.skr.mdm.handle.IssuedUserHandler;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.manager.DeviceManager;
import com.skr.mdm.manager.oss.MinIoManager;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.*;
import lombok.extern.slf4j.Slf4j;
import net.dongliu.apk.parser.bean.ApkMeta;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2022-11-22 11:03
 */
@Service
@Slf4j
public class MdmAppStoreServiceImpl implements MdmAppStoreService {

    /**
     * 轻应用类型
     */
    private final int LIGHT_APP = 2;
    private final String MDM_CLIENT_PACKAGE = "com.android.online.devmdm";

    private final String MODULE = "应用发布";

    @Autowired
    private MdmAppStoreDao appStoreDao;
    @Autowired
    private MdmAppStoreVersionDao appStoreVersionDao;
    @Autowired
    private MdmAppStoreUserDao appStoreUserDao;
    @Autowired
    private MdmStrategyDeptDao strategyDeptDao;
    @Autowired
    private MdmStrategyUserDao strategyUserDao;
    @Autowired
    private LogAdminHandler logAdminHandler;
    @Autowired
    private FileChunkUtil fileChunkUtil;
    @Autowired
    private MinIoManager minIoManager;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private AppConfigService appConfigService;
    @Autowired
    private IssuedUserHandler issuedUserHandler;
    @Autowired
    private DeviceManager deviceManager;
    @Autowired
    private MdmAppPushDao appPushDao;

    private static MdmApkInfoVO getMdmApkInfoVO(MultipartFile file, ApkMeta apkInfo, String iconUrl, String fileUrl) {
        double fileSize = file.getSize() / (double) (1024 * 1024);
        BigDecimal fileSizeDecimal = new BigDecimal(fileSize);

        MdmApkInfoVO apkInfoVO = MdmApkInfoVO.builder()
                .fileName(file.getOriginalFilename())
                .appName(StringUtils.replace(apkInfo.getName(), " ", ""))
                .appSign(ApkUtil.getApkSign(file))
                .fileSize(fileSizeDecimal.setScale(2, RoundingMode.HALF_UP).floatValue() + "M")
                .iconUrl(iconUrl)
                .fileUrl(fileUrl)
                .build();
        BeanUtils.copyProperties(apkInfo, apkInfoVO);
        return apkInfoVO;
    }

    private static void checkApkIfo(ApkMeta apkInfo) {
        if (apkInfo == null) {
            throw new ServiceException(ResultCodeEnum.APP_ANALYSIS_FAILURE);
        }
    }

    private static void checkFileExtName(MultipartFile file) {
        String extName = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
        if (!StringUtils.equals("apk", extName)) {
            throw new ServiceException("仅支持apk文件！");
        }
    }

    @Override
    public ResponseResult listApp(AppStoreSearchQO appStoreSearchQO, LoginAdminDTO loginAdminDTO) {
        appStoreSearchQO.setCompanyId(loginAdminDTO.getCompanyId());
        PageHelper.startPage(appStoreSearchQO.getPageNum(), appStoreSearchQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(appStoreDao.listMdmAppStoreVO(appStoreSearchQO)));
    }

    @Override
    public ResponseResult getAppVersionInfo(Integer appId) {
        return ResponseResult.success(appStoreVersionDao.listMdmAppStoreVersionVO(appId));
    }

    @Override
    public ResponseResult listAllAppName(String appName, LoginAdminDTO loginAdminDTO) {
        return ResponseResult.success(appStoreDao.listMdmAppStoreNameVO(appName, loginAdminDTO.getCompanyId()));
    }

    @Override
    public ResponseResult checkChunkExist(FileChunkQO chunkQO) {
        return ResponseResult.success(fileChunkUtil.checkChunkExist(chunkQO));
    }

    @Override
    public ResponseResult uploadChunk(FileChunkQO chunkQO, LoginAdminDTO loginAdminDTO) {
        TimeInterval timer = DateUtil.timer();
        File file = fileChunkUtil.uploadChunk(chunkQO);
        if (file == null || !file.exists()) {
            return ResponseResult.success();
        }
        log.info("上传分片总耗时：{}毫秒", timer.intervalMs());
        ApkMeta apkInfo = ApkUtil.getApkInfo(file);
        // 获取icon
        String iconPath = ApkUtil.iconPath;
        File iconFile = new File(iconPath);
        String iconObject = MinIoMessageConstants.MINIO_APP_ICON_OBJECT + MdmIdUtil.getSnowflakeNextId() + ".png";
        String iconUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), iconObject,
                FileUtils.fileToMultipartFile(iconFile), Boolean.FALSE);
        MdmApkInfoVO apkInfoVO = MdmApkInfoVO.builder()
                .appName(StringUtils.replace(apkInfo.getName(), " ", ""))
                .appSign("")
//                .fileSize(String.valueOf(chunkQO.getFile().getSize()))
                .iconUrl(iconUrl)
                .build();
        BeanUtils.copyProperties(apkInfo, apkInfoVO);
        return ResponseResult.success(apkInfoVO);
    }

    @Override
    public ResponseResult analyticNewApp(MultipartFile file, LoginAdminDTO loginAdminDTO) {
        // 判断是否为apk文件
        checkFileExtName(file);
        // 解析apk文件
        ApkMeta apkInfo = ApkUtil.getApkInfo(file);
        checkApkIfo(apkInfo);
        // 客户端包禁止上传
        checkAppPackageName(apkInfo.getPackageName());
        // 包名是否存在
        checkAppPackageNameExist(loginAdminDTO.getCompanyId(), apkInfo.getPackageName(), true);
        // 获取icon
        String iconUrl = "";
        if (StringUtils.isNotBlank(ApkUtil.iconPath)) {
            String iconPath = ApkUtil.iconPath;
            // 图标文件上传到文件服务
            File iconFile = new File(iconPath);
            String iconObject = MinIoMessageConstants.MINIO_APP_ICON_OBJECT + MdmIdUtil.getSnowflakeNextId() + ".png";
            iconUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), iconObject,
                    FileUtils.fileToMultipartFile(iconFile), Boolean.FALSE);
            // 删除图标文件
            boolean delete = iconFile.delete();
            log.info("应用图标缓存文件是否删除成功：{}", delete);
        }
        // 上传应用到文件服务
        String minioObject = MinIoMessageConstants.MINIO_APP_OBJECT +
                apkInfo.getName() + "-" +
                apkInfo.getPackageName() + "-" +
                apkInfo.getVersionName() + ".apk";
        String fileUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), minioObject, file, true);
        // 文件大小
        MdmApkInfoVO apkInfoVO = getMdmApkInfoVO(file, apkInfo, iconUrl, fileUrl);
        return ResponseResult.success(apkInfoVO);
    }

    private void checkAppPackageNameExist(Integer companyId, String packageName, boolean isNewApp) {
        Long count = new LambdaQueryChainWrapper<>(appStoreDao)
                .eq(MdmAppStoreDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .eq(MdmAppStoreDO::getPackageName, packageName)
                .eq(MdmAppStoreDO::getCompanyId, companyId)
                .count();
        if (isNewApp && count > 0) {
            throw new ServiceException("当前应用已存在！如需上传不同的版本应用，请跳转到版本管理页面！");
        }
        // 新版本应用
        if (!isNewApp && count == 0) {
            throw new ServiceException("应用不存在！如需添加新应用，请跳转到添加应用页面！");
        }
    }

    private void checkAppPackageName(String packageName) {
        if (StringUtils.equals(MDM_CLIENT_PACKAGE, packageName)) {
            throw new ServiceException("客户端安装包禁止上传！如需上传客户端安装，请跳转到到客户端升级页面！");
        }
    }

    @Override
    public ResponseResult uploadAppIcon(MultipartFile file, Integer appId, LoginAdminDTO loginAdminDTO) {
        String extName = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
        if (!StringUtils.equals("jpg", extName) && !StringUtils.equals("jpeg", extName) && !StringUtils.equals("png", extName)) {
            return ResponseResult.failure("仅支持png、jpg、jpeg文件！");
        }
        if (file.getSize() > 50 * 1024) {
            return ResponseResult.failure("文件不能超过50kb!");
        }
        String iconObject = MinIoMessageConstants.MINIO_APP_ICON_OBJECT + MdmIdUtil.getSnowflakeNextId() + "." + extName;
        String iconUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), iconObject, file, Boolean.FALSE);
        if (appId != null) {
            // 删除当前图标文件
            MdmAppStoreDO mdmAppStoreDO = appStoreDao.selectById(appId);
            if (mdmAppStoreDO != null && StringUtils.isNotBlank(mdmAppStoreDO.getIconUrl())) {
                minIoManager.removeObject(ListUtil.toList(mdmAppStoreDO.getIconUrl()));
            }
            // 更新图标文件地址
            new LambdaUpdateChainWrapper<>(appStoreDao)
                    .set(MdmAppStoreDO::getIconUrl, iconUrl)
                    .eq(MdmAppStoreDO::getId, appId)
                    .update();
        }
        return ResponseResult.success(iconUrl);
    }

    @Override
    public ResponseResult analyticAppNewVersion(MultipartFile file, Integer appId, LoginAdminDTO loginAdminDTO) {
        // 判断是否为apk文件
        checkFileExtName(file);
        // 解析apk文件
        ApkMeta apkInfo = ApkUtil.getApkInfo(file);
        // 删除图标文件
        if (StringUtils.isNotBlank(ApkUtil.iconPath)) {
            String iconPath = ApkUtil.iconPath;
            File iconFile = new File(iconPath);
            boolean isDelete = iconFile.delete();
            log.info("应用图标缓存文件是否删除成功：{}", isDelete);
        }
        checkApkIfo(apkInfo);
        // 客户端包禁止上传
        checkAppPackageName(apkInfo.getPackageName());
        // 应用是否存在
        checkAppPackageNameExist(loginAdminDTO.getCompanyId(), apkInfo.getPackageName(), false);
        // 检查包名是否一致
        MdmAppStoreDO mdmAppStoreDO = appStoreDao.selectById(appId);
        if (!mdmAppStoreDO.getPackageName().equals(apkInfo.getPackageName())) {
            throw new ServiceException("包名验证不通过");
        }
        // 检查应用版本是否存在
        checkAppVersionExist(appId, apkInfo.getVersionName());
        // 校验apk签名
        String apkSign = ApkUtil.getApkSign(file);
        if (!apkSign.equals(mdmAppStoreDO.getAppSign())) {
            return ResponseResult.failure("证书校验不通过！");
        }
        // 上传应用到文件服务
        String minioObject = MinIoMessageConstants.MINIO_APP_OBJECT +
                apkInfo.getName() + "-" +
                apkInfo.getPackageName() + "-" +
                apkInfo.getVersionName() + ".apk";
        String fileUrl = minIoManager.putObjectMinIo(loginAdminDTO.getCompanyId(), minioObject, file, true);
        // 文件大小
        MdmApkInfoVO apkInfoVO = getMdmApkInfoVO(file, apkInfo, null, fileUrl);
        return ResponseResult.success(apkInfoVO);
    }

    private void checkAppVersionExist(Integer appId, String versionName) {
        Long countVersion = new LambdaQueryChainWrapper<>(appStoreVersionDao)
                .eq(MdmAppStoreVersionDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .eq(MdmAppStoreVersionDO::getAppId, appId)
                .eq(MdmAppStoreVersionDO::getVersionName, versionName)
                .count();
        if (countVersion > 0) {
            throw new ServiceException("当前应用版本已存在！");
        }
    }

    @Override
    public ResponseResult upgradeVersion(AppStoreVersionUpgradeQO appStoreVersionUpgradeQO, LoginAdminDTO loginAdminDTO) {
        MdmAppStoreDO appStoreDO = appStoreDao.selectById(appStoreVersionUpgradeQO.getAppId());
        MdmAppStoreVersionDO appStoreVersionDO = MdmAppStoreVersionDO
                .builder()
                .state(OnOffStateEnum.ON.getState())
                .build();
        BeanUtils.copyProperties(appStoreVersionUpgradeQO, appStoreVersionDO);
        appStoreVersionDao.insert(appStoreVersionDO);
        // log
        String content = "应用版本升级：" + appStoreDO.getAppName() + "(" + appStoreVersionDO.getVersionName() + ")";
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult addApp(MdmAppStoreAddQO mdmAppStoreAddQO, LoginAdminDTO loginAdminDTO) {
        // 检查应用名称是否存在
        checkAppNameDuplication(null, mdmAppStoreAddQO.getAppName(), loginAdminDTO.getCompanyId());
        // 非轻应用检查包名是否存在
        if (!mdmAppStoreAddQO.getAppCategory().equals(LIGHT_APP)) {
            checkAppPackageNameExist(loginAdminDTO.getCompanyId(), mdmAppStoreAddQO.getPackageName(), true);
        }
        // 保存应用信息
        MdmAppStoreDO appStoreDO = saveAppStore(mdmAppStoreAddQO, loginAdminDTO);
        // 保存应用版本
        saveAppVersion(mdmAppStoreAddQO, appStoreDO);
        // 保存应用配置
        saveAppConfig(loginAdminDTO, appStoreDO);
        // 指定部门发布
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(mdmAppStoreAddQO.getGlobalType())) {
            saveAppDept(mdmAppStoreAddQO.getCandidateIdList(), appStoreDO.getId());
        }
        // 指定用户发布
        if (IssuedTypeEnum.USER.getIssuedType().equals(mdmAppStoreAddQO.getGlobalType())) {
            saveAppUser(mdmAppStoreAddQO.getCandidateIdList(), appStoreDO.getId());
        }
        // 指定设备发布
        if (IssuedTypeEnum.DEVICE.getIssuedType().equals(mdmAppStoreAddQO.getGlobalType())) {
            saveAppDevice(mdmAppStoreAddQO.getCandidateIdList(), appStoreDO.getId());
        }
        // log
        String content = "添加应用：" + mdmAppStoreAddQO.getAppName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    private MdmAppStoreDO saveAppStore(MdmAppStoreAddQO mdmAppStoreAddQO, LoginAdminDTO loginAdminDTO) {
        MdmAppStoreDO appStoreDO = MdmAppStoreDO.builder()
                .companyId(loginAdminDTO.getCompanyId())
                .state(OnOffStateEnum.ON.getState())
                .operator(loginAdminDTO.getAdminName())
                .operateTime(new Date())
                .build();
        BeanUtils.copyProperties(mdmAppStoreAddQO, appStoreDO);
        appStoreDao.insert(appStoreDO);
        return appStoreDO;
    }

    private void saveAppVersion(MdmAppStoreAddQO mdmAppStoreAddQO, MdmAppStoreDO appStoreDO) {
        MdmAppStoreVersionDO appStoreVersionDO = MdmAppStoreVersionDO
                .builder()
                .appId(appStoreDO.getId())
                .state(OnOffStateEnum.ON.getState())
                .build();
        BeanUtils.copyProperties(mdmAppStoreAddQO, appStoreVersionDO);
        appStoreVersionDao.insert(appStoreVersionDO);
    }

    private void saveAppConfig(LoginAdminDTO loginAdminDTO, MdmAppStoreDO appStoreDO) {
        // 应用配置排除轻应用
        if (appStoreDO.getAppCategory().equals(LIGHT_APP)) {
            return;
        }
        AppConfigQO appConfigQO = new AppConfigQO();
        appConfigQO.setAppName(appStoreDO.getAppName());
        appConfigQO.setPackageName(appStoreDO.getPackageName());
        appConfigQO.setAppSign(appStoreDO.getAppSign());
        appConfigService.add(ListUtil.toList(appConfigQO), loginAdminDTO);
    }

    private void checkAppNameDuplication(Integer appId, String appName, Integer companyId) {
        Long count = new LambdaQueryChainWrapper<>(appStoreDao)
                .eq(MdmAppStoreDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .eq(MdmAppStoreDO::getAppName, appName)
                .eq(MdmAppStoreDO::getCompanyId, companyId)
                .ne(appId != null, MdmAppStoreDO::getId, appId)
                .count();
        if (count > 0) {
            throw new ServiceException("应用名称已存在！");
        }
    }

    @Override
    public ResponseResult updateApp(MdmAppStoreUpdateQO mdmAppStoreUpdateQO, LoginAdminDTO loginAdminDTO) {
        checkAppNameDuplication(mdmAppStoreUpdateQO.getId(), mdmAppStoreUpdateQO.getAppName(), loginAdminDTO.getCompanyId());

        MdmAppStoreDO mdmAppStoreDO = appStoreDao.selectById(mdmAppStoreUpdateQO.getId());
        MdmAppStoreDO appStoreDO = MdmAppStoreDO.builder()
                .operator(loginAdminDTO.getAdminName())
                .operateTime(new Date())
                .build();
        BeanUtils.copyProperties(mdmAppStoreUpdateQO, appStoreDO);
        appStoreDao.updateById(appStoreDO);
        // 修改发布方式后清除部门和用户相关表数据
        removeDeptAndUser(mdmAppStoreUpdateQO.getGlobalType(), mdmAppStoreDO.getGlobalType(), appStoreDO.getId());
        // 部门删除重新添加
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(mdmAppStoreUpdateQO.getGlobalType())) {
            strategyDeptDao.delete(Wrappers.lambdaQuery(MdmStrategyDeptDO.class)
                    .eq(MdmStrategyDeptDO::getStrategyId, appStoreDO.getId())
                    .eq(MdmStrategyDeptDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
            saveAppDept(mdmAppStoreUpdateQO.getCandidateIdList(), appStoreDO.getId());
        }
        // 用户删除重新添加
        if (IssuedTypeEnum.USER.getIssuedType().equals(mdmAppStoreUpdateQO.getGlobalType())) {
            strategyUserDao.delete(Wrappers.lambdaQuery(MdmStrategyUserDO.class)
                    .eq(MdmStrategyUserDO::getStrategyId, appStoreDO.getId())
                    .eq(MdmStrategyUserDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
            saveAppUser(mdmAppStoreUpdateQO.getCandidateIdList(), appStoreDO.getId());
        }
        // 用户删除重新添加
        if (IssuedTypeEnum.DEVICE.getIssuedType().equals(mdmAppStoreUpdateQO.getGlobalType())) {
            appStoreUserDao.delete(Wrappers.lambdaQuery(MdmAppStoreUserDO.class)
                    .eq(MdmAppStoreUserDO::getAppId, appStoreDO.getId()));
            saveAppDevice(mdmAppStoreUpdateQO.getCandidateIdList(), appStoreDO.getId());
        }
        // log
        String content = "编辑应用：" + mdmAppStoreUpdateQO.getAppName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    private void removeDeptAndUser(Integer newGlobalType, Integer oldGlobalType, Integer appId) {
        if (!newGlobalType.equals(oldGlobalType)) {
            strategyDeptDao.delete(Wrappers.lambdaQuery(MdmStrategyDeptDO.class)
                    .eq(MdmStrategyDeptDO::getStrategyId, appId)
                    .eq(MdmStrategyDeptDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
            strategyUserDao.delete(Wrappers.lambdaQuery(MdmStrategyUserDO.class)
                    .eq(MdmStrategyUserDO::getStrategyId, appId)
                    .eq(MdmStrategyUserDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
            appStoreUserDao.delete(Wrappers.lambdaQuery(MdmAppStoreUserDO.class)
                    .eq(MdmAppStoreUserDO::getAppId, appId));
        }
    }

    @Override
    public ResponseResult updateLightApp(MdmLightAppUpdateQO mdmLightAppUpdateQO, LoginAdminDTO loginAdminDTO) {
        updateApp(mdmLightAppUpdateQO, loginAdminDTO);
        // 更新轻应用url地址和版本
        new LambdaUpdateChainWrapper<>(appStoreVersionDao)
                .set(MdmAppStoreVersionDO::getFileUrl, mdmLightAppUpdateQO.getFileUrl())
                .set(MdmAppStoreVersionDO::getVersionName, mdmLightAppUpdateQO.getVersionName())
                .eq(MdmAppStoreVersionDO::getAppId, mdmLightAppUpdateQO.getId())
                .update();
        return ResponseResult.success();
    }

    @Override
    public ResponseResult deleteApp(List<Integer> appIdList, LoginAdminDTO loginAdminDTO) {
        // 检查应用是否可删除
        List<Integer> appVersionIdList = new LambdaQueryChainWrapper<>(appStoreVersionDao)
                .eq(MdmAppStoreVersionDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .in(MdmAppStoreVersionDO::getAppId, appIdList)
                .list()
                .stream().map(MdmAppStoreVersionDO::getId)
                .collect(Collectors.toList());
        checkAppVersionRemovable(appVersionIdList);

        List<String> fileUrlList = appStoreDao.listFileUrl(appIdList, 1);
        if (CollectionUtils.isNotEmpty(fileUrlList)) {
            minIoManager.removeObject(fileUrlList);
        }
        // 删除图标文件和轻应用
        List<String> iconUrlList = appStoreDao.selectBatchIds(appIdList)
                .stream().map(MdmAppStoreDO::getIconUrl)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(iconUrlList)) {
            minIoManager.removeObject(iconUrlList);
        }

        // 逻辑删除
        new LambdaUpdateChainWrapper<>(appStoreDao)
                .set(MdmAppStoreDO::getIsDelete, DeleteStateEnum.DELETED.getState())
                .in(MdmAppStoreDO::getId, appIdList)
                .update();
        new LambdaUpdateChainWrapper<>(appStoreVersionDao)
                .set(MdmAppStoreVersionDO::getIsDelete, DeleteStateEnum.DELETED.getState())
                .in(MdmAppStoreVersionDO::getAppId, appIdList)
                .update();
        // 物理删除
        strategyDeptDao.delete(Wrappers.lambdaQuery(MdmStrategyDeptDO.class)
                .in(MdmStrategyDeptDO::getStrategyId, appIdList)
                .eq(MdmStrategyDeptDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
        // 物理删除
        strategyUserDao.delete(Wrappers.lambdaQuery(MdmStrategyUserDO.class)
                .in(MdmStrategyUserDO::getStrategyId, appIdList)
                .eq(MdmStrategyUserDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType()));
        appStoreUserDao.delete(Wrappers.lambdaQuery(MdmAppStoreUserDO.class)
                .in(MdmAppStoreUserDO::getAppId, appIdList));
        // log
        List<String> appNameList = appStoreDao.selectBatchIds(appIdList)
                .stream()
                .map(MdmAppStoreDO::getAppName)
                .collect(Collectors.toList());
        String content = "删除应用：" + StringUtils.join(appNameList, ",");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult deleteAppVersion(List<Integer> appVersionIdList, LoginAdminDTO loginAdminDTO) {
        // 检查应用版本是否可删除
        checkAppVersionRemovable(appVersionIdList);
        List<MdmAppStoreVersionDO> appStoreVersionDOList = appStoreVersionDao.selectBatchIds(appVersionIdList);
        MdmAppStoreDO mdmAppStoreDO = appStoreDao.selectById(appStoreVersionDOList.get(0).getAppId());

        new LambdaUpdateChainWrapper<>(appStoreVersionDao)
                .set(MdmAppStoreVersionDO::getIsDelete, DeleteStateEnum.DELETED.getState())
                .in(MdmAppStoreVersionDO::getId, appVersionIdList)
                .update();
        // 删除文件
        if (!mdmAppStoreDO.getAppCategory().equals(LIGHT_APP)) {
            List<String> fileUrlList = appStoreVersionDOList
                    .stream()
                    .map(MdmAppStoreVersionDO::getFileUrl)
                    .collect(Collectors.toList());
            minIoManager.removeObject(fileUrlList);
        }
        // log
        List<String> appVersionNameList = appStoreVersionDOList
                .stream()
                .map(MdmAppStoreVersionDO::getVersionName)
                .collect(Collectors.toList());
        String content = "删除应用版本：" + mdmAppStoreDO.getAppName() + "(" + StringUtils.join(appVersionNameList, ",") + ")";
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    private void checkAppVersionRemovable(List<Integer> appVersionIdList) {
        if (CollectionUtils.isNotEmpty(appVersionIdList)) {
            Long count = new LambdaQueryChainWrapper<>(appPushDao)
                    .in(MdmAppPushDO::getAppVersionId, appVersionIdList)
                    .count();
            if (count > 0) {
                throw new ServiceException("应用推送列表存在需要删除的应用版本，删除失败！");
            }
        }
    }

    @Override
    public ResponseResult onOrOffApp(Integer appId, Integer state, LoginAdminDTO loginAdminDTO) {
        new LambdaUpdateChainWrapper<>(appStoreDao)
                .set(MdmAppStoreDO::getState, state)
                .eq(MdmAppStoreDO::getId, appId)
                .update();
        new LambdaUpdateChainWrapper<>(appStoreVersionDao)
                .set(MdmAppStoreVersionDO::getState, state)
                .eq(MdmAppStoreVersionDO::getAppId, appId)
                .update();
        // 下发
        issuedOffApp(appId, null, loginAdminDTO);
        // log
        MdmAppStoreDO appStoreDO = appStoreDao.selectById(appId);
        String content = "禁用应用：" + appStoreDO.getAppName();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    private void issuedOffApp(Integer appId, Integer appVersionId, LoginAdminDTO loginAdminDTO) {
        MdmAppStoreDO mdmAppStoreDO = appStoreDao.selectById(appId);
        List<Integer> appVersionIdList = new ArrayList<>();
        if (appVersionId == null) {
            appVersionIdList = new LambdaQueryChainWrapper<>(appStoreVersionDao)
                    .eq(MdmAppStoreVersionDO::getAppId, appId)
                    .eq(MdmAppStoreVersionDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                    .list()
                    .stream().map(MdmAppStoreVersionDO::getId)
                    .collect(Collectors.toList());
        } else {
            appVersionIdList.add(appVersionId);
        }
        // 获取下发的设备ID
        List<Integer> equipmentIdList = new ArrayList<>();
        if (IssuedTypeEnum.GLOBAL.getIssuedType().equals(mdmAppStoreDO.getGlobalType())) {
            equipmentIdList = deviceManager.getDeviceIdList(mdmAppStoreDO.getGlobalType(), null, loginAdminDTO.getCompanyId());
        }
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(mdmAppStoreDO.getGlobalType())) {
            List<Integer> departmentIdList = new LambdaQueryChainWrapper<>(strategyDeptDao)
                    .eq(MdmStrategyDeptDO::getStrategyId, appId)
                    .eq(MdmStrategyDeptDO::getStrategyType, StrategyDeptTypeEnum.APP_STORE.getStrategyType())
                    .list()
                    .stream().map(MdmStrategyDeptDO::getDepartmentId)
                    .collect(Collectors.toList());
            equipmentIdList = deviceManager.getDeviceIdList(mdmAppStoreDO.getGlobalType(), departmentIdList, loginAdminDTO.getCompanyId());
        }
        if (IssuedTypeEnum.DEVICE.getIssuedType().equals(mdmAppStoreDO.getGlobalType())) {
            equipmentIdList = new LambdaQueryChainWrapper<>(appStoreUserDao)
                    .eq(MdmAppStoreUserDO::getAppId, appId)
                    .list()
                    .stream().map(MdmAppStoreUserDO::getEquipmentId)
                    .collect(Collectors.toList());
        }
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(), "disableApp", JSONUtil.toJsonStr(appVersionIdList));
    }

    @Override
    public ResponseResult onOrOffAppVersion(Integer appVersionId, Integer state, LoginAdminDTO loginAdminDTO) {
        new LambdaUpdateChainWrapper<>(appStoreVersionDao)
                .set(MdmAppStoreVersionDO::getState, state)
                .eq(MdmAppStoreVersionDO::getId, appVersionId)
                .update();
        // 下发
        MdmAppStoreVersionDO storeVersionDO = appStoreVersionDao.selectById(appVersionId);
        issuedOffApp(storeVersionDO.getAppId(), appVersionId, loginAdminDTO);
        // log
        MdmAppStoreVersionDO appStoreVersionDO = appStoreVersionDao.selectById(appVersionId);
        MdmAppStoreDO appStoreDO = appStoreDao.selectById(appStoreVersionDO.getAppId());
        String content = "禁用应用版本：" + appStoreDO.getAppName() + "(" + appStoreVersionDO.getVersionName() + ")";
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.APP_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult listAppUser(StrategyUserQO strategyUserQO) {
        // 获取全部数据
        if (strategyUserQO.getPageNum().equals(0)) {
            return ResponseResult.success(appStoreUserDao.listAppStoreUserVO(strategyUserQO.getStrategyId(),
                    strategyUserQO.getSearchType(), strategyUserQO.getSearchValue()));
        }
        PageHelper.startPage(strategyUserQO.getPageNum(), strategyUserQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(appStoreUserDao.listAppStoreUserVO(
                strategyUserQO.getStrategyId(), strategyUserQO.getSearchType(), strategyUserQO.getSearchValue())));
    }

    @Override
    public ResponseResult listAppDept(StrategyUserQO strategyUserQO) {
        StrategyDeptQO strategyDeptQO = StrategyDeptQO.builder()
                .strategyType(StrategyDeptTypeEnum.APP_STORE.getStrategyType())
                .build();
        BeanUtils.copyProperties(strategyUserQO, strategyDeptQO);
        // 获取全部数据
        if (strategyUserQO.getPageNum().equals(0)) {
            return ResponseResult.success(strategyDeptDao.listDept(strategyDeptQO));
        }
        PageHelper.startPage(strategyUserQO.getPageNum(), strategyUserQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(strategyDeptDao.listDept(strategyDeptQO)));
    }

    @Override
    public ResponseResult getFileUploadProgress(String fileSize, String sessionId, LoginAdminDTO loginAdminDTO) {
        redisUtils.set(RedisGetKeyUtil.getFileUploadProgressKey(fileSize), loginAdminDTO.getId() + "-" + sessionId, 60 * 60);
        return ResponseResult.success();
    }

    private void saveAppDept(List<Integer> departmentIdList, Integer appId) {
        if (CollectionUtils.isNotEmpty(departmentIdList)) {
            List<MdmStrategyDeptDO> strategyDeptDOList = departmentIdList
                    .stream()
                    .map(departmentId -> MdmStrategyDeptDO
                            .builder()
                            .strategyId(appId)
                            .strategyType(StrategyDeptTypeEnum.APP_STORE.getStrategyType())
                            .departmentId(departmentId)
                            .build())
                    .collect(Collectors.toList());
            strategyDeptDao.insertBatch(strategyDeptDOList);
        }
    }

    private void saveAppUser(List<Integer> userIDList, Integer appId) {
        if (CollectionUtils.isNotEmpty(userIDList)) {
            List<MdmStrategyUserDO> strategyUserDOList = userIDList
                    .stream()
                    .map(userId -> MdmStrategyUserDO
                            .builder()
                            .strategyId(appId)
                            .strategyType(StrategyDeptTypeEnum.APP_STORE.getStrategyType())
                            .userId(userId)
                            .build())
                    .collect(Collectors.toList());
            strategyUserDao.insertBatch(strategyUserDOList);

            List<Integer> deviceIdList = deviceManager.getDeviceIdList(null, userIDList, null, null);
            saveAppDevice(deviceIdList, appId);
        }
    }

    private void saveAppDevice(List<Integer> equipmentIdList, Integer appId) {
        if (CollectionUtils.isNotEmpty(equipmentIdList)) {
            List<MdmAppStoreUserDO> appStoreUserDOList = equipmentIdList
                    .stream()
                    .map(equipmentId -> MdmAppStoreUserDO
                            .builder()
                            .appId(appId)
                            .equipmentId(equipmentId)
                            .build())
                    .collect(Collectors.toList());
            appStoreUserDao.insertBatch(appStoreUserDOList);
        }
    }
}