在 Yii 2.0 中,Redis 的活动记录(Active Record),基于 row 查看结构,与模型字段顺序不一致的排查分析
1、在 Yii 2.0 中,Redis 的活动记录(Active Record),基于 row 查看结构,与模型字段顺序不一致。如图1
2、查看 Redis 活动记录类,/common/models/redis/DouyinWebAppUserAccessToken.php
<?php namespace common\models\redis; use Yii; use common\components\redis\ActiveRecord; /** * This is the model class for table "{{%douyin_web_app_user_access_token}}". * * @property integer $id * @property integer $douyin_web_app_id 抖音的第三方服务平台网站应用ID * @property integer $channel_app_source_id 渠道的应用的来源ID * @property string $channel_app_source_uuid 渠道的应用的来源ID(UUID) * @property integer $douyin_web_app_user_id 抖音的第三方服务平台网站应用的用户ID * @property string $douyin_web_app_user_uuid 抖音的第三方服务平台网站应用的用户ID(UUID) * @property string $grant_type 授权类型,authorization_code:第三方平台授权模式 * @property string $access_token 访问令牌 * @property integer $expires_in 访问令牌有效期,单位(秒) * @property integer $expires_at 访问令牌有效截止时间 * @property string $refresh_token 刷新令牌 * @property integer $refresh_token_expires_in 刷新令牌有效期,单位(秒) * @property integer $refresh_token_expires_at 刷新令牌有效截止时间 * @property string $scope 权限范围 * @property string $open_id 授权第三方用户的用户唯一标识 * @property integer $status 状态,0:禁用;1:启用 * @property integer $is_deleted 是否被删除,0:否;1:是 * @property integer $created_at 创建时间 * @property integer $updated_at 更新时间 * @property integer $deleted_at 删除时间 */ class DouyinWebAppUserAccessToken extends ActiveRecord { /** * @return array the list of attributes for this record */ public function attributes() { return ['id', 'douyin_web_app_id', 'channel_app_source_id', 'channel_app_source_uuid', 'douyin_web_app_user_id', 'douyin_web_app_user_uuid', 'grant_type', 'access_token','expires_in', 'expires_at', 'refresh_token', 'refresh_token_expires_in', 'refresh_token_expires_at', 'scope', 'open_id', 'status', 'is_deleted', 'created_at', 'updated_at', 'deleted_at']; } /** * @inheritdoc */ public function rules() { return [ [['id', 'douyin_web_app_id', 'channel_app_source_id', 'channel_app_source_uuid', 'douyin_web_app_user_id', 'douyin_web_app_user_uuid', 'grant_type', 'access_token','expires_in', 'expires_at', 'refresh_token', 'refresh_token_expires_in', 'refresh_token_expires_at', 'scope', 'open_id', 'status', 'is_deleted', 'created_at', 'updated_at', 'deleted_at'], 'safe'], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('model/redis/douyin-web-app-user-access-token', 'ID'), 'douyin_web_app_id' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Douyin Web App Id'), 'channel_app_source_id' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Channel App Source Id'), 'channel_app_source_uuid' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Channel App Source Uuid'), 'douyin_web_app_user_id' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Douyin Web App User Id'), 'douyin_web_app_user_uuid' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Douyin Web App User Uuid'), 'grant_type' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Grant Type'), 'access_token' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Access Token'), 'expires_in' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Expires In'), 'expires_at' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Expires At'), 'refresh_token' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Refresh Token'), 'refresh_token_expires_in' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Refresh Token Expires In'), 'refresh_token_expires_at' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Refresh Token Expires At'), 'scope' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Scope'), 'open_id' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Open Id'), 'status' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Status'), 'is_deleted' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Is Deleted'), 'created_at' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Created At'), 'updated_at' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Updated At'), 'deleted_at' => Yii::t('model/redis/douyin-web-app-user-access-token', 'Deleted At'), ]; } /** * {@inheritdoc} * @return DouyinWebAppUserAccessTokenQuery the active query used by this AR class. */ public static function find() { return new DouyinWebAppUserAccessTokenQuery(get_called_class()); } }
3、打印一下 Redis 的模型对象(更新前),$redisDouyinWebAppUserAccessToken。
/** * 基于 Access Token 插入/更新数据至抖音的第三方服务平台网站应用的用户的访问令牌(Redis) * @param array $data 数据 * 格式如下: * * [ * 'douyinWebAppId' => 1, // 抖音的第三方服务平台网站应用ID * 'channelAppSourceId' => 42, // 渠道的应用的来源ID * 'channelAppSourceUuid' => '4dd4924a223d11ebb87054ee75d2ebc1', // 渠道的应用的来源ID(UUID) * 'douyinWebAppUserId' => 4, // 抖音的第三方服务平台网站应用的用户ID * 'douyinWebAppUserUuid' => '4dd5a770223d11eb988c54ee75d2ebc1', // 抖音的第三方服务平台网站应用的用户ID(UUID) * 'grantType' => 'authorization_code', // 授权类型,authorization_code:第三方平台授权模式 * 'access_token' => 'act.fbb67f15948da5ab6d832b69daa58b7e3HEgNAeS3vv7hOBVqjNpjiJ1fZYY', // 访问令牌 * 'captcha' => '', // * 'desc_url' => '', // * 'description' => '', // 错误码描述 * 'error_code' => 0, // 错误码 * 'expires_in' => 1296000, // 访问令牌有效期,单位(秒) * 'open_id' => '3c61649c-2cd5-4479-b2a3-0c89e8f808f8', // 授权第三方用户的用户唯一标识 * 'refresh_expires_in' => 2592000, // 刷新令牌有效期,单位(秒) * 'refresh_token' => 'rft.1b82e0d7acbea76d68f45b44461e649fQzF6hSBGPuq9w7FVWv0cJ53J4aSg', // 刷新令牌 * 'scope' => 'fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list', // 权限范围 * ] * * @return array * 格式如下: * * [ * 'status' => true, // 成功 * 'data' => [ // array * 'channel_app_source_uuid' => 'ddb8822607ec11e9beda54ee75d2ebc1', // 渠道的应用的来源ID(UUID) * ] * ] * * [ * 'status' => false, // 失败 * 'code' => 214010, // 返回码 * 'message' => '', // 说明 * ] * * @throws ServerErrorHttpException */ public function saveModel($data) { /* 基于抖音的第三方服务平台网站应用的用户ID查找状态为启用的单个资源 */ $redisDouyinWebAppUserAccessToken = RedisDouyinWebAppUserAccessToken::find()->where(['douyin_web_app_user_id' => $data['douyinWebAppUserId']])->isDeletedNo()->enabled()->one(); if (!isset($redisDouyinWebAppUserAccessToken)) { $redisDouyinWebAppUserAccessToken = new RedisDouyinWebAppUserAccessToken(); } $time = time(); // 设置访问令牌的有效截止时间 $accessTokenExpireAt = $time + $data['expires_in'] - Yii::$app->params['accessToken']['timeOut']; // 设置刷新令牌的有效截止时间 $refreshTokenExpireAt = $time + $data['refresh_expires_in'] - Yii::$app->params['refreshToken']['timeOut']; $redisDouyinWebAppUserAccessToken->attributes = [ 'douyin_web_app_id' => $data['douyinWebAppId'], 'channel_app_source_id' => $data['channelAppSourceId'], 'channel_app_source_uuid' => $data['channelAppSourceUuid'], 'douyin_web_app_user_id' => $data['douyinWebAppUserId'], 'douyin_web_app_user_uuid' => $data['douyinWebAppUserUuid'], 'grant_type' => $data['grantType'], 'access_token' => $data['access_token'], 'expires_in' => $data['expires_in'], 'expires_at' => $accessTokenExpireAt, 'refresh_token' => $data['refresh_token'], 'refresh_token_expires_in' => $data['refresh_expires_in'], 'refresh_token_expires_at' => $refreshTokenExpireAt, 'scope' => $data['scope'], 'open_id' => $data['open_id'], 'status' => RedisDouyinWebAppUserAccessToken::STATUS_ENABLED, ]; print_r($redisDouyinWebAppUserAccessToken); exit; $return = []; if ($redisDouyinWebAppUserAccessToken->save()) { $return = ['status' => true, 'data' => ['channel_app_source_uuid' => $data['channelAppSourceUuid']]]; } elseif ($redisDouyinWebAppUserAccessToken->hasErrors()) { $firstError = ''; foreach ($redisDouyinWebAppUserAccessToken->getFirstErrors() as $message) { $firstError = $message; break; } $return = ['status' => false, 'code' => 215004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '215004'), ['first_error' => $firstError]))]; } elseif (!$redisDouyinWebAppUserAccessToken->hasErrors()) { throw new ServerErrorHttpException('Failed to insert/update the object (Douyin\'s third-party service platform website application user\'s access tokenn (Redis)) for unknown reason.'); } return $return; }
4、打印一下 Redis 的模型对象(更新前),$redisDouyinWebAppUserAccessToken。结果如下:
frontend\models\redis\DouyinWebAppUserAccessToken Object ( [_attributes:yii\db\BaseActiveRecord:private] => Array ( [douyin_web_app_id] => 1 [channel_app_source_id] => 46 [channel_app_source_uuid] => 24a4a41a225a11eb8b4a54ee75d2ebc1 [douyin_web_app_user_id] => 7 [douyin_web_app_user_uuid] => 24a5cc46225a11ebb31154ee75d2ebc1 [grant_type] => authorization_code [access_token] => act.fbb67f15948da5ab6d832b69daa58b7e3HEgNAeS3vv7hOBVqjNpjiJ1fZYY [expires_in] => 1296000 [expires_at] => 1606201312 [refresh_token] => rft.1b82e0d7acbea76d68f45b44461e649fQzF6hSBGPuq9w7FVWv0cJ53J4aSg [refresh_token_expires_in] => 2592000 [refresh_token_expires_at] => 1607490412 [scope] => fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list [open_id] => 3c61649c-2cd5-4479-b2a3-0c89e8f808f8 [status] => 1 ) )
5、打印一下 Redis 的模型对象(更新后),$redisDouyinWebAppUserAccessToken。
/** * 基于 Access Token 插入/更新数据至抖音的第三方服务平台网站应用的用户的访问令牌(Redis) * @param array $data 数据 * 格式如下: * * [ * 'douyinWebAppId' => 1, // 抖音的第三方服务平台网站应用ID * 'channelAppSourceId' => 42, // 渠道的应用的来源ID * 'channelAppSourceUuid' => '4dd4924a223d11ebb87054ee75d2ebc1', // 渠道的应用的来源ID(UUID) * 'douyinWebAppUserId' => 4, // 抖音的第三方服务平台网站应用的用户ID * 'douyinWebAppUserUuid' => '4dd5a770223d11eb988c54ee75d2ebc1', // 抖音的第三方服务平台网站应用的用户ID(UUID) * 'grantType' => 'authorization_code', // 授权类型,authorization_code:第三方平台授权模式 * 'access_token' => 'act.fbb67f15948da5ab6d832b69daa58b7e3HEgNAeS3vv7hOBVqjNpjiJ1fZYY', // 访问令牌 * 'captcha' => '', // * 'desc_url' => '', // * 'description' => '', // 错误码描述 * 'error_code' => 0, // 错误码 * 'expires_in' => 1296000, // 访问令牌有效期,单位(秒) * 'open_id' => '3c61649c-2cd5-4479-b2a3-0c89e8f808f8', // 授权第三方用户的用户唯一标识 * 'refresh_expires_in' => 2592000, // 刷新令牌有效期,单位(秒) * 'refresh_token' => 'rft.1b82e0d7acbea76d68f45b44461e649fQzF6hSBGPuq9w7FVWv0cJ53J4aSg', // 刷新令牌 * 'scope' => 'fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list', // 权限范围 * ] * * @return array * 格式如下: * * [ * 'status' => true, // 成功 * 'data' => [ // array * 'channel_app_source_uuid' => 'ddb8822607ec11e9beda54ee75d2ebc1', // 渠道的应用的来源ID(UUID) * ] * ] * * [ * 'status' => false, // 失败 * 'code' => 214010, // 返回码 * 'message' => '', // 说明 * ] * * @throws ServerErrorHttpException */ public function saveModel($data) { /* 基于抖音的第三方服务平台网站应用的用户ID查找状态为启用的单个资源 */ $redisDouyinWebAppUserAccessToken = RedisDouyinWebAppUserAccessToken::find()->where(['douyin_web_app_user_id' => $data['douyinWebAppUserId']])->isDeletedNo()->enabled()->one(); if (!isset($redisDouyinWebAppUserAccessToken)) { $redisDouyinWebAppUserAccessToken = new RedisDouyinWebAppUserAccessToken(); } $time = time(); // 设置访问令牌的有效截止时间 $accessTokenExpireAt = $time + $data['expires_in'] - Yii::$app->params['accessToken']['timeOut']; // 设置刷新令牌的有效截止时间 $refreshTokenExpireAt = $time + $data['refresh_expires_in'] - Yii::$app->params['refreshToken']['timeOut']; $redisDouyinWebAppUserAccessToken->attributes = [ 'douyin_web_app_id' => $data['douyinWebAppId'], 'channel_app_source_id' => $data['channelAppSourceId'], 'channel_app_source_uuid' => $data['channelAppSourceUuid'], 'douyin_web_app_user_id' => $data['douyinWebAppUserId'], 'douyin_web_app_user_uuid' => $data['douyinWebAppUserUuid'], 'grant_type' => $data['grantType'], 'access_token' => $data['access_token'], 'expires_in' => $data['expires_in'], 'expires_at' => $accessTokenExpireAt, 'refresh_token' => $data['refresh_token'], 'refresh_token_expires_in' => $data['refresh_expires_in'], 'refresh_token_expires_at' => $refreshTokenExpireAt, 'scope' => $data['scope'], 'open_id' => $data['open_id'], 'status' => RedisDouyinWebAppUserAccessToken::STATUS_ENABLED, ]; $return = []; if ($redisDouyinWebAppUserAccessToken->save()) { $return = ['status' => true, 'data' => ['channel_app_source_uuid' => $data['channelAppSourceUuid']]]; } elseif ($redisDouyinWebAppUserAccessToken->hasErrors()) { $firstError = ''; foreach ($redisDouyinWebAppUserAccessToken->getFirstErrors() as $message) { $firstError = $message; break; } $return = ['status' => false, 'code' => 215004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '215004'), ['first_error' => $firstError]))]; } elseif (!$redisDouyinWebAppUserAccessToken->hasErrors()) { throw new ServerErrorHttpException('Failed to insert/update the object (Douyin\'s third-party service platform website application user\'s access tokenn (Redis)) for unknown reason.'); } print_r($redisDouyinWebAppUserAccessToken); exit; return $return; }
6、打印一下 Redis 的模型对象(更新后),$redisDouyinWebAppUserAccessToken。符合预期。字段顺序与定义一致。结果如下:
frontend\models\redis\DouyinWebAppUserAccessToken Object ( [_attributes:yii\db\BaseActiveRecord:private] => Array ( [douyin_web_app_id] => 1 [channel_app_source_id] => 48 [channel_app_source_uuid] => 73d70b7e225d11eb932654ee75d2ebc1 [douyin_web_app_user_id] => 8 [douyin_web_app_user_uuid] => 73d82e5a225d11eb932554ee75d2ebc1 [grant_type] => authorization_code [access_token] => act.fbb67f15948da5ab6d832b69daa58b7e3HEgNAeS3vv7hOBVqjNpjiJ1fZYY [expires_in] => 1296000 [expires_at] => 1606202733 [refresh_token] => rft.1b82e0d7acbea76d68f45b44461e649fQzF6hSBGPuq9w7FVWv0cJ53J4aSg [refresh_token_expires_in] => 2592000 [refresh_token_expires_at] => 1607491833 [scope] => fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list [open_id] => 3c61649c-2cd5-4479-b2a3-0c89e8f808f8 [status] => 1 [is_deleted] => 0 [deleted_at] => 0 [created_at] => 1604907033 [updated_at] => 1604907033 [id] => 2 ) )
7、查看 GUI for Redis 的日志,以确认真正执行的命令。如图2
hscan cpa:ar:douyin_web_app_user_access_token:a:2 0 count 100
8、打开控制台,执行与上面同样的命令。其响应与 GUI 一致。如图3
本地环境:10>hscan cpa:ar:douyin_web_app_user_access_token:a:2 0 count 100 1) "0" 2) 1) "id" 2) "2" 3) "channel_app_source_uuid" 4) "73d70b7e225d11eb932654ee75d2ebc1" 5) "refresh_token" 6) "rft.1b82e0d7acbea76d68f45b44461e649fQzF6hSBGPuq9w7FVWv0cJ53J4aSg" 7) "open_id" 8) "3c61649c-2cd5-4479-b2a3-0c89e8f808f8" 9) "created_at" 10) "1604907033" 11) "is_deleted" 12) "0" 13) "status" 14) "1" 15) "refresh_token_expires_at" 16) "1607491833" 17) "channel_app_source_id" 18) "48" 19) "expires_in" 20) "1296000" 21) "refresh_token_expires_in" 22) "2592000" 23) "deleted_at" 24) "0" 25) "douyin_web_app_id" 26) "1" 27) "access_token" 28) "act.fbb67f15948da5ab6d832b69daa58b7e3HEgNAeS3vv7hOBVqjNpjiJ1fZYY" 29) "grant_type" 30) "authorization_code" 31) "douyin_web_app_user_uuid" 32) "73d82e5a225d11eb932554ee75d2ebc1" 33) "expires_at" 34) "1606202733" 35) "updated_at" 36) "1604907033" 37) "douyin_web_app_user_id" 38) "8" 39) "scope" 40) "fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list" 本地环境:10>
9、在 Yii 2.0 中,Redis 的活动记录(Active Record),基于 row 查看结构,与模型字段顺序一致(在另一个类似结构的模型中)。如图4
10、# 配置域字段最大个数限制,hash-max-zipmap-entries 512。# 配置字段值最大字节限制,hash-max-zipmap-value 64。当满足以上两个条件时,哈希表 key 会被压缩,否则将按照正常的哈希结构来存储。由于字段:scope 的值:fans.data,fans.list,following.list,im,user_info,video.comment,video.create,video.data,video.delete,video.list 已经超出最大字节限制。决定修改字段:scope 的值为空字符串。基于 row 查看结构,与模型字段顺序一致。最终符合预期。如图5
近期评论