9f6e099c39b61027cc96df77200b98388d48d348..6e210cdaada04907dd127ce6db5a67f2e6e11f58
2026-07-17 wangzhibo
add
6e210c 对比 | 目录
2026-07-17 wangzhibo
add 设备上线/下线 消息
8e23a5 对比 | 目录
4个文件已添加
2个文件已修改
4801 ■■■■■ 已修改文件
src/modules/push/controller/admin/deviceonoff.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/push/controller/app/open.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/push/entity/deviceonoff.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/push/service/deviceonoff.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/push/service/open.ts 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
womp-20260716230000.sql 4591 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/push/controller/admin/deviceonoff.ts
New file
@@ -0,0 +1,36 @@
import { Inject } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { PushDeviceOnoffEntity } from '../../entity/deviceonoff';
import { PushDeviceOnoffService } from '../../service/deviceonoff';
/**
 * 设备对象管理
 */
@CoolController({
  api: ['add', 'delete', 'update', 'info', 'list', 'page'],
  entity: PushDeviceOnoffEntity,
  service: PushDeviceOnoffService,
  pageQueryOp: {
    keyWordLikeFields: ['a.deviceId', 'a.subSerial', 'a.deviceName'],
    fieldEq: [
      { column: 'a.status', requestParam: 'status' },
      { column: 'a.msgType', requestParam: 'msgType' },
      { column: 'a.devType', requestParam: 'devType' }
    ],
    where: async (ctx) => {
      const { startTime, endTime } = ctx.request.body;
      const condition = [];
      if (startTime && endTime) {
        condition.push([
          'a.occurTime BETWEEN :startTime AND :endTime',
          { startTime, endTime }
        ]);
      }
      return condition;
    }
  }
})
export class AdminPushDeviceOnoffController extends BaseController {
  @Inject()
  pushDeviceOnoffService: PushDeviceOnoffService;
}
src/modules/push/controller/app/open.ts
@@ -89,12 +89,12 @@
  }
  @Post('/cameragps', { summary: '原始车载监控上报接口' })
  async uploadCameraGps(
  @Post('/yingshiyun', { summary: '萤石云Webhook上报接口' })
  async saveYingshiyunMessage(
    @Body() body: any
  ) {
    const result = await this.pushOpenService.receiveCameraGpsMessage(body);
    const result = await this.pushOpenService.saveYingshiyunMessage(body);
    //return this.ok(result);
    return { "messageId": result }
src/modules/push/entity/deviceonoff.ts
New file
@@ -0,0 +1,45 @@
import { BaseEntity } from '../../base/entity/base';
import { Column, Entity, Index } from 'typeorm';
/**
 * 终端设备的上线/下线数据
 */
@Entity('t_push_device_onoff')
export class PushDeviceOnoffEntity extends BaseEntity {
  @Index()
  @Column({ comment: '设备ID' })
  deviceId: string;
  @Column({ comment: '通道号', type: 'int' , nullable: true,default:0 })
  channelNo: number;
  @Column({ comment: '消息唯一ID' })
  messageId: string;
  @Column({ comment: '消息时间' })
  messageTime: Date;
  @Column({ comment: '消息类型', dict: ['ONLINE', 'OFFLINE'], default: 'ONLINE' })
  msgType: string;
  @Column({ comment: '设备类型' , nullable: true })
  devType: string;
  @Column({ comment: '设备名称', nullable: true })
  deviceName: string;
  @Column({ comment: '子序列号' , nullable: true })
  subSerial: string;
  @Column({ comment: '源端ID', nullable: true })
  dasId: string;
  @Column({ comment: 'NAT的IP', nullable: true })
  natIp: string;
  @Column({ comment: '事件发生时间', nullable: true })
  occurTime: Date;
  @Column({ comment: '注册时间', nullable: true })
  regTime: Date;
}
src/modules/push/service/deviceonoff.ts
New file
@@ -0,0 +1,14 @@
import { Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import { PushDeviceOnoffEntity } from '../entity/deviceonoff';
/**
 * 监控设备的上线/下线消息服务
 */
@Provide()
export class PushDeviceOnoffService extends BaseService {
  @InjectEntityModel(PushDeviceOnoffEntity)
  pushDeviceOnoffEntity: Repository<PushDeviceOnoffEntity>;
}
src/modules/push/service/open.ts
@@ -7,6 +7,7 @@
import { PushAuditlogEntity } from '../entity/auditlog';
import { PushCameraGpsEntity } from '../entity/cameragps';
import { PushVehicleGpsEntity } from '../entity/vehiclegps';
import { PushDeviceOnoffEntity } from '../entity/deviceonoff';
import { CachingFactory, MidwayCache } from '@midwayjs/cache-manager';
/**
@@ -25,6 +26,10 @@
  @InjectEntityModel(PushVehicleGpsEntity)
  pushVehicleGpsEntity: Repository<PushVehicleGpsEntity>;
  @InjectEntityModel(PushDeviceOnoffEntity)
  pushDeviceOnoffEntity: Repository<PushDeviceOnoffEntity>;
  @InjectEntityModel(PushAuditlogEntity)
  pushAuditlogEntity: Repository<PushAuditlogEntity>;
@@ -76,34 +81,105 @@
      } else {
        siteWeight.businessId = 'UNKNOWN';
        siteWeight.departmentId = -1;
        await this.writePushAuditLog("weight",{ "deviceNo": data.deviceNo, "requestContent": data, "errorMessage": "电子秤还未分配到垃圾收集点." });
        await this.writePushAuditLog("weight", { "deviceNo": data.deviceNo, "requestContent": data, "errorMessage": "电子秤还未分配到垃圾收集点." });
      }
    } catch (err) {
      await this.writePushAuditLog("weight",{ "deviceNo": data.deviceNo || "UNKNOW", "requestContent": data, "errorMessage": err });
      await this.writePushAuditLog("weight", { "deviceNo": data.deviceNo || "UNKNOW", "requestContent": data, "errorMessage": err });
    }
  }
  /**
   * 接收GPS消息并缓存
   * 接收萤石云 消息并缓存
   */
  async receiveCameraGpsMessage(body: any) {
    const messageId = body.msgSeq || Math.random().toString(36).substring(2);
    await this.midwayCache.set(`push:cameragps:queue:${messageId}`, JSON.stringify(body), 24 * 3600);
    this.processCameraGpsAsync(messageId).catch(err => {
  async saveYingshiyunMessage(body: any) {
    const messageId = body?.header?.messageId || Math.random().toString(36).substring(2);
    await this.midwayCache.set(`push:yingshiyun:queue:${messageId}`, JSON.stringify(body), 24 * 3600);
    this.processYingshiyunAsync(messageId).catch(err => {
      console.error('异步处理GPS消息失败:', err);
    });
    return  messageId ;
    return messageId;
  }
  /**
   * 异步解析车载监控视频的位置数据并入库
   * 异步解析萤石云消息,按照消息类型 入库
   */
  async processCameraGpsAsync(messageId: string) {
    const dataStr = await this.midwayCache.get(`push:cameragps:queue:${messageId}`) as string | null;
  async processYingshiyunAsync(messageId: string) {
    const dataStr = await this.midwayCache.get(`push:yingshiyun:queue:${messageId}`) as string | null;
    if (!dataStr) return;
    const body = JSON.parse(dataStr);
    const type = body?.header?.type;
    try {
      switch (type) {
        // 设备上下线
        case 'ys.onoffline': {
          await this.pushDeviceOnoffEntity.save({
            deviceId:body.header.deviceId,
            channelNo: body.header.channelNo,
            messageId: body.header.messageId,
            messageTime: new Date(body.header.messageTime),
            msgType: body.body.msgType,
            devType: body.body.devType,
            deviceName: body.body.deviceName,
            subSerial: body.body.subSerial,
            dasId: body.body.dasId,
            natIp: body.body.natIp,
            occurTime: body.body.occurTime,
            regTime: body.body.regTime
          });
          break;
        }
        // 萤石告警
        /*
        case 'ys.alarm': {
          await this.pushAlarmRepo.save({
            messageId,
            raw: JSON.stringify(body),
            createTime: new Date(),
          });
          break;
        }
          */
        // 海康 ISAPI
        //case 'ys.open.isapi': {
          //processCameraGpsAsync(body)
          //await this.pushIsapiRepo.save({
          //  messageId,
          //  raw: JSON.stringify(body),
          //  createTime: new Date(),
          //});
          //break;
        //}
        default:
          console.warn('Unknown yingshi webhook type:', type);
      }
    } catch (err) {
      await this.writePushAuditLog(type || 'unknown', {
        deviceNo: body?.sn || 'UNKNOWN',
        requestContent: body,
        errorMessage: err,
      });
    }
    // 不管成功与否,删除缓存中的任务队列
    await this.midwayCache.del(`push:yingshiyun:queue:${messageId}`);
}
  /**
   * 异步解析车载监控的GPS数据,存储并添加业务表
   */
  async processCameraGpsAsync(body: any) {
    try {
      let vehicleNo = '';
@@ -155,21 +231,20 @@
          });
        } else {
          await this.writePushAuditLog("camera_gps",{ "deviceNo": body.sn, "requestContent": body, "errorMessage": "车载监控还未分配到车辆" });
          await this.writePushAuditLog("camera_gps", { "deviceNo": body.sn, "requestContent": body, "errorMessage": "车载监控还未分配到车辆" });
        }
      } else {
        await this.writePushAuditLog("camera_gps",{ "deviceNo": "UNKOWN", "requestContent": body, "errorMessage": "未包含设备号SN" });
        await this.writePushAuditLog("camera_gps", { "deviceNo": "UNKOWN", "requestContent": body, "errorMessage": "未包含设备号SN" });
      }
      await this.midwayCache.del(`push:gps:queue:${messageId}`);
    } catch (err: any) {
      await this.writePushAuditLog("camera_gps",{ "deviceNo": body.sn || "UNKNOW", "requestContent": body, "errorMessage": err });
      await this.writePushAuditLog("camera_gps", { "deviceNo": body.sn || "UNKNOW", "requestContent": body, "errorMessage": err });
    }
  }
  async writePushAuditLog(pushType:string,data: any) {
  async writePushAuditLog(pushType: string, data: any) {
    let isSuccess = true;
    let errorMsg = '';
    try {
womp-20260716230000.sql
New file
Diff too large