更新
This commit is contained in:
+112
-30
@@ -127,10 +127,10 @@
|
|||||||
<span class="yeji-table-tabs-head__title">表格统计</span>
|
<span class="yeji-table-tabs-head__title">表格统计</span>
|
||||||
<span class="yeji-table-tabs-head__hint">列较多时可横向滚动浏览;「部门 / 排名」列在滚动时保持固定。</span>
|
<span class="yeji-table-tabs-head__hint">列较多时可横向滚动浏览;「部门 / 排名」列在滚动时保持固定。</span>
|
||||||
</div>
|
</div>
|
||||||
<el-tabs v-model="yejiDisplayTab" class="yeji-view-tabs">
|
<el-tabs v-if="hasAnyYejiTableTab" v-model="yejiDisplayTab" class="yeji-view-tabs">
|
||||||
<el-tab-pane label="医生统计" name="doctor" lazy>
|
<el-tab-pane v-if="canViewDoctorTab" label="医生统计" name="doctor" lazy>
|
||||||
<el-card
|
<el-card
|
||||||
v-if="canViewDoctorDailyStats"
|
v-if="canViewDoctorTab"
|
||||||
class="yeji-panel doctor-daily-card doctor-daily-card--nested"
|
class="yeji-panel doctor-daily-card doctor-daily-card--nested"
|
||||||
shadow="never"
|
shadow="never"
|
||||||
>
|
>
|
||||||
@@ -189,7 +189,7 @@
|
|||||||
<el-empty v-else description="当前账号无医生统计权限" />
|
<el-empty v-else description="当前账号无医生统计权限" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="医助排行榜" name="leaderboard" lazy>
|
<el-tab-pane v-if="canViewLeaderboardTab" label="医助排行榜" name="leaderboard" lazy>
|
||||||
<div
|
<div
|
||||||
v-if="leaderboardBlock || leaderboardsLoading"
|
v-if="leaderboardBlock || leaderboardsLoading"
|
||||||
class="leaderboards-wrap"
|
class="leaderboards-wrap"
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="甄养堂互联网医院诊金" name="zyyt" lazy>
|
<el-tab-pane v-if="canViewZyytTab" label="甄养堂互联网医院诊金" name="zyyt" lazy>
|
||||||
<!-- ── 业绩表(4 张/N 张);目标看板见「目标看板(卡片)」Tab ── -->
|
<!-- ── 业绩表(4 张/N 张);目标看板见「目标看板(卡片)」Tab ── -->
|
||||||
<div v-loading="loading" class="tables-wrap">
|
<div v-loading="loading" class="tables-wrap">
|
||||||
<div v-if="!loading && tables.length === 0" class="empty-tip">
|
<div v-if="!loading && tables.length === 0" class="empty-tip">
|
||||||
@@ -445,12 +445,8 @@
|
|||||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||||
<<<<<<< HEAD
|
|
||||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>(<b>NULL 计入</b>);按<b>订单创建人</b>的人事部门落在该部门子树即计入(表格多行命中时取最深的展示部门)。与<b>合计业绩 / 医助排行榜接诊诊单</b>同口径。选定渠道时本列仍为全量。
|
|
||||||
=======
|
|
||||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>;与列表筛选
|
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>;与列表筛选
|
||||||
<b>assistant_dept_id</b> 时一致——<b>创建人</b>人事部门优先,无创建人则诊单 <b>医助</b>;落在该部门子树即计入(表格多行命中时取最深的展示部门)。与侧栏勾选「与表格业绩对齐」时的集合可能略有差异。选定渠道时本列仍为全量。
|
<b>assistant_dept_id</b> 时一致——<b>创建人</b>人事部门优先,无创建人则诊单 <b>医助</b>;落在该部门子树即计入(表格多行命中时取最深的展示部门)。与侧栏勾选「与表格业绩对齐」时的集合可能略有差异。选定渠道时本列仍为全量。
|
||||||
>>>>>>> master
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-icon class="col-info"><InfoFilled /></el-icon>
|
<el-icon class="col-info"><InfoFilled /></el-icon>
|
||||||
@@ -636,7 +632,7 @@
|
|||||||
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="目标看板(卡片)" name="zyyt_matrix" lazy>
|
<el-tab-pane v-if="canViewTargetMatrixTab" label="目标看板(卡片)" name="zyyt_matrix" lazy>
|
||||||
<div v-loading="targetProgressLoading" class="tp-matrix-wrap">
|
<div v-loading="targetProgressLoading" class="tp-matrix-wrap">
|
||||||
<template v-if="targetProgressCard">
|
<template v-if="targetProgressCard">
|
||||||
<h2 class="tp-matrix__title">{{ targetProgressCard.title }}</h2>
|
<h2 class="tp-matrix__title">{{ targetProgressCard.title }}</h2>
|
||||||
@@ -702,18 +698,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
<el-empty v-else description="当前账号无表格统计 Tab 权限" />
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="yeji-panel yeji-charts-only-card" shadow="never">
|
<el-card class="yeji-panel yeji-charts-only-card" shadow="never">
|
||||||
<div class="yeji-charts-only-head">统计图</div>
|
<div class="yeji-charts-only-head">统计图</div>
|
||||||
<div
|
<div
|
||||||
v-loading="loading || (canViewDoctorDailyStats && doctorDailyLoading) || leaderboardsLoading"
|
v-loading="loading || (canViewDoctorTab && doctorDailyLoading) || (canViewLeaderboardTab && leaderboardsLoading)"
|
||||||
class="yeji-charts-stack"
|
class="yeji-charts-stack"
|
||||||
>
|
>
|
||||||
<template v-if="chartsHasAnyData">
|
<template v-if="chartsHasAnyData">
|
||||||
<section
|
<section
|
||||||
v-if="
|
v-if="
|
||||||
canViewDoctorDailyStats &&
|
canViewDoctorTab &&
|
||||||
(doctorDealBarHasData ||
|
(doctorDealBarHasData ||
|
||||||
doctorRxStackHasData ||
|
doctorRxStackHasData ||
|
||||||
doctorAppointmentPieHasData ||
|
doctorAppointmentPieHasData ||
|
||||||
@@ -855,7 +852,7 @@
|
|||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="leaderboardBlock">
|
<template v-if="canViewLeaderboardTab && leaderboardBlock">
|
||||||
<section
|
<section
|
||||||
v-for="lb in leaderboardChartBlocks"
|
v-for="lb in leaderboardChartBlocks"
|
||||||
:key="'lb-chart-' + lb.dept_id"
|
:key="'lb-chart-' + lb.dept_id"
|
||||||
@@ -898,8 +895,8 @@
|
|||||||
<el-empty
|
<el-empty
|
||||||
v-else-if="
|
v-else-if="
|
||||||
!loading &&
|
!loading &&
|
||||||
(!canViewDoctorDailyStats || !doctorDailyLoading) &&
|
(!canViewDoctorTab || !doctorDailyLoading) &&
|
||||||
!leaderboardsLoading
|
(!canViewLeaderboardTab || !leaderboardsLoading)
|
||||||
"
|
"
|
||||||
class="yeji-charts-empty"
|
class="yeji-charts-empty"
|
||||||
description="当前筛选下暂无图表数据,请调整条件后查询"
|
description="当前筛选下暂无图表数据,请调整条件后查询"
|
||||||
@@ -1318,7 +1315,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Search, RefreshRight, InfoFilled } from '@element-plus/icons-vue'
|
import { Search, RefreshRight, InfoFilled } from '@element-plus/icons-vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
@@ -1337,7 +1334,14 @@ import {
|
|||||||
} from '@/api/stats'
|
} from '@/api/stats'
|
||||||
import { deptPerformanceTargetMonthMatrix } from '@/api/finance'
|
import { deptPerformanceTargetMonthMatrix } from '@/api/finance'
|
||||||
import { prescriptionOrderLists } from '@/api/tcm'
|
import { prescriptionOrderLists } from '@/api/tcm'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import { hasPermission } from '@/utils/perm'
|
||||||
|
|
||||||
|
type YejiDisplayTab = 'doctor' | 'leaderboard' | 'zyyt' | 'zyyt_matrix'
|
||||||
|
|
||||||
|
/** 任一权限命中即可(兼容原有接口按钮权限 + 新增 Tab 独立权限) */
|
||||||
|
function hasAnyPermission(perms: string[]): boolean {
|
||||||
|
return perms.some(p => hasPermission([p]))
|
||||||
|
}
|
||||||
|
|
||||||
interface DeptOption {
|
interface DeptOption {
|
||||||
id: number
|
id: number
|
||||||
@@ -1664,14 +1668,60 @@ const appointmentLinesShowChannelColumn = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
/** 图表 / 数据表切换 */
|
/** 图表 / 数据表切换 */
|
||||||
const yejiDisplayTab = ref<'doctor' | 'leaderboard' | 'zyyt' | 'zyyt_matrix'>('zyyt')
|
const yejiDisplayTab = ref<YejiDisplayTab>('zyyt')
|
||||||
|
|
||||||
/** 与菜单 stats.doctorDailyStats/overview 一致;无权限则不展示医生统计表/图、不请求接口 */
|
/** Tab 可见性:优先认 Tab 独立权限;未配置时兼容原有接口按钮权限,避免破坏已有角色 */
|
||||||
const userStore = useUserStore()
|
const YEJI_TAB_DOCTOR_PERM = 'stats.yejiStats/tabDoctor'
|
||||||
const DOCTOR_DAILY_STATS_PERM = 'stats.doctorDailyStats/overview'
|
const YEJI_TAB_LEADERBOARD_PERM = 'stats.yejiStats/tabLeaderboard'
|
||||||
const canViewDoctorDailyStats = computed(() => {
|
const YEJI_TAB_ZYYT_PERM = 'stats.yejiStats/tabZyyt'
|
||||||
const perms = userStore.perms || []
|
const YEJI_TAB_TARGET_MATRIX_PERM = 'stats.yejiStats/tabTargetMatrix'
|
||||||
return perms.some(p => p === '*' || p === DOCTOR_DAILY_STATS_PERM)
|
|
||||||
|
const canViewDoctorTab = computed(() =>
|
||||||
|
hasAnyPermission([YEJI_TAB_DOCTOR_PERM, 'stats.doctorDailyStats/overview'])
|
||||||
|
)
|
||||||
|
const canViewLeaderboardTab = computed(() =>
|
||||||
|
hasAnyPermission([YEJI_TAB_LEADERBOARD_PERM, 'stats.yejiStats/leaderboard'])
|
||||||
|
)
|
||||||
|
const canViewZyytTab = computed(() =>
|
||||||
|
hasAnyPermission([
|
||||||
|
YEJI_TAB_ZYYT_PERM,
|
||||||
|
'stats.yejiStats/multi',
|
||||||
|
'stats.yejiStats/overview',
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const canViewTargetMatrixTab = computed(() =>
|
||||||
|
hasAnyPermission([
|
||||||
|
YEJI_TAB_TARGET_MATRIX_PERM,
|
||||||
|
'stats.yejiStats/targetMatrix',
|
||||||
|
'stats.yejiStats/multi',
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const hasAnyYejiTableTab = computed(
|
||||||
|
() =>
|
||||||
|
canViewDoctorTab.value ||
|
||||||
|
canViewLeaderboardTab.value ||
|
||||||
|
canViewZyytTab.value ||
|
||||||
|
canViewTargetMatrixTab.value
|
||||||
|
)
|
||||||
|
|
||||||
|
function pickInitialYejiTab(): YejiDisplayTab {
|
||||||
|
if (canViewZyytTab.value) return 'zyyt'
|
||||||
|
if (canViewDoctorTab.value) return 'doctor'
|
||||||
|
if (canViewLeaderboardTab.value) return 'leaderboard'
|
||||||
|
if (canViewTargetMatrixTab.value) return 'zyyt_matrix'
|
||||||
|
return 'zyyt'
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(yejiDisplayTab, tab => {
|
||||||
|
if (tab === 'doctor' && !canViewDoctorTab.value) {
|
||||||
|
yejiDisplayTab.value = pickInitialYejiTab()
|
||||||
|
} else if (tab === 'leaderboard' && !canViewLeaderboardTab.value) {
|
||||||
|
yejiDisplayTab.value = pickInitialYejiTab()
|
||||||
|
} else if (tab === 'zyyt' && !canViewZyytTab.value) {
|
||||||
|
yejiDisplayTab.value = pickInitialYejiTab()
|
||||||
|
} else if (tab === 'zyyt_matrix' && !canViewTargetMatrixTab.value) {
|
||||||
|
yejiDisplayTab.value = pickInitialYejiTab()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const leaderboardChartBlocks = computed(() =>
|
const leaderboardChartBlocks = computed(() =>
|
||||||
@@ -1715,7 +1765,7 @@ const doctorRxStackHasData = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const doctorChartsHasData = computed(() => {
|
const doctorChartsHasData = computed(() => {
|
||||||
if (!canViewDoctorDailyStats.value) {
|
if (!canViewDoctorTab.value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -1731,12 +1781,14 @@ const chartsHasAnyData = computed(() => {
|
|||||||
if (doctorChartsHasData.value) {
|
if (doctorChartsHasData.value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for (const tb of tables.value) {
|
if (canViewZyytTab.value) {
|
||||||
if (yejiDeptChartHasData(tb)) {
|
for (const tb of tables.value) {
|
||||||
return true
|
if (yejiDeptChartHasData(tb)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (leaderboardChartBlocks.value.length > 0) {
|
if (canViewLeaderboardTab.value && leaderboardChartBlocks.value.length > 0) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -2073,6 +2125,12 @@ function getDoctorDailySummaries(param: { columns: any[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadDoctorDailyStats() {
|
async function loadDoctorDailyStats() {
|
||||||
|
if (!canViewDoctorTab.value) {
|
||||||
|
doctorDailyRows.value = []
|
||||||
|
doctorDailyTotal.value = {}
|
||||||
|
doctorDailyRange.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
doctorDailyLoading.value = true
|
doctorDailyLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res: any = await doctorDailyStatsOverview(buildDoctorDailyRequestParams())
|
const res: any = await doctorDailyStatsOverview(buildDoctorDailyRequestParams())
|
||||||
@@ -2211,6 +2269,10 @@ function daysInMonth(ym: string): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadTargetProgress() {
|
async function loadTargetProgress() {
|
||||||
|
if (!canViewTargetMatrixTab.value) {
|
||||||
|
targetProgressCard.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
targetProgressLoading.value = true
|
targetProgressLoading.value = true
|
||||||
targetProgressCard.value = null
|
targetProgressCard.value = null
|
||||||
try {
|
try {
|
||||||
@@ -2458,6 +2520,10 @@ async function loadChannelOptions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadLeaderboard(range: { start: string; end: string }) {
|
async function loadLeaderboard(range: { start: string; end: string }) {
|
||||||
|
if (!canViewLeaderboardTab.value) {
|
||||||
|
leaderboardBlock.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
leaderboardsLoading.value = true
|
leaderboardsLoading.value = true
|
||||||
try {
|
try {
|
||||||
const p: Record<string, any> = {
|
const p: Record<string, any> = {
|
||||||
@@ -2491,6 +2557,18 @@ async function loadLeaderboard(range: { start: string; end: string }) {
|
|||||||
async function loadData() {
|
async function loadData() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
leaderboardBlock.value = null
|
leaderboardBlock.value = null
|
||||||
|
if (!canViewZyytTab.value) {
|
||||||
|
tables.value = []
|
||||||
|
loading.value = false
|
||||||
|
if (canViewDoctorTab.value) {
|
||||||
|
void loadDoctorDailyStats()
|
||||||
|
} else {
|
||||||
|
doctorDailyRows.value = []
|
||||||
|
doctorDailyTotal.value = {}
|
||||||
|
doctorDailyRange.value = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const baseParams: Record<string, any> = {}
|
const baseParams: Record<string, any> = {}
|
||||||
if (selectedDeptIds.value.length > 0) {
|
if (selectedDeptIds.value.length > 0) {
|
||||||
@@ -2542,7 +2620,7 @@ async function loadData() {
|
|||||||
ElMessage.error(any?.msg || any?.message || '加载失败')
|
ElMessage.error(any?.msg || any?.message || '加载失败')
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
if (canViewDoctorDailyStats.value) {
|
if (canViewDoctorTab.value) {
|
||||||
void loadDoctorDailyStats()
|
void loadDoctorDailyStats()
|
||||||
} else {
|
} else {
|
||||||
doctorDailyRows.value = []
|
doctorDailyRows.value = []
|
||||||
@@ -2661,6 +2739,9 @@ function buildOrderDrawerListParams(): Record<string, any> | null {
|
|||||||
if (selectedDeptIds.value.length > 0) {
|
if (selectedDeptIds.value.length > 0) {
|
||||||
params.dept_ids = selectedDeptIds.value.join(',')
|
params.dept_ids = selectedDeptIds.value.join(',')
|
||||||
}
|
}
|
||||||
|
if (f.yejiTableRowDeptIds && f.yejiTableRowDeptIds.length > 0) {
|
||||||
|
params.yeji_table_row_dept_ids = f.yejiTableRowDeptIds.join(',')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
params.assistant_id = f.assistantId
|
params.assistant_id = f.assistantId
|
||||||
if (f.erCenterRevisitSlot !== undefined) {
|
if (f.erCenterRevisitSlot !== undefined) {
|
||||||
@@ -3561,6 +3642,7 @@ function formatLocalYmd(d: Date): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
yejiDisplayTab.value = pickInitialYejiTab()
|
||||||
await Promise.all([loadDeptOptions(), loadChannelOptions()])
|
await Promise.all([loadDeptOptions(), loadChannelOptions()])
|
||||||
loadData()
|
loadData()
|
||||||
loadTargetProgress()
|
loadTargetProgress()
|
||||||
|
|||||||
@@ -107,7 +107,10 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
|||||||
$this->applyServiceChannelFilter($query);
|
$this->applyServiceChannelFilter($query);
|
||||||
$this->applySupplyModeFilter($query);
|
$this->applySupplyModeFilter($query);
|
||||||
if (!$this->shouldBypassListVisibilityForDiagnosisEdit()) {
|
if (!$this->shouldBypassListVisibilityForDiagnosisEdit()) {
|
||||||
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
// 业绩看板按部门点「合计业绩」/复诊下钻:部门或数据域内医助筛选已收口,勿再叠「仅本人订单」
|
||||||
|
if (!$this->shouldSkipCreatorOnlyForYejiDrawer()) {
|
||||||
|
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
||||||
|
}
|
||||||
$this->applyDataScopeForPrescriptionOrder($query);
|
$this->applyDataScopeForPrescriptionOrder($query);
|
||||||
}
|
}
|
||||||
// 业绩看板侧栏等:不展示履约 4/9/10,与 stats 业绩口径一致
|
// 业绩看板侧栏等:不展示履约 4/9/10,与 stats 业绩口径一致
|
||||||
@@ -323,6 +326,50 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
|||||||
return $diagnosisPatientId > 0 && $diagnosisPatientId === $patientId;
|
return $diagnosisPatientId > 0 && $diagnosisPatientId === $patientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业绩看板侧栏:按部门行点合计业绩/接诊诊单(assistant_dept_id),组长等应看到组内全员订单。
|
||||||
|
*/
|
||||||
|
private function isYejiDrawerDeptPerformanceScope(): bool
|
||||||
|
{
|
||||||
|
if ((int) ($this->params['yeji_order_drawer'] ?? 0) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((int) ($this->params['yeji_drawer_match_table_performance'] ?? 0) === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->params['assistant_dept_id']) && (int) $this->params['assistant_dept_id'] > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业绩看板侧栏:部门业绩或复诊下钻查看数据域内其他医助订单时,跳过「仅本人可见」。
|
||||||
|
*/
|
||||||
|
private function shouldSkipCreatorOnlyForYejiDrawer(): bool
|
||||||
|
{
|
||||||
|
if ($this->isYejiDrawerDeptPerformanceScope()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ((int) ($this->params['yeji_order_drawer'] ?? 0) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((int) ($this->params['yeji_er_center_revisit_only'] ?? 0) !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$assistantId = (int) ($this->params['assistant_id'] ?? 0);
|
||||||
|
if ($assistantId <= 0 || $assistantId === (int) $this->adminId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!$this->dataScopeShouldApply()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$visibleIds = $this->getDataScopeVisibleAdminIds();
|
||||||
|
if ($visibleIds === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return in_array($assistantId, $visibleIds, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 医助角色统计:仅「订单创建人=本人」计入
|
* 医助角色统计:仅「订单创建人=本人」计入
|
||||||
*/
|
*/
|
||||||
@@ -341,7 +388,9 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
|||||||
$query = PrescriptionOrder::where($this->searchWhereWithoutCreateTimeRange())->whereNull('delete_time');
|
$query = PrescriptionOrder::where($this->searchWhereWithoutCreateTimeRange())->whereNull('delete_time');
|
||||||
$this->applyDoctorAssistantFilters($query);
|
$this->applyDoctorAssistantFilters($query);
|
||||||
$this->applyPatientKeywordFilter($query);
|
$this->applyPatientKeywordFilter($query);
|
||||||
if (PrescriptionOrderLogic::canViewOrderListStatsAllScope($this->adminInfo)) {
|
if ($this->shouldSkipCreatorOnlyForYejiDrawer()) {
|
||||||
|
$this->applyDataScopeForPrescriptionOrder($query);
|
||||||
|
} elseif (PrescriptionOrderLogic::canViewOrderListStatsAllScope($this->adminInfo)) {
|
||||||
$this->applyDataScopeForPrescriptionOrder($query);
|
$this->applyDataScopeForPrescriptionOrder($query);
|
||||||
} else {
|
} else {
|
||||||
$assistantRid = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2);
|
$assistantRid = (int) Config::get('project.prescription_order_stats_assistant_role_id', 2);
|
||||||
|
|||||||
@@ -243,7 +243,8 @@ class DeptLogic extends BaseLogic
|
|||||||
private static function computeErCenterRevisitFullPack(
|
private static function computeErCenterRevisitFullPack(
|
||||||
array $subtreeDeptIds,
|
array $subtreeDeptIds,
|
||||||
?int $orderStartTs = null,
|
?int $orderStartTs = null,
|
||||||
?int $orderEndTs = null
|
?int $orderEndTs = null,
|
||||||
|
?array $visibleCreatorIds = null
|
||||||
): array {
|
): array {
|
||||||
$emptySlots = [];
|
$emptySlots = [];
|
||||||
$emptyTotals = [];
|
$emptyTotals = [];
|
||||||
@@ -284,6 +285,14 @@ class DeptLogic extends BaseLogic
|
|||||||
/** 复用 buildAssistantCanonicalDeptInSubtree —— 该方法实际是 admin → 子树内 canonical 部门,与 admin 角色无关 */
|
/** 复用 buildAssistantCanonicalDeptInSubtree —— 该方法实际是 admin → 子树内 canonical 部门,与 admin 角色无关 */
|
||||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree($creatorIds, $subtreeSet);
|
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree($creatorIds, $subtreeSet);
|
||||||
|
|
||||||
|
$creatorFlip = null;
|
||||||
|
if ($visibleCreatorIds !== null && $visibleCreatorIds !== []) {
|
||||||
|
$creatorFlip = array_flip(array_values(array_unique(array_filter(
|
||||||
|
array_map('intval', $visibleCreatorIds),
|
||||||
|
static fn (int $id): bool => $id > 0
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
/** @var array<int, array<int, int>> $leafByDeptSlot dept_id => [ slot => cnt ],slot≥2 */
|
/** @var array<int, array<int, int>> $leafByDeptSlot dept_id => [ slot => cnt ],slot≥2 */
|
||||||
$leafByDeptSlot = [];
|
$leafByDeptSlot = [];
|
||||||
/** @var array<int, array<int, int>> $byCreatorSlot 复诊按订单创建人聚合(与诊金/接诊诊单口径一致) */
|
/** @var array<int, array<int, int>> $byCreatorSlot 复诊按订单创建人聚合(与诊金/接诊诊单口径一致) */
|
||||||
@@ -293,6 +302,9 @@ class DeptLogic extends BaseLogic
|
|||||||
for ($i = 1; $i < $n; $i++) {
|
for ($i = 1; $i < $n; $i++) {
|
||||||
$slot = $i + 1;
|
$slot = $i + 1;
|
||||||
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
$cid = (int) ($orders[$i]['creator_id'] ?? 0);
|
||||||
|
if ($creatorFlip !== null && ($cid <= 0 || !isset($creatorFlip[$cid]))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$deptId = $canonicalDept[$cid] ?? null;
|
$deptId = $canonicalDept[$cid] ?? null;
|
||||||
if ($deptId === null || !isset($subtreeSet[$deptId])) {
|
if ($deptId === null || !isset($subtreeSet[$deptId])) {
|
||||||
continue;
|
continue;
|
||||||
@@ -337,14 +349,17 @@ class DeptLogic extends BaseLogic
|
|||||||
*
|
*
|
||||||
* @return array{totals: array<int, int>, slots: array<int, array<int, int>>}
|
* @return array{totals: array<int, int>, slots: array<int, array<int, int>>}
|
||||||
*/
|
*/
|
||||||
public static function getErCenterRevisitRollupIndexedByDept(?int $orderStartTs = null, ?int $orderEndTs = null): array
|
public static function getErCenterRevisitRollupIndexedByDept(
|
||||||
{
|
?int $orderStartTs = null,
|
||||||
|
?int $orderEndTs = null,
|
||||||
|
?array $visibleCreatorIds = null
|
||||||
|
): array {
|
||||||
$erRoots = self::findErCenterRootDeptIds();
|
$erRoots = self::findErCenterRootDeptIds();
|
||||||
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
||||||
if ($subtreeIds === []) {
|
if ($subtreeIds === []) {
|
||||||
return ['totals' => [], 'slots' => []];
|
return ['totals' => [], 'slots' => []];
|
||||||
}
|
}
|
||||||
$pack = self::computeErCenterRevisitFullPack($subtreeIds, $orderStartTs, $orderEndTs);
|
$pack = self::computeErCenterRevisitFullPack($subtreeIds, $orderStartTs, $orderEndTs, $visibleCreatorIds);
|
||||||
|
|
||||||
return ['totals' => $pack['totals'], 'slots' => $pack['slots']];
|
return ['totals' => $pack['totals'], 'slots' => $pack['slots']];
|
||||||
}
|
}
|
||||||
@@ -354,14 +369,17 @@ class DeptLogic extends BaseLogic
|
|||||||
*
|
*
|
||||||
* @return array{totals: array<int, int>, slots: array<int, array<int, int>>}
|
* @return array{totals: array<int, int>, slots: array<int, array<int, int>>}
|
||||||
*/
|
*/
|
||||||
public static function getErCenterRevisitCountsByCreator(?int $orderStartTs = null, ?int $orderEndTs = null): array
|
public static function getErCenterRevisitCountsByCreator(
|
||||||
{
|
?int $orderStartTs = null,
|
||||||
|
?int $orderEndTs = null,
|
||||||
|
?array $visibleCreatorIds = null
|
||||||
|
): array {
|
||||||
$erRoots = self::findErCenterRootDeptIds();
|
$erRoots = self::findErCenterRootDeptIds();
|
||||||
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
$subtreeIds = self::unionErCenterSubtreeDeptIds($erRoots);
|
||||||
if ($subtreeIds === []) {
|
if ($subtreeIds === []) {
|
||||||
return ['totals' => [], 'slots' => []];
|
return ['totals' => [], 'slots' => []];
|
||||||
}
|
}
|
||||||
$pack = self::computeErCenterRevisitFullPack($subtreeIds, $orderStartTs, $orderEndTs);
|
$pack = self::computeErCenterRevisitFullPack($subtreeIds, $orderStartTs, $orderEndTs, $visibleCreatorIds);
|
||||||
$by = $pack['by_creator_slots'];
|
$by = $pack['by_creator_slots'];
|
||||||
$totals = [];
|
$totals = [];
|
||||||
$slots = [];
|
$slots = [];
|
||||||
@@ -436,7 +454,8 @@ class DeptLogic extends BaseLogic
|
|||||||
int $rootDeptId,
|
int $rootDeptId,
|
||||||
int $revisitSlot,
|
int $revisitSlot,
|
||||||
int $orderStartTs,
|
int $orderStartTs,
|
||||||
int $orderEndTs
|
int $orderEndTs,
|
||||||
|
?array $visibleCreatorIds = null
|
||||||
): array {
|
): array {
|
||||||
if ($rootDeptId <= 0 || $orderStartTs <= 0 || $orderEndTs < $orderStartTs) {
|
if ($rootDeptId <= 0 || $orderStartTs <= 0 || $orderEndTs < $orderStartTs) {
|
||||||
return ['rows' => []];
|
return ['rows' => []];
|
||||||
@@ -479,6 +498,13 @@ class DeptLogic extends BaseLogic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree(array_keys($creatorNeed), $erSubtreeSet);
|
$canonicalDept = self::buildAssistantCanonicalDeptInSubtree(array_keys($creatorNeed), $erSubtreeSet);
|
||||||
|
$creatorFlip = null;
|
||||||
|
if ($visibleCreatorIds !== null && $visibleCreatorIds !== []) {
|
||||||
|
$creatorFlip = array_flip(array_values(array_unique(array_filter(
|
||||||
|
array_map('intval', $visibleCreatorIds),
|
||||||
|
static fn (int $id): bool => $id > 0
|
||||||
|
))));
|
||||||
|
}
|
||||||
/** @var array<int, int> $counts */
|
/** @var array<int, int> $counts */
|
||||||
$counts = [];
|
$counts = [];
|
||||||
foreach ($byPatient as $orders) {
|
foreach ($byPatient as $orders) {
|
||||||
@@ -492,6 +518,9 @@ class DeptLogic extends BaseLogic
|
|||||||
if ($cid <= 0) {
|
if ($cid <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ($creatorFlip !== null && !isset($creatorFlip[$cid])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$canon = $canonicalDept[$cid] ?? null;
|
$canon = $canonicalDept[$cid] ?? null;
|
||||||
if ($canon === null || !isset($erSubtreeSet[$canon]) || !isset($descFlip[$canon])) {
|
if ($canon === null || !isset($erSubtreeSet[$canon]) || !isset($descFlip[$canon])) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ class YejiStatsLogic
|
|||||||
/**
|
/**
|
||||||
* 按角色「数据范围」收窄业绩看板上下文(adminToPrimary、tableRowDeptIds)。
|
* 按角色「数据范围」收窄业绩看板上下文(adminToPrimary、tableRowDeptIds)。
|
||||||
* 与 DataScopeService / 列表 HasDataScopeFilter 一致:ALL 不处理;SELF/本部门/本部门及下级 仅保留可见 admin_id。
|
* 与 DataScopeService / 列表 HasDataScopeFilter 一致:ALL 不处理;SELF/本部门/本部门及下级 仅保留可见 admin_id。
|
||||||
|
*
|
||||||
|
* 表格仍保持默认「中心」层级(图1);合计业绩等数值在聚合层按 dataScopeVisibleAdminIds 收窄。
|
||||||
*/
|
*/
|
||||||
private static function applyYejiDataScope(array &$c, int $adminId, array $adminInfo): void
|
private static function applyYejiDataScope(array &$c, int $adminId, array $adminInfo): void
|
||||||
{
|
{
|
||||||
@@ -178,6 +180,17 @@ class YejiStatsLogic
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::narrowYejiContextTableRowsByVisibleAdmins($c, $visibleIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在现有展示行(默认中心层级)上按可见 admin 收窄表格行与台账归属。
|
||||||
|
*
|
||||||
|
* @param int[] $visibleIds
|
||||||
|
*/
|
||||||
|
private static function narrowYejiContextTableRowsByVisibleAdmins(array &$c, array $visibleIds): void
|
||||||
|
{
|
||||||
$flip = array_flip($visibleIds);
|
$flip = array_flip($visibleIds);
|
||||||
$filtered = [];
|
$filtered = [];
|
||||||
foreach ($c['adminToPrimary'] as $aid => $pid) {
|
foreach ($c['adminToPrimary'] as $aid => $pid) {
|
||||||
@@ -402,7 +415,10 @@ class YejiStatsLogic
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
$tableRowDeptIds,
|
$tableRowDeptIds,
|
||||||
$deptById
|
$deptById,
|
||||||
|
null,
|
||||||
|
[],
|
||||||
|
$dataScopeAdminIds
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
// 业绩:部门行「合计业绩」与列表部门摊行一致;「{渠道}业绩」同列表摊行 + 渠道 EXISTS(与抽屉一致)
|
// 业绩:部门行「合计业绩」与列表部门摊行一致;「{渠道}业绩」同列表摊行 + 渠道 EXISTS(与抽屉一致)
|
||||||
@@ -417,7 +433,8 @@ class YejiStatsLogic
|
|||||||
$tableRowDeptIds,
|
$tableRowDeptIds,
|
||||||
$deptById,
|
$deptById,
|
||||||
$listAlignedPack !== null ? $listAlignedPack['amount'] : null,
|
$listAlignedPack !== null ? $listAlignedPack['amount'] : null,
|
||||||
$channelInfo
|
$channelInfo,
|
||||||
|
$dataScopeAdminIds
|
||||||
);
|
);
|
||||||
$performance = $perfBoth['all'];
|
$performance = $perfBoth['all'];
|
||||||
$channelPerformance = $perfBoth['scoped'];
|
$channelPerformance = $perfBoth['scoped'];
|
||||||
@@ -470,7 +487,8 @@ class YejiStatsLogic
|
|||||||
}
|
}
|
||||||
|
|
||||||
@set_time_limit(120);
|
@set_time_limit(120);
|
||||||
$revisitPack = DeptLogic::getErCenterRevisitRollupIndexedByDept($startTs, $endTs);
|
$revisitCreatorFilter = self::resolveYejiRevisitCreatorFilter($c);
|
||||||
|
$revisitPack = DeptLogic::getErCenterRevisitRollupIndexedByDept($startTs, $endTs, $revisitCreatorFilter);
|
||||||
$revisitTotals = $revisitPack['totals'];
|
$revisitTotals = $revisitPack['totals'];
|
||||||
$revisitSlotsByDept = $revisitPack['slots'];
|
$revisitSlotsByDept = $revisitPack['slots'];
|
||||||
|
|
||||||
@@ -807,7 +825,10 @@ class YejiStatsLogic
|
|||||||
|
|
||||||
$erCenterDeptSet = DeptLogic::getErCenterSubtreeDeptIdSet();
|
$erCenterDeptSet = DeptLogic::getErCenterSubtreeDeptIdSet();
|
||||||
/** 医助排行榜复诊:按订单创建人归属(与诊金 / 接诊诊单同口径),不再用 dg.assistant_id */
|
/** 医助排行榜复诊:按订单创建人归属(与诊金 / 接诊诊单同口径),不再用 dg.assistant_id */
|
||||||
$revisitByAssistant = DeptLogic::getErCenterRevisitCountsByCreator($startTs, $endTs);
|
$revisitCreatorFilter = ($dataScopeRestricted && $dataScopeAdminIds !== null && $dataScopeAdminIds !== [])
|
||||||
|
? $dataScopeAdminIds
|
||||||
|
: null;
|
||||||
|
$revisitByAssistant = DeptLogic::getErCenterRevisitCountsByCreator($startTs, $endTs, $revisitCreatorFilter);
|
||||||
|
|
||||||
$assistantIdRows = Db::name('admin_role')->where('role_id', 2)->column('admin_id');
|
$assistantIdRows = Db::name('admin_role')->where('role_id', 2)->column('admin_id');
|
||||||
$assistantSet = [];
|
$assistantSet = [];
|
||||||
@@ -1938,7 +1959,8 @@ class YejiStatsLogic
|
|||||||
$tableRowDeptIds,
|
$tableRowDeptIds,
|
||||||
$deptById,
|
$deptById,
|
||||||
$pack[0],
|
$pack[0],
|
||||||
$pack[1]
|
$pack[1],
|
||||||
|
$dataScopeAdminIds
|
||||||
);
|
);
|
||||||
$scopedByDept = $scopedAligned['count'];
|
$scopedByDept = $scopedAligned['count'];
|
||||||
} else {
|
} else {
|
||||||
@@ -3011,25 +3033,16 @@ class YejiStatsLogic
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$revisitCreatorFilter = self::resolveYejiRevisitCreatorFilter($c);
|
||||||
$pack = DeptLogic::getErCenterRevisitAssistantBreakdownForDept(
|
$pack = DeptLogic::getErCenterRevisitAssistantBreakdownForDept(
|
||||||
$deptId,
|
$deptId,
|
||||||
$revisitSlot,
|
$revisitSlot,
|
||||||
(int) $c['startTs'],
|
(int) $c['startTs'],
|
||||||
(int) $c['endTs']
|
(int) $c['endTs'],
|
||||||
|
$revisitCreatorFilter
|
||||||
);
|
);
|
||||||
$rows = $pack['rows'];
|
$rows = $pack['rows'];
|
||||||
|
|
||||||
if (!empty($c['dataScopeRestricted']) && isset($c['dataScopeVisibleAdminIds'])) {
|
|
||||||
$flip = array_flip($c['dataScopeVisibleAdminIds']);
|
|
||||||
$filtered = [];
|
|
||||||
foreach ($rows as $rw) {
|
|
||||||
if (isset($flip[(int) ($rw['admin_id'] ?? 0)])) {
|
|
||||||
$filtered[] = $rw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rows = $filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
$out['rows'] = $rows;
|
$out['rows'] = $rows;
|
||||||
if ($rows === []) {
|
if ($rows === []) {
|
||||||
$out['note'] = '当前部门与区间下无符合条件的复诊业务订单(口径与看板「二中心复诊」列一致)。';
|
$out['note'] = '当前部门与区间下无符合条件的复诊业务订单(口径与看板「二中心复诊」列一致)。';
|
||||||
@@ -3038,6 +3051,28 @@ class YejiStatsLogic
|
|||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二中心复诊统计:数据范围下仅统计可见创建人的复诊笔数(与合计业绩一致)。
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $c
|
||||||
|
*
|
||||||
|
* @return int[]|null null=不限制;[]=无可见创建人
|
||||||
|
*/
|
||||||
|
private static function resolveYejiRevisitCreatorFilter(array $c): ?array
|
||||||
|
{
|
||||||
|
if (empty($c['dataScopeRestricted']) || !isset($c['dataScopeVisibleAdminIds'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!\is_array($c['dataScopeVisibleAdminIds']) || $c['dataScopeVisibleAdminIds'] === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique(array_filter(
|
||||||
|
array_map('intval', $c['dataScopeVisibleAdminIds']),
|
||||||
|
static fn (int $v): bool => $v > 0
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与 sumPerformance 对应,按 admin 维度返回全量/渠道业绩金额(不按部门折叠)。
|
* 与 sumPerformance 对应,按 admin 维度返回全量/渠道业绩金额(不按部门折叠)。
|
||||||
* 归属固定为 sqlPerformanceAttributionAdminExpr()——按订单创建人。
|
* 归属固定为 sqlPerformanceAttributionAdminExpr()——按订单创建人。
|
||||||
@@ -3711,7 +3746,8 @@ class YejiStatsLogic
|
|||||||
array $tableRowDeptIds = [],
|
array $tableRowDeptIds = [],
|
||||||
array $deptById = [],
|
array $deptById = [],
|
||||||
?array $precomputedListAlignedAmountByDept = null,
|
?array $precomputedListAlignedAmountByDept = null,
|
||||||
?array $channelInfo = null
|
?array $channelInfo = null,
|
||||||
|
?array $dataScopeAdminIds = null
|
||||||
): array {
|
): array {
|
||||||
if ($adminToPrimary === []) {
|
if ($adminToPrimary === []) {
|
||||||
return ['all' => [], 'scoped' => []];
|
return ['all' => [], 'scoped' => []];
|
||||||
@@ -3730,7 +3766,10 @@ class YejiStatsLogic
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
$tableRowDeptIds,
|
$tableRowDeptIds,
|
||||||
$deptById
|
$deptById,
|
||||||
|
null,
|
||||||
|
[],
|
||||||
|
$dataScopeAdminIds
|
||||||
);
|
);
|
||||||
$allByDept = [];
|
$allByDept = [];
|
||||||
foreach ($aligned['amount'] as $deptId => $v) {
|
foreach ($aligned['amount'] as $deptId => $v) {
|
||||||
@@ -3770,7 +3809,8 @@ class YejiStatsLogic
|
|||||||
$tableRowDeptIds,
|
$tableRowDeptIds,
|
||||||
$deptById,
|
$deptById,
|
||||||
$pack[0],
|
$pack[0],
|
||||||
$pack[1]
|
$pack[1],
|
||||||
|
$dataScopeAdminIds
|
||||||
);
|
);
|
||||||
$scopedByDept = [];
|
$scopedByDept = [];
|
||||||
foreach ($scopedAligned['amount'] as $deptId => $v) {
|
foreach ($scopedAligned['amount'] as $deptId => $v) {
|
||||||
@@ -4085,7 +4125,8 @@ class YejiStatsLogic
|
|||||||
array $tableRowDeptIds,
|
array $tableRowDeptIds,
|
||||||
array $deptById,
|
array $deptById,
|
||||||
?string $channelScopedExistsSql = null,
|
?string $channelScopedExistsSql = null,
|
||||||
array $channelScopedExistsBindings = []
|
array $channelScopedExistsBindings = [],
|
||||||
|
?array $dataScopeAdminIds = null
|
||||||
): array {
|
): array {
|
||||||
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
if ($tagDiagIds !== null && $tagDiagIds === []) {
|
||||||
return ['count' => [], 'amount' => []];
|
return ['count' => [], 'amount' => []];
|
||||||
@@ -4115,6 +4156,9 @@ class YejiStatsLogic
|
|||||||
$in = implode(',', $ids);
|
$in = implode(',', $ids);
|
||||||
$query->whereRaw("o.creator_id IN ({$in})");
|
$query->whereRaw("o.creator_id IN ({$in})");
|
||||||
}
|
}
|
||||||
|
if ($dataScopeAdminIds !== null && $dataScopeAdminIds !== []) {
|
||||||
|
$query->whereIn('o.creator_id', $dataScopeAdminIds);
|
||||||
|
}
|
||||||
if ($channelScopedExistsSql !== null && $channelScopedExistsSql !== '') {
|
if ($channelScopedExistsSql !== null && $channelScopedExistsSql !== '') {
|
||||||
$query->whereRaw($channelScopedExistsSql, $channelScopedExistsBindings);
|
$query->whereRaw($channelScopedExistsSql, $channelScopedExistsBindings);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -651,7 +651,7 @@ class PrescriptionOrderLogic
|
|||||||
$rows = Order::whereIn('id', $ids)->whereNull('delete_time')
|
$rows = Order::whereIn('id', $ids)->whereNull('delete_time')
|
||||||
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
|
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
|
||||||
->order('id', 'asc')
|
->order('id', 'asc')
|
||||||
->whereIn('status', [2,5])
|
->whereIn('status', [2, 4, 5])
|
||||||
->select()
|
->select()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
@@ -709,7 +709,10 @@ class PrescriptionOrderLogic
|
|||||||
$arr['linked_pay_orders'] = self::linkedPayOrdersPayload($ids);
|
$arr['linked_pay_orders'] = self::linkedPayOrdersPayload($ids);
|
||||||
$sum = 0.0;
|
$sum = 0.0;
|
||||||
foreach ($arr['linked_pay_orders'] as $o) {
|
foreach ($arr['linked_pay_orders'] as $o) {
|
||||||
$sum += (float) ($o['amount'] ?? 0);
|
$st = (int) ($o['status'] ?? 0);
|
||||||
|
if ($st === 2 || $st === 5) {
|
||||||
|
$sum += (float) ($o['amount'] ?? 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$arr['linked_pay_paid_total'] = round($sum, 2);
|
$arr['linked_pay_paid_total'] = round($sum, 2);
|
||||||
$arr['deposit_min_amount'] = self::depositMinAmount();
|
$arr['deposit_min_amount'] = self::depositMinAmount();
|
||||||
@@ -2457,7 +2460,10 @@ class PrescriptionOrderLogic
|
|||||||
$linkedPayOrders = self::linkedPayOrdersPayload($linkedPayOrderIds);
|
$linkedPayOrders = self::linkedPayOrdersPayload($linkedPayOrderIds);
|
||||||
$linkedPayPaidTotal = 0.0;
|
$linkedPayPaidTotal = 0.0;
|
||||||
foreach ($linkedPayOrders as $o) {
|
foreach ($linkedPayOrders as $o) {
|
||||||
$linkedPayPaidTotal += (float) ($o['amount'] ?? 0);
|
$st = (int) ($o['status'] ?? 0);
|
||||||
|
if ($st === 2 || $st === 5) {
|
||||||
|
$linkedPayPaidTotal += (float) ($o['amount'] ?? 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$linkedPayPaidTotal = round($linkedPayPaidTotal, 2);
|
$linkedPayPaidTotal = round($linkedPayPaidTotal, 2);
|
||||||
|
|
||||||
@@ -2468,7 +2474,13 @@ class PrescriptionOrderLogic
|
|||||||
$order->fulfillment_status = $targetFulfillmentStatus;
|
$order->fulfillment_status = $targetFulfillmentStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$refundedPayCount = 0;
|
||||||
try {
|
try {
|
||||||
|
if ($targetFulfillmentStatus === 10 && $linkedPayOrderIds !== []) {
|
||||||
|
$refundedPayCount = Order::whereIn('id', $linkedPayOrderIds)
|
||||||
|
->whereNull('delete_time')
|
||||||
|
->update(['status' => 4]);
|
||||||
|
}
|
||||||
$order->save();
|
$order->save();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
self::$error = $e->getMessage();
|
self::$error = $e->getMessage();
|
||||||
@@ -2484,6 +2496,9 @@ class PrescriptionOrderLogic
|
|||||||
$logLine = '订单完成,状态变更为「' . $label . '」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary;
|
$logLine = '订单完成,状态变更为「' . $label . '」,实付金额更新为 ¥' . $linkedPayPaidTotal . $unassignSummary;
|
||||||
} else {
|
} else {
|
||||||
$logLine = '订单处理完成,状态变更为「' . $label . '」' . $unassignSummary;
|
$logLine = '订单处理完成,状态变更为「' . $label . '」' . $unassignSummary;
|
||||||
|
if ($targetFulfillmentStatus === 10 && $refundedPayCount > 0) {
|
||||||
|
$logLine .= ',关联支付单 ' . $refundedPayCount . ' 笔已标记为已退款';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self::writeLog(
|
self::writeLog(
|
||||||
$id,
|
$id,
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
-- 业绩看板四个 Tab 独立按钮权限(fans/yeji · pid=293)
|
||||||
|
-- Tab 权限用于单独控制 Tab 可见性;前端同时兼容下方已有接口按钮权限,旧角色无需改配即可继续使用。
|
||||||
|
-- 若仅勾选 Tab 权限、未勾选对应接口,Tab 可见但请求会 403:
|
||||||
|
-- tabDoctor → stats.doctorDailyStats/overview
|
||||||
|
-- tabLeaderboard → stats.yejiStats/leaderboard
|
||||||
|
-- tabZyyt → stats.yejiStats/multi、stats.yejiStats/overview
|
||||||
|
-- tabTargetMatrix → stats.yejiStats/multi
|
||||||
|
-- 幂等:按 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', '业绩看板-医生统计', '', 8,
|
||||||
|
'stats.yejiStats/tabDoctor', '', '', '', '',
|
||||||
|
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/tabDoctor');
|
||||||
|
|
||||||
|
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', '业绩看板-医助排行榜', '', 9,
|
||||||
|
'stats.yejiStats/tabLeaderboard', '', '', '', '',
|
||||||
|
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/tabLeaderboard');
|
||||||
|
|
||||||
|
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', '业绩看板-甄养堂诊金', '', 10,
|
||||||
|
'stats.yejiStats/tabZyyt', '', '', '', '',
|
||||||
|
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/tabZyyt');
|
||||||
|
|
||||||
|
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', '业绩看板-目标看板卡片', '', 11,
|
||||||
|
'stats.yejiStats/tabTargetMatrix', '', '', '', '',
|
||||||
|
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/tabTargetMatrix');
|
||||||
Reference in New Issue
Block a user