更新
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
function eventTagSnapshotExpect(bool $condition, string $message): void
|
||||
{
|
||||
if (!$condition) {
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
$root = dirname(__DIR__);
|
||||
$snapshotSource = file_get_contents(
|
||||
$root . '/app/common/service/qywx/QywxExternalContactEventTagSnapshotService.php'
|
||||
);
|
||||
$customerSource = file_get_contents($root . '/app/adminapi/logic/qywx/CustomerLogic.php');
|
||||
$callbackSource = file_get_contents($root . '/app/api/controller/QywxExternalContactCallbackController.php');
|
||||
$storeSource = file_get_contents($root . '/app/common/service/qywx/QywxPromotionAutomationStore.php');
|
||||
$datedMigration = file_get_contents($root . '/sql/1.9.20260904/add_qywx_external_contact_event_tag.sql');
|
||||
$baseMigration = file_get_contents($root . '/database/migrations/create_qywx_external_contact_event_tag.sql');
|
||||
|
||||
foreach ([$snapshotSource, $customerSource, $callbackSource, $storeSource, $datedMigration, $baseMigration] as $source) {
|
||||
eventTagSnapshotExpect(is_string($source), '无法读取事件标签快照实现或迁移文件');
|
||||
}
|
||||
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($snapshotSource, "'tag_id' => ''")
|
||||
&& str_contains($snapshotSource, 'Db::transaction(')
|
||||
&& str_contains($snapshotSource, 'INSERT IGNORE INTO')
|
||||
&& str_contains($snapshotSource, 'if ($inserted === 0)'),
|
||||
'事件标签快照必须原子写入完成标记,并对重复回调保持幂等'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($snapshotSource, 'public static function installed(): bool')
|
||||
&& str_contains($snapshotSource, "str_contains(\$message, '1146')"),
|
||||
'快照服务必须能识别迁移缺表,供读取侧安全降级'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
!str_contains($snapshotSource, '->delete(')
|
||||
&& !str_contains($snapshotSource, '->update('),
|
||||
'历史事件标签快照只能追加,不能随当前客户关系删除或改写'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($snapshotSource, "trim((string) (\$followUser['userid'] ?? '')) !== \$followUserId")
|
||||
&& str_contains($snapshotSource, "['tags']['status']")
|
||||
&& str_contains($snapshotSource, "\$tagStatus !== 'success'")
|
||||
&& str_contains($snapshotSource, 'self::appendTags('),
|
||||
'快照必须只读取事件员工标签,推广成功标签只能追加证据而不能提前冻结完整快照'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($customerSource, 'public static function recordExternalContactEvent(array $data): int')
|
||||
&& str_contains($customerSource, 'QywxExternalContactEventTagSnapshotService::captureFromFollowUsers('),
|
||||
'事件入库必须返回事件ID,并在客户详情同步后按员工保存快照'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($callbackSource, '$eventId = CustomerLogic::recordExternalContactEvent([')
|
||||
&& str_contains($callbackSource, 'QywxExternalContactEventTagSnapshotService::captureForPromotionEvent($eventId)')
|
||||
&& str_contains($callbackSource, "\$changeType === 'add_external_contact' ? \$eventId : 0"),
|
||||
'普通新增与推广新增回调都必须把标签快照绑定到实际事件ID'
|
||||
);
|
||||
eventTagSnapshotExpect(
|
||||
str_contains($storeSource, 'QywxExternalContactEventTagSnapshotService::captureFromPromotionTask($row)'),
|
||||
'推广标签动作异步重试成功后必须补写事件标签快照'
|
||||
);
|
||||
|
||||
foreach ([$datedMigration, $baseMigration] as $migration) {
|
||||
foreach ([
|
||||
'CREATE TABLE IF NOT EXISTS `zyt_qywx_external_contact_event_tag`',
|
||||
'UNIQUE KEY `uk_event_user_tag` (`event_id`, `follow_user_id`, `tag_id`)',
|
||||
'KEY `idx_tag_event_user` (`tag_id`, `event_id`, `follow_user_id`)',
|
||||
"t.`change_type` = 'add_external_contact'",
|
||||
"'$.tags.status'",
|
||||
'current_tag.`follow_user_id` = e.`user_id`',
|
||||
'existing_snapshot.`event_id` = e.`id`',
|
||||
"existing_snapshot.`tag_id` = ''",
|
||||
] as $needle) {
|
||||
eventTagSnapshotExpect(str_contains($migration, $needle), '事件标签快照迁移缺少契约:' . $needle);
|
||||
}
|
||||
eventTagSnapshotExpect(
|
||||
!str_contains($migration, "IN ('success', 'skipped', 'failed')"),
|
||||
'推广标签未启用或失败不能生成空完成标记,否则会遮蔽客户实际渠道标签'
|
||||
);
|
||||
}
|
||||
|
||||
echo "QYWX_EXTERNAL_CONTACT_EVENT_TAG_SNAPSHOT_OK\n";
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use app\adminapi\logic\firstvisit\WecomPromotionLogic;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$assert = static function (bool $condition, string $message): void {
|
||||
if (!$condition) {
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
};
|
||||
$preview = new ReflectionMethod(WecomPromotionLogic::class, 'previewPoolMemberStatus');
|
||||
$preview->setAccessible(true);
|
||||
$assertDispatchReady = new ReflectionMethod(WecomPromotionLogic::class, 'assertMemberDispatchReady');
|
||||
$assertDispatchReady->setAccessible(true);
|
||||
$today = date('Y-m-d');
|
||||
$members = [
|
||||
['id' => 1, 'admin_id' => 11, 'userid' => 'A', 'enabled' => 1, 'daily_limit' => 0, 'today_count' => 0, 'today_date' => $today],
|
||||
['id' => 2, 'admin_id' => 12, 'userid' => 'B', 'enabled' => 1, 'daily_limit' => 0, 'today_count' => 0, 'today_date' => $today],
|
||||
];
|
||||
|
||||
$result = $preview->invoke(null, $members, [11], 0, []);
|
||||
$assert($result['matched_ids'] === [1], '批量下线没有正确匹配员工规则');
|
||||
$assert($result['update_ids'] === [1], '批量下线没有标记需要更新的规则');
|
||||
$assert((int) $result['members'][0]['enabled'] === 0, '批量下线预览没有更新目标状态');
|
||||
$assert((int) $result['members'][1]['enabled'] === 1, '批量下线错误修改了未选员工');
|
||||
|
||||
$result = $preview->invoke(null, [
|
||||
[
|
||||
'id' => 3,
|
||||
'admin_id' => 13,
|
||||
'userid' => 'C',
|
||||
'enabled' => 0,
|
||||
'daily_limit' => 0,
|
||||
'today_count' => 0,
|
||||
'today_date' => $today,
|
||||
'active_start' => time() + 3600,
|
||||
],
|
||||
], [13], 1, []);
|
||||
$assert(
|
||||
$result['update_ids'] === [3] && (int) $result['members'][0]['enabled'] === 1,
|
||||
'尚未到生效时间的员工也应允许提前批量上线'
|
||||
);
|
||||
|
||||
try {
|
||||
$preview->invoke(null, $members, [11, 12], 0, [], '全员方案');
|
||||
throw new RuntimeException('批量下线不应允许方案变成零上线员工');
|
||||
} catch (ReflectionException $error) {
|
||||
throw $error;
|
||||
} catch (Throwable $error) {
|
||||
$assert(str_contains($error->getMessage(), '至少需要保留一名上线员工'), '零上线员工错误提示不正确');
|
||||
}
|
||||
|
||||
try {
|
||||
$preview->invoke(null, [
|
||||
['id' => 4, 'admin_id' => 14, 'userid' => 'D', 'enabled' => 1, 'daily_limit' => 0, 'today_count' => 0, 'today_date' => $today],
|
||||
['id' => 5, 'admin_id' => 15, 'userid' => 'E', 'enabled' => 1, 'daily_limit' => 1, 'today_count' => 1, 'today_date' => $today],
|
||||
], [14], 0, [], '额度方案');
|
||||
throw new RuntimeException('批量下线不应保留零个当前可用员工');
|
||||
} catch (ReflectionException $error) {
|
||||
throw $error;
|
||||
} catch (Throwable $error) {
|
||||
$assert(str_contains($error->getMessage(), '当前可用'), '零可用员工错误提示不正确');
|
||||
}
|
||||
|
||||
$result = $preview->invoke(null, $members, [999], 0, []);
|
||||
$assert($result['matched_ids'] === [] && $result['update_ids'] === [], '不存在的员工不应产生状态更新');
|
||||
|
||||
$assertDispatchReady->invoke(null, ['blocked' => false, 'queued' => true]);
|
||||
try {
|
||||
$assertDispatchReady->invoke(null, ['blocked' => true, 'queued' => false]);
|
||||
throw new RuntimeException('同步阻塞时不应提交员工状态');
|
||||
} catch (ReflectionException $error) {
|
||||
throw $error;
|
||||
} catch (Throwable $error) {
|
||||
$assert(str_contains($error->getMessage(), '无法同步'), '同步阻塞错误提示不正确');
|
||||
}
|
||||
|
||||
echo "WECOM_PROMOTION_BATCH_MEMBER_STATUS_OK\n";
|
||||
Reference in New Issue
Block a user