This commit is contained in:
Your Name
2026-03-27 18:06:12 +08:00
parent 9160c36735
commit 099bc1dd22
645 changed files with 276473 additions and 957 deletions
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace app\common\command;
use app\adminapi\logic\tcm\DiagnosisLogic;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\facade\Log;
/**
* 从腾讯云 IM 拉取诊单单聊漫游消息并写入本地归档表(建议 cron 每几小时执行)
*/
class SyncImChatArchive extends Command
{
protected function configure()
{
$this->setName('sync_im_chat_archive')
->setDescription('同步诊单腾讯云 IM 聊天记录到数据库归档')
->addOption('since-days', null, Option::VALUE_OPTIONAL, '仅处理最近 N 天内更新过的诊单;0 表示不限制', '7')
->addOption('limit', 'l', Option::VALUE_OPTIONAL, '本轮最多处理的诊单数量(1-500)', '50')
->addOption('diagnosis-id', null, Option::VALUE_OPTIONAL, '只同步指定诊单 ID,设置后忽略 since-days', '0');
}
protected function execute(Input $input, Output $output)
{
$sinceDays = (int)$input->getOption('since-days');
$sinceDays = max(0, $sinceDays);
$limit = (int)$input->getOption('limit');
$onlyId = (int)$input->getOption('diagnosis-id');
$only = $onlyId > 0 ? $onlyId : null;
if ($only !== null) {
$output->writeln("同步诊单 ID={$only} ...");
} else {
$output->writeln('同步 IM 归档:since-days=' . ($sinceDays > 0 ? $sinceDays : '不限制') . ", limit={$limit}");
}
$stats = DiagnosisLogic::syncImChatArchiveBatch($sinceDays, $limit, $only);
$output->writeln("处理诊单数: {$stats['diagnoses']},新插入行数(INSERT IGNORE 成功数): {$stats['inserted']}");
if (!empty($stats['errors'])) {
foreach ($stats['errors'] as $e) {
$output->writeln("<error>{$e}</error>");
Log::error('sync_im_chat_archive: ' . $e);
}
}
}
}
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace app\common\command;
use app\common\model\doctor\Medicine;
use app\common\service\doctor\MedicineNameAbbrService;
use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
/**
* 批量回填药材库 name_pinyin_abbr(拼音首字母,供检索)
*
* 用法:
* php think sync_medicine_pinyin_abbr # 仅更新 abbr 为空的记录
* php think sync_medicine_pinyin_abbr --all # 全部按当前名称重算
* php think sync_medicine_pinyin_abbr --dry # 只打印将更新多少条,不写库
*/
class SyncMedicinePinyinAbbr extends Command
{
protected function configure()
{
$this->setName('sync_medicine_pinyin_abbr')
->setDescription('回填/刷新药材表 name_pinyin_abbr(需已执行迁移并 composer 安装 overtrue/pinyin')
->addOption('all', null, Option::VALUE_NONE, '重算全部记录(默认只处理 abbr 为空的)')
->addOption('dry', null, Option::VALUE_NONE, '仅统计/预览,不写入数据库');
}
protected function execute(Input $input, Output $output)
{
if (!class_exists(\Overtrue\Pinyin\Pinyin::class)) {
$output->writeln('<error>未检测到 overtrue/pinyin,请在 server 目录执行: composer update</error>');
return 1;
}
$all = (bool) $input->getOption('all');
$dry = (bool) $input->getOption('dry');
$makeQuery = function () use ($all) {
$q = Medicine::order('id', 'asc');
if (!$all) {
$q->where('name_pinyin_abbr', '');
}
return $q;
};
$total = $makeQuery()->count();
if ($total === 0) {
$output->writeln('没有需要处理的记录。');
return 0;
}
$output->writeln(($all ? '模式: 全部重算' : '模式: 仅空 abbr') . ",待处理 {$total}" . ($dry ? 'dry-run' : ''));
$updated = 0;
$skipped = 0;
$makeQuery()->chunk(200, function ($rows) use (&$updated, &$skipped, $dry) {
foreach ($rows as $row) {
$data = $row instanceof \think\Model ? $row->toArray() : (array) $row;
$id = (int) ($data['id'] ?? 0);
$name = trim((string) ($data['name'] ?? ''));
if ($id <= 0 || $name === '') {
++$skipped;
continue;
}
$abbr = MedicineNameAbbrService::build($name);
if ($abbr === '') {
++$skipped;
continue;
}
if ($dry) {
++$updated;
continue;
}
Medicine::where('id', $id)->update([
'name_pinyin_abbr' => $abbr,
'update_time' => time(),
]);
++$updated;
}
});
if ($dry) {
$output->writeln("[预览] 将写入/覆盖拼音首字母: {$updated} 条,跳过(空名或无法生成): {$skipped}");
} else {
$output->writeln("完成: 已更新 {$updated} 条,跳过 {$skipped} 条。");
}
return 0;
}
}