| New file |
| | |
| | | import { Inject, Provide } from '@midwayjs/core'; |
| | | import { BaseService } from '@cool-midway/core'; |
| | | import { InjectEntityModel } from '@midwayjs/typeorm'; |
| | | import { Repository, SelectQueryBuilder } from 'typeorm'; |
| | | import { Context } from '@midwayjs/koa'; |
| | | import { BasicdataSiteEntity } from '../../basicdata/entity/site'; |
| | | import { BasicdataSorterEntity } from '../../basicdata/entity/sorter'; |
| | | import { BasicdataVehicleEntity } from '../../basicdata/entity/vehicle'; |
| | | import { BasicdataWastetypeEntity } from '../../basicdata/entity/wastetype'; |
| | | import { BasicdataIotEntity } from '../../basicdata/entity/iot'; |
| | | import { PushSorterGpsEntity } from '../../push/entity/sortergps'; |
| | | import { PushSorterHealthEntity } from '../../push/entity/sorterhealth'; |
| | | import { PushSorterAlarmEntity } from '../../push/entity/sorteralarm'; |
| | | import { PushVehicleGpsEntity } from '../../push/entity/vehiclegps'; |
| | | import { PushSiteweightEntity } from '../../push/entity/siteweight'; |
| | | import { PushDevicealarmEntity } from '../../push/entity/devicealarm'; |
| | | |
| | | function n(v: any, d = 0) { |
| | | const x = Number(v); |
| | | return Number.isFinite(x) ? x : d; |
| | | } |
| | | |
| | | @Provide() |
| | | export class MapsLiveService extends BaseService { |
| | | @Inject() |
| | | ctx: Context; |
| | | |
| | | @InjectEntityModel(BasicdataSiteEntity) |
| | | siteRepo: Repository<BasicdataSiteEntity>; |
| | | |
| | | @InjectEntityModel(BasicdataSorterEntity) |
| | | sorterRepo: Repository<BasicdataSorterEntity>; |
| | | |
| | | @InjectEntityModel(BasicdataVehicleEntity) |
| | | vehicleRepo: Repository<BasicdataVehicleEntity>; |
| | | |
| | | @InjectEntityModel(PushSorterGpsEntity) |
| | | sorterGps: Repository<PushSorterGpsEntity>; |
| | | |
| | | @InjectEntityModel(PushSorterHealthEntity) |
| | | sorterHealthRepo: Repository<PushSorterHealthEntity>; |
| | | |
| | | @InjectEntityModel(PushSorterAlarmEntity) |
| | | sorterAlarm: Repository<PushSorterAlarmEntity>; |
| | | |
| | | @InjectEntityModel(PushVehicleGpsEntity) |
| | | vehicleGps: Repository<PushVehicleGpsEntity>; |
| | | |
| | | @InjectEntityModel(PushSiteweightEntity) |
| | | siteWeightRepo: Repository<PushSiteweightEntity>; |
| | | |
| | | @InjectEntityModel(PushDevicealarmEntity) |
| | | deviceAlarm: Repository<PushDevicealarmEntity>; |
| | | |
| | | @InjectEntityModel(BasicdataIotEntity) |
| | | iotRepo: Repository<BasicdataIotEntity>; |
| | | |
| | | private deptIds(): number[] | null { |
| | | const admin = this.ctx.admin; |
| | | if (!admin || admin.username === 'admin') return null; |
| | | return Array.isArray(admin.departmentIds) ? admin.departmentIds.map(Number) : []; |
| | | } |
| | | |
| | | private applyDept(qb: SelectQueryBuilder<any>, alias: string) { |
| | | const ids = this.deptIds(); |
| | | if (ids === null) return qb; |
| | | if (!ids.length) { |
| | | qb.andWhere('1 = 0'); |
| | | return qb; |
| | | } |
| | | qb.andWhere(`${alias}.departmentId IN (:...deptIds)`, { deptIds: ids }); |
| | | return qb; |
| | | } |
| | | |
| | | async sites() { |
| | | const qb = this.siteRepo |
| | | .createQueryBuilder('a') |
| | | .select([ |
| | | 'a.id', |
| | | 'a.businessId', |
| | | 'a.businessName', |
| | | 'a.departmentId', |
| | | 'a.siteType', |
| | | 'a.address', |
| | | 'a.leader', |
| | | 'a.longitude', |
| | | 'a.latitude', |
| | | 'a.photo', |
| | | 'a.status', |
| | | 'a.remark', |
| | | ]) |
| | | .where('a.status = :status', { status: 1 }) |
| | | .andWhere('a.longitude IS NOT NULL') |
| | | .andWhere('a.latitude IS NOT NULL'); |
| | | this.applyDept(qb, 'a'); |
| | | const rows = await qb.getMany(); |
| | | return rows.filter(e => n(e.longitude) && n(e.latitude)); |
| | | } |
| | | |
| | | async siteWeight(businessId: string, startTime: string, endTime: string) { |
| | | if (!businessId) return []; |
| | | const qb = this.siteWeightRepo |
| | | .createQueryBuilder('a') |
| | | .leftJoin(BasicdataWastetypeEntity, 'c', 'a.garbageType = c.code') |
| | | .select('a.id', 'id') |
| | | .addSelect('a.businessId', 'businessId') |
| | | .addSelect('a.deviceNo', 'deviceNo') |
| | | .addSelect('a.uploadTime', 'uploadTime') |
| | | .addSelect('a.garbageType', 'garbageType') |
| | | .addSelect('c.name', 'garbageName') |
| | | .addSelect('a.weight', 'weight') |
| | | .where('a.businessId = :businessId', { businessId }) |
| | | .andWhere('a.uploadTime BETWEEN :startTime AND :endTime', { startTime, endTime }) |
| | | .orderBy('a.uploadTime', 'DESC') |
| | | .limit(200); |
| | | this.applyDept(qb, 'a'); |
| | | return qb.getRawMany(); |
| | | } |
| | | |
| | | async sorterOnline() { |
| | | const since = new Date(Date.now() - 15 * 60 * 1000); |
| | | const qb = this.sorterGps |
| | | .createQueryBuilder('a') |
| | | .where('a.uploadTime >= :since', { since }) |
| | | .orderBy('a.uploadTime', 'DESC') |
| | | .take(4000); |
| | | this.applyDept(qb, 'a'); |
| | | const rows = await qb.getMany(); |
| | | const latest = new Map<string, PushSorterGpsEntity>(); |
| | | rows.forEach(r => { |
| | | if (r.idCard && !latest.has(r.idCard)) latest.set(r.idCard, r); |
| | | }); |
| | | const cards = [...latest.keys()]; |
| | | const sorters = cards.length |
| | | ? await this.sorterRepo |
| | | .createQueryBuilder('s') |
| | | .where('s.businessId IN (:...cards)', { cards }) |
| | | .getMany() |
| | | : []; |
| | | const sorterMap = new Map(sorters.map(s => [s.businessId, s])); |
| | | return [...latest.values()].map(g => { |
| | | const s = sorterMap.get(g.idCard); |
| | | return { |
| | | idCard: g.idCard, |
| | | name: s?.businessName || g.name, |
| | | phone: s?.phone || '', |
| | | sex: s?.sex, |
| | | workSiteId: s?.workSiteId, |
| | | departmentId: s?.departmentId || g.departmentId, |
| | | departmentName: g.departmentName, |
| | | longitude: n(g.longitude), |
| | | latitude: n(g.latitude), |
| | | speed: n(g.speed), |
| | | uploadTime: g.uploadTime, |
| | | status: s?.status, |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | async sorterTrack(idCard: string, startTime: string, endTime: string) { |
| | | if (!idCard) return []; |
| | | const qb = this.sorterGps |
| | | .createQueryBuilder('a') |
| | | .where('a.idCard = :idCard', { idCard }) |
| | | .andWhere('a.uploadTime BETWEEN :startTime AND :endTime', { startTime, endTime }) |
| | | .andWhere('a.longitude IS NOT NULL') |
| | | .andWhere('a.latitude IS NOT NULL') |
| | | .orderBy('a.uploadTime', 'ASC') |
| | | .take(5000); |
| | | this.applyDept(qb, 'a'); |
| | | const rows = await qb.getMany(); |
| | | return rows.map(r => ({ |
| | | longitude: n(r.longitude), |
| | | latitude: n(r.latitude), |
| | | speed: n(r.speed), |
| | | time: r.uploadTime, |
| | | })); |
| | | } |
| | | |
| | | async sorterHealth(idCard: string) { |
| | | if (!idCard) return { recent: [], abnormal: [], alarms: [] }; |
| | | const recentQb = this.sorterHealthRepo |
| | | .createQueryBuilder('a') |
| | | .where('a.idCard = :idCard', { idCard }) |
| | | .orderBy('a.uploadTime', 'DESC') |
| | | .take(20); |
| | | this.applyDept(recentQb, 'a'); |
| | | const recent = await recentQb.getMany(); |
| | | |
| | | const abQb = this.sorterHealthRepo |
| | | .createQueryBuilder('a') |
| | | .where('a.idCard = :idCard', { idCard }) |
| | | .andWhere("a.dataType = '异常'") |
| | | .orderBy('a.uploadTime', 'DESC') |
| | | .take(20); |
| | | this.applyDept(abQb, 'a'); |
| | | const abnormal = await abQb.getMany(); |
| | | |
| | | const alarmQb = this.sorterAlarm |
| | | .createQueryBuilder('a') |
| | | .where('a.idCard = :idCard', { idCard }) |
| | | .orderBy('a.uploadTime', 'DESC') |
| | | .take(20); |
| | | this.applyDept(alarmQb, 'a'); |
| | | const alarms = await alarmQb.getMany(); |
| | | |
| | | return { recent, abnormal, alarms }; |
| | | } |
| | | |
| | | async vehicleOnline() { |
| | | const since = new Date(Date.now() - 15 * 60 * 1000); |
| | | const qb = this.vehicleGps |
| | | .createQueryBuilder('a') |
| | | .where('a.gpsTime >= :since', { since }) |
| | | .andWhere("a.vehicleNo IS NOT NULL AND a.vehicleNo <> ''") |
| | | .orderBy('a.gpsTime', 'DESC') |
| | | .take(4000); |
| | | this.applyDept(qb, 'a'); |
| | | const rows = await qb.getMany(); |
| | | const latest = new Map<string, PushVehicleGpsEntity>(); |
| | | rows.forEach(r => { |
| | | if (r.vehicleNo && !latest.has(r.vehicleNo)) latest.set(r.vehicleNo, r); |
| | | }); |
| | | const plates = [...latest.keys()]; |
| | | const vehicles = plates.length |
| | | ? await this.vehicleRepo |
| | | .createQueryBuilder('v') |
| | | .where('v.businessId IN (:...plates)', { plates }) |
| | | .getMany() |
| | | : []; |
| | | const vehicleMap = new Map(vehicles.map(v => [v.businessId, v])); |
| | | return [...latest.values()].map(g => { |
| | | const v = vehicleMap.get(g.vehicleNo); |
| | | return { |
| | | vehicleNo: g.vehicleNo, |
| | | sn: g.sn, |
| | | vehicleType: v?.vehicleType, |
| | | capacityTon: v?.capacityTon, |
| | | departmentId: v?.departmentId || g.departmentId, |
| | | departmentName: g.departmentName, |
| | | longitude: n(g.longitude), |
| | | latitude: n(g.latitude), |
| | | speed: n(g.speed), |
| | | direction: g.direction, |
| | | gpsTime: g.gpsTime, |
| | | status: v?.status, |
| | | gpsStatus: g.status, |
| | | }; |
| | | }); |
| | | } |
| | | |
| | | async vehicleTrack(vehicleNo: string, startTime: string, endTime: string) { |
| | | if (!vehicleNo) return []; |
| | | const qb = this.vehicleGps |
| | | .createQueryBuilder('a') |
| | | .where('a.vehicleNo = :vehicleNo', { vehicleNo }) |
| | | .andWhere('a.gpsTime BETWEEN :startTime AND :endTime', { startTime, endTime }) |
| | | .andWhere('a.longitude IS NOT NULL') |
| | | .andWhere('a.latitude IS NOT NULL') |
| | | .orderBy('a.gpsTime', 'ASC') |
| | | .take(5000); |
| | | this.applyDept(qb, 'a'); |
| | | const rows = await qb.getMany(); |
| | | return rows.map(r => ({ |
| | | longitude: n(r.longitude), |
| | | latitude: n(r.latitude), |
| | | speed: n(r.speed), |
| | | direction: r.direction, |
| | | time: r.gpsTime, |
| | | })); |
| | | } |
| | | |
| | | async vehicleEvents(vehicleNo: string) { |
| | | if (!vehicleNo) return []; |
| | | const iots = await this.iotRepo.find({ |
| | | where: { businessId: vehicleNo }, |
| | | select: ['iotCode', 'iotName', 'iotTypeCode'], |
| | | }); |
| | | const codes = iots.map(e => e.iotCode).filter(Boolean); |
| | | if (!codes.length) return []; |
| | | const nameMap = new Map(iots.map(e => [e.iotCode, e.iotName])); |
| | | const qb = this.deviceAlarm |
| | | .createQueryBuilder('a') |
| | | .where('a.devSerial IN (:...codes)', { codes }) |
| | | .orderBy('a.createTime', 'DESC') |
| | | .take(30); |
| | | const rows = await qb.getMany(); |
| | | return rows.map(r => ({ |
| | | ...r, |
| | | deviceName: nameMap.get(r.devSerial) || r.devSerial, |
| | | })); |
| | | } |
| | | } |