This commit is contained in:
Your Name
2026-08-07 09:16:15 +08:00
parent 8bbd6f7885
commit 16d301f302
354 changed files with 967 additions and 456 deletions
@@ -30,7 +30,7 @@ class PerformanceDashboardLogic
/**
* @return array<string, mixed>
*/
public static function overview(int $adminId, array $adminInfo): array
public static function overview(int $adminId, array $adminInfo, array $params = []): array
{
$today = date('Y-m-d');
$yesterday = date('Y-m-d', strtotime('-1 day'));
@@ -49,9 +49,15 @@ class PerformanceDashboardLogic
/** @var array<int>|null $visibleAdminIds */
$visibleAdminIds = $scope['_visible_admin_ids'];
unset($scope['_visible_admin_ids']);
$rankingDeptId = self::resolveRankingDeptId(
max(0, (int) ($params['ranking_dept_id'] ?? 0)),
$adminId,
$adminInfo
);
$orderDaily = self::loadPerformanceOrderDaily($previousMonthStart, $today, $visibleAdminIds);
$personalOrderDaily = self::loadPerformanceOrderDaily($monthStart, $today, [$adminId]);
$lowAmountPaymentDaily = self::loadLowAmountPaymentDaily($yesterday, $today, $visibleAdminIds);
$monthAmount = self::sumDailyMetric($orderDaily, $monthStart, $today, 'amount');
$previousMonthAmount = self::sumDailyMetric(
@@ -92,7 +98,13 @@ class PerformanceDashboardLogic
'start_date' => $today,
'end_date' => $today,
], $adminId, $adminInfo);
$appointmentRanking = self::buildAppointmentRanking($adminId, $adminInfo, $scope);
$appointmentRanking = self::buildAppointmentRanking(
$adminId,
$adminInfo,
$scope,
$visibleAdminIds,
$rankingDeptId
);
$performanceRanking = self::buildPerformanceRanking(
is_array($todayPerformanceOverview['rows'] ?? null) ? $todayPerformanceOverview['rows'] : []
);
@@ -114,6 +126,8 @@ class PerformanceDashboardLogic
$yesterdayOrderCount = (int) self::dailyMetric($orderDaily, $yesterday, 'count');
$todayOrderAmount = self::dailyMetric($orderDaily, $today, 'amount');
$yesterdayOrderAmount = self::dailyMetric($orderDaily, $yesterday, 'amount');
$todayLowAmountPaymentCount = (int) self::dailyMetric($lowAmountPaymentDaily, $today, 'count');
$yesterdayLowAmountPaymentCount = (int) self::dailyMetric($lowAmountPaymentDaily, $yesterday, 'count');
$todayPaidAppointmentCount = self::paidAppointmentCountWithCenterRule($todayOverview);
$yesterdayPaidAppointmentCount = self::paidAppointmentCountWithCenterRule($yesterdayOverview);
$todayPaidAppointmentRate = self::percent($todayPaidAppointmentCount, $todayAddFansCount);
@@ -147,6 +161,7 @@ class PerformanceDashboardLogic
'today' => [
'add_fans_count' => $todayAddFansCount,
'appointment_total_count' => $todayAppointmentCount,
'low_amount_payment_count' => $todayLowAmountPaymentCount,
'interview_count' => $todayInterviewCount,
// 保留原响应字段名以兼容已发布前端,数值含义已统一为“计入业绩的业务订单”。
'completed_order_count' => $todayOrderCount,
@@ -160,6 +175,10 @@ class PerformanceDashboardLogic
$todayAppointmentCount,
$yesterdayAppointmentCount
),
'low_amount_payment_count' => self::buildComparison(
$todayLowAmountPaymentCount,
$yesterdayLowAmountPaymentCount
),
'interview_count' => self::buildComparison($todayInterviewCount, $yesterdayInterviewCount),
'completed_order_count' => self::buildComparison($todayOrderCount, $yesterdayOrderCount),
'completed_order_amount' => self::buildComparison($todayOrderAmount, $yesterdayOrderAmount),
@@ -181,13 +200,17 @@ class PerformanceDashboardLogic
'items' => $performanceRanking,
],
],
'filters' => [
'ranking_departments' => self::rankingDepartmentOptions($adminId, $adminInfo),
'ranking_dept_id' => $rankingDeptId,
],
'trend' => $trend,
'target' => $target,
'meta' => [
'generated_at' => date('Y-m-d H:i:s'),
'timezone' => date_default_timezone_get(),
'commission_note' => '本人业绩按当前账号创建的业务订单统计,排除已取消、拒收和退款订单。',
'rate_note' => '付费挂号率:一中心员工的有效挂号全部按付费挂号,其他部门按 5 元实付挂号;面诊接诊率:有效业务诊单数 / 已完成面诊数。近 7 天挂号排除已取消记录,诊单按订单创建时间统计并排除履约 4/9/10。',
'rate_note' => '付费挂号率:一中心员工的有效挂号全部按付费挂号,其他部门按 5 元实付挂号;接诊率:有效业务诊单数 / 已完成面诊数。近 7 天挂号排除已取消记录,诊单按订单创建时间统计并排除履约 4/9/10。',
],
];
}
@@ -300,6 +323,30 @@ class PerformanceDashboardLogic
];
}
private static function resolveRankingDeptId(int $requestedDeptId, int $adminId, array $adminInfo): int
{
if ($requestedDeptId <= 0) {
return 0;
}
$exists = Dept::where('id', $requestedDeptId)->whereNull('delete_time')->count() > 0;
if (!$exists) {
return 0;
}
$allowedDeptSet = DataScopeService::getAllowedDeptIdSet($adminId, $adminInfo);
if ($allowedDeptSet !== null && !isset($allowedDeptSet[$requestedDeptId])) {
return 0;
}
return $requestedDeptId;
}
/** @return array<int, array<string, mixed>> */
private static function rankingDepartmentOptions(int $adminId, array $adminInfo): array
{
return DeptLogic::getAllDataScoped($adminId, $adminInfo);
}
/**
* @param array<int>|null $visibleAdminIds
* @return array<string, array{amount: float, count: int}>
@@ -345,6 +392,53 @@ class PerformanceDashboardLogic
return $out;
}
/**
* 当日 0 < 实收金额 <= 10 元的已支付订单笔数;退款订单状态为 4,不会进入统计。
*
* @param array<int>|null $visibleAdminIds
* @return array<string, array{count:int}>
*/
private static function loadLowAmountPaymentDaily(
string $startDate,
string $endDate,
?array $visibleAdminIds
): array {
if ($visibleAdminIds === []) {
return [];
}
$query = Db::name('order')
->whereNull('delete_time')
->where('status', 2)
->where('amount', '>', 0)
->where('amount', '<=', 10)
->whereNotNull('payment_time')
->where('payment_time', '<>', '')
->whereBetweenTime(
'payment_time',
$startDate . ' 00:00:00',
$endDate . ' 23:59:59'
);
if ($visibleAdminIds !== null) {
$query->whereIn('creator_id', $visibleAdminIds);
}
$rows = $query
->fieldRaw("DATE(payment_time) AS date_label, COUNT(*) AS item_count")
->group('date_label')
->select()
->toArray();
$out = [];
foreach ($rows as $row) {
$date = (string) ($row['date_label'] ?? '');
if ($date !== '') {
$out[$date] = ['count' => (int) ($row['item_count'] ?? 0)];
}
}
return $out;
}
/**
* @param array<string, array{amount: float, count: int}> $daily
*/
@@ -408,7 +502,13 @@ class PerformanceDashboardLogic
* @param array<string, mixed> $scope
* @return array<string, mixed>
*/
private static function buildAppointmentRanking(int $adminId, array $adminInfo, array $scope): array
private static function buildAppointmentRanking(
int $adminId,
array $adminInfo,
array $scope,
?array $baseVisibleAdminIds,
int $rankingDeptId
): array
{
$roleIds = array_map('intval', $scope['role_ids'] ?? []);
$isDoctorSelf = ($scope['key'] ?? '') === 'self'
@@ -442,7 +542,7 @@ class PerformanceDashboardLogic
];
}
$rankingVisibleAdminIds = null;
$rankingVisibleAdminIds = $baseVisibleAdminIds;
$rankingScopeLabel = (string) ($scope['label'] ?? '');
if (
(int) ($scope['scope_value'] ?? DataScopeService::SCOPE_SELF) === DataScopeService::SCOPE_SELF
@@ -454,6 +554,13 @@ class PerformanceDashboardLogic
$rankingScopeLabel = '本人所属部门';
}
}
if ($rankingDeptId > 0) {
$deptAdminIds = self::departmentAdminIds($rankingDeptId);
$rankingVisibleAdminIds = $rankingVisibleAdminIds === null
? $deptAdminIds
: array_values(array_intersect($rankingVisibleAdminIds, $deptAdminIds));
$rankingScopeLabel = (string) (Dept::where('id', $rankingDeptId)->value('name') ?? $rankingScopeLabel);
}
$assistantStats = ConversionLogic::overview([
'dimension' => 'assistant',
@@ -496,6 +603,23 @@ class PerformanceDashboardLogic
];
}
/** @return int[] */
private static function departmentAdminIds(int $deptId): array
{
$deptIds = array_values(array_unique(array_filter(
array_map('intval', DeptLogic::getSelfAndDescendantIds($deptId)),
static fn (int $id): bool => $id > 0
)));
if ($deptIds === []) {
return [];
}
return array_values(array_unique(array_filter(
array_map('intval', AdminDept::whereIn('dept_id', $deptIds)->column('admin_id')),
static fn (int $id): bool => $id > 0
)));
}
/**
* SELF 医助排行的卡片级例外:只扩展到当前账号所有有效直接部门内的有效医助。
* 不展开子部门,也不改变驾驶舱其它指标的数据范围。