Merge branch 'master' of https://gitee.com/v_cms/zyt
This commit is contained in:
@@ -9,6 +9,8 @@ use app\common\service\doctor\RosterSegmentService;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\DiagnosisGuahaoLog;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminRole;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\api\logic\ChatNotifyLogic;
|
||||
use app\adminapi\logic\tcm\PrescriptionLogic;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
@@ -42,6 +44,61 @@ class AppointmentLogic extends BaseLogic
|
||||
return in_array($name, self::CHANNEL_NAMES_REQUIRING_SOURCE_DETAIL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $cols Db::name('doctor_appointment')->getTableFields()
|
||||
*/
|
||||
private static function assertAppointmentChannelWritable(array $cols, string $chSrc): ?string
|
||||
{
|
||||
$hasChannelSource = in_array('channel_source', $cols, true);
|
||||
$hasChannels = in_array('channels', $cols, true);
|
||||
if (!$hasChannelSource && !$hasChannels) {
|
||||
return '挂号表缺少渠道字段(channel_source 或 channels),无法保存渠道来源';
|
||||
}
|
||||
if (!$hasChannelSource && $hasChannels && $chSrc !== '' && !is_numeric($chSrc)) {
|
||||
return '当前挂号表仅有数值型渠道字段 channels,无法写入该渠道;请在数据库增加 channel_source 列';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int|string, mixed> $cols
|
||||
*/
|
||||
private static function mergeAppointmentChannelIntoRow(array &$row, array $cols, string $chSrc, string $chDetail): void
|
||||
{
|
||||
$hasChannelSource = in_array('channel_source', $cols, true);
|
||||
$hasChannelDetail = in_array('channel_source_detail', $cols, true);
|
||||
$hasChannels = in_array('channels', $cols, true);
|
||||
if ($hasChannelSource) {
|
||||
$row['channel_source'] = $chSrc;
|
||||
}
|
||||
if ($hasChannelDetail) {
|
||||
$row['channel_source_detail'] = $chDetail;
|
||||
}
|
||||
if ($hasChannels && is_numeric($chSrc)) {
|
||||
$row['channels'] = (int) $chSrc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @param array<int|string, mixed> $cols
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function filterAppointmentRowByExistingColumns(array $row, array $cols): array
|
||||
{
|
||||
$out = [];
|
||||
foreach ($row as $k => $v) {
|
||||
if (in_array((string) $k, $cols, true)) {
|
||||
$out[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 获取可用时间段
|
||||
* @param array $params
|
||||
@@ -262,25 +319,49 @@ class AppointmentLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'patient_id' => $params['patient_id'],
|
||||
'assistant_id'=>$params['assistant_id'],
|
||||
'doctor_id' => $params['doctor_id'],
|
||||
'roster_id' => 0, // 不再关联排班表
|
||||
$cols = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = is_array($cols) ? $cols : [];
|
||||
$channelErr = self::assertAppointmentChannelWritable($cols, $chSrc);
|
||||
if ($channelErr !== null) {
|
||||
self::setError($channelErr);
|
||||
Db::rollback();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$insert = [
|
||||
'patient_id' => (int) $params['patient_id'],
|
||||
'doctor_id' => (int) $params['doctor_id'],
|
||||
'appointment_date' => $params['appointment_date'],
|
||||
'period' => $params['period'] ?? 'all',
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $params['appointment_type'] ?? 'video',
|
||||
'remark' => $params['remark'] ?? '',
|
||||
// 表字段为 channel_source(与字典 type_value=channels 的 value 一致);勿再写入不存在的 channels 列
|
||||
'channel_source' => $chSrc,
|
||||
'channel_source_detail' => $chDetail,
|
||||
'status' => 1,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
];
|
||||
if (in_array('roster_id', $cols, true)) {
|
||||
$insert['roster_id'] = 0;
|
||||
}
|
||||
if (in_array('assistant_id', $cols, true)) {
|
||||
$insert['assistant_id'] = (int) ($params['assistant_id'] ?? 0);
|
||||
}
|
||||
$periodVal = $params['period'] ?? 'all';
|
||||
if (in_array('period', $cols, true)) {
|
||||
$insert['period'] = $periodVal;
|
||||
} elseif (in_array('type', $cols, true)) {
|
||||
$insert['type'] = $periodVal;
|
||||
}
|
||||
self::mergeAppointmentChannelIntoRow($insert, $cols, $chSrc, $chDetail);
|
||||
|
||||
$appointment = Appointment::create($data);
|
||||
$insert = self::filterAppointmentRowByExistingColumns($insert, $cols);
|
||||
$newId = (int) Db::name('doctor_appointment')->insertGetId($insert);
|
||||
if ($newId <= 0) {
|
||||
self::setError('预约创建失败');
|
||||
Db::rollback();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::commit();
|
||||
|
||||
@@ -288,21 +369,21 @@ class AppointmentLogic extends BaseLogic
|
||||
$hm = substr((string) $appointmentTime, 0, 5);
|
||||
$summary = sprintf(
|
||||
'挂号 #%d:医生「%s」,%s %s',
|
||||
(int) $appointment->id,
|
||||
$newId,
|
||||
$doctorName !== '' ? $doctorName : ('ID:' . (int) $params['doctor_id']),
|
||||
(string) $params['appointment_date'],
|
||||
$hm
|
||||
);
|
||||
self::appendDiagnosisGuahaoLog(
|
||||
(int) $params['patient_id'],
|
||||
(int) $appointment->id,
|
||||
$newId,
|
||||
$operatorAdminId,
|
||||
$operatorAdminInfo,
|
||||
'create',
|
||||
$summary
|
||||
);
|
||||
|
||||
return ['id' => $appointment->id];
|
||||
return ['id' => $newId];
|
||||
} catch (\Exception $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
@@ -787,6 +868,244 @@ class AppointmentLogic extends BaseLogic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 AppointmentLists 一致的可见性(不含 progress_board / diag_scope_relax)
|
||||
*/
|
||||
private static function appointmentRowManageableByAdmin(
|
||||
Appointment $appointment,
|
||||
?Diagnosis $diag,
|
||||
int $adminId,
|
||||
array $adminInfo
|
||||
): bool {
|
||||
$roleIds = array_map('intval', AdminRole::where('admin_id', $adminId)->column('role_id'));
|
||||
|
||||
if (in_array(1, $roleIds, true) && (int) $appointment->doctor_id !== $adminId) {
|
||||
return false;
|
||||
}
|
||||
if (in_array(2, $roleIds, true)) {
|
||||
$asst = $diag ? (int) $diag->assistant_id : 0;
|
||||
if ($asst !== $adminId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DataScopeService::isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
$ids = DataScopeService::getVisibleAdminIds($adminId, $adminInfo);
|
||||
if ($ids === []) {
|
||||
return false;
|
||||
}
|
||||
if ($ids === null) {
|
||||
return true;
|
||||
}
|
||||
$docId = (int) $appointment->doctor_id;
|
||||
$asstId = $diag ? (int) $diag->assistant_id : 0;
|
||||
|
||||
return in_array($docId, $ids, true)
|
||||
|| ($asstId > 0 && in_array($asstId, $ids, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台编辑挂号(预约日期/时段/类型/状态/备注/医助)
|
||||
*
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public static function adminEdit(array $params, int $adminId, array $adminInfo): bool
|
||||
{
|
||||
try {
|
||||
$id = (int) ($params['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
self::setError('参数错误');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointment = Appointment::findOrEmpty($id);
|
||||
if ($appointment->isEmpty()) {
|
||||
self::setError('预约记录不存在');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$diag = Diagnosis::where('id', (int) $appointment->patient_id)->whereNull('delete_time')->find();
|
||||
|
||||
if (!self::appointmentRowManageableByAdmin($appointment, $diag ?: null, $adminId, $adminInfo)) {
|
||||
self::setError('无权限编辑');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$period = trim((string) ($params['period'] ?? ''));
|
||||
if (!in_array($period, ['morning', 'afternoon', 'all'], true)) {
|
||||
self::setError('时段无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointmentDate = trim((string) ($params['appointment_date'] ?? ''));
|
||||
if ($appointmentDate === '') {
|
||||
self::setError('预约日期不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$rawTime = trim((string) ($params['appointment_time'] ?? ''));
|
||||
if ($rawTime === '') {
|
||||
self::setError('预约时间不能为空');
|
||||
|
||||
return false;
|
||||
}
|
||||
$appointmentTime = strlen($rawTime) === 5 ? $rawTime . ':00' : $rawTime;
|
||||
|
||||
$status = (int) ($params['status'] ?? 0);
|
||||
if ($status < 1 || $status > 4) {
|
||||
self::setError('状态无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$appointmentType = trim((string) ($params['appointment_type'] ?? ''));
|
||||
if (!in_array($appointmentType, ['video', 'text', 'phone'], true)) {
|
||||
self::setError('预约类型无效');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$remark = isset($params['remark']) ? trim((string) $params['remark']) : '';
|
||||
if (mb_strlen($remark) > 500) {
|
||||
self::setError('备注过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$chSrc = trim((string) ($params['channel_source'] ?? ''));
|
||||
if ($chSrc === '') {
|
||||
self::setError('请选择渠道来源');
|
||||
|
||||
return false;
|
||||
}
|
||||
$chDetail = trim((string) ($params['channel_source_detail'] ?? ''));
|
||||
$channelDictName = trim((string) (DictData::where('type_value', 'channels')
|
||||
->where('value', $chSrc)
|
||||
->value('name') ?? ''));
|
||||
if ($channelDictName !== '' && self::channelDictNameRequiresSourceDetail($channelDictName)) {
|
||||
if ($chDetail === '') {
|
||||
self::setError('请填写自媒体渠道补充信息');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (mb_strlen($chDetail) > 128) {
|
||||
self::setError('渠道补充说明过长');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$postAssistant = array_key_exists('assistant_id', $params);
|
||||
$assistantId = null;
|
||||
if ($postAssistant) {
|
||||
$assistantRaw = $params['assistant_id'];
|
||||
if ($assistantRaw !== null && $assistantRaw !== '') {
|
||||
$aid = (int) $assistantRaw;
|
||||
$assistantId = $aid > 0 ? $aid : null;
|
||||
}
|
||||
}
|
||||
|
||||
$patientId = (int) $appointment->patient_id;
|
||||
$doctorId = (int) $appointment->doctor_id;
|
||||
|
||||
if (in_array($status, [1, 4], true)) {
|
||||
$patientDup = Appointment::where('patient_id', $patientId)
|
||||
->where('appointment_date', $appointmentDate)
|
||||
->whereIn('status', [1, 4])
|
||||
->where('id', '<>', $id)
|
||||
->find();
|
||||
if ($patientDup) {
|
||||
self::setError('该诊单在所选日期已有其他有效挂号(已预约/已过号)');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($status === 1) {
|
||||
$slotDup = Appointment::where('doctor_id', $doctorId)
|
||||
->where('appointment_date', $appointmentDate)
|
||||
->where('appointment_time', $appointmentTime)
|
||||
->where('status', 1)
|
||||
->where('id', '<>', $id)
|
||||
->find();
|
||||
if ($slotDup) {
|
||||
self::setError('该医生在所选时段已被占用');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$beforeDate = (string) ($appointment->appointment_date ?? '');
|
||||
$beforeTimeRaw = (string) ($appointment->appointment_time ?? '');
|
||||
$beforeHm = strlen($beforeTimeRaw) >= 5 ? substr($beforeTimeRaw, 0, 5) : $beforeTimeRaw;
|
||||
$snap = $appointment->toArray();
|
||||
$beforePeriod = (string) ($snap['period'] ?? $snap['type'] ?? '');
|
||||
$beforeStatus = (int) $appointment->status;
|
||||
|
||||
$tblFields = Db::name('doctor_appointment')->getTableFields();
|
||||
$cols = is_array($tblFields) ? $tblFields : [];
|
||||
$channelErr = self::assertAppointmentChannelWritable($cols, $chSrc);
|
||||
if ($channelErr !== null) {
|
||||
self::setError($channelErr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
|
||||
$saveData = [
|
||||
'appointment_date' => $appointmentDate,
|
||||
'appointment_time' => $appointmentTime,
|
||||
'appointment_type' => $appointmentType,
|
||||
'status' => $status,
|
||||
'remark' => $remark,
|
||||
'update_time' => time(),
|
||||
];
|
||||
if (in_array('assistant_id', $cols, true) && $postAssistant) {
|
||||
$saveData['assistant_id'] = $assistantId;
|
||||
}
|
||||
if (in_array('period', $cols, true)) {
|
||||
$saveData['period'] = $period;
|
||||
} elseif (in_array('type', $cols, true)) {
|
||||
$saveData['type'] = $period;
|
||||
}
|
||||
self::mergeAppointmentChannelIntoRow($saveData, $cols, $chSrc, $chDetail);
|
||||
|
||||
Db::name('doctor_appointment')->where('id', $id)->update($saveData);
|
||||
|
||||
Db::commit();
|
||||
|
||||
$hm = substr($appointmentTime, 0, 5);
|
||||
$summary = sprintf(
|
||||
'后台修改挂号 #%d:日期 %s %s→%s %s,时段 %s→%s,状态 %d→%d',
|
||||
$id,
|
||||
$beforeDate,
|
||||
$beforeHm,
|
||||
$appointmentDate,
|
||||
$hm,
|
||||
$beforePeriod !== '' ? $beforePeriod : '—',
|
||||
$period,
|
||||
$beforeStatus,
|
||||
$status
|
||||
);
|
||||
self::appendDiagnosisGuahaoLog($patientId, $id, $adminId, $adminInfo, 'admin_edit', $summary);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
self::setError($e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 诊单列表维度:记录谁在后台发起挂号 / 取消挂号(写入失败不影响主流程)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user