更新
This commit is contained in:
@@ -42,6 +42,8 @@ class ConversionLogic
|
||||
$includeFilters = (int)($params['include_filters'] ?? 0) === 1;
|
||||
// 仅供需要“有效挂号”口径的内部看板调用;默认保持转换统计历史口径不变。
|
||||
$excludeCancelledAppointments = (int)($params['exclude_cancelled_appointments'] ?? 0) === 1;
|
||||
// 一诊综合转化复用处方订单页的业绩口径;其它调用方继续保留历史“双审完成单”口径。
|
||||
$usePerformanceOrderMetrics = strtolower(trim((string)($params['order_metric_mode'] ?? ''))) === 'performance';
|
||||
$dimension = self::normalizeDimension((string)($params['dimension'] ?? 'dept'));
|
||||
$mediaChannelCode = MediaChannelService::normalizeStatsCode((string) ($params['media_channel_code'] ?? ''));
|
||||
$mediaChannel = $mediaChannelCode !== '' ? MediaChannelService::getChannelByCode($mediaChannelCode) : null;
|
||||
@@ -156,7 +158,17 @@ class ConversionLogic
|
||||
$visibleAdminIds,
|
||||
$excludeCancelledAppointments
|
||||
);
|
||||
self::hydrateOrderAndAmountStats($entities, $dimension, $entityIds, $adminToDeptIds, $startTimestamp, $endTimestamp, $mediaChannel, $visibleAdminIds);
|
||||
self::hydrateOrderAndAmountStats(
|
||||
$entities,
|
||||
$dimension,
|
||||
$entityIds,
|
||||
$adminToDeptIds,
|
||||
$startTimestamp,
|
||||
$endTimestamp,
|
||||
$mediaChannel,
|
||||
$visibleAdminIds,
|
||||
$usePerformanceOrderMetrics
|
||||
);
|
||||
// 数据隔离:可见部门 = 可见 admin 所属部门并集;用于 account_cost 与下游 cost 分摊。
|
||||
$visibleDeptIds = self::resolveVisibleDeptIds($visibleAdminIds);
|
||||
[$globalAccountCost, $accountCostDeptIds] = self::hydrateAccountCostStats($entities, $startDate, $endDate, $mediaChannelCode, $visibleDeptIds);
|
||||
@@ -713,8 +725,8 @@ class ConversionLogic
|
||||
/**
|
||||
* @return array<int, int[]>
|
||||
*
|
||||
* 返回 admin_id => [dept_id, ...],每个 admin 的 dept_id 列表按 dept_id 升序排列
|
||||
* (zyt_admin_dept 联合主键 admin_id+dept_id,无独立 id 列)。
|
||||
* 返回 admin_id => [dept_id, ...]。跨部门时优先最深层、再按部门排序,
|
||||
* 与一诊挂号统计和业绩看板的人员归属规则保持一致。
|
||||
* 当 admin 跨部门时,下游 mapEntityIds 只取列表中第一个落在当前 entityIds 内的部门,
|
||||
* 避免同一笔加粉/挂号/接诊被多次累加到不同部门。
|
||||
*/
|
||||
@@ -722,10 +734,38 @@ class ConversionLogic
|
||||
{
|
||||
$rows = Db::name('admin_dept')
|
||||
->field('admin_id, dept_id')
|
||||
->order('admin_id', 'asc')
|
||||
->order('dept_id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$deptRows = Db::name('dept')
|
||||
->whereNull('delete_time')
|
||||
->field('id, pid, sort')
|
||||
->select()
|
||||
->toArray();
|
||||
$deptById = [];
|
||||
foreach ($deptRows as $deptRow) {
|
||||
$deptId = (int)($deptRow['id'] ?? 0);
|
||||
if ($deptId > 0) {
|
||||
$deptById[$deptId] = [
|
||||
'pid' => (int)($deptRow['pid'] ?? 0),
|
||||
'sort' => (int)($deptRow['sort'] ?? 0),
|
||||
];
|
||||
}
|
||||
}
|
||||
$depthCache = [];
|
||||
$depthOf = static function (int $deptId) use (&$depthOf, &$depthCache, $deptById): int {
|
||||
if ($deptId <= 0 || !isset($deptById[$deptId])) {
|
||||
return 0;
|
||||
}
|
||||
if (isset($depthCache[$deptId])) {
|
||||
return $depthCache[$deptId];
|
||||
}
|
||||
$parentId = (int)($deptById[$deptId]['pid'] ?? 0);
|
||||
if ($parentId <= 0 || $parentId === $deptId || !isset($deptById[$parentId])) {
|
||||
return $depthCache[$deptId] = 0;
|
||||
}
|
||||
|
||||
return $depthCache[$deptId] = $depthOf($parentId) + 1;
|
||||
};
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
$adminId = (int)($row['admin_id'] ?? 0);
|
||||
@@ -736,6 +776,21 @@ class ConversionLogic
|
||||
$map[$adminId] ??= [];
|
||||
$map[$adminId][] = $deptId;
|
||||
}
|
||||
foreach ($map as &$deptIds) {
|
||||
usort($deptIds, static function (int $left, int $right) use ($depthOf, $deptById): int {
|
||||
$depthCompare = $depthOf($right) <=> $depthOf($left);
|
||||
if ($depthCompare !== 0) {
|
||||
return $depthCompare;
|
||||
}
|
||||
$sortCompare = (int)($deptById[$right]['sort'] ?? 0) <=> (int)($deptById[$left]['sort'] ?? 0);
|
||||
if ($sortCompare !== 0) {
|
||||
return $sortCompare;
|
||||
}
|
||||
|
||||
return $left <=> $right;
|
||||
});
|
||||
}
|
||||
unset($deptIds);
|
||||
|
||||
return $map;
|
||||
}
|
||||
@@ -930,7 +985,9 @@ class ConversionLogic
|
||||
?array $visibleAdminIds = null,
|
||||
bool $excludeCancelledAppointments = false
|
||||
): void {
|
||||
$sourceExpr = $dimension === 'doctor' ? 'a.doctor_id' : 'u.assistant_id';
|
||||
$sourceExpr = $dimension === 'doctor'
|
||||
? 'a.doctor_id'
|
||||
: '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')
|
||||
@@ -940,7 +997,7 @@ class ConversionLogic
|
||||
->group("{$sourceExpr}, a.patient_id");
|
||||
|
||||
if ($excludeCancelledAppointments) {
|
||||
$query->where('a.status', '<>', 2);
|
||||
$query->whereIn('a.status', [1, 3, 4]);
|
||||
}
|
||||
|
||||
$query->where(static function (Query $subQuery): void {
|
||||
@@ -1059,8 +1116,59 @@ class ConversionLogic
|
||||
int $startTimestamp,
|
||||
int $endTimestamp,
|
||||
?array $mediaChannel,
|
||||
?array $visibleAdminIds = null
|
||||
?array $visibleAdminIds = null,
|
||||
bool $usePerformanceOrderMetrics = false
|
||||
): void {
|
||||
if ($usePerformanceOrderMetrics) {
|
||||
$sourceExpr = $dimension === 'doctor' ? 'rx.creator_id' : 'po.creator_id';
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('po')
|
||||
->leftJoin('tcm_prescription rx', 'rx.id = po.prescription_id AND rx.delete_time IS NULL')
|
||||
->leftJoin('tcm_diagnosis dg', 'dg.id = po.diagnosis_id AND dg.delete_time IS NULL')
|
||||
->whereNull('po.delete_time')
|
||||
->where('po.create_time', 'between', [$startTimestamp, $endTimestamp]);
|
||||
YejiStatsLogic::applyPrescriptionOrderEffectiveAmountQuery($query, 'po');
|
||||
$query
|
||||
->fieldRaw("{$sourceExpr} AS source_admin_id, COUNT(*) AS order_count, SUM(po.amount) AS total_amount")
|
||||
->group($sourceExpr);
|
||||
|
||||
if ($mediaChannel !== null) {
|
||||
$query
|
||||
->leftJoin('order o', 'o.id = po.linked_pay_order_id')
|
||||
->leftJoin('qywx_external_contact q', 'q.external_userid = o.payer_external_userid');
|
||||
MediaChannelService::applyFollowUsersChannelFilter($query, 'q.follow_users', $mediaChannel);
|
||||
}
|
||||
|
||||
foreach ($query->select()->toArray() as $row) {
|
||||
$sourceAdminId = (int)($row['source_admin_id'] ?? 0);
|
||||
$mappedEntityIds = self::mapEntityIds(
|
||||
$dimension,
|
||||
$sourceAdminId,
|
||||
$entityIds,
|
||||
$adminToDeptIds,
|
||||
$visibleAdminIds
|
||||
);
|
||||
if ($mappedEntityIds === []) {
|
||||
continue;
|
||||
}
|
||||
$orderCount = (int)($row['order_count'] ?? 0);
|
||||
$amount = round((float)($row['total_amount'] ?? 0), 2);
|
||||
foreach ($mappedEntityIds as $entityId) {
|
||||
$entities[$entityId]['completed_order_count'] += $orderCount;
|
||||
$entities[$entityId]['completed_order_amount'] = round(
|
||||
(float)$entities[$entityId]['completed_order_amount'] + $amount,
|
||||
2
|
||||
);
|
||||
$entities[$entityId]['business_order_amount'] = round(
|
||||
(float)$entities[$entityId]['business_order_amount'] + $amount,
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$completedSourceExpr = $dimension === 'doctor' ? 'rx.creator_id' : 'rx.assistant_id';
|
||||
$completedQuery = Db::name('tcm_prescription_order')
|
||||
->alias('po')
|
||||
|
||||
Reference in New Issue
Block a user