1048 lines
44 KiB
PHP
1048 lines
44 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\adminapi\lists\tcm;
|
||
|
||
use app\adminapi\lists\BaseAdminDataLists;
|
||
use app\adminapi\logic\dept\DeptLogic;
|
||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||
use app\adminapi\logic\tcm\PrescriptionOrderLogic;
|
||
use app\common\lists\ListsExtendInterface;
|
||
use app\common\lists\ListsSearchInterface;
|
||
use app\common\lists\Traits\HasDataScopeFilter;
|
||
use app\common\model\Order;
|
||
use app\common\model\tcm\Diagnosis;
|
||
use app\common\model\auth\AdminDept;
|
||
use app\common\model\tcm\Prescription;
|
||
use app\common\model\tcm\PrescriptionOrder;
|
||
use app\common\model\tcm\PrescriptionOrderPayOrder;
|
||
use app\common\model\ExpressTracking;
|
||
use app\common\service\gancao\GancaoScmRecipelService;
|
||
use think\facade\Config;
|
||
use think\facade\Db;
|
||
use think\db\Query;
|
||
|
||
class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchInterface, ListsExtendInterface
|
||
{
|
||
use HasDataScopeFilter;
|
||
|
||
private const SCENE_DIAGNOSIS_EDIT = 'diagnosis_edit';
|
||
|
||
public function setSearch(): array
|
||
{
|
||
return [
|
||
'=' => ['prescription_id', 'diagnosis_id', 'fulfillment_status', 'prescription_audit_status', 'payment_slip_audit_status'],
|
||
'%like%' => ['order_no'],
|
||
'between_time' => 'create_time',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 列表/统计条数:含全部搜索条件(含创建时间区间)
|
||
*/
|
||
private function buildFilteredQuery(): Query
|
||
{
|
||
$query = PrescriptionOrder::where($this->searchWhere)->whereNull('delete_time');
|
||
$this->applyPermissionAndExtraFilters($query);
|
||
|
||
return $query;
|
||
}
|
||
|
||
/**
|
||
* 今日汇总:与列表相同的「非时间」条件(医生/医助/患者/状态/单号等),不含创建时间区间,避免与「今日」语义重复
|
||
*
|
||
* @return array<int, array{0: string, 1: string, 2: mixed}>
|
||
*/
|
||
private function searchWhereWithoutCreateTimeRange(): array
|
||
{
|
||
return array_values(array_filter(
|
||
$this->searchWhere,
|
||
static function ($w): bool {
|
||
if (!\is_array($w) || \count($w) < 2) {
|
||
return true;
|
||
}
|
||
if (($w[0] ?? '') === 'create_time' && ($w[1] ?? '') === 'between') {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
));
|
||
}
|
||
|
||
/**
|
||
* 重点看板统计:去掉审核/履约状态筛选,保留其它筛选(时间、医生、医助、患者、供货方式等)
|
||
*
|
||
* @return array<int, array{0: string, 1: string, 2: mixed}>
|
||
*/
|
||
private function searchWhereWithoutStatusFilters(): array
|
||
{
|
||
return array_values(array_filter(
|
||
$this->searchWhere,
|
||
static function ($w): bool {
|
||
if (!\is_array($w) || \count($w) < 2) {
|
||
return true;
|
||
}
|
||
$field = (string) ($w[0] ?? '');
|
||
if (!in_array($field, ['prescription_audit_status', 'payment_slip_audit_status', 'fulfillment_status'], true)) {
|
||
return true;
|
||
}
|
||
$op = strtolower((string) ($w[1] ?? ''));
|
||
|
||
return $op !== '=';
|
||
}
|
||
));
|
||
}
|
||
|
||
private function applyPermissionAndExtraFilters($query): void
|
||
{
|
||
$this->applyDoctorAssistantFilters($query);
|
||
$this->applyPatientKeywordFilter($query);
|
||
$this->applyPatientIdFilter($query);
|
||
$this->applyExpressCompanyFilter($query);
|
||
$this->applyExpressKeywordFilter($query);
|
||
$this->applySupplyModeFilter($query);
|
||
if (!$this->shouldBypassListVisibilityForDiagnosisEdit()) {
|
||
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
||
$this->applyDataScopeForPrescriptionOrder($query);
|
||
}
|
||
// 业绩看板侧栏等:不展示履约已取消(4),与 stats 业绩统计口径一致
|
||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||
}
|
||
$this->applyYejiErCenterRevisitOrderFilter($query);
|
||
YejiStatsLogic::applyYejiDrawerChannelFilterToPrescriptionOrderQuery($query, $this->params);
|
||
}
|
||
|
||
/**
|
||
* 业绩看板:仅列出二中心复诊口径下的业务订单(须同时传 assistant_id、start_time、end_time;revisit_slot=0 为全部复诊分项合计)。
|
||
* admin 维度按订单创建人 o.creator_id(与部门表复诊列、排行榜复诊列同口径)。
|
||
*/
|
||
private function applyYejiErCenterRevisitOrderFilter($query): void
|
||
{
|
||
if ((int) ($this->params['yeji_er_center_revisit_only'] ?? 0) !== 1) {
|
||
return;
|
||
}
|
||
$assistantId = (int) ($this->params['assistant_id'] ?? 0);
|
||
$slot = (int) ($this->params['yeji_er_center_revisit_slot'] ?? 0);
|
||
if ($assistantId <= 0) {
|
||
return;
|
||
}
|
||
$st = trim((string) ($this->params['start_time'] ?? ''));
|
||
$et = trim((string) ($this->params['end_time'] ?? ''));
|
||
if ($st === '' || $et === '') {
|
||
return;
|
||
}
|
||
$t0 = strtotime($st);
|
||
$t1 = strtotime($et);
|
||
if ($t0 === false || $t1 === false) {
|
||
return;
|
||
}
|
||
$ids = DeptLogic::listErCenterRevisitOrderIdsForCreator(
|
||
$assistantId,
|
||
$slot,
|
||
(int) $t0,
|
||
(int) $t1
|
||
);
|
||
if ($ids === []) {
|
||
$query->whereRaw('0 = 1');
|
||
|
||
return;
|
||
}
|
||
$query->whereIn('id', $ids);
|
||
}
|
||
|
||
/**
|
||
* 按快递公司筛选(点击快捷标签:顺丰 / 京东)
|
||
*
|
||
* 说明:主表 express_company 常为 auto/空;快递100 回写承运商在 zyt_express_tracking,需一并匹配。
|
||
*/
|
||
private function applyExpressCompanyFilter($query): void
|
||
{
|
||
$company = strtolower(trim((string) ($this->params['express_company'] ?? '')));
|
||
if (!in_array($company, ['sf', 'jd'], true)) {
|
||
return;
|
||
}
|
||
$poTbl = (new PrescriptionOrder())->getTable();
|
||
$etTbl = (new ExpressTracking())->getTable();
|
||
|
||
if ($company === 'sf') {
|
||
$po = "(LOWER(TRIM(IFNULL(`{$poTbl}`.`express_company`,''))) IN ('sf','shunfeng')"
|
||
. " OR `{$poTbl}`.`express_company` LIKE '%顺丰%'"
|
||
. " OR (TRIM(IFNULL(`{$poTbl}`.`tracking_number`,'')) <> ''"
|
||
. " AND (`{$poTbl}`.`tracking_number` LIKE 'SF%' OR `{$poTbl}`.`tracking_number` LIKE 'sf%')))";
|
||
$et = "EXISTS (SELECT 1 FROM `{$etTbl}` et WHERE et.`delete_time` IS NULL"
|
||
. " AND et.`order_id` = `{$poTbl}`.`id` AND et.`order_type` = 'prescription'"
|
||
. " AND (LOWER(TRIM(IFNULL(et.`express_company`,''))) IN ('sf','shunfeng')"
|
||
. " OR et.`express_company_name` LIKE '%顺丰%'"
|
||
. " OR (TRIM(IFNULL(et.`tracking_number`,'')) <> ''"
|
||
. " AND (et.`tracking_number` LIKE 'SF%' OR et.`tracking_number` LIKE 'sf%'))))";
|
||
$query->whereRaw("(($po) OR ($et))");
|
||
|
||
return;
|
||
}
|
||
|
||
$po = "(LOWER(TRIM(IFNULL(`{$poTbl}`.`express_company`,''))) IN ('jd','jingdong')"
|
||
. " OR `{$poTbl}`.`express_company` LIKE '%京东%'"
|
||
. " OR (TRIM(IFNULL(`{$poTbl}`.`tracking_number`,'')) <> ''"
|
||
. " AND (`{$poTbl}`.`tracking_number` LIKE 'JD%' OR `{$poTbl}`.`tracking_number` LIKE 'jd%'"
|
||
. " OR `{$poTbl}`.`tracking_number` LIKE 'JDVB%' OR `{$poTbl}`.`tracking_number` LIKE 'jdvb%')))";
|
||
$et = "EXISTS (SELECT 1 FROM `{$etTbl}` et WHERE et.`delete_time` IS NULL"
|
||
. " AND et.`order_id` = `{$poTbl}`.`id` AND et.`order_type` = 'prescription'"
|
||
. " AND (LOWER(TRIM(IFNULL(et.`express_company`,''))) IN ('jd','jingdong')"
|
||
. " OR et.`express_company_name` LIKE '%京东%'"
|
||
. " OR (TRIM(IFNULL(et.`tracking_number`,'')) <> ''"
|
||
. " AND (et.`tracking_number` LIKE 'JD%' OR et.`tracking_number` LIKE 'jd%'"
|
||
. " OR et.`tracking_number` LIKE 'JDVB%' OR et.`tracking_number` LIKE 'jdvb%'))))";
|
||
$query->whereRaw("(($po) OR ($et))");
|
||
}
|
||
|
||
/**
|
||
* 供货方式:甘草(已上传甘草药方单号)/ 自营(无甘草单号,走药房直发等)
|
||
*
|
||
* 入参 supply_mode:gancao | self,空表示不限
|
||
*/
|
||
private function applySupplyModeFilter($query): void
|
||
{
|
||
$mode = strtolower(trim((string) ($this->params['supply_mode'] ?? '')));
|
||
if ($mode === '') {
|
||
return;
|
||
}
|
||
if ($mode === 'gancao') {
|
||
$query->whereRaw("TRIM(IFNULL(`gancao_reciperl_order_no`,'')) <> ''");
|
||
|
||
return;
|
||
}
|
||
if ($mode === 'self') {
|
||
$query->whereRaw("TRIM(IFNULL(`gancao_reciperl_order_no`,'')) = ''");
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 非「业务订单全量」角色:默认仅本人创建;若持有 viewOrdersForOwnPrescription 则包含关联处方开方人为本人的订单;
|
||
* 另含「关联诊单医助为本人」以便医助跟进出单与履约。
|
||
* 显式筛选 assistant_id 时:若该医助落在当前账号数据域内,则允许按「诊单医助 / 订单创建人」命中(与 applyDoctorAssistantFilters 一致)。
|
||
*/
|
||
private function applyCreatorOrOwnPrescriptionVisibility($query): void
|
||
{
|
||
if (PrescriptionOrderLogic::canSeeAllPrescriptionOrders($this->adminInfo)) {
|
||
return;
|
||
}
|
||
$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;
|
||
if ($explicitAssistant > 0) {
|
||
if (!$this->dataScopeShouldApply()) {
|
||
$allowExplicitAssistantVisibility = true;
|
||
} else {
|
||
$visibleIds = $this->getDataScopeVisibleAdminIds();
|
||
if ($visibleIds === null || in_array($explicitAssistant, $visibleIds, true)) {
|
||
$allowExplicitAssistantVisibility = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
$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) {
|
||
$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})"
|
||
);
|
||
});
|
||
}
|
||
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
|
||
*/
|
||
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}))"
|
||
);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 仅诊单编辑页内的患者订单查询可跳过列表页权限,但必须显式带场景且保留患者/诊单范围收敛。
|
||
*/
|
||
private function shouldBypassListVisibilityForDiagnosisEdit(): bool
|
||
{
|
||
$scene = trim((string) ($this->params['scene'] ?? ''));
|
||
if ($scene !== self::SCENE_DIAGNOSIS_EDIT) {
|
||
return false;
|
||
}
|
||
|
||
$patientId = (int) ($this->params['patient_id'] ?? 0);
|
||
$contextDiagnosisId = (int) ($this->params['context_diagnosis_id'] ?? 0);
|
||
if ($patientId <= 0 || $contextDiagnosisId <= 0) {
|
||
return false;
|
||
}
|
||
|
||
$diagnosisPatientId = (int) Diagnosis::where('id', $contextDiagnosisId)
|
||
->whereNull('delete_time')
|
||
->value('patient_id');
|
||
|
||
return $diagnosisPatientId > 0 && $diagnosisPatientId === $patientId;
|
||
}
|
||
|
||
/**
|
||
* 医助:仅「诊单医助=本人」的订单计业绩
|
||
*/
|
||
private function applyAssistantDiagnosisOnlyFilter($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}"
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 统计用查询骨架:不含 create_time 区间;其它筛选项 + 统计专用数据域(不重复 where)
|
||
*
|
||
* @param bool $applyYejiDrawerChannel 是否应用业绩侧栏渠道收窄(合计业绩统计须传 false)
|
||
*/
|
||
private function buildBaseQueryForStats(bool $applyYejiDrawerChannel = true): Query
|
||
{
|
||
$query = PrescriptionOrder::where($this->searchWhereWithoutCreateTimeRange())->whereNull('delete_time');
|
||
$this->applyDoctorAssistantFilters($query);
|
||
$this->applyPatientKeywordFilter($query);
|
||
if (PrescriptionOrderLogic::canViewOrderListStatsAllScope($this->adminInfo)) {
|
||
$this->applyDataScopeForPrescriptionOrder($query);
|
||
} else {
|
||
$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->applyDataScopeForPrescriptionOrder($query);
|
||
} else {
|
||
if (!PrescriptionOrderLogic::canSeeAllPrescriptionOrders($this->adminInfo)) {
|
||
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
||
}
|
||
$this->applyDataScopeForPrescriptionOrder($query);
|
||
}
|
||
}
|
||
// 与看板「合计业绩」一致:NULL 安全剔除履约已取消(4),勿用 `<>` 以免误丢 NULL 状态行
|
||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||
}
|
||
if ($applyYejiDrawerChannel) {
|
||
YejiStatsLogic::applyYejiDrawerChannelFilterToPrescriptionOrderQuery($query, $this->params);
|
||
}
|
||
|
||
return $query;
|
||
}
|
||
|
||
/**
|
||
* 统计所对应的时间:与入参 start_time / end_time 一致;未传则按自然日「今天」
|
||
*
|
||
* @return array{0: int, 1: int, 2: string, 3: string}
|
||
*/
|
||
private function resolveStatsTimeWindow(): array
|
||
{
|
||
$st = trim((string) ($this->params['start_time'] ?? ''));
|
||
$et = trim((string) ($this->params['end_time'] ?? ''));
|
||
if ($st !== '' && $et !== '') {
|
||
$t0 = strtotime($st);
|
||
$t1 = strtotime($et);
|
||
if ($t0 !== false && $t1 !== false) {
|
||
return [(int) $t0, (int) $t1, $st, $et];
|
||
}
|
||
}
|
||
$t0 = strtotime(date('Y-m-d 00:00:00'));
|
||
$t1 = strtotime(date('Y-m-d 23:59:59'));
|
||
if ($t0 === false || $t1 === false) {
|
||
return [0, 0, '', ''];
|
||
}
|
||
|
||
return [
|
||
(int) $t0,
|
||
(int) $t1,
|
||
date('Y-m-d 00:00:00'),
|
||
date('Y-m-d 23:59:59'),
|
||
];
|
||
}
|
||
|
||
public function lists(): array
|
||
{
|
||
$query = $this->buildFilteredQuery();
|
||
$lists = $query
|
||
->order('id', 'desc')
|
||
->limit($this->limitOffset, $this->limitLength)
|
||
->select()
|
||
->toArray();
|
||
|
||
$poIds = array_values(array_filter(array_map('intval', array_column($lists, 'id'))));
|
||
$paidSumByPo = [];
|
||
if ($poIds !== []) {
|
||
$links = PrescriptionOrderPayOrder::whereIn('prescription_order_id', $poIds)
|
||
->field(['prescription_order_id', 'pay_order_id'])
|
||
->select()
|
||
->toArray();
|
||
$byPo = [];
|
||
foreach ($links as $l) {
|
||
$poid = (int) ($l['prescription_order_id'] ?? 0);
|
||
$oid = (int) ($l['pay_order_id'] ?? 0);
|
||
if ($poid > 0 && $oid > 0) {
|
||
$byPo[$poid][] = $oid;
|
||
}
|
||
}
|
||
$allOids = [];
|
||
foreach ($byPo as $oids) {
|
||
$allOids = array_merge($allOids, $oids);
|
||
}
|
||
$allOids = array_values(array_unique($allOids));
|
||
$amountByOid = [];
|
||
if ($allOids !== []) {
|
||
$amountByOid = Order::whereIn('id', $allOids)->whereNull('delete_time')->column('amount', 'id');
|
||
}
|
||
foreach ($poIds as $pid) {
|
||
$s = 0.0;
|
||
foreach ($byPo[$pid] ?? [] as $oid) {
|
||
$s += (float) ($amountByOid[$oid] ?? 0);
|
||
}
|
||
$paidSumByPo[$pid] = round($s, 2);
|
||
}
|
||
}
|
||
|
||
// 获取创建人姓名
|
||
$creatorIds = array_values(array_unique(array_filter(array_map('intval', array_column($lists, 'creator_id')))));
|
||
$creatorNames = [];
|
||
if ($creatorIds !== []) {
|
||
$creatorNames = \app\common\model\auth\Admin::whereIn('id', $creatorIds)->column('name', 'id');
|
||
}
|
||
|
||
// 获取处方ID对应的开方人姓名
|
||
$prescriptionIds = array_values(array_unique(array_filter(array_map('intval', array_column($lists, 'prescription_id')))));
|
||
$prescriptionCreators = [];
|
||
if ($prescriptionIds !== []) {
|
||
$prescriptions = \app\common\model\tcm\Prescription::whereIn('id', $prescriptionIds)
|
||
->whereNull('delete_time')
|
||
->field(['id', 'creator_id', 'doctor_name', 'phone'])
|
||
->select()
|
||
->toArray();
|
||
$doctorIds = [];
|
||
foreach ($prescriptions as $rx) {
|
||
$rxId = (int) ($rx['id'] ?? 0);
|
||
$doctorId = (int) ($rx['creator_id'] ?? 0);
|
||
$doctorName = (string) ($rx['doctor_name'] ?? '');
|
||
if ($rxId > 0) {
|
||
$prescriptionCreators[$rxId] = [
|
||
'doctor_id' => $doctorId,
|
||
'doctor_name' => $doctorName,
|
||
'phone' => (string) ($rx['phone'] ?? ''),
|
||
];
|
||
if ($doctorId > 0) {
|
||
$doctorIds[] = $doctorId;
|
||
}
|
||
}
|
||
}
|
||
// 如果 doctor_name 为空,从 Admin 表获取
|
||
$doctorIds = array_values(array_unique($doctorIds));
|
||
if ($doctorIds !== []) {
|
||
$doctorNamesFromAdmin = \app\common\model\auth\Admin::whereIn('id', $doctorIds)->column('name', 'id');
|
||
foreach ($prescriptionCreators as &$pc) {
|
||
if (empty($pc['doctor_name']) && $pc['doctor_id'] > 0) {
|
||
$pc['doctor_name'] = $doctorNamesFromAdmin[$pc['doctor_id']] ?? '';
|
||
}
|
||
}
|
||
unset($pc);
|
||
}
|
||
}
|
||
|
||
$depMin = PrescriptionOrderLogic::depositMinAmount();
|
||
$diagIdsForAssist = array_values(array_unique(array_filter(array_map('intval', array_column($lists, 'diagnosis_id')))));
|
||
$assistantByDiag = [];
|
||
if ($diagIdsForAssist !== []) {
|
||
$assistantByDiag = Diagnosis::whereIn('id', $diagIdsForAssist)->whereNull('delete_time')->column('assistant_id', 'id');
|
||
}
|
||
$assistantIds = array_values(array_unique(array_filter(array_map('intval', array_values($assistantByDiag)))));
|
||
$assistantNames = $assistantIds !== []
|
||
? \app\common\model\auth\Admin::whereIn('id', $assistantIds)->column('name', 'id')
|
||
: [];
|
||
foreach ($lists as &$item) {
|
||
PrescriptionOrderLogic::maskInternalCostIfNeeded($item, $this->adminInfo);
|
||
PrescriptionOrderLogic::maskRemarkExtraIfNeeded($item, $this->adminInfo);
|
||
$pid = (int) ($item['id'] ?? 0);
|
||
$item['linked_pay_order_count'] = (int) PrescriptionOrderPayOrder::where(
|
||
'prescription_order_id',
|
||
$pid
|
||
)->count();
|
||
$item['linked_pay_paid_total'] = $paidSumByPo[$pid] ?? 0.0;
|
||
$item['deposit_min_amount'] = $depMin;
|
||
$item['can_upload_gancao_reciperl'] = PrescriptionOrderLogic::canUploadGancaoRecipel(
|
||
$item,
|
||
$this->adminId,
|
||
$this->adminInfo,
|
||
$assistantByDiag
|
||
);
|
||
|
||
// 添加创建人姓名
|
||
$creatorId = (int) ($item['creator_id'] ?? 0);
|
||
$item['creator_name'] = $creatorId > 0 ? ($creatorNames[$creatorId] ?? '') : '';
|
||
|
||
// 添加医助(来自诊单 assistant_id)
|
||
$diagIdForAssist = (int) ($item['diagnosis_id'] ?? 0);
|
||
$assistantIdForItem = (int) ($assistantByDiag[$diagIdForAssist] ?? 0);
|
||
$item['assistant_id'] = $assistantIdForItem;
|
||
$item['assistant_name'] = $assistantIdForItem > 0
|
||
? ($assistantNames[$assistantIdForItem] ?? '')
|
||
: '';
|
||
|
||
// 添加开方人姓名、处方登记手机(与业务订单收货手机比对用)
|
||
$rxId = (int) ($item['prescription_id'] ?? 0);
|
||
$item['doctor_name'] = $rxId > 0 ? ($prescriptionCreators[$rxId]['doctor_name'] ?? '') : '';
|
||
$item['prescription_phone'] = $rxId > 0 ? ($prescriptionCreators[$rxId]['phone'] ?? '') : '';
|
||
}
|
||
unset($item);
|
||
|
||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) === 1) {
|
||
PrescriptionOrderLogic::appendLinkedAppointmentsToListRows($lists, $this->params);
|
||
}
|
||
|
||
return $lists;
|
||
}
|
||
|
||
public function extend(): array
|
||
{
|
||
$s = $this->computeListStatsForExtend();
|
||
$focus = $this->computeFocusCountsForExtend();
|
||
|
||
$base = [
|
||
'deposit_min_amount' => PrescriptionOrderLogic::depositMinAmount(),
|
||
'gancao_scm_enabled' => GancaoScmRecipelService::isConfigured(),
|
||
'stats_order_amount' => $s['order_amount'],
|
||
'stats_order_amount_not_cancelled' => $s['order_amount_not_cancelled'],
|
||
'stats_order_amount_cancelled' => $s['order_amount_cancelled'],
|
||
/** 业绩:业务订单金额,剔除履约已取消(fulfillment_status=4),其余状态全部计入 */
|
||
'stats_order_amount_performance' => $s['order_amount_not_cancelled'],
|
||
'stats_linked_pay_amount' => $s['linked_pay_amount'],
|
||
'stats_linked_pay_amount_not_cancelled' => $s['linked_pay_amount_not_cancelled'],
|
||
'stats_linked_pay_amount_cancelled' => $s['linked_pay_amount_cancelled'],
|
||
/** 业绩-关联实付:剔除履约已取消订单的关联支付金额 */
|
||
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_not_cancelled'],
|
||
'stats_time_start' => $s['label_start'],
|
||
'stats_time_end' => $s['label_end'],
|
||
/** 统计口径:all=支付审核白名单全量;assistant=医助仅诊单协助业绩;restricted=与列表同域 */
|
||
'stats_scope' => $s['scope'],
|
||
'focus_pending_rx_count' => $focus['pending_rx'],
|
||
'focus_pending_pay_count' => $focus['pending_pay'],
|
||
'focus_pending_ship_count' => $focus['pending_ship'],
|
||
'focus_risk_count' => $focus['risk'],
|
||
];
|
||
$yejiConsult = $this->computeYejiDrawerConsultCountIfRequested();
|
||
if ($yejiConsult !== null) {
|
||
$base['stats_yeji_consult_count'] = $yejiConsult;
|
||
}
|
||
$yejiDrawerTotal = $this->computeYejiDrawerPerformanceTotalForExtend();
|
||
if ($yejiDrawerTotal !== null) {
|
||
$base['stats_yeji_drawer_total_performance_amount'] = $yejiDrawerTotal['amount'];
|
||
$base['stats_yeji_drawer_total_performance_order_count'] = $yejiDrawerTotal['order_count'];
|
||
}
|
||
|
||
return $base;
|
||
}
|
||
|
||
/**
|
||
* 业绩看板侧栏专用:约诊/接诊条数,与 YejiStatsLogic::applyConsultFiltersAlignedWithAppointmentLists 同口径
|
||
* (doctor_appointment.status=3,appointment_date 落入区间,有效医助 COALESCE(挂号.assistant_id,诊单.assistant_id))。
|
||
* 请求须带 yeji_order_drawer=1,且 assistant_id 或 assistant_dept_id 与起止时间有效。
|
||
*/
|
||
private function computeYejiDrawerConsultCountIfRequested(): ?int
|
||
{
|
||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) !== 1) {
|
||
return null;
|
||
}
|
||
$assistantId = (int) ($this->params['assistant_id'] ?? 0);
|
||
$deptRootId = (int) ($this->params['assistant_dept_id'] ?? 0);
|
||
if ($assistantId <= 0 && $deptRootId <= 0) {
|
||
return null;
|
||
}
|
||
[$startDate, $endDate] = $this->resolveYejiConsultAppointmentDateRange();
|
||
if ($startDate === '' || $endDate === '') {
|
||
return null;
|
||
}
|
||
|
||
$eff = 'COALESCE(NULLIF(a.assistant_id, 0), NULLIF(u.assistant_id, 0))';
|
||
$query = Db::name('doctor_appointment')->alias('a')
|
||
->leftJoin('tcm_diagnosis u', 'a.patient_id = u.id')
|
||
->where('a.status', 3)
|
||
->whereBetween('a.appointment_date', [$startDate, $endDate])
|
||
->whereRaw('(u.id IS NULL OR u.delete_time IS NULL)');
|
||
|
||
if ($assistantId > 0) {
|
||
$query->whereRaw("({$eff}) = " . $assistantId);
|
||
} else {
|
||
$deptIds = DeptLogic::getSelfAndDescendantIds($deptRootId);
|
||
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
|
||
return $id > 0;
|
||
}));
|
||
if ($deptIds === []) {
|
||
return 0;
|
||
}
|
||
$adTbl = (new AdminDept())->getTable();
|
||
$inList = implode(',', $deptIds);
|
||
$query->whereRaw(
|
||
"EXISTS (SELECT 1 FROM `{$adTbl}` ad WHERE ad.admin_id = {$eff} AND ad.dept_id IN ({$inList}))"
|
||
);
|
||
}
|
||
|
||
return (int) $query->count();
|
||
}
|
||
|
||
/**
|
||
* @return array{0: string, 1: string} Y-m-d
|
||
*/
|
||
private function resolveYejiConsultAppointmentDateRange(): array
|
||
{
|
||
$st = trim((string) ($this->params['start_time'] ?? ''));
|
||
$et = trim((string) ($this->params['end_time'] ?? ''));
|
||
if ($st === '' || $et === '') {
|
||
return ['', ''];
|
||
}
|
||
$t0 = strtotime($st);
|
||
$t1 = strtotime($et);
|
||
if ($t0 === false || $t1 === false) {
|
||
return ['', ''];
|
||
}
|
||
if ($t1 < $t0) {
|
||
$tmp = $t0;
|
||
$t0 = $t1;
|
||
$t1 = $tmp;
|
||
}
|
||
|
||
return [date('Y-m-d', $t0), date('Y-m-d', $t1)];
|
||
}
|
||
|
||
/**
|
||
* 侧栏已选渠道时:「合计业绩」金额与订单数(与看板列同口径,不限渠道收窄)
|
||
*
|
||
* @return array{amount: float, order_count: int}|null
|
||
*/
|
||
private function computeYejiDrawerPerformanceTotalForExtend(): ?array
|
||
{
|
||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) !== 1) {
|
||
return null;
|
||
}
|
||
if (trim((string) ($this->params['channel_code'] ?? '')) === '') {
|
||
return null;
|
||
}
|
||
[$t0, $t1] = $this->resolveStatsTimeWindow();
|
||
if ($t0 <= 0 || $t1 <= 0) {
|
||
return null;
|
||
}
|
||
if ($t1 < $t0) {
|
||
$tmp = $t0;
|
||
$t0 = $t1;
|
||
$t1 = $tmp;
|
||
}
|
||
$q = $this->buildBaseQueryForStats(false)->whereBetween('create_time', [$t0, $t1]);
|
||
// 全量「合计业绩」与看板列同口径:始终剔除履约已取消(4),不依赖 exclude 参数
|
||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, '');
|
||
$amount = round((float) (clone $q)->sum('amount'), 2);
|
||
$count = (int) (clone $q)->count();
|
||
|
||
return ['amount' => $amount, 'order_count' => $count];
|
||
}
|
||
|
||
/**
|
||
* 重点看板计数(基于“当前筛选(去除审核/履约状态)”后的全量命中)
|
||
*
|
||
* @return array{pending_rx:int,pending_pay:int,pending_ship:int,risk:int}
|
||
*/
|
||
private function computeFocusCountsForExtend(): array
|
||
{
|
||
$query = PrescriptionOrder::where($this->searchWhereWithoutStatusFilters())->whereNull('delete_time');
|
||
$this->applyPermissionAndExtraFilters($query);
|
||
|
||
$pendingRxQuery = clone $query;
|
||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($pendingRxQuery, '');
|
||
|
||
return [
|
||
// 处方审核待处理(不含履约已取消)
|
||
'pending_rx' => (int) $pendingRxQuery->where('prescription_audit_status', 0)->count(),
|
||
// 支付审核待处理(前提:处方已通过)
|
||
'pending_pay' => (int) (clone $query)->where('prescription_audit_status', 1)->where('payment_slip_audit_status', 0)->count(),
|
||
// 履约待发货
|
||
'pending_ship' => (int) (clone $query)->where('fulfillment_status', 2)->count(),
|
||
// 重点异常:支付审核驳回(与前端「重点异常」快捷筛选口径一致)
|
||
'risk' => (int) (clone $query)->where('payment_slip_audit_status', 2)->count(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 关联实付按业务订单 id 列表汇总
|
||
*
|
||
* @param array<int> $poIds
|
||
*/
|
||
private function sumLinkedPayForPrescriptionOrderIds(array $poIds): float
|
||
{
|
||
if ($poIds === []) {
|
||
return 0.0;
|
||
}
|
||
$sum = (float) PrescriptionOrderPayOrder::alias('l')
|
||
->join('order o', 'l.pay_order_id = o.id')
|
||
->whereIn('l.prescription_order_id', $poIds)
|
||
->whereNull('o.delete_time')
|
||
->sum('o.amount');
|
||
|
||
return round($sum, 2);
|
||
}
|
||
|
||
/**
|
||
* @return array{order_amount: float, order_amount_not_cancelled: float, order_amount_cancelled: float, linked_pay_amount: float, linked_pay_amount_not_cancelled: float, linked_pay_amount_cancelled: float, label_start: string, label_end: string, scope: string}
|
||
*/
|
||
private function computeListStatsForExtend(): array
|
||
{
|
||
[$t0, $t1, $l0, $l1] = $this->resolveStatsTimeWindow();
|
||
if ($t0 <= 0 || $t1 <= 0) {
|
||
return $this->statsZeroPayload();
|
||
}
|
||
if ($t1 < $t0) {
|
||
$tmp = $t0;
|
||
$t0 = $t1;
|
||
$t1 = $tmp;
|
||
}
|
||
|
||
$q = $this->buildBaseQueryForStats()->whereBetween('create_time', [$t0, $t1]);
|
||
$orderTotal = round((float) (clone $q)->sum('amount'), 2);
|
||
$orderCancelled = round((float) (clone $q)->where('fulfillment_status', 4)->sum('amount'), 2);
|
||
$orderNotCancelled = round($orderTotal - $orderCancelled, 2);
|
||
|
||
$idsAll = (clone $q)->column('id');
|
||
$idsAll = array_values(array_filter(array_map('intval', $idsAll), static function (int $id): bool {
|
||
return $id > 0;
|
||
}));
|
||
if ($idsAll === []) {
|
||
return $this->statsExtendPayload(
|
||
$orderTotal,
|
||
$orderNotCancelled,
|
||
$orderCancelled,
|
||
0.0,
|
||
0.0,
|
||
0.0,
|
||
$l0,
|
||
$l1
|
||
);
|
||
}
|
||
$idsCancel = (clone $q)->where('fulfillment_status', 4)->column('id');
|
||
$idsCancel = array_values(array_filter(array_map('intval', $idsCancel), static function (int $id): bool {
|
||
return $id > 0;
|
||
}));
|
||
$idsNotCancelled = array_values(array_diff($idsAll, $idsCancel));
|
||
|
||
$linkedTotal = $this->sumLinkedPayForPrescriptionOrderIds($idsAll);
|
||
$linkedNotCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsNotCancelled);
|
||
$linkedCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsCancel);
|
||
|
||
return $this->statsExtendPayload(
|
||
$orderTotal,
|
||
$orderNotCancelled,
|
||
$orderCancelled,
|
||
$linkedTotal,
|
||
$linkedNotCancelled,
|
||
$linkedCancelled,
|
||
$l0,
|
||
$l1
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @return array{order_amount: float, order_amount_not_cancelled: float, order_amount_cancelled: float, linked_pay_amount: float, linked_pay_amount_not_cancelled: float, linked_pay_amount_cancelled: float, label_start: string, label_end: string, scope: string}
|
||
*/
|
||
private function statsZeroPayload(): array
|
||
{
|
||
return $this->statsExtendPayload(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, '', '');
|
||
}
|
||
|
||
/**
|
||
* @return array{order_amount: float, order_amount_not_cancelled: float, order_amount_cancelled: float, linked_pay_amount: float, linked_pay_amount_not_cancelled: float, linked_pay_amount_cancelled: float, label_start: string, label_end: string, scope: string}
|
||
*/
|
||
private function statsExtendPayload(
|
||
float $orderTotal,
|
||
float $orderNotCancelled,
|
||
float $orderCancelled,
|
||
float $linkedTotal,
|
||
float $linkedNotCancelled,
|
||
float $linkedCancelled,
|
||
string $l0,
|
||
string $l1
|
||
): array {
|
||
$scope = 'restricted';
|
||
if (PrescriptionOrderLogic::canViewOrderListStatsAllScope($this->adminInfo)) {
|
||
$scope = 'all';
|
||
} else {
|
||
$ar = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2);
|
||
if ($ar > 0 && in_array($ar, array_map('intval', $this->adminInfo['role_id'] ?? []), true)) {
|
||
$scope = 'assistant';
|
||
} elseif (PrescriptionOrderLogic::canViewOrdersForOwnPrescription($this->adminInfo)) {
|
||
$scope = 'own_rx';
|
||
}
|
||
}
|
||
|
||
return [
|
||
'order_amount' => $orderTotal,
|
||
'order_amount_not_cancelled' => $orderNotCancelled,
|
||
'order_amount_cancelled' => $orderCancelled,
|
||
'linked_pay_amount' => $linkedTotal,
|
||
'linked_pay_amount_not_cancelled' => $linkedNotCancelled,
|
||
'linked_pay_amount_cancelled' => $linkedCancelled,
|
||
'label_start' => $l0,
|
||
'label_end' => $l1,
|
||
'scope' => $scope,
|
||
];
|
||
}
|
||
|
||
public function count(): int
|
||
{
|
||
$query = $this->buildFilteredQuery();
|
||
|
||
return (int) $query->count();
|
||
}
|
||
|
||
/**
|
||
* 按开方医生(关联处方 creator_id)/ 诊单医助筛选
|
||
*
|
||
* 医助:诊单 assistant_id 与业务订单 creator_id 并举——医助常代建单但诊单未写 assistant_id。
|
||
*/
|
||
private function applyDoctorAssistantFilters($query): void
|
||
{
|
||
$poTbl = (new PrescriptionOrder())->getTable();
|
||
|
||
if (isset($this->params['doctor_id']) && (int) $this->params['doctor_id'] > 0) {
|
||
$doctorId = (int) $this->params['doctor_id'];
|
||
$rxTbl = (new Prescription())->getTable();
|
||
$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})"
|
||
);
|
||
});
|
||
}
|
||
}
|
||
|
||
if (isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) {
|
||
if ((int) ($this->params['yeji_drawer_match_table_performance'] ?? 0) === 1) {
|
||
$rowDeptId = (int) $this->params['assistant_dept_id'];
|
||
$adminIds = YejiStatsLogic::collectAdminIdsAttributedToYejiTableRow(
|
||
$rowDeptId,
|
||
$this->params['dept_ids'] ?? null
|
||
);
|
||
if ($adminIds === []) {
|
||
$query->whereRaw('0 = 1');
|
||
|
||
return;
|
||
}
|
||
$this->applyPerformanceAttributionAdminInFilter($query, $adminIds);
|
||
|
||
return;
|
||
}
|
||
$yejiTableRowDeptIds = [];
|
||
if (isset($this->params['yeji_table_row_dept_ids']) && trim((string) $this->params['yeji_table_row_dept_ids']) !== '') {
|
||
foreach (explode(',', (string) $this->params['yeji_table_row_dept_ids']) as $p) {
|
||
$v = (int) trim((string) $p);
|
||
if ($v > 0) {
|
||
$yejiTableRowDeptIds[] = $v;
|
||
}
|
||
}
|
||
}
|
||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) === 1 && $yejiTableRowDeptIds !== []) {
|
||
YejiStatsLogic::applyPrescriptionOrderListAlignedWinnerDeptFilter(
|
||
$query,
|
||
(int) $this->params['assistant_dept_id'],
|
||
$yejiTableRowDeptIds,
|
||
$this->params['dept_ids'] ?? null
|
||
);
|
||
|
||
return;
|
||
}
|
||
$rootDeptId = (int) $this->params['assistant_dept_id'];
|
||
$deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId);
|
||
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
|
||
return $id > 0;
|
||
}));
|
||
if ($deptIds === []) {
|
||
$query->whereRaw('0 = 1');
|
||
|
||
return;
|
||
}
|
||
$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'
|
||
);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 业绩看板侧栏:只保留「业绩归属医助」落在给定 admin_id 集合的订单
|
||
* (与 YejiStatsLogic::sqlPerformanceAttributionAdminExpr 一致——按订单创建人)。
|
||
*
|
||
* @param int[] $adminIds
|
||
*/
|
||
private function applyPerformanceAttributionAdminInFilter($query, array $adminIds): void
|
||
{
|
||
$adminIds = array_values(array_unique(array_filter(array_map('intval', $adminIds), static function (int $x): bool {
|
||
return $x > 0;
|
||
})));
|
||
if ($adminIds === []) {
|
||
$query->whereRaw('0 = 1');
|
||
|
||
return;
|
||
}
|
||
$query->whereIn('creator_id', $adminIds);
|
||
}
|
||
|
||
/**
|
||
* 按诊单患者姓名 / 手机号模糊检索(关联 diagnosis_id)
|
||
*/
|
||
private function applyPatientKeywordFilter($query): void
|
||
{
|
||
$kw = trim((string) ($this->params['patient_keyword'] ?? ''));
|
||
if ($kw === '') {
|
||
return;
|
||
}
|
||
|
||
$poTbl = (new PrescriptionOrder())->getTable();
|
||
$diagTbl = (new Diagnosis())->getTable();
|
||
$likeInner = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $kw);
|
||
$like = '%' . $likeInner . '%';
|
||
$likeSql = str_replace("'", "''", $like);
|
||
|
||
$query->whereExists(
|
||
"SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` AND dg.`delete_time` IS NULL "
|
||
. "AND (dg.`patient_name` LIKE '{$likeSql}' OR dg.`phone` LIKE '{$likeSql}')"
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 按真实患者ID精确过滤(tcm_diagnosis.patient_id 是跨诊单的患者唯一ID)
|
||
* 用于诊单编辑抽屉「业务订单」tab 列出该患者所有诊单产生的处方业务订单。
|
||
*/
|
||
private function applyPatientIdFilter($query): void
|
||
{
|
||
$patientId = (int) ($this->params['patient_id'] ?? 0);
|
||
if ($patientId <= 0) {
|
||
return;
|
||
}
|
||
|
||
$poTbl = (new PrescriptionOrder())->getTable();
|
||
$diagTbl = (new Diagnosis())->getTable();
|
||
|
||
$query->whereExists(
|
||
"SELECT 1 FROM `{$diagTbl}` dg WHERE dg.`id` = `{$poTbl}`.`diagnosis_id` "
|
||
. "AND dg.`delete_time` IS NULL AND dg.`patient_id` = {$patientId}"
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 按快递单号 / 快递公司 模糊检索
|
||
*/
|
||
private function applyExpressKeywordFilter($query): void
|
||
{
|
||
$kw = trim((string) ($this->params['express_keyword'] ?? ''));
|
||
if ($kw === '') {
|
||
return;
|
||
}
|
||
$likeInner = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $kw);
|
||
$like = '%' . $likeInner . '%';
|
||
$likeSql = str_replace("'", "''", $like);
|
||
$poTbl = (new PrescriptionOrder())->getTable();
|
||
$etTbl = (new ExpressTracking())->getTable();
|
||
$query->whereRaw(
|
||
"(IFNULL(`{$poTbl}`.`tracking_number`,'') LIKE '{$likeSql}'"
|
||
. " OR IFNULL(`{$poTbl}`.`express_company`,'') LIKE '{$likeSql}'"
|
||
. " OR EXISTS (SELECT 1 FROM `{$etTbl}` et WHERE et.`delete_time` IS NULL"
|
||
. " AND et.`order_id` = `{$poTbl}`.`id` AND et.`order_type` = 'prescription'"
|
||
. " AND (IFNULL(et.`tracking_number`,'') LIKE '{$likeSql}'"
|
||
. " OR IFNULL(et.`express_company`,'') LIKE '{$likeSql}'"
|
||
. " OR IFNULL(et.`express_company_name`,'') LIKE '{$likeSql}'))"
|
||
. ")"
|
||
);
|
||
}
|
||
}
|