feat(testing): 增加质检报告独立编辑与详情页面,不再使用弹窗
这个提交包含在:
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
// CLI only. First run schema, then publish files, then run with "menu".
|
||||
if (PHP_SAPI !== 'cli') exit;
|
||||
error_reporting(E_ALL & ~E_DEPRECATED & ~E_NOTICE);
|
||||
chdir('/www/wwwroot/grain.gynhc.com');
|
||||
define('APP_PATH', getcwd() . '/application/');
|
||||
require getcwd() . '/thinkphp/base.php';
|
||||
\think\App::initCommon();
|
||||
$prefix = config('database.prefix');
|
||||
if (!preg_match('/^[a-zA-Z0-9_]+$/D', $prefix)) throw new Exception('Invalid prefix');
|
||||
$backup = $argv[1];
|
||||
if (!is_dir($backup)) throw new Exception('Backup directory required');
|
||||
if (($argv[2] ?? '') !== 'menu') {
|
||||
$sql = str_replace('`fa_', '`' . $prefix, file_get_contents(__DIR__ . '/20261028_grain_forecast.sql'));
|
||||
foreach (preg_split('/;\s*(?:\r?\n|$)/', trim($sql)) as $statement) {
|
||||
if (trim($statement)) \think\Db::execute($statement);
|
||||
}
|
||||
echo "FORECAST_SCHEMA_READY\n";
|
||||
exit;
|
||||
}
|
||||
$rules = \think\Db::name('auth_rule')->select();
|
||||
$groups = \think\Db::name('auth_group')->select();
|
||||
$snapshot = $backup . '/forecast-auth-backup.json';
|
||||
if (!file_exists($snapshot)) {
|
||||
file_put_contents($snapshot, json_encode(['rules' => $rules, 'groups' => $groups], JSON_UNESCAPED_UNICODE));
|
||||
chmod($snapshot, 0600);
|
||||
}
|
||||
\think\Db::transaction(function () use ($rules, $groups) {
|
||||
$testing = \think\Db::name('auth_rule')->where('name', 'testing')->find();
|
||||
$nodes = [
|
||||
'forecast' => ['温湿度预测与实测', 1],
|
||||
'forecast/index' => ['查看记录', 0],
|
||||
'forecast/detail' => ['查看对比', 0],
|
||||
'forecast/add' => ['录入温湿度并预测', 0],
|
||||
'forecast/retry' => ['重新生成预测', 0],
|
||||
'forecast/actual' => ['补录真实监测值', 0],
|
||||
];
|
||||
$ids = [];
|
||||
foreach ($nodes as $name => $info) {
|
||||
$id = \think\Db::name('auth_rule')->where('name', $name)->value('id');
|
||||
if (!$id) {
|
||||
$id = \think\Db::name('auth_rule')->insertGetId([
|
||||
'name' => $name, 'title' => $info[0], 'ismenu' => $info[1], 'type' => 'file',
|
||||
'pid' => $info[1] ? ($testing ? $testing['pid'] : 0) : $ids['forecast'],
|
||||
'icon' => $info[1] ? 'fa fa-line-chart' : 'fa fa-circle-o', 'menutype' => 'addtabs',
|
||||
'weigh' => $info[1] && $testing ? $testing['weigh'] - 1 : 0, 'status' => 'normal',
|
||||
'createtime' => time(), 'updatetime' => time(),
|
||||
]);
|
||||
}
|
||||
$ids[$name] = $id;
|
||||
}
|
||||
$oldIds = [];
|
||||
foreach ($rules as $rule) $oldIds[$rule['name']] = (string)$rule['id'];
|
||||
foreach ($groups as $group) {
|
||||
if ($group['rules'] === '*') continue;
|
||||
$granted = explode(',', $group['rules']);
|
||||
$canRead = false;
|
||||
foreach (['testing', 'testing/index'] as $name) if (isset($oldIds[$name]) && in_array($oldIds[$name], $granted, true)) $canRead = true;
|
||||
$canWrite = isset($oldIds['testing/add']) && in_array($oldIds['testing/add'], $granted, true);
|
||||
if (!$canRead && !$canWrite) continue;
|
||||
$extra = [$ids['forecast'], $ids['forecast/index'], $ids['forecast/detail']];
|
||||
if ($canWrite) $extra = array_merge($extra, [$ids['forecast/add'], $ids['forecast/retry'], $ids['forecast/actual']]);
|
||||
\think\Db::name('auth_group')->where('id', $group['id'])->update(['rules' => implode(',', array_unique(array_merge($granted, $extra)))]);
|
||||
}
|
||||
});
|
||||
echo "FORECAST_MENU_READY\n";
|
||||
@@ -0,0 +1,35 @@
|
||||
-- Replace fa_ with the configured database prefix when executing manually.
|
||||
CREATE TABLE IF NOT EXISTS `fa_grain_forecast` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`gid` int unsigned NOT NULL,
|
||||
`granary_name` varchar(100) NOT NULL,
|
||||
`storage` varchar(255) NOT NULL DEFAULT '',
|
||||
`grain_type` varchar(10) NOT NULL,
|
||||
`observed_date` date NOT NULL,
|
||||
`tm_i` decimal(8,4) NOT NULL,
|
||||
`tm_o` decimal(8,4) NOT NULL,
|
||||
`hm_i` decimal(8,4) NOT NULL,
|
||||
`hm_o` decimal(8,4) NOT NULL,
|
||||
`request_key` varchar(64) NOT NULL,
|
||||
`status` varchar(12) NOT NULL DEFAULT 'pending',
|
||||
`error` varchar(255) NOT NULL DEFAULT '',
|
||||
`attempt_time` int unsigned NOT NULL DEFAULT 0,
|
||||
`admin_id` int unsigned NOT NULL DEFAULT 0,
|
||||
`create_time` int unsigned NOT NULL,
|
||||
`update_time` int unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`), UNIQUE KEY `request_key` (`request_key`), KEY `gid_id` (`gid`,`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='人工温湿度及预测批次';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `fa_grain_forecast_point` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT,
|
||||
`forecast_id` int unsigned NOT NULL,
|
||||
`horizon` tinyint unsigned NOT NULL,
|
||||
`target_date` date NOT NULL,
|
||||
`predicted` text NOT NULL,
|
||||
`actual` text NOT NULL,
|
||||
`actual_date` date DEFAULT NULL,
|
||||
`actual_admin_id` int unsigned NOT NULL DEFAULT 0,
|
||||
`version` int unsigned NOT NULL DEFAULT 0,
|
||||
`update_time` int unsigned NOT NULL,
|
||||
PRIMARY KEY (`id`), UNIQUE KEY `forecast_horizon` (`forecast_id`,`horizon`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预测目标日期与人工实测对比';
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// CLI: php this-file.php /absolute/backup/directory
|
||||
if (PHP_SAPI !== 'cli') exit;
|
||||
chdir(dirname(__DIR__));
|
||||
define('APP_PATH', getcwd() . '/application/');
|
||||
require getcwd() . '/thinkphp/base.php';
|
||||
\think\App::initCommon();
|
||||
$backup = isset($argv[1]) ? $argv[1] : '';
|
||||
if (!is_dir($backup)) throw new Exception('Backup directory required');
|
||||
$prefix = config('database.prefix');
|
||||
if (!preg_match('/^[a-zA-Z0-9_]+$/D', $prefix)) throw new Exception('Invalid prefix');
|
||||
$rows = \think\Db::name('granary')->select();
|
||||
$mapping = [];
|
||||
$keys = [];
|
||||
foreach ($rows as $row) {
|
||||
$site = isset($row['site']) && in_array($row['site'], ['center', 'xishui'], true) ? $row['site'] : '';
|
||||
if ($site === '') {
|
||||
if (strpos($row['storage'], '习水') !== false) $site = 'xishui';
|
||||
elseif (strpos($row['storage'], '中心库') !== false) $site = 'center';
|
||||
else throw new Exception('Unknown site for granary ' . $row['id']);
|
||||
}
|
||||
$key = $site . ':' . $row['num'];
|
||||
if (isset($keys[$key])) throw new Exception('Duplicate site/number: ' . $key);
|
||||
$keys[$key] = true;
|
||||
$mapping[$row['id']] = $site;
|
||||
}
|
||||
$snapshot = $backup . '/granary-sites-before.json';
|
||||
if (!file_exists($snapshot)) {
|
||||
file_put_contents($snapshot, json_encode(['granaries' => $rows,
|
||||
'admin_scopes' => \think\Db::name('admin')->field('id,username')->select()], JSON_UNESCAPED_UNICODE));
|
||||
chmod($snapshot, 0600);
|
||||
}
|
||||
$columns = array_column(\think\Db::query('SHOW COLUMNS FROM `' . $prefix . 'granary`'), 'Field');
|
||||
if (!in_array('site', $columns)) \think\Db::execute('ALTER TABLE `' . $prefix . 'granary` ADD `site` varchar(16) NOT NULL DEFAULT \'\' COMMENT \'库区:center中心库,xishui习水库\' AFTER `storage`');
|
||||
// Use bound SQL because this process may have cached the pre-migration fields.
|
||||
foreach ($mapping as $id => $site) \think\Db::execute('UPDATE `' . $prefix . 'granary` SET site = ? WHERE id = ?', [$site, $id]);
|
||||
$indexes = array_column(\think\Db::query('SHOW INDEX FROM `' . $prefix . 'granary`'), 'Key_name');
|
||||
if (!in_array('uq_granary_site_num', $indexes)) \think\Db::execute('ALTER TABLE `' . $prefix . 'granary` ADD UNIQUE KEY `uq_granary_site_num` (`site`,`num`)');
|
||||
$columns = array_column(\think\Db::query('SHOW COLUMNS FROM `' . $prefix . 'admin`'), 'Field');
|
||||
if (!in_array('granary_sites', $columns)) \think\Db::execute('ALTER TABLE `' . $prefix . 'admin` ADD `granary_sites` varchar(16) NOT NULL DEFAULT \'all\' COMMENT \'库区权限:all/center/xishui/none\'');
|
||||
foreach (['granary', 'admin'] as $table) {
|
||||
$schema = RUNTIME_PATH . 'schema/' . config('database.database') . '.' . $prefix . $table . '.php';
|
||||
if (is_file($schema)) {
|
||||
copy($schema, $backup . '/' . $table . '-schema-before.php');
|
||||
unlink($schema);
|
||||
}
|
||||
}
|
||||
echo "GRANARY_SITES_READY\n";
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// CLI only: php file.php /absolute/backup/directory fixture.json
|
||||
if (PHP_SAPI !== 'cli') exit;
|
||||
chdir(dirname(__DIR__));
|
||||
define('APP_PATH', getcwd() . '/application/');
|
||||
require getcwd() . '/thinkphp/base.php';
|
||||
\think\App::initCommon();
|
||||
$backup = $argv[1];
|
||||
if (!is_dir($backup)) throw new Exception('Backup directory required');
|
||||
$fixture = json_decode(file_get_contents($argv[2]), true);
|
||||
if (!is_array($fixture) || count($fixture) !== 11) throw new Exception('Expected 11 Xishui warehouses');
|
||||
$prefix = config('database.prefix');
|
||||
if (!preg_match('/^[a-zA-Z0-9_]+$/D', $prefix)) throw new Exception('Invalid prefix');
|
||||
$ids = array_column($fixture, 'gid');
|
||||
$rows = \think\Db::name('granary')->where('site', 'xishui')->where('id', 'in', $ids)->select();
|
||||
if (count($rows) !== 11) throw new Exception('Warehouse ownership mismatch');
|
||||
$snapshot = $backup . '/xishui-before-demo.json';
|
||||
if (!file_exists($snapshot)) {
|
||||
file_put_contents($snapshot, json_encode(['granaries'=>$rows,
|
||||
'tests'=>\think\Db::name('testing')->where('gid','in',$ids)->select(),
|
||||
'params'=>\think\Db::name('testing_params')->where('gid','in',$ids)->select()], JSON_UNESCAPED_UNICODE));
|
||||
chmod($snapshot, 0600);
|
||||
}
|
||||
$columns = array_column(\think\Db::query('SHOW COLUMNS FROM `' . $prefix . 'testing`'), 'Field');
|
||||
if (!in_array('is_demo', $columns)) \think\Db::execute('ALTER TABLE `' . $prefix . 'testing` ADD `is_demo` tinyint unsigned NOT NULL DEFAULT 0 COMMENT \'1为演示化验记录,非实测\'');
|
||||
$schema = RUNTIME_PATH . 'schema/' . config('database.database') . '.' . $prefix . 'testing.php';
|
||||
if (is_file($schema)) { copy($schema, $backup . '/testing-schema-before.php'); unlink($schema); }
|
||||
\think\Db::clear();
|
||||
$created = [];
|
||||
\think\Db::transaction(function () use ($fixture, &$created, $prefix) {
|
||||
foreach ($fixture as $warehouse) {
|
||||
foreach ($warehouse['records'] as $record) {
|
||||
$month = str_replace('-', '', $record['date']);
|
||||
// Any existing record for this period is preserved, real or demo.
|
||||
if (\think\Db::name('testing')->where('gid', $warehouse['gid'])->where('type', $warehouse['type'])->where('test_date', $month)->count()) continue;
|
||||
if (\app\common\library\QualityRule::validate($record['params']) !== '') throw new Exception('Invalid demo parameters');
|
||||
$id = (new \app\common\model\TestingModel())->add([
|
||||
'gid'=>$warehouse['gid'], 'type'=>$warehouse['type'], 'season'=>2,
|
||||
'sort'=>0, 'create_time'=>time(), 'update_time'=>time(),
|
||||
], $record['params'], $record['date']);
|
||||
// This CLI process may have cached the schema before ADD COLUMN.
|
||||
\think\Db::execute('UPDATE `' . $prefix . 'testing` SET is_demo = 1 WHERE id = ?', [$id]);
|
||||
$created[] = $id;
|
||||
}
|
||||
}
|
||||
});
|
||||
$manifest = $backup . '/xishui-demo-created-ids.json';
|
||||
$previous = is_file($manifest) ? json_decode(file_get_contents($manifest), true) : [];
|
||||
file_put_contents($manifest, json_encode(array_values(array_unique(array_merge(is_array($previous) ? $previous : [], $created)))));
|
||||
echo json_encode(['result'=>'XISHUI_DEMO_READY','created_tests'=>count($created),'created_ids'=>$created], JSON_UNESCAPED_UNICODE) . "\n";
|
||||
在新工单中引用
屏蔽一个用户