This commit is contained in:
Your Name
2026-08-18 14:08:38 +08:00
parent 8b9df1154c
commit bc1228a310
77 changed files with 10763 additions and 1181 deletions
@@ -12,6 +12,7 @@ from typing import Any
from PySide6.QtCore import QDate, QSize, Qt, QTimer, QUrl, Signal
from PySide6.QtGui import QAction, QBrush, QColor, QDesktopServices, QPixmap
from PySide6.QtWidgets import (
QAbstractItemView,
QButtonGroup,
QCheckBox,
QComboBox,
@@ -34,6 +35,7 @@ from PySide6.QtWidgets import (
)
from ..dialogs import DiagnosisDialog
from ..dialogs.ai_consult import can_open_ai_consult, present_ai_consult
from ..dialogs.prescription import (
PrescriptionDetailDialog,
PrescriptionEditorDialog,
@@ -92,12 +94,12 @@ _SEMANTIC_COLORS = {
APPOINTMENTS_LIGHT_QSS = """
#AppointmentsPage QWidget#PageHeader {
min-height: 42px;
max-height: 42px;
min-height: 26px;
max-height: 26px;
}
#AppointmentsPage QFrame#AppointmentFilterPanel {
min-height: 92px;
max-height: 92px;
min-height: 80px;
max-height: 80px;
background-color: #FFFFFF;
border: 1px solid #E2E7F4;
border-radius: 10px;
@@ -109,9 +111,9 @@ APPOINTMENTS_LIGHT_QSS = """
border-radius: 10px;
}
#AppointmentsPage QPushButton[appointmentStat="true"] {
min-height: 34px;
max-height: 34px;
padding: 0 10px;
min-height: 30px;
max-height: 30px;
padding: 0 7px;
color: #59698E;
background-color: #F8F9FD;
border: 0;
@@ -141,8 +143,8 @@ APPOINTMENTS_LIGHT_QSS = """
background-color: #FFF8ED;
}
#AppointmentsPage QLineEdit#AppointmentPatientSearch {
min-height: 34px;
max-height: 34px;
min-height: 30px;
max-height: 30px;
background-color: #FFFFFF;
border: 1px solid #DDE3F0;
border-radius: 7px;
@@ -157,10 +159,10 @@ APPOINTMENTS_LIGHT_QSS = """
padding: 0 2px;
}
#AppointmentsPage QTableWidget#AppointmentTable::item {
padding: 6px 8px;
padding: 5px 7px;
}
#AppointmentsPage QTableWidget#AppointmentTable QHeaderView::section {
min-height: 35px;
min-height: 34px;
background-color: #F8FAFF;
}
#AppointmentsPage QTableWidget#AppointmentTable {
@@ -211,8 +213,9 @@ APPOINTMENTS_LIGHT_QSS = """
background-color: #FFFFFF;
}
#AppointmentsPage QPushButton[compactAction="true"] {
min-height: 30px;
padding: 0 10px;
min-height: 28px;
max-height: 28px;
padding: 0 8px;
border-radius: 7px;
font-size: 11px;
}
@@ -238,7 +241,7 @@ APPOINTMENTS_LIGHT_QSS = """
#AppointmentsPage QPushButton[variant="chip"]:focus { border-color: #8D9BFF; }
#AppointmentsPage QTabBar#AppointmentStatusTabs::tab {
min-width: 62px;
min-height: 32px;
min-height: 28px;
padding: 0 7px;
color: #7481A3;
background-color: transparent;
@@ -326,9 +329,9 @@ APPOINTMENTS_LIGHT_QSS = """
#AppointmentsPage QPushButton[videoActionKind="success"] { color: #159C79; }
#AppointmentsPage QPushButton[videoActionKind="warning"] { color: #C17A16; }
#AppointmentsPage QPushButton[filterChoice="true"] {
min-height: 32px;
max-height: 32px;
padding: 0 13px;
min-height: 28px;
max-height: 28px;
padding: 0 10px;
color: #405074;
background-color: transparent;
border: 1px solid transparent;
@@ -436,14 +439,9 @@ def _diagnosis_id(row: Any) -> int:
def _video_patient_id(row: Any) -> int:
source = _as_int(first_value(row, "source_patient_id", default=0))
if source > 0:
return source
diagnosis = _as_int(first_value(row, "diagnosis_id", default=0))
patient = _as_int(first_value(row, "patient_id", default=0))
if diagnosis > 0 and patient > 0 and diagnosis != patient:
return patient
return patient
"""Return only the appointment row's explicitly separated patient owner."""
return _as_int(first_value(row, "source_patient_id", default=0))
def _appointment_id(row: Any) -> int:
@@ -604,6 +602,56 @@ def _flatten_departments(
return result
def _stable_signature_value(value: Any) -> Any:
"""Return a deterministic, order-independent representation of API data."""
if value is None or isinstance(value, (bool, int, str)):
return value
if isinstance(value, float):
return repr(value)
if isinstance(value, Mapping):
items = (
(str(key), _stable_signature_value(item))
for key, item in value.items()
)
return tuple(sorted(items, key=lambda pair: pair[0]))
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
return tuple(_stable_signature_value(item) for item in value)
if isinstance(value, (set, frozenset)):
frozen = (_stable_signature_value(item) for item in value)
return tuple(sorted(frozen, key=repr))
dataclass_fields = getattr(type(value), "__dataclass_fields__", None)
if isinstance(dataclass_fields, Mapping):
return (
type(value).__qualname__,
tuple(
(name, _stable_signature_value(getattr(value, name, None)))
for name in dataclass_fields
),
)
state = getattr(value, "__dict__", None)
if isinstance(state, Mapping):
return (type(value).__qualname__, _stable_signature_value(state))
isoformat = getattr(value, "isoformat", None)
if callable(isoformat):
try:
return (type(value).__qualname__, isoformat())
except (TypeError, ValueError):
pass
return (type(value).__qualname__, str(value))
def _appointment_result_signature(rows: Sequence[Any], total: int, extend: Any) -> tuple[Any, ...]:
"""Build the stable render signature used to short-circuit unchanged polling."""
return (
"appointments-render-v1",
total,
_stable_signature_value(rows),
_stable_signature_value(extend),
)
class AppointmentsPage(QWidget):
"""Desktop appointment list with status tabs, filters, call and prescription."""
@@ -639,6 +687,9 @@ class AppointmentsPage(QWidget):
self._end_date = self._start_date
self._status_counts: dict[int, int] = {1: 0, 2: 0, 3: 0, 4: 0}
self._dict_labels: dict[str, dict[str, str]] = {}
self._last_render_signature: tuple[Any, ...] | None = None
self._responsive_narrow: bool | None = None
self._video_panel_user_open = False
self._can_plain_phone = _canonical_allowed(
permissions, "tcm.diagnosis/phonePlain", default=False
)
@@ -646,20 +697,23 @@ class AppointmentsPage(QWidget):
self._is_admin = _is_admin_user(current_user)
root = QVBoxLayout(self)
root.setContentsMargins(22, 7, 6, 8)
root.setContentsMargins(18, 3, 6, 8)
root.setSpacing(4)
header = PageHeader("问诊列表")
header.title_label.hide()
header.subtitle_label.hide()
root.addWidget(header)
root.addWidget(self._build_filter_panel())
self.header = PageHeader("问诊列表")
self.header.title_label.hide()
self.header.subtitle_label.hide()
root.addWidget(self.header)
self.filter_panel = self._build_filter_panel()
root.addWidget(self.filter_panel)
self.banner = MessageBanner()
root.addWidget(self.banner)
root.addWidget(self._build_content(), 1)
self.content_host = self._build_content()
root.addWidget(self.content_host, 1)
self.poll_timer = QTimer(self)
self.poll_timer.setInterval(LIST_POLL_MS)
self.poll_timer.timeout.connect(lambda: self.refresh(silent=True))
self._apply_responsive_layout()
def _diagnosis_dialog(self) -> DiagnosisDialog:
dialog = getattr(self, "_diagnosis_dialog_impl", None)
@@ -673,76 +727,77 @@ class AppointmentsPage(QWidget):
frame = QFrame()
frame.setObjectName("AppointmentFilterPanel")
layout = QVBoxLayout(frame)
layout.setContentsMargins(4, 2, 12, 12)
layout.setSpacing(10)
layout.setContentsMargins(6, 4, 8, 6)
layout.setSpacing(4)
date_row = QHBoxLayout()
date_row.setSpacing(8)
date_row.setSpacing(4)
self.date_buttons: dict[str, QPushButton] = {}
self._date_stat_labels: dict[str, str] = {}
stat_widths = {
"yesterday": 106,
"day_before": 98,
"today": 98,
"tomorrow": 94,
"day_after": 94,
"": 110,
}
for source_label, preset in DATE_PRESETS:
label = "全部" if preset == "" else source_label
button = QPushButton(f"{label} 0")
button.setCheckable(True)
button.setProperty("appointmentStat", True)
button.setFixedWidth(stat_widths[preset])
button.setMinimumWidth(0)
button.clicked.connect(
lambda _checked=False, value=preset: self._set_date_preset(value)
)
self.date_buttons[preset] = button
self._date_stat_labels[preset] = label
date_row.addWidget(button)
date_row.addWidget(button, 1)
self.date_buttons["today"].setChecked(True)
self.date_overflow_button = QPushButton("日期")
self.date_overflow_button.setProperty("appointmentStat", True)
date_menu = QMenu(self.date_overflow_button)
for source_label, preset in DATE_PRESETS:
action = date_menu.addAction(source_label if preset else "不限日期")
action.triggered.connect(
lambda _checked=False, value=preset: self._set_date_preset(value)
)
date_menu.addSeparator()
date_menu.addAction("自定义日期").triggered.connect(self._open_custom_date)
self.date_overflow_button.setMenu(date_menu)
date_row.addWidget(self.date_overflow_button)
self.pending_stat_button = QPushButton("待预约 0")
self.pending_stat_button.setProperty("appointmentStat", True)
self.pending_stat_button.setProperty("appointmentStatKind", "pending")
self.pending_stat_button.setFixedWidth(104)
self.pending_stat_button.setMinimumWidth(0)
self.pending_stat_button.clicked.connect(lambda: self._set_status_from_stat(1))
date_row.addWidget(self.pending_stat_button)
date_row.addWidget(self.pending_stat_button, 1)
self.completed_stat_button = QPushButton("已完成 0")
self.completed_stat_button.setProperty("appointmentStat", True)
self.completed_stat_button.setProperty("appointmentStatKind", "success")
self.completed_stat_button.setFixedWidth(104)
self.completed_stat_button.setMinimumWidth(0)
self.completed_stat_button.clicked.connect(lambda: self._set_status_from_stat(3))
date_row.addWidget(self.completed_stat_button)
date_row.addWidget(self.completed_stat_button, 1)
self.unassigned_stat_button = QPushButton("待分配医助 0")
self.unassigned_stat_button.setCheckable(True)
self.unassigned_stat_button.setProperty("appointmentStat", True)
self.unassigned_stat_button.setProperty("appointmentStatKind", "warning")
self.unassigned_stat_button.setFixedWidth(130)
self.unassigned_stat_button.setMinimumWidth(0)
self.unassigned_stat_button.clicked.connect(self._toggle_unassigned_filter)
date_row.addWidget(self.unassigned_stat_button)
date_row.addStretch(1)
date_row.addWidget(self.unassigned_stat_button, 1)
self.patient_input = QLineEdit()
self.patient_input.setObjectName("AppointmentPatientSearch")
self.patient_input.setPlaceholderText("患者姓名 / 手机号")
self.patient_input.setClearButtonEnabled(True)
self.patient_input.setFixedWidth(204)
self.patient_input.returnPressed.connect(self._search)
date_row.addWidget(self.patient_input)
date_row.addWidget(self.patient_input, 2)
search = QPushButton("查询")
search.setProperty("variant", "primary")
search.setFixedWidth(62)
search.clicked.connect(self._search)
date_row.addWidget(search)
layout.addLayout(date_row)
filter_row = QHBoxLayout()
filter_row.setSpacing(7)
status_label = QLabel("挂号状态:")
status_label.setObjectName("FilterRowLabel")
filter_row.addWidget(status_label)
filter_row.setSpacing(5)
self.status_filter_label = QLabel("挂号状态:")
self.status_filter_label.setObjectName("FilterRowLabel")
filter_row.addWidget(self.status_filter_label)
self.tab_bar = QTabBar()
self.tab_bar.setObjectName("AppointmentStatusTabs")
self._tab_indexes: dict[str | int, int] = {}
@@ -753,13 +808,15 @@ class AppointmentsPage(QWidget):
self.tab_bar.currentChanged.connect(self._tab_changed)
filter_row.addWidget(self.tab_bar)
self.filter_dividers: list[QLabel] = []
divider = QLabel("│")
divider.setObjectName("FilterDivider")
self.filter_dividers.append(divider)
filter_row.addWidget(divider)
confirm_label = QLabel("确认状态:")
confirm_label.setObjectName("FilterRowLabel")
filter_row.addWidget(confirm_label)
self.confirm_filter_label = QLabel("确认状态:")
self.confirm_filter_label.setObjectName("FilterRowLabel")
filter_row.addWidget(self.confirm_filter_label)
self.confirmed_filter = QComboBox(frame)
self.confirmed_filter.addItem("全部", "")
self.confirmed_filter.addItem("已确认", "1")
@@ -783,6 +840,7 @@ class AppointmentsPage(QWidget):
divider = QLabel("│")
divider.setObjectName("FilterDivider")
self.filter_dividers.append(divider)
filter_row.addWidget(divider)
self.more_filters_button = QPushButton("更多筛选")
@@ -825,26 +883,32 @@ class AppointmentsPage(QWidget):
def _build_content(self) -> QWidget:
host = QWidget()
host.setMinimumHeight(0)
layout = QHBoxLayout(host)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(4)
layout.addWidget(self._build_table_card(), 3)
layout.addWidget(self._build_video_panel(), 1)
self.content_layout = layout
self.table_card = self._build_table_card()
self.video_panel = self._build_video_panel()
layout.addWidget(self.table_card, 3)
layout.addWidget(self.video_panel, 1)
return host
def _build_video_panel(self) -> QWidget:
panel = QFrame()
panel.setObjectName("VideoConsultPanel")
panel.setFixedWidth(420)
panel.setMinimumWidth(0)
panel.setMaximumWidth(420)
layout = QVBoxLayout(panel)
layout.setContentsMargins(18, 17, 18, 10)
layout.setSpacing(10)
layout.setContentsMargins(14, 12, 14, 8)
layout.setSpacing(8)
title = QLabel("视频问诊")
title.setObjectName("VideoConsultTitle")
layout.addWidget(title)
self.video_list = QListWidget()
self.video_list.setObjectName("VideoConsultList")
self.video_list.setSpacing(0)
self.video_list.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.video_list.currentRowChanged.connect(self._video_row_selected)
self.video_list.itemDoubleClicked.connect(lambda _item: self._open_detail())
layout.addWidget(self.video_list, 1)
@@ -857,9 +921,10 @@ class AppointmentsPage(QWidget):
def _build_table_card(self) -> QWidget:
card = QFrame()
card.setObjectName("AppointmentMainCard")
card.setMinimumHeight(0)
layout = QVBoxLayout(card)
layout.setContentsMargins(4, 18, 10, 10)
layout.setSpacing(14)
layout.setContentsMargins(4, 8, 8, 8)
layout.setSpacing(8)
compatibility_host = QWidget(card)
compatibility_host.hide()
@@ -904,7 +969,7 @@ class AppointmentsPage(QWidget):
widget.setProperty("compactAction", True)
actions = QHBoxLayout()
actions.setSpacing(9)
actions.setSpacing(6)
self.toolbar_edit_button = QPushButton("编辑患者", card)
self.toolbar_edit_button.setProperty("variant", "primary")
self.toolbar_edit_button.setProperty("compactAction", True)
@@ -923,6 +988,13 @@ class AppointmentsPage(QWidget):
self.toolbar_qr_button.setEnabled(False)
self.toolbar_qr_button.clicked.connect(self._request_video_qr)
actions.addWidget(self.toolbar_qr_button)
self.toolbar_ai_consult_button = QPushButton("AI 分析", card)
self.toolbar_ai_consult_button.setProperty("variant", "secondary")
self.toolbar_ai_consult_button.setProperty("compactAction", True)
self.toolbar_ai_consult_button.setVisible(can_open_ai_consult(self.permissions))
self.toolbar_ai_consult_button.setEnabled(False)
self.toolbar_ai_consult_button.clicked.connect(self._open_ai_consult)
actions.addWidget(self.toolbar_ai_consult_button)
self.toolbar_cancel_button = QPushButton("取消挂号", card)
self.toolbar_cancel_button.setProperty("variant", "danger")
self.toolbar_cancel_button.setProperty("compactAction", True)
@@ -932,9 +1004,16 @@ class AppointmentsPage(QWidget):
self.toolbar_cancel_button.setEnabled(False)
self.toolbar_cancel_button.clicked.connect(self._cancel_selected)
actions.addWidget(self.toolbar_cancel_button)
self.video_panel_button = QPushButton("视频问诊", card)
self.video_panel_button.setCheckable(True)
self.video_panel_button.setProperty("variant", "ghost")
self.video_panel_button.setProperty("compactAction", True)
self.video_panel_button.clicked.connect(self._toggle_video_panel)
actions.addWidget(self.video_panel_button)
actions.addStretch(1)
refresh = QPushButton("刷新", card)
refresh.setProperty("variant", "ghost")
refresh.setProperty("compactAction", True)
refresh.clicked.connect(lambda: self.refresh())
actions.addWidget(refresh)
layout.addLayout(actions)
@@ -958,17 +1037,56 @@ class AppointmentsPage(QWidget):
]
)
self.table.setObjectName("AppointmentTable")
self.table.setMinimumHeight(0)
self.table.setWordWrap(True)
self.table.horizontalHeader().setFixedHeight(38)
self.table.verticalHeader().setDefaultSectionSize(66)
self.table.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.table.horizontalHeader().setFixedHeight(34)
self.table.verticalHeader().setDefaultSectionSize(60)
self.table.itemSelectionChanged.connect(self._selection_changed)
self.table.itemDoubleClicked.connect(lambda _item: self._open_detail())
layout.addWidget(self.table, 1)
self.pager = Pager(self._page_size)
self.pager.setMaximumHeight(38)
self.pager.page_changed.connect(self._page_changed)
layout.addWidget(self.pager)
return card
def _toggle_video_panel(self, checked: bool) -> None:
self._video_panel_user_open = checked
self._apply_responsive_layout()
def _apply_responsive_layout(self) -> None:
"""Keep the table usable while retaining a video-list entry at low widths."""
panel = getattr(self, "video_panel", None)
button = getattr(self, "video_panel_button", None)
if not isinstance(panel, QWidget) or not isinstance(button, QPushButton):
return
narrow = self.width() < 1120
for preset in ("yesterday", "day_before", "tomorrow", "day_after"):
self.date_buttons[preset].setVisible(not narrow)
self.date_overflow_button.setVisible(narrow)
self.status_filter_label.setVisible(not narrow)
self.confirm_filter_label.setVisible(not narrow)
if narrow:
if self._responsive_narrow is not True:
self._video_panel_user_open = False
button.show()
button.setChecked(self._video_panel_user_open)
panel.setVisible(self._video_panel_user_open)
if self._video_panel_user_open:
panel.setFixedWidth(max(250, min(300, round(self.width() * 0.32))))
else:
button.hide()
button.setChecked(False)
panel.show()
panel.setFixedWidth(max(300, min(420, round(self.width() * 0.30))))
self._responsive_narrow = narrow
def resizeEvent(self, event: Any) -> None:
super().resizeEvent(event)
self._apply_responsive_layout()
def _sync_video_list(self, rows: Sequence[Any]) -> None:
selected_id = (
_appointment_id(self.video_list.currentItem().data(Qt.ItemDataRole.UserRole))
@@ -1002,6 +1120,7 @@ class AppointmentsPage(QWidget):
self.video_list.setCurrentRow(row_to_select)
else:
self.video_list.setCurrentRow(-1)
self.video_panel_button.setText(f"视频问诊 {len(rows)}")
def _build_video_patient_card(self, row: Any) -> QWidget:
card = QFrame()
@@ -1066,6 +1185,8 @@ class AppointmentsPage(QWidget):
)
add_action("查看", self._open_detail)
add_action("诊单", self._edit_patient, "tcm.diagnosis/edit")
if can_open_ai_consult(self.permissions):
add_action("AI 分析", self._open_ai_consult)
add_action("开方", self._open_prescription, "tcm.diagnosis/kaifang")
add_action("二维码", self._request_video_qr, "tcm.diagnosis/videoQr", "success")
more = QPushButton("更多", card)
@@ -1109,6 +1230,8 @@ class AppointmentsPage(QWidget):
add_action("完成问诊", self._complete_selected, "doctor.appointment/complete")
add_action("查看病历", self._view_case, "tcm.diagnosis/kaifang")
if can_open_ai_consult(self.permissions):
add_action("AI 分析", self._open_ai_consult)
if can_open_diagnosis_ai_report(self.permissions):
add_action("AI 报告", self._open_ai_report)
add_action("取消挂号", self._cancel_selected, "doctor.appointment/cancel")
@@ -1193,6 +1316,13 @@ class AppointmentsPage(QWidget):
self._date_preset = preset
for key, button in self.date_buttons.items():
button.setChecked(key == preset)
overflow_labels = {
"yesterday": "昨天",
"day_before": "前天",
"tomorrow": "明天",
"day_after": "后天",
}
self.date_overflow_button.setText(overflow_labels.get(preset, "日期"))
target = _offset_date(preset)
if target is None:
self._start_date = ""
@@ -1248,6 +1378,7 @@ class AppointmentsPage(QWidget):
for button in self.date_buttons.values():
button.setChecked(False)
self.date_buttons[""].setChecked(True)
self.date_overflow_button.setText("自定义")
self._start_date = start_date
self._end_date = end_date
self._page = 1
@@ -1332,6 +1463,16 @@ class AppointmentsPage(QWidget):
rows = page_items(result)
total = page_total(result)
extend = get_value(result, "extend", {}) or {}
signature = (
self._page,
_stable_signature_value(self._query_filters()),
*_appointment_result_signature(rows, total, extend),
)
if signature == self._last_render_signature:
if not silent:
self.banner.clear()
return
self._last_render_signature = signature
status_count = get_value(extend, "status_count", {}) or {}
if isinstance(status_count, Mapping):
for key in (1, 2, 3, 4):
@@ -1507,7 +1648,7 @@ class AppointmentsPage(QWidget):
default=1,
)
height = line_count * line_height + TABLE_CELL_VERTICAL_PADDING
self.table.setRowHeight(row_index, max(66, height))
self.table.setRowHeight(row_index, max(60, min(66, height)))
def _load_error(self, error: Exception, generation: int, silent: bool) -> None:
if generation != self._generation:
@@ -1551,6 +1692,9 @@ class AppointmentsPage(QWidget):
self.prescription_button.setText(prescription_action_label(row) if has_row else "开方")
self.case_button.setEnabled(has_row and _appointment_id(row) > 0)
self.ai_button.setEnabled(has_row and _diagnosis_id(row) > 0)
self.toolbar_ai_consult_button.setEnabled(
has_row and can_open_ai_consult(self.permissions) and _diagnosis_id(row) > 0
)
self.cancel_button.setEnabled(has_row and status == 1)
self.toolbar_edit_button.setEnabled(has_row and _diagnosis_id(row) > 0)
self.toolbar_qr_button.setEnabled(
@@ -1592,6 +1736,19 @@ class AppointmentsPage(QWidget):
)
present_diagnosis_ai_report(self.repository, self.permissions, self, payload)
def _open_ai_consult(self) -> None:
row = self._current_row()
diagnosis_id = _diagnosis_id(row)
present_ai_consult(
self.repository,
self.permissions,
self,
diagnosis_id=diagnosis_id,
patient_id=_video_patient_id(row),
seed=row,
source_title="问诊列表",
)
def _request_video(self) -> None:
if not _canonical_allowed(
self.permissions, "doctor.appointment/prescription", default=False
@@ -1640,7 +1797,7 @@ class AppointmentsPage(QWidget):
if row is None or _status_value(row) == 3:
return
diagnosis_id = _diagnosis_id(row)
patient_id = _video_patient_id(row) or _as_int(first_value(row, "patient_id", default=0))
patient_id = _video_patient_id(row)
doctor_id = _as_int(first_value(row, "doctor_id", default=0))
share_user_id = _as_int(first_value(self.current_user, "id", default=0))
if diagnosis_id <= 0 or patient_id <= 0 or doctor_id <= 0 or share_user_id <= 0: