diff --git a/admin/src/views/consumer/prescription/order_list.vue b/admin/src/views/consumer/prescription/order_list.vue index 843115d2..122ce60b 100644 --- a/admin/src/views/consumer/prescription/order_list.vue +++ b/admin/src/views/consumer/prescription/order_list.vue @@ -252,7 +252,7 @@ v-model="queryParams.assistant_id" clearable filterable - placeholder="诊单医助" + placeholder="订单创建人" class="!w-full" > /** 与 server/config/project.php prescription_order_finance_roles 保持一致 */ const FINANCE_ROLE_IDS = [0, 3, 6] +/** 与 server/config/project.php order_edit_all_roles 一致:可绕过「双审通过后创建人禁编」 */ +const ORDER_EDIT_ALL_ROLE_IDS = [0, 3] /** 判断当前用户是否有查看财务字段(内部成本)的权限 */ const canViewFinanceFields = () => { @@ -2887,9 +2889,9 @@ const queryParams = reactive({ express_company: '' as '' | 'sf' | 'jd', /** 开方医生(后台:关联处方 creator_id) */ doctor_id: '' as number | '', - /** 诊单医助(后台:关联诊单 assistant_id) */ + /** 订单创建人(后台:业务订单 creator_id) */ assistant_id: '' as number | '', - /** 诊单医助所属部门(后台:assistant_dept_id,la_admin_dept) */ + /** 创建人所属部门(后台:assistant_dept_id,la_admin_dept) */ assistant_dept_id: '' as number | '', fulfillment_status: '' as number | '', prescription_audit_status: '' as number | '', @@ -3336,8 +3338,32 @@ function formatTime(v: unknown) { return String(v) } +function canBypassCreatorDualAuditEditLock(): boolean { + const u = userStore.userInfo + if (!u) return false + if (Number(u.root) === 1) return true + const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : [] + return ids.some((id) => ORDER_EDIT_ALL_ROLE_IDS.includes(id)) +} + +function isCurrentUserOrderCreator(row: { creator_id?: number }) { + const uid = Number(userStore.userInfo?.id) + return uid > 0 && Number(row.creator_id) === uid +} + +/** 处方审核 + 支付单审核均已通过 */ +function isDualAuditPassed(row: { + prescription_audit_status?: number + payment_slip_audit_status?: number +}) { + return Number(row.prescription_audit_status) === 1 && Number(row.payment_slip_audit_status) === 1 +} + function canEditRow(row: { + creator_id?: number fulfillment_status?: number + prescription_audit_status?: number + payment_slip_audit_status?: number gancao_reciperl_order_no?: string | null gancao_submit_time?: number | null }) { @@ -3345,6 +3371,15 @@ function canEditRow(row: { // 已完成(3)、已取消(4) 不可编辑 if (fs === 3 || fs === 4) return false + // 双审均已通过:仅订单创建人不可再编辑(超管/全量编辑角色仍可改) + if ( + isCurrentUserOrderCreator(row) && + isDualAuditPassed(row) && + !canBypassCreatorDualAuditEditLock() + ) { + return false + } + const gcNo = String(row.gancao_reciperl_order_no || '').trim() const gcTime = Number(row.gancao_submit_time || 0) const gcLocked = gcNo !== '' || gcTime > 0 @@ -4337,7 +4372,25 @@ async function goEditOrderNextStep() { } } -async function openEdit(row: { id: number }) { +async function openEdit(row: { + id: number + creator_id?: number + prescription_audit_status?: number + payment_slip_audit_status?: number + fulfillment_status?: number + gancao_reciperl_order_no?: string | null + gancao_submit_time?: number | null +}) { + if (!canEditRow(row)) { + if ( + isCurrentUserOrderCreator(row) && + isDualAuditPassed(row) && + !canBypassCreatorDualAuditEditLock() + ) { + feedback.msgWarning('处方与支付单均已审核通过,创建人不可再编辑') + } + return + } editOrderStep.value = 0 editOrderPrescription.value = null editVisible.value = true diff --git a/admin/src/views/consumer/prescription/order_list_h5.vue b/admin/src/views/consumer/prescription/order_list_h5.vue index f071c6de..c9e675c1 100644 --- a/admin/src/views/consumer/prescription/order_list_h5.vue +++ b/admin/src/views/consumer/prescription/order_list_h5.vue @@ -3047,14 +3047,48 @@ function formatTime(v: unknown) { return String(v) } +/** 与 server/config/project.php order_edit_all_roles 一致 */ +const ORDER_EDIT_ALL_ROLE_IDS = [0, 3] + +function canBypassCreatorDualAuditEditLock(): boolean { + const u = userStore.userInfo + if (!u) return false + if (Number(u.root) === 1) return true + const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : [] + return ids.some((id) => ORDER_EDIT_ALL_ROLE_IDS.includes(id)) +} + +function isCurrentUserOrderCreator(row: { creator_id?: number }) { + const uid = Number(userStore.userInfo?.id) + return uid > 0 && Number(row.creator_id) === uid +} + +function isDualAuditPassed(row: { + prescription_audit_status?: number + payment_slip_audit_status?: number +}) { + return Number(row.prescription_audit_status) === 1 && Number(row.payment_slip_audit_status) === 1 +} + function canEditRow(row: { + creator_id?: number fulfillment_status?: number + prescription_audit_status?: number + payment_slip_audit_status?: number gancao_reciperl_order_no?: string | null gancao_submit_time?: number | null }) { const fs = Number(row.fulfillment_status) if (fs === 3 || fs === 4) return false + if ( + isCurrentUserOrderCreator(row) && + isDualAuditPassed(row) && + !canBypassCreatorDualAuditEditLock() + ) { + return false + } + const gcNo = String(row.gancao_reciperl_order_no || '').trim() const gcTime = Number(row.gancao_submit_time || 0) const gcLocked = gcNo !== '' || gcTime > 0 @@ -3889,7 +3923,25 @@ async function goEditOrderNextStep() { } } -async function openEdit(row: { id: number }) { +async function openEdit(row: { + id: number + creator_id?: number + prescription_audit_status?: number + payment_slip_audit_status?: number + fulfillment_status?: number + gancao_reciperl_order_no?: string | null + gancao_submit_time?: number | null +}) { + if (!canEditRow(row)) { + if ( + isCurrentUserOrderCreator(row) && + isDualAuditPassed(row) && + !canBypassCreatorDualAuditEditLock() + ) { + feedback.msgWarning('处方与支付单均已审核通过,创建人不可再编辑') + } + return + } editOrderStep.value = 0 editOrderPrescription.value = null editVisible.value = true diff --git a/server/app/adminapi/lists/tcm/PrescriptionOrderLists.php b/server/app/adminapi/lists/tcm/PrescriptionOrderLists.php index 8acbadbe..96949497 100755 --- a/server/app/adminapi/lists/tcm/PrescriptionOrderLists.php +++ b/server/app/adminapi/lists/tcm/PrescriptionOrderLists.php @@ -243,9 +243,8 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn } /** - * 非「业务订单全量」角色:默认仅本人创建;若持有 viewOrdersForOwnPrescription 则包含关联处方开方人为本人的订单; - * 另含「关联诊单医助为本人」以便医助跟进出单与履约。 - * 显式筛选 assistant_id 时:若该医助落在当前账号数据域内,则允许按「诊单医助 / 订单创建人」命中(与 applyDoctorAssistantFilters 一致)。 + * 非「业务订单全量」角色:默认仅本人创建;若持有 viewOrdersForOwnPrescription 则额外包含关联处方开方人为本人的订单。 + * 显式筛选 assistant_id 时:若该医助落在当前账号数据域内,则允许按订单创建人命中(与 applyDoctorAssistantFilters 一致)。 */ private function applyCreatorOrOwnPrescriptionVisibility($query): void { @@ -255,7 +254,6 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn $adminId = (int) $this->adminId; $poTbl = (new PrescriptionOrder())->getTable(); $rxTbl = (new Prescription())->getTable(); - $diagTbl = (new Diagnosis())->getTable(); $explicitAssistant = (int) ($this->params['assistant_id'] ?? 0); $allowExplicitAssistantVisibility = false; @@ -273,69 +271,33 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn $query->where(function ($q) use ( $poTbl, $rxTbl, - $diagTbl, $adminId, $explicitAssistant, $allowExplicitAssistantVisibility ) { if (PrescriptionOrderLogic::canViewOrdersForOwnPrescription($this->adminInfo)) { - $q->where(function ($qq) use ($poTbl, $rxTbl, $diagTbl, $adminId) { + $q->where(function ($qq) use ($poTbl, $rxTbl, $adminId) { $qq->where('creator_id', $adminId); $qq->whereOrRaw( "EXISTS (SELECT 1 FROM `{$rxTbl}` rx WHERE rx.`id` = `{$poTbl}`.`prescription_id`" . " AND rx.`delete_time` IS NULL AND rx.`creator_id` = {$adminId})" ); - $qq->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$adminId})" - ); }); } else { - $q->where(function ($qq) use ($poTbl, $diagTbl, $adminId) { - $qq->where('creator_id', $adminId); - $qq->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$adminId})" - ); - }); + $q->where('creator_id', $adminId); } if ($allowExplicitAssistantVisibility) { $q->whereOr('creator_id', $explicitAssistant); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$explicitAssistant})" - ); } }); } /** - * 数据隔离:创建者 ∈ 可见 admin,或 关联诊单医助 ∈ 可见 admin + * 数据隔离:订单创建人 ∈ 可见 admin */ private function applyDataScopeForPrescriptionOrder($query): void { - if (!$this->dataScopeShouldApply()) { - return; - } - $ids = $this->getDataScopeVisibleAdminIds(); - if ($ids === null) { - return; - } - if ($ids === []) { - $query->whereRaw('0 = 1'); - - return; - } - $poTbl = (new PrescriptionOrder())->getTable(); - $diagTbl = (new Diagnosis())->getTable(); - $inList = implode(',', $ids); - $query->where(function ($q) use ($poTbl, $diagTbl, $inList) { - $q->whereIn('creator_id', explode(',', $inList)); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` " - . "AND dg.`delete_time` IS NULL AND dg.`assistant_id` IN ({$inList}))" - ); - }); + $this->applyDataScopeByOwner($query, 'creator_id'); } /** @@ -362,16 +324,11 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn } /** - * 医助:仅「诊单医助=本人」的订单计业绩 + * 医助角色统计:仅「订单创建人=本人」计入 */ - private function applyAssistantDiagnosisOnlyFilter($query): void + private function applyCreatorOnlyFilter($query): void { - $poTbl = (new PrescriptionOrder())->getTable(); - $diagTbl = (new Diagnosis())->getTable(); - $aid = (int) $this->adminId; - $query->whereExists( - "SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$aid}" - ); + $query->where('creator_id', (int) $this->adminId); } /** @@ -390,7 +347,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn $assistantRid = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2); $myRoles = array_map('intval', $this->adminInfo['role_id'] ?? []); if ($assistantRid > 0 && in_array($assistantRid, $myRoles, true)) { - $this->applyAssistantDiagnosisOnlyFilter($query); + $this->applyCreatorOnlyFilter($query); $this->applyDataScopeForPrescriptionOrder($query); } else { if (!PrescriptionOrderLogic::canSeeAllPrescriptionOrders($this->adminInfo)) { @@ -959,9 +916,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn } /** - * 按开方医生(关联处方 creator_id)/ 诊单医助筛选 - * - * 医助:诊单 assistant_id 与业务订单 creator_id 并举——医助常代建单但诊单未写 assistant_id。 + * 按开方医生(关联处方 creator_id)/ 医助(订单 creator_id)筛选 */ private function applyDoctorAssistantFilters($query): void { @@ -973,23 +928,8 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn $query->whereExists("SELECT 1 FROM {$rxTbl} rx WHERE rx.id = {$poTbl}.prescription_id AND rx.delete_time IS NULL AND rx.creator_id = {$doctorId}"); } - /** 业绩看板入口(yeji_order_drawer=1)所有筛选统一按订单创建人;其它入口(处方订单列表 / 患者跟进等)保留旧口径(创建人 ∪ 诊单医助) */ - $yejiPanel = (int) ($this->params['yeji_order_drawer'] ?? 0) === 1; - if (isset($this->params['assistant_id']) && (int) $this->params['assistant_id'] > 0) { - $assistantId = (int) $this->params['assistant_id']; - if ($yejiPanel) { - $query->where('creator_id', $assistantId); - } else { - $diagTbl = (new Diagnosis())->getTable(); - $query->where(function ($q) use ($poTbl, $diagTbl, $assistantId) { - $q->where('creator_id', $assistantId); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$assistantId})" - ); - }); - } + $query->where('creator_id', (int) $this->params['assistant_id']); } if (isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) { @@ -1039,24 +979,10 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn } $inList = implode(',', $deptIds); $adTbl = (new AdminDept())->getTable(); - if ($yejiPanel) { - /** 业绩看板部门侧栏:仅订单创建人人事部门 ∈ 子树(与「合计业绩」/「接诊诊单」/「复诊」聚合同口径) */ - $query->whereExists( - "SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = `{$poTbl}`.`creator_id` AND adc.`dept_id` IN ({$inList})" - ); - } else { - $diagTbl = (new Diagnosis())->getTable(); - $query->where(function ($q) use ($poTbl, $diagTbl, $adTbl, $inList) { - $q->whereExists( - "SELECT 1 FROM `{$adTbl}` ad INNER JOIN `{$diagTbl}` dg ON ad.admin_id = dg.assistant_id AND dg.delete_time IS NULL " - . "WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` AND ad.dept_id IN ({$inList})" - ); - $q->whereExists( - "SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = `{$poTbl}`.`creator_id` AND adc.`dept_id` IN ({$inList})", - 'OR' - ); - }); - } + /** 按订单创建人人事部门 ∈ 子树(与业绩看板「合计业绩」/「接诊诊单」同口径) */ + $query->whereExists( + "SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = `{$poTbl}`.`creator_id` AND adc.`dept_id` IN ({$inList})" + ); } } diff --git a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php index 59c34e3f..67e796c3 100755 --- a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php +++ b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php @@ -141,7 +141,7 @@ class PrescriptionOrderLogic } /** - * 与 PrescriptionOrderLists 数据域一致:创建人或关联诊单医助属于当前账号可见 admin 集合 + * 与 PrescriptionOrderLists 数据域一致:订单创建人属于当前账号可见 admin 集合 */ private static function orderWithinDataScopeOwners(PrescriptionOrder $row, int $adminId, array $adminInfo): bool { @@ -152,16 +152,8 @@ class PrescriptionOrderLogic if ($ids === null || $ids === []) { return false; } - if (in_array((int) $row->creator_id, $ids, true)) { - return true; - } - $did = (int) $row->diagnosis_id; - if ($did <= 0) { - return false; - } - $da = (int) Diagnosis::where('id', $did)->whereNull('delete_time')->value('assistant_id'); - return $da > 0 && in_array($da, $ids, true); + return in_array((int) $row->creator_id, $ids, true); } /** @@ -175,13 +167,6 @@ class PrescriptionOrderLogic if ((int) $row->creator_id === $adminId) { return true; } - $did = (int) $row->diagnosis_id; - if ($did > 0) { - $da = (int) Diagnosis::where('id', $did)->whereNull('delete_time')->value('assistant_id'); - if ($da === $adminId) { - return true; - } - } if (self::canViewOrdersForOwnPrescription($adminInfo)) { if (self::isPrescriptionOrderPrescriber((int) $row->prescription_id, $adminId)) { return true; @@ -288,7 +273,7 @@ class PrescriptionOrderLogic } /** - * 提成结算等与列表对齐:按开方医生 / 诊单医助 ∪ 订单创建人 筛选(与 PrescriptionOrderLists::applyDoctorAssistantFilters 在非业绩抽屉场景一致)。 + * 提成结算等与列表对齐:按开方医生 / 订单创建人 筛选。 * * @param array $params */ @@ -304,15 +289,7 @@ class PrescriptionOrderLogic } if (isset($params['assistant_id']) && (int) $params['assistant_id'] > 0) { - $assistantId = (int) $params['assistant_id']; - $diagTbl = (new Diagnosis())->getTable(); - $query->where(function ($q) use ($alias, $diagTbl, $assistantId): void { - $q->where("{$alias}.creator_id", $assistantId); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$assistantId})" - ); - }); + $query->where("{$alias}.creator_id", (int) $params['assistant_id']); } } @@ -341,7 +318,6 @@ class PrescriptionOrderLogic } $rxTbl = (new Prescription())->getTable(); - $diagTbl = (new Diagnosis())->getTable(); $explicitAssistant = (int) ($params['assistant_id'] ?? 0); $allowExplicitAssistantVisibility = false; @@ -359,39 +335,24 @@ class PrescriptionOrderLogic $query->where(function ($q) use ( $alias, $rxTbl, - $diagTbl, $viewerAdminId, $explicitAssistant, $allowExplicitAssistantVisibility, $viewerAdminInfo ): void { if (self::canViewOrdersForOwnPrescription($viewerAdminInfo)) { - $q->where(function ($qq) use ($alias, $rxTbl, $diagTbl, $viewerAdminId): void { + $q->where(function ($qq) use ($alias, $rxTbl, $viewerAdminId): void { $qq->where("{$alias}.creator_id", $viewerAdminId); $qq->whereOrRaw( "EXISTS (SELECT 1 FROM `{$rxTbl}` rx WHERE rx.`id` = `{$alias}`.`prescription_id`" . " AND rx.`delete_time` IS NULL AND rx.`creator_id` = {$viewerAdminId})" ); - $qq->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$viewerAdminId})" - ); }); } else { - $q->where(function ($qq) use ($alias, $diagTbl, $viewerAdminId): void { - $qq->where("{$alias}.creator_id", $viewerAdminId); - $qq->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$viewerAdminId})" - ); - }); + $q->where("{$alias}.creator_id", $viewerAdminId); } if ($allowExplicitAssistantVisibility) { $q->whereOr("{$alias}.creator_id", $explicitAssistant); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id`" - . " AND dg.`delete_time` IS NULL AND dg.`assistant_id` = {$explicitAssistant})" - ); } }); @@ -399,7 +360,7 @@ class PrescriptionOrderLogic } /** - * 列表数据域:创建人 ∈ 可见 或 诊单医助 ∈ 可见。 + * 列表数据域:订单创建人 ∈ 可见 admin。 */ private static function applyCommissionOrderDataScope( Query $query, @@ -419,15 +380,7 @@ class PrescriptionOrderLogic return; } - $diagTbl = (new Diagnosis())->getTable(); - $inList = implode(',', array_map('intval', $ids)); - $query->where(function ($q) use ($alias, $diagTbl, $inList): void { - $q->whereIn("{$alias}.creator_id", explode(',', $inList)); - $q->whereOrRaw( - "EXISTS (SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$alias}`.`diagnosis_id` " - . "AND dg.`delete_time` IS NULL AND dg.`assistant_id` IN ({$inList}))" - ); - }); + $query->whereIn("{$alias}.creator_id", $ids); } /** @@ -1437,6 +1390,16 @@ class PrescriptionOrderLogic return false; } + if ( + (int) $order->creator_id === $adminId + && (int) $order->prescription_audit_status === 1 + && (int) $order->payment_slip_audit_status === 1 + && !self::canSeeAllPrescriptionOrders($adminInfo) + ) { + self::$error = '处方与支付单均已审核通过,创建人不可再编辑'; + + return false; + } $fs = (int) $order->fulfillment_status; if ($fs === 3 || $fs === 4) { self::$error = '已完成或已取消的订单不可编辑';