更新
:i# Please enter the commit message for your changes. Lines starting
This commit is contained in:
@@ -512,6 +512,8 @@ class FirstVisitConversionLogic
|
||||
$channelCode,
|
||||
$channel['channel_name'] ?? '',
|
||||
$channel['source_tag_name'] ?? '',
|
||||
$channel['legacy_channel_name'] ?? '',
|
||||
$channel['legacy_source_tag_name'] ?? '',
|
||||
]
|
||||
), static fn (string $value): bool => $value !== '')));
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ class ConversionLogic
|
||||
'departments' => self::buildDepartmentOptions($visibleAdminIds, $eligibleDeptIds, $allowedDeptIds),
|
||||
'assistants' => DiagnosisLogic::getAssistants($adminId, $adminInfo),
|
||||
'doctors' => self::buildDoctorOptions($visibleAdminIds),
|
||||
'media_channels' => self::buildMediaChannelOptions($visibleAdminIds, $eligibleDeptIds, $allowedDeptIds),
|
||||
'media_channels' => self::buildMediaChannelOptions(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -411,40 +411,16 @@ class ConversionLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体渠道下拉:按可见部门绑定的渠道收窄。
|
||||
* 媒体渠道下拉:返回全局启用渠道;事实数据仍由 DataScope 单独约束。
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private static function buildMediaChannelOptions(?array $visibleAdminIds, array $eligibleDeptIds, ?array $allowedDeptIds = null): array
|
||||
private static function buildMediaChannelOptions(): array
|
||||
{
|
||||
$allOptions = MediaChannelService::getOptions();
|
||||
if ($visibleAdminIds === null) {
|
||||
return $allOptions;
|
||||
}
|
||||
if (!AccountCost::supportsDeptBinding()) {
|
||||
return $allOptions;
|
||||
}
|
||||
|
||||
$allowedDeptIds ??= self::resolveFilterAllowedDeptIds($visibleAdminIds, $eligibleDeptIds);
|
||||
if ($allowedDeptIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$allowedCodes = Db::name('account_cost')
|
||||
->whereIn('dept_id', $allowedDeptIds)
|
||||
->where('media_channel_code', '<>', '')
|
||||
->distinct(true)
|
||||
->column('media_channel_code');
|
||||
$allowedCodeSet = array_fill_keys(array_filter(array_map('strval', $allowedCodes)), true);
|
||||
if ($allowedCodeSet === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_filter($allOptions, static function ($option) use ($allowedCodeSet): bool {
|
||||
$code = is_array($option) ? (string)($option['code'] ?? '') : '';
|
||||
|
||||
return $code !== '' && isset($allowedCodeSet[$code]);
|
||||
}));
|
||||
// Channels are dimension values, while account_cost rows are optional
|
||||
// facts. Using cost bindings as an option whitelist hid valid labels and
|
||||
// made the dropdown change when department/assistant filters changed.
|
||||
return MediaChannelService::getOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,18 @@ use think\db\Query;
|
||||
|
||||
class MediaChannelService
|
||||
{
|
||||
private const ACTIVE_ROWS_CACHE_TTL_SECONDS = 1.0;
|
||||
private const SCAN_DUPLICATE_UPDATE_FIELDS = [
|
||||
'source_group_name',
|
||||
'last_seen_time',
|
||||
'update_time',
|
||||
];
|
||||
|
||||
/** @var array<int, array<string, mixed>>|null */
|
||||
private static ?array $activeChannelRowsCache = null;
|
||||
|
||||
private static float $activeChannelRowsCachedAt = 0.0;
|
||||
|
||||
/**
|
||||
* 与业绩看板「渠道来源」相同的分组(按 source_group_name),不含客户数统计。
|
||||
*
|
||||
@@ -21,11 +33,7 @@ class MediaChannelService
|
||||
*/
|
||||
public static function getOptionGroups(): array
|
||||
{
|
||||
$rows = QywxMediaChannel::where('status', 1)
|
||||
->field('channel_code, channel_name, source_group_name')
|
||||
->order('source_group_name asc, id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$rows = self::getActiveChannelRows();
|
||||
|
||||
if ($rows === []) {
|
||||
return [];
|
||||
@@ -60,11 +68,17 @@ class MediaChannelService
|
||||
*/
|
||||
public static function getOptions(): array
|
||||
{
|
||||
$rows = QywxMediaChannel::where('status', 1)
|
||||
->field('channel_code, channel_name')
|
||||
->order('channel_name asc, id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$rows = self::getActiveChannelRows();
|
||||
usort($rows, static function (array $a, array $b): int {
|
||||
$nameCompare = strnatcasecmp(
|
||||
(string) ($a['channel_name'] ?? ''),
|
||||
(string) ($b['channel_name'] ?? '')
|
||||
);
|
||||
|
||||
return $nameCompare !== 0
|
||||
? $nameCompare
|
||||
: strcmp((string) ($a['channel_code'] ?? ''), (string) ($b['channel_code'] ?? ''));
|
||||
});
|
||||
|
||||
return array_map(static fn (array $row): array => [
|
||||
'code' => (string) ($row['channel_code'] ?? ''),
|
||||
@@ -74,9 +88,9 @@ class MediaChannelService
|
||||
|
||||
public static function getDefaultCode(): string
|
||||
{
|
||||
return (string) QywxMediaChannel::where('status', 1)
|
||||
->order('id asc')
|
||||
->value('channel_code');
|
||||
$rows = self::getActiveChannelRows();
|
||||
|
||||
return (string) ($rows[0]['channel_code'] ?? '');
|
||||
}
|
||||
|
||||
public static function isValidCode(string $channelCode): bool
|
||||
@@ -86,9 +100,7 @@ class MediaChannelService
|
||||
return false;
|
||||
}
|
||||
|
||||
return QywxMediaChannel::where('status', 1)
|
||||
->where('channel_code', $channelCode)
|
||||
->count() > 0;
|
||||
return self::getChannelByCode($channelCode) !== null;
|
||||
}
|
||||
|
||||
public static function normalizeStatsCode(string $channelCode): string
|
||||
@@ -106,11 +118,31 @@ class MediaChannelService
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = QywxMediaChannel::where('status', 1)
|
||||
->where('channel_code', $channelCode)
|
||||
->find();
|
||||
$model = QywxMediaChannel::where('channel_code', $channelCode)->find();
|
||||
|
||||
return $row ? $row->toArray() : null;
|
||||
if ($model !== null) {
|
||||
$row = $model->toArray();
|
||||
if ((int) ($row['status'] ?? 0) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tagId = trim((string) ($row['source_tag_id'] ?? ''));
|
||||
if ($tagId === '') {
|
||||
return $row;
|
||||
}
|
||||
|
||||
$merged = self::mergeConfiguredChannelsWithTags([$row], self::loadCurrentTagRows([$tagId]));
|
||||
|
||||
return $merged[0] ?? $row;
|
||||
}
|
||||
|
||||
foreach (self::getActiveChannelRows() as $row) {
|
||||
if ((string) ($row['channel_code'] ?? '') === $channelCode) {
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getNameByCode(string $channelCode): string
|
||||
@@ -134,6 +166,8 @@ class MediaChannelService
|
||||
$names = array_values(array_unique(array_filter([
|
||||
trim((string) ($channel['channel_name'] ?? '')),
|
||||
trim((string) ($channel['source_tag_name'] ?? '')),
|
||||
trim((string) ($channel['legacy_channel_name'] ?? '')),
|
||||
trim((string) ($channel['legacy_source_tag_name'] ?? '')),
|
||||
])));
|
||||
|
||||
if ($names === []) {
|
||||
@@ -233,6 +267,7 @@ class MediaChannelService
|
||||
|
||||
while (true) {
|
||||
$rows = QywxExternalContact::where('id', '>', $lastId)
|
||||
->whereNull('delete_time')
|
||||
->field('id, follow_users')
|
||||
->order('id asc')
|
||||
->limit($batchSize)
|
||||
@@ -276,20 +311,17 @@ class MediaChannelService
|
||||
'update_time' => $now,
|
||||
];
|
||||
|
||||
Db::name('qywx_media_channel')->duplicate([
|
||||
'channel_name',
|
||||
'source_tag_id',
|
||||
'source_tag_name',
|
||||
'source_group_name',
|
||||
'status',
|
||||
'last_seen_time',
|
||||
'update_time',
|
||||
])->insert($rowData);
|
||||
Db::name('qywx_media_channel')
|
||||
->duplicate(self::SCAN_DUPLICATE_UPDATE_FIELDS)
|
||||
->insert($rowData);
|
||||
$upserted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$activeChannelRowsCache = null;
|
||||
self::$activeChannelRowsCachedAt = 0.0;
|
||||
|
||||
return [
|
||||
'scanned_contacts' => $scannedContacts,
|
||||
'discovered_tags' => count($discoveredTags),
|
||||
@@ -297,6 +329,191 @@ class MediaChannelService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the persistent channel registry with the latest tag snapshots in
|
||||
* the normalized relation table. The registry keeps stable channel codes
|
||||
* and historical name-only channels; relation rows supply newly discovered
|
||||
* tags and names for tags that were renamed in WeCom.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private static function getActiveChannelRows(): array
|
||||
{
|
||||
$now = microtime(true);
|
||||
if (self::$activeChannelRowsCache !== null
|
||||
&& ($now - self::$activeChannelRowsCachedAt) < self::ACTIVE_ROWS_CACHE_TTL_SECONDS) {
|
||||
return self::$activeChannelRowsCache;
|
||||
}
|
||||
|
||||
$configuredRows = QywxMediaChannel::field(
|
||||
'id, channel_code, channel_name, source_tag_id, source_tag_name, source_group_name, '
|
||||
. 'tag_uniq_key, status, last_seen_time, create_time, update_time'
|
||||
)
|
||||
->order('id asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$rows = self::mergeConfiguredChannelsWithTags($configuredRows, self::loadCurrentTagRows());
|
||||
self::$activeChannelRowsCache = $rows;
|
||||
self::$activeChannelRowsCachedAt = microtime(true);
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]|null $tagIds null loads all current tags
|
||||
* @return array<int, array{source_tag_id: string, source_tag_name: string, source_group_name: string}>
|
||||
*/
|
||||
private static function loadCurrentTagRows(?array $tagIds = null): array
|
||||
{
|
||||
$bindings = [];
|
||||
$tagFilter = '';
|
||||
if ($tagIds !== null) {
|
||||
$tagIds = array_values(array_unique(array_filter(array_map(
|
||||
static fn ($tagId): string => trim((string) $tagId),
|
||||
$tagIds
|
||||
), static fn (string $tagId): bool => $tagId !== '')));
|
||||
if ($tagIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$tagFilter = ' AND newest_tag.tag_id IN (' . implode(', ', array_fill(0, count($tagIds), '?')) . ')';
|
||||
$bindings = $tagIds;
|
||||
}
|
||||
|
||||
$tagTable = self::tableWithPrefix('qywx_external_contact_tag');
|
||||
$sql = <<<SQL
|
||||
SELECT latest_tag.tag_id AS source_tag_id,
|
||||
COALESCE(latest_tag.tag_name, '') AS source_tag_name,
|
||||
COALESCE(latest_tag.group_name, '') AS source_group_name
|
||||
FROM {$tagTable} latest_tag
|
||||
INNER JOIN (
|
||||
SELECT latest_time.tag_id, MAX(tag_at_time.id) AS latest_id
|
||||
FROM (
|
||||
SELECT newest_tag.tag_id, MAX(newest_tag.update_time) AS latest_update_time
|
||||
FROM {$tagTable} newest_tag
|
||||
WHERE newest_tag.tag_id <> ''
|
||||
{$tagFilter}
|
||||
GROUP BY newest_tag.tag_id
|
||||
) latest_time
|
||||
INNER JOIN {$tagTable} tag_at_time
|
||||
ON tag_at_time.tag_id = latest_time.tag_id
|
||||
AND tag_at_time.update_time = latest_time.latest_update_time
|
||||
GROUP BY latest_time.tag_id
|
||||
) selected_tag
|
||||
ON selected_tag.latest_id = latest_tag.id
|
||||
ORDER BY latest_tag.tag_id ASC
|
||||
SQL;
|
||||
|
||||
return array_map(static fn (array $row): array => [
|
||||
'source_tag_id' => trim((string) ($row['source_tag_id'] ?? '')),
|
||||
'source_tag_name' => trim((string) ($row['source_tag_name'] ?? '')),
|
||||
'source_group_name' => trim((string) ($row['source_group_name'] ?? '')),
|
||||
], Db::query($sql, $bindings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabled configured tags stay disabled. Active configured rows keep their
|
||||
* stable codes, while automatic display names follow the newest tag name.
|
||||
* Tags not yet present in the registry receive the same deterministic code
|
||||
* that the scanner would create.
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $configuredRows
|
||||
* @param array<int, array<string, mixed>> $tagRows
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private static function mergeConfiguredChannelsWithTags(array $configuredRows, array $tagRows): array
|
||||
{
|
||||
$tagMap = [];
|
||||
foreach ($tagRows as $tagRow) {
|
||||
$tagId = trim((string) ($tagRow['source_tag_id'] ?? $tagRow['tag_id'] ?? ''));
|
||||
if ($tagId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tagMap[$tagId] = [
|
||||
'source_tag_id' => $tagId,
|
||||
'source_tag_name' => trim((string) ($tagRow['source_tag_name'] ?? $tagRow['tag_name'] ?? '')),
|
||||
'source_group_name' => trim((string) ($tagRow['source_group_name'] ?? $tagRow['group_name'] ?? '')),
|
||||
];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$configuredTagIds = [];
|
||||
$configuredCodes = [];
|
||||
foreach ($configuredRows as $configuredRow) {
|
||||
$channelCode = trim((string) ($configuredRow['channel_code'] ?? ''));
|
||||
$tagId = trim((string) ($configuredRow['source_tag_id'] ?? ''));
|
||||
if ($channelCode !== '') {
|
||||
$configuredCodes[$channelCode] = true;
|
||||
}
|
||||
if ($tagId !== '') {
|
||||
// A disabled registry row is an explicit opt-out and must not be
|
||||
// reintroduced as a dynamically discovered channel.
|
||||
$configuredTagIds[$tagId] = true;
|
||||
}
|
||||
if ($channelCode === '' || (int) ($configuredRow['status'] ?? 0) !== 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = $configuredRow;
|
||||
$currentTag = $tagId !== '' ? ($tagMap[$tagId] ?? null) : null;
|
||||
if ($currentTag !== null) {
|
||||
$oldTagName = trim((string) ($row['source_tag_name'] ?? ''));
|
||||
$oldChannelName = trim((string) ($row['channel_name'] ?? ''));
|
||||
$currentTagName = (string) $currentTag['source_tag_name'];
|
||||
|
||||
if ($currentTagName !== '') {
|
||||
if ($oldTagName !== '' && $oldTagName !== $currentTagName) {
|
||||
$row['legacy_source_tag_name'] = $oldTagName;
|
||||
}
|
||||
|
||||
$isAutomaticName = $oldChannelName === ''
|
||||
|| $oldChannelName === $oldTagName
|
||||
|| $oldChannelName === $tagId;
|
||||
if ($isAutomaticName) {
|
||||
if ($oldChannelName !== '' && $oldChannelName !== $currentTagName) {
|
||||
$row['legacy_channel_name'] = $oldChannelName;
|
||||
}
|
||||
$row['channel_name'] = $currentTagName;
|
||||
}
|
||||
$row['source_tag_name'] = $currentTagName;
|
||||
}
|
||||
$row['source_group_name'] = (string) $currentTag['source_group_name'];
|
||||
}
|
||||
|
||||
$result[] = $row;
|
||||
}
|
||||
|
||||
foreach ($tagMap as $tagId => $tagRow) {
|
||||
if (isset($configuredTagIds[$tagId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$channelName = (string) ($tagRow['source_tag_name'] ?? '');
|
||||
$channelCode = self::buildChannelCode($tagId, $channelName);
|
||||
if ($channelCode === '' || isset($configuredCodes[$channelCode])) {
|
||||
continue;
|
||||
}
|
||||
$configuredCodes[$channelCode] = true;
|
||||
$result[] = [
|
||||
'id' => 0,
|
||||
'channel_code' => $channelCode,
|
||||
'channel_name' => $channelName !== '' ? $channelName : $tagId,
|
||||
'source_tag_id' => $tagId,
|
||||
'source_tag_name' => $channelName,
|
||||
'source_group_name' => (string) ($tagRow['source_group_name'] ?? ''),
|
||||
'tag_uniq_key' => self::buildTagUniqKey($tagId, $channelName),
|
||||
'status' => 1,
|
||||
'last_seen_time' => 0,
|
||||
'create_time' => 0,
|
||||
'update_time' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $channel
|
||||
* @return string[]
|
||||
|
||||
@@ -17,3 +17,22 @@ SET @add_idx_tag_ext_sql := IF(
|
||||
PREPARE add_idx_tag_ext_stmt FROM @add_idx_tag_ext_sql;
|
||||
EXECUTE add_idx_tag_ext_stmt;
|
||||
DEALLOCATE PREPARE add_idx_tag_ext_stmt;
|
||||
|
||||
-- 渠道选项按标签读取最新名称,覆盖 tag_id + update_time 可避免重复扫描关系表。
|
||||
SET @idx_tag_update_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'zyt_qywx_external_contact_tag'
|
||||
AND INDEX_NAME = 'idx_tag_update'
|
||||
);
|
||||
|
||||
SET @add_idx_tag_update_sql := IF(
|
||||
@idx_tag_update_exists = 0,
|
||||
'ALTER TABLE `zyt_qywx_external_contact_tag` ADD INDEX `idx_tag_update` (`tag_id`, `update_time`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
|
||||
PREPARE add_idx_tag_update_stmt FROM @add_idx_tag_update_sql;
|
||||
EXECUTE add_idx_tag_update_stmt;
|
||||
DEALLOCATE PREPARE add_idx_tag_update_stmt;
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use app\common\service\qywx\MediaChannelService;
|
||||
use think\facade\Db;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$mergeMethod = new ReflectionMethod(MediaChannelService::class, 'mergeConfiguredChannelsWithTags');
|
||||
$mergeMethod->setAccessible(true);
|
||||
$scanUpdateFields = (new ReflectionClass(MediaChannelService::class))
|
||||
->getReflectionConstant('SCAN_DUPLICATE_UPDATE_FIELDS')
|
||||
?->getValue();
|
||||
if (!is_array($scanUpdateFields)
|
||||
|| array_intersect(['channel_name', 'source_tag_name', 'status'], $scanUpdateFields) !== []) {
|
||||
throw new RuntimeException('Channel scan would overwrite rename aliases, manual labels, or disabled status');
|
||||
}
|
||||
|
||||
$configuredRows = [
|
||||
[
|
||||
'id' => 1,
|
||||
'channel_code' => 'stable-a',
|
||||
'channel_name' => 'Old tag name',
|
||||
'source_tag_id' => 'tag-a',
|
||||
'source_tag_name' => 'Old tag name',
|
||||
'source_group_name' => 'Old group',
|
||||
'status' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'channel_code' => 'manual-name',
|
||||
'channel_name' => 'Manual campaign label',
|
||||
'source_tag_id' => 'tag-manual',
|
||||
'source_tag_name' => 'Old manual tag',
|
||||
'source_group_name' => 'Old group',
|
||||
'status' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'channel_code' => 'disabled-tag',
|
||||
'channel_name' => 'Disabled tag',
|
||||
'source_tag_id' => 'tag-disabled',
|
||||
'source_tag_name' => 'Disabled tag',
|
||||
'source_group_name' => 'Group',
|
||||
'status' => 0,
|
||||
],
|
||||
[
|
||||
'id' => 4,
|
||||
'channel_code' => 'legacy-name-only',
|
||||
'channel_name' => 'Legacy name-only channel',
|
||||
'source_tag_id' => '',
|
||||
'source_tag_name' => 'Legacy name-only channel',
|
||||
'source_group_name' => 'Legacy',
|
||||
'status' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
$tagRows = [
|
||||
[
|
||||
'source_tag_id' => 'tag-a',
|
||||
'source_tag_name' => 'Renamed tag',
|
||||
'source_group_name' => 'New group',
|
||||
],
|
||||
[
|
||||
'source_tag_id' => 'tag-manual',
|
||||
'source_tag_name' => 'Renamed manual tag',
|
||||
'source_group_name' => 'New group',
|
||||
],
|
||||
[
|
||||
'source_tag_id' => 'tag-disabled',
|
||||
'source_tag_name' => 'Disabled tag returned by relation table',
|
||||
'source_group_name' => 'Group',
|
||||
],
|
||||
[
|
||||
'source_tag_id' => 'tag-new',
|
||||
'source_tag_name' => 'Newly discovered tag',
|
||||
'source_group_name' => 'New group',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array<int, array<string, mixed>> $mergedRows */
|
||||
$mergedRows = $mergeMethod->invoke(null, $configuredRows, $tagRows);
|
||||
$byCode = [];
|
||||
foreach ($mergedRows as $row) {
|
||||
$byCode[(string) ($row['channel_code'] ?? '')] = $row;
|
||||
}
|
||||
|
||||
$renamed = $byCode['stable-a'] ?? null;
|
||||
if (!is_array($renamed)
|
||||
|| ($renamed['channel_name'] ?? '') !== 'Renamed tag'
|
||||
|| ($renamed['source_tag_name'] ?? '') !== 'Renamed tag'
|
||||
|| ($renamed['source_group_name'] ?? '') !== 'New group'
|
||||
|| ($renamed['legacy_channel_name'] ?? '') !== 'Old tag name'
|
||||
|| ($renamed['legacy_source_tag_name'] ?? '') !== 'Old tag name') {
|
||||
throw new RuntimeException('Automatic channel name was not refreshed with backward-compatible aliases');
|
||||
}
|
||||
|
||||
$manual = $byCode['manual-name'] ?? null;
|
||||
if (!is_array($manual)
|
||||
|| ($manual['channel_name'] ?? '') !== 'Manual campaign label'
|
||||
|| ($manual['source_tag_name'] ?? '') !== 'Renamed manual tag'
|
||||
|| ($manual['legacy_source_tag_name'] ?? '') !== 'Old manual tag') {
|
||||
throw new RuntimeException('Manual display name or refreshed tag metadata was not preserved');
|
||||
}
|
||||
|
||||
if (isset($byCode['disabled-tag']) || isset($byCode['tag_tag-disabled'])) {
|
||||
throw new RuntimeException('Explicitly disabled tag was reintroduced');
|
||||
}
|
||||
|
||||
$newTag = $byCode['tag_tag-new'] ?? null;
|
||||
if (!is_array($newTag)
|
||||
|| ($newTag['channel_name'] ?? '') !== 'Newly discovered tag'
|
||||
|| ($newTag['source_tag_id'] ?? '') !== 'tag-new') {
|
||||
throw new RuntimeException('New relation-table tag was not added with a deterministic channel code');
|
||||
}
|
||||
|
||||
if (!isset($byCode['legacy-name-only'])) {
|
||||
throw new RuntimeException('Historical name-only channel was removed');
|
||||
}
|
||||
|
||||
if (in_array('--integration', $argv, true)) {
|
||||
$app = new think\App();
|
||||
$app->initialize();
|
||||
|
||||
$loadTagsMethod = new ReflectionMethod(MediaChannelService::class, 'loadCurrentTagRows');
|
||||
$loadTagsMethod->setAccessible(true);
|
||||
$activeRowsMethod = new ReflectionMethod(MediaChannelService::class, 'getActiveChannelRows');
|
||||
$activeRowsMethod->setAccessible(true);
|
||||
|
||||
$startedAt = microtime(true);
|
||||
/** @var array<int, array<string, mixed>> $currentTags */
|
||||
$currentTags = $loadTagsMethod->invoke(null, null);
|
||||
/** @var array<int, array<string, mixed>> $activeRows */
|
||||
$activeRows = $activeRowsMethod->invoke(null);
|
||||
$options = MediaChannelService::getOptions();
|
||||
$elapsedMs = round((microtime(true) - $startedAt) * 1000, 1);
|
||||
|
||||
$activeByTagId = [];
|
||||
foreach ($activeRows as $row) {
|
||||
$tagId = trim((string) ($row['source_tag_id'] ?? ''));
|
||||
if ($tagId !== '') {
|
||||
$activeByTagId[$tagId] = $row;
|
||||
}
|
||||
}
|
||||
$disabledTagIds = array_fill_keys(array_map(
|
||||
'strval',
|
||||
Db::name('qywx_media_channel')
|
||||
->where('status', 0)
|
||||
->where('source_tag_id', '<>', '')
|
||||
->column('source_tag_id')
|
||||
), true);
|
||||
|
||||
foreach ($currentTags as $tag) {
|
||||
$tagId = (string) ($tag['source_tag_id'] ?? '');
|
||||
if ($tagId === '' || isset($disabledTagIds[$tagId])) {
|
||||
continue;
|
||||
}
|
||||
$channel = $activeByTagId[$tagId] ?? null;
|
||||
if (!is_array($channel)) {
|
||||
throw new RuntimeException("Current relation-table tag {$tagId} is absent from channel options");
|
||||
}
|
||||
$currentName = (string) ($tag['source_tag_name'] ?? '');
|
||||
if ($currentName !== '' && ($channel['source_tag_name'] ?? '') !== $currentName) {
|
||||
throw new RuntimeException("Current tag name for {$tagId} was not refreshed");
|
||||
}
|
||||
}
|
||||
|
||||
if (count($options) !== count($activeRows)) {
|
||||
throw new RuntimeException('Public option count differs from merged active channel count');
|
||||
}
|
||||
|
||||
$matchingNames = array_values(array_map(
|
||||
static fn (array $option): string => (string) ($option['name'] ?? ''),
|
||||
array_filter(
|
||||
$options,
|
||||
static fn (array $option): bool => mb_strpos((string) ($option['name'] ?? ''), '4') !== false
|
||||
)
|
||||
));
|
||||
echo json_encode([
|
||||
'current_tag_count' => count($currentTags),
|
||||
'channel_option_count' => count($options),
|
||||
'elapsed_ms' => $elapsedMs,
|
||||
'matching_4' => $matchingNames,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
|
||||
}
|
||||
|
||||
echo "MEDIA_CHANNEL_OPTIONS_MERGE_OK\n";
|
||||
Reference in New Issue
Block a user