320 lines
12 KiB
PHP
Executable File
320 lines
12 KiB
PHP
Executable File
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | likeadmin快速开发前后端分离管理后台(PHP版)
|
|
// +----------------------------------------------------------------------
|
|
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
|
|
// | 开源版本可自由商用,可去除界面版权logo
|
|
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
|
|
// | github下载:https://github.com/likeshop-github/likeadmin
|
|
// | 访问官网:https://www.likeadmin.cn
|
|
// | likeadmin团队 版权所有 拥有最终解释权
|
|
// +----------------------------------------------------------------------
|
|
// | author: likeadminTeam
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace app\adminapi\validate\tcm;
|
|
|
|
use app\common\model\tcm\Diagnosis;
|
|
use app\common\validate\BaseValidate;
|
|
|
|
/**
|
|
* 中医辨房病因诊单验证
|
|
* Class DiagnosisValidate
|
|
* @package app\adminapi\validate\tcm
|
|
*/
|
|
class DiagnosisValidate extends BaseValidate
|
|
{
|
|
protected $rule = [
|
|
'id' => 'require|checkDiagnosis',
|
|
'patient_name' => 'require|length:1,50',
|
|
'id_card' => 'length:15,18',
|
|
'phone' => 'require|mobile',
|
|
'gender' => 'require|in:0,1',
|
|
'age' => 'require|number|between:0,150',
|
|
'diagnosis_type' => 'require',
|
|
'local_hospital_name' => 'require|max:255',
|
|
'status' => 'in:0,1',
|
|
'show_card' => 'in:0,1',
|
|
'create_source' => 'max:32',
|
|
'current_medications' => 'max:2000',
|
|
'tongue_images' => 'array',
|
|
'report_files' => 'array',
|
|
'diabetes_discovery_year' => 'max:50',
|
|
'start_date' => 'date',
|
|
'end_date' => 'date|checkDateRange',
|
|
'diagnosis_id' => 'require|integer|checkDiagnosisId',
|
|
'tracking_content' => 'require|length:1,1000',
|
|
'revisit_slot_start_offset' => 'integer|between:0,20',
|
|
'report_id' => 'number|gt:0',
|
|
'content' => 'max:12000',
|
|
'task' => 'require|in:summary,tcm_pattern,prescription_review,medication_review,exam_review,complication_risk,guideline_review,custom',
|
|
'prompt' => 'max:500',
|
|
'model' => 'in:qwen,openai',
|
|
'patient_id' => 'integer|gt:0',
|
|
];
|
|
|
|
protected $message = [
|
|
'id.require' => '参数缺失',
|
|
'patient_name.require' => '请输入患者姓名',
|
|
'patient_name.length' => '患者姓名长度须在1-50位字符',
|
|
'id_card.length' => '身份证号长度不正确',
|
|
'id_card.require' => '请输入身份证号',
|
|
'phone.require' => '请输入手机号',
|
|
'phone.mobile' => '手机号格式不正确',
|
|
'gender.require' => '请选择性别',
|
|
'gender.in' => '性别参数错误',
|
|
'age.require' => '请输入年龄',
|
|
'age.number' => '年龄必须为数字',
|
|
'age.between' => '年龄范围0-150',
|
|
'diagnosis_type.require' => '请选择诊断类型',
|
|
'local_hospital_name.require' => '请输入当地就诊医院名称',
|
|
'local_hospital_name.max' => '当地就诊医院名称最多255个字符',
|
|
'status.in' => '状态参数错误',
|
|
'create_source.max' => '渠道来源长度不能超过32个字符',
|
|
'current_medications.max' => '在用药物最多2000个字符',
|
|
'diabetes_discovery_year.max' => '发现糖尿病患病史最多50个字符',
|
|
'start_date.date' => '开始日期格式不正确',
|
|
'end_date.date' => '结束日期格式不正确',
|
|
'diagnosis_id.require' => '诊单ID不能为空',
|
|
'tracking_content.require' => '跟踪备注内容不能为空',
|
|
'tracking_content.length' => '跟踪备注最多1000个字符',
|
|
'report_id.require' => '报告ID不能为空',
|
|
'report_id.number' => '报告ID必须为数字',
|
|
'report_id.gt' => '报告ID必须大于0',
|
|
'content.require' => '报告内容不能为空',
|
|
'content.max' => '报告内容最多12000个字符',
|
|
'task.require' => '请选择 AI 助手任务',
|
|
'task.in' => 'AI 助手任务不受支持',
|
|
'prompt.max' => '问题最多500个字符',
|
|
'model.in' => 'AI模型仅支持qwen或openai',
|
|
'patient_id.require' => '患者ID不能为空',
|
|
'patient_id.integer' => '患者ID必须为整数',
|
|
'patient_id.gt' => '患者ID必须大于0',
|
|
];
|
|
|
|
public function sceneAdd()
|
|
{
|
|
// 与 sceneEdit 一样用 only() 白名单,避免全局规则里的 AI 助手 / 跟踪备注字段泄漏到新增诊单
|
|
return $this->only([
|
|
'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type',
|
|
'syndrome_type', 'marital_status', 'height', 'weight', 'region', 'systolic_pressure',
|
|
'diastolic_pressure', 'fasting_blood_sugar', 'diabetes_discovery_year',
|
|
'local_hospital_diagnosis', 'local_hospital_name', 'past_history', 'symptoms',
|
|
'tongue_coating', 'pulse', 'treatment_principle', 'prescription', 'doctor_advice',
|
|
'remark', 'current_medications', 'status', 'show_card', 'create_source',
|
|
'tongue_images', 'report_files',
|
|
]);
|
|
}
|
|
|
|
public function sceneEdit()
|
|
{
|
|
return $this->only(['id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'marital_status', 'height', 'weight', 'region', 'systolic_pressure', 'diastolic_pressure', 'fasting_blood_sugar', 'diabetes_discovery_year', 'local_hospital_diagnosis', 'local_hospital_name', 'past_history', 'symptoms', 'tongue_coating', 'pulse', 'treatment_principle', 'prescription', 'doctor_advice', 'remark', 'current_medications', 'status', 'show_card', 'create_source']);
|
|
}
|
|
|
|
public function sceneId()
|
|
{
|
|
return $this->only(['id']);
|
|
}
|
|
|
|
public function sceneReadonlyDetail()
|
|
{
|
|
return $this->only(['id']);
|
|
}
|
|
|
|
public function sceneTrackingWindow()
|
|
{
|
|
return $this->only(['id', 'start_date', 'end_date']);
|
|
}
|
|
|
|
/** 新增跟踪备注:诊单ID + 备注内容 */
|
|
public function sceneAddTrackingNote()
|
|
{
|
|
return $this->only(['diagnosis_id', 'tracking_content']);
|
|
}
|
|
|
|
/** 拉取跟踪备注列表:诊单ID */
|
|
public function sceneTrackingNotes()
|
|
{
|
|
return $this->only(['diagnosis_id']);
|
|
}
|
|
|
|
public function sceneGenerateQrcode()
|
|
{
|
|
return $this->only(['diagnosis_id', 'doctor_id', 'patient_id', 'share_user_id', 'mini_program_path'])
|
|
// The global diagnosis_id rule is required for diagnosis APIs, but
|
|
// video QR codes identify the doctor instead. Keep diagnosis_id
|
|
// optional here while still validating it when it is supplied.
|
|
->remove('diagnosis_id', 'require')
|
|
->append('patient_id', 'require|integer|checkQrcodeIds')
|
|
->append('share_user_id', 'require|integer')
|
|
->append('doctor_id', 'integer');
|
|
}
|
|
|
|
public function sceneGenerateOrderQrcode()
|
|
{
|
|
return $this->only([]);
|
|
}
|
|
|
|
public function sceneFillIdCard()
|
|
{
|
|
return $this->only(['id', 'id_card'])
|
|
->append('id', 'require')
|
|
->append('id_card', 'require|length:15,18');
|
|
}
|
|
|
|
public function sceneGuahaoLogList()
|
|
{
|
|
return $this->only(['id']);
|
|
}
|
|
|
|
/** 业务订单 tab:设置复诊接诊率统计起始偏移 */
|
|
public function sceneSetRevisitSlotStartOffset()
|
|
{
|
|
return $this->only(['id', 'revisit_slot_start_offset'])
|
|
->append('revisit_slot_start_offset', 'require|integer|between:0,20');
|
|
}
|
|
|
|
public function sceneAiReports()
|
|
{
|
|
return $this->only(['id']);
|
|
}
|
|
|
|
public function sceneAiAssistant()
|
|
{
|
|
return $this->only(['id', 'task', 'prompt']);
|
|
}
|
|
|
|
public function sceneAiAnalysis()
|
|
{
|
|
return $this->only(['id', 'model'])
|
|
->remove('id', 'checkDiagnosis')
|
|
->append('id', 'integer|gt:0|checkAiAnalysisPayload');
|
|
}
|
|
|
|
public function scenePatientAiReports()
|
|
{
|
|
return $this->only(['patient_id'])
|
|
->append('patient_id', 'require|integer|gt:0|checkPatientAiReportsPayload');
|
|
}
|
|
|
|
public function sceneGeneratePatientAiReport()
|
|
{
|
|
return $this->only(['patient_id', 'model'])
|
|
->append('patient_id', 'require|integer|gt:0|checkGeneratePatientAiReportPayload')
|
|
->append('model', 'require|in:qwen,openai');
|
|
}
|
|
|
|
public function sceneGenerateAiReports()
|
|
{
|
|
return $this->only(['id']);
|
|
}
|
|
|
|
public function sceneEditAiReport()
|
|
{
|
|
return $this->only(['id', 'report_id', 'content'])
|
|
->append('report_id', 'require|number|gt:0')
|
|
->append('content', 'require|max:12000');
|
|
}
|
|
|
|
protected function checkDiagnosis($value)
|
|
{
|
|
$diagnosis = Diagnosis::findOrEmpty($value);
|
|
if ($diagnosis->isEmpty()) {
|
|
return '诊单不存在';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** 校验 diagnosis_id 字段(addTrackingNote 等场景使用,避开 id 字段) */
|
|
protected function checkDiagnosisId($value)
|
|
{
|
|
$diagnosis = Diagnosis::findOrEmpty($value);
|
|
if ($diagnosis->isEmpty()) {
|
|
return '诊单不存在';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** end_date 不能早于 start_date */
|
|
protected function checkDateRange($value, $rule, $data = [])
|
|
{
|
|
$start = $data['start_date'] ?? '';
|
|
if ($start && $value && strtotime($value) < strtotime($start)) {
|
|
return '结束日期不能早于开始日期';
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** 视频场景需 doctor_id,确认诊单需 diagnosis_id */
|
|
protected function checkQrcodeIds($value, $rule, $data = [])
|
|
{
|
|
$page = $data['mini_program_path'] ?? '';
|
|
$hasDoctor = !empty($data['doctor_id']);
|
|
$hasDiagnosis = !empty($data['diagnosis_id']);
|
|
if ($page === 'pages/login/login') {
|
|
return $hasDoctor ? true : '视频二维码需传挂号医生ID';
|
|
}
|
|
return $hasDiagnosis ? true : '诊单ID不能为空';
|
|
}
|
|
|
|
/**
|
|
* AI 智能分析仅接受诊单 ID 和可选的固定模型键。客户端不得提供
|
|
* provider、凭据、上游地址、提示词等服务端配置或控制字段。
|
|
*
|
|
* @param mixed $value
|
|
* @param mixed $rule
|
|
* @param array<string,mixed> $data
|
|
* @return bool|string
|
|
*/
|
|
protected function checkAiAnalysisPayload($value, $rule, array $data = [])
|
|
{
|
|
if (!array_key_exists('id', $data)) {
|
|
return 'AI智能分析请求缺少id参数';
|
|
}
|
|
|
|
$extraFields = array_diff(array_keys($data), ['id', 'model']);
|
|
if ($extraFields !== []) {
|
|
return 'AI智能分析请求仅允许id及可选model参数';
|
|
}
|
|
|
|
if (array_key_exists('model', $data)) {
|
|
$model = $data['model'];
|
|
if (!is_string($model) || !in_array($model, ['qwen', 'openai'], true)) {
|
|
return 'AI模型仅支持qwen或openai';
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** @param array<string,mixed> $data */
|
|
protected function checkPatientAiReportsPayload($value, $rule, array $data = [])
|
|
{
|
|
if (!array_key_exists('patient_id', $data)) {
|
|
return '请求缺少patient_id参数';
|
|
}
|
|
$extraFields = array_diff(array_keys($data), ['patient_id']);
|
|
return $extraFields === [] ? true : '请求仅允许patient_id参数';
|
|
}
|
|
|
|
/**
|
|
* 患者报告生成端点只接受稳定患者ID与服务端模型白名单键。
|
|
* provider、凭据、BASE_URL、提示词和任意来源正文均不得由客户端注入。
|
|
*
|
|
* @param array<string,mixed> $data
|
|
*/
|
|
protected function checkGeneratePatientAiReportPayload($value, $rule, array $data = [])
|
|
{
|
|
if (!array_key_exists('patient_id', $data) || !array_key_exists('model', $data)) {
|
|
return '请求缺少patient_id或model参数';
|
|
}
|
|
$extraFields = array_diff(array_keys($data), ['patient_id', 'model']);
|
|
if ($extraFields !== []) {
|
|
return '请求仅允许patient_id和model参数';
|
|
}
|
|
if (!is_string($data['model']) || !in_array($data['model'], ['qwen', 'openai'], true)) {
|
|
return 'AI模型仅支持qwen或openai';
|
|
}
|
|
return true;
|
|
}
|
|
}
|