更新
This commit is contained in:
@@ -5,8 +5,10 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\common\service\qywx\MediaChannelService;
|
||||
use think\db\Query;
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
@@ -19,7 +21,7 @@ use think\facade\Db;
|
||||
* - 复诊统计 = 仅**名称含「二中心」的部门及其组织架构下级**:区间内业务单按患者 create_time 排序,第 1 笔不计,第 2 笔起分列「复诊2」「复诊3」…;口径同业绩单条件;按诊单医助在组织树内归属 rollup(含下级),与 dept 模块 ErCenter 复诊算法一致
|
||||
* - 接诊诊单 = 已完成挂号 status=3,**与 admin 挂号列表条数同口径**(appointment_date、诊单未软删),不限医助角色、不要求 assistant_id 非 0;
|
||||
* 部门归属:优先 COALESCE(挂号.assistant_id, 诊单.assistant_id) 映射到展示「中心」,否则回退接诊医生 doctor_id;仍无法归属则计入「未归属中心」行。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4;NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag/creator 两套口径均按订单计,非挂号条数。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4;NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(tag 时按诊单医助收窄),非挂号条数。
|
||||
* - 合计业绩 = 区间内 zyt_tcm_prescription_order.amount 求和(排除已取消(4) 与软删),按 dg.assistant_id 归属到「中心」展示部门;
|
||||
* 无法归属到任一展示部门的金额单列「未归属中心」行;合计行 = 与列表同条件的全表 SUM(不限部门)
|
||||
* · 时间窗与业绩条件与列表 stats_order_amount_performance 一致;列表若另有患者/单号等筛选或账号数据域收窄,金额可能仍不同
|
||||
@@ -311,7 +313,7 @@ class YejiStatsLogic
|
||||
);
|
||||
$performance = $perfBoth['all'];
|
||||
$channelPerformance = $perfBoth['scoped'];
|
||||
// 成交单数(全量):与合计业绩同 WHERE,按 creator_id 归属部门,合计用全表 COUNT
|
||||
// 成交单数(全量):与合计业绩同 WHERE,按诊单医助归属部门,合计用全表 COUNT
|
||||
$dealOrderByDept = self::aggregateDealOrderCount($startTs, $endTs, $adminToPrimary, null, null);
|
||||
$globalDealOrderCount = self::countGlobalDealOrders($startTs, $endTs);
|
||||
|
||||
@@ -485,7 +487,7 @@ class YejiStatsLogic
|
||||
. ($dataScopeRestricted
|
||||
? ' 当前账号受角色「数据范围」限制,仅统计可见成员及其归属展示中心;合计与表格一致,不含权限范围外数据。'
|
||||
: '');
|
||||
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入(与订单列表 stats_order_amount_performance 一致)。无法归入「中心」展示部门的创建人业绩计入「未归属中心」行;合计=全表 SUM。**投放成本**按进线占比分摊至各部门。';
|
||||
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入(与订单列表 stats_order_amount_performance 一致)。无法归入「中心」展示部门的诊单医助业绩(及无诊单医助的订单)计入「未归属中心」行;合计=全表 SUM。**投放成本**按进线占比分摊至各部门。';
|
||||
|
||||
return [
|
||||
'start_date' => $startDate,
|
||||
@@ -1440,7 +1442,7 @@ class YejiStatsLogic
|
||||
/**
|
||||
* 接诊诊单聚合:返回两组按部门归属的接诊计数。
|
||||
* - all: 区间内 status=3 挂号条数按部门归属(与列表计数一致),不含「仅 role_id=2」等额外限制。
|
||||
* - scoped: 选定渠道时「{渠道}成交单」= **非取消业务订单条数**(与 {渠道}业绩同源):有 dict(channels) 走诊单医助+EXISTS 口径;否则走 tag(diagnosis_id IN / creator_id IN)与 aggregatePerformance 完全一致。
|
||||
* - scoped: 选定渠道时「{渠道}成交单」= **非取消业务订单条数**(与 {渠道}业绩同源):有 dict(channels) 走诊单医助+EXISTS 口径;否则走 tag(diagnosis_id IN / **诊单医助** IN)与 aggregatePerformance 完全一致。
|
||||
* 未选渠道时 scoped == all(不再额外查一次)。
|
||||
*
|
||||
* @param int[] $appointmentChannelValues dict_data value 整型列表(通常 1=自媒体1)
|
||||
@@ -1483,7 +1485,7 @@ class YejiStatsLogic
|
||||
);
|
||||
}
|
||||
} elseif ($tagDiagIds !== null || $tagAssistantIds !== null) {
|
||||
$scopedByDept = self::aggregateDealOrderCountByTagScope(
|
||||
$scopedByDept = self::aggregateDealOrderCount(
|
||||
$startTs,
|
||||
$endTs,
|
||||
$adminToPrimary,
|
||||
@@ -2327,13 +2329,26 @@ class YejiStatsLogic
|
||||
|
||||
/**
|
||||
* 业绩/成交单统计:仅排除履约已取消 fulfillment_status = 4,其余(含 NULL 与任意其它状态)全部计入。
|
||||
* 与处方订单列表 stats_order_amount_performance(总业绩 − 已取消业绩)一致;避免 SQL 中 `<> 4` 把 NULL 行误排除。
|
||||
* 与看板「合计业绩」、处方列表业绩 extend 口径一致;勿用 `<> 4`,以免误排除 NULL。
|
||||
*
|
||||
* @param \think\db\Query $query
|
||||
* @param \think\db\BaseQuery|\think\Model $query Db::name / Model::where 等查询实例
|
||||
* @param string $tableAlias 主表别名(如 Db 侧 `alias('o')` 传 `o`);无别名时传空字符串
|
||||
*/
|
||||
public static function applyPrescriptionOrderNotCancelledForPerformanceQuery($query, string $tableAlias = ''): void
|
||||
{
|
||||
if ($tableAlias !== '') {
|
||||
$query->whereRaw("NOT ({$tableAlias}.fulfillment_status <=> ?)", [4]);
|
||||
} else {
|
||||
$query->whereRaw('NOT (fulfillment_status <=> ?)', [4]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \think\db\BaseQuery|\think\Model $query
|
||||
*/
|
||||
private static function applyPrescriptionOrderNotCancelledForPerformance($query, string $tableAlias = 'o'): void
|
||||
{
|
||||
$query->whereRaw("NOT ({$tableAlias}.fulfillment_status <=> ?)", [4]);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, $tableAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2365,16 +2380,20 @@ class YejiStatsLogic
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === []) {
|
||||
return 0.0;
|
||||
}
|
||||
$diagTable = self::tableWithPrefix('tcm_diagnosis');
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->join("{$diagTable} dg", 'dg.id = o.diagnosis_id AND dg.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
->where('o.create_time', 'between', [$startTs, $endTs])
|
||||
->where('o.diagnosis_id', '>', 0)
|
||||
->where('dg.assistant_id', '>', 0);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('o.diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagAssistantIds !== null) {
|
||||
$query->whereIn('o.creator_id', array_keys($tagAssistantIds));
|
||||
$query->whereIn('dg.assistant_id', array_keys($tagAssistantIds));
|
||||
}
|
||||
|
||||
return round((float) $query->sum('o.amount'), 1);
|
||||
@@ -2630,62 +2649,10 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 标签口径下的「{渠道}成交单」计数:与 aggregatePerformance 同一套 WHERE(仅剔除已取消=4,含 NULL),
|
||||
* 按 o.creator_id 分组 COUNT 后映射到 primary 部门,用于 tag 路径的「成交单」列展示。
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private static function aggregateDealOrderCountByTagScope(
|
||||
int $startTs,
|
||||
int $endTs,
|
||||
array $adminToPrimary,
|
||||
?array $tagDiagIds,
|
||||
?array $tagAssistantIds
|
||||
): array {
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
return [];
|
||||
}
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === []) {
|
||||
return [];
|
||||
}
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
$query->field(['o.creator_id', 'COUNT(o.id) AS cnt'])
|
||||
->group('o.creator_id');
|
||||
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('o.diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagAssistantIds !== null) {
|
||||
$query->whereIn('o.creator_id', array_keys($tagAssistantIds));
|
||||
}
|
||||
|
||||
$rows = $query->select()->toArray();
|
||||
if ($rows === []) {
|
||||
return [];
|
||||
}
|
||||
$byDept = [];
|
||||
foreach ($rows as $r) {
|
||||
$cid = (int) ($r['creator_id'] ?? 0);
|
||||
if ($cid <= 0) {
|
||||
continue;
|
||||
}
|
||||
$primary = (int) ($adminToPrimary[$cid] ?? 0);
|
||||
if ($primary <= 0) {
|
||||
continue;
|
||||
}
|
||||
$byDept[$primary] = ($byDept[$primary] ?? 0) + (int) $r['cnt'];
|
||||
}
|
||||
|
||||
return $byDept;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单次业绩聚合查询。WHERE: delete_time IS NULL AND NOT(fulfillment <=> 4) AND create_time BETWEEN ...
|
||||
* 可选 diagnosis_id IN tagDiagIds、creator_id IN tagAssistantIds。
|
||||
* 单次业绩聚合:与 aggregatePerformanceAmountsByAdmin / aggregateDealOrderCount 同源 ——
|
||||
* INNER 诊单,按 dg.assistant_id SUM(amount) 后映射到展示「中心」(与 PrescriptionOrderLists assistant_dept 一致)。
|
||||
* WHERE: delete_time IS NULL、NOT(fulfillment <=> 4)、create_time 区间、diagnosis_id>0、dg.assistant_id>0;
|
||||
* 可选 tag:diagnosis_id IN、dg.assistant_id IN。
|
||||
*
|
||||
* @return array<int, float>
|
||||
*/
|
||||
@@ -2697,20 +2664,23 @@ class YejiStatsLogic
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === []) {
|
||||
return [];
|
||||
}
|
||||
$diagTable = self::tableWithPrefix('tcm_diagnosis');
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->join("{$diagTable} dg", 'dg.id = o.diagnosis_id AND dg.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
->where('o.create_time', 'between', [$startTs, $endTs])
|
||||
->where('o.diagnosis_id', '>', 0)
|
||||
->where('dg.assistant_id', '>', 0);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
$query->field(['o.creator_id', 'SUM(o.amount) AS amt'])
|
||||
->group('o.creator_id');
|
||||
$query->field(['dg.assistant_id', 'SUM(o.amount) AS amt'])
|
||||
->group('dg.assistant_id');
|
||||
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('o.diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagAssistantIds !== null) {
|
||||
// 兜底口径:限定订单创建人为「跟进过该标签客户的医助」
|
||||
$query->whereIn('o.creator_id', array_keys($tagAssistantIds));
|
||||
$query->whereIn('dg.assistant_id', array_keys($tagAssistantIds));
|
||||
}
|
||||
|
||||
$rows = $query->select()->toArray();
|
||||
@@ -2719,11 +2689,11 @@ class YejiStatsLogic
|
||||
}
|
||||
$byDept = [];
|
||||
foreach ($rows as $r) {
|
||||
$cid = (int) ($r['creator_id'] ?? 0);
|
||||
if ($cid <= 0) {
|
||||
$aid = (int) ($r['assistant_id'] ?? 0);
|
||||
if ($aid <= 0) {
|
||||
continue;
|
||||
}
|
||||
$primary = (int) ($adminToPrimary[$cid] ?? 0);
|
||||
$primary = (int) ($adminToPrimary[$aid] ?? 0);
|
||||
if ($primary <= 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -3059,7 +3029,7 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 「未归属中心」合计业绩按订单创建人拆解(与 aggregatePerformance /「合计业绩」列归属一致:仅 null,null 标签口径,不按渠道收窄)。
|
||||
* 「未归属中心」业绩下钻:按诊单医助汇总无法映射到展示「中心」的金额(与 aggregatePerformance 部门行互补)。
|
||||
*
|
||||
* @return array{
|
||||
* start_date: string,
|
||||
@@ -3078,18 +3048,64 @@ class YejiStatsLogic
|
||||
$startTs = (int) $c['startTs'];
|
||||
$endTs = (int) $c['endTs'];
|
||||
$adminToPrimary = $c['adminToPrimary'];
|
||||
$visibleCreatorIds = null;
|
||||
if (!empty($c['dataScopeRestricted'])) {
|
||||
$visibleCreatorIds = $c['dataScopeVisibleAdminIds'] ?? [];
|
||||
|
||||
$byAdminAmt = self::aggregatePerformanceAmountsByAdmin($startTs, $endTs, null, null);
|
||||
$byAdminCnt = self::aggregateDealOrderCountGroupedByAssistant($startTs, $endTs, null, null);
|
||||
|
||||
$visFlip = null;
|
||||
if (!empty($c['dataScopeRestricted']) && isset($c['dataScopeVisibleAdminIds'])) {
|
||||
$visFlip = array_flip(array_values(array_unique(array_filter(
|
||||
array_map('intval', $c['dataScopeVisibleAdminIds']),
|
||||
static function (int $v): bool {
|
||||
return $v > 0;
|
||||
}
|
||||
))));
|
||||
}
|
||||
$rows = self::aggregateUnassignedByCreator(
|
||||
$startTs,
|
||||
$endTs,
|
||||
$adminToPrimary,
|
||||
null,
|
||||
null,
|
||||
$visibleCreatorIds
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
foreach ($byAdminAmt as $aid => $amt) {
|
||||
$aid = (int) $aid;
|
||||
if ($aid <= 0) {
|
||||
continue;
|
||||
}
|
||||
if ($visFlip !== null && !isset($visFlip[$aid])) {
|
||||
continue;
|
||||
}
|
||||
if ((int) ($adminToPrimary[$aid] ?? 0) > 0) {
|
||||
continue;
|
||||
}
|
||||
$rows[] = [
|
||||
'admin_id' => $aid,
|
||||
'name' => '',
|
||||
'order_count' => (int) ($byAdminCnt[$aid] ?? 0),
|
||||
'amount' => round((float) $amt, 2),
|
||||
];
|
||||
}
|
||||
|
||||
$globalAmt = self::sumGlobalPerformanceAmount($startTs, $endTs);
|
||||
$globalCnt = self::countGlobalDealOrders($startTs, $endTs);
|
||||
$innerAmt = array_sum($byAdminAmt);
|
||||
$innerCnt = array_sum($byAdminCnt);
|
||||
$orphanAmt = round(max(0.0, $globalAmt - $innerAmt), 2);
|
||||
$orphanCnt = max(0, $globalCnt - $innerCnt);
|
||||
if ($orphanAmt >= 0.01 || $orphanCnt > 0) {
|
||||
$rows[] = [
|
||||
'admin_id' => 0,
|
||||
'name' => '',
|
||||
'order_count' => $orphanCnt,
|
||||
'amount' => $orphanAmt,
|
||||
];
|
||||
}
|
||||
|
||||
usort($rows, static function (array $a, array $b): int {
|
||||
$c = $b['amount'] <=> $a['amount'];
|
||||
if ($c !== 0) {
|
||||
return $c;
|
||||
}
|
||||
|
||||
return $b['order_count'] <=> $a['order_count'];
|
||||
});
|
||||
|
||||
$ids = [];
|
||||
foreach ($rows as $row) {
|
||||
if ($row['admin_id'] > 0) {
|
||||
@@ -3100,7 +3116,7 @@ class YejiStatsLogic
|
||||
foreach ($rows as &$row) {
|
||||
$aid = (int) $row['admin_id'];
|
||||
if ($aid <= 0) {
|
||||
$row['name'] = '(无创建人/0)';
|
||||
$row['name'] = '(无诊单或医助未填)';
|
||||
} else {
|
||||
$row['name'] = (string) ($names[$aid] ?? ('#' . $aid));
|
||||
}
|
||||
@@ -3112,80 +3128,10 @@ class YejiStatsLogic
|
||||
'end_date' => (string) $c['endDate'],
|
||||
'channel_code' => (string) $c['channelCode'],
|
||||
'rows' => $rows,
|
||||
'note' => '与看板「合计业绩」列口径一致:按订单创建时间、剔除履约已取消(4);创建人无法映射到任一展示「中心」部门的业绩计入未归属。账号数据权限下仅列出可见创建人相关订单。',
|
||||
'note' => '与看板「未归属中心」补差一致:按订单 create_time、剔除履约已取消(4);诊单医助无法映射到任一展示「中心」计入各行;其余(无诊单/医助为空)汇总在首行。账号数据权限下仅列出可见医助。',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[]|null $visibleCreatorIds 数据权限:仅统计这些创建人(null=不额外限制);须含 creator_id=0 时用 OR 条件已并入查询
|
||||
*
|
||||
* @return list<array{admin_id:int, name:string, order_count:int, amount:float}>
|
||||
*/
|
||||
private static function aggregateUnassignedByCreator(
|
||||
int $startTs,
|
||||
int $endTs,
|
||||
array $adminToPrimary,
|
||||
?array $tagDiagIds,
|
||||
?array $tagAssistantIds,
|
||||
?array $visibleCreatorIds
|
||||
): array {
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
return [];
|
||||
}
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === []) {
|
||||
return [];
|
||||
}
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('o.diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagAssistantIds !== null) {
|
||||
$query->whereIn('o.creator_id', array_keys($tagAssistantIds));
|
||||
}
|
||||
if ($visibleCreatorIds !== null) {
|
||||
$visibleCreatorIds = array_values(array_unique(array_filter(array_map('intval', $visibleCreatorIds), static fn (int $v): bool => $v > 0)));
|
||||
if ($visibleCreatorIds === []) {
|
||||
return [];
|
||||
}
|
||||
$ph = implode(',', array_fill(0, count($visibleCreatorIds), '?'));
|
||||
$query->whereRaw("(o.creator_id = 0 OR o.creator_id IN ({$ph}))", $visibleCreatorIds);
|
||||
}
|
||||
$query->field(['o.creator_id', 'COUNT(*) AS order_count', 'SUM(o.amount) AS amount'])
|
||||
->group('o.creator_id');
|
||||
$dbRows = $query->select()->toArray();
|
||||
if ($dbRows === []) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($dbRows as $r) {
|
||||
$cid = (int) ($r['creator_id'] ?? 0);
|
||||
$primary = (int) ($adminToPrimary[$cid] ?? 0);
|
||||
if ($primary > 0) {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'admin_id' => $cid,
|
||||
'name' => '',
|
||||
'order_count' => (int) ($r['order_count'] ?? 0),
|
||||
'amount' => round((float) ($r['amount'] ?? 0), 2),
|
||||
];
|
||||
}
|
||||
usort($out, static function (array $a, array $b): int {
|
||||
$c = $b['amount'] <=> $a['amount'];
|
||||
if ($c !== 0) {
|
||||
return $c;
|
||||
}
|
||||
|
||||
return $b['order_count'] <=> $a['order_count'];
|
||||
});
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $ids
|
||||
*
|
||||
@@ -3219,4 +3165,156 @@ class YejiStatsLogic
|
||||
|
||||
return $prefix . $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号字典 channels:与渠道配置名称匹配的 value 字符串列表(用于 ap.channel_source)
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function appointmentChannelDictStringValuesForDrawer(?array $channelInfo): array
|
||||
{
|
||||
if ($channelInfo === null) {
|
||||
return [];
|
||||
}
|
||||
$names = array_values(array_unique(array_filter([
|
||||
trim((string) ($channelInfo['channel_name'] ?? '')),
|
||||
trim((string) ($channelInfo['source_tag_name'] ?? '')),
|
||||
])));
|
||||
if ($names === []) {
|
||||
return [];
|
||||
}
|
||||
$vals = Db::name('dict_data')->where('type_value', 'channels')->whereIn('name', $names)->column('value');
|
||||
$out = [];
|
||||
foreach ($vals as $v) {
|
||||
$s = trim((string) $v);
|
||||
if ($s !== '') {
|
||||
$out[$s] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($out);
|
||||
}
|
||||
|
||||
/** @var bool|null 进程内缓存:doctor_appointment 是否已加 channel_source 列 */
|
||||
private static ?bool $doctorAppointmentHasChannelSourceColumn = null;
|
||||
|
||||
private static function doctorAppointmentHasChannelSourceColumn(): bool
|
||||
{
|
||||
if (self::$doctorAppointmentHasChannelSourceColumn !== null) {
|
||||
return self::$doctorAppointmentHasChannelSourceColumn;
|
||||
}
|
||||
try {
|
||||
$fields = Db::name('doctor_appointment')->getTableFields();
|
||||
self::$doctorAppointmentHasChannelSourceColumn = is_array($fields)
|
||||
&& in_array('channel_source', $fields, true);
|
||||
} catch (\Throwable) {
|
||||
self::$doctorAppointmentHasChannelSourceColumn = false;
|
||||
}
|
||||
|
||||
return self::$doctorAppointmentHasChannelSourceColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业绩看板业务订单侧栏:按 channel_code 收窄(与 overview「{渠道}业绩」同患者判定;含 ap.channels 与 ap.channel_source)
|
||||
*
|
||||
* @param Query $query PrescriptionOrder 主表查询
|
||||
* @param array<string, mixed> $params
|
||||
*/
|
||||
public static function applyYejiDrawerChannelFilterToPrescriptionOrderQuery(Query $query, array $params): void
|
||||
{
|
||||
if ((int) ($params['yeji_order_drawer'] ?? 0) !== 1) {
|
||||
return;
|
||||
}
|
||||
$channelCode = trim((string) ($params['channel_code'] ?? ''));
|
||||
if ($channelCode === '') {
|
||||
return;
|
||||
}
|
||||
$startDate = '';
|
||||
$endDate = '';
|
||||
$st = trim((string) ($params['start_time'] ?? ''));
|
||||
if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $st, $m)) {
|
||||
$startDate = $m[1];
|
||||
}
|
||||
$et = trim((string) ($params['end_time'] ?? ''));
|
||||
if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $et, $m)) {
|
||||
$endDate = $m[1];
|
||||
}
|
||||
if ($startDate === '' || $endDate === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$ctx = self::resolveYejiContext([
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'channel_code' => $channelCode,
|
||||
'dept_ids' => null,
|
||||
]);
|
||||
|
||||
if (!$ctx['channelFilterActive'] || $ctx['channelInfo'] === null) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$poTbl = (new PrescriptionOrder())->getTable();
|
||||
$apTable = self::tableWithPrefix('doctor_appointment');
|
||||
$adminRoleTable = self::tableWithPrefix('admin_role');
|
||||
|
||||
$norm = self::normalizeAppointmentChannelValues($ctx['appointmentChannelValues']);
|
||||
$srcStrings = self::appointmentChannelDictStringValuesForDrawer($ctx['channelInfo']);
|
||||
|
||||
if ($norm !== [] || $srcStrings !== []) {
|
||||
$subParts = [];
|
||||
$subBind = [];
|
||||
if ($norm !== []) {
|
||||
$strVals = array_values(array_unique(array_map(static fn (int $v): string => (string) $v, $norm)));
|
||||
$ph = implode(',', array_fill(0, count($strVals), '?'));
|
||||
$subParts[] = "ap.channels IN ({$ph})";
|
||||
$subBind = array_merge($subBind, $strVals);
|
||||
}
|
||||
if ($srcStrings !== [] && self::doctorAppointmentHasChannelSourceColumn()) {
|
||||
$ph = implode(',', array_fill(0, count($srcStrings), '?'));
|
||||
$subParts[] = "ap.channel_source IN ({$ph})";
|
||||
$subBind = array_merge($subBind, $srcStrings);
|
||||
}
|
||||
if ($subParts !== []) {
|
||||
$innerOr = implode(' OR ', $subParts);
|
||||
$existsSql = "EXISTS (SELECT 1 FROM {$apTable} ap INNER JOIN {$adminRoleTable} ar "
|
||||
. "ON ar.admin_id = ap.assistant_id AND ar.role_id = 2 WHERE ap.patient_id = `{$poTbl}`.`diagnosis_id` "
|
||||
. "AND ap.status = 3 AND ({$innerOr}))";
|
||||
$query->whereRaw($existsSql, $subBind);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$tagDiagIds = $ctx['tagDiagIds'];
|
||||
$tagAssistantIds = $ctx['tagAssistantIds'];
|
||||
$tagFallback = $ctx['tagFallback'];
|
||||
$tagFilterId = (string) ($ctx['tagFilterId'] ?? '');
|
||||
if ($tagFilterId === '') {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === [] && $tagFallback) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagFallback && $tagAssistantIds !== null && $tagAssistantIds !== []) {
|
||||
$query->whereIn('creator_id', array_map('intval', array_keys($tagAssistantIds)));
|
||||
}
|
||||
if ($tagDiagIds === null && !($tagFallback && $tagAssistantIds !== null && $tagAssistantIds !== [])) {
|
||||
$query->whereRaw('0 = 1');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user