169 lines
7.0 KiB
PHP
169 lines
7.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use app\adminapi\logic\firstvisit\WecomPromotionLogic;
|
||
use app\common\service\qywx\QywxCustomerAcquisitionApiService;
|
||
use think\facade\Db;
|
||
|
||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||
|
||
$app = new think\App();
|
||
$app->initialize();
|
||
|
||
final class PromotionDeleteApiFake extends QywxCustomerAcquisitionApiService
|
||
{
|
||
/** @var list<string> */
|
||
public array $deleted = [];
|
||
|
||
public function __construct(
|
||
private string $failLinkId = '',
|
||
private string $failMessage = 'mock remote deletion failed',
|
||
private int $failCode = 0
|
||
) {
|
||
}
|
||
|
||
public function deleteLink(string $linkId): void
|
||
{
|
||
$this->deleted[] = $linkId;
|
||
if ($linkId === $this->failLinkId) {
|
||
throw new RuntimeException($this->failMessage, $this->failCode);
|
||
}
|
||
}
|
||
}
|
||
|
||
$assert = static function (bool $condition, string $message): void {
|
||
if (!$condition) {
|
||
throw new RuntimeException($message);
|
||
}
|
||
};
|
||
$admin = Db::name('admin')->where('root', 1)->whereNull('delete_time')->find();
|
||
if (!$admin) {
|
||
throw new RuntimeException('未找到 root 管理员,无法执行删除行为测试');
|
||
}
|
||
$now = time();
|
||
$suffix = bin2hex(random_bytes(6));
|
||
$firstRemoteId = 'contract_first_' . $suffix;
|
||
$secondRemoteId = 'contract_second_' . $suffix;
|
||
|
||
Db::startTrans();
|
||
try {
|
||
$poolId = (int) Db::name('qywx_promotion_pool')->insertGetId([
|
||
'name' => '删除契约测试',
|
||
'public_key' => bin2hex(random_bytes(16)),
|
||
'status' => 1,
|
||
'owner_admin_id' => (int) $admin['id'],
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
$firstLinkId = (int) Db::name('qywx_promotion_link')->insertGetId([
|
||
'pool_id' => $poolId,
|
||
'name' => '当前官方链接',
|
||
'wecom_url' => 'https://work.weixin.qq.com/ca/' . $firstRemoteId,
|
||
'remote_link_id' => $firstRemoteId,
|
||
'remote_status' => 1,
|
||
'status' => 1,
|
||
'owner_admin_id' => (int) $admin['id'],
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
$secondLinkId = (int) Db::name('qywx_promotion_link')->insertGetId([
|
||
'pool_id' => $poolId,
|
||
'name' => '历史已软删官方链接',
|
||
'wecom_url' => 'https://work.weixin.qq.com/ca/' . $secondRemoteId,
|
||
'remote_link_id' => $secondRemoteId,
|
||
'remote_status' => 1,
|
||
'status' => 0,
|
||
'owner_admin_id' => (int) $admin['id'],
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
'delete_time' => $now - 10,
|
||
]);
|
||
$memberId = (int) Db::name('qywx_promotion_pool_member')->insertGetId([
|
||
'pool_id' => $poolId,
|
||
'admin_id' => (int) $admin['id'],
|
||
'userid' => 'contract_user_' . $suffix,
|
||
'enabled' => 1,
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
Db::name('qywx_promotion_range_sync')->insert([
|
||
'pool_id' => $poolId,
|
||
'promotion_link_id' => $firstLinkId,
|
||
'desired_version' => 1,
|
||
'applied_version' => 1,
|
||
'status' => 0,
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
|
||
$firstAttempt = new PromotionDeleteApiFake(
|
||
$secondRemoteId,
|
||
'企业微信获客助手接口失败[40058]:invalid range',
|
||
40058
|
||
);
|
||
$message = '';
|
||
try {
|
||
WecomPromotionLogic::deletePool($poolId, (int) $admin['id'], $admin, $firstAttempt);
|
||
} catch (RuntimeException $e) {
|
||
$message = $e->getMessage();
|
||
}
|
||
$assert(str_contains($message, '本地方案已保留'), '远端部分删除失败没有明确保留本地方案');
|
||
$assert($firstAttempt->deleted === [$firstRemoteId, $secondRemoteId], '首次删除没有按顺序处理全部远端链接');
|
||
$assert(Db::name('qywx_promotion_pool')->where('id', $poolId)->whereNull('delete_time')->count() === 1, '远端失败时错误删除了本地方案');
|
||
$assert((int) Db::name('qywx_promotion_link')->where('id', $firstLinkId)->value('remote_status') === 2, '部分成功的远端删除进度没有保存');
|
||
$assert((int) Db::name('qywx_promotion_link')->where('id', $secondLinkId)->value('remote_status') === 1, '失败的远端链接被错误标记为已删除');
|
||
$assert((int) Db::name('qywx_promotion_pool_member')->where('id', $memberId)->value('enabled') === 1, '远端失败时错误禁用了方案成员');
|
||
|
||
$retry = new PromotionDeleteApiFake();
|
||
WecomPromotionLogic::deletePool($poolId, (int) $admin['id'], $admin, $retry);
|
||
$assert($retry->deleted === [$secondRemoteId], '重试时重复删除了已经成功的远端链接');
|
||
$assert(Db::name('qywx_promotion_pool')->where('id', $poolId)->whereNotNull('delete_time')->count() === 1, '远端全部成功后没有软删除本地方案');
|
||
$assert((int) Db::name('qywx_promotion_link')->where('id', $secondLinkId)->value('remote_status') === 2, '历史软删链接没有同步删除企业微信端');
|
||
$assert((int) Db::name('qywx_promotion_pool_member')->where('id', $memberId)->value('enabled') === 0, '方案删除后成员仍处于启用状态');
|
||
$sync = Db::name('qywx_promotion_range_sync')->where('pool_id', $poolId)->find();
|
||
$assert((int) ($sync['status'] ?? 0) === 4 && (string) ($sync['lock_token'] ?? '') === '', '方案删除后同步租约没有终止');
|
||
|
||
// 企业微信明确返回 invalid link_id,说明远端目标已不存在或当前应用已无法操作;
|
||
// 删除应保持幂等并继续清理本地方案,不能让陈旧 link_id 永久阻塞用户。
|
||
$staleRemoteId = 'contract_stale_' . $suffix;
|
||
$stalePoolId = (int) Db::name('qywx_promotion_pool')->insertGetId([
|
||
'name' => '失效链接删除测试',
|
||
'public_key' => bin2hex(random_bytes(16)),
|
||
'status' => 1,
|
||
'owner_admin_id' => (int) $admin['id'],
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
$staleLinkId = (int) Db::name('qywx_promotion_link')->insertGetId([
|
||
'pool_id' => $stalePoolId,
|
||
'name' => '远端已失效链接',
|
||
'wecom_url' => 'https://work.weixin.qq.com/ca/' . $staleRemoteId,
|
||
'remote_link_id' => $staleRemoteId,
|
||
'remote_status' => 1,
|
||
'status' => 1,
|
||
'owner_admin_id' => (int) $admin['id'],
|
||
'create_time' => $now,
|
||
'update_time' => $now,
|
||
]);
|
||
$staleApi = new PromotionDeleteApiFake(
|
||
$staleRemoteId,
|
||
'企业微信获客助手接口失败[40058]:invalid link_id',
|
||
40058
|
||
);
|
||
WecomPromotionLogic::deletePool($stalePoolId, (int) $admin['id'], $admin, $staleApi);
|
||
$assert($staleApi->deleted === [$staleRemoteId], '失效链接没有请求企业微信删除接口');
|
||
$assert(
|
||
Db::name('qywx_promotion_pool')->where('id', $stalePoolId)->whereNotNull('delete_time')->count() === 1,
|
||
'invalid link_id 阻塞了本地方案删除'
|
||
);
|
||
$assert(
|
||
(int) Db::name('qywx_promotion_link')->where('id', $staleLinkId)->value('remote_status') === 2,
|
||
'invalid link_id 没有按远端已删除状态收敛'
|
||
);
|
||
} finally {
|
||
Db::rollback();
|
||
}
|
||
|
||
echo "WECOM_PROMOTION_DELETE_POOL_BEHAVIOR_OK\n";
|