This commit is contained in:
Your Name
2026-03-18 14:53:09 +08:00
parent 338b104d50
commit 7832514f28
315 changed files with 7071 additions and 1136 deletions
@@ -159,6 +159,20 @@ class DiagnosisController extends BaseAdminController
$result = DiagnosisLogic::checkIdCard($params);
return $this->data($result);
}
/**
* @notes 补全身份证号(自动计算年龄并更新)
* @return \think\response\Json
*/
public function fillIdCard()
{
$params = (new DiagnosisValidate())->post()->goCheck('fillIdCard');
$result = DiagnosisLogic::fillIdCard($params);
if ($result) {
return $this->success('补全成功,年龄已自动更新', [], 1, 1);
}
return $this->fail(DiagnosisLogic::getError());
}
/**
* @notes 指派医助
@@ -65,4 +65,21 @@ class PrescriptionController extends BaseAdminController
$prescription = PrescriptionLogic::getByAppointment($appointmentId);
return $this->data($prescription ?? []);
}
/**
* @notes 作废处方
*/
public function void()
{
$id = (int)($this->request->post('id') ?? 0);
if (!$id) {
return $this->fail('处方ID不能为空');
}
$admin = $this->adminInfo;
$ok = PrescriptionLogic::void($id, (int)$this->adminId, $admin['name'] ?? '');
if (!$ok) {
return $this->fail(PrescriptionLogic::getError());
}
return $this->success('作废成功');
}
}
@@ -3,7 +3,9 @@
namespace app\adminapi\lists\doctor;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\model\DiagnosisViewRecord;
use app\common\model\doctor\Appointment;
use app\common\model\tcm\Prescription;
use app\common\model\auth\AdminRole;
use app\common\lists\ListsSearchInterface;
@@ -55,14 +57,26 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
} elseif (!empty($this->params['end_date'])) {
$this->searchWhere[] = ['a.appointment_date', '<=', $this->params['end_date']];
}
// 构建查询
$query = Appointment::alias('a')
->with('diagnosis')
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
->leftJoin('admin ad', 'a.doctor_id = ad.id')
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, ad.name as doctor_name, u.id as diagnosis_id')
->field('a.*, u.patient_name as patient_name, u.phone as patient_phone, u.gender as gender, u.age as age, u.weight as weight, u.height as height, ad.name as doctor_name, u.id as diagnosis_id')
->where($this->searchWhere);
// 是否确认诊单:1=已确认 0=未确认
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed'];
$tbl = (new DiagnosisViewRecord())->getTable();
$subSql = "SELECT 1 FROM {$tbl} dvr WHERE dvr.diagnosis_id = u.id AND dvr.is_confirmed = 1 AND dvr.delete_time IS NULL";
if ($confirmed === 1) {
$query->whereExists($subSql);
} else {
$query->whereNotExists($subSql);
}
}
// 如果是医生角色(role_id=1),只显示挂自己号的预约
if (in_array(1, $roleIds)) {
@@ -82,7 +96,25 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
->select()
->toArray();
// 添加状态类型描述
// 添加状态类型描述及确认诊单状态
$diagnosisIds = array_filter(array_unique(array_column($lists, 'diagnosis_id')));
$confirmedMap = [];
if (!empty($diagnosisIds)) {
$confirmedIds = \think\facade\Db::name('diagnosis_view_records')
->whereIn('diagnosis_id', $diagnosisIds)
->where('is_confirmed', 1)
->whereNull('delete_time')
->column('diagnosis_id');
$confirmedMap = array_flip($confirmedIds ?: []);
}
// 开方状态:诊单是否有处方(按 diagnosis_id 查)
$prescribedDiagnosisIds = [];
if (!empty($diagnosisIds)) {
$prescribedDiagnosisIds = Prescription::whereIn('diagnosis_id', $diagnosisIds)
->whereNull('delete_time')
->column('diagnosis_id');
$prescribedDiagnosisIds = array_flip($prescribedDiagnosisIds ?: []);
}
foreach ($lists as &$item) {
$statusMap = [
1 => '已预约',
@@ -99,6 +131,9 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
];
$item['appointment_type_desc'] = $typeMap[$item['appointment_type']] ?? '未知';
$item['diagnosis_confirmed'] = isset($confirmedMap[$item['diagnosis_id'] ?? 0]) ? 1 : 0;
$item['has_prescription'] = isset($prescribedDiagnosisIds[$item['diagnosis_id'] ?? 0]) ? 1 : 0;
// 格式化时间戳为日期时间
if (isset($item['create_time']) && is_numeric($item['create_time'])) {
$item['create_time'] = date('Y-m-d H:i:s', $item['create_time']);
@@ -149,6 +184,18 @@ class AppointmentLists extends BaseAdminDataLists implements ListsSearchInterfac
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
->leftJoin('admin ad', 'a.doctor_id = ad.id')
->where($this->searchWhere);
// 是否确认诊单
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed'];
$tbl = (new DiagnosisViewRecord())->getTable();
$subSql = "SELECT 1 FROM {$tbl} dvr WHERE dvr.diagnosis_id = u.id AND dvr.is_confirmed = 1 AND dvr.delete_time IS NULL";
if ($confirmed === 1) {
$query->whereExists($subSql);
} else {
$query->whereNotExists($subSql);
}
}
// 如果是医生角色(role_id=1),只显示挂自己号的预约
if (in_array(1, $roleIds)) {
@@ -16,6 +16,10 @@ namespace app\adminapi\lists\tcm;
use app\adminapi\lists\BaseAdminDataLists;
use app\common\model\tcm\Diagnosis;
use app\common\model\DiagnosisViewRecord;
use app\common\model\doctor\Appointment;
use app\common\model\tcm\Prescription;
use app\common\model\auth\Admin;
use app\common\model\auth\AdminRole;
use app\common\lists\ListsSearchInterface;
@@ -58,6 +62,38 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
if (in_array(2, $roleIds)) {
$query->where('assistant_id', $this->adminId);
}
// 是否确认诊单:1=已确认 0=未确认
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed'];
$dvrTbl = (new DiagnosisViewRecord())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$subSql = "SELECT 1 FROM {$dvrTbl} dvr WHERE dvr.diagnosis_id = {$diagTbl}.id AND dvr.is_confirmed = 1 AND dvr.delete_time IS NULL";
if ($confirmed === 1) {
$query->whereExists($subSql);
} else {
$query->whereNotExists($subSql);
}
}
// 挂号日期筛选:当天/明天/后天
if (!empty($this->params['appointment_date'])) {
$aptDate = addslashes($this->params['appointment_date']);
$aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.appointment_date = '{$aptDate}' AND apt.status IN (1,3)");
}
// 挂号状态:1=已挂号 0=未挂号
if (isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') {
$aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable();
if ((int)$this->params['has_appointment'] === 1) {
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.status IN (1,3)");
} else {
$query->whereNotExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.status IN (1,3)");
}
}
$lists = $query
->with(['DiagnosisViewRecord'])
@@ -68,6 +104,70 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
->select()
->toArray();
// 关联挂号信息:获取患者最新有效挂号(已预约或已完成)
$patientIds = array_filter(array_unique(array_column($lists, 'patient_id')));
if (!empty($patientIds)) {
$appointmentMap = [];
$subQuery = Appointment::where('patient_id', 'in', $patientIds)
->where('status', 'in', [1, 3]) // 1=已预约 3=已完成
->field('id, patient_id, doctor_id, appointment_date, appointment_time, create_time')
->order('id', 'desc');
$appointments = $subQuery->select()->toArray();
foreach ($appointments as $apt) {
$pid = $apt['patient_id'];
if (!isset($appointmentMap[$pid])) {
$appointmentMap[$pid] = $apt;
}
}
// 获取医生名称
$doctorIds = array_unique(array_column($appointmentMap, 'doctor_id'));
$doctorNames = [];
if (!empty($doctorIds)) {
$doctors = Admin::whereIn('id', $doctorIds)->column('name', 'id');
$doctorNames = $doctors ?: [];
}
// 合并到诊单列表
foreach ($lists as &$item) {
$apt = $appointmentMap[$item['patient_id']] ?? null;
if ($apt) {
$item['has_appointment'] = 1;
$item['appointment_doctor_id'] = $apt['doctor_id'];
$item['appointment_doctor_name'] = $doctorNames[$apt['doctor_id']] ?? '-';
$timePart = $apt['appointment_time'] ?? '';
if (strlen($timePart) > 5) {
$timePart = substr($timePart, 0, 5); // 09:00:00 -> 09:00
}
$item['appointment_time_text'] = trim(($apt['appointment_date'] ?? '') . ' ' . $timePart);
} else {
$item['has_appointment'] = 0;
$item['appointment_doctor_id'] = null;
$item['appointment_doctor_name'] = '';
$item['appointment_time_text'] = '';
}
}
} else {
foreach ($lists as &$item) {
$item['has_appointment'] = 0;
$item['appointment_doctor_id'] = null;
$item['appointment_doctor_name'] = '';
$item['appointment_time_text'] = '';
}
}
// 关联是否开方:诊单是否有处方记录
$diagnosisIds = array_column($lists, 'id');
$prescriptionMap = [];
if (!empty($diagnosisIds)) {
$prescriptionTbl = (new Prescription())->getTable();
$prescribedIds = Prescription::whereIn('diagnosis_id', $diagnosisIds)
->whereNull('delete_time')
->column('diagnosis_id');
$prescriptionMap = array_fill_keys($prescribedIds, 1);
}
foreach ($lists as &$item) {
$item['has_prescription'] = isset($prescriptionMap[$item['id']]) ? 1 : 0;
}
return $lists;
}
@@ -87,6 +187,38 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
if (in_array(2, $roleIds)) {
$query->where('assistant_id', $this->adminId);
}
// 是否确认诊单
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed'];
$dvrTbl = (new DiagnosisViewRecord())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$subSql = "SELECT 1 FROM {$dvrTbl} dvr WHERE dvr.diagnosis_id = {$diagTbl}.id AND dvr.is_confirmed = 1 AND dvr.delete_time IS NULL";
if ($confirmed === 1) {
$query->whereExists($subSql);
} else {
$query->whereNotExists($subSql);
}
}
// 挂号日期筛选
if (!empty($this->params['appointment_date'])) {
$aptDate = addslashes($this->params['appointment_date']);
$aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.appointment_date = '{$aptDate}' AND apt.status IN (1,3)");
}
// 挂号状态
if (isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') {
$aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable();
if ((int)$this->params['has_appointment'] === 1) {
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.status IN (1,3)");
} else {
$query->whereNotExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.patient_id AND apt.status IN (1,3)");
}
}
return $query->count();
}
@@ -132,10 +132,13 @@ class WebSettingLogic extends BaseLogic
{
$serviceContent = clear_file_domain($params['service_content'] ?? '');
$privacyContent = clear_file_domain($params['privacy_content'] ?? '');
$healthContent = clear_file_domain($params['health_content'] ?? '');
ConfigService::set('agreement', 'service_title', $params['service_title'] ?? '');
ConfigService::set('agreement', 'service_content', $serviceContent);
ConfigService::set('agreement', 'privacy_title', $params['privacy_title'] ?? '');
ConfigService::set('agreement', 'privacy_content', $privacyContent);
ConfigService::set('agreement', 'health_title', $params['health_title'] ?? '');
ConfigService::set('agreement', 'health_content', $healthContent);
}
@@ -152,11 +155,13 @@ class WebSettingLogic extends BaseLogic
'service_content' => ConfigService::get('agreement', 'service_content'),
'privacy_title' => ConfigService::get('agreement', 'privacy_title'),
'privacy_content' => ConfigService::get('agreement', 'privacy_content'),
'health_title' => ConfigService::get('agreement', 'health_title'),
'health_content' => ConfigService::get('agreement', 'health_content'),
];
$config['service_content'] = get_file_domain($config['service_content']);
$config['privacy_content'] = get_file_domain($config['privacy_content']);
$config['health_content'] = get_file_domain($config['health_content']);
return $config;
}
@@ -403,6 +403,50 @@ class DiagnosisLogic extends BaseLogic
];
}
/**
* @notes 补全身份证号(自动计算年龄并更新)
* @param array $params id, id_card
* @return bool
*/
public static function fillIdCard(array $params): bool
{
try {
$diagnosis = Diagnosis::find($params['id']);
if (!$diagnosis) {
self::setError('诊单不存在');
return false;
}
$idCard = trim($params['id_card']);
$len = strlen($idCard);
if ($len === 15) {
$birthYear = 1900 + (int)substr($idCard, 6, 2);
$birthMonth = (int)substr($idCard, 8, 2);
$birthDay = (int)substr($idCard, 10, 2);
} elseif ($len === 18 && preg_match('/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/', $idCard)) {
$birthYear = (int)substr($idCard, 6, 4);
$birthMonth = (int)substr($idCard, 10, 2);
$birthDay = (int)substr($idCard, 12, 2);
} else {
self::setError('身份证号格式不正确');
return false;
}
// 从身份证提取出生日期计算年龄
$today = getdate();
$age = $today['year'] - $birthYear;
if ($today['mon'] < $birthMonth || ($today['mon'] == $birthMonth && $today['mday'] < $birthDay)) {
$age--;
}
$age = max(0, min(150, $age));
$diagnosis->id_card = $idCard;
$diagnosis->age = $age;
$diagnosis->save();
return true;
} catch (\Exception $e) {
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 指派医助
* @param array $params
@@ -954,21 +998,28 @@ class DiagnosisLogic extends BaseLogic
*/
public static function generateMiniProgramQrcode(array $params)
{
try {
$diagnosisId = $params['diagnosis_id'];
$diagnosisId = $params['diagnosis_id'] ?? '';
$doctorId = $params['doctor_id'] ?? '';
$patientId = $params['patient_id'];
$shareUserId = $params['share_user_id'];
// 视频登录页(pages/login/login)需要传挂号医生id,其余场景传诊单id
$page = !empty($params['mini_program_path']) ? $params['mini_program_path'] : 'pages/order/monad/monad';
$sceneId = ($page === 'pages/login/login' && !empty($doctorId)) ? $doctorId : $diagnosisId;
if (empty($sceneId)) {
throw new \Exception($page === 'pages/login/login' ? '挂号医生ID不能为空' : '诊单ID不能为空');
}
// 获取小程序配置
$config = self::getMiniProgramConfig();
if (!$config) {
throw new \Exception('小程序配置未设置');
}
// 构建小程序路径和参数(前端可传 mini_program_path 覆盖默认路径)
$page = !empty($params['mini_program_path']) ? $params['mini_program_path'] : 'pages/order/monad/monad';
$scene = "id={$diagnosisId}&share_user={$shareUserId}";
$scene = "id={$sceneId}&share_user={$shareUserId}";
// 调用微信接口生成小程序码
$qrcodeUrl = self::generateWxQrcode($config, $page, $scene);
@@ -71,6 +71,7 @@ class PrescriptionLogic
'clinical_diagnosis' => $params['clinical_diagnosis'] ?? '',
'herbs' => $herbs,
'dose_count' => (int)($params['dose_count'] ?? 1),
'dose_unit' => $params['dose_unit'] ?? '剂',
'usage_instruction' => $params['usage_instruction'] ?? '水煎服一日二次',
'amount' => (float)($params['amount'] ?? 0),
'doctor_name' => $params['doctor_name'] ?? '',
@@ -121,4 +122,25 @@ class PrescriptionLogic
->find();
return $row ? $row->toArray() : null;
}
/**
* 作废处方
*/
public static function void(int $id, int $adminId, string $adminName): bool
{
$row = Prescription::find($id);
if (!$row) {
self::setError('处方不存在');
return false;
}
if ((int)($row->void_status ?? 0) === 1) {
self::setError('该处方已作废');
return false;
}
$row->void_status = 1;
$row->void_time = time();
$row->void_by = $adminId;
$row->void_by_name = $adminName;
return $row->save();
}
}
@@ -72,10 +72,11 @@ class DiagnosisValidate extends BaseValidate
public function sceneGenerateQrcode()
{
return $this->only(['diagnosis_id', 'patient_id', 'share_user_id'])
->append('diagnosis_id', 'require')
return $this->only(['diagnosis_id', 'doctor_id', 'patient_id', 'share_user_id', 'mini_program_path'])
->append('patient_id', 'require')
->append('share_user_id', 'require');
->append('share_user_id', 'require')
->append('diagnosis_id', 'checkQrcodeIds')
->append('doctor_id', 'checkQrcodeIds');
}
public function sceneGenerateOrderQrcode()
@@ -83,6 +84,13 @@ class DiagnosisValidate extends BaseValidate
return $this->only([]);
}
public function sceneFillIdCard()
{
return $this->only(['id', 'id_card'])
->append('id', 'require')
->append('id_card', 'require|length:15,18');
}
protected function checkDiagnosis($value)
{
$diagnosis = Diagnosis::findOrEmpty($value);
@@ -91,4 +99,16 @@ class DiagnosisValidate extends BaseValidate
}
return true;
}
/** 视频场景需 doctor_id,确认诊单需 diagnosis_id */
protected function checkQrcodeIds($value, $rule, $data = [])
{
$page = $data['mini_program_path'] ?? '';
$hasDoctor = !empty($data['diagnosis_id']);
$hasDiagnosis = !empty($data['diagnosis_id']);
if ($page === 'pages/login/login') {
return $hasDoctor ? true : '视频二维码需传挂号医生ID';
}
return $hasDiagnosis ? true : '诊单ID不能为空';
}
}