diff --git a/app/Aspect/Api/ApiReturnAspect.php b/app/Aspect/Api/ApiReturnAspect.php new file mode 100644 index 0000000..d41bfab --- /dev/null +++ b/app/Aspect/Api/ApiReturnAspect.php @@ -0,0 +1,82 @@ +process(); + // 在调用后进行处理 + $this->userId = Context::get('user_id',0); + + // 加密前写请求日志 + $this->writeResponseLog(json_encode($result)); + + //正式服加密 测试服不做处理 + if (SystemUtil::checkProEnv()) { + $cryptoFactory = $this->CryptoFactory->cryptoClass('api', json_encode($result['data'])); + $result['data'] = $cryptoFactory->encrypt(); + } + + return $result; + } + + /** + * 请求日志 + * @param string $result + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + private function writeResponseLog(string $result): void + { + $post = $this->request->all(); + $logParam = !$post ? '{}' : json_encode($post); + $header = json_encode($this->request->getHeaders()); + + $this->log->requestApiLog("\napi==path:{$this->request->getPathInfo()}\nuserId:$this->userId\nrequestData:$logParam\nheader:$header\nresponseData:$result"); + } +} \ No newline at end of file diff --git a/app/Cache/Redis/Api/ApiRedisKey.php b/app/Cache/Redis/Api/ApiRedisKey.php new file mode 100644 index 0000000..afe8ac2 --- /dev/null +++ b/app/Cache/Redis/Api/ApiRedisKey.php @@ -0,0 +1,26 @@ +handle(); + } +} diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php new file mode 100644 index 0000000..8fd2375 --- /dev/null +++ b/app/Exception/ApiException.php @@ -0,0 +1,10 @@ +return->error($throwable->getMessage(),$throwable->getCode()); + + // 阻止异常冒泡 + $this->stopPropagation(); + + return $response->withHeader("Content-Type", "application/json") + ->withStatus(200) + ->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE))); + } + + // 交给下一个异常处理器 + return $response; + } + + public function isValid(Throwable $throwable): bool + { + return true; + } +} \ No newline at end of file diff --git a/app/Lib/ApiReturn.php b/app/Lib/ApiReturn.php new file mode 100644 index 0000000..6191e27 --- /dev/null +++ b/app/Lib/ApiReturn.php @@ -0,0 +1,40 @@ + $code, + 'msg' => $msg, + 'data' => $data + ]; + } + + /** + * 通用api返回 + * @param string $msg + * @param array $data + * @param int $code + * @return array + */ + public function error(string $msg = 'error', int $code = ReturnCode::ERROR, array $data = []): array + { + return [ + 'code' => $code, + 'msg' => $msg, + 'data' => $data + ]; + } +} \ No newline at end of file diff --git a/app/Model/User.php b/app/Model/User.php new file mode 100644 index 0000000..5fbf2e0 --- /dev/null +++ b/app/Model/User.php @@ -0,0 +1,51 @@ + 'integer', 'avatar_id' => 'integer', 'gender' => 'integer', 'age' => 'integer', 'city' => 'integer', 'is_del' => 'integer']; + + const CREATED_AT = 'create_time'; + const UPDATED_AT = 'update_time'; + + +} diff --git a/app/Model/UserThird.php b/app/Model/UserThird.php new file mode 100644 index 0000000..5efa19f --- /dev/null +++ b/app/Model/UserThird.php @@ -0,0 +1,43 @@ + 'integer', 'user_id' => 'integer', 'type' => 'integer']; + + /** + * @param string $openId + * @return Builder|\Hyperf\Database\Model\Model|null + */ + public function getThirdInfoByOpenId(string $openId): \Hyperf\Database\Model\Model|Builder|null + { + return $this->where('open_id', $openId)->first(); + } +} diff --git a/app/Request/Api/LoginRequest.php b/app/Request/Api/LoginRequest.php new file mode 100644 index 0000000..9f6abbb --- /dev/null +++ b/app/Request/Api/LoginRequest.php @@ -0,0 +1,28 @@ +userId = Context::get("user_id",0); + } + + /** + * 主体函数抽象类 + */ + abstract public function handle(); +} \ No newline at end of file diff --git a/app/Service/Api/Login/LoginBaseService.php b/app/Service/Api/Login/LoginBaseService.php new file mode 100644 index 0000000..efaa822 --- /dev/null +++ b/app/Service/Api/Login/LoginBaseService.php @@ -0,0 +1,164 @@ +userId = empty($this->userId) ? $this->userInfo->id : $this->userId; + + if (empty($this->userId)) { + throw new ApiException('登录失败'); + } + + //todo 判断注销 判断封号 + + //todo 更新登录时间 + } + + + /** + * 返回值 + * @return array + * @throws \Exception + */ + protected function getReturn():array + { + $loginReturn = [ + 'id' => $this->userId, + 'is_bind_mobile' => $this->userInfo->mobile ? UserCode::IS_BIND_PHONE : UserCode::IS_NOT_BIND_PHONE, + 'nickName' => $this->userInfo->nickName, + 'is_default_avatar' => $this->userInfo->avatar_id == 0 ? UserCode::IS_DEFAULT_AVATAR : UserCode::IS_NOT_DEFAULT_AVATAR, + ]; + + $loginReturn['token'] = $this->cryptoFactory->cryptoClass('jwt', json_encode($loginReturn))->encrypt(); + + + return $loginReturn; + } + + + /** + * 判断是不是没有加锁 + * @param $type + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \RedisException + */ + protected function checkLock($type): void + { + $this->lockKey = match ($type){ + 1 => ApiRedisKey::LoginAndRegisterByMobileLock($this->mobile), + 2 => ApiRedisKey::LoginAndRegisterByCodeLock($this->jsCode) + }; + + if (0 == ($this->redis->addLock($this->lockKey))){ + throw new ApiException('请勿重复点击'); + } + } + + /** + * 添加用户 + * @return void + */ + protected function addUser(): void + { + $model = new User(); + + //默认头像和默认名称 + $model->nickname = '用户'.StringUtil::randStr(6); + $model->avatar_id = 0; + $model->reg_ip = SystemUtil::getClientIp(); + + if (!$model->save()) throw new ApiException('数据保存失败-注册失败'); + + $this->userId = $model->id; + $this->userInfo = $model; + } + + abstract protected function register(); +} \ No newline at end of file diff --git a/app/Service/Api/Login/LoginService.php b/app/Service/Api/Login/LoginService.php new file mode 100644 index 0000000..8acec16 --- /dev/null +++ b/app/Service/Api/Login/LoginService.php @@ -0,0 +1,36 @@ +loginType = $this->request->input('login_type',''); + + $factory = new LoginTypeFactory(); + $service = match ($this->request->input('login_type','')) + { + 'wx_login' => $factory->wxFastLogin(), + 'mobile_code' => $factory->mobileCodeLogin(), + default => throw new AdminException('登录类型错误'), + }; + + $loginInfo = $service->handle(); + + return $this->return->success('登录成功',$loginInfo); + } +} \ No newline at end of file diff --git a/app/Service/Api/Login/LoginTypeFactory.php b/app/Service/Api/Login/LoginTypeFactory.php new file mode 100644 index 0000000..de261ea --- /dev/null +++ b/app/Service/Api/Login/LoginTypeFactory.php @@ -0,0 +1,32 @@ +jsCode = $this->request->input('js_code'); + + if (empty($this->jsCode)) throw new ApiException('登录参数错误'); + + $wxInfo = $this->jsCodeGetOpenId($this->jsCode); + $this->openId = $wxInfo['openid']; + $this->unionId = $wxInfo['unionid']; + + $this->checkLock(self::LOGIN_TYPE); + + $this->register(); + + $this->login(); + + $this->redis->delLock($this->lockKey); + + return $this->getReturn(); + } + + protected function register(): void + { + $thirdInfo = $this->userThirdModel->getThirdInfoByOpenId($this->openId); + if (!empty($thirdInfo)) { + $this->userId = $thirdInfo->user_id; + return; + } + + // todo 设备封禁不可注册 注册限制 + + Db::transaction(function () { + $this->addUser(); + + $this->addUserThird(); + + //todo 要不要生成邀请码 有没有注册奖励 + }); + } + + /** + * @return void + */ + private function addUserThird(): void + { + $model = new UserThird(); + + $model->user_id = $this->userId; + $model->open_id = $this->openId; + $model->union_id = $this->unionId; + + if (!$model->save()) throw new ApiException('注册失败-00001'); + } +} \ No newline at end of file diff --git a/app/Service/ServiceTrait/Api/WxMiniTrait.php b/app/Service/ServiceTrait/Api/WxMiniTrait.php new file mode 100644 index 0000000..42a413e --- /dev/null +++ b/app/Service/ServiceTrait/Api/WxMiniTrait.php @@ -0,0 +1,79 @@ +clientFactory->create([ + 'base_uri' => $this->BaseUri, + 'timeout' => 5 + ])->get($url); + + $contents = $tencentResponse->getBody()->getContents(); + + $this->log->info(__class__.':微信服务器返回token信息:', [$contents]); + + $res = json_decode($contents,true); + + if (empty($res['errcode']) || $res['errcode'] != 0) throw new ApiException($res['errmsg'] ?? '系统繁忙'); + + return $res; + }catch (GuzzleException $e) { + $this->log->debug(__CLASS__.':debug:'.$e->getMessage()); + throw new ApiException($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/config/autoload/exceptions.php b/config/autoload/exceptions.php index 0faa59d..360d55c 100644 --- a/config/autoload/exceptions.php +++ b/config/autoload/exceptions.php @@ -13,6 +13,7 @@ return [ 'handler' => [ 'http' => [ Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class, + App\Exception\Handler\ApiExceptionHandler::class, App\Exception\Handler\AdminExceptionHandler::class, App\Exception\Handler\ValidationDataExceptionHandler::class, App\Exception\Handler\AppExceptionHandler::class, diff --git a/config/autoload/system.php b/config/autoload/system.php index 79253fb..afe0250 100644 --- a/config/autoload/system.php +++ b/config/autoload/system.php @@ -20,5 +20,9 @@ return [ // admin jwt 过期时间 'admin_jwt_expire' => env('ADMIN_JWT_EXPIRE',86400 * 30), // admin 默认密码 - 'admin_default_password' => '123456' + 'admin_default_password' => '123456', + // 微信小程序的appid + 'wx_appid' => env('WX_APPID',''), + // 微信小程序的secret + 'wx_secret' => env('WX_SECRET',''), ]; \ No newline at end of file