From c4d7079ac30b4ed0393c2dc831627825c7a545d6 Mon Sep 17 00:00:00 2001
From: wangrong <wangrong@shsening.com>
Date: 星期一, 10 八月 2026 18:51:37 +0800
Subject: [PATCH] 增加监控点每日抓图

---
 src/modules/monitor/service/ys7.ts |  314 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 308 insertions(+), 6 deletions(-)

diff --git a/src/modules/monitor/service/ys7.ts b/src/modules/monitor/service/ys7.ts
index 91037e3..bbdfaff 100644
--- a/src/modules/monitor/service/ys7.ts
+++ b/src/modules/monitor/service/ys7.ts
@@ -5,6 +5,7 @@
 import { InjectEntityModel } from '@midwayjs/typeorm';
 import { Repository } from 'typeorm';
 
+
 interface Ys7TokenData {
   accessToken: string;
   /** 钀ょ煶杩斿洖鐨勬绉掔骇杩囨湡鏃堕棿鎴� */
@@ -37,13 +38,11 @@
   private async fetchAndSaveRemote(): Promise<Ys7TokenData> {
     const appKey_result = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.appKey' });
     const appSecret_result = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.appSecret' });
-
     if (!appKey_result || !appSecret_result) {
       throw new Error(
         '钀ょ煶浜� appKey/appSecret 鏈厤缃紝璇峰湪銆愮郴缁熷弬鏁般�戜腑缁存姢 ys7.appKey / ys7.appSecret'
       );
     }
-
     const { data: res } = await axios.post(
       'https://open.ys7.com/api/lapp/token/get',
       new URLSearchParams({ appKey: appKey_result.data, appSecret: appSecret_result.data }),
@@ -52,7 +51,6 @@
         timeout: 8000,
       }
     );
-
     if (String(res.code) !== '200') {
       throw new Error(`钀ょ煶浜戣幏鍙� Token 澶辫触锛�${res.msg}锛坈ode: ${res.code}锛塦);
     }
@@ -61,8 +59,6 @@
       accessToken: res.data.accessToken,
       expireTime: res.data.expireTime,
     };
-
-    // 鍐欏洖绯荤粺鍙傛暟琛紙Cool-Admin 鑷甫 upsert锛�
     const token = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
     this.baseSysParamEntity.save({
       ...token,
@@ -70,7 +66,313 @@
       updateTime: new Date(),
     }).catch(() => {
       // 蹇界暐鍐欏洖澶辫触
-    }); 
+    });
     return tokenData;
   }
+
+  async getSpaceID(): Promise<string> {
+    const entity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
+    if (!entity ||
+      !entity.updateTime ||
+      Date.now() - new Date(entity.updateTime).getTime() >
+      6 * 24 * 60 * 60 * 1000) {
+      const spaceId = await this.createCaptureSpace();
+      await this.baseSysParamEntity.save({
+        id: entity?.id,
+        keyName: 'ys7.storageSpaceID',
+        data: spaceId,
+        updateTime: new Date(),
+      });
+      return spaceId;
+    }
+    return entity!.data as string;
+  }
+
+  /**
+   * 鍒涘缓钀ょ煶浜戙�愯澶囨姄鍥俱�戝瓨鍌ㄧ┖闂�
+   * - spaceName 鍙栬嚜绯荤粺鍙傛暟 ys7.spaceName
+   * - 杩囨湡澶╂暟榛樿 7 澶�
+   * - 鍒涘缓鎴愬姛鍚� storageSpaceID 鍐欏叆 ys7.storageSpaceID
+   * - 璋冪敤钀ょ煶浜戠┖闂村垱寤烘帴鍙o細https://open.ys7.com/api/service/open/storage/engine/space
+   * - 鎺ュ彛璇存槑鍦板潃锛歨ttps://open.ys7.com/help/5236
+   * - 灏嗗垱寤哄ソ鐨勫瓨鍌ㄧ┖闂碔D瀛樺叆鍙傛暟锛歽s7.storageSpaceID
+   */
+  async createCaptureSpace(expireDays = 7): Promise<string> {
+    // 1. accessToken
+    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
+    if (!tokenEntity?.data) {
+      console.error('[Ys7Service] ys7.accessToken 涓虹┖');
+      throw new Error('ys7.accessToken 涓虹┖');
+    }
+    const accessToken = String(tokenEntity.data).trim();
+
+    // 2. spaceName
+    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.spaceName' });
+    if (!spaceEntity?.data) {
+      console.error('[Ys7Service] ys7.spaceName 鏈厤缃�');
+      throw new Error('ys7.spaceName 鏈厤缃�');
+    }
+    const spaceName = String(spaceEntity.data).trim();
+    // 4. 钀ょ煶渚ф槸鍚﹀凡瀛樺湪鍚屽悕绌洪棿
+    const existId = await this.getSpaceIdByName(accessToken, spaceName);
+    if (existId) {
+      console.log('[Ys7Service] 钀ょ煶渚у凡瀛樺湪鍚屽悕绌洪棿锛宻paceId=%s', existId);
+      await this.saveStorageSpaceID(existId);
+      return existId;
+    }
+    // 5. 鐪熸鍒涘缓
+    const params = new URLSearchParams({
+      bizType: 'capture',
+      expireDays: String(expireDays),
+      spaceName,
+      storageType: '1',
+    });
+
+    let res: any;
+    try {
+      const resp = await axios.post(
+        'https://open.ys7.com/api/service/open/storage/engine/space',
+        params,
+        {
+          headers: {
+            'Content-Type': 'application/x-www-form-urlencoded',
+            accessToken,
+          },
+          timeout: 8000,
+        }
+      );
+      res = resp.data;
+    } catch (err: any) {
+      console.error('[Ys7Service] 钀ょ煶鎺ュ彛璇锋眰澶辫触');
+      if (err.response) {
+        console.error('[Ys7Service] RESPONSE BODY=%s', JSON.stringify(err.response.data));
+      } else {
+        console.error('[Ys7Service] ERROR MSG=%s', err.message);
+      }
+      throw err;
+    }
+    // 6. 鏍¢獙钀ょ煶涓氬姟杩斿洖
+    if (!res || res.meta?.code !== 200) {
+      console.error('[Ys7Service] 钀ょ煶鍒涘缓绌洪棿涓氬姟澶辫触: %s', JSON.stringify(res?.meta));
+      throw new Error(`钀ょ煶鍒涘缓绌洪棿澶辫触: ${JSON.stringify(res?.meta)}`);
+    }
+    const storageSpaceID = String(res.data); // data 灏辨槸 spaceId
+    // 7. 淇濆瓨鍒扮郴缁熷弬鏁�
+    await this.saveStorageSpaceID(storageSpaceID);
+    return storageSpaceID;
+  }
+
+  /**
+   * 淇敼钀ょ煶浜戙�愯澶囨姄鍥俱�戝瓨鍌ㄧ┖闂�
+   * - spaceId 鍙栬嚜 ys7.storageSpaceID
+   * - accessToken 鍙栬嚜 ys7.accessToken
+   * - 榛樿杩囨湡澶╂暟 7 澶�
+   */
+  async updateCaptureSpace(expireDays = 7): Promise<boolean> {
+    // 1. accessToken
+    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
+    if (!tokenEntity?.data) {
+      throw new Error('钀ょ煶 accessToken 涓嶅瓨鍦紝璇峰厛鑾峰彇 Token');
+    }
+    const accessToken = tokenEntity.data as string;
+
+    // 2. spaceId
+    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
+    if (!spaceEntity?.data) {
+      throw new Error('钀ょ煶 storageSpaceID 涓嶅瓨鍦紝璇峰厛鍒涘缓鎶撳浘绌洪棿');
+    }
+    const spaceId = Number(spaceEntity.data);
+
+    // 3. 缁勮鍙傛暟锛圥UT 浣跨敤 URLSearchParams锛�
+    const params = new URLSearchParams();
+    params.append('spaceId', String(spaceId));
+    params.append('expireDays', String(expireDays));
+
+    const url = 'https://open.ys7.com/api/service/open/storage/engine/space';
+    const { data: res } = await axios.put(url, params, {
+      headers: {
+        accessToken,
+        'Content-Type': 'application/x-www-form-urlencoded',
+      },
+      timeout: 8000,
+    });
+    if (res.meta?.code !== 200) {
+      throw new Error(
+        `钀ょ煶浜戜慨鏀瑰瓨鍌ㄧ┖闂村け璐ワ細${res.meta?.message}锛坈ode: ${res.meta?.code}锛塦
+      );
+    }
+    if (res.data) {
+      this.baseSysParamEntity.save({
+        ...spaceEntity,
+        updateTime: new Date(),
+      }).catch(() => {
+        // 蹇界暐鍐欏洖澶辫触
+      });
+    }
+    return !!res.data;
+  }
+
+  /**
+   * 鍒犻櫎钀ょ煶浜戙�愯澶囨姄鍥俱�戝瓨鍌ㄧ┖闂�
+   * - spaceId 鍙栬嚜 ys7.storageSpaceID
+   * - accessToken 鍙栬嚜 ys7.accessToken
+   * - 鍒犻櫎鎴愬姛鍚庯紝娓呯┖ ys7.storageSpaceID
+   */
+  async deleteCaptureSpace(): Promise<boolean> {
+    // 1. accessToken
+    const tokenEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.accessToken' });
+    if (!tokenEntity?.data) {
+      throw new Error('钀ょ煶 accessToken 涓嶅瓨鍦紝璇峰厛鑾峰彇 Token');
+    }
+    const accessToken = tokenEntity.data as string;
+
+    // 2. spaceId
+    const spaceEntity = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
+    if (!spaceEntity?.data) {
+      throw new Error('钀ょ煶 storageSpaceID 涓嶅瓨鍦紝鏃犳硶鍒犻櫎');
+    }
+    const spaceId = Number(spaceEntity.data);
+
+    // 3. DELETE 璇锋眰锛坆ody 浣跨敤 URLSearchParams锛�
+    const params = new URLSearchParams();
+    params.append('spaceId', String(spaceId));
+
+    const url = 'https://open.ys7.com/api/service/open/storage/engine/space';
+    const { data: res } = await axios.delete(url, {
+      headers: {
+        accessToken,
+        'Content-Type': 'application/x-www-form-urlencoded',
+      },
+      data: params.toString(),
+      timeout: 8000,
+    });
+    if (res.meta?.code !== 200) {
+      throw new Error(
+        `钀ょ煶浜戝垹闄ゅ瓨鍌ㄧ┖闂村け璐ワ細${res.meta?.message}锛坈ode: ${res.meta?.code}锛塦
+      );
+    }
+    // 4. 娓呯悊鏈湴鍙傛暟
+    spaceEntity.data = '';
+    spaceEntity.updateTime = new Date();
+    await this.baseSysParamEntity.save(spaceEntity);
+
+    return !!res.data;
+  }
+
+  /**
+   * 鏍规嵁 spaceName 鏌ヨ钀ょ煶鏄惁宸插瓨鍦ㄣ�愯澶囨姄鍥俱�戠┖闂�
+   * 浣跨敤瀹樻柟 listById 鎺ュ彛锛屾敮鎸佺炕椤�
+   * 杩斿洖 storageSpaceID 鎴� null
+   */
+  private async getSpaceIdByName(
+    accessToken: string,
+    spaceName: string
+  ): Promise<string | null> {
+
+    const url = 'https://open.ys7.com/api/service/open/storage/engine/space/listById';
+
+    // 鏌ヨ鏃堕棿绐楀彛锛氭渶杩� 30 澶╋紙钀ょ煶寮哄埗瑕佹眰锛�
+    const endTime = new Date();
+    const startTime = new Date(endTime.getTime() - 30 * 24 * 60 * 60 * 1000);
+
+    const format = (d: Date) =>
+      d.getFullYear() +
+      '-' +
+      String(d.getMonth() + 1).padStart(2, '0') +
+      '-' +
+      String(d.getDate()).padStart(2, '0') +
+      ' ' +
+      String(d.getHours()).padStart(2, '0') +
+      ':' +
+      String(d.getMinutes()).padStart(2, '0') +
+      ':' +
+      String(d.getSeconds()).padStart(2, '0');
+
+    const queryParams = new URLSearchParams({
+      startTime: format(startTime),
+      endTime: format(endTime),
+      pageSize: '50',
+      bizTypeList: 'capture', // 鍙煡璁惧鎶撳浘绌洪棿
+    });
+
+    let lastId: number | null = null;
+    let page = 1;
+
+    try {
+      while (true) {
+        if (lastId !== null) {
+          queryParams.set('lastSpaceId', String(lastId));
+        }
+
+        const fullUrl = `${url}?${queryParams.toString()}`;
+
+        const resp = await axios.get(fullUrl, {
+          headers: {
+            accessToken,
+          },
+          timeout: 8000,
+        });
+        const res = resp.data;
+        if (res?.meta?.code !== 200) {
+          console.error('[Ys7Service] 鏌ヨ绌洪棿鍒楄〃澶辫触: %s', JSON.stringify(res.meta));
+          throw new Error(`鏌ヨ钀ょ煶绌洪棿澶辫触: ${JSON.stringify(res.meta)}`);
+        }
+
+        const data = res.data || {};
+        const list = data.result || [];
+
+        // 绮剧‘鍖归厤 spaceName
+        const match = list.find(
+          (item: any) => item.spaceName === spaceName && item.bizType === 'capture'
+        );
+
+        if (match) {
+          console.log(
+            '[Ys7Service] 鉁� 鎵惧埌宸插瓨鍦ㄧ殑绌洪棿锛歴paceId=%s, spaceName=%s, bizType=%s',
+            match.spaceId,
+            match.spaceName,
+            match.bizType
+          );
+          return String(match.spaceId);
+        }
+
+        // 娌℃湁涓嬩竴椤碉紝缁堟
+        if (!data.hasNext) {
+          break;
+        }
+
+        lastId = data.lastId;
+        page++;
+      }
+      return null;
+    } catch (err: any) {
+      console.error('[Ys7Service] 鏌ヨ钀ょ煶绌洪棿寮傚父');
+      if (err.response) {
+        console.error('[Ys7Service] HTTP STATUS=%d', err.response.status);
+        console.error('[Ys7Service] RESPONSE=%s', JSON.stringify(err.response.data));
+      } else {
+        console.error('[Ys7Service] ERROR=%s', err.message);
+      }
+      throw err;
+    }
+  }
+
+  /** 鍏叡淇濆瓨鏂规硶 */
+  private async saveStorageSpaceID(spaceId: string) {
+    let e = await this.baseSysParamEntity.findOneBy({ keyName: 'ys7.storageSpaceID' });
+    if (!e) {
+      e = this.baseSysParamEntity.create({
+        keyName: 'ys7.storageSpaceID',
+        name: '钀ょ煶浜戞姄鍥剧┖闂碔D',
+        dataType: 1,
+        data: spaceId,
+        updateTime: new Date(),
+      });
+    } else {
+      e.data = spaceId;
+      e.updateTime = new Date();
+    }
+    await this.baseSysParamEntity.save(e);
+  }
+
 }
\ No newline at end of file

--
Gitblit v1.9.1