62 lines
3.1 KiB
PHP
62 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace app\adminapi\logic\tcm;
|
|
|
|
use app\adminapi\logic\firstvisit\MyPatientLogic;
|
|
use app\common\cache\AdminAuthCache;
|
|
use app\common\logic\BaseLogic;
|
|
use app\common\service\DifyTongueScreenshotService;
|
|
use app\common\service\TongueScreenshotImage;
|
|
|
|
/** Read-only assistant operation: authorization + this upload, never a chart snapshot or persistence. */
|
|
final class TongueScreenshotLogic extends BaseLogic
|
|
{
|
|
public static function analyze(array $params, $file, int $adminId, array $adminInfo): ?array
|
|
{
|
|
$image = null;
|
|
try {
|
|
if (array_diff(array_keys($params), ['id', 'request_id', 'model']) !== []) {
|
|
throw new \DomainException('截图分析请求包含不支持的字段');
|
|
}
|
|
$model = array_key_exists('model', $params) ? $params['model'] : 'qwen';
|
|
if (!is_string($model) || !in_array($model, ['qwen', 'openai'], true)) {
|
|
throw new \DomainException('截图分析模型无效');
|
|
}
|
|
$rawId = $params['id'] ?? null;
|
|
$id = (is_string($rawId) || is_int($rawId))
|
|
? filter_var($rawId, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]) : false;
|
|
if ($id === false) throw new \DomainException('诊单ID必须大于0');
|
|
$requestId = $params['request_id'] ?? null;
|
|
if (!is_string($requestId) || !preg_match('/\A[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/Di', $requestId)) {
|
|
throw new \DomainException('截图请求标识无效');
|
|
}
|
|
if ($adminId < 1) throw new \DomainException('请先登录');
|
|
if ((int) ($adminInfo['root'] ?? 0) !== 1) {
|
|
$uris = (new AdminAuthCache($adminId))->getAdminUri() ?? [];
|
|
$uris = array_map(static fn ($uri): string => strtolower(trim((string) $uri)), is_array($uris) ? $uris : []);
|
|
if (!in_array('tcm.diagnosis/aiassistant', $uris, true)) {
|
|
throw new \DomainException('权限不足,无法使用诊单 AI 助手');
|
|
}
|
|
}
|
|
if (!MyPatientLogic::canAccessDiagnosis($id, $adminId, $adminInfo)) {
|
|
throw new \DomainException('诊单不存在或无权访问');
|
|
}
|
|
$image = TongueScreenshotImage::fromUpload($file);
|
|
$result = DifyTongueScreenshotService::analyze($image, $model);
|
|
return ['request_id' => $requestId, 'diagnosis_id' => $id, 'image_sha256' => $image->sha256(),
|
|
'analysis' => $result['analysis'], 'model' => $result['model'], 'model_label' => $result['model_label']];
|
|
} catch (\DomainException $error) {
|
|
self::setError($error->getMessage());
|
|
return null;
|
|
} catch (\Throwable $error) {
|
|
// Never log image bytes, patient data, model text, transport details or exception messages.
|
|
self::setError('截图分析暂不可用,请稍后重试');
|
|
return null;
|
|
} finally {
|
|
if ($image !== null) $image->release();
|
|
}
|
|
}
|
|
}
|