no message

This commit is contained in:
kuaifan 2025-04-19 09:03:33 +08:00
parent b64d4fd96f
commit 63c6e12aca
7 changed files with 555 additions and 535 deletions

View File

@ -19,6 +19,7 @@ use App\Models\UserBot;
use App\Models\WebSocket;
use App\Models\UmengAlias;
use App\Models\UserDelete;
use App\Models\UserDevice;
use App\Models\UserTransfer;
use App\Models\AbstractModel;
use App\Models\UserCheckinFace;
@ -266,6 +267,11 @@ class UsersController extends AbstractController
return Base::retSuccess('请求成功', $captcha);
}
public function logout()
{
$user = User::auth();
}
/**
* @api {get} api/users/reg/needinvite 06. 是否需要邀请码
*
@ -2425,4 +2431,79 @@ class UsersController extends AbstractController
//
return Base::retSuccess('success', $data);
}
/**
* @api {get} api/users/device/count 38. 获取设备数量
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName device__count
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function device__count()
{
$user = User::auth();
//
return Base::retSuccess('success', [
'count' => UserDevice::whereUserid($user->userid)->count()
]);
}
/**
* @api {get} api/users/device/list 39. 获取设备列表
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName device__list
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function device__list()
{
$user = User::auth();
//
$list = UserDevice::whereUserid($user->userid)->orderByDesc('id')->take(100)->get();
//
return Base::retSuccess('success', [
'list' => $list
]);
}
/**
* @api {get} api/users/device/logout 40. 登出设备(删除设备)
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName device__logout
*
* @apiParam {Number} id 设备id
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function device__logout()
{
$user = User::auth();
//
$id = intval(Request::input('id'));
if (empty($id)) {
return Base::retError('参数错误');
}
$userDevice = UserDevice::whereUserid($user->userid)->whereId($id)->first();
if (empty($userDevice)) {
return Base::retError('设备不存在或已被删除');
}
UserDevice::forget($userDevice->id);
//
return Base::retSuccess('删除成功');
}
}

View File

@ -220,13 +220,16 @@ class AbstractModel extends Model
$row = static::where($where)->first();
if (empty($row)) {
$row = new static;
if ($update instanceof \Closure) {
$update = $update();
}
if ($insert instanceof \Closure) {
$insert = $insert();
}
$array = array_merge($where, $insert ?: $update);
if (empty($insert)) {
if ($update instanceof \Closure) {
$update = $update();
}
$insert = $update;
}
$array = array_merge($where, $insert);
if (isset($array[$row->primaryKey])) {
unset($array[$row->primaryKey]);
}

View File

@ -445,6 +445,7 @@ class User extends AbstractModel
$user = self::authInfo();
if (!$user) {
if (Base::token()) {
UserDevice::forget();
throw new ApiException('身份已失效,请重新登录', [], -1);
} else {
throw new ApiException('请登录后继续...', [], -1);
@ -466,31 +467,46 @@ class User extends AbstractModel
private static function authInfo()
{
if (RequestContext::has('auth')) {
// 缓存
return RequestContext::get('auth');
}
if (Doo::userId() > 0
&& !Doo::userExpired()
&& $user = self::whereUserid(Doo::userId())->whereEmail(Doo::userEmail())->whereEncrypt(Doo::userEncrypt())->first()) {
$upArray = [];
if (Base::getIp() && $user->line_ip != Base::getIp()) {
$upArray['line_ip'] = Base::getIp();
}
if (Carbon::parse($user->line_at)->addSeconds(30)->lt(Carbon::now())) {
$upArray['line_at'] = Carbon::now();
}
$headerLanguage = RequestContext::get('header_language');
if (empty($user->lang) || $headerLanguage) {
if (Doo::checkLanguage($headerLanguage) && $user->lang != $headerLanguage) {
$upArray['lang'] = $headerLanguage;
}
}
if ($upArray) {
$user->updateInstance($upArray);
$user->save();
}
return RequestContext::save('auth', $user);
if (Doo::userId() <= 0) {
// 没有登录
return RequestContext::save('auth', false);
}
return RequestContext::save('auth', false);
if (Doo::userExpired()) {
// 登录过期
return RequestContext::save('auth', false);
}
if (!UserDevice::check()) {
// token 不存在
return RequestContext::save('auth', false);
}
$user = self::whereUserid(Doo::userId())->whereEmail(Doo::userEmail())->whereEncrypt(Doo::userEncrypt())->first();
if (!$user) {
// 登录信息不匹配
return RequestContext::save('auth', false);
}
// 更新登录信息
$upArray = [];
if (Base::getIp() && $user->line_ip != Base::getIp()) {
$upArray['line_ip'] = Base::getIp();
}
if (Carbon::parse($user->line_at)->addSeconds(30)->lt(Carbon::now())) {
$upArray['line_at'] = Carbon::now();
}
$headerLanguage = RequestContext::get('header_language');
if (empty($user->lang) || $headerLanguage) {
if (Doo::checkLanguage($headerLanguage) && $user->lang != $headerLanguage) {
$upArray['lang'] = $headerLanguage;
}
}
if ($upArray) {
$user->updateInstance($upArray);
$user->save();
}
return RequestContext::save('auth', $user);
}
/**
@ -514,6 +530,7 @@ class User extends AbstractModel
} else {
$token = Doo::userToken();
}
UserDevice::record($token);
unset($userinfo->encrypt);
unset($userinfo->password);
return $userinfo->token = $token;

263
app/Models/UserDevice.php Normal file
View File

@ -0,0 +1,263 @@
<?php
namespace App\Models;
use App\Module\Base;
use App\Module\Doo;
use Cache;
use Carbon\Carbon;
use DeviceDetector\DeviceDetector;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Models\UserDevice
*
* @property int $id
* @property int|null $userid 会员ID
* @property string|null $hash TOKEN MD5
* @property string|null $detail 详细信息
* @property string|null $expired_at 过期时间
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelHidden()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel change($array)
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel getKeyValue()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice query()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel remove()
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel saveOrIgnore()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereDetail($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereExpiredAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereHash($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice whereUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice withTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|UserDevice withoutTrashed()
* @mixin \Eloquent
*/
class UserDevice extends AbstractModel
{
use SoftDeletes;
protected $table = 'user_devices';
protected $appends = [
'is_current',
];
public function getDetailAttribute($value)
{
if (is_array($value)) {
return $value;
}
return Base::json2array($value);
}
public function getIsCurrentAttribute(): int
{
return $this->hash === md5(Doo::userToken()) ? 1 : 0;
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* 缓存key
* @param string $hash
* @return string
*/
private static function ck(string $hash): string
{
return "user_devices:{$hash}";
}
/**
* 解析 UA 获取设备信息
* @param string $ua
* @return array
*/
private static function getDeviceInfo(string $ua): array
{
$result = [
'ip' => Base::getIp(),
'type' => '电脑',
'os' => 'Unknown',
'browser' => 'Unknown',
'version' => '',
'app_type' => '', // 客户端类型
'app_version' => '', // 客户端版本
];
if (empty($ua)) {
return $result;
}
// 使用 Device-Detector 解析 UA
$dd = new DeviceDetector($ua);
// 解析 UA 字符串
$dd->parse();
// 获取客户端信息(浏览器)
$clientInfo = $dd->getClient();
if (!empty($clientInfo)) {
$result['browser'] = $clientInfo['name'] ?? 'Unknown';
$result['version'] = $clientInfo['version'] ?? '';
}
// 获取操作系统信息
$osInfo = $dd->getOs();
if (!empty($osInfo)) {
$result['os'] = trim(($osInfo['name'] ?? '') . ' ' . ($osInfo['version'] ?? ''));
if (empty($result['os'])) {
$result['os'] = 'Unknown';
}
}
if (preg_match("/android_kuaifan_eeui/i", $ua)) {
// Android 客户端
$result['app_type'] = 'Android';
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
} elseif (preg_match("/ios_kuaifan_eeui/i", $ua)) {
// iOS 客户端
$result['app_type'] = 'iOS';
$result['app_version'] = self::getAfterVersion($ua, 'kuaifan_eeui/');
} elseif (preg_match("/dootask/i", $ua)) {
// DooTask 客户端
$result['app_type'] = $osInfo['name'];
$result['app_version'] = self::getAfterVersion($ua, 'dootask/');
} else {
// 其他客户端
$result['app_type'] = 'Web';
$result['app_version'] = Base::getClientVersion();
}
return $result;
}
/**
* ua find 之后的内容获取版本号
* @param string $ua
* @param string $find
* @return string
*/
private static function getAfterVersion(string $ua, string $find): string
{
$findPattern = preg_quote($find, '/');
if (preg_match("/{$findPattern}(.*?)(?:\s|$)/i", $ua, $matches)) {
$appInfo = $matches[1];
// 从内容中提取版本号寻找符合x.x.x格式的部分
if (preg_match("/(\d+\.\d+(?:\.\d+)*)/", $appInfo, $versionMatches)) {
return $versionMatches[1];
}
}
return '';
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* 检查用户是否存在
* @return bool
*/
public static function check(): bool
{
$token = Doo::userToken();
$userid = Doo::userId();
$hash = md5($token);
if (Cache::has(self::ck($hash))) {
return true;
}
$row = self::whereHash($hash)->first();
if ($row) {
// 判断是否过期
if (Carbon::parse($row->expired_at)->isPast()) {
Cache::forget(self::ck($hash));
$row->delete();
return false;
}
// 更新缓存
self::record();
return true;
}
// 没有记录,尝试创建一个(防止升级后所有登录都失效,保证留一个可以保持登录) // todo 后期删除
return AbstractModel::transaction(function () use ($userid) {
if (self::whereUserid($userid)->withoutTrashed()->lockForUpdate()->exists()) {
return false;
}
return (bool)self::record();
});
}
/**
* 记录设备(添加、更新)
* @param string|null $token
* @return self|null
*/
public static function record(string $token = null): ?self
{
if (empty($token)) {
$token = Doo::userToken();
$userid = Doo::userId();
$expiredAt = Doo::userExpiredAt() ?: null;
} else {
$info = Doo::tokenDecode($token);
$userid = $info['userid'] ?? 0;
$expiredAt = $info['expired_at'] ?? null;
}
$hash = md5($token);
$row = self::updateInsert([
'userid' => $userid,
'hash' => $hash,
], [
'detail' => Base::array2json(self::getDeviceInfo($_SERVER['HTTP_USER_AGENT'] ?? '')),
'expired_at' => $expiredAt,
]);
if ($row) {
Cache::put(self::ck($hash), $row->userid, now()->addHour());
return $row;
}
return null;
}
/**
* 忘记设备(删除)
* @param string|int|null $token
* - null 表示当前登录的设备
* - string 表示指定的 token
* - int 表示指定的数据ID
* @return void
*/
public static function forget(string|int $token = null): void
{
if ($token === null) {
$token = Doo::userToken();
}
if (Base::isNumber($token)) {
$row = self::find(intval($token));
if ($row) {
Cache::forget(self::ck($row->hash));
$row->delete();
}
} elseif ($token) {
$hash = md5($token);
Cache::forget(self::ck($hash));
self::whereHash($hash)->delete();
}
}
}

View File

@ -33,6 +33,7 @@
"league/html-to-markdown": "^5.1",
"maatwebsite/excel": "^3.1.31",
"madnest/madzipper": "^v1.1.0",
"matomo/device-detector": "^6.4",
"mews/captcha": "^3.2.6",
"orangehill/iseed": "^3.0.1",
"overtrue/pinyin": "^4.0",

634
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ade0fa5c3619ee85dd7fca4cf68afbb2",
"content-hash": "c0fa92e60d19e0c371dc4d62c6555058",
"packages": [
{
"name": "asm89/stack-cors",
@ -861,122 +861,6 @@
],
"time": "2020-12-29T14:50:06+00:00"
},
{
"name": "elastic/transport",
"version": "v8.10.0",
"source": {
"type": "git",
"url": "https://github.com/elastic/elastic-transport-php.git",
"reference": "8be37d679637545e50b1cea9f8ee903888783021"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/elastic/elastic-transport-php/zipball/8be37d679637545e50b1cea9f8ee903888783021",
"reference": "8be37d679637545e50b1cea9f8ee903888783021",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
"open-telemetry/api": "^1.0",
"php": "^7.4 || ^8.0",
"php-http/discovery": "^1.14",
"php-http/httplug": "^2.3",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0 || ^2.0",
"psr/log": "^1 || ^2 || ^3"
},
"require-dev": {
"nyholm/psr7": "^1.5",
"open-telemetry/sdk": "^1.0",
"php-http/mock-client": "^1.5",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^9.5",
"symfony/http-client": "^5.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Elastic\\Transport\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "HTTP transport PHP library for Elastic products",
"keywords": [
"PSR_17",
"elastic",
"http",
"psr-18",
"psr-7",
"transport"
],
"support": {
"issues": "https://github.com/elastic/elastic-transport-php/issues",
"source": "https://github.com/elastic/elastic-transport-php/tree/v8.10.0"
},
"time": "2024-08-14T08:55:07+00:00"
},
{
"name": "elasticsearch/elasticsearch",
"version": "v8.17.0",
"source": {
"type": "git",
"url": "https://github.com/elastic/elasticsearch-php.git",
"reference": "6cd0fe6a95fdb7198a2795624927b094813b3d8b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/6cd0fe6a95fdb7198a2795624927b094813b3d8b",
"reference": "6cd0fe6a95fdb7198a2795624927b094813b3d8b",
"shasum": ""
},
"require": {
"elastic/transport": "^8.10",
"guzzlehttp/guzzle": "^7.0",
"php": "^7.4 || ^8.0",
"psr/http-client": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"psr/log": "^1|^2|^3"
},
"require-dev": {
"ext-yaml": "*",
"ext-zip": "*",
"mockery/mockery": "^1.5",
"nyholm/psr7": "^1.5",
"php-http/message-factory": "^1.0",
"php-http/mock-client": "^1.5",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^9.5",
"psr/http-factory": "^1.0",
"symfony/finder": "~4.0",
"symfony/http-client": "^5.0|^6.0|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Elastic\\Elasticsearch\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHP Client for Elasticsearch",
"keywords": [
"client",
"elastic",
"elasticsearch",
"search"
],
"support": {
"issues": "https://github.com/elastic/elasticsearch-php/issues",
"source": "https://github.com/elastic/elasticsearch-php/tree/v8.17.0"
},
"time": "2024-12-18T11:00:27+00:00"
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.18.0",
@ -3064,6 +2948,76 @@
},
"time": "2022-12-02T22:17:43+00:00"
},
{
"name": "matomo/device-detector",
"version": "6.4.5",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
"reference": "270bbc41f80994e80805ac377b67324eba53c412"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/270bbc41f80994e80805ac377b67324eba53c412",
"reference": "270bbc41f80994e80805ac377b67324eba53c412",
"shasum": ""
},
"require": {
"mustangostang/spyc": "*",
"php": "^7.2|^8.0"
},
"replace": {
"piwik/device-detector": "self.version"
},
"require-dev": {
"matthiasmullie/scrapbook": "^1.4.7",
"mayflower/mo4-coding-standard": "^v9.0.0",
"phpstan/phpstan": "^1.10.44",
"phpunit/phpunit": "^8.5.8",
"psr/cache": "^1.0.1",
"psr/simple-cache": "^1.0.1",
"slevomat/coding-standard": "<8.16.0",
"symfony/yaml": "^5.1.7"
},
"suggest": {
"doctrine/cache": "Can directly be used for caching purpose",
"ext-yaml": "Necessary for using the Pecl YAML parser"
},
"type": "library",
"autoload": {
"psr-4": {
"DeviceDetector\\": ""
},
"exclude-from-classmap": [
"Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The Matomo Team",
"email": "hello@matomo.org",
"homepage": "https://matomo.org/team/"
}
],
"description": "The Universal Device Detection library, that parses User Agents and detects devices (desktop, tablet, mobile, tv, cars, console, etc.), clients (browsers, media players, mobile apps, feed readers, libraries, etc), operating systems, devices, brands and models.",
"homepage": "https://matomo.org",
"keywords": [
"devicedetection",
"parser",
"useragent"
],
"support": {
"forum": "https://forum.matomo.org/",
"issues": "https://github.com/matomo-org/device-detector/issues",
"source": "https://github.com/matomo-org/matomo",
"wiki": "https://dev.matomo.org/"
},
"time": "2025-02-26T17:37:32+00:00"
},
{
"name": "mews/captcha",
"version": "3.3.0",
@ -3239,6 +3193,60 @@
],
"time": "2024-11-12T12:43:37+00:00"
},
{
"name": "mustangostang/spyc",
"version": "0.6.3",
"source": {
"type": "git",
"url": "https://github.com/mustangostang/spyc.git",
"reference": "4627c838b16550b666d15aeae1e5289dd5b77da0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mustangostang/spyc/zipball/4627c838b16550b666d15aeae1e5289dd5b77da0",
"reference": "4627c838b16550b666d15aeae1e5289dd5b77da0",
"shasum": ""
},
"require": {
"php": ">=5.3.1"
},
"require-dev": {
"phpunit/phpunit": "4.3.*@dev"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.5.x-dev"
}
},
"autoload": {
"files": [
"Spyc.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "mustangostang",
"email": "vlad.andersen@gmail.com"
}
],
"description": "A simple YAML loader/dumper class for PHP",
"homepage": "https://github.com/mustangostang/spyc/",
"keywords": [
"spyc",
"yaml",
"yml"
],
"support": {
"issues": "https://github.com/mustangostang/spyc/issues",
"source": "https://github.com/mustangostang/spyc/tree/0.6.3"
},
"time": "2019-09-10T13:16:29+00:00"
},
{
"name": "myclabs/php-enum",
"version": "1.8.5",
@ -3615,134 +3623,6 @@
},
"time": "2024-12-30T11:07:19+00:00"
},
{
"name": "open-telemetry/api",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/api.git",
"reference": "87de95d926f46262885d0d390060c095af13e2e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/87de95d926f46262885d0d390060c095af13e2e5",
"reference": "87de95d926f46262885d0d390060c095af13e2e5",
"shasum": ""
},
"require": {
"open-telemetry/context": "^1.0",
"php": "^7.4 || ^8.0",
"psr/log": "^1.1|^2.0|^3.0",
"symfony/polyfill-php80": "^1.26",
"symfony/polyfill-php81": "^1.26",
"symfony/polyfill-php82": "^1.26"
},
"conflict": {
"open-telemetry/sdk": "<=1.0.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.0.x-dev"
}
},
"autoload": {
"files": [
"Trace/functions.php"
],
"psr-4": {
"OpenTelemetry\\API\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "opentelemetry-php contributors",
"homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
}
],
"description": "API for OpenTelemetry PHP.",
"keywords": [
"Metrics",
"api",
"apm",
"logging",
"opentelemetry",
"otel",
"tracing"
],
"support": {
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
"docs": "https://opentelemetry.io/docs/php",
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2024-02-06T01:32:25+00:00"
},
{
"name": "open-telemetry/context",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/context.git",
"reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/e9d254a7c89885e63fd2fde54e31e81aaaf52b7c",
"reference": "e9d254a7c89885e63fd2fde54e31e81aaaf52b7c",
"shasum": ""
},
"require": {
"php": "^7.4 || ^8.0",
"symfony/polyfill-php80": "^1.26",
"symfony/polyfill-php81": "^1.26",
"symfony/polyfill-php82": "^1.26"
},
"suggest": {
"ext-ffi": "To allow context switching in Fibers"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.0.x-dev"
}
},
"autoload": {
"files": [
"fiber/initialize_fiber_handler.php"
],
"psr-4": {
"OpenTelemetry\\Context\\": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "opentelemetry-php contributors",
"homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
}
],
"description": "Context implementation for OpenTelemetry PHP.",
"keywords": [
"Context",
"opentelemetry",
"otel"
],
"support": {
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
"docs": "https://opentelemetry.io/docs/php",
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2024-01-13T05:50:44+00:00"
},
{
"name": "opis/closure",
"version": "3.6.3",
@ -4043,194 +3923,6 @@
},
"time": "2014-06-05T11:42:24+00:00"
},
{
"name": "php-http/discovery",
"version": "1.20.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d",
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0|^2.0",
"php": "^7.1 || ^8.0"
},
"conflict": {
"nyholm/psr7": "<1.0",
"zendframework/zend-diactoros": "*"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "*",
"psr/http-factory-implementation": "*",
"psr/http-message-implementation": "*"
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"graham-campbell/phpspec-skip-example-extension": "^5.0",
"php-http/httplug": "^1.0 || ^2.0",
"php-http/message-factory": "^1.0",
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
"sebastian/comparator": "^3.0.5 || ^4.0.8",
"symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
},
"type": "composer-plugin",
"extra": {
"class": "Http\\Discovery\\Composer\\Plugin",
"plugin-optional": true
},
"autoload": {
"psr-4": {
"Http\\Discovery\\": "src/"
},
"exclude-from-classmap": [
"src/Composer/Plugin.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
"homepage": "http://php-http.org",
"keywords": [
"adapter",
"client",
"discovery",
"factory",
"http",
"message",
"psr17",
"psr7"
],
"support": {
"issues": "https://github.com/php-http/discovery/issues",
"source": "https://github.com/php-http/discovery/tree/1.20.0"
},
"time": "2024-10-02T11:20:13+00:00"
},
{
"name": "php-http/httplug",
"version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/php-http/httplug.git",
"reference": "5cad731844891a4c282f3f3e1b582c46839d22f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4",
"reference": "5cad731844891a4c282f3f3e1b582c46839d22f4",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"php-http/promise": "^1.1",
"psr/http-client": "^1.0",
"psr/http-message": "^1.0 || ^2.0"
},
"require-dev": {
"friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0",
"phpspec/phpspec": "^5.1 || ^6.0 || ^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eric GELOEN",
"email": "geloen.eric@gmail.com"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "HTTPlug, the HTTP client abstraction for PHP",
"homepage": "http://httplug.io",
"keywords": [
"client",
"http"
],
"support": {
"issues": "https://github.com/php-http/httplug/issues",
"source": "https://github.com/php-http/httplug/tree/2.4.1"
},
"time": "2024-09-23T11:39:58+00:00"
},
{
"name": "php-http/promise",
"version": "1.3.1",
"source": {
"type": "git",
"url": "https://github.com/php-http/promise.git",
"reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
"reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"require-dev": {
"friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3",
"phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Http\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Joel Wurtz",
"email": "joel.wurtz@gmail.com"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "Promise used for asynchronous HTTP requests",
"homepage": "http://httplug.io",
"keywords": [
"promise"
],
"support": {
"issues": "https://github.com/php-http/promise/issues",
"source": "https://github.com/php-http/promise/tree/1.3.1"
},
"time": "2024-03-15T13:55:21+00:00"
},
{
"name": "phpoffice/common",
"version": "1.0.5",
@ -7258,82 +6950,6 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php82",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php82.git",
"reference": "5d2ed36f7734637dacc025f179698031951b1692"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/5d2ed36f7734637dacc025f179698031951b1692",
"reference": "5d2ed36f7734637dacc025f179698031951b1692",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php82\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php82/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/process",
"version": "v5.4.47",

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserDevicesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('user_devices'))
return;
Schema::create('user_devices', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('userid')->index()->nullable()->default(0)->comment('会员ID');
$table->string('hash')->index()->nullable()->default('')->comment('TOKEN MD5');
$table->longText('detail')->nullable()->comment('详细信息');
$table->timestamp('expired_at')->nullable()->comment('过期时间');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_devices');
}
}