Compare commits

...

44 Commits

Author SHA1 Message Date
Jing Li a81c6e0e64 Upate 2023-10-20 16:41:34 +08:00
vine_liutk a557c1c1ab 调整权限返回 2023-10-20 15:41:14 +08:00
vine_liutk 5fd786e6a6 补充前端权限初始化文件 2023-10-20 15:26:52 +08:00
Jing Li 6cbb8c0336 Update 2023-10-20 11:05:45 +08:00
Jing Li 2cbf5314a5 Fix 2023-10-20 10:58:45 +08:00
vine_liutk 7137b11950 Merge branch 'main' of https://gitea.hmily.club/pdkj/lcly-data-admin 2023-09-26 17:36:12 +08:00
vine_liutk d700385a1c 调整 2023-09-26 17:36:07 +08:00
Jing Li a73ba36aa9 Update 2023-08-31 19:43:31 +08:00
Jing Li 8cdf72150d Update 2023-08-31 15:57:08 +08:00
Jing Li 8bbe097382 Fix 2023-08-31 15:51:55 +08:00
Jing Li 4358858f1f 监测点 2023-08-31 15:42:57 +08:00
Jing Li 261997d651 Update 2023-08-30 20:08:00 +08:00
Jing Li 74fc14fd90 Update 2023-08-30 16:04:29 +08:00
Jing Li 01a057bfc8 Update 2023-08-30 14:09:15 +08:00
Jing Li a037f82a5f 优化虫情统计和虫情照片 2023-08-30 12:13:13 +08:00
vine_liutk a723e40d25 调整处理 2023-08-30 10:08:36 +08:00
vine_liutk b8dc7bb497 Merge branch 'main' of https://gitea.hmily.club/pdkj/lcly-data-admin 2023-08-30 09:50:31 +08:00
vine_liutk bbe315f9df 调整 2023-08-30 09:50:24 +08:00
Jing Li fe42931606 Update 2023-08-29 20:58:09 +08:00
Jing Li 644b37da28 Update 2023-08-29 20:48:37 +08:00
Jing Li c8ae7e78a9 Update 2023-08-29 20:16:51 +08:00
Jing Li f365898601 虫情统计 2023-08-29 20:03:45 +08:00
Jing Li d6f424a38a Update 2023-08-28 21:02:31 +08:00
Jing Li 375d230460 Fix 2023-08-28 20:07:43 +08:00
Jing Li cd9a6bc3d0 虫情数据 2023-08-28 16:39:51 +08:00
Jing Li 61dc022054 Update 2023-08-28 16:34:58 +08:00
Jing Li 83281649fb Update 2023-08-25 20:46:51 +08:00
Jing Li 84bb61ec7c Update 2023-08-25 18:04:56 +08:00
Jing Li ffc2155e3f 云飞数据回调 2023-08-25 17:54:28 +08:00
Jing Li 97f3f89631 Update 2023-08-24 15:55:15 +08:00
Jing Li 550ea41ab7 Update 2023-08-12 21:52:47 +08:00
Jing Li 5c28b8c093 虫情设备/昆虫性诱设备状态检查 2023-08-12 18:14:55 +08:00
Jing Li f491a1bf84 Update 2023-08-12 17:31:07 +08:00
Jing Li 943099a5d3 Update 2023-08-12 13:59:46 +08:00
Jing Li 9c037037c6 Update 2023-08-12 13:25:24 +08:00
Jing Li 317622f0fc 杀虫灯每日报告 2023-08-12 13:22:50 +08:00
Jing Li 31f0c0a88b Update 2023-08-12 12:51:10 +08:00
Jing Li 49df94fd0b Update 2023-08-11 21:00:37 +08:00
Jing Li 72a0b03412 Fix 2023-08-11 21:00:13 +08:00
Jing Li 56226b84a6 Update 2023-08-11 20:56:09 +08:00
Jing Li 72f056cf04 Fix 2023-08-11 20:55:28 +08:00
Jing Li 6e8b53e50d Update 2023-08-11 20:35:23 +08:00
Jing Li 7715f5213c Fix 2023-08-11 19:47:56 +08:00
Jing Li d304f30a52 Update 2023-08-11 19:44:51 +08:00
45 changed files with 3368 additions and 816 deletions

View File

@ -1,17 +1,16 @@
<?php <?php
namespace App\Console\Commands; namespace App\Console\Commands\BiAng;
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\Models\WaterQualityMonitoringDailyLog; use App\Services\BiAngDeviceService;
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
@ -21,8 +20,7 @@ class DeviceLogDailyReportCommand extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'device-log:daily-report protected $signature = 'biang:device-log-daily-report
{factory}
{--sleep=300 : 监控报告生产后的休眠时间(秒)}'; {--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/** /**
@ -42,30 +40,21 @@ 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::with(['supplier'])->supplierBy("device-supplier-{$factory}")->get(); $devices = Device::supplierBy("device-supplier-biang")->get();
foreach ($devices as $device) { foreach ($devices as $device) {
switch ($device->supplier?->key) { $this->createReport($device);
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;
@ -104,6 +93,22 @@ 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];
@ -113,7 +118,7 @@ class DeviceLogDailyReportCommand extends Command
return; return;
} }
$service = new BiAngDeviceLogService(); $service = new BiAngDeviceService();
/** @var \Carbon\Carbon */ /** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfDay(); $startAt = $lastReportedAt->copy()->startOfDay();

View File

@ -1,13 +1,14 @@
<?php <?php
namespace App\Console\Commands; namespace App\Console\Commands\BiAng;
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\BiAngDeviceLogService; use App\Services\BiAngDeviceService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class DeviceLogReportCommand extends Command class DeviceLogReportCommand extends Command
@ -17,8 +18,7 @@ class DeviceLogReportCommand extends Command
* *
* @var string * @var string
*/ */
protected $signature = 'device-log:report protected $signature = 'biang:device-log-report
{factory}
{--sleep=300 : 监控报告生产后的休眠时间(秒)}'; {--sleep=300 : 监控报告生产后的休眠时间(秒)}';
/** /**
@ -38,20 +38,14 @@ 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::with(['supplier'])->supplierBy("device-supplier-{$factory}")->get(); $devices = Device::supplierBy("device-supplier-biang")->get();
foreach ($devices as $device) { foreach ($devices as $device) {
switch ($device->supplier?->key) { $this->createReport($device);
case 'device-supplier-biang':
$this->createReportToBiAngDevice($device);
break;
}
} }
sleep($sleep); sleep($sleep);
@ -61,11 +55,12 @@ class DeviceLogReportCommand extends Command
/** /**
* 创建比昂设备报告 * 创建比昂设备报告
*/ */
protected function createReportToBiAngDevice(Device $device): void protected function createReport(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,
}; };
@ -77,7 +72,7 @@ class DeviceLogReportCommand extends Command
return; return;
} }
$service = new BiAngDeviceLogService(); $service = new BiAngDeviceService();
/** @var \Carbon\Carbon */ /** @var \Carbon\Carbon */
$startAt = $lastReportedAt->copy()->startOfHour(); $startAt = $lastReportedAt->copy()->startOfHour();

View File

@ -0,0 +1,220 @@
<?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

@ -0,0 +1,92 @@
<?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,92 +0,0 @@
<?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

@ -0,0 +1,158 @@
<?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

@ -1,74 +0,0 @@
<?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

@ -1,197 +0,0 @@
<?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

@ -1,93 +0,0 @@
<?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

@ -1,93 +0,0 @@
<?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

@ -0,0 +1,96 @@
<?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

@ -0,0 +1,82 @@
<?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\SoilMonitoringLogFixCommand::class) $schedule->command(Commands\BiAng\WormStatisticsSyncCommand::class)
->hourly() ->hourly()
->runInBackground(); ->runInBackground();
$schedule->command(Commands\WaterQualityMonitoringLogFixCommand::class) $schedule->command(Commands\Linkos\WormReportCommand::class)
->hourly() ->hourlyAt(15)
->runInBackground(); ->runInBackground();
} }

View File

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

View File

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

View File

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

View File

@ -27,6 +27,7 @@ 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,6 +9,7 @@ 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;
@ -126,9 +127,13 @@ 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');
$list = AgriculturalBase::filter($request->all())->whereHas('devices', function ($q) use ($deviceType) { $isRecommend = $request->input('is_recommended', 0) ?? 0;
$list = AgriculturalBase::filter($request->all())->whereHas('devices', function ($q) use ($deviceType, $isRecommend) {
if($deviceType == DeviceType::Monitor || $deviceType == 1){ if($deviceType == DeviceType::Monitor || $deviceType == 1){
$q->where('is_recommend', 1)->where('status', 1); if($isRecommend){
$q->where('is_recommend', 1);
}
$q->where('status', 1);
} }
return $q->where('type', $deviceType); return $q->where('type', $deviceType);
})->sort()->get(); })->sort()->get();
@ -138,9 +143,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,
@ -148,4 +153,11 @@ 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,6 +3,7 @@
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;
@ -34,6 +35,12 @@ class AuthController extends Controller
{ {
$token = $user->createToken($name)->plainTextToken; $token = $user->createToken($name)->plainTextToken;
return $this->json(['token' => $token, 'info' => $user, 'permissions' => $user->permissionIds()]); $permissionsQuery = AdminPermission::query();
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

@ -0,0 +1,121 @@
<?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,18 +8,25 @@ 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
@ -415,48 +422,46 @@ class DeviceController extends Controller
} }
} }
/** public function timeZoneList(Request $request)
* 查询设备今天(按天),近一周(按天),近一个月(按天) {
*/ $request->validate([
public function timeZoneList(Request $request){ 'device_id' => ['bail', 'required'],
$deviceId = $request->input('device_id'); 'start_time' => ['bail', 'nullable', 'date_format:Y-m-d'],
//不传开始时间,结束时间,则默认是查当天(按小时) 'end_time' => ['bail', 'nullable', 'date_format:Y-m-d'],
$startTime = $request->input('start_time'); ]);
$endTime = $request->input('end_time');
$diffDays = 0; $isSameDay = true;
$day = date('Y-m-d');
//如果传了开始时间和结束时间,计算中间天数 if ($request->filled('start_time') && $request->filled('end_time')) {
if($startTime && $endTime){ $startTime = Carbon::parse($request->input('start_time'))->startOfDay();
if($startTime == $endTime){//查询某一天 $endTime = Carbon::parse($request->input('end_time'))->startOfDay();
$day = $startTime;
}else{ if ($startTime->gt($endTime)) {
$startDay = Carbon::parse($startTime); throw ValidationException::withMessages([
$endDay = Carbon::parse($endTime); 'start_time' => ['开始时间不能大于结束时间'],
$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();
} }
$xKeys = []; $device = Device::findOrFail($request->input('device_id'));
if($diffDays){
for ($i = 0; $i<=$diffDays; $i++) { $fields = [];
$xKeys[] =(clone $startDay)->addDays($i)->startOfDay()->format('Y-m-d H:i:s'); $monitoringLogs = collect();
}
}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://气象设备 // 气象设备
$getArr = [ case DeviceType::Meteorological:
$fields = [
'wind_speed', 'wind_speed',
'wind_direction', 'wind_direction',
'wind_degree', 'wind_degree',
@ -468,17 +473,34 @@ class DeviceController extends Controller
'illumination', 'illumination',
'pm25', 'pm25',
'pm10', 'pm10',
'rainfall',
]; ];
if($diffDays) { /** @var \Illuminate\Support\Collection */
$getArr[] = 'daily_rainfall'; $monitoringLogs = (
$modelQuery = MeteorologicalMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]); $isSameDay
}else{ ? MeteorologicalMonitoringLog::query()
$getArr[] = 'current_rainfall'; : MeteorologicalMonitoringDailyLog::query()
$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',
@ -486,14 +508,25 @@ class DeviceController extends Controller
'p', 'p',
'k', 'k',
]; ];
if($diffDays) { /** @var \Illuminate\Support\Collection */
$modelQuery = SoilMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]); $monitoringLogs = (
}else{ $isSameDay
$modelQuery = SoilMonitoringLog::query()->whereDate('monitored_at', $day); ? SoilMonitoringLog::query()
} : 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',
@ -501,62 +534,96 @@ class DeviceController extends Controller
'temperature', 'temperature',
'turbidity', 'turbidity',
]; ];
if($diffDays) { /** @var \Illuminate\Support\Collection */
$modelQuery = WaterQualityMonitoringDailyLog::query()->whereBetween('monitored_at', [$startTime, $endTime]); $monitoringLogs = (
}else{ $isSameDay
$modelQuery = WaterQualityMonitoringLog::query()->whereDate('monitored_at', $day); ? WaterQualityMonitoringLog::query()
} : 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){
$datalist = $modelQuery->where('device_id', $deviceId)->get()->keyBy('monitored_at')->toArray(); return $this->json(
} collect($fields)->mapWithKeys(function ($field) use ($device, $monitoringLogs, $isSameDay, $startTime, $endTime) {
$data = []; $data = [];
foreach ($getArr as $column){
$data[$column] = []; $beginTime = $startTime->copy();
$_value = null;
foreach($xKeys as $key){ do {
if($device->type == DeviceType::WaterQuality){//如果是水质设备,则写死假数据 $key = $beginTime->toDateTimeString();
switch($column){
case 'chlorine': $monitoringLog = $monitoringLogs->get($key);
$data[$column][$key] = 0.016;
break; if (is_null($monitoringLog)) {
case 'conductivity': // 如果是水质设备,则写死假数据
$data[$column][$key] = 563 ;//电导率 if($device->type == DeviceType::WaterQuality){
break; switch($field){
case 'oxygen': case 'chlorine':
$data[$column][$key] = 0.09;//含氧量 $data[$key] = 0.016;
break; break;
case 'ph': case 'conductivity':
$data[$column][$key] = rand(750, 770) / 100; $data[$key] = rand(560, 565);//电导率
break; break;
case 'temperature': case 'oxygen':
$data[$column][$key] = rand(2400, 2600) / 100; $data[$key] = 0.09;//含氧量
break; break;
case 'turbidity': case 'ph':
$data[$column][$key] = 0.33; $data[$key] = rand(750, 770) / 100;
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{
// if($datalist[$key][$column] ?? null){//如果存在数据则暂存该值 $isSameDay ? $beginTime->addHours(1) : $beginTime->addDays(1);
// $_value = $datalist[$key][$column]; } while ($beginTime->lte($endTime));
// }
// //判断是否超过离线时间; 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(){
@ -572,4 +639,90 @@ 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(null); return response()->json(['message'=>'删除成功']);
} }
} }

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(null); return response()->json(['message'=>'删除成功']);
} }
} }

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); return RiceShrimpPriceResource::make($riceShrimpPrice)->additional(['message'=>'保存成功']);
} }
/** /**
@ -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(null); return response()->json(['message'=>'删除成功']);
} }
} }

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'])); return RiceShrimpWeeklyPriceResource::make($riceShrimpWeeklyPrice->loadMissing(['weekObj']))->additional(['message'=>'保存成功']);
} }
/** /**
@ -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(null); return response()->json(['message'=>'删除成功']);
} }
} }

View File

@ -5,14 +5,21 @@ 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()]); return $this->json(['info' => $user, 'permissions' => $user->permissionIds(), 'permissions_slug'=>$permissions]);
} }
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

@ -0,0 +1,17 @@
<?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,6 +2,7 @@
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;
@ -27,13 +28,7 @@ class HttpClient
] ]
); );
if (data_get($result, 'code') === 200) { return $result['data'] ?? [];
return $result['data'];
}
throw new RuntimeException(
sprintf("%s:%s", data_get($result, 'code', 500), data_get($result, 'msg', '出错啦!'))
);
} }
/** /**
@ -48,13 +43,56 @@ class HttpClient
] ]
); );
if (data_get($result, 'code') === 200) { return $result['data'] ?? [];
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
@ -108,7 +146,13 @@ class HttpClient
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
])->send($method, $url, $options); ])->send($method, $url, $options);
return $response->throw()->json(); $result = $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

@ -0,0 +1,153 @@
<?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

@ -0,0 +1,27 @@
<?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

@ -0,0 +1,27 @@
<?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

@ -0,0 +1,33 @@
<?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

@ -0,0 +1,20 @@
<?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,69 +2,48 @@
namespace App\Services; namespace App\Services;
use App\Enums\DeviceStatus;
use App\Enums\DeviceType; use App\Enums\DeviceType;
use App\Iot\BiAng\HttpClient as BiAngHttpClient; use App\Exceptions\BizException;
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 BiAngDeviceLogService class BiAngDeviceService
{ {
public function sync(Device $device): void /**
* 虫情设备/昆虫性诱设备 - 查询某个时间段内的图片
*/
public function getWormPhotos(Device $device, Carbon $start, Carbon $end): array
{ {
$config = json_decode($device->project?->value, true); try {
if (! isset($config['username'], $config['password'])) { $httpClient = $this->buildHttpClient($device);
return; } catch (BizException $e) {
return [];
} }
$httpClient = new BiAngHttpClient($config['username'], $config['password']); return $httpClient->getWormPhotos($device->sn, $start, $end);
}
$data = null; /**
* 虫情设备 - (识别款)图片虫数识别统计
switch ($device->type) { */
case DeviceType::Soil: public function getWormStatistics(Device $device, Carbon $start, Carbon $end): array
$data = $httpClient->getLatestSoilReport($device->sn); {
break; try {
$httpClient = $this->buildHttpClient($device);
case DeviceType::Meteorological: } catch (BizException $e) {
$data = $httpClient->getLatestMeteorologicalReport($device->sn); return [];
break;
} }
if (is_array($data) && isset($data['time'])) { return $httpClient->getWormStatistics($device->sn, $start, $end);
$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;
}
}
} }
/** /**
@ -80,6 +59,10 @@ class BiAngDeviceLogService
case DeviceType::Meteorological: case DeviceType::Meteorological:
$this->createMeteorologicalReport($device, $time); $this->createMeteorologicalReport($device, $time);
break; break;
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampReport($device, $time);
break;
} }
} }
@ -239,6 +222,90 @@ class BiAngDeviceLogService
$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();
}
/** /**
* 创建设备每日报告 * 创建设备每日报告
*/ */
@ -252,6 +319,10 @@ class BiAngDeviceLogService
case DeviceType::Soil: case DeviceType::Soil:
$this->createSoilDailyReport($device, $time); $this->createSoilDailyReport($device, $time);
break; break;
case DeviceType::InsecticidalLamp:
$this->createInsecticidalLampDailyReport($device, $time);
break;
} }
} }
@ -450,4 +521,84 @@ class BiAngDeviceLogService
$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

@ -0,0 +1,211 @@
<?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

@ -0,0 +1,36 @@
<?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

@ -0,0 +1,40 @@
<?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

@ -0,0 +1,40 @@
<?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

@ -0,0 +1,36 @@
<?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,46 +15,112 @@ class EndpointPermissionSeeder extends Seeder
public function run() public function run()
{ {
$permissions = [ $permissions = [
'endpoint' => ['name' => '系统权限', 'curd' => false, 'in_path' => true, 'children' => [ 'endpoint' => [
'monitor_data' => ['name' => '监测数据管理', 'curd' => false, 'children' => [ 'name' => '系统权限',
'weather' => ['name' => '气象管理', 'curd' => ['index']], 'curd' => false,
'camera' => ['name' => '智能监控', 'curd' => ['index']], 'in_path' => true,
'soil' => ['name' => '土壤监控', 'curd' => ['index'], 'children' => ['setting' => '设置', 'setting_edit' => '更新设置']], 'children' => [
'water' => ['name' => '水质监控', 'curd' => ['index'], 'children' => ['setting' => '设置', 'setting_edit' => '更新设置']], 'data_dashboard' => [
]], 'name' => '数据看板',
'base_data' => ['name' => '基础数据管理', 'curd' => false, 'children' => [ 'curd' => false,
'citydata_statistics' => ['name' => '全市基础数据', 'curd' => ['index', 'edit']], ],
//全市基础数据 'monitor_data' => [
'agricultural_basic' => ['name' => '基地数据', 'curd' => true], 'name' => '监测数据管理',
'town_street' => ['name' => '城镇数据', 'curd' => ['index', 'edit']], 'curd' => false,
//农作物数据 'children' => [
'crops' => ['name' => '基地农作物', 'curd' => true], 'weather' => ['name' => '气象管理', 'curd' => ['index']],
'town_crops' => ['name' => '镇街农作物', 'curd' => true], 'camera' => ['name' => '智能监控', 'curd' => ['index']],
//农作物产量 'soil' => [
'crops_output' => ['name' => '基地产量', 'curd' => true], 'name' => '土壤监控',
'town_crops_output' => ['name' => '镇街产量', 'curd' => true], 'curd' => ['index'],
// 'crops_flow' => ['name' => '农产品流向', 'curd' => true], 'children' => [
// 'crops_price' => ['name' => '农产品价格走势', 'curd' => ['index'], 'children' => ['is_enable' => '自动监测开关']], 'setting' => '设置',
]], 'setting_edit' => '更新设置'
'industry_data' => ['name' => '重点产业', 'curd' => false, 'children' => [ ],
'rice_shrimp_prices' => ['name' => '稻虾价格', 'curd' => true], ],
'rice_shrimp_weekly_prices' => ['name' => '稻虾每周价格', 'curd' => true], 'water' => [
'rice_shrimp_industries' => ['name' => '稻虾产业', 'curd' => true], 'name' => '水质监控',
'rice_shrimp_flows' => ['name' => '稻虾走向', 'curd' => true], 'curd' => ['index'],
'materiels' => ['name' => '大宗物资', 'curd' => true], 'children' => [
]], 'setting' => '设置',
'device_data' => ['name' => '设备管理', 'curd' => false, 'children' => [ 'setting_edit' => '更新设置',
'device' => ['name' => '设备管理', 'curd' => true], ],
]], ],
'manage' => ['name' => '系统管理', 'curd' => false, 'children' => [ 'insect' => ['name' => '昆虫性诱监测', 'curd' => ['index']],
'admin_users' => ['name' => '管理员管理', 'curd' => true, 'children' => [ 'worm_statics' => ['name' => '虫情监测', 'curd' => ['index']],
'edit_password' => '修改密码', 'enable' => '启用/禁用', 'insecticidal_lamp' => ['name' => '杀虫灯监测', 'curd' => ['index']],
],
],
'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'=>'标记'],
],
]], ]],
'admin_roles' => ['name' => '角色管理', 'curd' => true], 'manage' => [
'operation_log' => ['name' => '操作日志', 'curd' => ['index']], 'name' => '系统管理',
'friend_links' => ['name' => '友情链接', 'curd' => true] 'curd' => false,
]], '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,6 +52,7 @@ 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,7 +17,9 @@ use Illuminate\Support\Facades\Route;
Route::post('auth/login', [AuthController::class, 'login']); Route::post('auth/login', [AuthController::class, 'login']);
Route::group(['middleware' => 'auth:sanctum'], function () { Route::group([
'middleware' => ['auth:sanctum'],
], function () {
Route::post('web/upload', [WebController::class, 'upload']); Route::post('web/upload', [WebController::class, 'upload']);
@ -39,6 +41,7 @@ Route::group(['middleware' => 'auth:sanctum'], function () {
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');//基地农作物
@ -54,6 +57,8 @@ Route::group(['middleware' => 'auth:sanctum'], function () {
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,11 +1,15 @@
<?php <?php
use App\Http\Controllers\Callback\LinkosController; use App\Http\Controllers\Callback\LinkosController;
use Illuminate\Support\Facades\Route; use App\Http\Controllers\Callback\YunFeiController;
use App\Http\Middleware\ApiCustomToken;
use App\Http\Controllers\ThirdApi\SendSmsController; use App\Http\Controllers\ThirdApi\SendSmsController;
use App\Http\Middleware\ApiCustomToken;
use Illuminate\Support\Facades\Route;
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(){