更新
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$root = dirname(__DIR__, 2);
|
||||
$paths = [
|
||||
'callback' => $root . '/server/app/api/controller/QywxExternalContactCallbackController.php',
|
||||
'logic' => $root . '/server/app/adminapi/logic/firstvisit/WecomPromotionLogic.php',
|
||||
'initialMigration' => $root . '/server/sql/1.9.20260831/add_wecom_promotion_automation.sql',
|
||||
'upgradeMigration' => $root . '/server/sql/1.9.20260901/upgrade_qywx_promotion_automation_runtime.sql',
|
||||
'compose' => $root . '/docker/docker-compose.yml',
|
||||
];
|
||||
|
||||
$sources = [];
|
||||
foreach ($paths as $name => $path) {
|
||||
$source = file_get_contents($path);
|
||||
if (!is_string($source)) {
|
||||
throw new RuntimeException("无法读取 {$name}: {$path}");
|
||||
}
|
||||
$sources[$name] = $source;
|
||||
}
|
||||
|
||||
if (!str_contains($sources['callback'], 'enqueueVerifiedEvent($event, true)')) {
|
||||
throw new RuntimeException('企微回调未启用欢迎语和标签即时通道');
|
||||
}
|
||||
if (!str_contains($sources['logic'], "'automation_saved' => \$automation !== null")) {
|
||||
throw new RuntimeException('保存接口未向前端确认自动化配置已落库');
|
||||
}
|
||||
foreach (['initialMigration', 'upgradeMigration'] as $name) {
|
||||
foreach (['qywx:retry-promotion-automation', 'qywx:refresh-promotion-media'] as $command) {
|
||||
if (!str_contains($sources[$name], $command)) {
|
||||
throw new RuntimeException("{$name} 缺少 {$command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!str_contains($sources['compose'], 'qywx:work-promotion-automation')) {
|
||||
throw new RuntimeException('Docker 部署未启动企微欢迎语常驻进程');
|
||||
}
|
||||
|
||||
echo "QYWX_PROMOTION_AUTOMATION_RUNTIME_CONTRACT_OK\n";
|
||||
@@ -72,9 +72,10 @@ final class AutomationMemoryStore extends QywxPromotionAutomationStore
|
||||
public function due(string $lane, int $now, int $limit): array {
|
||||
return array_keys(array_filter($this->rows, static function (array $r) use ($lane, $now): bool {
|
||||
$pending = in_array($r['welcome_status'], ['pending', 'retry', 'running'], true);
|
||||
return $r['status'] !== 'done' && $r['lock_until'] <= $now && ($lane === 'welcome'
|
||||
? $pending && $r['welcome_next_retry'] <= $now
|
||||
: (!$pending || $r['welcome_expires_at'] <= $now) && $r['next_retry'] <= $now);
|
||||
if ($r['status'] === 'done' || $r['lock_until'] > $now) { return false; }
|
||||
if ($lane === 'welcome') { return $pending && $r['welcome_next_retry'] <= $now; }
|
||||
if ($lane === 'inline_metadata') { return $r['next_retry'] <= $now; }
|
||||
return (!$pending || $r['welcome_expires_at'] <= $now) && $r['next_retry'] <= $now;
|
||||
}));
|
||||
}
|
||||
public function claim(int $id, string $lane, int $now): ?array {
|
||||
@@ -130,6 +131,17 @@ automationCheck(count(array_filter($api->calls, static fn (array $v): bool => $v
|
||||
automationCheck(count(array_filter($api->calls, static fn (array $v): bool => $v[0] === 'remark')) === 2, 'do not repeat successful remark/description');
|
||||
automationCheck(!str_contains(json_encode($store->logs), 'NEVER_LOG_THIS_CODE'), 'audit contains no code');
|
||||
|
||||
[$service, $store, $api] = $fixture();
|
||||
$api->welcomeError = new QywxPromotionContactApiException('other app currently sending', 41096);
|
||||
automationCheck($service->enqueueVerifiedEvent(array_replace($event, ['CreateTime' => $now]), true), 'callback immediate lane handled');
|
||||
$actions = json_decode($store->rows[1]['actions_json'], true);
|
||||
automationCheck(array_column($api->calls, 0) === ['welcome', 'tags'], 'callback immediately attempts welcome then tags');
|
||||
automationCheck(
|
||||
$actions['welcome']['status'] === 'retry' && $actions['tags']['status'] === 'success',
|
||||
'tag send is independent from retryable welcome: ' . json_encode([$actions['welcome'], $actions['tags']])
|
||||
);
|
||||
automationCheck($store->syncs === [], 'callback immediate lane leaves slow actions to background job');
|
||||
|
||||
foreach (['default', 'none'] as $mode) {
|
||||
[$service, $store, $api] = $fixture(); $store->config['welcome_mode'] = $mode;
|
||||
$service->enqueueVerifiedEvent(array_replace($event, ['CreateTime' => $now]));
|
||||
|
||||
@@ -3,6 +3,8 @@ import fs from 'node:fs'
|
||||
import ts from '../../admin/node_modules/typescript/lib/typescript.js'
|
||||
|
||||
const source = fs.readFileSync(new URL('../../admin/src/views/first_visit/wecom_promotion/components/promotion-automation.ts', import.meta.url), 'utf8')
|
||||
const formSource = fs.readFileSync(new URL('../../admin/src/views/first_visit/wecom_promotion/components/PromotionAutomationForm.vue', import.meta.url), 'utf8')
|
||||
const pageSource = fs.readFileSync(new URL('../../admin/src/views/first_visit/wecom_promotion/index.vue', import.meta.url), 'utf8')
|
||||
const compiled = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }).outputText
|
||||
const { defaultAutomationConfig, cloneAutomationConfig, serializeAutomationConfig, validateAutomationConfig, validateCustomTagName, validateWelcomeMessage, welcomeScheduleOverlap, previewTemplate } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`)
|
||||
|
||||
@@ -58,4 +60,7 @@ assert.equal(welcomeScheduleOverlap([
|
||||
]), false)
|
||||
assert.match(previewTemplate('{customer_name}-{employee_name}-{add_time}', '小陈'), /^张女士-小陈-\d{4}-\d{2}-\d{2}$/)
|
||||
assert.equal(Array.from(previewTemplate('王'.repeat(30), '小陈', 20)).length, 20)
|
||||
assert.match(formSource, /不会写入企微获客链接详情中的“欢迎语\/客户标签”配置/)
|
||||
assert.match(formSource, /客户添加回调中立即发送渠道欢迎语并添加标签/)
|
||||
assert.match(pageSource, /result\?\.automation_saved !== true/)
|
||||
console.log('WECOM_PROMOTION_AUTOMATION_UI_OK')
|
||||
|
||||
@@ -13,6 +13,18 @@ namespace app\adminapi\logic\auth {
|
||||
|
||||
namespace app\common\service\qywx {
|
||||
// API行为由MockHandler测试;此处只验证控制器权限、HTTP方法及参数边界。
|
||||
class QywxPromotionOperatorAccess
|
||||
{
|
||||
public static function hasBasePagePermission(int $adminId, array $adminInfo): bool
|
||||
{
|
||||
return !empty($adminInfo['root'])
|
||||
|| in_array('firstvisit.wecomPromotion/overview', \app\adminapi\logic\auth\AuthLogic::getAuthByAdminId($adminId), true);
|
||||
}
|
||||
public static function hasPagePermission(int $adminId, array $adminInfo): bool
|
||||
{
|
||||
return self::hasBasePagePermission($adminId, $adminInfo);
|
||||
}
|
||||
}
|
||||
class QywxPromotionContactApiService
|
||||
{
|
||||
public static array $calls = [];
|
||||
|
||||
Reference in New Issue
Block a user