Kaynağa Gözat

Merge branch 'master' of http://117.50.213.128:10880/lihao16/smsb-plus

Shinohara Haruna 6 ay önce
ebeveyn
işleme
4c73a682ea
23 değiştirilmiş dosya ile 348 ekleme ve 756 silme
  1. 53 0
      smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/bo/HttpHeartbeatReq.java
  2. 6 0
      smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/constants/DeviceConstants.java
  3. 14 0
      smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/vo/HttpHeartbeatRspVo.java
  4. 9 0
      smsb-modules/smsb-device/src/main/java/com/inspur/device/service/ISmsbDeviceService.java
  5. 25 7
      smsb-modules/smsb-device/src/main/java/com/inspur/device/service/impl/SmsbDeviceServiceImpl.java
  6. 1 1
      smsb-modules/smsb-job/src/main/java/org/dromara/job/snailjob/DeviceStatusReviewExecutor.java
  7. 5 0
      smsb-modules/smsb-netty/src/main/java/com/inspur/netty/message/push/PushMessageType.java
  8. 1 1
      smsb-modules/smsb-netty/src/main/java/com/inspur/netty/util/NettyConstants.java
  9. 13 0
      smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbFrontController.java
  10. 11 0
      smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbItemPushDeviceController.java
  11. 3 2
      smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbMinioDataController.java
  12. 4 0
      smsb-modules/smsb-source/src/main/java/com/inspur/source/domain/bo/SmsbItemPushDeviceBo.java
  13. 8 0
      smsb-modules/smsb-source/src/main/java/com/inspur/source/service/ISmsbItemPushDeviceService.java
  14. 44 0
      smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbItemPushDeviceServiceImpl.java
  15. 2 1
      smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbMinioDataServiceImpl.java
  16. 3 3
      smsb-plus-ui/src/api/smsb/device/device_run_type.ts
  17. 8 0
      smsb-plus-ui/src/api/smsb/source/item_push_device.ts
  18. 4 0
      smsb-plus-ui/src/api/smsb/source/item_push_device_type.ts
  19. 25 11
      smsb-plus-ui/src/views/smsb/device/index.vue
  20. 109 55
      smsb-plus-ui/src/views/smsb/itemPush/index.vue
  21. 0 225
      smsb-plus-ui/vite.config.ts.timestamp-1737700502429-c6453b1b35b5e.mjs
  22. 0 225
      smsb-plus-ui/vite.config.ts.timestamp-1738734766950-3118c61dd84be.mjs
  23. 0 225
      smsb-plus-ui/vite.config.ts.timestamp-1740461761631-438ca296213cc.mjs

+ 53 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/bo/HttpHeartbeatReq.java

@@ -0,0 +1,53 @@
+package com.inspur.device.domain.bo;
+
+
+import lombok.Data;
+
+/**
+ * 前端设备心跳上报参数
+ * @author lihao16
+ */
+@Data
+public class HttpHeartbeatReq {
+
+    /** 设备标识 */
+    private String identifier;
+
+    /** 亮度 */
+    private Integer bright;
+
+    /** CUP 百分比 */
+    private String cpuUsage;
+
+    /** 外部存储 总量 */
+    private Long externalMemoryTotal;
+
+    /** 外部存储 已使用 */
+    private Long externalMemoryUsage;
+
+    /** 外部存储 使用百分比 */
+    private String externalUsage;
+
+    /** 内存 总量 */
+    private Long ramTotalOfByte;
+
+    /** 内存 已使用 */
+    private Long ramUsageOfByte;
+
+    /** 系统构建时间 */
+    private String systemBuildDate;
+
+    /** 系统构建版本 */
+    private String systemBuildVersion;
+
+    /** apk version code */
+    private String versionCode;
+
+    /** apk version name */
+    private String versionName;
+
+    /** 音量 */
+    private Integer volume;
+
+
+}

+ 6 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/constants/DeviceConstants.java

@@ -38,5 +38,11 @@ public class DeviceConstants {
     /** 设备截屏 Redis key */
     public static final String REDIS_SCREENSHOT_KEY = "global:device:screenshot:";
 
+    /** 设备运行状态 */
+    public static final String REDIS_DEVICE_RUN_INFO_KEY = "global:device:run:info:";
+
+    /** 设备http最后一次心跳时间 */
+    public static final String REDIS_DEVICE_HEARTBEAT_HTTP_KEY = "global:device:heartbeat:http:";
+
 
 }

+ 14 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/vo/HttpHeartbeatRspVo.java

@@ -0,0 +1,14 @@
+package com.inspur.device.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 设备心跳响应
+ * @author lihao16
+ */
+@Data
+public class HttpHeartbeatRspVo {
+
+    private String message;
+
+}

+ 9 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/ISmsbDeviceService.java

@@ -1,8 +1,10 @@
 package com.inspur.device.service;
 
 import com.inspur.device.domain.SmsbDevice;
+import com.inspur.device.domain.bo.HttpHeartbeatReq;
 import com.inspur.device.domain.bo.SmsbDeviceBo;
 import com.inspur.device.domain.vo.DeviceStatisticsVo;
+import com.inspur.device.domain.vo.HttpHeartbeatRspVo;
 import com.inspur.device.domain.vo.SmsbDeviceRunInfoVo;
 import com.inspur.device.domain.vo.SmsbDeviceVo;
 import org.dromara.common.core.domain.R;
@@ -131,4 +133,11 @@ public interface ISmsbDeviceService {
      * @return
      */
     R<Void> screenshotUpload(String identifier, Long timestamp, MultipartFile file);
+
+    /**
+     * 前端设备心跳接口
+     * @param requestParam
+     * @return
+     */
+    R<HttpHeartbeatRspVo> heartbeat(HttpHeartbeatReq requestParam);
 }

+ 25 - 7
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/impl/SmsbDeviceServiceImpl.java

@@ -9,13 +9,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.common.hash.Hashing;
 import com.inspur.device.domain.SmsbDevice;
 import com.inspur.device.domain.SmsbDeviceRunInfo;
+import com.inspur.device.domain.bo.HttpHeartbeatReq;
 import com.inspur.device.domain.bo.SmsbDeviceBo;
 import com.inspur.device.domain.constants.DeviceConstants;
 import com.inspur.device.domain.constants.ResultCodeEnum;
-import com.inspur.device.domain.vo.DeviceStatisticsVo;
-import com.inspur.device.domain.vo.SmsbDeviceManufacturerVo;
-import com.inspur.device.domain.vo.SmsbDeviceRunInfoVo;
-import com.inspur.device.domain.vo.SmsbDeviceVo;
+import com.inspur.device.domain.vo.*;
 import com.inspur.device.mapper.SmsbDeviceMapper;
 import com.inspur.device.mapper.SmsbDeviceRunInfoMapper;
 import com.inspur.device.service.ISmsbDeviceManufacturerService;
@@ -24,6 +22,7 @@ import com.inspur.device.service.ISmsbProductService;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
 import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
@@ -117,6 +116,7 @@ public class SmsbDeviceServiceImpl implements ISmsbDeviceService {
 
     /**
      * 根据租户ID查询设备列表
+     *
      * @param bo
      * @param pageQuery
      * @return
@@ -124,7 +124,7 @@ public class SmsbDeviceServiceImpl implements ISmsbDeviceService {
     @Override
     public TableDataInfo<SmsbDeviceVo> queryPageListByTenantId(SmsbDeviceBo bo, PageQuery pageQuery) {
         LambdaQueryWrapper<SmsbDevice> lqw = buildQueryWrapper(bo);
-        Page<SmsbDeviceVo> result = baseMapper.queryPageListByTenantId(pageQuery.build(),lqw,bo.getTenantId());
+        Page<SmsbDeviceVo> result = baseMapper.queryPageListByTenantId(pageQuery.build(), lqw, bo.getTenantId());
         return TableDataInfo.build(result);
     }
 
@@ -209,7 +209,7 @@ public class SmsbDeviceServiceImpl implements ISmsbDeviceService {
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(SmsbDevice entity){
+    private void validEntityBeforeSave(SmsbDevice entity) {
         // 做一些数据校验,如唯一约束
         boolean flag = this.checkExist(entity.getSerialNumber(), entity.getMac());
         if (flag) {
@@ -248,7 +248,7 @@ public class SmsbDeviceServiceImpl implements ISmsbDeviceService {
     @Cacheable(cacheNames = "global:msr:device:identifier", key = "#identifier")
     public SmsbDeviceVo getDeviceByIdentifier(String identifier) {
         return baseMapper.selectVoOne(new LambdaQueryWrapper<SmsbDevice>()
-                .eq(SmsbDevice::getIdentifier, identifier));
+            .eq(SmsbDevice::getIdentifier, identifier));
     }
 
     @Override
@@ -330,4 +330,22 @@ public class SmsbDeviceServiceImpl implements ISmsbDeviceService {
         messageDto.setUserIds(List.of(userId));
         SseMessageUtils.publishMessage(messageDto);
     }
+
+    @Override
+    public R<HttpHeartbeatRspVo> heartbeat(HttpHeartbeatReq requestParam) {
+        SmsbDeviceVo smsbDeviceVo = getDeviceByIdentifier(requestParam.getIdentifier());
+        if (smsbDeviceVo == null) {
+            return R.fail("设备不存在");
+        }
+        Long currentTime = System.currentTimeMillis();
+        // 1 缓存设备最后一次http 心跳时间
+        RedisUtils.setCacheObject(DeviceConstants.REDIS_DEVICE_HEARTBEAT_HTTP_KEY + requestParam.getIdentifier(), currentTime);
+        // 2 更新设备状态 若设备未在线 更新为在线
+        if (smsbDeviceVo.getOnlineStatus().equals(DeviceConstants.DEVICE_STATUS_ONLINE.longValue())) {
+            smsbDeviceVo.setOnlineStatus(DeviceConstants.DEVICE_STATUS_ONLINE.longValue());
+            smsbDeviceVo.setLastOnline(DateUtils.getNowDate());
+            updateDeviceStatus(smsbDeviceVo);
+        }
+        return R.ok();
+    }
 }

+ 1 - 1
smsb-modules/smsb-job/src/main/java/org/dromara/job/snailjob/DeviceStatusReviewExecutor.java

@@ -31,7 +31,7 @@ public class DeviceStatusReviewExecutor {
     /**
      * redis netty client last heartbeat key
      */
-    public static final String DEVICE_LAST_HEART_PREFIX = "device:heart:last:";
+    public static final String DEVICE_LAST_HEART_PREFIX = "global:device:heartbeat:netty:";
 
 
     @Autowired

+ 5 - 0
smsb-modules/smsb-netty/src/main/java/com/inspur/netty/message/push/PushMessageType.java

@@ -21,6 +21,11 @@ public enum PushMessageType {
      */
     CONTENT_UPDATE("/content/update"),
 
+    /**
+     * 内容发布下架
+     */
+    CONTENT_REMOVE("/content/remove"),
+
     /**
      * 下发状态更新 replay
      */

+ 1 - 1
smsb-modules/smsb-netty/src/main/java/com/inspur/netty/util/NettyConstants.java

@@ -44,6 +44,6 @@ public class NettyConstants {
     /**
      * redis netty client last heartbeat key
      */
-    public static final String DEVICE_LAST_HEART_PREFIX = "device:heart:last:";
+    public static final String DEVICE_LAST_HEART_PREFIX = "global:device:heartbeat:netty:";
 
 }

+ 13 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbFrontController.java

@@ -1,6 +1,8 @@
 package com.inspur.source.controller;
 
 import cn.dev33.satoken.annotation.SaIgnore;
+import com.inspur.device.domain.bo.HttpHeartbeatReq;
+import com.inspur.device.domain.vo.HttpHeartbeatRspVo;
 import com.inspur.device.domain.vo.SmsbOtaRecordVo;
 import com.inspur.device.service.ISmsbDeviceService;
 import com.inspur.device.service.ISmsbOtaRecordService;
@@ -82,4 +84,15 @@ public class SmsbFrontController {
         return smsbDeviceService.screenshotUpload(identifier,timestamp, file);
     }
 
+    /**
+     * 前端设备心跳接口
+     *
+     * @param requestParam 主键
+     */
+    @SaIgnore
+    @PostMapping("/heartbeat")
+    public R<HttpHeartbeatRspVo> heartbeat(@RequestBody HttpHeartbeatReq requestParam) {
+        return smsbDeviceService.heartbeat(requestParam);
+    }
+
 }

+ 11 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbItemPushDeviceController.java

@@ -104,4 +104,15 @@ public class SmsbItemPushDeviceController extends BaseController {
                           @PathVariable Long[] ids) {
         return toAjax(smsbItemPushDeviceService.deleteWithValidByIds(List.of(ids), true));
     }
+
+    /**
+     * 内容发布设备下架
+     *
+     * @param bo
+     */
+    @Log(title = "内容发布下架", businessType = BusinessType.DELETE)
+    @DeleteMapping("/remove")
+    public R<Void> pushRemoveDevice(@RequestBody SmsbItemPushDeviceBo bo) {
+        return smsbItemPushDeviceService.pushRemoveDevice(bo);
+    }
 }

+ 3 - 2
smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbMinioDataController.java

@@ -82,7 +82,7 @@ public class SmsbMinioDataController extends BaseController {
     }
 
     /**
-     * 上传OSS对象存储
+     * 上传OSS对象存储 (舍弃)
      *
      * @param file 文件
      */
@@ -92,7 +92,8 @@ public class SmsbMinioDataController extends BaseController {
         if (ObjectUtil.isNull(file)) {
             return R.fail("上传文件不能为空");
         }
-        SysOssVo oss = smsbMinioDataService.uploadSmsb(file);
+        // SysOssVo oss = smsbMinioDataService.uploadSmsb(file);
+        SysOssVo oss = new SysOssVo();
         if (null == oss) {
             return R.fail("上传失败,已存在相同文件");
         }

+ 4 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/domain/bo/SmsbItemPushDeviceBo.java

@@ -6,6 +6,8 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 
+import java.util.List;
+
 /**
  * 内容发布设备业务对象 smsb_item_push_device
  *
@@ -42,5 +44,7 @@ public class SmsbItemPushDeviceBo extends BaseEntity {
      */
     private Long pushState;
 
+    private List<Long> deviceIds;
+
 
 }

+ 8 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/ISmsbItemPushDeviceService.java

@@ -2,6 +2,7 @@ package com.inspur.source.service;
 
 import com.inspur.source.domain.bo.SmsbItemPushDeviceBo;
 import com.inspur.source.domain.vo.SmsbItemPushDeviceVo;
+import org.dromara.common.core.domain.R;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 
@@ -68,4 +69,11 @@ public interface ISmsbItemPushDeviceService {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 内容发布下架
+     * @param bo
+     * @return
+     */
+    public R<Void> pushRemoveDevice(SmsbItemPushDeviceBo bo);
 }

+ 44 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbItemPushDeviceServiceImpl.java

@@ -1,17 +1,28 @@
 package com.inspur.source.service.impl;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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.inspur.device.domain.constants.ResultCodeEnum;
+import com.inspur.device.domain.vo.SmsbDeviceVo;
+import com.inspur.device.service.ISmsbDeviceService;
+import com.inspur.netty.message.push.PushMessageType;
+import com.inspur.netty.util.PushMsgUtil;
 import com.inspur.source.domain.SmsbItemPushDevice;
 import com.inspur.source.domain.bo.SmsbItemPushDeviceBo;
 import com.inspur.source.domain.vo.SmsbItemPushDeviceVo;
 import com.inspur.source.mapper.SmsbItemPushDeviceMapper;
 import com.inspur.source.service.ISmsbItemPushDeviceService;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.utils.DateUtils;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
@@ -26,10 +37,14 @@ import java.util.Map;
  */
 @RequiredArgsConstructor
 @Service
+@Slf4j
 public class SmsbItemPushDeviceServiceImpl implements ISmsbItemPushDeviceService {
 
     private final SmsbItemPushDeviceMapper baseMapper;
 
+    @Autowired
+    private ISmsbDeviceService smsbDeviceService;
+
     /**
      * 查询内容发布设备
      *
@@ -136,4 +151,33 @@ public class SmsbItemPushDeviceServiceImpl implements ISmsbItemPushDeviceService
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    public R<Void> pushRemoveDevice(SmsbItemPushDeviceBo bo) {
+        if (CollectionUtil.isEmpty(bo.getDeviceIds())) {
+            return R.fail(Integer.parseInt(ResultCodeEnum.DEVICE_IDS_IS_NULL.getValue()), ResultCodeEnum.DEVICE_IDS_IS_NULL.getMessage());
+        }
+        Integer pushState = 4;
+        for (Long deviceId : bo.getDeviceIds()) {
+            baseMapper.update(new LambdaUpdateWrapper<SmsbItemPushDevice>()
+                .set(SmsbItemPushDevice::getPushState, pushState)
+                .set(SmsbItemPushDevice::getUpdateTime, DateUtils.getNowDate())
+                .eq(SmsbItemPushDevice::getDeviceId, deviceId)
+                .eq(SmsbItemPushDevice::getPushId, bo.getPushId()));
+            // 发送长连接消息给设备
+            pushRemoveMessage(deviceId, bo.getPushId());
+        }
+        return R.ok();
+    }
+
+    private void pushRemoveMessage(Long deviceId, Long pushId) {
+        SmsbDeviceVo deviceVo = smsbDeviceService.getDeviceCacheById(deviceId);
+        if (null == deviceVo) {
+            return;
+        }
+        String identifier = deviceVo.getIdentifier();
+        String nettyMessage = PushMessageType.CONTENT_REMOVE.getValue() + "/" + pushId;
+        boolean pushResult = PushMsgUtil.sendV2(identifier, nettyMessage);
+        log.info("push content remove identifier: {}, result:{}", identifier, pushResult);
+    }
 }

+ 2 - 1
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbMinioDataServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.idrsolutions.image.png.PngCompressor;
+import com.inspur.device.domain.constants.ResultCodeEnum;
 import com.inspur.source.domain.*;
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
 import com.inspur.source.domain.vo.*;
@@ -828,7 +829,7 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         // 根据Md5查询当前租户下是否存在相同文件
         SysOssVo sysOss = sysOssMapper.selectVoOne(new LambdaQueryWrapper<SysOss>().eq(SysOss::getFileMd5, md5));
         if (sysOss != null) {
-            return null;
+            throw new ServiceException(ResultCodeEnum.UPLOAD_REPEAT.getMessage(), Integer.parseInt(ResultCodeEnum.UPLOAD_REPEAT.getValue()));
         }
         String originalfileName = file.getOriginalFilename();
         String suffix = StringUtils.substring(originalfileName,

+ 3 - 3
smsb-plus-ui/src/api/smsb/device/device_run_type.ts

@@ -15,17 +15,17 @@ export interface DeviceRunInfoVO {
   /**
    * CPU
    */
-  cpuInfo: string;
+  cpuInfo: number;
 
   /**
    * 内存
    */
-  memoryInfo: string;
+  memoryInfo: number;
 
   /**
    * 硬盘
    */
-  diskInfo: string;
+  diskInfo: number;
 
   /**
    * rom版本

+ 8 - 0
smsb-plus-ui/src/api/smsb/source/item_push_device.ts

@@ -24,6 +24,14 @@ export const listItemPushDeviceV2 = (query?: ItemPushDeviceQuery): AxiosPromise<
   });
 };
 
+export const removeItemPushDevice = (param: ItemPushDeviceVO) => {
+  return request({
+    url: '/source/itemPushDevice/remove',
+    method: 'delete',
+    data: param
+  });
+};
+
 /**
  * 查询内容发布设备详细
  * @param id

+ 4 - 0
smsb-plus-ui/src/api/smsb/source/item_push_device_type.ts

@@ -33,6 +33,10 @@ export interface ItemPushDeviceVO {
    * 更新时间
    */
   updateTime: string;
+
+  pushId: number | string;
+
+  deviceIds: Array<number | string>;
 }
 
 export interface ItemPushDeviceForm extends BaseEntity {

+ 25 - 11
smsb-plus-ui/src/views/smsb/device/index.vue

@@ -199,18 +199,32 @@
       <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClickTab">
         <el-tab-pane label="状态检测" name="info">
           <div>
-            <el-row :gutter="20" style="height: 100%; display: flex">
-              <el-col :span="8">
-                <div class="grid-content">
-                  <div>CPU : {{ deviceRunInfo.cpuInfo }}</div>
-                  <div>内存 : {{ deviceRunInfo.memoryInfo }}</div>
-                  <div>硬盘 : {{ deviceRunInfo.diskInfo }}</div>
-                  <div>ROM : {{ deviceRunInfo.rom }}</div>
-                  <div>系统信息 : {{ deviceRunInfo.osInfo }}</div>
-                  <div>APK版本 : {{ deviceRunInfo.apkVersion }}</div>
+            <div style="display: flex; justify-content: space-around; align-items: center">
+              <div style="text-align: center">
+                <div style="position: relative">
+                  CPU :
+                  <el-progress type="circle" :percentage="deviceRunInfo.cpuInfo"></el-progress>
+                  <div style="position: absolute; top: -30px; left: 50%; transform: translateX(-50%)">{{ deviceRunInfo.cpuInfo }}%</div>
                 </div>
-              </el-col>
-            </el-row>
+              </div>
+              <div style="text-align: center">
+                <div style="position: relative">
+                  内存 :
+                  <el-progress type="circle" :percentage="deviceRunInfo.memoryInfo"></el-progress>
+                  <div style="position: absolute; top: -30px; left: 50%; transform: translateX(-50%)">{{ deviceRunInfo.memoryInfo }}%</div>
+                </div>
+              </div>
+              <div style="text-align: center">
+                <div style="position: relative">
+                  硬盘 :
+                  <el-progress type="circle" :percentage="deviceRunInfo.diskInfo"></el-progress>
+                  <div style="position: absolute; top: -30px; left: 50%; transform: translateX(-50%)">{{ deviceRunInfo.diskInfo }}%</div>
+                </div>
+              </div>
+            </div>
+            <div>ROM : {{ deviceRunInfo.rom }}</div>
+            <div>系统信息 : {{ deviceRunInfo.osInfo }}</div>
+            <div>APK版本 : {{ deviceRunInfo.apkVersion }}</div>
           </div>
         </el-tab-pane>
         <el-tab-pane label="远程操作" name="control">

+ 109 - 55
smsb-plus-ui/src/views/smsb/itemPush/index.vue

@@ -106,12 +106,22 @@
             <el-tooltip content="删除" placement="top">
               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['source:itemPush:remove']"></el-button>
             </el-tooltip>
-<!--            <el-tooltip content="提交审核" placement="top">
-              <el-button link type="primary" icon="Top" @click="handleReview(scope.row)" v-hasPermi="['source:minioData:add']"></el-button>
-            </el-tooltip>-->
+            <!--            <el-tooltip content="提交审核" placement="top">
+                          <el-button link type="primary" icon="Top" @click="handleReview(scope.row)" v-hasPermi="['source:minioData:add']"></el-button>
+                        </el-tooltip>-->
             <el-tooltip content="下发详情" placement="top">
               <el-button link type="primary" icon="List" @click="handleDevice(scope.row)" v-hasPermi="['source:itemPush:edit']"></el-button>
             </el-tooltip>
+            <el-tooltip content="下架" placement="top">
+              <el-button
+                link
+                type="primary"
+                icon="Bottom"
+                v-if="scope.row.status == 'finish'"
+                @click="handleRemoveItem(scope.row)"
+                v-hasPermi="['source:itemPush:edit']"
+              ></el-button>
+            </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
@@ -199,9 +209,9 @@
               </el-table-column>
               <el-table-column label="名称" align="left" prop="itemName" :show-overflow-tooltip="true" />
               <el-table-column label="资源数量" align="center" prop="sourceNum" width="80" />
-  <!--
-                <el-table-column label="创建人" align="left" prop="createUser" width="100" :show-overflow-tooltip="true" />
-  -->
+              <!--
+                            <el-table-column label="创建人" align="left" prop="createUser" width="100" :show-overflow-tooltip="true" />
+              -->
             </el-table>
           </div>
         </el-col>
@@ -216,51 +226,50 @@
 
     <!-- 下发详情弹窗 -->
     <el-dialog :title="deviceDialog.title" v-model="deviceDialog.visible" width="900px" append-to-body>
-      <el-row :gutter="20" style="height: 100%; display: flex">
-        <!--发布名称-->
-        <el-col :span="10" style="height: 100%; overflow: auto; padding-left: 10px">
-          <el-form ref="itemPushFormRef" :model="form" :rules="rules" label-width="60px">
-            <el-form-item label="名称" prop="name">
-              <el-input v-model="form.name" placeholder="请输入名称" :disabled="true" />
-            </el-form-item>
-            <el-row>
-              <el-col :span="12">
-                <el-form-item label="类型" prop="itemType">
-                  <el-select v-model="form.itemType" placeholder="请选择类型" @change="getItemList" :disabled="true">
-                    <el-option v-for="dict in smsb_push_type" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
-                <el-form-item label="优先级" prop="level">
-                  <el-select v-model="form.level" placeholder="请选择优先级" :disabled="true">
-                    <el-option v-for="dict in smsb_push_level" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-col>
-        <!--设备列表-->
-        <el-col :span="14" style="height: 100%; overflow: auto; border-right: 1px solid #eee; padding-right: 10px">
-          <el-table v-loading="loading" :data="pushDeviceList">
-            <el-table-column label="设备名称" align="left" prop="deviceName" :show-overflow-tooltip="true" />
-            <el-table-column label="发布排序" align="center" prop="sortNum" width="100" :show-overflow-tooltip="true" />
-            <el-table-column label="发布状态" align="center" prop="pushState" width="120" :show-overflow-tooltip="true">
-              <template #default="scope">
-                <dict-tag :options="smsb_push_device_state" :value="scope.row.pushState" />
-              </template>
-            </el-table-column>
-          </el-table>
-          <pagination
-            v-show="deviceTotal > 0"
-            :total="deviceTotal"
-            v-model:page="dialogQueryParams.pageNum"
-            v-model:limit="dialogQueryParams.pageSize"
-            @pagination="getList"
-          />
-        </el-col>
-      </el-row>
+      <!--发布名称-->
+      <div>
+        <el-form ref="itemPushFormRef" :model="form" :rules="rules" label-width="70px">
+          <el-form-item label="发布名称" prop="name">
+            <el-input v-model="form.name" style="width: 500px" placeholder="请输入名称" :disabled="true" />
+          </el-form-item>
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="发布类型" prop="itemType">
+                <el-select v-model="form.itemType" placeholder="请选择类型" @change="getItemList" :disabled="true">
+                  <el-option v-for="dict in smsb_push_type" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="优先级" prop="level">
+                <el-select v-model="form.level" placeholder="请选择优先级" :disabled="true">
+                  <el-option v-for="dict in smsb_push_level" :key="dict.value" :label="dict.label" :value="parseInt(dict.value)"></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </div>
+      <!--设备列表-->
+      <el-button type="primary" v-if="removeItemTag" icon="Bottom" :disabled="removeButtonTag" @click="handleDoRemoveItem()"> 下架 </el-button>
+      <el-table v-loading="deviceLoading" :data="pushDeviceList" style="height: 500px; margin-top: 10px" @selection-change="handleDeviceSelectionChange">
+        <el-table-column type="selection" v-if="removeItemTag" width="55" align="center" />
+        <el-table-column label="设备名称" align="left" prop="deviceName" :show-overflow-tooltip="true" />
+        <el-table-column label="发布排序" align="center" prop="sortNum" width="100" :show-overflow-tooltip="true" />
+        <el-table-column label="发布状态" align="center" prop="pushState" width="120" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <dict-tag :options="smsb_push_device_state" :value="scope.row.pushState" />
+          </template>
+        </el-table-column>
+        <el-table-column label="发布时间" align="left" prop="createTime" width="170" :show-overflow-tooltip="true" />
+        <el-table-column label="操作" v-if="removeItemTag" align="center" class-name="small-padding fixed-width" width="150">
+          <template #default="scope">
+            <el-tooltip content="下架" placement="top">
+              <el-button link type="primary" icon="Bottom" @click="handleDoRemoveItem(scope.row)" v-hasPermi="['source:itemPush:edit']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="cancel">取 消</el-button>
@@ -278,7 +287,7 @@ import { listDevice } from '@/api/smsb/device/device';
 import { ItemQuery, ItemVO } from '@/api/smsb/source/item_type';
 import { listItem } from '@/api/smsb/source/item';
 import { ItemPushDeviceVO } from '@/api/smsb/source/item_push_device_type';
-import { listItemPushDeviceV2 } from '@/api/smsb/source/item_push_device';
+import { listItemPushDeviceV2, removeItemPushDevice } from '@/api/smsb/source/item_push_device';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { smsb_push_state, smsb_push_device_state, smsb_push_type, smsb_push_level } = toRefs<any>(
@@ -289,8 +298,11 @@ const itemPushList = ref<ItemPushVO[]>([]);
 const itemList = ref<ItemVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
+const deviceLoading = ref(true);
 const showSearch = ref(true);
 const ids = ref<Array<string | number>>([]);
+const removePushId = ref<number | string>();
+const removeIds = ref<Array<string | number>>([]);
 const deviceIds = ref<Array<string | number>>([]);
 const itemIds = ref<Array<string | number>>([]);
 const single = ref(true);
@@ -307,7 +319,8 @@ const deviceList = ref<DeviceVO[]>([]);
 const pushDeviceList = ref<ItemPushDeviceVO[]>([]);
 // 存储选中行的唯一标识
 const selectedRowId = ref<number | null>(null);
-
+const removeItemTag = ref(false);
+const removeButtonTag = ref(true);
 const queryFormRef = ref<ElFormInstance>();
 const itemPushFormRef = ref<ElFormInstance>();
 
@@ -434,21 +447,57 @@ const handleQuery = () => {
 };
 
 const handleDevice = async (row?: ItemPushVO) => {
+  removeItemTag.value = false;
   const pushId = row.id;
   deviceDialog.title = '发布详情';
   deviceDialog.visible = true;
   dialogQueryParams.value.pushId = pushId;
+  dialogQueryParams.value.pageSize = 1000;
+  pushDeviceList.value = [];
+  const pushInfo = await getItemPush(pushId);
+  Object.assign(form.value, pushInfo.data);
+  await getItemDeviceList(pushId);
+};
+const handleRemoveItem = async (row?: ItemPushVO) => {
+  removeItemTag.value = true;
+  removeButtonTag.value = true;
+  removeIds.value = [];
+  const pushId = row.id;
+  deviceDialog.title = '内容下架';
+  deviceDialog.visible = true;
+  dialogQueryParams.value.pushId = pushId;
+  removePushId.value = pushId;
+  dialogQueryParams.value.pageSize = 1000;
+  pushDeviceList.value = [];
   const pushInfo = await getItemPush(pushId);
   Object.assign(form.value, pushInfo.data);
-  getItemDeviceList(pushId);
+  await getItemDeviceList(pushId);
+};
+const handleDoRemoveItem = async (row?: ItemPushDeviceVO) => {
+  const deviceIds = Array<number | string>();
+  if (null != row) {
+    deviceIds.push(row.deviceId);
+  }
+  if (removeIds.value.length > 0) {
+    removeIds.value.forEach((deviceId) => {
+      deviceIds.push(deviceId);
+    });
+  }
+  const removeParam = {
+    pushId: removePushId.value,
+    deviceIds: deviceIds
+  };
+  await removeItemPushDevice(removeParam);
+  await getItemDeviceList(removePushId.value);
 };
 
 const getItemDeviceList = async (pushId: number | string) => {
+  deviceLoading.value = true;
   dialogQueryParams.value.pushId = pushId;
   const res = await listItemPushDeviceV2(dialogQueryParams.value);
   pushDeviceList.value = res.rows;
   deviceTotal.value = res.total;
-  loading.value = false;
+  deviceLoading.value = false;
 };
 
 /** 重置按钮操作 */
@@ -465,6 +514,11 @@ const handleSelectionChange = (selection: ItemPushVO[]) => {
   multiple.value = !selection.length;
 };
 
+const handleDeviceSelectionChange = (selection: ItemPushDeviceVO[]) => {
+  removeIds.value = selection.map((item) => item.deviceId);
+  removeButtonTag.value = removeIds.value.length == 0;
+};
+
 /** 多选框选中数据 */
 const handleSelectDevice = (selection: DeviceVO[]) => {
   deviceIds.value = selection.map((item) => item.id);

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 225
smsb-plus-ui/vite.config.ts.timestamp-1737700502429-c6453b1b35b5e.mjs


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 225
smsb-plus-ui/vite.config.ts.timestamp-1738734766950-3118c61dd84be.mjs


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 225
smsb-plus-ui/vite.config.ts.timestamp-1740461761631-438ca296213cc.mjs


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor