瀏覽代碼

Merge branch 'master' of https://git.woshs.com/914769835/water_bus

shs 1 年之前
父節點
當前提交
33520f818e

+ 6 - 1
aidex-common/src/main/java/com/aidex/common/constant/Constants.java

@@ -216,5 +216,10 @@ public class Constants {
     /**
      * 航线存储前缀
      */
-public static final String ROUTE_STORE_PREFIX =  "route_data:";
+    public static final String ROUTE_STORE_PREFIX =  "route_data:";
+
+    /**
+     * 摄像头access_token 存储前缀
+     */
+    public static final String CAMERA_ACCESS_TOKEN_PREFIX= "camera_token:";
 }

+ 33 - 0
aidex-common/src/main/java/com/aidex/common/utils/CryptoHelper.java

@@ -0,0 +1,33 @@
+package com.aidex.common.utils;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.Charset;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author :ChenSir
+ * @date :Created in 2024/3/27 14:56
+ * @description:签名验证
+ */
+public class CryptoHelper {
+	public static String byteToHexString(byte[] bytes) {
+				StringBuilder hexString = new StringBuilder();
+				for (byte b : bytes) {
+						String hex = Integer.toHexString(0xFF & b);
+						if (hex.length() == 1) {
+								hexString.append('0');
+							}
+						hexString.append(hex);
+					}
+				return hexString.toString();
+			}
+	public static String hmacSha1(String key, String data, Charset bytesEncode)throws NoSuchAlgorithmException, InvalidKeyException {
+				SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(bytesEncode), "HmacSHA1");
+				Mac mac = Mac.getInstance("HmacSHA1");
+				mac.init(signingKey);
+				//通用方法,自己实现即可(由 byte[]转 hex string)
+						return byteToHexString(mac.doFinal(data.getBytes(bytesEncode)));
+			}
+}

+ 34 - 4
aidex-controller/src/main/java/com/aidex/web/controller/app/AppController.java

@@ -7,10 +7,8 @@ import com.aidex.common.app.server.IAppService;
 import com.aidex.common.core.domain.R;
 import com.aidex.common.core.page.PageDomain;
 import com.aidex.common.gps.domain.LocationEntity;
-import com.aidex.system.domain.SysConfig;
-import com.aidex.system.domain.SysDictData;
-import com.aidex.system.domain.SysNotice;
-import com.aidex.system.domain.SysWharf;
+import com.aidex.quartz.task.RefreshAccessTokenTask;
+import com.aidex.system.domain.*;
 import com.aidex.system.domain.vo.SysRouteVo;
 import com.aidex.system.service.*;
 import com.github.pagehelper.PageInfo;
@@ -18,10 +16,15 @@ import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
+import java.util.Map;
 
 
 @RestController
@@ -29,6 +32,8 @@ import java.util.List;
 @Tag(name = "小程序相关接口", description = "小程序管调用接口")
 public class AppController {
 
+    protected static final Logger logger = LoggerFactory.getLogger(AppController.class);
+
 
     @Autowired
     private IAppService iAppService;
@@ -139,4 +144,29 @@ public class AppController {
         return R.data(noticeService.findPage(sysNotice));
     }
 
+    @PostMapping("/message")
+    public R<PageInfo> getMessage(@RequestBody Message message, HttpServletRequest request, HttpServletResponse response)
+    {
+        // 验证签名时使用
+        // String timestamp = request.getHeader("timestamp");
+        // String signature = request.getHeader("signature");
+        // String type = request.getHeader("type");
+        // String device_sn = request.getHeader("device_sn");
+        // String message_id = request.getHeader("message_id");
+        Map<String, Object> extend = message.getExtend();
+        if (null != extend) {
+            if (extend.containsKey("count")) {
+                int[] alarm_count = (int[]) extend.get("count");
+                if (alarm_count.length > 0){
+                    logger.info("webhook消息推送,告警人数----人数---{}", alarm_count[0]);
+                    logger.info("webhook消息推送,请求头----时间戳---{}", message.getTimestamp());
+                    logger.info("webhook消息推送,请求头----设备号---{}", message.getDevice_sn());
+
+
+                }
+            }
+        }
+        return R.data(null);
+    }
+
 }

+ 180 - 0
aidex-controller/src/main/java/com/aidex/web/controller/camera/SysCameraController.java

@@ -0,0 +1,180 @@
+package com.aidex.web.controller.camera;
+
+import cn.hutool.http.HttpRequest;
+import com.aidex.common.annotation.Log;
+import com.aidex.common.constant.Constants;
+import com.aidex.common.core.controller.BaseController;
+import com.aidex.common.core.domain.BaseEntity;
+import com.aidex.common.core.domain.R;
+import com.aidex.common.core.page.PageDomain;
+import com.aidex.common.core.redis.RedisCache;
+import com.aidex.common.enums.BusinessType;
+import com.aidex.common.utils.poi.ExcelUtil;
+import com.aidex.framework.cache.ConfigUtils;
+import com.aidex.framework.cache.DictUtils;
+import com.aidex.system.domain.SysCamera;
+import com.aidex.system.domain.SysConfig;
+import com.aidex.system.domain.SysWharf;
+import com.aidex.system.service.SysWharfService;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.github.pagehelper.PageInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 摄像头管理Controller
+ * @author ChenSir
+ * @email 914769835
+ * @date 2024-03-16
+ */
+@RestController
+@RequestMapping("/system/camera")
+public class SysCameraController extends BaseController {
+
+    @Autowired
+    private SysWharfService sysWharfService;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 查询摄像头管理列表
+     */
+    @GetMapping("/list")
+    public R list(SysCamera sysCamera, HttpServletRequest request, HttpServletResponse response) {
+        String result = "";
+        sysCamera.setPage(new PageDomain(request, response));
+        String accessToken = redisCache.getStringValue(Constants.CAMERA_ACCESS_TOKEN_PREFIX);
+        SysConfig configUrl = ConfigUtils.getConfigByKey("sys.monitor.camera.url");
+        JSONObject params = new JSONObject();
+        params.put("pageNum", sysCamera.getPage().getPageNum());
+        params.put("pageSize", sysCamera.getPage().getPageSize());
+        params.put("accessToken", accessToken);
+        /*JSONObject params1 = JSONObject.parseObject(JSON.toJSONString(sysCamera));
+        params.putAll(params1);*/
+        if (configUrl != null){
+            String url = configUrl.getConfigValue();
+            //链式构建请求
+            result = HttpRequest.post(url + "/api/device-service/lapp/v1/camera/list")
+                    .body(params.toJSONString())//表单内容
+                    .timeout(50000)//超时,毫秒
+                    .execute().body();
+            R resultR = JSONObject.parseObject(result, R.class);
+            return resultR;
+        }
+        return R.data(null);
+    }
+
+    /**
+     * 查询码头管理列表
+     */
+    @PostMapping("/openLive")
+    public R openLive(@RequestBody @Validated  SysCamera sysCamera) {
+        String result = "";
+        String accessToken = redisCache.getStringValue(Constants.CAMERA_ACCESS_TOKEN_PREFIX);
+        SysConfig configUrl = ConfigUtils.getConfigByKey("sys.monitor.camera.url");
+        JSONObject params = new JSONObject();
+        params.put("deviceSn", sysCamera.getDeviceSn());
+        params.put("channelNo", sysCamera.getChannelNo());
+        // 有效期 30天 可以取30s-360d
+        params.put("expireTime", 60*60*24*30);
+        // 流播放协议,0-rtc、1-hls、2-rtsp、3-rtmp、4-flv,默认为 0
+        params.put("protocol", 4);
+        // 	视频清晰度,0-流畅(子码流)、1-高清(主码流)默认为 0
+        params.put("quality", 1);
+        params.put("accessToken", accessToken);
+        if (configUrl != null){
+            String url = configUrl.getConfigValue();
+            //链式构建请求
+            result = HttpRequest.post(url + "/api/device-service/lapp/v1/live/address/get")
+                    .body(params.toJSONString())//表单内容
+                    .timeout(50000)//超时,毫秒
+                    .execute().body();
+            R resultR = JSONObject.parseObject(result, R.class);
+            return resultR;
+        }
+        return R.data(null);
+    }
+
+    /**
+     * 获取码头管理详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:query')")
+    @GetMapping(value = "/{id}")
+    public R<SysWharf> detail(@PathVariable("id") String id) {
+        return R.data(sysWharfService.get(id));
+    }
+
+    /**
+     * 新增码头管理
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:add')")
+    @Log(title = "码头管理", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R add(@RequestBody @Validated  SysWharf sysWharf) {
+        return R.status(sysWharfService.save(sysWharf));
+    }
+
+    /**
+     * 修改码头管理
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:edit')")
+    @Log(title = "码头管理", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public R edit(@RequestBody @Validated SysWharf sysWharf) {
+        return R.status(sysWharfService.save(sysWharf));
+    }
+
+    /**
+     * 修改码头状态
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:edit')")
+    @Log(title = "码头管理", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateStatus")
+    public R updateStatus(@RequestBody @Validated SysWharf sysWharf) {
+        return R.status(sysWharfService.updateStatus(sysWharf));
+    }
+
+    /**
+     * 删除码头管理
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:remove')")
+    @Log(title = "码头管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R remove(@PathVariable String[] ids) {
+        return R.status(sysWharfService.deleteSysWharfByIds(ids));
+    }
+
+
+    /**
+     * 导出码头管理列表
+     */
+    @PreAuthorize("@ss.hasPermi('system:sysWharf:export')")
+    @Log(title = "码头管理", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public R export(SysWharf sysWharf) {
+        List<SysWharf> list = sysWharfService.findList(sysWharf);
+        ExcelUtil<SysWharf> util = new ExcelUtil<SysWharf>(SysWharf.class);
+        return util.exportExcel(list, "码头管理数据");
+    }
+
+    /**
+     * 根据字典类型查询字典数据信息等其他自定义信息
+     */
+    @GetMapping(value = "/getInitData/{dictTypes}")
+    public R getInitData(@PathVariable String dictTypes) {
+        Map<String, Object> dataMap = new HashMap<String, Object>();
+        dataMap.putAll(DictUtils.getMultiDictList(dictTypes));
+        return R.data(dataMap);
+    }
+
+}

+ 75 - 0
aidex-quartz/src/main/java/com/aidex/quartz/task/RefreshAccessTokenTask.java

@@ -0,0 +1,75 @@
+package com.aidex.quartz.task;
+
+import cn.hutool.http.HttpRequest;
+import com.aidex.common.constant.Constants;
+import com.aidex.common.constant.HttpStatus;
+import com.aidex.common.core.redis.RedisCache;
+import com.aidex.common.utils.StringUtils;
+import com.aidex.framework.cache.ConfigUtils;
+import com.aidex.system.domain.SysConfig;
+import com.alibaba.fastjson2.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author :ChenSir
+ * @date :Created in 2024/3/17 20:07
+ * @description:由于access_token令牌有时效性,某些场景(模块或页面)需要线程常驻,那么需要mds长期有效,由于接口调用可以顺延mds的时效,建议定时器每隔5分钟请求一次本接口,确保mds不会过期。
+ */
+@Component("RefreshAccessTokenTask")
+public class RefreshAccessTokenTask {
+
+	protected static final Logger logger = LoggerFactory.getLogger(RefreshAccessTokenTask.class);
+
+	@Autowired
+	private RedisCache redisCache;
+
+	private final String ACCESS_TOKEN_URL = "/api/user-service/lapp/v1/token/get";
+
+	/**
+	 * 刷新ACCESS_TOKEN
+	 * @return token
+	 */
+	public String refreshAccessToken(){
+		logger.info("刷新摄像头的ACCESS_TOKEN----------开始");
+		String token = "";
+		try{
+			SysConfig sysConfigAppKey = ConfigUtils.getConfigByKey("sys.monitor.camera.appKey");
+			SysConfig sysConfigSecret = ConfigUtils.getConfigByKey("sys.monitor.camera.secret");
+			SysConfig sysConfigUrl=  ConfigUtils.getConfigByKey("sys.monitor.camera.url");
+			if (sysConfigAppKey != null && sysConfigSecret != null && sysConfigUrl != null) {
+				String appKey = sysConfigAppKey.getConfigValue();
+				String secret = sysConfigSecret.getConfigValue();
+				String url = sysConfigUrl.getConfigValue();
+				JSONObject paramMap = new JSONObject();
+				paramMap.put("appKey", appKey);
+				paramMap.put("secret", secret);
+				//链式构建请求
+				String result = HttpRequest.post(url +ACCESS_TOKEN_URL).body(paramMap.toJSONString()).timeout(50000).execute().body();
+				if (result != null) {
+					JSONObject jsonObjec = JSONObject.parseObject(result);
+					if (HttpStatus.SUCCESS == jsonObjec.getIntValue("code")){
+						JSONObject accessToken = jsonObjec.getJSONObject("data");
+						if (accessToken != null && StringUtils.isNotEmpty(accessToken.getString("accessToken"))){
+							redisCache.setCacheObject(Constants.CAMERA_ACCESS_TOKEN_PREFIX, accessToken.getString("accessToken"), Constants.GPS_TOKEN_EXPIRATION, TimeUnit.DAYS);
+						}else {
+							logger.info("摄像头平台获取accessToken刷新结束,Http 摄像头平台获取accessToken初始化结束!-----{}", jsonObjec);
+						}
+					}else {
+						logger.info("摄像头平台获取accessToken刷新结束,Http 请求结果错误!-----{}", jsonObjec);
+					}
+				}
+			}
+		}catch (Exception e){
+			token = e.getLocalizedMessage();
+		}
+		logger.info("刷新摄像头的ACCESS_TOKEN----------结束");
+		return token;
+	}
+
+
+}

+ 9 - 13
aidex-system/src/main/java/com/aidex/common/app/server/impl/AppService.java

@@ -107,8 +107,6 @@ public class AppService implements IAppService {
 //            }
 //            locationList.add(locationVO);
 //        }
-        List<LocationEntity> noDir = locationEntities.stream().filter(local -> Double.valueOf(local.getDir()) <= 0).collect(Collectors.toList());
-        calcList.addAll(noDir);
         return  calcList;
     }
 
@@ -268,19 +266,17 @@ public class AppService implements IAppService {
 
             res.setDistSiteNum(distSiteNum);
 
-            if(res.getIsLongStop())
-                res.setStopWharf(wharfShip);
-            else {
-                if(shipSite == null && mySite != null){
-                    res.setStopWharf(wharf);
-                }else {
-                    res.setStopWharf(wharfShip);
-                }
-            }
-
             // 船只已经停靠入站离我最近的站
             if (isStopOperation && (mySite != null || shipSite != null)) {
-
+                if(res.getIsLongStop())
+                    res.setStopWharf(wharfShip);
+                else {
+                    if(shipSite == null && mySite != null){
+                        res.setStopWharf(wharf);
+                    }else {
+                        res.setStopWharf(wharfShip);
+                    }
+                }
                 res.setIsArrival(Boolean.TRUE);
                 // 如果离我最近与停靠站点不是同一站点则计算时间
                 if(!wharfShip.getWharfNanme().equals(wharf.getWharfNanme())) {

+ 37 - 0
aidex-system/src/main/java/com/aidex/framework/cache/MyInitializer.java

@@ -1,7 +1,9 @@
 package com.aidex.framework.cache;
 
 import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
 import com.aidex.common.constant.Constants;
+import com.aidex.common.constant.HttpStatus;
 import com.aidex.common.core.redis.RedisCache;
 import com.aidex.common.utils.StringUtils;
 import com.aidex.system.domain.SysConfig;
@@ -32,6 +34,8 @@ public class MyInitializer {
 
 	private final String ACTION_URL = "/GetDataService.aspx";
 
+	private final String ACCESS_TOKEN_URL = "/api/user-service/lapp/v1/token/get";
+
 	@PostConstruct
 	public void init() {
 		logger.info("Gps平台获取token初始化开始");
@@ -71,6 +75,39 @@ public class MyInitializer {
 			}
 		}
 		logger.info("Gps平台获取token初始化结束");
+
+		logger.info("|----------------------------------------初始化摄像头请求token-----------------------------------------|");
+
+
+		logger.info("摄像头平台获取accessToken初始化开始");
+		SysConfig sysConfigAppKey = ConfigUtils.getConfigByKey("sys.monitor.camera.appKey");
+		SysConfig sysConfigSecret = ConfigUtils.getConfigByKey("sys.monitor.camera.secret");
+		SysConfig sysConfigUrl=  ConfigUtils.getConfigByKey("sys.monitor.camera.url");
+		if (sysConfigAppKey != null && sysConfigSecret != null && sysConfigUrl != null) {
+			String appKey = sysConfigAppKey.getConfigValue();
+			String secret = sysConfigSecret.getConfigValue();
+			String url = sysConfigUrl.getConfigValue();
+			JSONObject paramMap = new JSONObject();
+			paramMap.put("appKey", appKey);
+			paramMap.put("secret", secret);
+			//链式构建请求
+			String result = HttpRequest.post(url +ACCESS_TOKEN_URL).body(paramMap.toJSONString()).timeout(50000).execute().body();
+			logger.info("ACCESS_TOKE----请求结果:{}", result);
+			if (result != null) {
+				JSONObject jsonObjec = JSONObject.parseObject(result);
+				if (HttpStatus.SUCCESS == jsonObjec.getIntValue("code")){
+					JSONObject token = jsonObjec.getJSONObject("data");
+					if (token != null && StringUtils.isNotEmpty(token.getString("accessToken"))){
+						redisCache.setCacheObject(Constants.CAMERA_ACCESS_TOKEN_PREFIX, token.getString("accessToken"), Constants.GPS_TOKEN_EXPIRATION, TimeUnit.DAYS);
+					}else {
+						logger.info("摄像头平台获取accessToken初始化结束,Http 摄像头平台获取accessToken初始化结束!-----{}", jsonObjec);
+					}
+				}else {
+					logger.info("摄像头平台获取accessToken初始化结束,Http 请求结果错误!-----{}", jsonObjec);
+				}
+			}
+		}
+		logger.info("摄像头平台获取accessToken初始化结束");
 	}
 
 

+ 50 - 0
aidex-system/src/main/java/com/aidex/system/domain/Message.java

@@ -0,0 +1,50 @@
+package com.aidex.system.domain;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author :ChenSir
+ * @date :Created in 2024/3/27 10:47
+ * @description:webhook推送消息体
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor()
+public class Message implements Serializable {
+
+	private static final long serialVersionUID = 1657487550347333162L;
+
+	@ApiModelProperty(value = "设备序列号")
+	@JSONField(name = "device_sn")
+	private String device_sn;
+
+	@ApiModelProperty(value = "设备内部上传时间戳 ")
+	@JSONField(name = "timestamp")
+	private String timestamp;
+
+	@ApiModelProperty(value = "告警图片")
+	@JSONField(name = "alarm_img")
+	private String alarm_img;
+
+	// 0:检测告警 1:密度告警 2:过线统计 3:遮挡告警 4:移动侦测
+	@ApiModelProperty(value = "告警类型")
+	@JSONField(name = "alarm_type")
+	private int alarm_type;
+
+	@ApiModelProperty(value = "告警扩展字段")
+	@JSONField(name = "extend")
+	private Map<String, Object> extend;
+
+	@ApiModelProperty(value = "mp4 文件")
+	@JSONField(name = "alarm_mp4")
+	private String[] alarm_mp4;
+
+
+}

+ 84 - 0
aidex-system/src/main/java/com/aidex/system/domain/SysCamera.java

@@ -0,0 +1,84 @@
+package com.aidex.system.domain;
+
+import com.aidex.common.annotation.Excel;
+import com.aidex.common.core.domain.BaseEntity;
+import com.aidex.common.utils.log.annotation.FieldRemark;
+import com.aidex.common.utils.log.annotation.LogField;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 摄像头管理对象
+ * @author ChenSir
+ * @email 914769835
+ * @date 2024-03-16
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SysCamera extends BaseEntity<SysCamera>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 设备号 */
+    @Excel(name = "设备号")
+    @LogField
+    @ApiModelProperty(value="设备号",required=true)
+    private String deviceSn;
+
+    /** 通道号 */
+    @Excel(name = "通道号")
+    @LogField
+    @ApiModelProperty(value="通道号",required=true)
+    private String channelNo;
+
+    /** 设备名称 */
+    @Excel(name = "设备名称")
+    @LogField
+    @ApiModelProperty(value="设备名称",required=true)
+    private String deviceName;
+
+    /** iccId */
+    @Excel(name = "iccId")
+    @LogField
+    @ApiModelProperty(value="iccId",required=true)
+    private String iccId;
+
+    /** 固件版本 */
+    @Excel(name = "固件版本")
+    @LogField
+    @ApiModelProperty(value="固件版本",required=true)
+    private String hwVer;
+
+    /** 软件版本 */
+    @Excel(name = "软件版本")
+    @LogField
+    @ApiModelProperty(value="软件版本",required=true)
+    private String swVer;
+
+    /** 软件版本 */
+    @Excel(name = "设备状态(0:离线、1:在线)", dictType = "sys_camera_status")
+    @LogField
+    @ApiModelProperty(value="设备状态",required=true)
+    private String onlineStatus ;
+
+    /** 摄像头类型 */
+    @Excel(name = "摄像头类型", dictType = "sys_camera_type")
+    @LogField
+    @ApiModelProperty(value="摄像头类型")
+    private String cameraType;
+
+    /** 码头序号 */
+    @Excel(name = "设备接入时间")
+    @LogField
+    @ApiModelProperty(value="设备接入时间")
+    private String accessTime;
+
+
+}

+ 6 - 1
aidex-ui/package.json

@@ -39,7 +39,10 @@
     "vue-router": "^3.1.2",
     "vue-svg-component-runtime": "^1.0.1",
     "vue-upload-component": "^2.8.20",
-    "vuex": "^3.1.1"
+    "vuex": "^3.1.1",
+    "xgplayer": "^3.0.10",
+    "xgplayer-flv": "^3.0.10",
+    "xgplayer-hls": "^3.0.10"
   },
   "devDependencies": {
     "@ant-design/colors": "^3.2.1",
@@ -62,6 +65,8 @@
     "git-revision-webpack-plugin": "^3.0.6",
     "less": "^3.0.4",
     "less-loader": "^5.0.0",
+    "sass": "1.26.8",
+    "sass-loader": "8.0.2",
     "opencollective": "^1.0.3",
     "opencollective-postinstall": "^2.0.2",
     "vue-svg-icon-loader": "^2.1.1",

+ 19 - 0
aidex-ui/src/api/camera/camera.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+// 获取摄像头列表
+export function list (query) {
+  return request({
+    url: '/system/camera/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 获取摄像头直播地址
+export function getLive (data) {
+  return request({
+    url: '/system/camera/openLive',
+    method: 'post',
+    data: data
+  })
+}

+ 296 - 0
aidex-ui/src/views/camera/index.vue

@@ -0,0 +1,296 @@
+<template>
+  <div>
+    <!--    <a-card :bordered="false" style="margin-bottom: 10px;">
+      &lt;!&ndash; 条件搜索 &ndash;&gt;
+      <div class="table-page-search-wrapper">
+        <a-form :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-row :gutter="48">
+            <a-col :md="6" :sm="24">
+              <a-form-item label="公告标题">
+                <a-input v-model="queryParam.noticeTitle" placeholder="请输入" allow-clear/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="操作人员">
+                <a-input v-model="queryParam.createBy" placeholder="请输入" allow-clear/>
+              </a-form-item>
+            </a-col>
+            <a-col :md="6" :sm="24">
+              <a-form-item label="公告类型">
+                <a-select placeholder="请选择" v-model="queryParam.noticeType" style="width: 100%" allow-clear>
+                  <a-select-option v-for="(d, index) in typeOptions" :key="index" :value="d.dictValue">{{ d.dictLabel }}</a-select-option>
+                </a-select>
+              </a-form-item>
+            </a-col>
+            <a-col>
+              <span class="table-page-search-submitButtons" style="float: right;">
+                <a-button type="primary" @click="handleQuery"><a-icon type="search" />查询</a-button>
+                <a-button style="margin-left: 8px" @click="resetQuery"><a-icon type="redo" />重置</a-button>
+              </span>
+            </a-col>
+          </a-row>
+        </a-form>
+      </div>
+    </a-card>-->
+    <a-card :bordered="false" class="table-card">
+      <advance-table
+        :columns="columns"
+        :data-source="list"
+        title="摄像头管理"
+        :loading="loading"
+        rowKey="id"
+        size="middle"
+        tableKey="system-notic-NoticIndex-table"
+        @refresh="getList"
+        :format-conditions="true"
+        :pagination="{
+          current: queryParam.pageNum,
+          pageSize: queryParam.pageSize,
+          total: total,
+          showSizeChanger: true,
+          showLessItems: true,
+          showQuickJumper: true,
+          showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条,总计 ${total} 条`,
+          onChange: changeSize,
+          onShowSizeChange: onSizeChange,
+        }"
+      >
+        <span slot="cameraType" slot-scope="{text, record}">
+          <a-tag :color="text | noticeTypeFilter">
+            {{ typeFormat(record) }}
+          </a-tag>
+        </span>
+        <span slot="onlineStatus" slot-scope="{text, record}">
+          <a-badge :status="Number(record.onlineStatus)===0?'error': 'processing'" :text="statusFormat(record) " />
+        </span>
+        <span slot="operation" slot-scope="{text, record}">
+          <a @click="handleLive(record)" >
+            实时视频
+          </a>
+        </span>
+      </advance-table>
+    </a-card>
+
+    <ant-modal
+      modalWidth="1000"
+      modalHeight="800"
+      :visible="showLiveModal"
+      modal-title="播放器"
+      :adjust-size="true"
+      @cancel="cancel"
+    >
+      <div class="videoPlayer" slot="content">
+        <SpeedDial :url="videoUrl" @triggerEvent="triggerEvent" ></SpeedDial>
+      </div>
+      <template slot="footer">
+        <div></div>
+      </template>
+    </ant-modal>
+  </div>
+</template>
+<script>
+import { list, getLive } from '@/api/camera/camera'
+import SpeedDial from '@/views/camera/modules/player'
+import AdvanceTable from '@/components/pt/table/AdvanceTable'
+import AntModal from '@/components/pt/dialog/AntModal'
+export default {
+  name: 'Camera',
+  components: {
+    SpeedDial, AdvanceTable, AntModal
+  },
+  data () {
+    return {
+      list: [],
+      total: 0,
+      // 类型数据字典
+      typeOptions: [],
+      statusOptions: [],
+      labelCol: { span: 6 },
+      wrapperCol: { span: 18 },
+      queryParam: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      columns: [
+        {
+          title: '设备号',
+          dataIndex: 'deviceSn',
+          ellipsis: true,
+          width: '10%'
+        },
+        {
+          title: '通道号',
+          dataIndex: 'channelNo',
+          align: 'center'
+        },
+        {
+          title: '设备名称',
+          dataIndex: 'deviceName',
+          align: 'center'
+        },
+        {
+          title: '固件版本',
+          dataIndex: 'hwVer',
+          align: 'center'
+        },
+        {
+          title: '软件版本',
+          dataIndex: 'swVer',
+          align: 'center'
+        },
+        {
+          title: '设备状态',
+          dataIndex: 'onlineStatus',
+          scopedSlots: { customRender: 'onlineStatus' },
+          align: 'center'
+        },
+        {
+          title: '摄像头类型',
+          dataIndex: 'cameraType',
+          scopedSlots: { customRender: 'cameraType' }
+        },
+        {
+          title: '设备接入时间',
+          dataIndex: 'createTime',
+          scopedSlots: { customRender: 'createTime' }
+        },
+        {
+          title: '操作',
+          dataIndex: 'operation',
+          width: '200',
+          scopedSlots: { customRender: 'operation' }
+        }
+      ],
+      showLiveModal: false,
+      showEditModal: false,
+      videoUrl: ''
+    }
+  },
+  filters: {
+    noticeTypeFilter (type) {
+      let value = 'blue'
+      if (type === 'CF') {
+        value = 'orange'
+      } else if (type === 'CG') {
+        value = 'pink'
+      } else if (type === 'CB') {
+        value = 'green'
+      } else if (type === 'CH') {
+        value = 'cyan'
+      }
+      return value
+    }
+  },
+  created () {
+    this.getDicts('sys_camera_status').then(response => {
+      this.statusOptions = response.data
+    })
+    this.getDicts('sys_camera_type').then(response => {
+      this.typeOptions = response.data
+    })
+    this.getList()
+  },
+  computed: {
+  },
+  watch: {
+  },
+  methods: {
+    /** 查询定时任务列表 */
+    getList () {
+      this.loading = true
+      list(this.queryParam).then(response => {
+          this.list = response.data.records
+          this.total = response.data.total
+          this.loading = false
+        }
+      )
+    },
+    // 状态字典翻译
+    statusFormat (row) {
+      return this.selectDictLabel(this.statusOptions, row.onlineStatus)
+    },
+    // 类型字典翻译
+    typeFormat (row) {
+      return this.selectDictLabel(this.typeOptions, row.cameraType)
+    },
+    /** 搜索按钮操作 */
+    handleQuery () {
+      this.queryParam.pageNum = 1
+      this.getList()
+    },
+    /** 重置按钮操作 */
+    resetQuery () {
+      this.queryParam = {
+        pageNum: 1,
+        pageSize: 10
+      }
+      this.handleQuery()
+    },
+    onShowSizeChange (current, pageSize) {
+      this.queryParam.pageSize = pageSize
+      this.getList()
+    },
+    onSizeChange (current, size) {
+      this.queryParam.pageNum = 1
+      this.queryParam.pageSize = size
+      this.getList()
+    },
+    changeSize (current, pageSize) {
+      this.queryParam.pageNum = current
+      this.queryParam.pageSize = pageSize
+      this.getList()
+    },
+    onSelectChange (selectedRowKeys, selectedRows) {
+      this.selectedRowKeys = selectedRowKeys
+      this.selectedRows = selectedRows
+      this.ids = this.selectedRows.map(item => item.id)
+      this.single = selectedRowKeys.length !== 1
+      this.multiple = !selectedRowKeys.length
+    },
+    toggleAdvanced () {
+      this.advanced = !this.advanced
+    },
+    handleAdd () {
+      this.showAddModal = true
+      this.$nextTick(() => (
+        this.$refs.noticeAddForm.handleAdd()
+      ))
+    },
+    handleUpdate (record) {
+      this.showEditModal = true
+      this.$nextTick(() => (
+        this.$refs.noticeEditForm.handleUpdate(record)
+      ))
+    },
+    handleLive(record) {
+      const _this = this
+      getLive(record).then((response) => {
+        console.log(response)
+        // 初始话播放器
+        _this.videoUrl = response.data.url
+        _this.showLiveModal = true
+      })
+    },
+    // 监听到子组件传过来的播放状态 true是播放 false是暂停
+    triggerEvent(value) {
+      console.log('是否播放:', value)
+    },
+    // 取消按钮
+    cancel () {
+      this.showLiveModal = false
+    }
+  }
+
+}
+</script>
+
+<style scoped>
+.videoPlayer{
+  height: 100%;
+  width: 100%;
+  position: absolute;
+  left: 10;
+  top: 18;
+  z-index: 99;
+}
+</style>

+ 109 - 0
aidex-ui/src/views/camera/modules/player.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="play-videos">
+    <div id="video-player" style="width: 100% !important;height: 100% !important; position:relative" class="xgPlayer"></div>
+  </div>
+</template>
+<script>
+import Player from 'xgplayer'
+import FlvPlugin from 'xgplayer-flv'
+import HlsPlugin from 'xgplayer-hls'
+import 'xgplayer/dist/index.min.css'
+
+export default {
+  name: 'XgPlayer',
+  props: {
+    url: {
+      // 父组件传过来的视频链接
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      player: null, // 实例
+      xOptions: {
+        id: 'video-player', // 播放器容器
+        isLive: true,
+        fluid: false,
+        fitVideoSize: 'auto',
+        videoFillMode: 'auto',
+        enableContextmenu: true,
+        loop: false, // 是否自动循环
+        lang: 'zh-cn', // 语言,'en', 'zh-cn', 'zh-tw'
+        screenShot: {// 是否允许截图(按钮)
+          saveImg: true,
+          quality: 0.92,
+          type: 'image/png',
+          format: '.png'
+        },
+        playsinline: false,
+        keyShortcut: 'true',
+        controls: {
+          autoHide: true,
+          mode: 'bottom'
+        },
+        cssullScreen: {
+          disable: true
+        },
+        ignores: ['cssfullscreen', 'playbackrate'],
+        playbackrate: {
+          disable: true
+        },
+        videoInit: true, // 自动预加载
+        volume: 0.7, // 初始化音量
+        autoplay: true,
+        url: ''
+      },
+      initVolume: 0.5
+    }
+  },
+  mounted() {
+    console.log('传过来的url:', this.url)
+    // 初始化播放器
+    this.initPlayer()
+  },
+  created() {},
+  // 监听播放路径的变化
+  watch: {
+    url: {
+      handler(newValue, oldValue) {
+        if (!this.player) {
+          this.initPlayer()
+          return
+        }
+        this.player.src = this.url
+      }
+    }
+  },
+  methods: {
+    // =========================1,设置播放器必要参数===================
+    initPlayer() {
+      console.log('初始化播放器!')
+      if (!this.url) return console.warn('url is not esist')
+      if (this.url != null) {
+        this.xOptions.url = this.url
+        if (this.url.toLowerCase().indexOf('.flv') > 0) {
+          this.xOptions.plugins = [FlvPlugin]
+        } else {
+          this.xOptions.plugins = [HlsPlugin]
+        }
+        this.xOptions.id = 'video-player'
+        this.xOptions.volume = this.initVolume
+        this.player = new Player(this.xOptions)
+      }
+    }
+  }
+}
+</script>
+<style scoped>
+.play-videos {
+  width: 94%;
+  height: calc(100vh - 260px);
+  background: black;
+  margin: 5px auto 1px 0;
+  .xgPlayer,.dplayer-video-wrap {
+    width: 100% !important;
+    height: 100% !important;
+  }
+}
+</style>