更新
This commit is contained in:
@@ -189,6 +189,7 @@ class AppointmentLogic extends BaseLogic
|
||||
$slots[] = [
|
||||
'time' => $time,
|
||||
'available' => true,
|
||||
'has_appointment' => false,
|
||||
'quota' => 1,
|
||||
'period' => $roster['period'] ?? 'segment',
|
||||
];
|
||||
@@ -224,28 +225,39 @@ class AppointmentLogic extends BaseLogic
|
||||
'last_3_slots' => array_slice($slots, -3),
|
||||
]);
|
||||
|
||||
// 4. 查询已预约的时间段
|
||||
$appointmentTimes = Appointment::where([
|
||||
// 4. 查询当天所有挂号记录。
|
||||
// available 只由当前有效预约(status=1)决定;has_appointment 保留历史挂号事实,
|
||||
// 避免预约在完成、过号或取消后被误显示为“空号”。
|
||||
$appointmentRows = Appointment::where([
|
||||
'doctor_id' => $doctorId,
|
||||
'appointment_date' => $date,
|
||||
'status' => 1 // 只查询有效预约
|
||||
])->column('appointment_time');
|
||||
])->field(['appointment_time', 'status'])->select()->toArray();
|
||||
|
||||
// 将时间格式统一为 HH:MM(去掉秒)
|
||||
$appointments = array_map(function($time) {
|
||||
// 如果是 HH:MM:SS 格式,截取前5位
|
||||
return substr($time, 0, 5);
|
||||
}, $appointmentTimes);
|
||||
$appointmentMap = [];
|
||||
$activeAppointmentMap = [];
|
||||
foreach ($appointmentRows as $appointmentRow) {
|
||||
// 将 HH:MM:SS 统一为 HH:MM
|
||||
$appointmentTime = substr((string) ($appointmentRow['appointment_time'] ?? ''), 0, 5);
|
||||
if ($appointmentTime === '') {
|
||||
continue;
|
||||
}
|
||||
$appointmentMap[$appointmentTime] = true;
|
||||
if ((int) ($appointmentRow['status'] ?? 0) === 1) {
|
||||
$activeAppointmentMap[$appointmentTime] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 标记已占用的时间段
|
||||
// 5. 分别标记历史挂号与当前占用状态
|
||||
foreach ($slots as &$slot) {
|
||||
if (in_array($slot['time'], $appointments)) {
|
||||
$slot['has_appointment'] = isset($appointmentMap[$slot['time']]);
|
||||
if (isset($activeAppointmentMap[$slot['time']])) {
|
||||
$slot['available'] = false;
|
||||
$slot['quota'] = 0;
|
||||
}
|
||||
}
|
||||
unset($slot);
|
||||
|
||||
// 5. 按时间排序
|
||||
// 6. 按时间排序
|
||||
usort($slots, function($a, $b) {
|
||||
return strcmp($a['time'], $b['time']);
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ class FirstVisitConversionLogic
|
||||
$selectedAssistantId = max(0, (int) ($params['assistant_id'] ?? 0));
|
||||
$requestedMediaChannelCode = trim((string)($params['media_channel_code'] ?? ''));
|
||||
$selectedMediaChannel = $requestedMediaChannelCode !== ''
|
||||
? MediaChannelService::getChannelByCode($requestedMediaChannelCode)
|
||||
? MediaChannelService::getCurrentTagChannelByCode($requestedMediaChannelCode)
|
||||
: null;
|
||||
$selectedMediaChannelCode = $selectedMediaChannel !== null ? $requestedMediaChannelCode : '';
|
||||
|
||||
@@ -79,7 +79,9 @@ class FirstVisitConversionLogic
|
||||
'time_type' => 'custom',
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'include_filters' => 1,
|
||||
// 一诊筛选项在本层按自身权限和“当前企微标签”口径生成,不再让通用
|
||||
// Conversion 额外加载一套包含历史渠道的筛选器。
|
||||
'include_filters' => 0,
|
||||
'include_members' => 1,
|
||||
'exclude_cancelled_appointments' => 1,
|
||||
'order_metric_mode' => 'performance',
|
||||
@@ -98,7 +100,8 @@ class FirstVisitConversionLogic
|
||||
$adminId,
|
||||
$adminInfo,
|
||||
$effectiveAdminIds,
|
||||
$costAllocationAdminIds
|
||||
$costAllocationAdminIds,
|
||||
$selectedMediaChannel
|
||||
);
|
||||
$rows = is_array($conversion['lists'] ?? null) ? $conversion['lists'] : [];
|
||||
$rowAllowedDeptIds = self::visibleRowDeptIds($effectiveAdminIds);
|
||||
@@ -149,10 +152,6 @@ class FirstVisitConversionLogic
|
||||
$selectedMediaChannelName = $selectedMediaChannelCode !== ''
|
||||
? (string) ($selectedMediaChannel['channel_name'] ?? $selectedMediaChannelCode)
|
||||
: '';
|
||||
$conversionFilters = is_array($conversion['extend']['filters'] ?? null)
|
||||
? $conversion['extend']['filters']
|
||||
: [];
|
||||
|
||||
return [
|
||||
'meta' => [
|
||||
'time_type' => $timeType,
|
||||
@@ -176,9 +175,7 @@ class FirstVisitConversionLogic
|
||||
'filters' => [
|
||||
'departments' => DeptLogic::getAllDataScoped($adminId, $adminInfo),
|
||||
'assistants' => self::assistantOptions($baseVisibleAdminIds, $selectedDeptIds, $selectedDeptId),
|
||||
'media_channels' => is_array($conversionFilters['media_channels'] ?? null)
|
||||
? $conversionFilters['media_channels']
|
||||
: [],
|
||||
'media_channels' => MediaChannelService::getCurrentTagOptions(),
|
||||
],
|
||||
'summary' => $summary,
|
||||
'rankings' => [
|
||||
|
||||
@@ -8,6 +8,7 @@ use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\QywxExternalContact;
|
||||
use app\common\model\QywxSyncSettings;
|
||||
use app\common\service\qywx\MediaChannelService;
|
||||
use app\common\service\wechat\WechatWorkService;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
@@ -306,6 +307,7 @@ class CustomerLogic extends BaseLogic
|
||||
}
|
||||
|
||||
// 4. 更新同步设置
|
||||
MediaChannelService::forgetCurrentTagCatalogCache();
|
||||
self::updateSyncStatus('success', $syncCount);
|
||||
|
||||
Log::info('同步完成 - 总数: ' . $syncCount . ', 新增: ' . $newCount . ', 更新: ' . $updateCount);
|
||||
@@ -650,6 +652,7 @@ class CustomerLogic extends BaseLogic
|
||||
$updateCount,
|
||||
$skippedCount
|
||||
);
|
||||
MediaChannelService::forgetCurrentTagCatalogCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -739,6 +742,7 @@ class CustomerLogic extends BaseLogic
|
||||
Db::name('qywx_external_contact_tag')
|
||||
->where('external_userid', $externalUserId)
|
||||
->delete();
|
||||
MediaChannelService::forgetCurrentTagCatalogCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -796,6 +800,7 @@ class CustomerLogic extends BaseLogic
|
||||
Db::name('qywx_external_contact_tag')
|
||||
->where('external_userid', $externalUserId)
|
||||
->delete();
|
||||
MediaChannelService::forgetCurrentTagCatalogCache();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -817,6 +822,7 @@ class CustomerLogic extends BaseLogic
|
||||
|
||||
// 关系表按剩余 kept follow_users 同步(自动清掉离开员工那行 + 保留其他员工的标签)
|
||||
self::syncContactTagsRelation($externalUserId, $kept);
|
||||
MediaChannelService::forgetCurrentTagCatalogCache();
|
||||
}
|
||||
|
||||
private static function upsertOneExternalContactBundle(
|
||||
@@ -1137,24 +1143,11 @@ class CustomerLogic extends BaseLogic
|
||||
*/
|
||||
public static function getTagStats(): array
|
||||
{
|
||||
// 关系表里 external_userid 可能指向已被软删的客户;这里 INNER JOIN 主表过滤未删除的
|
||||
$rows = Db::name('qywx_external_contact_tag')
|
||||
->alias('ect')
|
||||
->join('qywx_external_contact ec', 'ec.external_userid = ect.external_userid', 'INNER')
|
||||
->whereNull('ec.delete_time')
|
||||
->field([
|
||||
'ect.tag_id',
|
||||
'ect.tag_name',
|
||||
'ect.group_name',
|
||||
'COUNT(DISTINCT ect.external_userid) AS customer_count',
|
||||
])
|
||||
->group('ect.tag_id, ect.tag_name, ect.group_name')
|
||||
->order('customer_count', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
// 与一诊渠道共用同一份“当前有效标签”投影:一 tag_id 一条最新名称,
|
||||
// 避免企微标签改名后在筛选器里同时出现新旧快照。
|
||||
$rows = MediaChannelService::getCurrentTagCatalog();
|
||||
|
||||
$groupMap = [];
|
||||
$customersUnion = [];
|
||||
foreach ($rows as $r) {
|
||||
$g = (string) ($r['group_name'] ?? '');
|
||||
if (!isset($groupMap[$g])) {
|
||||
|
||||
@@ -32,6 +32,7 @@ class ConversionLogic
|
||||
* @param array $adminInfo 当前 admin 完整信息(含 root / role_id 数组等)
|
||||
* @param int[]|null $trustedVisibleAdminIdsOverride 仅供服务端内部可信调用覆盖本次可见管理员;不从 HTTP 参数读取
|
||||
* @param int[]|null $trustedCostAllocationAdminIdsOverride 仅用于成本按加粉占比分摊的分母,不会放大任何业务指标
|
||||
* @param array<string,mixed>|null $trustedMediaChannelOverride 仅供服务端内部传入已校验渠道,避免再次按全局历史渠道口径解析
|
||||
* @return array<string, mixed>
|
||||
*
|
||||
* 数据权限:通过 DataScopeService::getVisibleAdminIds 拿到当前用户的"可见 admin id 集合"。
|
||||
@@ -44,7 +45,8 @@ class ConversionLogic
|
||||
int $adminId = 0,
|
||||
?array $adminInfo = null,
|
||||
?array $trustedVisibleAdminIdsOverride = null,
|
||||
?array $trustedCostAllocationAdminIdsOverride = null
|
||||
?array $trustedCostAllocationAdminIdsOverride = null,
|
||||
?array $trustedMediaChannelOverride = null
|
||||
): array
|
||||
{
|
||||
self::$requestRowsCache = [];
|
||||
@@ -56,9 +58,10 @@ class ConversionLogic
|
||||
$usePerformanceOrderMetrics = strtolower(trim((string)($params['order_metric_mode'] ?? ''))) === 'performance';
|
||||
$dimension = self::normalizeDimension((string)($params['dimension'] ?? 'dept'));
|
||||
$requestedMediaChannelCode = trim((string)($params['media_channel_code'] ?? ''));
|
||||
$mediaChannel = $requestedMediaChannelCode !== ''
|
||||
? MediaChannelService::getChannelByCode($requestedMediaChannelCode)
|
||||
: null;
|
||||
$mediaChannel = $trustedMediaChannelOverride;
|
||||
if ($mediaChannel === null && $requestedMediaChannelCode !== '') {
|
||||
$mediaChannel = MediaChannelService::getChannelByCode($requestedMediaChannelCode);
|
||||
}
|
||||
$mediaChannelCode = $mediaChannel !== null ? $requestedMediaChannelCode : '';
|
||||
$filterEmptyEntities = $mediaChannel !== null;
|
||||
[$startTimestamp, $endTimestamp, $startDate, $endDate] = self::resolveTimeRange($params);
|
||||
|
||||
Reference in New Issue
Block a user