;rgb:0000/0000/0000

This commit is contained in:
gr
2026-09-09 10:02:43 +08:00
640 changed files with 51641 additions and 20099 deletions
@@ -6,8 +6,11 @@ namespace app\api\controller;
use app\adminapi\logic\qywx\CustomerLogic;
use app\common\service\qywx\QywxCustomerAcquisitionCustomerService;
use app\common\service\qywx\QywxExternalContactEventTagSnapshotService;
use app\common\service\qywx\QywxPromotionMemberSchedulerService;
use app\common\service\qywx\QywxPromotionRangeSyncService;
use app\common\service\qywx\QywxPromotionAutomationService;
use app\common\service\qywx\QywxPromotionEnqueueException;
use EasyWeChat\Kernel\Exceptions\BadRequestException;
use EasyWeChat\Work\Application;
use EasyWeChat\Work\Message;
@@ -64,6 +67,9 @@ class QywxExternalContactCallbackController extends BaseApiController
$server->addEventListener('change_external_contact', function (Message $message, \Closure $next) {
try {
$this->handleChangeExternalContact($message);
} catch (QywxPromotionEnqueueException $e) {
// 未持久化不能假应答成功:外层返回500,让企微重新投递。
throw $e;
} catch (\Throwable $e) {
Log::error('qywx external contact callback: ' . $e->getMessage(), [
'exception' => $e,
@@ -96,6 +102,11 @@ class QywxExternalContactCallbackController extends BaseApiController
}
return response($content, 200, $headers);
} catch (QywxPromotionEnqueueException) {
// 异常调用栈可能携带原始事件参数;这里只记固定信息,不记录WelcomeCode。
Log::error('qywx external contact callback: promotion event persistence failed');
return response('error', 500, ['Content-Type' => 'text/plain; charset=utf-8']);
} catch (BadRequestException $e) {
Log::warning('qywx external contact callback: bad request ' . $e->getMessage());
@@ -120,9 +131,17 @@ class QywxExternalContactCallbackController extends BaseApiController
$failReason = (string) ($message['FailReason'] ?? '');
$eventTime = (int) ($message['CreateTime'] ?? 0);
// 只接管已保存新配置且可核验方案/成员的推广事件。
// 回调内立即尝试欢迎语和正式客户标签,常驻/分钟任务继续承担重试及其余慢动作。
// 同时处理带欢迎码的半客户,避免原来的半客户早返回吞掉20秒欢迎语窗口。
$event = $message instanceof Message ? $message->toArray() : (array) $message;
$queued = (new QywxPromotionAutomationService())->enqueueVerifiedEvent($event, true);
$auditEvent = $event;
unset($auditEvent['WelcomeCode']);
// 事件流水:一进来就落库(幂等),用于"今天进来多少人"等零误差统计;
// 独立于业务 UPSERT,即便后续 DB 逻辑抛错也不影响计数。
CustomerLogic::recordExternalContactEvent([
$eventId = CustomerLogic::recordExternalContactEvent([
'change_type' => $changeType,
'user_id' => $userId,
'external_userid' => $extId,
@@ -130,9 +149,16 @@ class QywxExternalContactCallbackController extends BaseApiController
'fail_reason' => $failReason,
'welcome_code' => $welcomeCode !== '' ? 1 : 0,
'event_time' => $eventTime,
'raw' => $message,
'raw' => $auditEvent,
]);
if ($queued) {
// 推广任务先于事件流水执行;事件落库后再用任务内不可变配置补写渠道快照。
QywxExternalContactEventTagSnapshotService::captureForPromotionEvent($eventId);
// 即时通道已尝试欢迎语/标签;分钟补偿完成备注、成员记账、范围与客户资料同步。
return;
}
if ($extId === '') {
Log::info(sprintf('qywx external contact callback: 无 ExternalUserID type=%s user=%s', $changeType, $userId));
@@ -204,6 +230,10 @@ class QywxExternalContactCallbackController extends BaseApiController
}
}
// 其余变更(添加/编辑/转接成功/标签变化等):以 get 详情为准 UPSERT,避免遗漏未枚举的 ChangeType
CustomerLogic::upsertSingleExternalContactFromApi($extId);
CustomerLogic::upsertSingleExternalContactFromApi(
$extId,
$changeType === 'add_external_contact' ? $eventId : 0,
$userId
);
}
}