Update ConversionLogic.php

This commit is contained in:
2026-04-22 11:28:21 +08:00
parent 4f45ecbd63
commit f773e73de6
@@ -327,100 +327,82 @@ class ConversionLogic
int $startTimestamp,
int $endTimestamp
): void {
$wxUserIdToAdminIds = self::loadWorkWechatAdminMap();
$rows = Db::name('qywx_external_contact')
->whereNull('delete_time')
->whereRaw(
'((external_first_add_time BETWEEN ? AND ?) OR (external_first_add_time = 0 AND create_time BETWEEN ? AND ?))',
[$startTimestamp, $endTimestamp, $startTimestamp, $endTimestamp]
)
->field('follow_admin_ids, follow_users')
->select()
->toArray();
$timeSql = '((q.external_first_add_time BETWEEN ? AND ?) OR (q.external_first_add_time = 0 AND q.create_time BETWEEN ? AND ?))';
$bind = [$startTimestamp, $endTimestamp, $startTimestamp, $endTimestamp];
$indexSql = self::buildJsonIndexUnionSql(32);
$countsByAdmin = [];
foreach ($rows as $row) {
$adminIds = self::extractFollowAdminIds($row, $wxUserIdToAdminIds);
if ($adminIds === []) {
// 优先使用 follow_admin_ids(JSON 数组) 做 SQL 聚合,兼容不支持 JSON_TABLE 的 MySQL 版本。
$countsByAdmin = Db::query(
<<<SQL
SELECT
CAST(JSON_UNQUOTE(JSON_EXTRACT(q.follow_admin_ids, CONCAT('$[', idx.n, ']'))) AS UNSIGNED) AS admin_id,
COUNT(*) AS cnt
FROM zyt_qywx_external_contact q
JOIN (
{$indexSql}
) AS idx
WHERE q.delete_time IS NULL
AND COALESCE(JSON_LENGTH(q.follow_admin_ids), 0) > idx.n
AND {$timeSql}
GROUP BY admin_id
SQL,
$bind
);
$mergedCounts = [];
foreach ($countsByAdmin as $row) {
$adminId = (int) ($row['admin_id'] ?? 0);
if ($adminId <= 0) {
continue;
}
foreach ($adminIds as $adminId) {
$countsByAdmin[$adminId] = ($countsByAdmin[$adminId] ?? 0) + 1;
}
$mergedCounts[$adminId] = ($mergedCounts[$adminId] ?? 0) + (int) ($row['cnt'] ?? 0);
}
foreach ($countsByAdmin as $adminId => $count) {
// 兼容历史数据:follow_admin_ids 为空时,再从 follow_users(JSON) -> admin.work_wechat_userid 回填。
$fallbackRows = Db::query(
<<<SQL
SELECT
a.id AS admin_id,
COUNT(*) AS cnt
FROM zyt_qywx_external_contact q
JOIN (
{$indexSql}
) AS idx
ON COALESCE(JSON_LENGTH(q.follow_users), 0) > idx.n
JOIN zyt_admin a
ON a.work_wechat_userid = JSON_UNQUOTE(JSON_EXTRACT(q.follow_users, CONCAT('$[', idx.n, '].userid')))
AND a.delete_time IS NULL
WHERE q.delete_time IS NULL
AND COALESCE(JSON_LENGTH(q.follow_admin_ids), 0) = 0
AND {$timeSql}
GROUP BY a.id
SQL,
$bind
);
foreach ($fallbackRows as $row) {
$adminId = (int) ($row['admin_id'] ?? 0);
if ($adminId <= 0) {
continue;
}
$mergedCounts[$adminId] = ($mergedCounts[$adminId] ?? 0) + (int) ($row['cnt'] ?? 0);
}
foreach ($mergedCounts as $adminId => $count) {
foreach (self::mapEntityIds($dimension, (int) $adminId, $entityIds, $adminToDeptIds) as $entityId) {
$entities[$entityId]['add_fans_count'] += (int) $count;
}
}
}
/**
* @return array<string, int[]>
*/
private static function loadWorkWechatAdminMap(): array
private static function buildJsonIndexUnionSql(int $maxIndex): string
{
$rows = Db::name('admin')
->whereNull('delete_time')
->where('work_wechat_userid', '<>', '')
->field('id, work_wechat_userid')
->select()
->toArray();
$map = [];
foreach ($rows as $row) {
$userId = trim((string)($row['work_wechat_userid'] ?? ''));
$adminId = (int)($row['id'] ?? 0);
if ($userId === '' || $adminId <= 0) {
continue;
}
$map[$userId] ??= [];
$map[$userId][] = $adminId;
$parts = [];
for ($i = 0; $i < $maxIndex; $i++) {
$parts[] = 'SELECT ' . $i . ' AS n';
}
return $map;
}
/**
* @param array<string, mixed> $row
* @param array<string, int[]> $wxUserIdToAdminIds
* @return int[]
*/
private static function extractFollowAdminIds(array $row, array $wxUserIdToAdminIds): array
{
$adminIds = json_decode((string)($row['follow_admin_ids'] ?? '[]'), true);
if (is_array($adminIds)) {
$adminIds = array_values(array_unique(array_filter(array_map('intval', $adminIds), static fn (int $id): bool => $id > 0)));
} else {
$adminIds = [];
}
if ($adminIds !== []) {
return $adminIds;
}
$followUsers = json_decode((string)($row['follow_users'] ?? '[]'), true);
if (!is_array($followUsers)) {
return [];
}
$resolved = [];
foreach ($followUsers as $followUser) {
if (!is_array($followUser)) {
continue;
}
$userId = trim((string)($followUser['userid'] ?? ''));
if ($userId === '' || !isset($wxUserIdToAdminIds[$userId])) {
continue;
}
foreach ($wxUserIdToAdminIds[$userId] as $adminId) {
$resolved[$adminId] = $adminId;
}
}
return array_values($resolved);
return implode(' UNION ALL ', $parts);
}
/**