Compare commits

..

No commits in common. "a81c6e0e645d44f914d4482e651be67f0345b6e6" and "f95a64535cd82c6b1a7ab68c39cd09c666e80584" have entirely different histories.

45 changed files with 816 additions and 3368 deletions

View File

@ -1,220 +0,0 @@
<?php
namespace App\Console\Commands\BiAng;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType;
use App\Iot\BiAng\HttpClient;
use App\Models\Device;
use App\Models\DeviceLog;
use App\Models\WormPhoto;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use RuntimeException;
use Throwable;
class DeviceLogSyncCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'biang:device-log-sync
{--sleep=60 : 设备数据同步完成后的休眠时间(秒)}';
/**
* The console command description.
*
* @var string
*/
protected $description = '按设备厂商同步数据';
/**
* Execute the console command.
*/
public function handle()
{
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 60, $this->option('sleep'));
while (true) {
$this->sync();
sleep($sleep);
};
}
/**
* 执行同步
*/
protected function sync(): void
{
$now = now();
$this->info('------------------------------------------');
$this->info('同步时间: '. $now);
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::with(['project'])
->supplierBy("device-supplier-biang")
->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline])
->get();
/** @var \App\Models\Device */
foreach ($devices as $device) {
if (! in_array($device->type, [
DeviceType::Soil,
DeviceType::Meteorological,
DeviceType::Worm,
DeviceType::InsectSexLure,
DeviceType::InsecticidalLamp,
])) {
continue;
}
$this->info('==========================================');
$this->info('设备编号: ' . $device->sn);
$this->info('设备名称: ' . $device->name);
$this->info('设备类型: ' . match ($device->type) {
DeviceType::Soil => '土壤设备',
DeviceType::Meteorological => '气象设备',
DeviceType::Worm => '虫情设备',
DeviceType::InsectSexLure => '昆虫性诱设备',
DeviceType::InsecticidalLamp => '杀虫灯设备',
});
try {
$httpClient = $this->buildHttpClient($device);
switch ($device->type) {
case DeviceType::Soil:
$data = $httpClient->getLatestSoilReport($device->sn);
if (empty($data)) {
$this->warn('设备数据为空');
break;
}
$log = DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => $data['time'],
], [
'data' => Arr::except($data, ['deviceId', 'time']),
]);
$device->update([
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
]);
break;
case DeviceType::Meteorological:
$data = $httpClient->getLatestMeteorologicalReport($device->sn);
if (empty($data)) {
$this->warn('设备数据为空');
break;
}
$log = DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => $data['time'],
], [
'data' => Arr::except($data, ['deviceId', 'time']),
]);
$device->update([
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
]);
break;
case DeviceType::InsecticidalLamp:
$data = $httpClient->getLatestLampReport($device->sn);
if (empty($data)) {
$this->warn('设备数据为空');
break;
}
$log = DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => $data['time'],
], [
'data' => Arr::except($data, ['deviceId', 'time']),
]);
$device->update([
'status' => $now->copy()->subMinutes(60)->lt($log->reported_at) ? DeviceStatus::Online : DeviceStatus::Offline,
]);
break;
case DeviceType::Worm:
case DeviceType::InsectSexLure:
$dir = "worm-photos/{$device->id}";
$disk = Storage::disk('public');
$data = $httpClient->getWormPhotos($device->sn, $now->copy()->subHours(24), $now);
if (empty($data)) {
$this->warn('设备数据为空');
break;
}
foreach ($data['imgUrl'] as $item) {
// 下载图片
$name = md5($item['url']);
if ($ext = pathinfo($item['url'], PATHINFO_EXTENSION)) {
$name .= ".{$ext}";
}
$path = "{$dir}/{$name}";
$disk = Storage::disk('public');
if (! $disk->exists($path)) {
$disk->put($path, file_get_contents($item['url']));
}
WormPhoto::updateOrCreate([
'device_id' => $device->id,
'uploaded_at' => $item['time'],
], [
'url' => $path,
]);
}
$device->update([
'status' => count($data['imgUrl'] ?? []) > 0 ? DeviceStatus::Online : DeviceStatus::Offline,
]);
break;
}
$this->info('同步成功!');
} catch (Throwable $e) {
report($e);
$this->error('同步失败: '. $e->getMessage());
}
$this->info('==========================================');
}
$this->info('------------------------------------------');
$this->newLine();
}
/**
* 创建 HTTP 客户端
*/
public function buildHttpClient(Device $device): HttpClient
{
$config = json_decode($device->project?->value, true);
if (! is_array($config)) {
throw new RuntimeException('账户信息未找到');
}
return new HttpClient($config['username'] ?? '', $config['password'] ?? '');
}
}

View File

@ -1,92 +0,0 @@
<?php
namespace App\Console\Commands\BiAng;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\WormReport;
use App\Services\BiAngDeviceService;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
class WormStatisticsSyncCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'biang:worm-statistics-sync';
/**
* The console command description.
*
* @var string
*/
protected $description = '虫情设备统计数据同步';
/**
* Execute the console command.
*/
public function handle()
{
$this->sync();
}
protected function sync(): void
{
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::with(['project'])
->supplierBy('device-supplier-biang')
->where('type', DeviceType::Worm)
->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline])
->get();
if ($devices->isEmpty()) {
$this->warn('没有找到虫情设备');
return;
}
$today = today();
foreach ($devices as $device) {
$this->info('==================================');
$latestReportedAt = WormReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at');
$start = $latestReportedAt ? $latestReportedAt->copy() : $today->copy();
$days = $start->diffInDays($today, false);
if ($days > 179) {
$end = $start->copy()->addDays(179);
} else {
if ($days < 2) {
$start = $today->copy()->subDays(2);
}
$end = $today->copy();
}
$this->info('设备编号: '.$device->sn);
$this->info('开始日期: '.$start->toDateString());
$this->info('结束日期: '.$end->toDateString());
$statistics = (new BiAngDeviceService())->getWormStatistics($device, $start, $end);
foreach (data_get($statistics, 'data.records', []) as $item) {
$data = collect(Arr::except($item, '日期'))->map(fn ($v, $k) => ['name' => $k, 'num' => $v]);
WormReport::updateOrCreate([
'device_id' => $device->id,
'reported_at' => $item['日期'],
], [
'agricultural_base_id' => $device->agricultural_base_id,
'worm_num' => $data->sum('num'),
'data' => $data->values(),
]);
}
$this->info('==================================');
}
}
}

View File

@ -1,16 +1,17 @@
<?php <?php
namespace App\Console\Commands\BiAng; namespace App\Console\Commands;
use App\Enums\DeviceType; use App\Enums\DeviceType;
use App\Models\Device; use App\Models\Device;
use App\Models\InsecticidalLampDailyReport;
use App\Models\InsecticidalLampReport;
use App\Models\MeteorologicalMonitoringDailyLog; use App\Models\MeteorologicalMonitoringDailyLog;
use App\Models\MeteorologicalMonitoringLog; use App\Models\MeteorologicalMonitoringLog;
use App\Models\SoilMonitoringDailyLog; use App\Models\SoilMonitoringDailyLog;
use App\Models\SoilMonitoringLog; use App\Models\SoilMonitoringLog;
use App\Services\BiAngDeviceService; use App\Models\WaterQualityMonitoringDailyLog;
use App\Models\WaterQualityMonitoringLog;
use App\Services\BiAngDeviceLogService;
use App\Services\DeviceLogService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class DeviceLogDailyReportCommand extends Command class DeviceLogDailyReportCommand extends Command
@ -20,7 +21,8 @@ class DeviceLogDailyReportCommand extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'biang:device-log-daily-report protected $signature = 'device-log:daily-report
{factory}
{--sleep=300 : 监控报告生产后的休眠时间(秒)}'; {--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/** /**
@ -40,21 +42,30 @@ class DeviceLogDailyReportCommand extends Command
*/ */
public function handle() public function handle()
{ {
$factory = $this->argument('factory');
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep')); $sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
while (true) { while (true) {
/** @var \Illuminate\Database\Eloquent\Collection */ /** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::supplierBy("device-supplier-biang")->get(); $devices = Device::with(['supplier'])->supplierBy("device-supplier-{$factory}")->get();
foreach ($devices as $device) { foreach ($devices as $device) {
$this->createReport($device); switch ($device->supplier?->key) {
case 'device-supplier-biang':
$this->createReportToBiAngDevice($device);
break;
}
} }
sleep($sleep); sleep($sleep);
}; };
} }
protected function createReport(Device $device): void /**
* 创建 linkos 设备报告
*/
protected function createReportToBiAngDevice(Device $device): void
{ {
[$lastReportedAt, $latestReportedAt] = value(function (Device $device) { [$lastReportedAt, $latestReportedAt] = value(function (Device $device) {
$lastReportedAt = null; $lastReportedAt = null;
@ -93,22 +104,6 @@ class DeviceLogDailyReportCommand extends Command
->value('monitored_at'); ->value('monitored_at');
} }
break; break;
case DeviceType::InsecticidalLamp:
$lastReportedAt = InsecticidalLampDailyReport::where('device_id', $device->id)
->latest('reported_at')
->value('reported_at');
$lastReportedAt ??= InsecticidalLampReport::where('device_id', $device->id)
->oldest('reported_at')
->value('reported_at');
if ($lastReportedAt) {
$latestReportedAt = InsecticidalLampReport::where('device_id', $device->id)
->latest('reported_at')
->value('reported_at');
}
break;
} }
return [$lastReportedAt, $latestReportedAt]; return [$lastReportedAt, $latestReportedAt];
@ -118,7 +113,7 @@ class DeviceLogDailyReportCommand extends Command
return; return;
} }
$service = new BiAngDeviceService(); $service = new BiAngDeviceLogService();
/** @var \Carbon\Carbon */ /** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfDay(); $startAt = $lastReportedAt->copy()->startOfDay();

View File

@ -1,14 +1,13 @@
<?php <?php
namespace App\Console\Commands\BiAng; namespace App\Console\Commands;
use App\Enums\DeviceType; use App\Enums\DeviceType;
use App\Models\Device; use App\Models\Device;
use App\Models\DeviceLog; use App\Models\DeviceLog;
use App\Models\InsecticidalLampReport;
use App\Models\MeteorologicalMonitoringLog; use App\Models\MeteorologicalMonitoringLog;
use App\Models\SoilMonitoringLog; use App\Models\SoilMonitoringLog;
use App\Services\BiAngDeviceService; use App\Services\BiAngDeviceLogService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class DeviceLogReportCommand extends Command class DeviceLogReportCommand extends Command
@ -18,7 +17,8 @@ class DeviceLogReportCommand extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'biang:device-log-report protected $signature = 'device-log:report
{factory}
{--sleep=300 : 监控报告生产后的休眠时间(秒)}'; {--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/** /**
@ -38,14 +38,20 @@ class DeviceLogReportCommand extends Command
*/ */
public function handle() public function handle()
{ {
$factory = $this->argument('factory');
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep')); $sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 300, $this->option('sleep'));
while (true) { while (true) {
/** @var \Illuminate\Database\Eloquent\Collection */ /** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::supplierBy("device-supplier-biang")->get(); $devices = Device::with(['supplier'])->supplierBy("device-supplier-{$factory}")->get();
foreach ($devices as $device) { foreach ($devices as $device) {
$this->createReport($device); switch ($device->supplier?->key) {
case 'device-supplier-biang':
$this->createReportToBiAngDevice($device);
break;
}
} }
sleep($sleep); sleep($sleep);
@ -55,12 +61,11 @@ class DeviceLogReportCommand extends Command
/** /**
* 创建比昂设备报告 * 创建比昂设备报告
*/ */
protected function createReport(Device $device): void protected function createReportToBiAngDevice(Device $device): void
{ {
$lastReportedAt = match ($device->type) { $lastReportedAt = match ($device->type) {
DeviceType::Soil => SoilMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'), DeviceType::Soil => SoilMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'),
DeviceType::Meteorological => MeteorologicalMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'), DeviceType::Meteorological => MeteorologicalMonitoringLog::where('device_id', $device->id)->latest('monitored_at')->value('monitored_at'),
DeviceType::InsecticidalLamp => InsecticidalLampReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
default => null, default => null,
}; };
@ -72,7 +77,7 @@ class DeviceLogReportCommand extends Command
return; return;
} }
$service = new BiAngDeviceService(); $service = new BiAngDeviceLogService();
/** @var \Carbon\Carbon */ /** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfHour(); $startAt = $lastReportedAt->copy()->startOfHour();

View File

@ -0,0 +1,92 @@
<?php
namespace App\Console\Commands;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Services\BiAngDeviceLogService;
use Illuminate\Console\Command;
use Throwable;
class DeviceLogSyncCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'device-log:sync
{factory}
{--sleep=60 : 设备数据同步完成后的休眠时间(秒)}';
/**
* The console command description.
*
* @var string
*/
protected $description = '按设备厂商同步数据';
/**
* Execute the console command.
*/
public function handle()
{
$factory = $this->argument('factory');
$sleep = (int) value(fn ($sleep) => is_numeric($sleep) ? $sleep : 60, $this->option('sleep'));
while (true) {
$this->runSync($factory);
sleep($sleep);
};
}
/**
* 执行同步
*/
protected function runSync(string $factory): void
{
$end = now();
$start = $end->copy()->subHours(1);
$this->info('------------------------------------------');
$this->info('开始时间: '. $start);
$this->info('结束时间: '. $end);
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::with(['supplier', 'project'])->supplierBy("device-supplier-{$factory}")->get();
/** @var \App\Models\Device */
foreach ($devices as $device) {
$this->info('==========================================');
$this->info('设备编号: ' . $device->sn);
$this->info('设备名称: ' . $device->name);
$this->info('设备类型: ' . match ($device->type) {
DeviceType::Soil => '土壤设备',
DeviceType::WaterQuality => '水质设备',
DeviceType::Meteorological => '气象设备',
default => '其它',
});
try {
switch ($device->supplier?->key) {
case 'device-supplier-biang':
(new BiAngDeviceLogService())->sync($device);
break;
}
$this->info('同步成功!');
} catch (Throwable $e) {
report($e);
$this->error('同步失败: '. $e->getMessage());
}
$this->info('==========================================');
}
$this->info('------------------------------------------');
$this->newLine();
}
}

View File

@ -1,158 +0,0 @@
<?php
namespace App\Console\Commands\Linkos;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType;
use App\Iot\Linkos\FarmClient;
use App\Models\Device;
use App\Models\WormPhoto;
use App\Models\WormReport;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Throwable;
class WormReportCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'linkos:worm-report';
/**
* The console command description.
*
* @var string
*/
protected $description = 'linkos 虫情设备数据同步';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('------------------------------------------');
$this->info(now());
try {
$this->sync();
} catch (Throwable $e) {
report($e);
}
$this->info('------------------------------------------');
}
protected function sync(): void
{
// 接口接口限制每分钟最多访问6次因此每次访问后需休眠10秒
$client = new FarmClient('xunwang', 'qwer1234');
$now = now();
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::supplierBy('device-supplier-linkos')
->where('type', DeviceType::Worm)
->whereIn('status', [DeviceStatus::Online, DeviceStatus::Offline])
->get();
if ($devices->isEmpty()) {
$this->warn('没有找到虫情设备');
return;
}
$this->info('==================================');
$this->info('尝试更新设备状态...');
$realTimeData = $client->realTimeData($devices->pluck('sn')->all());
foreach ($realTimeData as $item) {
foreach ($devices as $device) {
if ($item['deviceAddr'] != $device->sn) {
continue;
}
// 更新设备状态
$device->update([
'state' => $item['status'] === 'online' ? DeviceStatus::Online : DeviceStatus::Offline,
]);
}
}
$this->info("设备状态更新完成");
$this->info('==================================');
$this->info('尝试同步虫情区域统计...');
for ($i=2; $i > 0; $i--) {
$reportedAt = $now->copy()->subDays($i);
$statistics = collect(
$client->wormStatistics(
'E05F10DAIB6F4I4977IB95FI82554A48DE7C',
$reportedAt->copy()->startOfDay(),
$reportedAt->copy()->endOfDay(),
)
)->mapWithKeys(fn ($item) => [$item['deviceAddr'] => $item['wornData']]);
foreach ($devices as $device) {
$data = $statistics[$device->sn] ?? [];
WormReport::updateOrCreate([
'device_id' => $device->id,
'reported_at' => $reportedAt->toDateString(),
], [
'agricultural_base_id' => $device->agricultural_base_id,
'worm_num' => collect($data)->sum('num'),
'data' => $data,
]);
}
}
$this->info("同步虫情区域统计完成");
$this->info('==================================');
// 接口请求次数
$requests = 4;
// 同步最近7天的分析报表记录
$this->info('尝试同步分析报表记录...');
foreach ($devices->pluck('sn') as $sn) {
$data = $client->wormAnalyseData($sn, $now->copy()->subDays(7), $now, 1, 100);
foreach ($data['rows'] as $item) {
foreach ($devices as $device) {
if ($item['deviceAddr'] != $device->sn) {
continue;
}
$url = $item['analyseCoordUrl'] ?: $item['imagesUrl'];
// 下载图片
$name = md5($url);
if ($ext = pathinfo($url, PATHINFO_EXTENSION)) {
$name .= ".{$ext}";
}
$path = "worm-photos/{$device->id}/{$name}";
$disk = Storage::disk('public');
if (! $disk->exists($path)) {
$disk->put($path, file_get_contents($url));
}
WormPhoto::updateOrCreate([
'device_id' => $device->id,
'uploaded_at' => $item['createTime'],
], [
'url' => $path,
]);
}
}
$requests++;
// 接口请求频率: 每分钟6次
if ($requests == 6) {
$requests = 0;
sleep(61);
}
}
$this->info("同步分析报表记录完成");
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Console\Commands;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\LinkosDeviceLog;
use App\Services\LinkosDeviceLogService;
use Carbon\Carbon;
use Illuminate\Console\Command;
class LinkosDeviceLogArchiveCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'linkos:device-log-archive';
/**
* The console command description.
*
* @var string
*/
protected $description = 'LinkOS 设备流水归档';
/**
* Execute the console command.
*
* @param \App\Services\LinkosDeviceLogService $linkosDeviceLogService
* @return int
*/
public function handle(LinkosDeviceLogService $linkosDeviceLogService)
{
$devices = Device::whereIn('type', [DeviceType::Meteorological, DeviceType::Soil, DeviceType::WaterQuality])->get();
// 物联平台目前只有水质监测设备和气象监测设备
LinkosDeviceLog::orderBy('reported_at', 'asc')->lazy()->each(function ($log) use ($devices, $linkosDeviceLogService) {
if (empty($log->data)) {
return;
}
foreach ($devices as $device) {
if ($device->sn !== $log->device_id) {
continue;
}
match ($device->type) {
DeviceType::Soil => $linkosDeviceLogService->handleSoilMonitoringLog($device, $log->data, $log->reported_at),
DeviceType::Meteorological => $linkosDeviceLogService->handleMeteorologicalMonitoringLog($device, $log->data, $log->reported_at),
DeviceType::WaterQuality => $linkosDeviceLogService->handleWaterQualityMonitoringLog($device, $log->data, $log->reported_at),
};
}
});
$now = now();
$date = Carbon::parse('2022-06-01');
while ($date->lt($now)) {
foreach ($devices as $device) {
match ($device->type) {
DeviceType::Soil => $linkosDeviceLogService->handleSoilMonitoringDailyLog($device, $date),
DeviceType::Meteorological => $linkosDeviceLogService->handleMeteorologicalMonitoringDailyLog($device, $date),
DeviceType::WaterQuality => $linkosDeviceLogService->handleWaterQualityMonitoringDailyLog($device, $date),
};
}
$date->addDay();
}
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,197 @@
<?php
namespace App\Console\Commands;
use App\Exceptions\BizException;
use App\Models\LinkosDeviceLog;
use App\Services\LinkosService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
class LinkosDeviceLogSyncCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'linkos:device-log-sync {device}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'LinkOS 设备流水同步';
/**
* @var \App\Services\LinkosService
*/
protected $linkosService;
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$now = now();
$device = $this->argument('device');
// 最近同步时间
$lastDate = $this->getLastDate($device);
do {
if ($lastDate === null) {
$lastDate = Carbon::parse('2022-06-01');
} else {
$lastDate->addDay();
}
$start = $lastDate->copy()->startOfDay();
if ($start->gt($now)) {
throw new BizException('开始时间大约当前时间');
}
$end = $lastDate->copy()->endOfDay();
if ($end->gt($now)) {
$end = $now;
}
$this->info('----------------------------------');
$this->info('设备编号: '.$device);
$this->info('开始时间: '.$start->toDateTimeString());
$this->info('结束时间: '.$end->toDateTimeString());
$this->info('开始同步');
$this->info('...');
$this->synchronize($device, $start, $end);
$this->info('Done!');
$this->info('----------------------------------');
if ($now->isSameDay($lastDate)) {
break;
}
$this->setLastDate($device, $lastDate);
} while (true);
return Command::SUCCESS;
}
/**
* 同步设备历史数据
*
* @param string $device
* @param \Carbon\Carbon $start
* @param \Carbon\Carbon $end
* @return void
*/
protected function synchronize(string $device, Carbon $start, Carbon $end)
{
// 分页页码
$page = 0;
// 每页条数
$size = 50;
// 开始时间戳
$startTime = $start->unix() * 1000;
// 结束时间戳
$endTime = $end->unix() * 1000;
LinkosDeviceLog::where('device_id', $device)->whereBetween('reported_at', [$start, $end])->delete();
do {
$result = retry(5, function () use ($device, $page, $size, $startTime, $endTime) {
return $this->linkosService()->post('/deviceFlow/v1/list', [
'device_id' => $device,
'start_time' => $startTime,
'end_time' => $endTime,
'pageable' => [
'page' => $page,
'size' => $size,
],
]);
}, 100);
$data = collect($result['data']['content']);
$count = $data->count();
if ($count === 0) {
break;
}
$time = now();
LinkosDeviceLog::insert(
$data->map(function ($item) use ($time) {
return [
'device_id' => $item['device_id'],
'device_unit' => $item['device_unit'],
'device_category' => $item['device_category'],
'data' => ! empty($item['data']) ? json_encode($item['data']) : '{}',
'reported_at' => $item['createTime'],
'created_at' => $time->toDateTimeString(),
'updated_at' => $time->toDateTimeString(),
];
})->toArray()
);
unset($result, $data);
$page++;
} while ($count === $size);
}
/**
* @return \App\Services\LinkosService
*/
protected function linkosService(): LinkosService
{
if ($this->linkosService === null) {
$this->linkosService = app(LinkosService::class);
}
return $this->linkosService;
}
/**
* 获取设备最后同步日期
*
* @param string $device
* @return \Carbon\Carbon|null
*/
protected function getLastDate(string $device): ?Carbon
{
if (is_null($date = Cache::get($this->generateKey($device)))) {
return null;
}
return Carbon::parse($date);
}
/**
* 设置设备最后同步日期
*
* @param string $device
* @param \Carbon\Carbon $date
* @return void
*/
protected function setLastDate(string $device, Carbon $date): void
{
Cache::put($this->generateKey($device), $date->toDateString(), 86400);
}
/**
* @param string $device
* @return string
*/
protected function generateKey(string $device): string
{
return 'linkos_device_log:'.$device.'_last_sync_date';
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace App\Console\Commands;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\SoilMonitoringLog;
use App\Services\LinkosDeviceLogService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Throwable;
class SoilMonitoringLogFixCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'soil-monitoring-log:fix {hour? : 时间格式 Y-m-d H:i:s, 示例: 2022-11-11 00:00:00}';
/**
* The console command description.
*
* @var string
*/
protected $description = '修复土壤监控数据';
/**
* Execute the console command.
*
* @return int
*/
public function handle(LinkosDeviceLogService $linkosDeviceLogService)
{
$time = now()->subHour()->startOfHour();
if ($hour = $this->argument('hour')) {
$time = Carbon::createFromFormat('Y-m-d H:i:s', $hour)->startOfHour();
}
$devices = Device::where('type', DeviceType::Soil)
->where('status', DeviceStatus::Online)
->get();
foreach ($devices as $device) {
$last = SoilMonitoringLog::where('device_id', $device->id)
->where('monitored_at', $time->copy()->subHour())
->first();
if ($last === null) {
continue;
}
try {
$log = SoilMonitoringLog::firstOrCreate([
'device_id' => $device->id,
'monitored_at' => $time,
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
foreach ([
'conductivity',
'humidity',
'temperature',
'n',
'p',
'k',
] as $key) {
if (is_null($log->{$key})) {
$log->{$key} = $last->{$key};
}
}
if ($log->isDirty()) {
$log->is_filled = true;
}
$log->save();
if ($log->wasChanged()) {
$linkosDeviceLogService->handleSoilMonitoringDailyLog($device, $time);
}
} catch (Throwable $e) {
report($e);
}
}
return Command::SUCCESS;
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace App\Console\Commands;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\WaterQualityMonitoringLog;
use App\Services\LinkosDeviceLogService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Throwable;
class WaterQualityMonitoringLogFixCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'water-quality-monitoring-log:fix {hour? : 时间格式 Y-m-d H:i:s, 示例: 2022-11-11 00:00:00}';
/**
* The console command description.
*
* @var string
*/
protected $description = '修复水质监控数据';
/**
* Execute the console command.
*
* @return int
*/
public function handle(LinkosDeviceLogService $linkosDeviceLogService)
{
$time = now()->subHour()->startOfHour();
if ($hour = $this->argument('hour')) {
$time = Carbon::createFromFormat('Y-m-d H:i:s', $hour)->startOfHour();
}
$devices = Device::where('type', DeviceType::WaterQuality)
->where('status', DeviceStatus::Online)
->get();
foreach ($devices as $device) {
$last = WaterQualityMonitoringLog::where('device_id', $device->id)
->where('monitored_at', $time->copy()->subHour())
->first();
if ($last === null) {
continue;
}
try {
$log = WaterQualityMonitoringLog::firstOrCreate([
'device_id' => $device->id,
'monitored_at' => $time,
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
foreach ([
'chlorine',
'conductivity',
'oxygen',
'ph',
'temperature',
'turbidity',
] as $key) {
if (is_null($log->{$key})) {
$log->{$key} = $last->{$key};
}
}
if ($log->isDirty()) {
$log->is_filled = true;
}
$log->save();
if ($log->wasChanged()) {
$linkosDeviceLogService->handleWaterQualityMonitoringDailyLog($device, $time);
}
} catch (Throwable $e) {
report($e);
}
}
return Command::SUCCESS;
}
}

View File

@ -1,96 +0,0 @@
<?php
namespace App\Console\Commands\YunFei;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\InsecticidalLampDailyReport;
use App\Models\InsecticidalLampReport;
use App\Services\YunFeiDeviceService;
use Illuminate\Console\Command;
class DeviceLogDailyReportCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'yunfei:device-log-daily-report
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/**
* The console command description.
*
* @var string
*/
protected $description = '按设备厂商生成监控报告';
/**
* @var \App\Services\DeviceLogService
*/
protected $deviceLogService;
/**
* Execute the console command.
*/
public function handle()
{
$seconds = (int) value(fn ($seconds) => is_numeric($seconds) ? $seconds : 300, $this->option('sleep'));
while (true) {
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::supplierBy('device-supplier-yunfei')->get();
foreach ($devices as $device) {
$this->createReport($device);
}
sleep($seconds);
};
}
protected function createReport(Device $device): void
{
[$lastReportedAt, $latestReportedAt] = value(function (Device $device) {
$lastReportedAt = null;
$latestReportedAt = null;
switch ($device->type) {
case DeviceType::InsecticidalLamp:
$lastReportedAt = InsecticidalLampDailyReport::where('device_id', $device->id)
->latest('reported_at')
->value('reported_at');
$lastReportedAt ??= InsecticidalLampReport::where('device_id', $device->id)
->oldest('reported_at')
->value('reported_at');
if ($lastReportedAt) {
$latestReportedAt = InsecticidalLampReport::where('device_id', $device->id)
->latest('reported_at')
->value('reported_at');
}
break;
}
return [$lastReportedAt, $latestReportedAt];
}, $device);
if ($lastReportedAt === null || $latestReportedAt === null) {
return;
}
$service = new YunFeiDeviceService();
/** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfDay();
do {
$service->createDailyReport($device, $startAt->copy());
$startAt->addDay();
} while ($latestReportedAt->gte($startAt));
}
}

View File

@ -1,82 +0,0 @@
<?php
namespace App\Console\Commands\YunFei;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\DeviceLog;
use App\Models\InsecticidalLampReport;
use App\Services\YunFeiDeviceService;
use Illuminate\Console\Command;
class DeviceLogReportCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'yunfei:device-log-report
{--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/**
* The console command description.
*
* @var string
*/
protected $description = '按设备厂商生成监控报告';
/**
* @var \App\Services\DeviceLogService
*/
protected $deviceLogService;
/**
* Execute the console command.
*/
public function handle()
{
$seconds = (int) value(fn ($seconds) => is_numeric($seconds) ? $seconds : 300, $this->option('sleep'));
while (true) {
/** @var \Illuminate\Database\Eloquent\Collection */
$devices = Device::supplierBy('device-supplier-yunfei')->get();
foreach ($devices as $device) {
$this->createReport($device);
}
sleep($seconds);
};
}
/**
* 创建比昂设备报告
*/
protected function createReport(Device $device): void
{
$lastReportedAt = match ($device->type) {
DeviceType::InsecticidalLamp => InsecticidalLampReport::where('device_id', $device->id)->latest('reported_at')->value('reported_at'),
default => null,
};
if (is_null($lastReportedAt ??= DeviceLog::where('device_id', $device->id)->oldest('reported_at')->value('reported_at'))) {
return;
}
if (is_null($latestReportedAt = DeviceLog::where('device_id', $device->id)->latest('reported_at')->value('reported_at'))) {
return;
}
$service = new YunFeiDeviceService();
/** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfHour();
do {
$service->createReport($device, $startAt->copy());
$startAt->addHour();
} while ($latestReportedAt->gte($startAt));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,12 +15,12 @@ class Kernel extends ConsoleKernel
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)
{ {
$schedule->command(Commands\BiAng\WormStatisticsSyncCommand::class) $schedule->command(Commands\SoilMonitoringLogFixCommand::class)
->hourly() ->hourly()
->runInBackground(); ->runInBackground();
$schedule->command(Commands\Linkos\WormReportCommand::class) $schedule->command(Commands\WaterQualityMonitoringLogFixCommand::class)
->hourlyAt(15) ->hourly()
->runInBackground(); ->runInBackground();
} }

View File

@ -8,9 +8,7 @@ enum DeviceType: int
case Soil = 2; // 土壤设备 case Soil = 2; // 土壤设备
case WaterQuality = 3; // 水质设备 case WaterQuality = 3; // 水质设备
case Meteorological = 4; // 气象设备 case Meteorological = 4; // 气象设备
case Worm = 5; // 虫情设备 case Insect = 5; // 虫情设备
case InsectSexLure = 6; // 昆虫性诱设备
case InsecticidalLamp = 7; // 杀虫灯设备
/** /**
* @return string * @return string
@ -30,9 +28,7 @@ enum DeviceType: int
static::Soil->value => '土壤设备', static::Soil->value => '土壤设备',
static::WaterQuality->value => '水质设备', static::WaterQuality->value => '水质设备',
static::Meteorological->value => '气象设备', static::Meteorological->value => '气象设备',
static::Worm->value => '虫情设备', static::Insect->value => '虫情设备',
static::InsectSexLure->value => '昆虫性诱设备',
static::InsecticidalLamp->value => '杀虫灯设备',
]; ];
} }
} }

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions;
use RuntimeException;
class BiAngException extends RuntimeException
{
}

View File

@ -1,9 +0,0 @@
<?php
namespace App\Exceptions;
use RuntimeException;
class LinkosFarmException extends RuntimeException
{
}

View File

@ -27,7 +27,6 @@ class AdminPermissionController extends Controller
$res[] = [ $res[] = [
'id' => $permission->id, 'id' => $permission->id,
'label' => $permission->name, 'label' => $permission->name,
'slug' => $permission->slug,
'children' => $this->formatPermissionsTreeToArray($permission->children ?? []), 'children' => $this->formatPermissionsTreeToArray($permission->children ?? []),
]; ];
} }

View File

@ -9,7 +9,6 @@ use App\Enums\OperationType;
use App\Helpers\Paginator; use App\Helpers\Paginator;
use App\Http\Requestes\AgriculturalBaseRequest; use App\Http\Requestes\AgriculturalBaseRequest;
use App\Http\Resources\AgriculturalBaseResource; use App\Http\Resources\AgriculturalBaseResource;
use App\Http\Resources\DeviceResource;
use App\Models\AgriculturalBase; use App\Models\AgriculturalBase;
use App\Models\Device; use App\Models\Device;
use App\Services\OperationLogService; use App\Services\OperationLogService;
@ -127,13 +126,9 @@ class AgriculturalBaseController extends Controller
{ {
$deviceType = $request->input('device_type', DeviceType::Monitor); $deviceType = $request->input('device_type', DeviceType::Monitor);
// $status = $request->input('status'); // $status = $request->input('status');
$isRecommend = $request->input('is_recommended', 0) ?? 0; $list = AgriculturalBase::filter($request->all())->whereHas('devices', function ($q) use ($deviceType) {
$list = AgriculturalBase::filter($request->all())->whereHas('devices', function ($q) use ($deviceType, $isRecommend) {
if($deviceType == DeviceType::Monitor || $deviceType == 1){ if($deviceType == DeviceType::Monitor || $deviceType == 1){
if($isRecommend){ $q->where('is_recommend', 1)->where('status', 1);
$q->where('is_recommend', 1);
}
$q->where('status', 1);
} }
return $q->where('type', $deviceType); return $q->where('type', $deviceType);
})->sort()->get(); })->sort()->get();
@ -143,9 +138,9 @@ class AgriculturalBaseController extends Controller
/** /**
* 获取指定基地指定设备类型下所有监控点名称选项 * 获取指定基地指定设备类型下所有监控点名称选项
* device_type
*/ */
public function basePointList(AgriculturalBase $agriculturalBasic, Request $request) public function basePointList(AgriculturalBase $agriculturalBasic, Request $request){
{
$deviceType = $request->input('device_type', DeviceType::Meteorological); $deviceType = $request->input('device_type', DeviceType::Meteorological);
$list = Device::where([ $list = Device::where([
'agricultural_base_id' => $agriculturalBasic->id, 'agricultural_base_id' => $agriculturalBasic->id,
@ -153,11 +148,4 @@ class AgriculturalBaseController extends Controller
])->orderBy('sort', 'desc')->get()->pluck('monitoring_point', 'id')->toArray(); ])->orderBy('sort', 'desc')->get()->pluck('monitoring_point', 'id')->toArray();
return $this->json($list); return $this->json($list);
} }
public function basePoints(Request $request)
{
$devices = Device::filter($request->input())->orderBy('sort', 'desc')->get();
return DeviceResource::collection($devices);
}
} }

View File

@ -3,7 +3,6 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\AdminUser; use App\Models\AdminUser;
use App\Models\AdminPermission;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@ -35,12 +34,6 @@ class AuthController extends Controller
{ {
$token = $user->createToken($name)->plainTextToken; $token = $user->createToken($name)->plainTextToken;
$permissionsQuery = AdminPermission::query(); return $this->json(['token' => $token, 'info' => $user, 'permissions' => $user->permissionIds()]);
if($user->id != 1){
$permissions = $permissionsQuery->whereIn('id', $user->permissionIds());
}
$permissions = $permissionsQuery->pluck('slug')->toArray();
return $this->json(['token' => $token, 'info' => $user, 'permissions' => $user->permissionIds(), 'permissions_slug'=>$permissions]);
} }
} }

View File

@ -1,121 +0,0 @@
<?php
namespace App\Http\Controllers\Callback;
use App\Enums\DeviceStatus;
use App\Http\Controllers\Controller;
use App\Models\Device;
use App\Models\DeviceLog;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
class YunFeiController extends Controller
{
/**
* 杀虫灯回调通知
*/
public function insecticidalLampNotify(Request $request)
{
logger()->debug('杀虫灯回调通知', $request->input());
$payload = $request->input('payload');
if (! isset($payload['ext'])) {
return;
}
$device = Device::supplierBy('device-supplier-yunfei')->where('sn', $payload['ext']['imei'])->first();
if ($device === null) {
return;
}
if (! in_array($device->status, [DeviceStatus::Online, DeviceStatus::Offline])) {
return;
}
switch ($payload['cmd']) {
case 'data':
$device->update([
'status' => DeviceStatus::Online,
]);
DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => Carbon::createFromFormat('YmdHis', $payload['ext']['stamp']),
], [
'data' => $payload['ext'],
]);
break;
case 'offline':
$device->update([
'status' => DeviceStatus::Offline,
]);
break;
}
}
/**
* 测报灯回调通知
*/
public function wormNotify(Request $request)
{
logger()->debug('虫情设备回调通知', $request->input());
$payload = $request->input('payload');
if (! isset($payload['ext'])) {
return;
}
$device = Device::supplierBy('device-supplier-yunfei')->where('sn', $payload['ext']['imei'])->first();
if ($device === null) {
return;
}
if (! in_array($device->status, [DeviceStatus::Online, DeviceStatus::Offline])) {
return;
}
switch ($payload['cmd']) {
case 'data':
$device->update([
'status' => DeviceStatus::Online,
]);
break;
case 'offline':
$device->update([
'status' => DeviceStatus::Offline,
]);
break;
}
}
/**
* 测报灯照片回调通知
*/
public function wormPhotoNotify(Request $request)
{
logger()->debug('虫情图片通知', $request->input());
$device = Device::supplierBy('device-supplier-yunfei')->where('sn', $request->input('imei'))->first();
if ($device === null) {
return;
}
if (! in_array($device->status, [DeviceStatus::Online, DeviceStatus::Offline])) {
return;
}
DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => now(),
], [
'data' => $request->input(),
]);
}
}

View File

@ -8,25 +8,18 @@ use App\Enums\OperationType;
use App\Helpers\Paginator; use App\Helpers\Paginator;
use App\Http\Requestes\DeviceRequest; use App\Http\Requestes\DeviceRequest;
use App\Http\Resources\DeviceResource; use App\Http\Resources\DeviceResource;
use App\Http\Resources\WormPhotoResource;
use App\Models\AgriculturalBase; use App\Models\AgriculturalBase;
use App\Models\Device; use App\Models\Device;
use App\Models\InsecticidalLampDailyReport;
use App\Models\InsecticidalLampReport;
use App\Models\MeteorologicalMonitoringDailyLog; use App\Models\MeteorologicalMonitoringDailyLog;
use App\Models\MeteorologicalMonitoringLog; use App\Models\MeteorologicalMonitoringLog;
use App\Models\SoilMonitoringDailyLog; use App\Models\SoilMonitoringDailyLog;
use App\Models\SoilMonitoringLog; use App\Models\SoilMonitoringLog;
use App\Models\WaterQualityMonitoringDailyLog; use App\Models\WaterQualityMonitoringDailyLog;
use App\Models\WaterQualityMonitoringLog; use App\Models\WaterQualityMonitoringLog;
use App\Models\WormPhoto;
use App\Models\WormReport;
use App\Services\BiAngDeviceService;
use App\Services\OperationLogService; use App\Services\OperationLogService;
use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Peidikeji\Setting\Models\Setting; use Peidikeji\Setting\Models\Setting;
class DeviceController extends Controller class DeviceController extends Controller
@ -422,46 +415,48 @@ class DeviceController extends Controller
} }
} }
public function timeZoneList(Request $request) /**
{ * 查询设备今天(按天),近一周(按天),近一个月(按天)
$request->validate([ */
'device_id' => ['bail', 'required'], public function timeZoneList(Request $request){
'start_time' => ['bail', 'nullable', 'date_format:Y-m-d'], $deviceId = $request->input('device_id');
'end_time' => ['bail', 'nullable', 'date_format:Y-m-d'], //不传开始时间,结束时间,则默认是查当天(按小时)
]); $startTime = $request->input('start_time');
$endTime = $request->input('end_time');
$isSameDay = true; $diffDays = 0;
$day = date('Y-m-d');
if ($request->filled('start_time') && $request->filled('end_time')) { //如果传了开始时间和结束时间,计算中间天数
$startTime = Carbon::parse($request->input('start_time'))->startOfDay(); if($startTime && $endTime){
$endTime = Carbon::parse($request->input('end_time'))->startOfDay(); if($startTime == $endTime){//查询某一天
$day = $startTime;
if ($startTime->gt($endTime)) { }else{
throw ValidationException::withMessages([ $startDay = Carbon::parse($startTime);
'start_time' => ['开始时间不能大于结束时间'], $endDay = Carbon::parse($endTime);
]); $diffDays = $startDay->diffInDays($endDay, false);
} }
// 如果开始时间和结束时间是同一天
if ($startTime->eq($endTime)) {
$endTime = $startTime->isToday() ? now() : $startTime->copy()->endOfDay();
} else {
$isSameDay = false;
}
} else {
$endTime = now();
$startTime = $endTime->copy()->startOfDay();
} }
$device = Device::findOrFail($request->input('device_id')); $xKeys = [];
if($diffDays){
$fields = []; for ($i = 0; $i<=$diffDays; $i++) {
$monitoringLogs = collect(); $xKeys[] =(clone $startDay)->addDays($i)->startOfDay()->format('Y-m-d H:i:s');
}
}else{
//调整截至到当前小时
$h = 23;
if($day == date('Y-m-d')){
$h = date('H');
}
for ($i = 0; $i < ($h+1); $i++) {
$xKeys[] = $day.' '.str_pad($i, 2, '0', STR_PAD_LEFT).':00:00';
}
}
$device = Device::find($deviceId);
$modelQuery = null;
$getArr = [];
switch ($device->type) { switch ($device->type) {
// 气象设备 case DeviceType::Meteorological://气象设备
case DeviceType::Meteorological: $getArr = [
$fields = [
'wind_speed', 'wind_speed',
'wind_direction', 'wind_direction',
'wind_degree', 'wind_degree',
@ -473,34 +468,17 @@ class DeviceController extends Controller
'illumination', 'illumination',
'pm25', 'pm25',
'pm10', 'pm10',
'rainfall',
]; ];
/** @var \Illuminate\Support\Collection */ if($diffDays) {
$monitoringLogs = ( $getArr[] = 'daily_rainfall';
$isSameDay $modelQuery = MeteorologicalMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
? MeteorologicalMonitoringLog::query() }else{
: MeteorologicalMonitoringDailyLog::query() $getArr[] = 'current_rainfall';
) $modelQuery = MeteorologicalMonitoringLog::query()->whereDate('monitored_at', $day);
->where('device_id', $device->id) }
->whereBetween('monitored_at', [$startTime, $endTime])
->get()
->mapWithKeys(function ($item) use ($fields) {
$key = $item->monitored_at->toDateTimeString();
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $field !== 'rainfall' ? $item[$field] : null])->all();
if ($item instanceof MeteorologicalMonitoringDailyLog) {
$data['rainfall'] = $item->daily_rainfall;
} else {
$data['rainfall'] = $item->current_rainfall;
}
return [$key => $data];
});
break; break;
case DeviceType::Soil://土壤设备
// 土壤设备 $getArr = [
case DeviceType::Soil:
$fields = [
'conductivity', 'conductivity',
'humidity', 'humidity',
'temperature', 'temperature',
@ -508,25 +486,14 @@ class DeviceController extends Controller
'p', 'p',
'k', 'k',
]; ];
/** @var \Illuminate\Support\Collection */ if($diffDays) {
$monitoringLogs = ( $modelQuery = SoilMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
$isSameDay }else{
? SoilMonitoringLog::query() $modelQuery = SoilMonitoringLog::query()->whereDate('monitored_at', $day);
: SoilMonitoringDailyLog::query() }
)
->where('device_id', $device->id)
->whereBetween('monitored_at', [$startTime, $endTime])
->get()
->mapWithKeys(function ($item) use ($fields) {
$key = $item->monitored_at->toDateTimeString();
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
return [$key => $data];
});
break; break;
case DeviceType::WaterQuality://水质设备
// 水质设备 $getArr = [
case DeviceType::WaterQuality:
$fields = [
'chlorine', 'chlorine',
'conductivity', 'conductivity',
'oxygen', 'oxygen',
@ -534,96 +501,62 @@ class DeviceController extends Controller
'temperature', 'temperature',
'turbidity', 'turbidity',
]; ];
/** @var \Illuminate\Support\Collection */ if($diffDays) {
$monitoringLogs = ( $modelQuery = WaterQualityMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]);
$isSameDay }else{
? WaterQualityMonitoringLog::query() $modelQuery = WaterQualityMonitoringLog::query()->whereDate('monitored_at', $day);
: WaterQualityMonitoringDailyLog::query() }
)
->where('device_id', $device->id)
->whereBetween('monitored_at', [$startTime, $endTime])
->get()
->mapWithKeys(function ($item) use ($fields) {
$key = $item->monitored_at->toDateTimeString();
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
return [$key => $data];
});
break;
// 杀虫灯
case DeviceType::InsecticidalLamp:
$fields = [
'battery_vol',
'killed_num',
'air_temperature',
'air_humidity',
'charging_vol',
'high_vol',
];
/** @var \Illuminate\Support\Collection */
$monitoringLogs = (
$isSameDay
? InsecticidalLampReport::query()
: InsecticidalLampDailyReport::query()
)
->where('device_id', $device->id)
->whereBetween('reported_at', [$startTime, $endTime])
->get()
->mapWithKeys(function ($item) use ($fields) {
$key = $item->reported_at->toDateTimeString();
$data = collect($fields)->mapWithKeys(fn ($field) => [$field => $item[$field]])->all();
return [$key => $data];
});
break; break;
} }
if($modelQuery){
return $this->json( $datalist = $modelQuery->where('device_id', $deviceId)->get()->keyBy('monitored_at')->toArray();
collect($fields)->mapWithKeys(function ($field) use ($device, $monitoringLogs, $isSameDay, $startTime, $endTime) { }
$data = []; $data = [];
foreach ($getArr as $column){
$beginTime = $startTime->copy(); $data[$column] = [];
$_value = null;
do { foreach($xKeys as $key){
$key = $beginTime->toDateTimeString(); if($device->type == DeviceType::WaterQuality){//如果是水质设备,则写死假数据
switch($column){
$monitoringLog = $monitoringLogs->get($key); case 'chlorine':
$data[$column][$key] = 0.016;
if (is_null($monitoringLog)) { break;
// 如果是水质设备,则写死假数据 case 'conductivity':
if($device->type == DeviceType::WaterQuality){ $data[$column][$key] = 563 ;//电导率
switch($field){ break;
case 'chlorine': case 'oxygen':
$data[$key] = 0.016; $data[$column][$key] = 0.09;//含氧量
break; break;
case 'conductivity': case 'ph':
$data[$key] = rand(560, 565);//电导率 $data[$column][$key] = rand(750, 770) / 100;
break; break;
case 'oxygen': case 'temperature':
$data[$key] = 0.09;//含氧量 $data[$column][$key] = rand(2400, 2600) / 100;
break; break;
case 'ph': case 'turbidity':
$data[$key] = rand(750, 770) / 100; $data[$column][$key] = 0.33;
break; break;
case 'temperature':
$data[$key] = rand(2400, 2600) / 100;
break;
case 'turbidity':
$data[$key] = 0.33;
break;
}
} else {
$data[$key] = null;
}
} else {
$data[$key] = $monitoringLog[$field];
} }
}else{
$isSameDay ? $beginTime->addHours(1) : $beginTime->addDays(1); // if($datalist[$key][$column] ?? null){//如果存在数据则暂存该值
} while ($beginTime->lte($endTime)); // $_value = $datalist[$key][$column];
// }
return [$field => $data]; // //判断是否超过离线时间;
}) // if(true){//未超过, 判断和设备离线时间关系-todo
); // $data[$column][$key] = $_value;
// }else{
$data[$column][$key] = $datalist[$key][$column] ?? null;
// }
}
}
}
//强制统一气象降雨量,日和天字段不统一问题
if(isset($data['daily_rainfall'])) {
$data['rainfall'] = $data['daily_rainfall'];
}elseif(isset($data['current_rainfall'])){
$data['rainfall'] = $data['current_rainfall'];
}
return $this->json($data);
} }
public function getFfmpegServiceIp(){ public function getFfmpegServiceIp(){
@ -639,90 +572,4 @@ class DeviceController extends Controller
return $this->json($data); return $this->json($data);
} }
/**
* 虫情统计
*/
public function wormStatics($id, Request $request)
{
$request->validate([
'start_time' => ['bail', 'required_with:end_time', 'date_format:Y-m-d'],
'end_time' => ['bail', 'required_with:start_time', 'date_format:Y-m-d'],
], [], [
'start_time' => '开始时间',
'end_time' => '结束时间',
]);
// 结束时间
$endTime = $request->whenFilled(
'end_time',
fn ($time) => Carbon::parse($time)->startOfDay(),
fn () => now()->startOfDay(),
);
// 开始时间
$startTime = $request->whenFilled(
'start_time',
fn ($time) => Carbon::parse($time)->startOfDay(),
fn () => $endTime->copy()->subDays(6),
);
if ($startTime->gt($endTime)) {
throw ValidationException::withMessages([
'start_time' => ['开始时间不能大于结束时间'],
]);
}
$wormReports = WormReport::where('device_id', $id)
->whereBetween('reported_at', [$startTime->toDateString(), $endTime->toDateString()])
->pluck('worm_num', 'reported_at');
$data = [];
if ($startTime->lte($endTime)) {
do {
// 日期
$date = $startTime->toDateString();
$data[$date] = $wormReports->get($date);
$startTime->addDay();
} while ($startTime->lte($endTime));
}
return $this->json($data);
}
/**
* 虫情图片
*/
public function wormPhotos($id, Request $request, BiAngDeviceService $biAngDeviceService)
{
$request->validate([
'start_time' => ['bail', 'required_with:end_time', 'date_format:Y-m-d'],
'end_time' => ['bail', 'required_with:start_time', 'date_format:Y-m-d'],
], [], [
'start_time' => '开始时间',
'end_time' => '结束时间',
]);
// 结束时间
$endTime = $request->whenFilled(
'end_time',
fn ($time) => Carbon::parse($time)->endOfDay(),
fn () => now()->endOfDay(),
);
// 开始时间
$startTime = $request->whenFilled(
'start_time',
fn ($time) => Carbon::parse($time)->startOfDay(),
fn () => $endTime->copy()->startOfDay(),
);
$wormPhotos = WormPhoto::where('device_id', $id)
->whereBetween('uploaded_at', [$startTime, $endTime])
->latest('uploaded_at')
->paginate($request->input('per_page', 20));
return WormPhotoResource::collection($wormPhotos);
}
} }

View File

@ -62,7 +62,7 @@ class RiceShrimpFlowController extends Controller
'createdBy' => $user, 'createdBy' => $user,
'updatedBy' => $user, 'updatedBy' => $user,
]) ])
)->additional(['message'=>'保存成功']); );
} }
/** /**
@ -98,7 +98,7 @@ class RiceShrimpFlowController extends Controller
return RiceShrimpFlowResource::make( return RiceShrimpFlowResource::make(
$riceShrimpFlow->loadMissing(['createdBy', 'updatedBy']) $riceShrimpFlow->loadMissing(['createdBy', 'updatedBy'])
)->additional(['message'=>'更新成功']); );
} }
/** /**
@ -115,6 +115,6 @@ class RiceShrimpFlowController extends Controller
(new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpFlow); (new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpFlow);
return response()->json(['message'=>'删除成功']); return response()->json(null);
} }
} }

View File

@ -65,7 +65,7 @@ class RiceShrimpIndustryController extends Controller
'createdBy' => $user, 'createdBy' => $user,
'updatedBy' => $user, 'updatedBy' => $user,
]) ])
)->additional(['message'=>'保存成功']); );
} }
/** /**
@ -104,7 +104,7 @@ class RiceShrimpIndustryController extends Controller
return RiceShrimpIndustryResource::make( return RiceShrimpIndustryResource::make(
$riceShrimpIndustry->loadMissing(['createdBy', 'updatedBy']) $riceShrimpIndustry->loadMissing(['createdBy', 'updatedBy'])
)->additional(['message'=>'更新成功']); );
} }
/** /**
@ -121,6 +121,6 @@ class RiceShrimpIndustryController extends Controller
(new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpIndustry); (new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpIndustry);
return response()->json(['message'=>'删除成功']); return response()->json(null);
} }
} }

View File

@ -59,7 +59,7 @@ class RiceShrimpPriceController extends Controller
(new OperationLogService())->inLog(OperationType::Create, '', $riceShrimpPrice, $request->input()); (new OperationLogService())->inLog(OperationType::Create, '', $riceShrimpPrice, $request->input());
return RiceShrimpPriceResource::make($riceShrimpPrice)->additional(['message'=>'保存成功']); return RiceShrimpPriceResource::make($riceShrimpPrice);
} }
/** /**
@ -93,7 +93,7 @@ class RiceShrimpPriceController extends Controller
return RiceShrimpPriceResource::make( return RiceShrimpPriceResource::make(
$riceShrimpPrice->loadMissing(['createdBy', 'updatedBy']) $riceShrimpPrice->loadMissing(['createdBy', 'updatedBy'])
)->additional(['message'=>'更新成功']); );
} }
/** /**
@ -110,6 +110,6 @@ class RiceShrimpPriceController extends Controller
(new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpPrice); (new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpPrice);
return response()->json(['message'=>'删除成功']); return response()->json(null);
} }
} }

View File

@ -60,7 +60,7 @@ class RiceShrimpWeeklyPriceController extends Controller
(new OperationLogService())->inLog(OperationType::Create, '', $riceShrimpWeeklyPrice, $request->input()); (new OperationLogService())->inLog(OperationType::Create, '', $riceShrimpWeeklyPrice, $request->input());
return RiceShrimpWeeklyPriceResource::make($riceShrimpWeeklyPrice->loadMissing(['weekObj']))->additional(['message'=>'保存成功']); return RiceShrimpWeeklyPriceResource::make($riceShrimpWeeklyPrice->loadMissing(['weekObj']));
} }
/** /**
@ -94,7 +94,7 @@ class RiceShrimpWeeklyPriceController extends Controller
return RiceShrimpWeeklyPriceResource::make( return RiceShrimpWeeklyPriceResource::make(
$riceShrimpWeeklyPrice->loadMissing(['createdBy', 'updatedBy', 'weekObj']) $riceShrimpWeeklyPrice->loadMissing(['createdBy', 'updatedBy', 'weekObj'])
)->additional(['message'=>'更新成功']); );
} }
/** /**
@ -111,6 +111,6 @@ class RiceShrimpWeeklyPriceController extends Controller
(new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpWeeklyPrice); (new OperationLogService())->inLog(OperationType::Delete, '', $riceShrimpWeeklyPrice);
return response()->json(['message'=>'删除成功']); return response()->json(null);
} }
} }

View File

@ -5,21 +5,14 @@ namespace App\Http\Controllers;
use App\Http\Requestes\RestPasswordRequest; use App\Http\Requestes\RestPasswordRequest;
use App\Services\OperationLogService; use App\Services\OperationLogService;
use App\Enums\OperationType; use App\Enums\OperationType;
use App\Models\AdminPermission;
class UserController extends Controller class UserController extends Controller
{ {
public function info() public function info()
{ {
$user = auth('api')->user(); $user = auth('api')->user();
$permissionsQuery = AdminPermission::query();
if($user->id != 1){
$permissions = $permissionsQuery->whereIn('id', $user->permissionIds());
}
$permissions = $permissionsQuery->pluck('slug')->toArray();
return $this->json(['info' => $user, 'permissions' => $user->permissionIds(), 'permissions_slug'=>$permissions]); return $this->json(['info' => $user, 'permissions' => $user->permissionIds()]);
} }
public function resetPwd(RestPasswordRequest $request) public function resetPwd(RestPasswordRequest $request)

View File

@ -39,7 +39,7 @@ class Kernel extends HttpKernel
], ],
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:600,1', 'throttle:600,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\FormatJsonResponse::class, \App\Http\Middleware\FormatJsonResponse::class,

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class WormPhotoResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'url' => $this->image_url,
'time' => $this->uploaded_at->toDateTimeString(),
];
}
}

View File

@ -2,7 +2,6 @@
namespace App\Iot\BiAng; namespace App\Iot\BiAng;
use App\Exceptions\BiAngException;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -28,7 +27,13 @@ class HttpClient
] ]
); );
return $result['data'] ?? []; if (data_get($result, 'code') === 200) {
return $result['data'];
}
throw new RuntimeException(
sprintf("%s:%s", data_get($result, 'code', 500), data_get($result, 'msg', '出错啦!'))
);
} }
/** /**
@ -43,56 +48,13 @@ class HttpClient
] ]
); );
return $result['data'] ?? []; if (data_get($result, 'code') === 200) {
} return $result['data'];
}
/** throw new RuntimeException(
* 获取最新的杀虫灯数据 sprintf("%s:%s", data_get($result, 'code', 500), data_get($result, 'msg', '出错啦!'))
*/
public function getLatestLampReport(string $deviceId)
{
$result = $this->get(
$this->apiUrl2('/open-api/open/getCurrentLampData'),
[
'deviceId' => $deviceId,
]
); );
return $result['data'] ?? [];
}
/**
* 虫情设备/昆虫性诱设备 - 查询某个时间段内的图片
*/
public function getWormPhotos(string $deviceId, Carbon $start, Carbon $end)
{
$result = $this->get(
$this->apiUrl('/api/open-api/open/getDevicePhotos'),
[
'deviceId' => $deviceId,
'startTime' => $start->toDateString(),
'endTime' => $end->toDateString(),
]
);
return $result['data'] ?? [];
}
/**
* 虫情设备 - (识别款)图片虫数识别统计
*/
public function getWormStatistics(string $deviceId, Carbon $start, Carbon $end)
{
$result = $this->get(
$this->apiUrl('/api/open-api/open/getAllStatistics'),
[
'imei' => $deviceId,
'startAt' => $start->toDateString(),
'endAt' => $end->toDateString(),
]
);
return $result['data'] ?? [];
} }
public function get(string $url, array $query = []): array public function get(string $url, array $query = []): array
@ -146,13 +108,7 @@ class HttpClient
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
])->send($method, $url, $options); ])->send($method, $url, $options);
$result = $response->throw()->json(); return $response->throw()->json();
if (data_get($result, 'code') === 200) {
return $result;
}
throw new BiAngException($result['code'].':'.($result['msg']??'出错啦!'), 500);
} }
protected function apiUrl(string $path): string protected function apiUrl(string $path): string

View File

@ -1,153 +0,0 @@
<?php
namespace App\Iot\Linkos;
use App\Exceptions\LinkosFarmException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
class FarmClient
{
public const ENDPOINT_URL = 'http://api.farm.0531yun.cn';
/**
* 授权令牌
*
* @var string
*/
protected $token;
/**
* 授权令牌有效期
*
* @var int
*/
protected $expires;
public function __construct(
protected readonly string $loginName,
protected readonly string $loginPwd,
) {
}
/**
* 获取设备列表
*/
public function devices(?string $groupId = null, ?string $deviceType = null): array
{
$result = $this->get('/api/v2.0/entrance/device/getsysUserDevice', [
'groupId' => $groupId,
'deviceType' => $deviceType,
]);
return $result['data'];
}
/**
* 获取用户区域
*/
public function groups(?string $groupName = null): array
{
$result = $this->get('/api/v2.0/entrance/group/getsysUserGroup', [
'groupName' => $groupName,
]);
return $result['data'];
}
/**
* 获取设备实时数据
*/
public function realTimeData(array $devices = []): array
{
$result = $this->get('/api/v2.0/entrance/device/getRealTimeData', [
'deviceAddrs' => implode(',', $devices),
]);
return $result['data'];
}
/**
* 虫情区域统计
*/
public function wormStatistics(string $groupId, Carbon $beginTime, Carbon $endTime): array
{
$result = $this->get('/api/v2.0/worm/deviceData/getWormStatisticsByGroup', [
'groupId' => $groupId,
'beginTime' => $beginTime->toDateTimeString(),
'endTime' => $endTime->toDateTimeString(),
]);
return $result['data'];
}
/**
* 虫情设备分析报表
*/
public function wormAnalyseData(string $device, Carbon $beginTime, Carbon $endTime, int $pages, int $limit = 100): array
{
$result = $this->get('/api/v2.0/worm/deviceData/getWormDataList', [
'deviceAddr' => $device,
'beginTime' => $beginTime->toDateTimeString(),
'endTime' => $endTime->toDateTimeString(),
'pages' => $pages,
'limit' => $limit,
]);
return $result['data'];
}
public function get(string $url, array $query = []): array
{
return $this->request('GET', $url, [
'query' => $query,
]);
}
public function post(string $url, array $data = []): array
{
return $this->request('POST', $url, [
'json' => $data,
]);
}
protected function request(string $method, string $url, array $options = []): array
{
$headers = [
'Content-Type' => 'application/json',
];
if ($url !== '/api/v2.0/entrance/user/userLogin') {
$headers['token'] = $this->token();
}
/** @var \Illuminate\Http\Client\Response */
$response = Http::withHeaders($headers)->baseUrl(self::ENDPOINT_URL)->send($method, $url, $options);
$json = $response->throw()->json();
if (data_get($json, 'code') === 1000) {
return $json;
}
throw new LinkosFarmException(
data_get($json, 'message', '出错啦'),
data_get($json, 'code', 0),
);
}
protected function token(): string
{
if ($this->token && $this->expires > now()->unix() + 30) {
return $this->token;
}
$result = $this->post('/api/v2.0/entrance/user/userLogin', [
'loginName' => $this->loginName,
'loginPwd' => $this->loginPwd,
]);
$this->expires = $result['data']['expDate'];
return $this->token = $result['data']['token'];
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class InsecticidalLampDailyReport extends Model
{
use HasFactory;
protected $casts = [
'reported_at' => 'date',
];
protected $fillable = [
'device_id',
'agricultural_base_id',
'battery_vol',
'killed_num',
'air_temperature',
'air_humidity',
'charging_vol',
'high_vol',
'reported_at',
];
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class InsecticidalLampReport extends Model
{
use HasFactory;
protected $casts = [
'reported_at' => 'datetime',
];
protected $fillable = [
'device_id',
'agricultural_base_id',
'battery_vol',
'killed_num',
'air_temperature',
'air_humidity',
'charging_vol',
'high_vol',
'reported_at',
];
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class WormPhoto extends Model
{
use HasFactory;
protected $casts = [
'uploaded_at' => 'datetime',
];
protected $fillable = [
'device_id', 'url', 'uploaded_at',
];
public function imageUrl(): Attribute
{
return Attribute::make(
get: function(mixed $value, array $attributes) {
if (preg_match('/^https?:\/\//', $attributes['url']) > 0) {
return $attributes['url'];
}
return Storage::disk('public')->url($attributes['url']);
},
);
}
}

View File

@ -1,20 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class WormReport extends Model
{
use HasFactory;
protected $casts = [
'data' => 'json',
'reported_at' => 'date',
];
protected $fillable = [
'device_id', 'agricultural_base_id', 'worm_num', 'data', 'reported_at',
];
}

View File

@ -2,48 +2,69 @@
namespace App\Services; namespace App\Services;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType; use App\Enums\DeviceType;
use App\Exceptions\BizException; use App\Iot\BiAng\HttpClient as BiAngHttpClient;
use App\Iot\BiAng\HttpClient;
use App\Models\Device; use App\Models\Device;
use App\Models\DeviceLog; use App\Models\DeviceLog;
use App\Models\InsecticidalLampDailyReport;
use App\Models\InsecticidalLampReport;
use App\Models\MeteorologicalMonitoringDailyLog; use App\Models\MeteorologicalMonitoringDailyLog;
use App\Models\MeteorologicalMonitoringLog; use App\Models\MeteorologicalMonitoringLog;
use App\Models\SoilMonitoringDailyLog; use App\Models\SoilMonitoringDailyLog;
use App\Models\SoilMonitoringLog; use App\Models\SoilMonitoringLog;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
class BiAngDeviceService class BiAngDeviceLogService
{ {
/** public function sync(Device $device): void
* 虫情设备/昆虫性诱设备 - 查询某个时间段内的图片
*/
public function getWormPhotos(Device $device, Carbon $start, Carbon $end): array
{ {
try { $config = json_decode($device->project?->value, true);
$httpClient = $this->buildHttpClient($device); if (! isset($config['username'], $config['password'])) {
} catch (BizException $e) { return;
return [];
} }
return $httpClient->getWormPhotos($device->sn, $start, $end); $httpClient = new BiAngHttpClient($config['username'], $config['password']);
}
/** $data = null;
* 虫情设备 - (识别款)图片虫数识别统计
*/ switch ($device->type) {
public function getWormStatistics(Device $device, Carbon $start, Carbon $end): array case DeviceType::Soil:
{ $data = $httpClient->getLatestSoilReport($device->sn);
try { break;
$httpClient = $this->buildHttpClient($device);
} catch (BizException $e) { case DeviceType::Meteorological:
return []; $data = $httpClient->getLatestMeteorologicalReport($device->sn);
break;
} }
return $httpClient->getWormStatistics($device->sn, $start, $end); if (is_array($data) && isset($data['time'])) {
$log = DeviceLog::firstOrCreate([
'device_id' => $device->id,
'reported_at' => $data['time'],
], [
'data' => Arr::except($data, ['deviceId', 'time']),
]);
switch ($device->status) {
case DeviceStatus::Online:
// 如果设备数据在线时最近60分钟内没有数据则视为离线
if (now()->subMinutes(60)->gt($log->reported_at)) {
$device->update([
'status' => DeviceStatus::Offline,
]);
}
break;
case DeviceStatus::Offline:
// 如果设备数据离线时最近60分钟内有数据则视为在线
if (now()->subMinutes(60)->lt($log->reported_at)) {
$device->update([
'status' => DeviceStatus::Online,
]);
}
break;
}
}
} }
/** /**
@ -59,10 +80,6 @@ class BiAngDeviceService
case DeviceType::Meteorological: case DeviceType::Meteorological:
$this->createMeteorologicalReport($device, $time); $this->createMeteorologicalReport($device, $time);
break; break;
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampReport($device, $time);
break;
} }
} }
@ -222,90 +239,6 @@ class BiAngDeviceService
$meteorologicalReport->fill($attributes)->save(); $meteorologicalReport->fill($attributes)->save();
} }
/**
* 创建杀虫灯设备报告
*/
protected function createInsecticidalLampReport(Device $device, Carbon $time): void
{
$reportedAt = $time->copy()->startOfHour();
/** @var \Illuminate\Database\Eloquent\Collection */
$logs = DeviceLog::where('device_id', $device->id)
->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()])
->oldest('reported_at')
->get();
if ($logs->isEmpty()) {
return;
}
$attributes = value(function ($logs) {
$data = [
'vol' => ['sum' => 0, 'count' => 0],
'sunVol' => ['sum' => 0, 'count' => 0],
'dct' => ['sum' => 0, 'count' => 0],
'temp' => ['sum' => 0, 'count' => 0],
'humidity' => ['sum' => 0, 'count' => 0],
'highVol' => ['sum' => 0, 'count' => 0],
];
/** @var \App\Models\DeviceLog */
foreach ($logs as $log) {
if (! is_array($log->data)) {
continue;
}
foreach ($data as $k => $item) {
$v = $log->data[$k] ?? null;
if (is_null($v)) {
continue;
}
$item['sum'] = bcadd($item['sum'], $v, 2);
$item['count']++;
$data[$k] = $item;
}
}
$attributes = [];
foreach ($data as $key => $item) {
$attribute = match ($key) {
'vol' => 'battery_vol',
'sunVol' => 'charging_vol',
'dct' => 'killed_num',
'temp' => 'air_temperature',
'humidity' => 'air_humidity',
'highVol' => 'high_vol',
};
if ($item['count'] > 0) {
if ($attribute === 'killed_num') {
$attributes[$attribute] = (int) $item['sum'];
} else {
$attributes[$attribute] = round(bcdiv($item['sum'], $item['count'], 2), 2);
}
} else {
$attributes[$attribute] = null;
}
}
return $attributes;
}, $logs);
/** @var \App\Models\InsecticidalLampReport */
$insecticidalLampReport = InsecticidalLampReport::firstOrNew([
'device_id' => $device->id,
'reported_at' => $reportedAt,
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
$insecticidalLampReport->fill($attributes)->save();
}
/** /**
* 创建设备每日报告 * 创建设备每日报告
*/ */
@ -319,10 +252,6 @@ class BiAngDeviceService
case DeviceType::Soil: case DeviceType::Soil:
$this->createSoilDailyReport($device, $time); $this->createSoilDailyReport($device, $time);
break; break;
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampDailyReport($device, $time);
break;
} }
} }
@ -521,84 +450,4 @@ class BiAngDeviceService
$meteorologicalDailyReport->fill($attributes)->save(); $meteorologicalDailyReport->fill($attributes)->save();
} }
/**
* 杀虫灯每日报告
*/
protected function createInsecticidalLampDailyReport(Device $device, Carbon $date): void
{
/** @var \Illuminate\Database\Eloquent\Collection */
$insecticidalLampReports = InsecticidalLampReport::where('device_id', $device->id)
->whereDate('reported_at', $date)
->oldest('reported_at')
->get();
if ($insecticidalLampReports->isEmpty()) {
return;
}
$attributes = value(function ($insecticidalLampReports) {
$data = [
'killed_num' => ['sum' => 0, 'count' => 0],
'battery_vol' => ['sum' => 0, 'count' => 0],
'air_temperature' => ['sum' => 0, 'count' => 0],
'air_humidity' => ['sum' => 0, 'count' => 0],
'charging_vol' => ['sum' => 0, 'count' => 0],
'high_vol' => ['sum' => 0, 'count' => 0],
];
foreach ($insecticidalLampReports as $insecticidalLampReport) {
foreach ($data as $k => $item) {
if (is_null($v = $insecticidalLampReport->{$k})) {
continue;
}
$item['sum'] = bcadd($item['sum'], $v, 2);
$item['count']++;
$data[$k] = $item;
}
}
$attributes = [];
foreach ($data as $key => $item) {
if ($item['count'] > 0) {
if ($key === 'killed_num') {
$attributes[$key] = (int) $item['sum'];
} else {
$attributes[$key] = round(bcdiv($item['sum'], $item['count'], 2), 2);
}
} else {
$attributes[$key] = null;
}
}
return $attributes;
}, $insecticidalLampReports);
/** @var \App\Models\InsecticidalLampDailyReport */
$insecticidalLampDailyReport = InsecticidalLampDailyReport::firstOrNew([
'device_id' => $device->id,
'reported_at' => $date->format('Y-m-d'),
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
$insecticidalLampDailyReport->fill($attributes)->save();
}
/**
* 创建 HTTP 客户端
*/
public function buildHttpClient(Device $device): HttpClient
{
$config = json_decode($device->project?->value, true);
if (! is_array($config)) {
throw new BizException('账户信息未找到');
}
return new HttpClient($config['username'] ?? '', $config['password'] ?? '');
}
} }

View File

@ -1,211 +0,0 @@
<?php
namespace App\Services;
use App\Enums\DeviceType;
use App\Models\Device;
use App\Models\DeviceLog;
use App\Models\InsecticidalLampDailyReport;
use App\Models\InsecticidalLampReport;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
class YunFeiDeviceService
{
/**
* 创建设备报告
*/
public function createReport(Device $device, Carbon $time): void
{
switch ($device->type) {
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampReport($device, $time);
break;
}
}
/**
* 创建杀虫灯设备报告
*/
protected function createInsecticidalLampReport(Device $device, Carbon $time): void
{
$reportedAt = $time->copy()->startOfHour();
/** @var \Illuminate\Database\Eloquent\Collection */
$logs = DeviceLog::where('device_id', $device->id)
->whereBetween('reported_at', [$reportedAt, $reportedAt->copy()->endOfHour()])
->oldest('reported_at')
->get();
if ($logs->isEmpty()) {
return;
}
$attributes = value(function ($logs) {
$data = [
'battery_vol' => ['sum' => 0, 'count' => 0],
'charging_vol' => ['sum' => 0, 'count' => 0],
'killed_num' => ['sum' => 0, 'count' => 0],
'air_temperature' => ['sum' => 0, 'count' => 0],
'air_humidity' => ['sum' => 0, 'count' => 0],
];
/** @var \App\Models\DeviceLog */
foreach ($logs as $log) {
if (! is_array($log->data)) {
continue;
}
foreach ($data as $key => $item) {
switch ($key) {
// 电池电压
case 'battery_vol':
if (! is_null($bv = Arr::get($log->data, 'bv'))) {
$item['sum'] = bcadd($item['sum'], $bv, 2);
$item['count'] += 1;
}
break;
// 充电电压
case 'charging_vol':
if (! is_null($cv = Arr::get($log->data, 'cv'))) {
$item['sum'] = bcadd($item['sum'], $cv, 2);
$item['count'] += 1;
}
break;
// 杀虫数
case 'killed_num':
if (! is_null($ct = Arr::get($log->data, 'ct'))) {
$item['sum'] += $ct;
$item['count'] += 1;
}
break;
// 温度
case 'air_temperature':
if (! is_null($at = Arr::get($log->data, 'at'))) {
$item['sum'] = bcadd($item['sum'], $at, 2);
$item['count'] += 1;
}
break;
// 湿度
case 'air_humidity':
if (! is_null($ah = Arr::get($log->data, 'ah'))) {
$item['sum'] = bcadd($item['sum'], $ah, 2);
$item['count'] += 1;
}
break;
}
$data[$key] = $item;
}
}
$attributes = [];
foreach ($data as $key => $item) {
if ($item['count'] > 0) {
if ($key === 'killed_num') {
$attributes[$key] = (int) $item['sum'];
} else {
$attributes[$key] = round(bcdiv($item['sum'], $item['count'], 2), 2);
}
} else {
$attributes[$key] = null;
}
}
return $attributes;
}, $logs);
/** @var \App\Models\InsecticidalLampReport */
$insecticidalLampReport = InsecticidalLampReport::firstOrNew([
'device_id' => $device->id,
'reported_at' => $reportedAt,
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
$insecticidalLampReport->fill($attributes)->save();
}
/**
* 创建设备每日报告
*/
public function createDailyReport(Device $device, Carbon $time): void
{
switch ($device->type) {
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampDailyReport($device, $time);
break;
}
}
/**
* 杀虫灯每日报告
*/
protected function createInsecticidalLampDailyReport(Device $device, Carbon $date): void
{
/** @var \Illuminate\Database\Eloquent\Collection */
$insecticidalLampReports = InsecticidalLampReport::where('device_id', $device->id)
->whereDate('reported_at', $date)
->oldest('reported_at')
->get();
if ($insecticidalLampReports->isEmpty()) {
return;
}
$attributes = value(function ($insecticidalLampReports) {
$data = [
'battery_vol' => ['sum' => 0, 'count' => 0],
'charging_vol' => ['sum' => 0, 'count' => 0],
'killed_num' => ['sum' => 0, 'count' => 0],
'air_temperature' => ['sum' => 0, 'count' => 0],
'air_humidity' => ['sum' => 0, 'count' => 0],
'high_vol' => ['sum' => 0, 'count' => 0],
];
foreach ($insecticidalLampReports as $insecticidalLampReport) {
foreach ($data as $k => $item) {
if (is_null($v = $insecticidalLampReport->{$k})) {
continue;
}
$item['sum'] = bcadd($item['sum'], $v, 2);
$item['count']++;
$data[$k] = $item;
}
}
$attributes = [];
foreach ($data as $key => $item) {
if ($item['count'] > 0) {
if ($key === 'killed_num') {
$attributes[$key] = (int) $item['sum'];
} else {
$attributes[$key] = round(bcdiv($item['sum'], $item['count'], 2), 2);
}
} else {
$attributes[$key] = null;
}
}
return $attributes;
}, $insecticidalLampReports);
/** @var \App\Models\InsecticidalLampDailyReport */
$insecticidalLampDailyReport = InsecticidalLampDailyReport::firstOrNew([
'device_id' => $device->id,
'reported_at' => $date->format('Y-m-d'),
], [
'agricultural_base_id' => $device->agricultural_base_id,
]);
$insecticidalLampDailyReport->fill($attributes)->save();
}
}

View File

@ -1,36 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('worm_reports', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('device_id');
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
$table->unsignedInteger('worm_num')->comment('虫子数量');
$table->json('data')->nullable();
$table->date('reported_at')->comment('报告日期');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('worm_reports');
}
};

View File

@ -1,40 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('insecticidal_lamp_reports', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('device_id');
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
$table->double('battery_vol')->nullable()->comment('蓄电池电压');
$table->unsignedInteger('killed_num')->default(0)->comment('杀虫数');
$table->double('air_temperature')->nullable()->comment('大气气温');
$table->double('air_humidity')->nullable()->comment('大气湿度');
$table->double('charging_vol')->nullable()->comment('充电电压');
$table->double('high_vol')->nullable()->comment('高压值');
$table->timestamp('reported_at')->comment('监控日期');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('insecticidal_lamp_reports');
}
};

View File

@ -1,40 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('insecticidal_lamp_daily_reports', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('device_id');
$table->unsignedBigInteger('agricultural_base_id')->comment('农业基地ID');
$table->double('battery_vol')->nullable()->comment('蓄电池电压');
$table->unsignedInteger('killed_num')->default(0)->comment('杀虫数');
$table->double('air_temperature')->nullable()->comment('大气气温');
$table->double('air_humidity')->nullable()->comment('大气湿度');
$table->double('charging_vol')->nullable()->comment('充电电压');
$table->double('high_vol')->nullable()->comment('高压值');
$table->date('reported_at')->comment('监控日期');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('insecticidal_lamp_reports');
}
};

View File

@ -1,36 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('worm_photos', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('device_id');
$table->text('url')->nullable();
$table->timestamp('uploaded_at')->comment('上传时间');
$table->timestamps();
$table->index('uploaded_at');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('worm_photos');
}
};

View File

@ -15,112 +15,46 @@ class EndpointPermissionSeeder extends Seeder
public function run() public function run()
{ {
$permissions = [ $permissions = [
'endpoint' => [ 'endpoint' => ['name' => '系统权限', 'curd' => false, 'in_path' => true, 'children' => [
'name' => '系统权限', 'monitor_data' => ['name' => '监测数据管理', 'curd' => false, 'children' => [
'curd' => false, 'weather' => ['name' => '气象管理', 'curd' => ['index']],
'in_path' => true, 'camera' => ['name' => '智能监控', 'curd' => ['index']],
'children' => [ 'soil' => ['name' => '土壤监控', 'curd' => ['index'], 'children' => ['setting' => '设置', 'setting_edit' => '更新设置']],
'data_dashboard' => [ 'water' => ['name' => '水质监控', 'curd' => ['index'], 'children' => ['setting' => '设置', 'setting_edit' => '更新设置']],
'name' => '数据看板', ]],
'curd' => false, 'base_data' => ['name' => '基础数据管理', 'curd' => false, 'children' => [
], 'citydata_statistics' => ['name' => '全市基础数据', 'curd' => ['index', 'edit']],
'monitor_data' => [ //全市基础数据
'name' => '监测数据管理', 'agricultural_basic' => ['name' => '基地数据', 'curd' => true],
'curd' => false, 'town_street' => ['name' => '城镇数据', 'curd' => ['index', 'edit']],
'children' => [ //农作物数据
'weather' => ['name' => '气象管理', 'curd' => ['index']], 'crops' => ['name' => '基地农作物', 'curd' => true],
'camera' => ['name' => '智能监控', 'curd' => ['index']], 'town_crops' => ['name' => '镇街农作物', 'curd' => true],
'soil' => [ //农作物产量
'name' => '土壤监控', 'crops_output' => ['name' => '基地产量', 'curd' => true],
'curd' => ['index'], 'town_crops_output' => ['name' => '镇街产量', 'curd' => true],
'children' => [ // 'crops_flow' => ['name' => '农产品流向', 'curd' => true],
'setting' => '设置', // 'crops_price' => ['name' => '农产品价格走势', 'curd' => ['index'], 'children' => ['is_enable' => '自动监测开关']],
'setting_edit' => '更新设置' ]],
], 'industry_data' => ['name' => '重点产业', 'curd' => false, 'children' => [
], 'rice_shrimp_prices' => ['name' => '稻虾价格', 'curd' => true],
'water' => [ 'rice_shrimp_weekly_prices' => ['name' => '稻虾每周价格', 'curd' => true],
'name' => '水质监控', 'rice_shrimp_industries' => ['name' => '稻虾产业', 'curd' => true],
'curd' => ['index'], 'rice_shrimp_flows' => ['name' => '稻虾走向', 'curd' => true],
'children' => [ 'materiels' => ['name' => '大宗物资', 'curd' => true],
'setting' => '设置', ]],
'setting_edit' => '更新设置', 'device_data' => ['name' => '设备管理', 'curd' => false, 'children' => [
], 'device' => ['name' => '设备管理', 'curd' => true],
], ]],
'insect' => ['name' => '昆虫性诱监测', 'curd' => ['index']], 'manage' => ['name' => '系统管理', 'curd' => false, 'children' => [
'worm_statics' => ['name' => '虫情监测', 'curd' => ['index']], 'admin_users' => ['name' => '管理员管理', 'curd' => true, 'children' => [
'insecticidal_lamp' => ['name' => '杀虫灯监测', 'curd' => ['index']], 'edit_password' => '修改密码', 'enable' => '启用/禁用',
],
],
'base_data' => [
'name' => '基础数据管理',
'curd' => false,
'children' => [
'citydata_statistics' => [
'name' => '全市基础数据',
'curd' => false,
'children'=>[
'town_street' => [
'name' => '城镇数据',
'curd' => ['index', 'edit'],
'children'=>[
'base_statistics'=>'市基础数据',
'base_statistics_edit'=>'修改市基础数据'
]
],
//全市基础数据
'agricultural_basic' => ['name' => '基地数据', 'curd' => true],
],
],
//农作物数据
'citydata_crops' =>['name'=>'农作物数据','curd'=>false, 'children'=>[
'town_crops' => ['name' => '城镇农作物', 'curd' => true],
'crops' => ['name' => '基地农作物', 'curd' => true],
]],
//农作物产量
'citydata_output'=>['name'=>'农作物产量','curd'=>false, 'children'=>[
'town_crops_output' => ['name' => '城镇产量', 'curd' => true],
'crops_output' => ['name' => '基地产量', 'curd' => true],
]],
// 重点产业
'industry_data' => [
'name' => '重点产业',
'curd' => false,
'children' => [
'rice_shrimp_prices' => ['name' => '稻虾价格', 'curd' => true],
'rice_shrimp_weekly_prices' => ['name' => '稻虾每周价格', 'curd' => true],
'rice_shrimp_industries' => ['name' => '稻虾产业', 'curd' => true],
'rice_shrimp_flows' => ['name' => '稻虾走向', 'curd' => true],
'materiels' => ['name' => '大宗物资', 'curd' => true],
],
],
],
],
'device_data' => [
'name' => '设备管理',
'curd' => false,
'children' => [
'device' => ['name' => '设备管理', 'curd' => true],
'warnings' => ['name' => '警报明细', 'curd' => false,'children'=>['mark'=>'标记'],
],
]], ]],
'manage' => [ 'admin_roles' => ['name' => '角色管理', 'curd' => true],
'name' => '系统管理', 'operation_log' => ['name' => '操作日志', 'curd' => ['index']],
'curd' => false, 'friend_links' => ['name' => '友情链接', 'curd' => true]
'children' => [ ]],
'admin_roles' => ['name' => '角色管理', 'curd' => true], ]]
'admin_users' => [
'name' => '账号管理',
'curd' => true,
'children' => [
'edit_password' => '修改密码', 'enable' => '启用/禁用',
]
],
'operation_log' => ['name' => '系统日志', 'curd' => ['index']],
'friend_links' => ['name' => '友情链接', 'curd' => true],
],
],
],
],
]; ];
$this->createPermissionData($permissions); $this->createPermissionData($permissions);
} }

View File

@ -52,7 +52,6 @@ class KeywordsTableSeeder extends Seeder
'list' => [ 'list' => [
['key' => 'device-supplier-linkos', 'name' => '慧联无限', 'value' => ''], ['key' => 'device-supplier-linkos', 'name' => '慧联无限', 'value' => ''],
['key' => 'device-supplier-biang', 'name' => '比昂', 'value' => ''], ['key' => 'device-supplier-biang', 'name' => '比昂', 'value' => ''],
['key' => 'device-supplier-yunfei', 'name' => '云飞', 'value' => ''],
['key' => 'device-supplier-other', 'name' => '其它', 'value' => ''], ['key' => 'device-supplier-other', 'name' => '其它', 'value' => ''],
], ],
], ],

View File

@ -17,9 +17,7 @@ use Illuminate\Support\Facades\Route;
Route::post('auth/login', [AuthController::class, 'login']); Route::post('auth/login', [AuthController::class, 'login']);
Route::group([ Route::group(['middleware' => 'auth:sanctum'], function () {
'middleware' => ['auth:sanctum'],
], function () {
Route::post('web/upload', [WebController::class, 'upload']); Route::post('web/upload', [WebController::class, 'upload']);
@ -41,7 +39,6 @@ Route::group([
Route::apiResource('town-street', AgriculturalBaseController::class)->names('town_street');//镇街 Route::apiResource('town-street', AgriculturalBaseController::class)->names('town_street');//镇街
Route::get('agricultural-device-basic', [AgriculturalBaseController::class, 'deviceBase'])->name('agricultural_basic.device_bases'); //通过设备类型查询基地 Route::get('agricultural-device-basic', [AgriculturalBaseController::class, 'deviceBase'])->name('agricultural_basic.device_bases'); //通过设备类型查询基地
Route::get('agricultural-device-point/{agricultural_basic}', [AgriculturalBaseController::class, 'basePointList']);//获取基地对应监控点 Route::get('agricultural-device-point/{agricultural_basic}', [AgriculturalBaseController::class, 'basePointList']);//获取基地对应监控点
Route::get('agricultural-device-points', [AgriculturalBaseController::class, 'basePoints']);
//农作物 //农作物
Route::apiResource('crops', CropController::class)->names('crops');//基地农作物 Route::apiResource('crops', CropController::class)->names('crops');//基地农作物
@ -57,8 +54,6 @@ Route::group([
Route::apiResource('crop-flows', CropFlowController::class)->names('crops_flow'); Route::apiResource('crop-flows', CropFlowController::class)->names('crops_flow');
//设备管理 //设备管理
Route::apiResource('devices', DeviceController::class)->names('device'); Route::apiResource('devices', DeviceController::class)->names('device');
Route::get('devices/{device}/worm-statics', [DeviceController::class, 'wormStatics']);
Route::get('devices/{device}/worm-photos', [DeviceController::class, 'wormPhotos']);
Route::put('devices-update-recommend/{device}', [DeviceController::class, 'updateRecommendStatus']); Route::put('devices-update-recommend/{device}', [DeviceController::class, 'updateRecommendStatus']);
Route::get('devices-num', [DeviceController::class, 'typeStatusNum'])->name('device.type_status_num'); Route::get('devices-num', [DeviceController::class, 'typeStatusNum'])->name('device.type_status_num');
Route::get('monitoring-data', [DeviceController::class, 'timeZoneList']); Route::get('monitoring-data', [DeviceController::class, 'timeZoneList']);

View File

@ -1,15 +1,11 @@
<?php <?php
use App\Http\Controllers\Callback\LinkosController; use App\Http\Controllers\Callback\LinkosController;
use App\Http\Controllers\Callback\YunFeiController;
use App\Http\Controllers\ThirdApi\SendSmsController;
use App\Http\Middleware\ApiCustomToken;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use App\Http\Middleware\ApiCustomToken;
use App\Http\Controllers\ThirdApi\SendSmsController;
Route::post('callback/linkos', LinkosController::class); Route::post('callback/linkos', LinkosController::class);
Route::post('callback/yunfei/insecticidal-lamp-notify', [YunFeiController::class, 'insecticidalLampNotify']);
Route::post('callback/yunfei/worm-notify', [YunFeiController::class, 'wormNotify']);
Route::post('callback/yunfei/worm-photo-notify', [YunFeiController::class, 'wormPhotoNotify']);
Route::group(['prefix'=>'third'], function(){ Route::group(['prefix'=>'third'], function(){
Route::middleware([ApiCustomToken::class])->group(function(){ Route::middleware([ApiCustomToken::class])->group(function(){