diff --git a/app/Const/Im.php b/app/Const/Im.php new file mode 100644 index 0000000..caf87ef --- /dev/null +++ b/app/Const/Im.php @@ -0,0 +1,33 @@ + 'vrcode:register:', + self::TOPIC_RESET_PASSWD => 'vrcode:reset_passwd:', + self::TOPIC_RESET_GOOGLE_AUTH => 'vrcode:reset_google_auth:', + self::TOPIC_SET_WALLET_PASSWD => 'vrcode:set_wallet_passwd:', + ]; + +} diff --git a/app/Exceptions/AppException.php b/app/Exceptions/AppException.php new file mode 100644 index 0000000..5a0f4bc --- /dev/null +++ b/app/Exceptions/AppException.php @@ -0,0 +1,8 @@ +validateMethodParams[$method])) { - request()->validate($this->validateMethodParams[$method]); + $a = request()->validate($this->validateMethodParams[$method]); } parent::__call($method, $parameters); } diff --git a/app/Http/Controllers/Customer/CustomerUserController.php b/app/Http/Controllers/Customer/CustomerUserController.php index 8deb315..b5f0fd4 100644 --- a/app/Http/Controllers/Customer/CustomerUserController.php +++ b/app/Http/Controllers/Customer/CustomerUserController.php @@ -1,28 +1,72 @@ [ - 'username' => 'required|alpha_dash:ascii|max:50', + 'phone_area' => 'required|alpha_dash:ascii|max:5', + 'phone' => 'required|numeric|max:15', 'password' => 'required|alpha_dash:ascii|max:50', - 'device' => 'required|alpha_dash:ascii|max:10', + 'device' => 'required|numeric|max:10', + ], + 'register' => [ + 'phone_area' => 'required|alpha_dash:ascii|max:5', + 'phone' => 'required|numeric|max:15', + 'password' => 'required|alpha_dash:ascii|max:50', + 'device' => 'required|numeric|max:2', + 'vr_code' => 'required|numeric|max:10', + ], + 'checkAccount' => [ + 'username' => 'alpha_dash:ascii|max:50', + 'phone_area' => 'alpha_dash:ascii|max:5', + 'phone' => 'numeric|max:15', + 'email' => 'email|max:30', + ], + 'setUserInfo' => [ + 'nickname' => 'max:20', + 'email' => 'email|max:30', + 'username' => 'alpha_dash:ascii|max:50', + 'phone_area' => 'alpha_dash:ascii|max:5', + 'phone' => 'numeric|max:15', + 'is_google_auth' => 'numeric|max:2', + ], + 'sendVrcodeCode' => [ + 'topic' => 'required|numeric', + 'phone_area' => 'alpha_dash:ascii|max:5', + 'phone' => 'numeric|max:15', ], ]; - function test() + function getCustomerUserInfo(): \Illuminate\Http\JsonResponse { - var_dump('asd'); - $a = Redis::get('test'); - var_dump($a); + $oAuthService = new AuthService(); + $token = $oAuthService->getTokenFromReq(); + $aUser = $oAuthService->getCurrentUser(); + $data = [ + 'token' => $token, + 'user' => [ + 'id' =>$aUser['id'], + 'username' => $aUser['username'], + 'nickname' => $aUser['nickname'], + 'is_google_auth' => $aUser['is_google_auth'], + 'created_at' => $aUser['created_at'], + 'updated_at' => $aUser['updated_at'], + ], + ]; + return $this->success($data); } function signIn(): \Illuminate\Http\JsonResponse @@ -31,6 +75,7 @@ class CustomerUserController extends CustomerBaseController $username = $request->input('username'); $password = $request->input('password'); $device = $request->input('device'); + if(!in_array($device,Im::PLATFORM)) return $this->error('invalid device'); $oCustomerUser = new CustomerUser(); $oUser = $oCustomerUser->findItemByUsername($username); @@ -44,10 +89,13 @@ class CustomerUserController extends CustomerBaseController } $oAuthService = new AuthService(); + $oImService = new ImService(); $token = $oAuthService->createTokenToUser($oUser->id,$device); + $imToken = $oImService->authUserToken($oUser->id,$device); $data = [ 'token' => $token, + 'im_token' => $imToken, 'user' => [ 'id' => $oUser->id, 'username' => $oUser->username, @@ -57,26 +105,45 @@ class CustomerUserController extends CustomerBaseController 'updated_at' => $oUser->updated_at, ], ]; - return $this->success($data); } + function setUserInfo(): \Illuminate\Http\JsonResponse + { + $request = request(); + $aReqData = $request->only([ + 'nickname', + 'email', + 'phone_area', + 'phone', + 'is_google_auth', + ]); + $oAuthService = new AuthService(); + $aUser = $oAuthService->getCurrentUser(); + $aReqData['id'] = $aUser['id']; + $aReqData = array_filter($aReqData); + $oCustomerUser = new CustomerUser(); + if(!$oCustomerUser->updateItem($aReqData)) return $this->error(); + return $this->success(); + } + function signOut(): \Illuminate\Http\JsonResponse { $oAuthService = new AuthService(); $token = $oAuthService->getTokenFromReq(); $aUser = $oAuthService->getCurrentUser(); - $oAuthService->delTokenToUser($aUser['uid'],$token); + $oAuthService->delTokenToUser($aUser['id'],$token); return $this->success(); } - function register() + function register(): \Illuminate\Http\JsonResponse { $request = request(); $username = $request->input('username'); $password = $request->input('password'); $device = $request->input('device'); + $sVrCode = $request->input('vr_code'); $oCustomerUser = new CustomerUser(); $oUser = $oCustomerUser->findItemByUsername($username,['id']); @@ -95,6 +162,10 @@ class CustomerUserController extends CustomerBaseController return $this->error('注册失败'); } + //向im注册 + $oImService = new ImService(); + if(!$oImService->userUserRegister($oUser->id)) throw new AppException('im register error'); + $oAuthService = new AuthService(); $token = $oAuthService->createTokenToUser($oUser->id,$device); @@ -113,4 +184,68 @@ class CustomerUserController extends CustomerBaseController return $this->success($data); } + function checkAccount() + { + $request = request(); + $aReqData = $request->only([ + 'nickname', + 'email', + 'phone_area', + 'phone', + ]); + $aReqData = array_filter($aReqData); + if (empty($aReqData)) return $this->error(); + $oCustomerUser = new CustomerUser(); + $oUser = $oCustomerUser->findItemByAccount($aReqData,['id']); + if ($oUser) { + return $this->error('用户已存在'); + } + return $this->success(); + } + + /** + * @throws ContainerExceptionInterface + * @throws AppException + * @throws NotFoundExceptionInterface + */ + function sendVrcodeCode(): \Illuminate\Http\JsonResponse + { + //发送短信验证码 + $request = request(); + $aReqData = $request->only([ + 'topic', + 'phone_area', + 'phone', + ]); + if(!in_array($aReqData['topic'],VrCode::TOPIC)) return $this->error('invalid vrcode type'); + $oVrCodeService = new VrCodeService(); + $oVrCodeService->setIType($aReqData['topic']); + if($aReqData['topic'] == VrCode::TOPIC_REGISTER){ + + $validator = Validator::make($aReqData, [ + 'phone_area' => 'required|alpha_dash:ascii|max:5', + 'phone' => 'required|numeric|max:15', + ]); + if ($validator->fails()) { + return $this->error($validator->errors()->first()); + } + $oCustomerUser = new CustomerUser(); + $oUser = $oCustomerUser->findItemByPhone($aReqData['phone_area'],$aReqData['phone'],['id']); + if ($oUser) { + return $this->error('vrcode error'); + } + $oVrCodeService->setSPhoneArea($aReqData['phone_area']); + $oVrCodeService->setSPhone($aReqData['phone']); + $oVrCodeService->sendCode(); + return $this->success(); + }else{ + $oAuthService = new AuthService(); + $aUser = $oAuthService->getCurrentUser(); + if($aUser == null) return $this->error('user not login'); + $oVrCodeService->setIUid($aUser['id']); + $oVrCodeService->sendCode(); + return $this->success(); + } + } + } diff --git a/app/Models/Base/BaseModel.php b/app/Models/Base/BaseModel.php index ad9b398..587a625 100644 --- a/app/Models/Base/BaseModel.php +++ b/app/Models/Base/BaseModel.php @@ -2,10 +2,13 @@ namespace App\Models\Base; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Hash; class BaseModel extends Model { + public $timestamps = false; function checkColInFill($aItem) { diff --git a/app/Models/Customer/CustomerUser.php b/app/Models/Customer/CustomerUser.php index 3b1e707..bf701f2 100644 --- a/app/Models/Customer/CustomerUser.php +++ b/app/Models/Customer/CustomerUser.php @@ -6,6 +6,7 @@ use App\Const\RedisConst; use App\Models\Base\CustomerBaseModel; use App\Service\AuthService; use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Log; @@ -45,17 +46,43 @@ class CustomerUser extends CustomerBaseModel return Hash::check($sPasswd,$oUser->password); } - function addUser($aItem): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool + function addUser($aItem): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|array|null { -// if(isset($aItem['password']) && !empty($aItem['password'])) $aItem['password'] = Hash::make($aItem['password']); + $sDateTime = Carbon::now()->toDateTimeString(); + $aItem['created_at'] = $sDateTime; + $aItem['updated_at'] = $sDateTime; return $this->addItem($aItem); } - function findItemByUsername($sUsername,$col=['*']): \Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Builder|array|null + function findItemByAccount($aData,$col=['*']): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|array|null + { + if(!empty($aData['username'])){ + return $this->findItemByUsername($aData['username'],$col); + } + if(!empty($aData['phone_area']) && !empty($aData['phone'])){ + return $this->findItemByPhone($aData['phone_area'],$aData['phone'],$col); + } + if(!empty($aData['email'])){ + return $this->findItemByEmail($aData['email'],$col); + } + return null; + } + + function findItemByUsername($sUsername,$col=['*']): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|array|null { return $this->newQuery()->where('username',$sUsername)->first($col); } + function findItemByPhone($sPhoneArea,$sPhone,$col=['*']): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|array|null + { + return $this->newQuery()->where('phone_area',$sPhoneArea)->where('phone',$sPhone)->first($col); + } + + function findItemByEmail($sEmail,$col=['*']): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|array|null + { + return $this->newQuery()->where('email',$sEmail)->first($col); + } + function findUserByUidWithCache($iUid): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Builder|array|null { return Cache::remember(RedisConst::ORM_CACHE_USER.$iUid,RedisConst::ORM_FIND_CACHE_SECOND,function ()use ($iUid){ @@ -63,6 +90,16 @@ class CustomerUser extends CustomerBaseModel }); } +// function setUserInfo($iUid,$sNickname): bool|int +// { +// return $this->updateItem([ +// 'id' => $iUid, +// 'nickname' => $sNickname, +// 'email' => $sNickname, +// 'phone_area' => $sNickname, +// ]); +// } + } diff --git a/app/Service/AuthService.php b/app/Service/AuthService.php index 0b3dc3b..0afaf05 100644 --- a/app/Service/AuthService.php +++ b/app/Service/AuthService.php @@ -6,6 +6,8 @@ use App\Const\RedisConst; use App\Tools\Tools; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Redis; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class AuthService { @@ -52,7 +54,7 @@ class AuthService function getAllTokenInfoByUid($iUid) { $sTokenList = Redis::get(RedisConst::UID_TOKENS . $iUid); - if (empty($sToken)) return null; + if (empty($sTokenList)) return null; return unserialize($sTokenList); } @@ -140,6 +142,10 @@ class AuthService }); } + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ function getCurrentUser() { if(app()->has('customerUser')){ diff --git a/app/Service/ImService.php b/app/Service/ImService.php new file mode 100644 index 0000000..15b5b22 --- /dev/null +++ b/app/Service/ImService.php @@ -0,0 +1,80 @@ + '', + 'users' => [ + 'userID' => '', + 'nickname' => '', + 'faceURL' => '', + ], + ]; + + /** + * @throws AppException + */ + function userUserRegister($userId, $nickname = '', $faceUrl=''): bool + { + $path = '/user/user_register'; + $aData['users'] = [ + 'userID' => $this->genImUid($userId), + 'nickname' => $nickname, + 'faceURL' => $faceUrl, + ]; + $jRes = $this->sendReq($path,$aData); + if(isset($jRes['errCode']) && $jRes['errCode'] === 0) return true; + return false; + } + + /** + * @throws AppException + */ + function authUserToken($userId,int $platform): false|array + { + $path = '/auth/user_token'; + $aData['users'] = [ + 'platformID' => $platform, + 'userID' => $this->genImUid($userId), + ]; + $jRes = $this->sendReq($path,$aData); + if(isset($jRes['errCode']) && $jRes['errCode'] === 0){ + return [ + 'token' => $jRes['token'], + 'expireTimeSeconds' => $jRes['expireTimeSeconds'], + ]; + } + return false; + } + + function getSecret() + { + return Env::get('IM_SECRET'); + } + + function getUrl(): string + { + return rtrim(Env::get('IM_URL'),'/'); + } + + /** + * @throws AppException + */ + function sendReq(string $path,array $aData = []) + { + if (!isset($aData['secret'])) $aData['secret'] = $this->getSecret(); + $resp = Http::post($this->getUrl().$path,$aData); + if(!$resp->ok()) throw new AppException('im request error'); + return $resp->json(); + } + + function genImUid($userId): string + { + return Im::USER_PREFIX.$userId; + } +} diff --git a/app/Service/VrCodeService.php b/app/Service/VrCodeService.php new file mode 100644 index 0000000..62d7b34 --- /dev/null +++ b/app/Service/VrCodeService.php @@ -0,0 +1,141 @@ +sKey; + } + + public function setSKey(null $sKey): void + { + $this->sKey = $sKey; + } + + public function getsCode(): null + { + return $this->sCode; + } + + public function setsCode(null $sCode): void + { + $this->sCode = $sCode; + } + + public function getSPhoneArea(): null + { + return $this->sPhoneArea; + } + + public function setSPhoneArea(null $sPhoneArea): void + { + $this->sPhoneArea = $sPhoneArea; + } + + public function getSPhone(): null + { + return $this->sPhone; + } + + public function setSPhone(null $sPhone): void + { + $this->sPhone = $sPhone; + } + + public function getIType(): null + { + return $this->iType; + } + + /** + * @throws AppException + */ + public function setIType(null $iType): void + { + if(!in_array($iType,VrCode::TOPIC)) throw new AppException('invalid sms type'); + $this->iType = $iType; + } + + public function getIUid(): null + { + return $this->iUid; + } + + public function setIUid(null $iUid): void + { + $this->iUid = $iUid; + } + + function sendSmsToPhone(): bool + { + return true; + } + + /** + * @throws AppException + */ + function sendCode(): void + { + $this->sKey = $this->genKey(); + $this->sCode = $this->genCode(); + if(!$this->sendSmsToPhone()) throw new AppException('send sms failed'); + $this->setCodeToCache(); + } + + /** + * @throws AppException + */ + function genKey(): false|string + { + if(!in_array($this->iType,VrCode::TOPIC_PREFIX)) throw new AppException('invalid sms type'); + if($this->iType == VrCode::TOPIC_REGISTER) { + return VrCode::TOPIC_PREFIX[VrCode::TOPIC_REGISTER] . md5($this->sPhoneArea . $this->sPhone); + }else { + return VrCode::TOPIC_PREFIX[VrCode::TOPIC_REGISTER] . $this->iUid; + } + } + + function genCode(): string + { + return (string)Tools::getRandNum(); + } + + function setCodeToCache() + { + return Redis::set($this->sKey,$this->sCode,VrCode::REDIS_CACHE_EXPIRE); + } + + /** + * @throws AppException + */ + function getCodeFromCache() + { + return Redis::get($this->genKey()); + } + + function checkCode($sReqCode): bool + { + $sCode = $this->getCodeFromCache(); + if(empty($sCode)) return false; + if($sCode != $sReqCode) return false; + return true; + } + + +} diff --git a/app/Tools/Tools.php b/app/Tools/Tools.php index 5e8bfa0..2c75c7d 100644 --- a/app/Tools/Tools.php +++ b/app/Tools/Tools.php @@ -39,5 +39,10 @@ class Tools return mb_substr($sStr, 0, $iStart) . $sHideStr . mb_substr($sStr, $iStart + $iEnd); } + static function getRandNum(): int + { + return rand(100000,999999); + } + } diff --git a/composer.json b/composer.json index 8a3d72d..8471ab5 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "license": "MIT", "require": { "php": "^8.1", - "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/guzzle": "^7.8", "laravel/framework": "^10.10", "laravel/sanctum": "^3.3", "laravel/tinker": "^2.8" diff --git a/composer.lock b/composer.lock index 109bfef..24a0464 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "9c491b8531eec05ba41a11d9276a5749", + "content-hash": "04667de44913e2c1bc4de60fd5fa0d04", "packages": [ { "name": "brick/math", diff --git a/routes/api.php b/routes/api.php index c724834..6c82920 100644 --- a/routes/api.php +++ b/routes/api.php @@ -20,15 +20,29 @@ use Illuminate\Support\Facades\Route; //不需要登录的路由 Route::middleware([])->group(function () { + $class = \App\Http\Controllers\Customer\CustomerUserController::class; Route::post('/test', [$class, 'test']); + + Route::prefix('customerUser')->group(function () { + $class = \App\Http\Controllers\Customer\CustomerUserController::class; + Route::post('/register', [$class, 'register']); + Route::post('/signIn', [$class, 'signIn']); + }); + + }); //需要登录的路由 Route::middleware(['auth'])->group(function () { - Route::prefix('customer_user')->group(function () { + + Route::prefix('customerUser')->group(function () { $class = \App\Http\Controllers\Customer\CustomerUserController::class; - Route::post('/test', [$class, 'test']); + Route::post('/signOut', [$class, 'signOut']); + Route::post('/getCustomerUserInfo', [$class, 'getCustomerUserInfo']); + Route::post('/setUserInfo', [$class, 'setUserInfo']); + Route::post('/checkAccount', [$class, 'checkAccount']); }); + });