feat: 飞书登录支持多租户和用户工号管理

后端改动:
- FeishuService: 从飞书用户信息获取 tenant_key 和手机号
- FeishuService: 新增 user_sn 表工号分配机制
- FeishuService: 优先使用 enterprise_email,支持手机号
- AuthController: 飞书免登支持 company 参数

前端改动:
- FeishuAutoLogin: 支持多租户 company 参数
- login/index: 支持 redirect 参数和已登录检测
- QRCodeLogin: 支持多租户参数传递
- router: 新增路由守卫和参数处理
- api.ts: 优化飞书登录 API 调用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-02-27 17:57:49 +08:00
parent b1895be50d
commit b1ca2c58b7
8 changed files with 241 additions and 41 deletions
+51 -6
View File
@@ -134,9 +134,18 @@ class FeishuService
$unionId = $feishuData['union_id'] ?? null;
$feishuUserId = $feishuData['user_id'] ?? null;
$openId = $feishuData['open_id'] ?? null;
$tenantKey = $feishuData['tenant_key'] ?? null; // 从飞书用户信息获取 tenant_key
$name = $feishuData['name'] ?? '未知';
$avatar = $feishuData['avatar_url'] ?? null;
$email = $feishuData['email'] ?? null;
// 邮箱:优先使用 enterprise_email,否则使用 email
$email = $feishuData['enterprise_email'] ?? $feishuData['email'] ?? null;
// 手机号:飞书返回的是 mobile 字段
$phone = $feishuData['mobile'] ?? null;
// 去掉手机号中的 +86 前缀
if ($phone && str_starts_with($phone, '+86')) {
$phone = substr($phone, 3);
}
if (!$unionId) {
throw new Exception('缺少 union_id');
@@ -160,20 +169,25 @@ class FeishuService
if (!$user) {
error_log("✨ 创建新用户: company={$companyId}, union_id={$unionId}, name={$name}");
// 初始密码使用 sn(工号),如果没有 sn 则使用 union_id 的前6位
$initialPassword = $feishuData['employee_no'] ?? substr($unionId, 0, 6);
// 从 user_sn 表中随机获取一个未分配的 sn
$userSn = $this->allocateUserSn();
$user = User::create([
'company_id' => $companyId,
'sn' => $userSn,
'union_id' => $unionId,
'name' => $name,
'email' => $email,
'phone' => $phone,
'avatar' => $avatar,
'password' => password_hash($initialPassword, PASSWORD_DEFAULT), // 初始密码加密
'password' => password_hash($userSn, PASSWORD_DEFAULT), // 使用 sn 作为初始密码
'feishu_app_id' => $appId, // 记录登录来源应用
'status' => 1, // 新用户默认在职
]);
error_log("🔑 初始密码设置为 sn: {$initialPassword}");
error_log("🔑 用户 sn: {$userSn}, 初始密码: {$userSn}");
// 更新 user_sn 表,标记 sn 已分配给新用户
$this->markUserSnAllocated($userSn, $user->id);
} else {
error_log("🔄 更新用户: id={$user->id}, company={$companyId}, union_id={$unionId}, 当前状态={$user->status}");
@@ -188,16 +202,47 @@ class FeishuService
'name' => $name,
'avatar' => $avatar,
'email' => $email,
'phone' => $phone,
'feishu_app_id' => $appId,
]);
}
// ⭐ 保存或更新用户飞书应用绑定关系
$this->saveUserFeishuBinding($user->id, $companyId, $appId, $feishuUserId, $openId, $app->tenant_key);
$this->saveUserFeishuBinding($user->id, $companyId, $appId, $feishuUserId, $openId, $tenantKey);
return $user;
}
/**
* 从 user_sn 表中随机获取一个未分配的 sn
*/
private function allocateUserSn(): string
{
// 随机获取一个未分配的 sn(user_id = 0)
$snRecord = \support\Db::table('user_sn')
->where('user_id', 0)
->inRandomOrder()
->first();
if (!$snRecord) {
throw new Exception('没有可用的用户编号,请联系管理员');
}
return (string) $snRecord->user_sn;
}
/**
* 标记 user_sn 已分配给指定用户
*/
private function markUserSnAllocated(string $userSn, int $userId): void
{
\support\Db::table('user_sn')
->where('user_sn', $userSn)
->update(['user_id' => $userId]);
error_log("✅ 用户编号 {$userSn} 已分配给用户 {$userId}");
}
/**
* 保存或更新用户飞书应用绑定关系
*/