文件
chengduzhongchuliang/application/api/controller/Index.php

509 行
20 KiB
PHP

<?php
namespace app\api\controller;
use app\common\controller\Api;
use app\common\model\ContentModel;
use app\common\model\GranaryModel;
use app\common\model\TestingModel;
use app\common\model\TestingParamsModel;
use think\Db;
use app\common\library\GranarySite;
/**
* 首页接口
*/
class Index extends Api
{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
/**
* 首页
*/
public function index()
{
$this->success('请求成功');
}
/**
* 获取仓库列表
* @param GranaryModel $granaryModel
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function granaryList(GranaryModel $granaryModel)
{
$site = $this->request->get('site', 'center');
if (!GranarySite::valid($site)) $this->error('无效库区');
$query = $granaryModel->field('province_id,city_id,update_time', true);
$query->where('site', $site);
$list = $query->select();
// 数据处理
if (!empty($list) && count($list) > 0) {
$list = collection($list)->toArray();
// Load saved rules in two queries rather than one query per granary.
$tests = TestingModel::with(['testingParams'])
->where('gid', 'in', array_column($list, 'id'))
->order('is_demo ASC, test_date DESC, id DESC')->select();
$latestTests = [];
foreach ($tests as $test) {
$test = $test->toArray();
$testKey = $test['gid'] . ':' . $test['type'];
if (!isset($latestTests[$testKey])) {
$latestTests[$testKey] = $test;
}
}
foreach ($list as &$v) {
$v['varieties'] = getVarietiesInfo($v['varieties']);
$testKey = $v['id'] . ':' . $v['varieties']['type'];
$v['quality_test'] = isset($latestTests[$testKey]) ? $latestTests[$testKey] : null;
$v['harvest_year'] = is_numeric($v['harvest_year']) ? date('Y-m-d', $v['harvest_year']) : $v['harvest_year'];
$v['enter_time'] = is_numeric($v['enter_time']) ? date('Y-m-d', $v['enter_time']) : $v['enter_time'];
$v['create_time'] = is_numeric($v['create_time']) ? date('Y-m-d', $v['create_time']) : $v['create_time'];
// 顶层 capacity 与 storage_num 智能反填
$content = is_string($v['content']) ? json_decode($v['content'], true) : $v['content'];
if (empty($v['capacity']) && !empty($content)) {
$v['capacity'] = isset($content['capacity']) ? $content['capacity'] : (isset($content['tankCapacity']) ? $content['tankCapacity'] : null);
}
if ((empty($v['storage_num']) || $v['storage_num'] == '0') && !empty($content['storageNum'])) {
$v['storage_num'] = $content['storageNum'];
}
}
}
$coords = $this->getSiteCoords($site);
if ($site !== 'xishui' && is_array($list)) {
foreach ($list as &$granary) {
$granary['screen_coord'] = \app\common\library\GranaryScreen::resolve($granary, $coords);
}
unset($granary);
}
if ($site === 'xishui') {
foreach ($list as &$granary) {
$key = 'xishui_' . $granary['num'];
$granary['screen_coord'] = isset($coords[$key]) ? $coords[$key] : null;
}
unset($granary);
}
$this->success('获取成功', [
'list' => $list,
'coords' => $coords,
'site' => $site,
'site_name' => GranarySite::NAMES[$site],
'source' => 'database'
]);
}
/**
* 仓库详情
* @param GranaryModel $granaryModel
* @param TestingModel $testingModel
* @param TestingParamsModel $testingParamsModel
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function granaryDetail(GranaryModel $granaryModel, TestingModel $testingModel, TestingParamsModel $testingParamsModel)
{
$id = $this->request->get('id');
$type = $this->request->get('type');
if (empty($id) || empty($type)) {
$this->error('参数错误', [], 401);
}
$granary = $granaryModel->where(['id' => $id])
->field('province_id,city_id,update_time', true)
->find();
if (empty($granary)) {
$this->error('您要查看的仓库不存在', [], 402);
}
// 获取农作物最新检测参数
$test = $testingModel
->with(['testingParams'])
->where(['gid' => $id, 'type' => $type])
->field(['id', 'type', 'test_date'])
->order('test_date DESC')
->find();
// 折线图统计数据
$seriesData = [];
$xAxisData = [];
if (!empty($test)) {
$seriesData = Db::name('testing_params')
->where(['tid' => $test->id, 'name' => ['NEQ', '容重'], 'is_number' => 1])
->field(['id', 'name', 'unit', 'color'])
->select();
$testList = Db::name('testing')
->field(['id', 'year'])
->where(['gid' => $id])
->group('year')
->order('sort DESC')
->limit(6)
->select();
// 重新排序
// $yearArr = array_column($testList, 'year');
// array_multisort($yearArr, SORT_ASC, $testList);
foreach ($testList as $index => $value) {
// 获取季度
$list = Db::name('testing')
->where(['gid' => $id, 'year' => $value['year']])
->field('season')
->select();
foreach ($list as $v) {
switch ($v['season']) {
case 1 :
$season = '春季';
break;
case 2 :
$season = '秋季';
break;
case 3 :
$season = '满仓';
break;
}
array_push($xAxisData, $value['year'] . $season);
}
foreach ($seriesData as &$item) {
// 获取参数数值
foreach ($list as $val) {
$val = Db::name('testing_params')->where([
'gid' => $id,
'name' => $item['name'],
'year' => $value['year'],
'season' => $val['season'],
])->value('val');
$item['data'][] = !empty($val) ? $val : 0;
$item['color'] = getColorCode($item['name']);
}
}
}
}
// 质检报告列表
$contentList = Db::name('content')
->where(['gid' => $id, 'status' => 1])
->field(['id', 'type', 'test_date'])
->order('test_date ASC')
->select();
if (!empty($contentList)) {
foreach ($contentList as &$v) {
switch ($v['type']) {
case 1 :
$v['title'] = $v['test_date'] . '满仓验收';
break;
case 2 :
$v['title'] = $v['test_date'] . '春普';
break;
case 3 :
$v['title'] = $v['test_date'] . '秋普';
break;
}
}
}
// 时间处理
$granary->create_time = date('Y-m-d H:i:s', $granary->create_time);
$this->success('获取成功', [
'granary' => $granary,
'test' => $test,
'xAxisData' => $xAxisData,
'seriesData' => $seriesData,
'contentList' => $contentList
]);
}
/**
* 获取质检报告详情
* @param ContentModel $contentModel
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function contentDetail(ContentModel $contentModel)
{
$id = $this->request->get('id');
if (empty($id)) {
$this->error('参数错误', [], 401);
}
$content = $contentModel->getItem($id, [
'id',
'content'
]);
if (empty($content)) {
$this->error('您要查看的质检报告不存在', [], 402);
}
$this->success('获取成功', [
'content' => $content,
]);
}
/**
* 获取大屏微气候环境工况
*/
public function env()
{
$site = $this->request->get('site', 'center');
if (!GranarySite::valid($site)) $this->error('无效库区');
$envData = [
'center' => [
'site' => 'center',
'name' => '遵义直属库中心库区',
'tm_i' => 18.5,
'tm_o' => 32.5,
'hm_i' => 72.4,
'hm_o' => 54.2,
'status' => 'normal',
'update_time' => time()
],
'xishui' => [
'site' => 'xishui',
'name' => '习水直属子库区',
'tm_i' => 16.8,
'tm_o' => 29.5,
'hm_i' => 68.2,
'hm_o' => 58.1,
'status' => 'normal',
'update_time' => time()
]
];
$data = isset($envData[$site]) ? $envData[$site] : $envData['center'];
// 优先读取该库区最新一条人工录入或设定的温湿度快照
try {
$latestRecord = Db::name('grain_forecast')
->alias('f')
->join('granary g', 'f.gid = g.id')
->where('g.site', $site)
->order('f.id DESC')
->field('f.tm_i, f.hm_i, f.tm_o, f.hm_o, f.update_time')
->find();
if ($latestRecord) {
$data['tm_i'] = (float)$latestRecord['tm_i'];
$data['hm_i'] = (float)$latestRecord['hm_i'];
$data['tm_o'] = (float)$latestRecord['tm_o'];
$data['hm_o'] = (float)$latestRecord['hm_o'];
$data['update_time'] = (int)$latestRecord['update_time'];
}
} catch (\Exception $e) {
// 异常时安全兜底基准工况
}
$this->success('获取成功', $data);
}
/**
* 大屏粮食质量多周期未来趋势推演预测
*/
public function forecast()
{
$granaryName = $this->request->get('granaryName', '');
$variety = $this->request->get('variety', '小麦');
$harvestYear = $this->request->get('harvestYear', '2020');
$origin = $this->request->get('origin', '河南');
$daysStr = $this->request->get('days', '7,15,30,60');
$baseDate = $this->request->get('baseDate', date('Y-m-d'));
$tm_i = floatval($this->request->get('tm_i', 18.5));
$tm_o = floatval($this->request->get('tm_o', 32.5));
$hm_i = floatval($this->request->get('hm_i', 72.4));
$hm_o = floatval($this->request->get('hm_o', 54.2));
$days = array_filter(array_map('intval', explode(',', $daysStr)));
if (empty($days)) {
$days = [7, 15, 30, 60];
}
if (strpos($granaryName, '油罐') !== false || strpos($variety, '油') !== false) {
$this->success('获取成功', ['list' => []]);
}
$grainType = (strpos($variety, '稻') !== false || strpos($variety, 'rice') !== false || strpos($variety, 'paddy') !== false) ? 'rice' : 'wheat';
$resultList = [];
$remoteUrl = 'http://www.probe-cdy-pests.cn:8116/alg/grainquality/forcast';
$baseTimestamp = strtotime($baseDate) ?: time();
foreach ($days as $day) {
// 根据基准采集日期精确累加周期天数,换算为目标日历的月和日
$targetTimestamp = strtotime("+$day days", $baseTimestamp);
$targetMonth = (int)date('n', $targetTimestamp);
$targetDay = (int)date('j', $targetTimestamp);
$targetDate = date('Y-m-d', $targetTimestamp);
$remoteData = $this->callRemoteForecast($remoteUrl, [
'type' => $grainType,
'commodity' => $grainType,
'month' => $targetMonth,
'day' => $targetDay,
'tm_i' => $tm_i,
'tm_o' => $tm_o,
'hm_i' => $hm_i,
'hm_o' => $hm_o,
]);
if (!empty($remoteData)) {
$targets = isset($remoteData['targets']) ? $remoteData['targets'] : [];
$resultList[] = [
'day' => $day,
'target_date' => $targetDate,
'target_month' => $targetMonth,
'target_day' => $targetDay,
'forecast_water' => isset($targets['wheat_moisture']) ? $targets['wheat_moisture'] : (isset($targets['paddy_moisture']) ? $targets['paddy_moisture'] : 12.0),
'forecast_impurity' => 0.6,
'forecast_weight' => isset($targets['wheat_GWAC']) ? $targets['wheat_GWAC'] : 790,
'forecast_imperfect' => isset($targets['wheat_DON']) ? round($targets['wheat_DON'] / 100, 2) : 3.5,
'forecast_taste' => isset($targets['wheat_ZEN']) ? 82.0 : 80.0,
'targets' => $targets,
'status' => 'normal'
];
} else {
$decayFactor = $day / 60.0;
$tempPenalty = max(0, ($tm_i - 15.0) * 0.015 * $decayFactor);
$resultList[] = [
'day' => $day,
'target_date' => $targetDate,
'target_month' => $targetMonth,
'target_day' => $targetDay,
'forecast_water' => round(12.2 + 0.3 * $decayFactor + $tempPenalty, 2),
'forecast_impurity' => 0.6,
'forecast_weight' => round(790 - 4.0 * $decayFactor, 1),
'forecast_imperfect' => round(3.5 + 0.8 * $decayFactor, 2),
'forecast_taste' => round(82 - 3.5 * $decayFactor, 1),
'status' => 'normal'
];
}
}
$this->success('获取成功', [
'granaryName' => $granaryName,
'variety' => $variety,
'list' => $resultList
]);
}
/**
* 安全调用远程算法服务
*/
protected function callRemoteForecast($url, array $params)
{
try {
$query = http_build_query($params);
$ch = curl_init($url . '?' . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 1,
CURLOPT_TIMEOUT => 2,
CURLOPT_PROXY => '',
CURLOPT_FOLLOWLOCATION => false
]);
$raw = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code === 200 && $raw) {
$json = json_decode($raw, true);
if (isset($json['code']) && (string)$json['code'] === '0' && !empty($json['output'])) {
return $json['output'];
}
}
} catch (\Exception $e) {
// 网络或解析异常静默兜底
}
return null;
}
/**
* 辅助获取库区热点坐标
*/
protected function getSiteCoords($site)
{
if ($site === 'xishui') {
return [
101 => ['top' => 47.5, 'left' => 26.4, 'x' => 26.4, 'y' => 47.5],
102 => ['top' => 48.0, 'left' => 39.6, 'x' => 39.6, 'y' => 48.0],
103 => ['top' => 48.0, 'left' => 60.4, 'x' => 60.4, 'y' => 48.0],
104 => ['top' => 47.5, 'left' => 73.7, 'x' => 73.7, 'y' => 47.5],
105 => ['top' => 33.5, 'left' => 27.0, 'x' => 27.0, 'y' => 33.5],
106 => ['top' => 34.0, 'left' => 39.0, 'x' => 39.0, 'y' => 34.0],
107 => ['top' => 34.0, 'left' => 60.2, 'x' => 60.2, 'y' => 34.0],
108 => ['top' => 33.5, 'left' => 73.2, 'x' => 73.2, 'y' => 33.5],
109 => ['top' => 22.5, 'left' => 55.0, 'x' => 55.0, 'y' => 22.5],
110 => ['top' => 29.0, 'left' => 84.5, 'x' => 84.5, 'y' => 29.0],
111 => ['top' => 47.0, 'left' => 85.5, 'x' => 85.5, 'y' => 47.0],
'xishui_1' => ['top' => 47.5, 'left' => 26.4, 'x' => 26.4, 'y' => 47.5],
'xishui_2' => ['top' => 48.0, 'left' => 39.6, 'x' => 39.6, 'y' => 48.0],
'xishui_3' => ['top' => 48.0, 'left' => 60.4, 'x' => 60.4, 'y' => 48.0],
'xishui_4' => ['top' => 47.5, 'left' => 73.7, 'x' => 73.7, 'y' => 47.5],
'xishui_5' => ['top' => 33.5, 'left' => 27.0, 'x' => 27.0, 'y' => 33.5],
'xishui_6' => ['top' => 34.0, 'left' => 39.0, 'x' => 39.0, 'y' => 34.0],
'xishui_7' => ['top' => 34.0, 'left' => 60.2, 'x' => 60.2, 'y' => 34.0],
'xishui_8' => ['top' => 33.5, 'left' => 73.2, 'x' => 73.2, 'y' => 33.5],
'xishui_9' => ['top' => 22.5, 'left' => 55.0, 'x' => 55.0, 'y' => 22.5],
'xishui_10' => ['top' => 29.0, 'left' => 84.5, 'x' => 84.5, 'y' => 29.0],
'xishui_11' => ['top' => 47.0, 'left' => 85.5, 'x' => 85.5, 'y' => 47.0],
];
}
$rawCenter = [
// 7 座立式储油罐 (含高位激光引线 stemY 错层防遮挡)
14 => ['top' => 12.8, 'left' => 4.8, 'stemX' => 2.8, 'stemY' => 20.3],
18 => ['top' => 18.5, 'left' => 6.5, 'stemY' => 21.0],
17 => ['top' => 9.2, 'left' => 9.6, 'stemY' => 18.4],
12 => ['top' => 15.8, 'left' => 10.5, 'stemY' => 22.0],
16 => ['top' => 12.2, 'left' => 13.7, 'stemY' => 22.4],
10 => ['top' => 16.0, 'left' => 16.9, 'stemY' => 23.1],
15 => ['top' => 13.0, 'left' => 19.8, 'stemY' => 19.8],
// 14 栋粮食平房仓
44 => ['top' => 18.7, 'left' => 45.1],
43 => ['top' => 33.5, 'left' => 44.0],
35 => ['top' => 23.8, 'left' => 38.5],
42 => ['top' => 29.9, 'left' => 52.1],
36 => ['top' => 28.0, 'left' => 33.8],
41 => ['top' => 35.0, 'left' => 65.9],
33 => ['top' => 34.5, 'left' => 29.7],
37 => ['top' => 41.0, 'left' => 40.0],
38 => ['top' => 42.0, 'left' => 59.0],
40 => ['top' => 41.0, 'left' => 80.6],
39 => ['top' => 49.5, 'left' => 75.8],
32 => ['top' => 51.3, 'left' => 56.3],
31 => ['top' => 60.2, 'left' => 73.2],
30 => ['top' => 61.8, 'left' => 50.5],
];
$result = [];
foreach ($rawCenter as $k => $c) {
$row = [
'top' => $c['top'],
'left' => $c['left'],
'x' => $c['left'],
'y' => $c['top']
];
if (isset($c['stemX'])) $row['stemX'] = $c['stemX'];
if (isset($c['stemY'])) $row['stemY'] = $c['stemY'];
$result[$k] = $row;
}
return $result;
}
}