feat: 新增文件传输助手功能及相关组件
新增文件传输助手功能,包含设备发现、配对、传输等核心模块。主要变更包括: 1. 新增局域网、蓝牙、NFC等多种设备发现方式 2. 实现基于WebRTC、TCP、USB等多种传输协议 3. 添加相关权限管理及状态监控 4. 完善UI界面及交互流程 5. 更新依赖库及版本号至4.19.0 同时优化部分现有功能: 1. 聊天会话增加隐藏功能 2. 完善本地通知权限处理 3. 修复部分已知问题
This commit is contained in:
@@ -14,12 +14,12 @@ use think\Validate;
|
||||
* 用户安全接口
|
||||
* @time 2026-04-29
|
||||
* @name UserSecurity
|
||||
* @description 用户安全相关API,含注册/登录/改密/改邮箱/重置密码/回执登录等
|
||||
* @lastUpdate v8.0.0 去除邮箱验证码依赖,引入HMAC-SHA256回执验证机制,新增receiptLogin
|
||||
* @description 用户安全相关API,含注册/登录/改密/改邮箱/重置密码/回执登录/二维码登录等
|
||||
* @lastUpdate v9.0.0 新增qrcodeLogin二维码登录; login/receiptLogin记录设备信息和在线状态
|
||||
*/
|
||||
class UserSecurity extends Api
|
||||
{
|
||||
protected $noNeedLogin = ['login', 'mobilelogin', 'register', 'resetpwd', 'tokenLogin', 'receiptLogin', 'sendEms', 'checkEms'];
|
||||
protected $noNeedLogin = ['login', 'mobilelogin', 'register', 'resetpwd', 'tokenLogin', 'receiptLogin', 'sendEms', 'checkEms', 'qrcodeGenerate', 'qrcodePoll', 'qrcodeCancel'];
|
||||
protected $noNeedRight = '*';
|
||||
|
||||
private static $rateLimitKey = 'api_rate_limit:';
|
||||
@@ -131,7 +131,8 @@ class UserSecurity extends Api
|
||||
|
||||
/**
|
||||
* @name 账号密码登录
|
||||
* @desc 支持用户名/邮箱+密码登录,无需回执
|
||||
* @desc 支持用户名/邮箱+密码登录,无需回执,登录后记录设备信息
|
||||
* @lastUpdate v9.0.0 登录后记录设备信息和在线状态
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
@@ -146,6 +147,8 @@ class UserSecurity extends Api
|
||||
$this->detectMaliciousInput($account);
|
||||
$ret = $this->auth->login($account, $password);
|
||||
if ($ret) {
|
||||
$this->recordLoginDevice($this->auth->id);
|
||||
$this->updateOnlineStatus($this->auth->id);
|
||||
$data = ['userinfo' => $this->auth->getUserinfo()];
|
||||
$this->success(__('Logged in successful'), $data);
|
||||
} else {
|
||||
@@ -156,7 +159,7 @@ class UserSecurity extends Api
|
||||
/**
|
||||
* @name 回执登录
|
||||
* @desc 客户端验证邮箱/手机后,用回执替代密码登录
|
||||
* @since v8.0.0
|
||||
* @lastUpdate v9.0.0 登录后记录设备信息和在线状态
|
||||
*/
|
||||
public function receiptLogin()
|
||||
{
|
||||
@@ -194,6 +197,8 @@ class UserSecurity extends Api
|
||||
|
||||
$ret = $this->auth->direct($user->id);
|
||||
if ($ret) {
|
||||
$this->recordLoginDevice($user->id);
|
||||
$this->updateOnlineStatus($user->id);
|
||||
$data = ['userinfo' => $this->auth->getUserinfo()];
|
||||
$this->success(__('Logged in successful'), $data);
|
||||
} else {
|
||||
@@ -593,4 +598,227 @@ class UserSecurity extends Api
|
||||
}
|
||||
$this->error(__('Operation failed'), $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 生成二维码
|
||||
* @desc 生成二维码登录标识,返回code用于轮询
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
public function qrcodeGenerate()
|
||||
{
|
||||
$code = md5(uniqid('qr', true) . mt_rand() . time());
|
||||
$expireTime = time() + 300;
|
||||
|
||||
db('qrcode_login')->insert([
|
||||
'code' => $code,
|
||||
'status' => 'pending',
|
||||
'user_id' => 0,
|
||||
'token' => '',
|
||||
'ip' => $this->request->ip(),
|
||||
'expire_time' => $expireTime,
|
||||
'createtime' => time(),
|
||||
'updatetime' => time(),
|
||||
]);
|
||||
|
||||
$this->success('二维码已生成', [
|
||||
'code' => $code,
|
||||
'expire_time' => $expireTime,
|
||||
'expire_seconds' => 300,
|
||||
'qrcode_url' => $this->request->domain() . '/qrcode?code=' . $code,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 扫码确认
|
||||
* @desc 已登录用户扫码后确认登录,需登录
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
public function qrcodeConfirm()
|
||||
{
|
||||
$code = $this->request->post('code', '', 'trim');
|
||||
if (!$code) {
|
||||
$this->error('code必填');
|
||||
}
|
||||
|
||||
$qr = db('qrcode_login')->where('code', $code)->find();
|
||||
if (!$qr) {
|
||||
$this->error('二维码不存在');
|
||||
}
|
||||
if ($qr['status'] !== 'pending' && $qr['status'] !== 'scanned') {
|
||||
$this->error('二维码状态无效');
|
||||
}
|
||||
if ($qr['expire_time'] < time()) {
|
||||
db('qrcode_login')->where('code', $code)->update(['status' => 'expired', 'updatetime' => time()]);
|
||||
$this->error('二维码已过期');
|
||||
}
|
||||
|
||||
$userId = $this->auth->id;
|
||||
$deviceInfo = json_encode([
|
||||
'ip' => $this->request->ip(),
|
||||
'platform' => $this->request->post('platform', '', 'trim'),
|
||||
'device_name' => $this->request->post('device_name', '', 'trim'),
|
||||
'app_name' => $this->request->post('app_name', '', 'trim'),
|
||||
]);
|
||||
|
||||
db('qrcode_login')->where('code', $code)->update([
|
||||
'status' => 'confirmed',
|
||||
'user_id' => $userId,
|
||||
'device_info' => $deviceInfo,
|
||||
'updatetime' => time(),
|
||||
]);
|
||||
|
||||
$this->success('确认登录成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 轮询二维码状态
|
||||
* @desc 客户端轮询二维码状态,confirmed时返回Token
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
public function qrcodePoll()
|
||||
{
|
||||
$code = $this->request->param('code', '', 'trim');
|
||||
if (!$code) {
|
||||
$this->error('code必填');
|
||||
}
|
||||
|
||||
$qr = db('qrcode_login')->where('code', $code)->find();
|
||||
if (!$qr) {
|
||||
$this->error('二维码不存在');
|
||||
}
|
||||
|
||||
if ($qr['expire_time'] < time() && $qr['status'] !== 'confirmed') {
|
||||
db('qrcode_login')->where('code', $code)->update(['status' => 'expired', 'updatetime' => time()]);
|
||||
$this->success('', ['status' => 'expired', 'message' => '二维码已过期']);
|
||||
}
|
||||
|
||||
if ($qr['status'] === 'pending') {
|
||||
$this->success('', ['status' => 'pending', 'message' => '等待扫码']);
|
||||
}
|
||||
|
||||
if ($qr['status'] === 'scanned') {
|
||||
$this->success('', ['status' => 'scanned', 'message' => '已扫码,等待确认']);
|
||||
}
|
||||
|
||||
if ($qr['status'] === 'expired') {
|
||||
$this->success('', ['status' => 'expired', 'message' => '二维码已过期']);
|
||||
}
|
||||
|
||||
if ($qr['status'] === 'cancelled') {
|
||||
$this->success('', ['status' => 'cancelled', 'message' => '已取消']);
|
||||
}
|
||||
|
||||
if ($qr['status'] === 'confirmed') {
|
||||
$user = \app\common\model\User::get($qr['user_id']);
|
||||
if (!$user || $user->status != 'normal') {
|
||||
$this->error('用户异常');
|
||||
}
|
||||
|
||||
$ret = $this->auth->direct($user->id);
|
||||
if ($ret) {
|
||||
$token = $this->auth->getToken();
|
||||
db('qrcode_login')->where('code', $code)->update([
|
||||
'token' => $token,
|
||||
'updatetime' => time(),
|
||||
]);
|
||||
|
||||
$this->recordLoginDevice($user->id);
|
||||
$this->updateOnlineStatus($user->id);
|
||||
|
||||
$this->success('登录成功', [
|
||||
'status' => 'confirmed',
|
||||
'token' => $token,
|
||||
'userinfo' => $this->auth->getUserinfo(),
|
||||
]);
|
||||
} else {
|
||||
$this->error('登录失败');
|
||||
}
|
||||
}
|
||||
|
||||
$this->error('未知状态');
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 取消二维码
|
||||
* @desc 取消二维码登录
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
public function qrcodeCancel()
|
||||
{
|
||||
$code = $this->request->post('code', '', 'trim');
|
||||
if (!$code) {
|
||||
$this->error('code必填');
|
||||
}
|
||||
|
||||
$qr = db('qrcode_login')->where('code', $code)->find();
|
||||
if (!$qr) {
|
||||
$this->error('二维码不存在');
|
||||
}
|
||||
|
||||
db('qrcode_login')->where('code', $code)->update(['status' => 'cancelled', 'updatetime' => time()]);
|
||||
$this->success('已取消');
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 记录登录设备
|
||||
* @desc 登录成功后记录设备信息到user_device表
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
private function recordLoginDevice($userId)
|
||||
{
|
||||
$deviceName = $this->request->post('device_name', '', 'trim');
|
||||
$deviceModel = $this->request->post('device_model', '', 'trim');
|
||||
$platform = $this->request->post('platform', '', 'trim');
|
||||
$appName = $this->request->post('app_name', '', 'trim');
|
||||
$deviceId = $this->request->post('device_id', '', 'trim');
|
||||
$userAgent = $this->request->header('user-agent', '');
|
||||
$ip = $this->request->ip();
|
||||
$now = time();
|
||||
|
||||
if ($deviceId) {
|
||||
$exists = db('user_device')->where('user_id', $userId)->where('device_id', $deviceId)->find();
|
||||
if ($exists) {
|
||||
db('user_device')->where('id', $exists['id'])->update([
|
||||
'device_name' => $deviceName ?: $exists['device_name'],
|
||||
'device_model' => $deviceModel ?: $exists['device_model'],
|
||||
'platform' => $platform ?: $exists['platform'],
|
||||
'app_name' => $appName ?: $exists['app_name'],
|
||||
'ip' => $ip,
|
||||
'last_active_time' => $now,
|
||||
'is_online' => 1,
|
||||
'user_agent' => substr($userAgent, 0, 500),
|
||||
'updatetime' => $now,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
db('user_device')->insert([
|
||||
'user_id' => $userId,
|
||||
'device_name' => $deviceName,
|
||||
'device_model' => $deviceModel,
|
||||
'platform' => $platform,
|
||||
'app_name' => $appName,
|
||||
'ip' => $ip,
|
||||
'device_id' => $deviceId ?: md5($userId . $ip . $userAgent . $now),
|
||||
'last_active_time' => $now,
|
||||
'is_online' => 1,
|
||||
'user_agent' => substr($userAgent, 0, 500),
|
||||
'createtime' => $now,
|
||||
'updatetime' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name 更新在线状态
|
||||
* @desc 更新用户最后活跃时间和在线状态
|
||||
* @lastUpdate v9.0.0 新增
|
||||
*/
|
||||
private function updateOnlineStatus($userId)
|
||||
{
|
||||
db('user')->where('id', $userId)->update([
|
||||
'last_active_time' => time(),
|
||||
'is_online' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user