This commit is contained in:
Your Name
2026-04-20 09:04:54 +08:00
parent 4097fe0cc6
commit 34ba9222fa
245 changed files with 537 additions and 242 deletions
@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace app\api\controller;
use app\adminapi\logic\qywx\CustomerLogic;
use EasyWeChat\Kernel\Exceptions\BadRequestException;
use EasyWeChat\Work\Application;
use EasyWeChat\Work\Message;
use think\facade\Log;
/**
* 企业微信「客户联系」事件回调(接收事件服务器)
*
* @see https://developer.work.weixin.qq.com/document/path/92130
*/
class QywxExternalContactCallbackController extends BaseApiController
{
/** 免登录:企微服务器回调 */
public array $notNeedLogin = ['notify'];
public function notify()
{
$corpId = (string) config('pay.wechat_work.corp_id', '');
$secret = (string) config('pay.wechat_work.external_pay_secret', '');
$token = (string) config('pay.wechat_work.contact_callback_token', '');
$aesKey = (string) config('pay.wechat_work.contact_callback_aes_key', '');
if ($corpId === '' || $secret === '' || $token === '' || $aesKey === '') {
Log::error('qywx external contact callback: 缺少配置 corp_id / external_pay_secret / contact_callback_token / contact_callback_aes_key');
return response('config error', 503, ['Content-Type' => 'text/plain; charset=utf-8']);
}
try {
$app = new Application([
'corp_id' => $corpId,
'secret' => $secret,
'token' => $token,
'aes_key' => $aesKey,
]);
$server = $app->getServer();
$server->addEventListener('change_external_contact', function (Message $message, \Closure $next) {
try {
$this->handleChangeExternalContact($message);
} catch (\Throwable $e) {
Log::error('qywx external contact callback: ' . $e->getMessage(), [
'exception' => $e,
]);
}
return $next($message);
});
$psr = $server->serve();
$body = $psr->getBody();
$body->rewind();
$content = $body->getContents();
$headers = [];
$contentType = $psr->getHeaderLine('Content-Type');
if ($contentType !== '') {
$headers['Content-Type'] = $contentType;
}
return response($content, 200, $headers);
} catch (BadRequestException $e) {
Log::warning('qywx external contact callback: bad request ' . $e->getMessage());
return response('bad request', 400, ['Content-Type' => 'text/plain; charset=utf-8']);
} catch (\Throwable $e) {
Log::error('qywx external contact callback: serve failed ' . $e->getMessage(), ['exception' => $e]);
return response('error', 500, ['Content-Type' => 'text/plain; charset=utf-8']);
}
}
/**
* @param Message $message
*/
private function handleChangeExternalContact($message): void
{
$changeType = (string) ($message['ChangeType'] ?? '');
$extId = trim((string) ($message['ExternalUserID'] ?? $message['ExternalUserId'] ?? ''));
if ($extId === '') {
Log::info('qywx external contact callback: 无 ExternalUserID', ['ChangeType' => $changeType]);
return;
}
Log::info('qywx external contact callback', ['ChangeType' => $changeType, 'ExternalUserID' => $extId]);
if ($changeType === 'del_external_contact') {
CustomerLogic::softDeleteExternalContactRow($extId);
return;
}
// 其余变更(添加/编辑/删跟进人等):以 get 详情为准 UPSERT,避免遗漏未枚举的 ChangeType
CustomerLogic::upsertSingleExternalContactFromApi($extId);
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
use think\facade\Route;
// 多应用下「api」应用的路由文件;URL 形如 /api/qywx/...,此处规则不含应用前缀 api。
// @see https://doc.thinkphp.cn/v8_0/multi_app_model.html
// 企业微信「客户联系」事件回调:GET 验签(echostr)、POST 收事件
Route::rule('qywx/external-contact/notify', 'QywxExternalContactCallbackController@notify', 'GET|POST');