调整综合转化统计医生维度口径
This commit is contained in:
@@ -129,7 +129,7 @@
|
|||||||
<el-card class="!border-none" shadow="never">
|
<el-card class="!border-none" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>诊单金额占比</span>
|
<span>{{ amountPieTitle }}</span>
|
||||||
<span class="card-hint">TOP 10</span>
|
<span class="card-hint">TOP 10</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -146,17 +146,17 @@
|
|||||||
<el-card class="!border-none" shadow="never">
|
<el-card class="!border-none" shadow="never">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>加粉占比</span>
|
<span>{{ secondaryPieTitle }}</span>
|
||||||
<span class="card-hint">TOP 10</span>
|
<span class="card-hint">TOP 10</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<v-charts
|
<v-charts
|
||||||
v-if="fanPieHasData"
|
v-if="secondaryPieHasData"
|
||||||
class="stats-chart"
|
class="stats-chart"
|
||||||
:option="fanPieOption"
|
:option="secondaryPieOption"
|
||||||
autoresize
|
autoresize
|
||||||
/>
|
/>
|
||||||
<el-empty v-else description="暂无加粉数据" />
|
<el-empty v-else :description="secondaryPieEmptyText" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -311,10 +311,13 @@ const overview = reactive<Record<string, any>>({
|
|||||||
amounts: [],
|
amounts: [],
|
||||||
order_counts: [],
|
order_counts: [],
|
||||||
fan_counts: [],
|
fan_counts: [],
|
||||||
rois: []
|
rois: [],
|
||||||
|
appointment_counts: [],
|
||||||
|
interview_counts: []
|
||||||
},
|
},
|
||||||
amount_share: [],
|
amount_share: [],
|
||||||
fan_share: []
|
fan_share: [],
|
||||||
|
order_share: []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -392,7 +395,7 @@ const currentEntityId = computed({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const summaryCards: MetricCard[] = [
|
const defaultSummaryCards: MetricCard[] = [
|
||||||
{ key: 'add_fans_count', label: '加粉数', type: 'count' },
|
{ key: 'add_fans_count', label: '加粉数', type: 'count' },
|
||||||
{ key: 'paid_appointment_count', label: '付费挂号', type: 'count' },
|
{ key: 'paid_appointment_count', label: '付费挂号', type: 'count' },
|
||||||
{ key: 'free_appointment_count', label: '免费挂号', type: 'count' },
|
{ key: 'free_appointment_count', label: '免费挂号', type: 'count' },
|
||||||
@@ -410,7 +413,15 @@ const summaryCards: MetricCard[] = [
|
|||||||
{ key: 'roi', label: 'ROI', type: 'ratio' }
|
{ key: 'roi', label: 'ROI', type: 'ratio' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const tableColumns: MetricColumn[] = [
|
const doctorSummaryCards: MetricCard[] = [
|
||||||
|
{ key: 'appointment_total_count', label: '挂号数', type: 'count' },
|
||||||
|
{ key: 'interview_count', label: '面诊数', type: 'count' },
|
||||||
|
{ key: 'completed_order_count', label: '接诊单数', type: 'count' },
|
||||||
|
{ key: 'completed_order_amount', label: '开药金额', type: 'money' },
|
||||||
|
{ key: 'receive_rate', label: '接诊率', type: 'percent' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const defaultTableColumns: MetricColumn[] = [
|
||||||
{ key: 'add_fans_count', label: '加粉数', type: 'count' },
|
{ key: 'add_fans_count', label: '加粉数', type: 'count' },
|
||||||
{ key: 'total_open_count', label: '总开口', type: 'count', placeholder: true },
|
{ key: 'total_open_count', label: '总开口', type: 'count', placeholder: true },
|
||||||
{ key: 'unreplied_count', label: '未回复', type: 'count', placeholder: true },
|
{ key: 'unreplied_count', label: '未回复', type: 'count', placeholder: true },
|
||||||
@@ -431,6 +442,18 @@ const tableColumns: MetricColumn[] = [
|
|||||||
{ key: 'roi', label: 'ROI', type: 'ratio', minWidth: 90 }
|
{ key: 'roi', label: 'ROI', type: 'ratio', minWidth: 90 }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const doctorTableColumns: MetricColumn[] = [
|
||||||
|
{ key: 'appointment_total_count', label: '挂号数', type: 'count' },
|
||||||
|
{ key: 'interview_count', label: '面诊数', type: 'count' },
|
||||||
|
{ key: 'completed_order_count', label: '接诊单数', type: 'count' },
|
||||||
|
{ key: 'completed_order_amount', label: '开药金额', type: 'money', minWidth: 120 },
|
||||||
|
{ key: 'receive_rate', label: '接诊率', type: 'percent', minWidth: 100 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const isDoctorDimension = computed(() => queryParams.dimension === 'doctor')
|
||||||
|
const summaryCards = computed(() => isDoctorDimension.value ? doctorSummaryCards : defaultSummaryCards)
|
||||||
|
const tableColumns = computed(() => isDoctorDimension.value ? doctorTableColumns : defaultTableColumns)
|
||||||
|
|
||||||
const dateRangeText = computed(() => {
|
const dateRangeText = computed(() => {
|
||||||
if (!overview.date_range?.length) return '未选择'
|
if (!overview.date_range?.length) return '未选择'
|
||||||
return `${overview.date_range[0]} 至 ${overview.date_range[1]}`
|
return `${overview.date_range[0]} 至 ${overview.date_range[1]}`
|
||||||
@@ -438,7 +461,14 @@ const dateRangeText = computed(() => {
|
|||||||
|
|
||||||
const rankingChartHasData = computed(() => overview.charts.ranking.names.length > 0)
|
const rankingChartHasData = computed(() => overview.charts.ranking.names.length > 0)
|
||||||
const amountPieHasData = computed(() => overview.charts.amount_share.length > 0)
|
const amountPieHasData = computed(() => overview.charts.amount_share.length > 0)
|
||||||
const fanPieHasData = computed(() => overview.charts.fan_share.length > 0)
|
const secondaryPieHasData = computed(() =>
|
||||||
|
isDoctorDimension.value
|
||||||
|
? (overview.charts.order_share?.length || 0) > 0
|
||||||
|
: (overview.charts.fan_share?.length || 0) > 0
|
||||||
|
)
|
||||||
|
const amountPieTitle = computed(() => isDoctorDimension.value ? '开药金额占比' : '诊单金额占比')
|
||||||
|
const secondaryPieTitle = computed(() => isDoctorDimension.value ? '接诊单数占比' : '加粉占比')
|
||||||
|
const secondaryPieEmptyText = computed(() => isDoctorDimension.value ? '暂无接诊数据' : '暂无加粉数据')
|
||||||
|
|
||||||
const rankingChartData = computed(() => {
|
const rankingChartData = computed(() => {
|
||||||
const ranking = overview.charts?.ranking || {}
|
const ranking = overview.charts?.ranking || {}
|
||||||
@@ -448,56 +478,110 @@ const rankingChartData = computed(() => {
|
|||||||
return {
|
return {
|
||||||
names,
|
names,
|
||||||
amounts: normalize(Array.isArray(ranking.amounts) ? ranking.amounts : []),
|
amounts: normalize(Array.isArray(ranking.amounts) ? ranking.amounts : []),
|
||||||
|
appointmentCounts: normalize(Array.isArray(ranking.appointment_counts) ? ranking.appointment_counts : []),
|
||||||
|
interviewCounts: normalize(Array.isArray(ranking.interview_counts) ? ranking.interview_counts : []),
|
||||||
orderCounts: normalize(Array.isArray(ranking.order_counts) ? ranking.order_counts : []),
|
orderCounts: normalize(Array.isArray(ranking.order_counts) ? ranking.order_counts : []),
|
||||||
fanCounts: normalize(Array.isArray(ranking.fan_counts) ? ranking.fan_counts : []),
|
fanCounts: normalize(Array.isArray(ranking.fan_counts) ? ranking.fan_counts : [])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const rankingChartOption = computed(() => ({
|
const rankingChartOption = computed(() => {
|
||||||
tooltip: { trigger: 'axis' },
|
const common = {
|
||||||
legend: { data: ['诊单金额', '接诊诊单', '加粉数'] },
|
tooltip: { trigger: 'axis' },
|
||||||
grid: { left: 48, right: 24, top: 48, bottom: 48 },
|
grid: { left: 48, right: 24, top: 48, bottom: 48 },
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: rankingChartData.value.names,
|
data: rankingChartData.value.names,
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
interval: 0,
|
interval: 0,
|
||||||
rotate: rankingChartData.value.names.length > 6 ? 25 : 0
|
rotate: rankingChartData.value.names.length > 6 ? 25 : 0
|
||||||
}
|
}
|
||||||
},
|
|
||||||
yAxis: [
|
|
||||||
{ type: 'value', name: '金额 / 数量' }
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '诊单金额',
|
|
||||||
type: 'bar',
|
|
||||||
barMaxWidth: 36,
|
|
||||||
data: rankingChartData.value.amounts,
|
|
||||||
itemStyle: { color: '#4a78ff' }
|
|
||||||
},
|
},
|
||||||
{
|
yAxis: [
|
||||||
name: '接诊诊单',
|
{ type: 'value', name: '金额 / 数量' }
|
||||||
type: 'line',
|
]
|
||||||
smooth: true,
|
}
|
||||||
showSymbol: true,
|
|
||||||
symbolSize: 8,
|
if (!isDoctorDimension.value) {
|
||||||
lineStyle: { width: 3 },
|
return {
|
||||||
data: rankingChartData.value.orderCounts,
|
...common,
|
||||||
itemStyle: { color: '#15b8a6' }
|
legend: { data: ['诊单金额', '接诊诊单', '加粉数'] },
|
||||||
},
|
series: [
|
||||||
{
|
{
|
||||||
name: '加粉数',
|
name: '诊单金额',
|
||||||
type: 'line',
|
type: 'bar',
|
||||||
smooth: true,
|
barMaxWidth: 36,
|
||||||
showSymbol: true,
|
data: rankingChartData.value.amounts,
|
||||||
symbolSize: 8,
|
itemStyle: { color: '#4a78ff' }
|
||||||
lineStyle: { width: 3 },
|
},
|
||||||
data: rankingChartData.value.fanCounts,
|
{
|
||||||
itemStyle: { color: '#ff9f43' }
|
name: '接诊诊单',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
data: rankingChartData.value.orderCounts,
|
||||||
|
itemStyle: { color: '#15b8a6' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '加粉数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
data: rankingChartData.value.fanCounts,
|
||||||
|
itemStyle: { color: '#ff9f43' }
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}))
|
|
||||||
|
return {
|
||||||
|
...common,
|
||||||
|
legend: { data: ['开药金额', '挂号数', '面诊数', '接诊单数'] },
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '开药金额',
|
||||||
|
type: 'bar',
|
||||||
|
barMaxWidth: 36,
|
||||||
|
data: rankingChartData.value.amounts,
|
||||||
|
itemStyle: { color: '#4a78ff' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '挂号数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
data: rankingChartData.value.appointmentCounts,
|
||||||
|
itemStyle: { color: '#15b8a6' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '面诊数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
data: rankingChartData.value.interviewCounts,
|
||||||
|
itemStyle: { color: '#ff9f43' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '接诊单数',
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 8,
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
data: rankingChartData.value.orderCounts,
|
||||||
|
itemStyle: { color: '#7c3aed' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const amountPieOption = computed(() => ({
|
const amountPieOption = computed(() => ({
|
||||||
tooltip: { trigger: 'item' },
|
tooltip: { trigger: 'item' },
|
||||||
@@ -511,7 +595,7 @@ const amountPieOption = computed(() => ({
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '诊单金额',
|
name: amountPieTitle.value,
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['42%', '70%'],
|
radius: ['42%', '70%'],
|
||||||
center: ['50%', '42%'],
|
center: ['50%', '42%'],
|
||||||
@@ -531,7 +615,7 @@ const amountPieOption = computed(() => ({
|
|||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const fanPieOption = computed(() => ({
|
const secondaryPieOption = computed(() => ({
|
||||||
tooltip: { trigger: 'item' },
|
tooltip: { trigger: 'item' },
|
||||||
legend: {
|
legend: {
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
@@ -543,7 +627,7 @@ const fanPieOption = computed(() => ({
|
|||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '加粉数',
|
name: secondaryPieTitle.value,
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['42%', '70%'],
|
radius: ['42%', '70%'],
|
||||||
center: ['50%', '42%'],
|
center: ['50%', '42%'],
|
||||||
@@ -558,7 +642,7 @@ const fanPieOption = computed(() => ({
|
|||||||
labelLayout: {
|
labelLayout: {
|
||||||
hideOverlap: true,
|
hideOverlap: true,
|
||||||
},
|
},
|
||||||
data: overview.charts.fan_share
|
data: isDoctorDimension.value ? overview.charts.order_share : overview.charts.fan_share
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ class ConversionLogic
|
|||||||
$emptyResult = [
|
$emptyResult = [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
'lists' => [],
|
'lists' => [],
|
||||||
'count' => 0,
|
'count' => 0,
|
||||||
'page_no' => $pageNo,
|
'page_no' => $pageNo,
|
||||||
@@ -57,8 +57,8 @@ class ConversionLogic
|
|||||||
'extend' => [
|
'extend' => [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if ($includeFilters) {
|
if ($includeFilters) {
|
||||||
@@ -75,8 +75,8 @@ class ConversionLogic
|
|||||||
$result = [
|
$result = [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
'lists' => [],
|
'lists' => [],
|
||||||
'count' => 0,
|
'count' => 0,
|
||||||
'page_no' => $pageNo,
|
'page_no' => $pageNo,
|
||||||
@@ -84,8 +84,8 @@ class ConversionLogic
|
|||||||
'extend' => [
|
'extend' => [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if ($includeFilters) {
|
if ($includeFilters) {
|
||||||
@@ -124,8 +124,8 @@ class ConversionLogic
|
|||||||
$result = [
|
$result = [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
'lists' => [],
|
'lists' => [],
|
||||||
'count' => 0,
|
'count' => 0,
|
||||||
'page_no' => $pageNo,
|
'page_no' => $pageNo,
|
||||||
@@ -133,8 +133,8 @@ class ConversionLogic
|
|||||||
'extend' => [
|
'extend' => [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary([]),
|
'summary' => self::buildSummary([], $dimension),
|
||||||
'charts' => self::buildCharts([]),
|
'charts' => self::buildCharts([], $dimension),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if ($includeFilters) {
|
if ($includeFilters) {
|
||||||
@@ -191,8 +191,8 @@ class ConversionLogic
|
|||||||
$result = [
|
$result = [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary($allRows),
|
'summary' => self::buildSummary($allRows, $dimension),
|
||||||
'charts' => self::buildCharts($chartRows),
|
'charts' => self::buildCharts($chartRows, $dimension),
|
||||||
'lists' => $pagedRows,
|
'lists' => $pagedRows,
|
||||||
'count' => count($allRows),
|
'count' => count($allRows),
|
||||||
'page_no' => $pageNo,
|
'page_no' => $pageNo,
|
||||||
@@ -200,8 +200,8 @@ class ConversionLogic
|
|||||||
'extend' => [
|
'extend' => [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary($allRows),
|
'summary' => self::buildSummary($allRows, $dimension),
|
||||||
'charts' => self::buildCharts($chartRows),
|
'charts' => self::buildCharts($chartRows, $dimension),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if ($includeFilters) {
|
if ($includeFilters) {
|
||||||
@@ -226,8 +226,8 @@ class ConversionLogic
|
|||||||
$result = [
|
$result = [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary($rows),
|
'summary' => self::buildSummary($rows, $dimension),
|
||||||
'charts' => self::buildCharts($rows),
|
'charts' => self::buildCharts($rows, $dimension),
|
||||||
'lists' => array_slice($rows, $offset, $pageSize),
|
'lists' => array_slice($rows, $offset, $pageSize),
|
||||||
'count' => $count,
|
'count' => $count,
|
||||||
'page_no' => $pageNo,
|
'page_no' => $pageNo,
|
||||||
@@ -235,8 +235,8 @@ class ConversionLogic
|
|||||||
'extend' => [
|
'extend' => [
|
||||||
'dimension' => $dimension,
|
'dimension' => $dimension,
|
||||||
'date_range' => [$startDate, $endDate],
|
'date_range' => [$startDate, $endDate],
|
||||||
'summary' => self::buildSummary($rows),
|
'summary' => self::buildSummary($rows, $dimension),
|
||||||
'charts' => self::buildCharts($rows),
|
'charts' => self::buildCharts($rows, $dimension),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
if ($includeFilters) {
|
if ($includeFilters) {
|
||||||
@@ -1226,7 +1226,7 @@ class ConversionLogic
|
|||||||
$entity['paid_appointment_rate'] = self::percent($paidAppointmentCount, $addFansCount);
|
$entity['paid_appointment_rate'] = self::percent($paidAppointmentCount, $addFansCount);
|
||||||
$entity['open_appointment_rate'] = self::percent($paidAppointmentCount, $totalOpenCount);
|
$entity['open_appointment_rate'] = self::percent($paidAppointmentCount, $totalOpenCount);
|
||||||
$entity['interview_rate'] = self::percent($interviewCount, $appointmentTotalCount);
|
$entity['interview_rate'] = self::percent($interviewCount, $appointmentTotalCount);
|
||||||
$entity['receive_rate'] = self::percent($completedOrderCount, $addFansCount);
|
$entity['receive_rate'] = self::receiveRate($completedOrderCount, $addFansCount, $interviewCount, $dimension);
|
||||||
$entity['interview_receive_rate'] = self::percent($completedOrderCount, $interviewCount);
|
$entity['interview_receive_rate'] = self::percent($completedOrderCount, $interviewCount);
|
||||||
$entity['open_receive_rate'] = self::percent($completedOrderCount, $totalOpenCount);
|
$entity['open_receive_rate'] = self::percent($completedOrderCount, $totalOpenCount);
|
||||||
$entity['avg_unit_price'] = self::safeDivideMoney($completedOrderAmount, $completedOrderCount);
|
$entity['avg_unit_price'] = self::safeDivideMoney($completedOrderAmount, $completedOrderCount);
|
||||||
@@ -1459,7 +1459,7 @@ class ConversionLogic
|
|||||||
$node['paid_appointment_rate'] = self::percent($paidAppointmentCount, $addFansCount);
|
$node['paid_appointment_rate'] = self::percent($paidAppointmentCount, $addFansCount);
|
||||||
$node['open_appointment_rate'] = self::percent($paidAppointmentCount, $totalOpenCount);
|
$node['open_appointment_rate'] = self::percent($paidAppointmentCount, $totalOpenCount);
|
||||||
$node['interview_rate'] = self::percent($interviewCount, $appointmentTotalCount);
|
$node['interview_rate'] = self::percent($interviewCount, $appointmentTotalCount);
|
||||||
$node['receive_rate'] = self::percent($completedOrderCount, $addFansCount);
|
$node['receive_rate'] = self::receiveRate($completedOrderCount, $addFansCount, $interviewCount, 'dept');
|
||||||
$node['interview_receive_rate'] = self::percent($completedOrderCount, $interviewCount);
|
$node['interview_receive_rate'] = self::percent($completedOrderCount, $interviewCount);
|
||||||
$node['open_receive_rate'] = self::percent($completedOrderCount, $totalOpenCount);
|
$node['open_receive_rate'] = self::percent($completedOrderCount, $totalOpenCount);
|
||||||
$node['avg_unit_price'] = self::safeDivideMoney($completedOrderAmount, $completedOrderCount);
|
$node['avg_unit_price'] = self::safeDivideMoney($completedOrderAmount, $completedOrderCount);
|
||||||
@@ -2010,7 +2010,12 @@ class ConversionLogic
|
|||||||
'paid_appointment_rate' => self::percent($paidAppointmentCount, $addFansCount),
|
'paid_appointment_rate' => self::percent($paidAppointmentCount, $addFansCount),
|
||||||
'open_appointment_rate' => self::percent($paidAppointmentCount, $totalOpenCount),
|
'open_appointment_rate' => self::percent($paidAppointmentCount, $totalOpenCount),
|
||||||
'interview_rate' => self::percent($interviewCount, $appointmentTotalCount),
|
'interview_rate' => self::percent($interviewCount, $appointmentTotalCount),
|
||||||
'receive_rate' => self::percent($completedOrderCount, $addFansCount),
|
'receive_rate' => self::receiveRate(
|
||||||
|
$completedOrderCount,
|
||||||
|
$addFansCount,
|
||||||
|
$interviewCount,
|
||||||
|
'member'
|
||||||
|
),
|
||||||
'interview_receive_rate' => self::percent($completedOrderCount, $interviewCount),
|
'interview_receive_rate' => self::percent($completedOrderCount, $interviewCount),
|
||||||
'open_receive_rate' => self::percent($completedOrderCount, $totalOpenCount),
|
'open_receive_rate' => self::percent($completedOrderCount, $totalOpenCount),
|
||||||
'avg_unit_price' => self::safeDivideMoney($completedOrderAmount, $completedOrderCount),
|
'avg_unit_price' => self::safeDivideMoney($completedOrderAmount, $completedOrderCount),
|
||||||
@@ -2149,7 +2154,7 @@ class ConversionLogic
|
|||||||
* @param array<int, array<string, mixed>> $rows
|
* @param array<int, array<string, mixed>> $rows
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private static function buildSummary(array $rows): array
|
private static function buildSummary(array $rows, string $dimension = 'dept'): array
|
||||||
{
|
{
|
||||||
$summary = [
|
$summary = [
|
||||||
'add_fans_count' => 0,
|
'add_fans_count' => 0,
|
||||||
@@ -2181,7 +2186,12 @@ class ConversionLogic
|
|||||||
$summary['paid_appointment_rate'] = self::percent($summary['paid_appointment_count'], $summary['add_fans_count']);
|
$summary['paid_appointment_rate'] = self::percent($summary['paid_appointment_count'], $summary['add_fans_count']);
|
||||||
$summary['open_appointment_rate'] = self::percent($summary['paid_appointment_count'], $summary['total_open_count']);
|
$summary['open_appointment_rate'] = self::percent($summary['paid_appointment_count'], $summary['total_open_count']);
|
||||||
$summary['interview_rate'] = self::percent($summary['interview_count'], $summary['appointment_total_count']);
|
$summary['interview_rate'] = self::percent($summary['interview_count'], $summary['appointment_total_count']);
|
||||||
$summary['receive_rate'] = self::percent($summary['completed_order_count'], $summary['add_fans_count']);
|
$summary['receive_rate'] = self::receiveRate(
|
||||||
|
$summary['completed_order_count'],
|
||||||
|
$summary['add_fans_count'],
|
||||||
|
$summary['interview_count'],
|
||||||
|
$dimension
|
||||||
|
);
|
||||||
$summary['interview_receive_rate'] = self::percent($summary['completed_order_count'], $summary['interview_count']);
|
$summary['interview_receive_rate'] = self::percent($summary['completed_order_count'], $summary['interview_count']);
|
||||||
$summary['open_receive_rate'] = self::percent($summary['completed_order_count'], $summary['total_open_count']);
|
$summary['open_receive_rate'] = self::percent($summary['completed_order_count'], $summary['total_open_count']);
|
||||||
$summary['avg_unit_price'] = self::safeDivideMoney($summary['completed_order_amount'], $summary['completed_order_count']);
|
$summary['avg_unit_price'] = self::safeDivideMoney($summary['completed_order_amount'], $summary['completed_order_count']);
|
||||||
@@ -2310,7 +2320,7 @@ class ConversionLogic
|
|||||||
* @param array<int, array<string, mixed>> $rows
|
* @param array<int, array<string, mixed>> $rows
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private static function buildCharts(array $rows): array
|
private static function buildCharts(array $rows, string $dimension = 'dept'): array
|
||||||
{
|
{
|
||||||
$chartableRows = array_values(array_filter($rows, static fn (array $row): bool => !((bool)($row['_virtual_bucket'] ?? false))));
|
$chartableRows = array_values(array_filter($rows, static fn (array $row): bool => !((bool)($row['_virtual_bucket'] ?? false))));
|
||||||
$topRows = array_slice($chartableRows, 0, 10);
|
$topRows = array_slice($chartableRows, 0, 10);
|
||||||
@@ -2319,29 +2329,56 @@ class ConversionLogic
|
|||||||
'names' => [],
|
'names' => [],
|
||||||
'amounts' => [],
|
'amounts' => [],
|
||||||
'order_counts' => [],
|
'order_counts' => [],
|
||||||
'fan_counts' => [],
|
|
||||||
'rois' => [],
|
|
||||||
];
|
];
|
||||||
|
if ($dimension === 'doctor') {
|
||||||
|
$ranking['appointment_counts'] = [];
|
||||||
|
$ranking['interview_counts'] = [];
|
||||||
|
} else {
|
||||||
|
$ranking['fan_counts'] = [];
|
||||||
|
$ranking['rois'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($topRows as $row) {
|
foreach ($topRows as $row) {
|
||||||
$ranking['names'][] = $row['name'];
|
$ranking['names'][] = $row['name'];
|
||||||
$ranking['amounts'][] = $row['completed_order_amount'];
|
$ranking['amounts'][] = $row['completed_order_amount'];
|
||||||
$ranking['order_counts'][] = $row['completed_order_count'];
|
$ranking['order_counts'][] = $row['completed_order_count'];
|
||||||
$ranking['fan_counts'][] = $row['add_fans_count'];
|
if ($dimension === 'doctor') {
|
||||||
$ranking['rois'][] = $row['roi'];
|
$ranking['appointment_counts'][] = $row['appointment_total_count'];
|
||||||
|
$ranking['interview_counts'][] = $row['interview_count'];
|
||||||
|
} else {
|
||||||
|
$ranking['fan_counts'][] = $row['add_fans_count'];
|
||||||
|
$ranking['rois'][] = $row['roi'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
$charts = [
|
||||||
'ranking' => $ranking,
|
'ranking' => $ranking,
|
||||||
'amount_share' => array_map(
|
'amount_share' => array_map(
|
||||||
static fn (array $row): array => ['name' => $row['name'], 'value' => $row['completed_order_amount']],
|
static fn (array $row): array => ['name' => $row['name'], 'value' => $row['completed_order_amount']],
|
||||||
array_filter($topRows, static fn (array $row): bool => (float)$row['completed_order_amount'] > 0)
|
array_filter($topRows, static fn (array $row): bool => (float)$row['completed_order_amount'] > 0)
|
||||||
),
|
),
|
||||||
'fan_share' => array_map(
|
];
|
||||||
|
|
||||||
|
if ($dimension === 'doctor') {
|
||||||
|
$charts['order_share'] = array_map(
|
||||||
|
static fn (array $row): array => ['name' => $row['name'], 'value' => $row['completed_order_count']],
|
||||||
|
array_filter($topRows, static fn (array $row): bool => (int)$row['completed_order_count'] > 0)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$charts['fan_share'] = array_map(
|
||||||
static fn (array $row): array => ['name' => $row['name'], 'value' => $row['add_fans_count']],
|
static fn (array $row): array => ['name' => $row['name'], 'value' => $row['add_fans_count']],
|
||||||
array_filter($topRows, static fn (array $row): bool => (int)$row['add_fans_count'] > 0)
|
array_filter($topRows, static fn (array $row): bool => (int)$row['add_fans_count'] > 0)
|
||||||
),
|
);
|
||||||
];
|
}
|
||||||
|
|
||||||
|
return $charts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function receiveRate(int $completedOrderCount, int $addFansCount, int $interviewCount, string $dimension): float
|
||||||
|
{
|
||||||
|
$denominator = $dimension === 'doctor' ? $interviewCount : $addFansCount;
|
||||||
|
|
||||||
|
return self::percent($completedOrderCount, $denominator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function percent(int $numerator, int $denominator): float
|
private static function percent(int $numerator, int $denominator): float
|
||||||
|
|||||||
Reference in New Issue
Block a user