From 1ac3a4cc9605ed77657295837b72fb0ff9ceab7c Mon Sep 17 00:00:00 2001 From: kuaifan Date: Thu, 8 Jan 2026 11:31:16 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20user=5Fupdate=20ho?= =?UTF-8?q?ok=20=E4=BA=8B=E4=BB=B6=E5=B9=B6=E9=87=8D=E6=9E=84=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 user_update 事件,当用户基本信息变更时触发 - 扩展 dispatchUserHook payload 包含完整用户信息(tel、profession、birthday、address、introduction、departments) - 将 user_onboard/user_offboard/user_update hook 触发逻辑集中到 UserObserver - 区分 profile_update(用户自己修改)和 admin_update(管理员修改)事件类型 - 修复 User::reg() 中 Manticore 索引同步遗漏问题 - 排除机器人账号的 hook 触发 --- app/Http/Controllers/Api/UsersController.php | 10 --- app/Models/User.php | 12 ++-- app/Module/Apps.php | 40 ++++++++++-- app/Observers/UserObserver.php | 66 ++++++++++++++++++-- 4 files changed, 104 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 6792deacd..27fa02a49 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -37,7 +37,6 @@ use App\Models\UserRecentItem; use App\Models\UserTag; use App\Models\UserTagRecognition; use App\Models\UserAppSort; -use App\Module\Apps; use Illuminate\Support\Facades\DB; use App\Models\UserEmailVerification; use App\Module\AgoraIO\AgoraTokenGenerator; @@ -1104,8 +1103,6 @@ class UsersController extends AbstractController $upArray = []; $upLdap = []; $transferUser = null; - $hookAction = ''; - $hookEvent = ''; switch ($type) { case 'setadmin': $msg = '设置成功'; @@ -1187,16 +1184,12 @@ class UsersController extends AbstractController return Base::retError('交接人已离职,请选择另一个交接人'); } } - $hookAction = 'user_offboard'; - $hookEvent = 'offboard'; break; case 'cleardisable': $msg = '操作成功'; $upArray['identity'] = array_diff($userInfo->identity, ['disable']); $upArray['disable_at'] = null; - $hookAction = 'user_onboard'; - $hookEvent = 'restore'; break; case 'delete': @@ -1315,9 +1308,6 @@ class UsersController extends AbstractController } }); } - if ($hookAction) { - Apps::dispatchUserHook($userInfo, $hookAction, $hookEvent); - } // return Base::retSuccess($msg, $userInfo); } diff --git a/app/Models/User.php b/app/Models/User.php index eb3c5c642..c1890c9b0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -7,7 +7,9 @@ use App\Module\Base; use App\Module\Doo; use App\Module\Apps; use App\Module\Table\OnlineData; +use App\Observers\AbstractObserver; use App\Services\RequestContext; +use App\Tasks\ManticoreSyncTask; use Cache; use Carbon\Carbon; @@ -335,9 +337,6 @@ class User extends AbstractModel // return $this->delete(); }); - if ($ret) { - Apps::dispatchUserHook($this, 'user_offboard', 'delete'); - } return $ret; } @@ -413,7 +412,12 @@ class User extends AbstractModel } } $createdUser = $user->find($user->userid); - Apps::dispatchUserHook($createdUser, 'user_onboard', 'onboard'); + if (!$createdUser->bot) { + // Manticore 索引同步 + AbstractObserver::taskDeliver(new ManticoreSyncTask('user_sync', $createdUser->toArray())); + // 触发 user_onboard hook + Apps::dispatchUserHook($createdUser, 'user_onboard', 'onboard'); + } return $createdUser; } diff --git a/app/Module/Apps.php b/app/Module/Apps.php index fff59bbf7..e0669a8df 100644 --- a/app/Module/Apps.php +++ b/app/Module/Apps.php @@ -4,6 +4,7 @@ namespace App\Module; use App\Exceptions\ApiException; use App\Models\User; +use App\Models\UserDepartment; use App\Services\RequestContext; use Symfony\Component\Yaml\Yaml; use App\Module\Base; @@ -62,9 +63,14 @@ class Apps } /** - * Dispatch user lifecycle hook to appstore (onboard/offboard/delete/restore). + * Dispatch user lifecycle hook to appstore (user_onboard/user_offboard/user_update). + * + * @param User $user 用户对象 + * @param string $action Hook 动作: user_onboard, user_offboard, user_update + * @param string $eventType 事件类型: onboard, restore, offboarded, delete, profile_update, admin_update + * @param array $changedFields 变更字段列表(仅 user_update 时有值) */ - public static function dispatchUserHook(User $user, string $action, string $eventType = ''): void + public static function dispatchUserHook(User $user, string $action, string $eventType = '', array $changedFields = []): void { $appKey = env('APP_KEY', ''); if (empty($appKey)) { @@ -72,18 +78,40 @@ class Apps return; } + // 获取用户部门信息 + $departments = []; + if (!empty($user->department)) { + $deptIds = is_array($user->department) + ? $user->department + : array_filter(explode(',', $user->department)); + if (!empty($deptIds)) { + $deptList = UserDepartment::whereIn('id', $deptIds)->get(['id', 'name']); + foreach ($deptList as $dept) { + $departments[] = [ + 'id' => (string) $dept->id, + 'name' => (string) $dept->name, + ]; + } + } + } + $url = sprintf('http://appstore/api/v1/internal/hooks/%s', $action); $payload = [ 'user' => [ 'id' => (string) $user->userid, 'email' => (string) $user->email, 'name' => (string) $user->nickname, - 'role' => in_array('admin', $user->identity ?? []) ? 'admin' : 'normal', + 'role' => $user->isAdmin() ? 'admin' : 'normal', + 'tel' => (string) ($user->tel ?? ''), + 'profession' => (string) ($user->profession ?? ''), + 'birthday' => $user->birthday ? (string) $user->birthday : '', + 'address' => (string) ($user->address ?? ''), + 'introduction' => (string) ($user->introduction ?? ''), + 'departments' => $departments, ], + 'event_type' => $eventType, + 'changed_fields' => $changedFields, ]; - if ($eventType !== '') { - $payload['event_type'] = $eventType; - } $headers = [ 'Content-Type' => 'application/json', diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 8176db3e9..5b694bc9d 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -3,10 +3,26 @@ namespace App\Observers; use App\Models\User; +use App\Module\Apps; use App\Tasks\ManticoreSyncTask; class UserObserver extends AbstractObserver { + /** + * 搜索相关字段(Manticore 同步) + */ + private static array $searchableFields = [ + 'nickname', 'email', 'profession', 'introduction', 'disable_at' + ]; + + /** + * 需要监控并触发 user_update hook 的字段 + */ + private static array $hookMonitoredFields = [ + 'email', 'tel', 'nickname', 'profession', + 'birthday', 'address', 'introduction', 'department' + ]; + /** * Handle the User "created" event. * @@ -30,15 +46,14 @@ class UserObserver extends AbstractObserver */ public function updated(User $user) { - // 机器人账号不同步 + // 机器人账号不处理 if ($user->bot) { return; } - // 检查是否有搜索相关字段变化 - $searchableFields = ['nickname', 'email', 'profession', 'introduction', 'disable_at']; + // 检查是否有搜索相关字段变化(Manticore 同步) $isDirty = false; - foreach ($searchableFields as $field) { + foreach (self::$searchableFields as $field) { if ($user->isDirty($field)) { $isDirty = true; break; @@ -53,6 +68,43 @@ class UserObserver extends AbstractObserver self::taskDeliver(new ManticoreSyncTask('user_sync', $user->toArray())); } } + + // 检测 onboard/offboard 场景(disable_at 变化) + if ($user->isDirty('disable_at')) { + $originalDisableAt = $user->getOriginal('disable_at'); + $currentDisableAt = $user->disable_at; + + if ($originalDisableAt && !$currentDisableAt) { + // disable_at 从有值变为 null → 取消离职 (restore) + Apps::dispatchUserHook($user, 'user_onboard', 'restore'); + } elseif (!$originalDisableAt && $currentDisableAt) { + // disable_at 从 null 变为有值 → 离职 (offboarded) + Apps::dispatchUserHook($user, 'user_offboard', 'offboarded'); + } + return; + } + + // 排除仅 identity 变化的场景 + if ($user->isDirty('identity')) { + return; + } + + // 检测监控字段变更,触发 user_update hook + $changedFields = []; + foreach (self::$hookMonitoredFields as $field) { + if ($user->isDirty($field)) { + $changedFields[] = $field; + } + } + + if (!empty($changedFields)) { + // 判断是用户自己修改还是管理员修改 + $currentUser = User::authInfo(); + $eventType = ($currentUser && $currentUser->userid === $user->userid) + ? 'profile_update' + : 'admin_update'; + Apps::dispatchUserHook($user, 'user_update', $eventType, $changedFields); + } } /** @@ -63,7 +115,13 @@ class UserObserver extends AbstractObserver */ public function deleted(User $user) { + // Manticore 索引删除 self::taskDeliver(new ManticoreSyncTask('user_delete', ['userid' => $user->userid])); + + // 触发 user_offboard (delete) hook + if (!$user->bot) { + Apps::dispatchUserHook($user, 'user_offboard', 'delete'); + } } }