更新
This commit is contained in:
@@ -96,6 +96,21 @@ export function yejiStatsRevisitBreakdown(params: {
|
||||
return request.get({ url: '/stats.yejiStats/revisitBreakdown', params })
|
||||
}
|
||||
|
||||
/** 被指派数明细:与看板「被指派数」同口径;部门行传 dept_id,医助排行榜传 assistant_id */
|
||||
export function yejiStatsAssignLines(params: {
|
||||
start_date: string
|
||||
end_date: string
|
||||
dept_id?: number
|
||||
assistant_id?: number
|
||||
dept_ids?: string
|
||||
channel_code?: string
|
||||
tag_id?: string
|
||||
page?: number
|
||||
page_size?: number
|
||||
}) {
|
||||
return request.get({ url: '/stats.yejiStats/assignLines', params })
|
||||
}
|
||||
|
||||
export function doctorDailyStatsOverview(params: {
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
|
||||
@@ -283,7 +283,15 @@
|
||||
<td class="col-name">{{ r.name }}</td>
|
||||
<td class="col-consult">{{ formatLeaderboardInt(r.consult_count) }}</td>
|
||||
<td class="col-deal">{{ formatLeaderboardInt(r.deal_order_count) }}</td>
|
||||
<td class="col-assign">{{ formatLeaderboardInt(r.assign_count ?? 0) }}</td>
|
||||
<td class="col-assign" @click.stop="onLeaderboardAssignCellClick(r)">
|
||||
<span
|
||||
v-if="Number(r.assign_count ?? 0) > 0"
|
||||
class="yeji-lead-cell--link"
|
||||
>{{ formatLeaderboardInt(r.assign_count ?? 0) }}</span>
|
||||
<template v-else>{{
|
||||
formatLeaderboardInt(r.assign_count ?? 0)
|
||||
}}</template>
|
||||
</td>
|
||||
<td class="col-appointment" @click.stop="onLeaderboardAppointmentCellClick(r)">
|
||||
<span
|
||||
v-if="Number(r.appointment_count ?? 0) > 0"
|
||||
@@ -561,7 +569,13 @@
|
||||
>{{ formatInt(r.lead_count) }}</span>
|
||||
<template v-else>{{ formatInt(r.lead_count) }}</template>
|
||||
</td>
|
||||
<td>{{ formatInt(r.assign_count ?? 0) }}</td>
|
||||
<td @click.stop="onAssignCountCellClick($event, tb, r)">
|
||||
<span
|
||||
v-if="yejiAssignCountCellClickable(r)"
|
||||
class="yeji-lead-cell--link"
|
||||
>{{ formatInt(r.assign_count ?? 0) }}</span>
|
||||
<template v-else>{{ formatInt(r.assign_count ?? 0) }}</template>
|
||||
</td>
|
||||
<td @click.stop="onYejiRevisitTotalCellClick(tb, r)">
|
||||
<span
|
||||
v-if="r.dept_id > 0 && Number(r.revisit_count ?? 0) > 0"
|
||||
@@ -1356,6 +1370,59 @@
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="assignLinesDialogVisible"
|
||||
width="min(960px, 96vw)"
|
||||
destroy-on-close
|
||||
class="yeji-leadlines-dialog"
|
||||
>
|
||||
<template #header>
|
||||
<div class="yeji-unassigned-dialog__head">
|
||||
<span class="yeji-unassigned-dialog__title">被指派明细</span>
|
||||
<p class="yeji-unassigned-dialog__sub">{{ assignLinesSubtitle }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<p v-if="assignLinesApiNote" class="yeji-unassigned-dialog__note">{{ assignLinesApiNote }}</p>
|
||||
<div v-loading="assignLinesLoading" class="yeji-unassigned-dialog__body">
|
||||
<el-table
|
||||
v-if="assignLinesRows.length > 0 || assignLinesLoading"
|
||||
:data="assignLinesRows"
|
||||
size="small"
|
||||
stripe
|
||||
border
|
||||
max-height="440"
|
||||
class="yeji-unassigned-dialog__table"
|
||||
:empty-text="assignLinesLoading ? '加载中…' : '暂无数据'"
|
||||
>
|
||||
<el-table-column type="index" label="#" width="46" :index="assignLinesIndexMethod" />
|
||||
<el-table-column prop="diagnosis_id" label="诊单ID" width="88" />
|
||||
<el-table-column prop="patient_name" label="患者" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column prop="patient_phone" label="联系电话" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="assistant_name" label="被指派医助" min-width="108" show-overflow-tooltip />
|
||||
<el-table-column prop="assign_count" label="指派次数" width="88" align="right" />
|
||||
<el-table-column prop="last_assign_time_text" label="最近指派时间" min-width="156" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!assignLinesLoading && assignLinesRows.length === 0"
|
||||
description="暂无被指派明细"
|
||||
/>
|
||||
<div v-if="assignLinesCount > 0" class="yeji-leadlines-dialog__pager">
|
||||
<el-pagination
|
||||
:current-page="assignLinesPage"
|
||||
:page-size="assignLinesPageSize"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="assignLinesCount"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:disabled="assignLinesLoading"
|
||||
:hide-on-single-page="false"
|
||||
@current-change="onAssignLinesPageChange"
|
||||
@size-change="onAssignLinesPageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1376,6 +1443,7 @@ import {
|
||||
yejiStatsLeadLines,
|
||||
yejiStatsAppointmentLines,
|
||||
yejiStatsRevisitBreakdown,
|
||||
yejiStatsAssignLines,
|
||||
} from '@/api/stats'
|
||||
import { deptPerformanceTargetMonthMatrix } from '@/api/finance'
|
||||
import { prescriptionOrderLists } from '@/api/tcm'
|
||||
@@ -1491,6 +1559,17 @@ interface YejiLeadLineRow {
|
||||
external_contact_name: string
|
||||
}
|
||||
|
||||
interface YejiAssignLineRow {
|
||||
diagnosis_id: number
|
||||
patient_name: string
|
||||
patient_phone: string
|
||||
assistant_id: number
|
||||
assistant_name: string
|
||||
assign_count: number
|
||||
last_assign_time: number
|
||||
last_assign_time_text: string
|
||||
}
|
||||
|
||||
interface YejiRow {
|
||||
dept_id: number
|
||||
dept_name: string
|
||||
@@ -1709,6 +1788,31 @@ type AppointmentLinesCtx =
|
||||
}
|
||||
const appointmentLinesContext = ref<AppointmentLinesCtx | null>(null)
|
||||
|
||||
const assignLinesDialogVisible = ref(false)
|
||||
const assignLinesLoading = ref(false)
|
||||
const assignLinesSubtitle = ref('')
|
||||
const assignLinesApiNote = ref('')
|
||||
const assignLinesRows = ref<YejiAssignLineRow[]>([])
|
||||
const assignLinesCount = ref(0)
|
||||
const assignLinesPage = ref(1)
|
||||
const assignLinesPageSize = ref(20)
|
||||
type AssignLinesCtx =
|
||||
| {
|
||||
mode: 'assistant'
|
||||
assistant_id: number
|
||||
name: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
| {
|
||||
mode: 'dept'
|
||||
dept_id: number
|
||||
dept_name: string
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
const assignLinesContext = ref<AssignLinesCtx | null>(null)
|
||||
|
||||
const appointmentLinesShowChannelColumn = computed(() =>
|
||||
appointmentLinesRows.value.some(r => String(r.channel_source ?? '') !== '')
|
||||
)
|
||||
@@ -3193,6 +3297,124 @@ function onLeadCountCellClick(ev: MouseEvent, tb: YejiTable, row: YejiRow) {
|
||||
void openLeadLinesDialog(tb, row)
|
||||
}
|
||||
|
||||
function yejiAssignCountCellClickable(r: YejiRow): boolean {
|
||||
return r.dept_id > 0 && Number(r.assign_count ?? 0) > 0
|
||||
}
|
||||
|
||||
function onAssignCountCellClick(ev: MouseEvent, tb: YejiTable, row: YejiRow) {
|
||||
if (!yejiAssignCountCellClickable(row)) {
|
||||
return
|
||||
}
|
||||
ev.stopPropagation()
|
||||
void openAssignLinesDialogForDept(tb, row)
|
||||
}
|
||||
|
||||
function assignLinesIndexMethod(index: number) {
|
||||
return (assignLinesPage.value - 1) * assignLinesPageSize.value + index + 1
|
||||
}
|
||||
|
||||
function buildAssignLinesRequestParams(page: number, pageSize: number): Record<string, string | number> | null {
|
||||
const ctx = assignLinesContext.value
|
||||
if (!ctx) {
|
||||
return null
|
||||
}
|
||||
const p: Record<string, string | number> = {
|
||||
start_date: ctx.start_date,
|
||||
end_date: ctx.end_date,
|
||||
page,
|
||||
page_size: pageSize,
|
||||
}
|
||||
if (ctx.mode === 'dept') {
|
||||
p.dept_id = ctx.dept_id
|
||||
} else {
|
||||
p.assistant_id = ctx.assistant_id
|
||||
}
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
p.dept_ids = selectedDeptIds.value.join(',')
|
||||
}
|
||||
if (selectedChannel.value) {
|
||||
p.channel_code = selectedChannel.value
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
async function fetchAssignLinesPage() {
|
||||
const params = buildAssignLinesRequestParams(assignLinesPage.value, assignLinesPageSize.value)
|
||||
if (!params) {
|
||||
return
|
||||
}
|
||||
assignLinesLoading.value = true
|
||||
try {
|
||||
const res: any = await yejiStatsAssignLines(params as any)
|
||||
assignLinesRows.value = Array.isArray(res?.lists) ? res.lists : []
|
||||
assignLinesCount.value = Number(res?.count ?? 0)
|
||||
assignLinesApiNote.value = typeof res?.note === 'string' ? res.note : ''
|
||||
} catch (e: unknown) {
|
||||
if (axios.isCancel(e)) return
|
||||
const any = e as any
|
||||
ElMessage.error(any?.msg || any?.message || '加载被指派明细失败')
|
||||
assignLinesRows.value = []
|
||||
assignLinesCount.value = 0
|
||||
assignLinesApiNote.value = ''
|
||||
} finally {
|
||||
assignLinesLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function openAssignLinesDialogForDept(tb: YejiTable, row: YejiRow) {
|
||||
assignLinesContext.value = {
|
||||
mode: 'dept',
|
||||
dept_id: row.dept_id,
|
||||
dept_name: row.dept_name,
|
||||
start_date: tb.start_date,
|
||||
end_date: tb.end_date,
|
||||
}
|
||||
assignLinesSubtitle.value = `${row.dept_name} · ${tb.start_date} ~ ${tb.end_date}`
|
||||
assignLinesApiNote.value = ''
|
||||
assignLinesRows.value = []
|
||||
assignLinesCount.value = 0
|
||||
assignLinesPage.value = 1
|
||||
assignLinesPageSize.value = 20
|
||||
assignLinesDialogVisible.value = true
|
||||
await fetchAssignLinesPage()
|
||||
}
|
||||
|
||||
function onLeaderboardAssignCellClick(r: LeaderboardPack['leaderboards'][0]['rows'][0]) {
|
||||
if (!r || r.admin_id <= 0 || !leaderboardBlock.value) {
|
||||
return
|
||||
}
|
||||
if (Number(r.assign_count ?? 0) <= 0) {
|
||||
return
|
||||
}
|
||||
const lb = leaderboardBlock.value
|
||||
assignLinesContext.value = {
|
||||
mode: 'assistant',
|
||||
assistant_id: r.admin_id,
|
||||
name: r.name,
|
||||
start_date: lb.start_date,
|
||||
end_date: lb.end_date,
|
||||
}
|
||||
assignLinesSubtitle.value = `${r.name} · 医助 · ${lb.start_date} ~ ${lb.end_date}`
|
||||
assignLinesApiNote.value = ''
|
||||
assignLinesRows.value = []
|
||||
assignLinesCount.value = 0
|
||||
assignLinesPage.value = 1
|
||||
assignLinesPageSize.value = 20
|
||||
assignLinesDialogVisible.value = true
|
||||
void fetchAssignLinesPage()
|
||||
}
|
||||
|
||||
function onAssignLinesPageChange(p: number) {
|
||||
assignLinesPage.value = p
|
||||
void fetchAssignLinesPage()
|
||||
}
|
||||
|
||||
function onAssignLinesPageSizeChange(size: number) {
|
||||
assignLinesPageSize.value = size
|
||||
assignLinesPage.value = 1
|
||||
void fetchAssignLinesPage()
|
||||
}
|
||||
|
||||
const LEAD_LINES_EXPORT_COLUMNS: { key: keyof YejiLeadLineRow; label: string }[] = [
|
||||
{ key: 'event_time_text', label: '进线时间' },
|
||||
{ key: 'reception_admin_name', label: '接待' },
|
||||
|
||||
@@ -17,6 +17,7 @@ use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
* - GET stats.yejiStats/leadLines 进线数据明细(add_external_contact 逐条)
|
||||
* - GET stats.yejiStats/appointmentLines 医助排行榜「预约诊单」挂号逐条明细
|
||||
* - GET stats.yejiStats/revisitBreakdown 二中心复诊下钻:医助 × 业务订单笔数
|
||||
* - GET stats.yejiStats/assignLines 被指派数明细(医助×诊单去重,与看板同口径)
|
||||
*/
|
||||
class YejiStatsController extends BaseAdminController
|
||||
{
|
||||
@@ -141,4 +142,13 @@ class YejiStatsController extends BaseAdminController
|
||||
|
||||
return $this->data(YejiStatsLogic::revisitDeptAssistantBreakdown($params, $this->adminId, $this->adminInfo));
|
||||
}
|
||||
|
||||
/** 被指派数明细:与看板「被指派数」同口径(区间内非继承指派,医助×诊单去重) */
|
||||
public function assignLines()
|
||||
{
|
||||
@set_time_limit(120);
|
||||
$params = $this->request->get();
|
||||
|
||||
return $this->data(YejiStatsLogic::assignLines($params, $this->adminId, $this->adminInfo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2616,6 +2616,420 @@ class YejiStatsLogic
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 被指派明细:与看板「被指派数」同口径(区间内非继承指派,医助×诊单去重)。
|
||||
*
|
||||
* @param array{
|
||||
* start_date?:string,end_date?:string,dept_ids?:int[]|string,channel_code?:string,tag_id?:string,
|
||||
* assistant_id?:int|string,admin_id?:int|string,dept_id?:int|string,
|
||||
* page?:int|string,page_size?:int|string
|
||||
* } $params
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function assignLines(array $params, int $viewerAdminId = 0, array $viewerAdminInfo = []): array
|
||||
{
|
||||
$c = self::resolveYejiContext($params);
|
||||
if ($viewerAdminId > 0) {
|
||||
self::applyYejiDataScope($c, $viewerAdminId, $viewerAdminInfo);
|
||||
}
|
||||
|
||||
$assistantId = (int) ($params['assistant_id'] ?? $params['admin_id'] ?? 0);
|
||||
|
||||
if ($assistantId <= 0 && array_key_exists('dept_id', $params)) {
|
||||
return self::yejiDeptAssignLinesInner($params, $c);
|
||||
}
|
||||
|
||||
return self::yejiAssistantAssignLinesInner($params, $c, $assistantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, mixed> $c
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function yejiDeptAssignLinesInner(array $params, array $c): array
|
||||
{
|
||||
$deptIdParam = (int) ($params['dept_id'] ?? 0);
|
||||
$channelCode = (string) $c['channelCode'];
|
||||
$deptName = $deptIdParam === 0
|
||||
? '未归属中心'
|
||||
: self::formatYejiDeptRowDisplayName($deptIdParam, $c['deptById']);
|
||||
|
||||
$empty = static function (string $note) use ($c, $channelCode, $deptIdParam, $deptName): array {
|
||||
return self::yejiAssignLinesEmptyPayload($c, $note, 0, '', $deptIdParam, $deptName, $channelCode);
|
||||
};
|
||||
|
||||
if ($deptIdParam <= 0) {
|
||||
return $empty('未归属中心无被指派明细(看板该行为 0)。');
|
||||
}
|
||||
if (!in_array($deptIdParam, $c['tableRowDeptIds'], true)) {
|
||||
return $empty('该部门不在当前展示部门筛选或数据权限范围内。');
|
||||
}
|
||||
|
||||
$erCenterDeptSet = DeptLogic::getErCenterSubtreeDeptIdSet();
|
||||
if ($channelCode !== '' && isset($erCenterDeptSet[$deptIdParam])) {
|
||||
return $empty('与看板一致:二中心及其组织下级在选定渠道下被指派数计 0,无明细。');
|
||||
}
|
||||
|
||||
$targetAssistantIds = [];
|
||||
foreach ($c['adminToPrimary'] as $aid => $primary) {
|
||||
if ((int) $primary === $deptIdParam) {
|
||||
$targetAssistantIds[] = (int) $aid;
|
||||
}
|
||||
}
|
||||
if ($targetAssistantIds === []) {
|
||||
return $empty('当前部门下没有可映射的被指派医助,无明细。');
|
||||
}
|
||||
|
||||
return self::yejiAssignLinesFetch($c, $params, $targetAssistantIds, 0, '', $deptIdParam, $deptName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, mixed> $c
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function yejiAssistantAssignLinesInner(array $params, array $c, int $assistantId): array
|
||||
{
|
||||
$channelCode = (string) $c['channelCode'];
|
||||
|
||||
$assistantName = '';
|
||||
if ($assistantId > 0) {
|
||||
$assistantName = (string) (Db::name('admin')->where('id', $assistantId)->whereNull('delete_time')->value('name') ?: '');
|
||||
}
|
||||
|
||||
$empty = static function (string $note) use ($c, $assistantId, $assistantName, $channelCode): array {
|
||||
return self::yejiAssignLinesEmptyPayload($c, $note, $assistantId, $assistantName, 0, '', $channelCode);
|
||||
};
|
||||
|
||||
if ($assistantId <= 0) {
|
||||
return $empty('请指定有效医助,或传入 dept_id 查看部门汇总明细。');
|
||||
}
|
||||
|
||||
$dataScopeRestricted = !empty($c['dataScopeRestricted']);
|
||||
$dataScopeAdminIds = $dataScopeRestricted && isset($c['dataScopeVisibleAdminIds'])
|
||||
? $c['dataScopeVisibleAdminIds']
|
||||
: null;
|
||||
if ($dataScopeRestricted && $dataScopeAdminIds !== null) {
|
||||
$flip = array_flip($dataScopeAdminIds);
|
||||
if (!isset($flip[$assistantId])) {
|
||||
return $empty('当前账号数据权限下不可查看该医助被指派明细。');
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($c['adminToPrimary'][$assistantId])) {
|
||||
return $empty('该医助不在当前业绩看板展示范围内。');
|
||||
}
|
||||
|
||||
$tagDiagIds = $c['tagDiagIds'];
|
||||
$tagAssistantIds = $c['tagAssistantIds'];
|
||||
$tagFallback = $c['tagFallback'];
|
||||
$scopedAssistants = $tagFallback ? $tagAssistantIds : null;
|
||||
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
return $empty('当前渠道标签下无关联诊单,无被指派明细。');
|
||||
}
|
||||
if ($scopedAssistants !== null && $scopedAssistants === []) {
|
||||
return $empty('当前渠道标签下无关联医助,无被指派明细。');
|
||||
}
|
||||
if ($scopedAssistants !== null && !isset($scopedAssistants[$assistantId])) {
|
||||
return $empty('当前渠道标签口径下该医助无被指派记录(与排行榜「被指派数」一致)。');
|
||||
}
|
||||
|
||||
$erCenterDeptSet = DeptLogic::getErCenterSubtreeDeptIdSet();
|
||||
$primaryDept = (int) ($c['adminToPrimary'][$assistantId] ?? 0);
|
||||
if ($channelCode !== '' && $primaryDept > 0 && isset($erCenterDeptSet[$primaryDept])) {
|
||||
return $empty('与看板一致:二中心及其组织下级医助在选定渠道下被指派数计 0,无明细。');
|
||||
}
|
||||
|
||||
return self::yejiAssignLinesFetch(
|
||||
$c,
|
||||
$params,
|
||||
[$assistantId],
|
||||
$assistantId,
|
||||
$assistantName,
|
||||
0,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $c
|
||||
* @param array<string, mixed> $params
|
||||
* @param list<int> $targetAssistantIds
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function yejiAssignLinesFetch(
|
||||
array $c,
|
||||
array $params,
|
||||
array $targetAssistantIds,
|
||||
int $assistantIdOut,
|
||||
string $assistantNameOut,
|
||||
int $deptIdOut,
|
||||
string $deptNameOut
|
||||
): array {
|
||||
$channelCode = (string) $c['channelCode'];
|
||||
$startTs = (int) $c['startTs'];
|
||||
$endTs = (int) $c['endTs'];
|
||||
$page = max(1, (int) ($params['page'] ?? 1));
|
||||
$limit = min(100, max(1, (int) ($params['page_size'] ?? 20)));
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
$dataScopeRestricted = !empty($c['dataScopeRestricted']);
|
||||
$dataScopeAdminIds = $dataScopeRestricted && isset($c['dataScopeVisibleAdminIds'])
|
||||
? $c['dataScopeVisibleAdminIds']
|
||||
: null;
|
||||
if ($dataScopeAdminIds !== null && $dataScopeAdminIds !== []) {
|
||||
$visFlip = array_flip($dataScopeAdminIds);
|
||||
$targetAssistantIds = array_values(array_filter(
|
||||
$targetAssistantIds,
|
||||
static fn (int $aid): bool => isset($visFlip[$aid])
|
||||
));
|
||||
}
|
||||
|
||||
if ($targetAssistantIds === []) {
|
||||
return self::yejiAssignLinesEmptyPayload(
|
||||
$c,
|
||||
'当前数据权限下无可查看的被指派医助。',
|
||||
$assistantIdOut,
|
||||
$assistantNameOut,
|
||||
$deptIdOut,
|
||||
$deptNameOut,
|
||||
$channelCode
|
||||
);
|
||||
}
|
||||
|
||||
$tagDiagIds = $c['tagDiagIds'];
|
||||
$tagAssistantIds = $c['tagAssistantIds'];
|
||||
$tagFallback = $c['tagFallback'];
|
||||
$scopedAssistants = $tagFallback ? $tagAssistantIds : null;
|
||||
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
return self::yejiAssignLinesEmptyPayload(
|
||||
$c,
|
||||
'当前渠道标签下无关联诊单,无被指派明细。',
|
||||
$assistantIdOut,
|
||||
$assistantNameOut,
|
||||
$deptIdOut,
|
||||
$deptNameOut,
|
||||
$channelCode
|
||||
);
|
||||
}
|
||||
if ($scopedAssistants !== null && $scopedAssistants === []) {
|
||||
return self::yejiAssignLinesEmptyPayload(
|
||||
$c,
|
||||
'当前渠道标签下无关联医助,无被指派明细。',
|
||||
$assistantIdOut,
|
||||
$assistantNameOut,
|
||||
$deptIdOut,
|
||||
$deptNameOut,
|
||||
$channelCode
|
||||
);
|
||||
}
|
||||
|
||||
$buildBaseQuery = static function () use (
|
||||
$c,
|
||||
$targetAssistantIds,
|
||||
$startTs,
|
||||
$endTs,
|
||||
$tagDiagIds,
|
||||
$scopedAssistants
|
||||
) {
|
||||
$diagTable = self::tableWithPrefix('tcm_diagnosis');
|
||||
$q = Db::name('tcm_diagnosis_assign_log')
|
||||
->alias('lg')
|
||||
->join("{$diagTable} dg", 'dg.id = lg.diagnosis_id AND dg.delete_time IS NULL', 'INNER')
|
||||
->where('lg.create_time', 'between', [$startTs, $endTs])
|
||||
->where('lg.to_assistant_id', '>', 0)
|
||||
->where('lg.is_inherit', 0)
|
||||
->whereIn('lg.to_assistant_id', $targetAssistantIds);
|
||||
|
||||
self::applyYejiAssignLogChannelFilters(
|
||||
$q,
|
||||
$tagDiagIds,
|
||||
$scopedAssistants,
|
||||
$c['appointmentChannelValues'],
|
||||
$c['channelFilterActive'],
|
||||
$c['channelInfo']
|
||||
);
|
||||
|
||||
return $q;
|
||||
};
|
||||
|
||||
$countRows = $buildBaseQuery()
|
||||
->field(['lg.to_assistant_id', 'lg.diagnosis_id'])
|
||||
->group('lg.to_assistant_id,lg.diagnosis_id')
|
||||
->select()
|
||||
->toArray();
|
||||
$count = \count($countRows);
|
||||
|
||||
$rawList = $buildBaseQuery()
|
||||
->field([
|
||||
'lg.to_assistant_id',
|
||||
'lg.diagnosis_id',
|
||||
Db::raw('COUNT(*) AS assign_count'),
|
||||
Db::raw('MAX(lg.create_time) AS last_assign_time'),
|
||||
])
|
||||
->group('lg.to_assistant_id,lg.diagnosis_id')
|
||||
->order('last_assign_time', 'desc')
|
||||
->order('lg.diagnosis_id', 'desc')
|
||||
->limit($offset, $limit)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$diagIds = [];
|
||||
$assistantIds = [];
|
||||
foreach ($rawList as $rw) {
|
||||
$diagIds[(int) ($rw['diagnosis_id'] ?? 0)] = true;
|
||||
$assistantIds[(int) ($rw['to_assistant_id'] ?? 0)] = true;
|
||||
}
|
||||
$diagInfo = self::fetchYejiAssignDiagnosisInfo(array_keys($diagIds));
|
||||
$nameMap = $assistantIds !== []
|
||||
? Db::name('admin')->whereIn('id', array_keys($assistantIds))->column('name', 'id')
|
||||
: [];
|
||||
|
||||
$lists = [];
|
||||
foreach ($rawList as $rw) {
|
||||
$did = (int) ($rw['diagnosis_id'] ?? 0);
|
||||
$aid = (int) ($rw['to_assistant_id'] ?? 0);
|
||||
$lastTs = (int) ($rw['last_assign_time'] ?? 0);
|
||||
$lists[] = [
|
||||
'diagnosis_id' => $did,
|
||||
'patient_name' => (string) ($diagInfo[$did]['patient_name'] ?? ''),
|
||||
'patient_phone' => (string) ($diagInfo[$did]['phone'] ?? ''),
|
||||
'assistant_id' => $aid,
|
||||
'assistant_name' => $aid > 0 ? (string) ($nameMap[$aid] ?? ('#' . $aid)) : '—',
|
||||
'assign_count' => (int) ($rw['assign_count'] ?? 0),
|
||||
'last_assign_time' => $lastTs,
|
||||
'last_assign_time_text' => $lastTs > 0 ? date('Y-m-d H:i:s', $lastTs) : '',
|
||||
];
|
||||
}
|
||||
|
||||
$note = '与看板「被指派数」同口径:区间内 `tcm_diagnosis_assign_log` 成功指派(to_assistant_id>0,剔除勾选「继承」),'
|
||||
. '按指派操作时间落区间,「医助 × 诊单」去重、剔除已删诊单。';
|
||||
|
||||
return [
|
||||
'start_date' => (string) $c['startDate'],
|
||||
'end_date' => (string) $c['endDate'],
|
||||
'assistant_id' => $assistantIdOut,
|
||||
'assistant_name' => $assistantNameOut,
|
||||
'dept_id' => $deptIdOut,
|
||||
'dept_name' => $deptNameOut,
|
||||
'channel_code' => $channelCode,
|
||||
'count' => $count,
|
||||
'lists' => $lists,
|
||||
'note' => $note,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $c
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function yejiAssignLinesEmptyPayload(
|
||||
array $c,
|
||||
string $note,
|
||||
int $assistantId,
|
||||
string $assistantName,
|
||||
int $deptId,
|
||||
string $deptName,
|
||||
string $channelCode
|
||||
): array {
|
||||
return [
|
||||
'start_date' => (string) $c['startDate'],
|
||||
'end_date' => (string) $c['endDate'],
|
||||
'assistant_id' => $assistantId,
|
||||
'assistant_name' => $assistantName,
|
||||
'dept_id' => $deptId,
|
||||
'dept_name' => $deptName,
|
||||
'channel_code' => $channelCode,
|
||||
'count' => 0,
|
||||
'lists' => [],
|
||||
'note' => $note,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \think\db\Query $query
|
||||
*/
|
||||
private static function applyYejiAssignLogChannelFilters(
|
||||
$query,
|
||||
?array $tagDiagIds,
|
||||
?array $tagAssistantIds,
|
||||
array $appointmentChannelValues,
|
||||
bool $channelFilterActive,
|
||||
?array $channelInfo
|
||||
): void {
|
||||
if (!$channelFilterActive) {
|
||||
return;
|
||||
}
|
||||
if ($appointmentChannelValues !== []) {
|
||||
$norm = self::normalizeAppointmentChannelValues($appointmentChannelValues);
|
||||
if ($norm === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$pack = self::buildChannelScopedAppointmentExistsSqlAndBindings('dg.id', $norm, $channelInfo);
|
||||
if ($pack === null) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$query->whereRaw($pack[0], $pack[1]);
|
||||
} elseif ($tagDiagIds !== null || $tagAssistantIds !== null) {
|
||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
if ($tagDiagIds !== null) {
|
||||
$query->whereIn('lg.diagnosis_id', $tagDiagIds);
|
||||
}
|
||||
if ($tagAssistantIds !== null) {
|
||||
$query->whereIn('lg.to_assistant_id', array_map('intval', array_keys($tagAssistantIds)));
|
||||
}
|
||||
} else {
|
||||
$query->whereRaw('0 = 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $diagIds
|
||||
*
|
||||
* @return array<int, array{patient_name:string,phone:string}>
|
||||
*/
|
||||
private static function fetchYejiAssignDiagnosisInfo(array $diagIds): array
|
||||
{
|
||||
if ($diagIds === []) {
|
||||
return [];
|
||||
}
|
||||
$rows = Db::name('tcm_diagnosis')
|
||||
->whereIn('id', $diagIds)
|
||||
->field(['id', 'patient_name', 'phone'])
|
||||
->select()
|
||||
->toArray();
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$out[(int) $r['id']] = [
|
||||
'patient_name' => trim((string) ($r['patient_name'] ?? '')),
|
||||
'phone' => trim((string) ($r['phone'] ?? '')),
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* admin → 展示「中心」映射:用于挂号明细按部门筛选(与 aggregateConsults 映射同源)。
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- 业绩看板「被指派数」明细接口(fans/yeji · pid=293)
|
||||
-- 幂等:按 perms 判重
|
||||
|
||||
INSERT INTO `zyt_system_menu`
|
||||
(`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_show`, `is_disable`, `create_time`, `update_time`)
|
||||
SELECT
|
||||
293, 'A', '业绩-被指派明细', '', 12,
|
||||
'stats.yejiStats/assignLines', '', '', '', '',
|
||||
1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()
|
||||
FROM DUAL
|
||||
WHERE EXISTS (SELECT 1 FROM `zyt_system_menu` p WHERE p.id = 293)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `zyt_system_menu` m
|
||||
WHERE m.perms = 'stats.yejiStats/assignLines'
|
||||
);
|
||||
Reference in New Issue
Block a user