diff --git a/admin/src/views/consumer/prescription/index.vue b/admin/src/views/consumer/prescription/index.vue index 3bb92825..e2ac0db7 100644 --- a/admin/src/views/consumer/prescription/index.vue +++ b/admin/src/views/consumer/prescription/index.vue @@ -1304,7 +1304,7 @@
- + 甘草药房 @@ -1715,6 +1715,7 @@ import { roleAll } from '@/api/perms/role' import { usePaging } from '@/hooks/usePaging' import useUserStore from '@/stores/modules/user' import feedback from '@/utils/feedback' +import { hasPermission } from '@/utils/perm' import type { FormInstance, FormRules } from 'element-plus' import DaterangePicker from '@/components/daterange-picker/index.vue' import MedicineNameSelect from '@/components/medicine-name-select/index.vue' @@ -2037,6 +2038,10 @@ const createOrderForm = reactive({ pay_order_ids: [] as number[] }) +// 只有具备「设置发货类型」权限的账号才可在创建业务订单时选择洛阳药房; +// 无权限账号保持历史默认逻辑,固定走甘草药房。 +const canSelectShipMode = computed(() => hasPermission(['tcm.prescriptionOrder/setShipMode'])) + // 省市区数据(简化版,实际项目中应该从 API 获取或使用完整的省市区数据) const regionOptions = ref([]) diff --git a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php index 1e1129e1..72ed42a6 100755 --- a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php +++ b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php @@ -343,6 +343,21 @@ class PrescriptionOrderLogic return false; } + /** + * 是否允许在业务订单中选择发货药房(甘草 / 洛阳)。 + * 无此权限时沿用历史逻辑,订单固定走甘草药房。 + */ + public static function canSelectShipMode(array $adminInfo): bool + { + if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) { + return true; + } + + $permissions = AuthLogic::getAuthByAdminId((int) ($adminInfo['admin_id'] ?? 0)); + + return in_array('tcm.prescriptionOrder/setShipMode', $permissions, true); + } + /** * @param PrescriptionOrder $row */ @@ -1022,7 +1037,9 @@ class PrescriptionOrderLogic $order->service_package = (string) ($params['service_package'] ?? ''); $order->tracking_number = (string) ($params['tracking_number'] ?? ''); $order->express_company = self::normalizeExpressCompany($params['express_company'] ?? 'auto'); - $order->ship_mode = self::normalizeShipMode((string) ($params['ship_mode'] ?? 'gancao')); + $order->ship_mode = self::canSelectShipMode($adminInfo) + ? self::normalizeShipMode((string) ($params['ship_mode'] ?? 'gancao')) + : 'gancao'; $order->fee_type = (int) $params['fee_type']; $order->amount = round((float) $params['amount'], 2); $order->internal_cost = $internalCost; @@ -5132,6 +5149,11 @@ class PrescriptionOrderLogic return false; } + if (!self::canSelectShipMode($adminInfo)) { + self::$error = '无权限设置发货药房'; + + return false; + } if (!self::assertRemoteSnapshotMutable($order)) { return false; } diff --git a/server/app/api/controller/EjPharmacyCallbackController.php b/server/app/api/controller/EjPharmacyCallbackController.php index 110ebcd0..90200b4d 100644 --- a/server/app/api/controller/EjPharmacyCallbackController.php +++ b/server/app/api/controller/EjPharmacyCallbackController.php @@ -244,7 +244,7 @@ class EjPharmacyCallbackController extends BaseApiController // Keep the same readable production-flow format as Gancao: // flow name and pharmacy are the primary information. $summaryParts = [ - '洛阳药房回传:' . (EjPharmacyShipmentPolicy::isCompletedEvent($payload) ? '订单已完成' : '订单药房流转制作中'), + '洛阳药房回传:订单药房流转制作中', '流程:' . $stepName, '药房:洛阳药房', ]; diff --git a/server/app/common/service/pharmacy/EjPharmacyShipmentPolicy.php b/server/app/common/service/pharmacy/EjPharmacyShipmentPolicy.php index 1211f5fb..1ae1ce5c 100644 --- a/server/app/common/service/pharmacy/EjPharmacyShipmentPolicy.php +++ b/server/app/common/service/pharmacy/EjPharmacyShipmentPolicy.php @@ -13,12 +13,19 @@ final class EjPharmacyShipmentPolicy || strtoupper(trim((string) ($payload['status'] ?? ''))) === 'SHIPPED'; } + /** @param array $payload */ + public static function isWorkflowStepEvent(array $payload): bool + { + return strtoupper(trim((string) ($payload['event_type'] ?? ''))) === 'WORKFLOW_STEP_COMPLETED'; + } + /** @param array $payload */ public static function isCompletedEvent(array $payload): bool { - return strtoupper(trim((string) ($payload['status'] ?? ''))) === 'COMPLETED' - || (strtoupper(trim((string) ($payload['event_type'] ?? ''))) === 'WORKFLOW_STEP_COMPLETED' - && (bool) ($payload['final'] ?? false)); + // A workflow node (including the final “ship” node) is only a + // pharmacy-process update. It must not close the ZYT business order. + return !self::isWorkflowStepEvent($payload) + && strtoupper(trim((string) ($payload['status'] ?? ''))) === 'COMPLETED'; } /** @param array $payload */ @@ -47,6 +54,12 @@ final class EjPharmacyShipmentPolicy ? $rollbackStatus : ($currentStatus === 9 ? 2 : $currentStatus); } + if (self::isWorkflowStepEvent($payload)) { + // EJ workflow callbacks are informational nodes. Keep the ZYT + // fulfillment status unchanged; only explicit shipment/completion + // events may advance it. + return $currentStatus; + } if (self::isShippedEvent($payload) && in_array($currentStatus, [1, 2], true)) { return 5; } diff --git a/server/tests/pharmacy/callback_auth_integration.php b/server/tests/pharmacy/callback_auth_integration.php index 2fb5288b..163cf3b1 100644 --- a/server/tests/pharmacy/callback_auth_integration.php +++ b/server/tests/pharmacy/callback_auth_integration.php @@ -235,17 +235,17 @@ $assertSame( 'non-shipment callbacks must not change local fulfillment' ); $assertSame( - true, + false, EjPharmacyShipmentPolicy::isCompletedEvent(['event_type' => 'WORKFLOW_STEP_COMPLETED', 'status' => 'COMPLETED', 'final' => true]), - 'a final EJ workflow callback must be recognized as order completion' + 'a final EJ workflow callback must remain a process update, not order completion' ); $assertSame( - 3, + 2, EjPharmacyShipmentPolicy::nextFulfillmentStatus( 2, ['event_type' => 'WORKFLOW_STEP_COMPLETED', 'status' => 'COMPLETED', 'final' => true] ), - 'a final EJ workflow callback must advance ZYT fulfillment to completed' + 'a final EJ workflow callback must not change ZYT fulfillment' ); $assertSame( 2, @@ -424,7 +424,6 @@ $assertSame( $assertSame( true, str_contains($controllerSource, '订单药房流转制作中') - && str_contains($controllerSource, '订单已完成') && str_contains($controllerSource, '流程:') && str_contains($controllerSource, '药房:洛阳药房') && str_contains($controllerSource, "implode(\$isWorkflowStep ? ' | ' : ';', \$summaryParts)"),