From be15531ab3c5405c7f06b5c1f3d291279a5efb28 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Jun 2026 18:34:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumer/prescription/order_list.vue | 2 +- .../logic/tcm/PrescriptionOrderLogic.php | 214 +++++++++++------- 2 files changed, 130 insertions(+), 86 deletions(-) diff --git a/admin/src/views/consumer/prescription/order_list.vue b/admin/src/views/consumer/prescription/order_list.vue index c8c044b7..94a6cbaa 100644 --- a/admin/src/views/consumer/prescription/order_list.vue +++ b/admin/src/views/consumer/prescription/order_list.vue @@ -293,7 +293,7 @@ :fetch-fun="prescriptionOrderExport" :params="prescriptionOrderExportParams" :page-size="pager.size" - export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:优先取该单关联处方登记的挂号;无则诊单下同患者挂号取 id 最大的一条(与前台挂号选择的记录一致);业绩侧栏带渠道筛选导出时与同页列表高亮挂号同源。「服务套餐」按字典 server_order 解析展示;「签收日期」与详情/业绩看板提成口径一致:以快递主表 sign_time 为主,无效时回退至签收类轨迹时间或已签收状态下的最新轨迹时间。" + export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:优先取该单关联处方登记的挂号;无则诊单下同患者挂号取 id 最大的一条(与前台挂号选择的记录一致);业绩侧栏带渠道筛选导出时与同页列表高亮挂号同源。「服务套餐」按字典 server_order 解析展示。「签收日期」与详情/业绩看板同源:优先库表轨迹;库内无轨迹时导出会按 logisticsTrace 口径补查快递100 并落库(如 PO 单在详情已签收但库内为空)。" /> diff --git a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php index 9e297c5f..b408247c 100755 --- a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php +++ b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php @@ -19,6 +19,7 @@ use app\common\model\doctor\Appointment; use app\common\model\auth\Admin; use app\common\service\DataScope\DataScopeService; use app\common\service\ExpressTrackService; +use app\common\service\ExpressTrackingService; use app\common\service\gancao\GancaoScmRecipelService; use think\db\Query; use think\facade\Config; @@ -2672,6 +2673,94 @@ class PrescriptionOrderLogic return 0; } + /** + * 复诊导出「初诊医助」:同 diagnosis_id 下最早 is_follow_up=0 的业务单医助(非 prev_staff 医生名) + * + * @param array> $diagById + * @return array diagnosis_id => 医助姓名 + */ + private static function batchFirstVisitAssistantNameByDiagnosis(array $diagIds, array $diagById): array + { + if ($diagIds === []) { + return []; + } + + $firstRows = PrescriptionOrder::whereIn('diagnosis_id', $diagIds) + ->whereNull('delete_time') + ->where('is_follow_up', 0) + ->field(['id', 'diagnosis_id', 'prescription_id', 'creator_id', 'create_time']) + ->order('create_time', 'asc') + ->order('id', 'asc') + ->select() + ->toArray(); + + $firstByDiag = []; + $rxIds = []; + foreach ($firstRows as $row) { + $did = (int) ($row['diagnosis_id'] ?? 0); + if ($did <= 0 || isset($firstByDiag[$did])) { + continue; + } + $firstByDiag[$did] = $row; + $rxId = (int) ($row['prescription_id'] ?? 0); + if ($rxId > 0) { + $rxIds[$rxId] = true; + } + } + + $rxAssistantById = []; + if ($rxIds !== []) { + $rxAssistantById = Prescription::whereIn('id', array_keys($rxIds)) + ->whereNull('delete_time') + ->column('assistant_id', 'id'); + } + + $adminIds = []; + foreach ($firstByDiag as $did => $row) { + $diag = $diagById[$did] ?? []; + $rxId = (int) ($row['prescription_id'] ?? 0); + $rxAst = $rxId > 0 ? (int) ($rxAssistantById[$rxId] ?? 0) : 0; + $aid = self::resolveAssistantAdminIdForPrescriptionOrderRow( + $rxAst, + (int) ($diag['assistant_id'] ?? 0) + ); + if ($aid <= 0) { + $aid = (int) ($row['creator_id'] ?? 0); + } + if ($aid > 0) { + $adminIds[$aid] = true; + } + } + + $nameByAdminId = []; + if ($adminIds !== []) { + $admins = Admin::whereIn('id', array_keys($adminIds)) + ->field(['id', 'name']) + ->select() + ->toArray(); + foreach ($admins as $a) { + $nameByAdminId[(int) $a['id']] = trim((string) ($a['name'] ?? '')); + } + } + + $out = []; + foreach ($firstByDiag as $did => $row) { + $diag = $diagById[$did] ?? []; + $rxId = (int) ($row['prescription_id'] ?? 0); + $rxAst = $rxId > 0 ? (int) ($rxAssistantById[$rxId] ?? 0) : 0; + $aid = self::resolveAssistantAdminIdForPrescriptionOrderRow( + $rxAst, + (int) ($diag['assistant_id'] ?? 0) + ); + if ($aid <= 0) { + $aid = (int) ($row['creator_id'] ?? 0); + } + $out[$did] = $aid > 0 ? (string) ($nameByAdminId[$aid] ?? '') : ''; + } + + return $out; + } + /** * 导出用:管理员在 admin_dept 中的部门路径(多部门「;」、路径内「 / 」;业务订单导出取 creator_id) */ @@ -2997,94 +3086,48 @@ class PrescriptionOrderLogic } } - // 签收时间批量解析:与 ExpressTrackingService::effectiveSignUnixFromTrackingDetail 同口径, - // 取 GREATEST(sign_time, max 签收类轨迹 trace_time_stamp, 已签收主表时的最新轨迹 trace_time_stamp); - // 与详情/业绩看板提成「签收时间」语义一致,避免历史数据 sign_time=0 但实际已签收时被当作「未签收」。 - /** @var array $signTsByTracking trim 后的 tracking_number => Unix 秒 */ - $signTsByTracking = []; - $trackingNumbers = []; - foreach ($lists as $row) { - $tn = trim((string) ($row['tracking_number'] ?? '')); - if ($tn !== '') { - $trackingNumbers[$tn] = true; + // 签收时间:与详情 logisticsTrace / 业绩提成同源;库内无轨迹时导出阶段补查快递100 并落库。 + /** @var array $signTsByPoId order_id => Unix 秒 */ + $signTsByPoId = []; + if ($poIds !== []) { + $signItems = []; + foreach ($lists as $row) { + $oid = (int) ($row['id'] ?? 0); + if ($oid <= 0) { + continue; + } + $signItems[] = [ + 'order_id' => $oid, + 'tracking_number' => trim((string) ($row['tracking_number'] ?? '')), + ]; } - } - $trackingNumbers = array_keys($trackingNumbers); - if ($trackingNumbers !== []) { try { - $etRows = Db::name('express_tracking') - ->whereIn('tracking_number', $trackingNumbers) - ->whereNull('delete_time') - ->field(['id', 'tracking_number', 'sign_time', 'current_state', 'is_signed']) - ->select() - ->toArray(); - $etIdToNum = []; - $signedEtIds = []; - $allEtIds = []; - foreach ($etRows as $r) { - $tn = trim((string) ($r['tracking_number'] ?? '')); - $eid = (int) ($r['id'] ?? 0); - if ($tn === '' || $eid <= 0) { - continue; - } - $etIdToNum[$eid] = $tn; - $allEtIds[] = $eid; - $st = (int) ($r['sign_time'] ?? 0); - if ($st > 0) { - $signTsByTracking[$tn] = max($signTsByTracking[$tn] ?? 0, $st); - } - if ((string) ($r['current_state'] ?? '') === '3' || (int) ($r['is_signed'] ?? 0) === 1) { - $signedEtIds[$eid] = true; - } - } - if ($allEtIds !== []) { - $sigCond = "(`status_code` IN ('3','301','302','304') " - . "OR `status` LIKE '%签收%' " - . "OR `trace_context` LIKE '%签收%' " - . "OR `trace_context` LIKE '%妥投%' " - . "OR `trace_context` LIKE '%已送达%' " - . "OR `trace_context` LIKE '%本人签收%')"; - $sigTraces = Db::name('express_trace') - ->whereIn('tracking_id', $allEtIds) - ->where('trace_time_stamp', '>', 0) - ->whereRaw($sigCond) - ->fieldRaw('tracking_id, MAX(trace_time_stamp) AS max_ts') - ->group('tracking_id') - ->select() - ->toArray(); - foreach ($sigTraces as $t) { - $eid = (int) ($t['tracking_id'] ?? 0); - $tn = $etIdToNum[$eid] ?? ''; - $ts = (int) ($t['max_ts'] ?? 0); - if ($tn !== '' && $ts > 0) { - $signTsByTracking[$tn] = max($signTsByTracking[$tn] ?? 0, $ts); - } - } - $signedIdList = array_keys($signedEtIds); - if ($signedIdList !== []) { - $anyTraces = Db::name('express_trace') - ->whereIn('tracking_id', $signedIdList) - ->where('trace_time_stamp', '>', 0) - ->fieldRaw('tracking_id, MAX(trace_time_stamp) AS max_ts') - ->group('tracking_id') - ->select() - ->toArray(); - foreach ($anyTraces as $t) { - $eid = (int) ($t['tracking_id'] ?? 0); - $tn = $etIdToNum[$eid] ?? ''; - $ts = (int) ($t['max_ts'] ?? 0); - if ($tn !== '' && $ts > 0) { - $signTsByTracking[$tn] = max($signTsByTracking[$tn] ?? 0, $ts); - } - } - } - } + $signTsByPoId = ExpressTrackingService::batchResolveSignUnixFromDbForOrders($signItems); } catch (\Throwable $e) { - Log::warning('appendPrescriptionOrderListExportRows resolve sign_time failed: ' . $e->getMessage()); - $signTsByTracking = []; + Log::warning('appendPrescriptionOrderListExportRows batch sign_time failed: ' . $e->getMessage()); + $signTsByPoId = []; + } + + foreach ($lists as $row) { + $oid = (int) ($row['id'] ?? 0); + if ($oid <= 0 || (int) ($signTsByPoId[$oid] ?? 0) > 0) { + continue; + } + if (trim((string) ($row['tracking_number'] ?? '')) === '') { + continue; + } + try { + $synced = ExpressTrackingService::syncSignUnixFromLogisticsForPrescriptionOrder($oid, true); + if ($synced > 0) { + $signTsByPoId[$oid] = $synced; + } + } catch (\Throwable $e) { + Log::warning('appendPrescriptionOrderListExportRows sync sign_time order ' . $oid . ': ' . $e->getMessage()); + } } } + $firstVisitAssistantByDiag = self::batchFirstVisitAssistantNameByDiagnosis($diagIdList, $diagById); $assistantDeptCache = []; foreach ($lists as &$item) { @@ -3145,8 +3188,7 @@ class PrescriptionOrderLogic $item['export_agency_collect'] = number_format(round($amt - $paid, 2), 2, '.', ''); $item['export_tracking_number'] = (string) ($item['tracking_number'] ?? ''); - $tnTrim = trim((string) ($item['tracking_number'] ?? '')); - $signTs = $tnTrim !== '' ? (int) ($signTsByTracking[$tnTrim] ?? 0) : 0; + $signTs = $poId > 0 ? (int) ($signTsByPoId[$poId] ?? 0) : 0; $item['export_sign_time'] = $signTs > 0 ? date('Y-m-d', $signTs) : ''; $isGc = trim((string) ($item['gancao_reciperl_order_no'] ?? '')) !== ''; $item['export_supply_mode'] = $isGc ? '甘草' : '自营'; @@ -3161,7 +3203,9 @@ class PrescriptionOrderLogic } $isFu = (int) ($item['is_follow_up'] ?? 0) === 1; - $item['export_first_visit_assistant'] = $isFu ? (string) ($item['prev_staff'] ?? '') : ''; + $item['export_first_visit_assistant'] = $isFu + ? (string) ($firstVisitAssistantByDiag[(int) ($item['diagnosis_id'] ?? 0)] ?? '') + : ''; $hit = (int) ($item['po_assign_snapshot_hit'] ?? 0); $rel = (int) ($item['po_assign_is_release'] ?? 0);