202 lines
7.8 KiB
PHP
202 lines
7.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use app\common\service\qywx\MediaChannelService;
|
|
use app\common\service\qywx\QywxExternalContactEventTagSnapshotService;
|
|
use think\facade\Db;
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
$app = new think\App();
|
|
$app->initialize();
|
|
|
|
if (!str_contains(
|
|
(string) file_get_contents(dirname(__DIR__) . '/app/common/service/qywx/MediaChannelService.php'),
|
|
'$tagIds === [] || !QywxExternalContactEventTagSnapshotService::installed()'
|
|
)) {
|
|
throw new RuntimeException('事件渠道快照缺表时未降级,代码先于迁移发布会导致接口 500');
|
|
}
|
|
|
|
$tagQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyExternalUserChannelFilter(
|
|
$tagQuery,
|
|
'e.external_userid',
|
|
[
|
|
'source_tag_id' => 'tag-regression-id',
|
|
'source_tag_name' => '回归渠道',
|
|
],
|
|
'e.user_id'
|
|
);
|
|
$tagSql = (string)$tagQuery->fetchSql()->select();
|
|
if (!str_contains($tagSql, 'qywx_external_contact_tag')) {
|
|
throw new RuntimeException('tag 渠道未使用结构化客户标签关系表');
|
|
}
|
|
if (!str_contains($tagSql, 'EXISTS (SELECT 1 FROM')) {
|
|
throw new RuntimeException('tag 渠道未使用 EXISTS 半连接,避免物化整渠客户 ID');
|
|
}
|
|
if (!str_contains($tagSql, 'channel_tag.external_userid = e.external_userid')) {
|
|
throw new RuntimeException('tag 渠道未按事实表 external_userid 相关查询');
|
|
}
|
|
if (!str_contains($tagSql, 'channel_tag.follow_user_id = e.user_id')) {
|
|
throw new RuntimeException('tag 渠道未限定为产生事件的同一企微员工');
|
|
}
|
|
if (!str_contains($tagSql, 'tag_id = ')) {
|
|
throw new RuntimeException('单标签渠道应使用 tag_id = 走组合索引');
|
|
}
|
|
if (str_contains($tagSql, 'tag_id IN (')) {
|
|
throw new RuntimeException('单标签渠道不应退化为 tag_id IN');
|
|
}
|
|
if (str_contains($tagSql, 'follow_users') || str_contains($tagSql, 'LIKE')) {
|
|
throw new RuntimeException('tag 渠道仍在扫描 follow_users JSON');
|
|
}
|
|
|
|
$customerLevelTagQuery = Db::name('order')->alias('o');
|
|
MediaChannelService::applyExternalUserChannelFilter(
|
|
$customerLevelTagQuery,
|
|
'o.payer_external_userid',
|
|
[
|
|
'source_tag_id' => 'tag-order-id',
|
|
'source_tag_name' => '订单渠道',
|
|
]
|
|
);
|
|
$customerLevelTagSql = (string)$customerLevelTagQuery->fetchSql()->select();
|
|
if (!str_contains($customerLevelTagSql, 'channel_tag.external_userid = o.payer_external_userid')
|
|
|| str_contains($customerLevelTagSql, 'channel_tag.follow_user_id')) {
|
|
throw new RuntimeException('没有员工维度的订单事实不应被强行关联不存在的企微员工字段');
|
|
}
|
|
|
|
$legacyQuery = Db::name('order')->alias('o');
|
|
MediaChannelService::applyExternalUserChannelFilter(
|
|
$legacyQuery,
|
|
'o.payer_external_userid',
|
|
[
|
|
'source_tag_id' => '',
|
|
'source_tag_name' => '仅名称老渠道',
|
|
]
|
|
);
|
|
$legacySql = (string)$legacyQuery->fetchSql()->select();
|
|
if (!str_contains($legacySql, ' IN (SELECT channel_contact.external_userid')) {
|
|
throw new RuntimeException('老渠道回退未使用去重 external_userid 子查询');
|
|
}
|
|
if (!str_contains($legacySql, 'channel_contact.delete_time IS NULL')) {
|
|
throw new RuntimeException('老渠道回退包含了已删除客户记录');
|
|
}
|
|
|
|
$groupQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyExternalUserChannelFilter(
|
|
$groupQuery,
|
|
'e.external_userid',
|
|
[
|
|
'source_tag_id' => '',
|
|
'source_tag_ids' => ['tag-group-a', 'tag-group-b'],
|
|
'channel_name' => '自媒体4',
|
|
'is_group' => true,
|
|
],
|
|
'e.user_id'
|
|
);
|
|
$groupSql = (string)$groupQuery->fetchSql()->select();
|
|
if (!str_contains($groupSql, 'EXISTS (SELECT 1 FROM')) {
|
|
throw new RuntimeException('分组渠道未使用 EXISTS 半连接');
|
|
}
|
|
if (!str_contains($groupSql, 'tag_id IN (')) {
|
|
throw new RuntimeException('分组渠道未按多个 tag_id 过滤');
|
|
}
|
|
if (!str_contains($groupSql, 'channel_tag.follow_user_id = e.user_id')) {
|
|
throw new RuntimeException('分组渠道未限定为产生事件的同一企微员工');
|
|
}
|
|
if (str_contains($groupSql, 'follow_users') || str_contains($groupSql, 'LIKE')) {
|
|
throw new RuntimeException('分组渠道仍在扫描 follow_users JSON');
|
|
}
|
|
|
|
$historicalQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyHistoricalExternalUserChannelFilter(
|
|
$historicalQuery,
|
|
'e.external_userid',
|
|
[
|
|
'source_tag_id' => 'deleted-fan-channel-id',
|
|
'source_tag_name' => '已删粉丝渠道',
|
|
],
|
|
'e.user_id'
|
|
);
|
|
$historicalSql = (string)$historicalQuery->fetchSql()->select();
|
|
if (!str_contains($historicalSql, 'historical_channel_contact.external_userid = e.external_userid')) {
|
|
throw new RuntimeException('已删粉丝渠道未按 external_userid 关联历史客户快照');
|
|
}
|
|
if (!str_contains($historicalSql, 'JSON_SEARCH')
|
|
|| !str_contains($historicalSql, 'e.user_id')
|
|
|| !str_contains($historicalSql, 'JSON_CONTAINS')) {
|
|
throw new RuntimeException('已删粉丝渠道未在保留的 follow_users 中精确匹配事件员工标签');
|
|
}
|
|
if (str_contains($historicalSql, 'historical_channel_contact.delete_time IS NULL')) {
|
|
throw new RuntimeException('已删粉丝渠道错误排除了软删客户');
|
|
}
|
|
|
|
$installedProperty = (new ReflectionClass(QywxExternalContactEventTagSnapshotService::class))
|
|
->getProperty('installed');
|
|
$installedProperty->setAccessible(true);
|
|
$installedProperty->setValue(null, true);
|
|
|
|
$eventSnapshotQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyExternalUserEventChannelFilter(
|
|
$eventSnapshotQuery,
|
|
'e.id',
|
|
'e.external_userid',
|
|
'e.user_id',
|
|
[
|
|
'source_tag_id' => '',
|
|
'source_tag_ids' => ['tag-event-a', 'tag-event-b'],
|
|
'is_group' => true,
|
|
]
|
|
);
|
|
$eventSnapshotSql = (string)$eventSnapshotQuery->fetchSql()->select();
|
|
foreach ([
|
|
'qywx_external_contact_event_tag',
|
|
'event_channel_tag.event_id = e.id',
|
|
'event_channel_tag.follow_user_id = e.user_id',
|
|
'captured_event_channel.event_id = e.id',
|
|
"captured_event_channel.tag_id = ''",
|
|
'channel_tag.follow_user_id = e.user_id',
|
|
] as $needle) {
|
|
if (!str_contains($eventSnapshotSql, $needle)) {
|
|
throw new RuntimeException('新增事件渠道快照查询缺少条件:' . $needle);
|
|
}
|
|
}
|
|
if (!str_contains($eventSnapshotSql, 'event_channel_tag.tag_id IN (')) {
|
|
throw new RuntimeException('新增事件分组渠道未按多个快照 tag_id 过滤');
|
|
}
|
|
|
|
$deletedEventSnapshotQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyExternalUserEventChannelFilter(
|
|
$deletedEventSnapshotQuery,
|
|
'e.id',
|
|
'e.external_userid',
|
|
'e.user_id',
|
|
['source_tag_id' => 'tag-deleted-event'],
|
|
true
|
|
);
|
|
$deletedEventSnapshotSql = (string)$deletedEventSnapshotQuery->fetchSql()->select();
|
|
if (!str_contains($deletedEventSnapshotSql, 'event_channel_tag.event_id = e.id')
|
|
|| !str_contains($deletedEventSnapshotSql, 'JSON_SEARCH')
|
|
|| !str_contains($deletedEventSnapshotSql, 'JSON_CONTAINS')) {
|
|
throw new RuntimeException('已删新增事件未优先用事件快照,并按同一员工执行老数据回退');
|
|
}
|
|
|
|
$installedProperty->setValue(null, false);
|
|
$missingSnapshotQuery = Db::name('qywx_external_contact_event')->alias('e');
|
|
MediaChannelService::applyExternalUserEventChannelFilter(
|
|
$missingSnapshotQuery,
|
|
'e.id',
|
|
'e.external_userid',
|
|
'e.user_id',
|
|
['source_tag_id' => 'tag-before-migration']
|
|
);
|
|
$missingSnapshotSql = (string)$missingSnapshotQuery->fetchSql()->select();
|
|
if (str_contains($missingSnapshotSql, 'qywx_external_contact_event_tag')
|
|
|| !str_contains($missingSnapshotSql, 'channel_tag.follow_user_id = e.user_id')) {
|
|
throw new RuntimeException('快照表未安装时没有安全降级到同员工当前标签口径');
|
|
}
|
|
$installedProperty->setValue(null, null);
|
|
|
|
echo "MEDIA_CHANNEL_EXTERNAL_USER_FILTER_OK\n";
|