diff --git a/backend/deploy/envs/user.env b/backend/deploy/envs/user.env index 5416ff1..844a8be 100644 --- a/backend/deploy/envs/user.env +++ b/backend/deploy/envs/user.env @@ -3,3 +3,23 @@ # 服务端口 PORT=20000 + +# ==================== 数据库配置 ==================== +DB_HOST=localhost +DB_PORT=15432 +DB_USER=postgres +DB_PASSWORD=123456 +DB_NAME=top-fans + +# ==================== Redis 配置 ==================== +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=0 + +# ==================== 短信配置 ==================== +# 与 asset.env 中的阿里云 AccessKey 相同(同一账号) +SMS_ACCESS_KEY_ID=LTAI5t6QcdJHpYbCPxM8SXYE +SMS_ACCESS_KEY_SECRET=ybvjSEb7wilMt3qT5nOppYPoNVayCD +SMS_SIGN_NAME=TopFans +SMS_TEMPLATE_CODE=SMS_314621237 +SMS_REGION=cn-hangzhou diff --git a/backend/dev.sh b/backend/dev.sh index 3a2ac4c..1d04dd4 100755 --- a/backend/dev.sh +++ b/backend/dev.sh @@ -86,23 +86,32 @@ DB_PORT="${DB_PORT:-5432}" DB_USER="${DB_USER:-haihuizhu}" DB_PASSWORD="${DB_PASSWORD:-admin}" DB_NAME="${DB_NAME:-top-fans}" +REDIS_HOST="${REDIS_HOST:-localhost}" +REDIS_PORT="${REDIS_PORT:-6379}" +REDIS_DB="${REDIS_DB:-0}" DB_ARGS=(-db-host="$DB_HOST" -db-port="$DB_PORT" -db-user="$DB_USER" -db-password="$DB_PASSWORD" -db-name="$DB_NAME") +REDIS_ARGS=(-redis-host="$REDIS_HOST" -redis-port="$REDIS_PORT" -redis-db="$REDIS_DB") # 启动一个服务 -# 用法: start_service name binary port use_db +# 用法: start_service name binary port use_db use_redis start_service() { local name=$1 local binary=$2 local port=$3 local use_db=$4 + local use_redis=$5 echo -e "${GREEN}🚀 启动 $name...${NC}" + local args=("-port=$port") if [ "$use_db" = "1" ]; then - "$SCRIPT_DIR/$binary" -port=$port "${DB_ARGS[@]}" > "/tmp/${name}.log" 2>&1 & - else - "$SCRIPT_DIR/$binary" -port=$port > "/tmp/${name}.log" 2>&1 & + args+=("${DB_ARGS[@]}") fi + if [ "$use_redis" = "1" ]; then + args+=("${REDIS_ARGS[@]}") + fi + + "$SCRIPT_DIR/$binary" "${args[@]}" > "/tmp/${name}.log" 2>&1 & local pid=$! # 保存 PID 到文件 echo $pid > "/tmp/dev_sh_${name}.pid" @@ -140,13 +149,14 @@ build_service() { } # 重建并重启单个服务(构建成功后才杀旧进程) -# 用法: restart_service name dir binary port use_db +# 用法: restart_service name dir binary port use_db use_redis restart_service() { local name=$1 local dir=$2 local binary=$3 local port=$4 local use_db=$5 + local use_redis=$6 local pid_file="/tmp/dev_sh_${name}.pid" local lock_file="/tmp/dev_sh_${name}.lock" @@ -199,11 +209,14 @@ restart_service() { # Step 3: 启动新进程 sleep 1 cd "$SCRIPT_DIR" + local args=("-port=$port") if [ "$use_db" = "1" ]; then - "$SCRIPT_DIR/$binary" -port=$port "${DB_ARGS[@]}" > "/tmp/${name}.log" 2>&1 & - else - "$SCRIPT_DIR/$binary" -port=$port > "/tmp/${name}.log" 2>&1 & + args+=("${DB_ARGS[@]}") fi + if [ "$use_redis" = "1" ]; then + args+=("${REDIS_ARGS[@]}") + fi + "$SCRIPT_DIR/$binary" "${args[@]}" > "/tmp/${name}.log" 2>&1 & local new_pid=$! echo $new_pid > "$pid_file" sleep 2 @@ -219,7 +232,7 @@ restart_service() { } # 启动文件监听器 -# 用法: start_watcher name dir1:dir2:dir3 binary port use_db +# 用法: start_watcher name dir1:dir2:dir3 binary port use_db use_redis # 注意: 多个目录用冒号分隔 start_watcher() { local name=$1 @@ -227,6 +240,7 @@ start_watcher() { local binary=$3 local port=$4 local use_db=$5 + local use_redis=$6 local watch_paths=() local watch_path="" local restart_marker="/tmp/dev_sh_${name}_restart" @@ -322,10 +336,10 @@ start_watcher() { build_proto # proto 变化时重启 gateway(gateway 是 proto 客户端的调用方) echo -e "${YELLOW}🔄 [Proto 变化] 重启 gateway...${NC}" - restart_service "gateway" "gateway" "gateway/gateway" "8080" "0" + restart_service "gateway" "gateway" "gateway/gateway" "8080" "0" "0" fi - restart_service "$name" "${watch_paths[0]}" "$binary" "$port" "$use_db" + restart_service "$name" "${watch_paths[0]}" "$binary" "$port" "$use_db" "$use_redis" fi done ) & @@ -387,31 +401,31 @@ cd "$SCRIPT_DIR" # 启动所有服务 echo "" echo -e "${YELLOW}🚀 启动所有服务...${NC}" -start_service "userService" "services/userService/userService" 20000 1 -start_service "assetService" "services/assetService/assetService" 20003 1 -start_service "socialService" "services/socialService/socialService" 20002 1 +start_service "userService" "services/userService/userService" 20000 1 1 +start_service "assetService" "services/assetService/assetService" 20003 1 0 +start_service "socialService" "services/socialService/socialService" 20002 1 0 # galleryService 需要连接 taskService (20006),单独处理 echo -e "${GREEN}🚀 启动 galleryService...${NC}" "$SCRIPT_DIR/services/galleryService/galleryService" -port=20004 -task-service-url="tri://localhost:20006" "${DB_ARGS[@]}" > "/tmp/galleryService.log" 2>&1 & echo $! > "/tmp/dev_sh_galleryService.pid" sleep 2 echo -e "${GREEN}✅ galleryService 已启动 (PID: $(cat /tmp/dev_sh_galleryService.pid), 端口: 20004)${NC}" -start_service "activityService" "services/activityService/activityService" 20005 1 -start_service "taskService" "services/taskService/taskService" 20006 1 -start_service "starbookService" "services/starbookService/starbookService" 20007 1 -start_service "gateway" "gateway/gateway" 8080 0 +start_service "activityService" "services/activityService/activityService" 20005 1 0 +start_service "taskService" "services/taskService/taskService" 20006 1 0 +start_service "starbookService" "services/starbookService/starbookService" 20007 1 0 +start_service "gateway" "gateway/gateway" 8080 0 0 # 启动所有文件监听器 echo "" echo -e "${YELLOW}👁️ 启动所有文件监听器...${NC}" -start_watcher "gateway" "gateway:pkg/proto" "gateway/gateway" 8080 0 -start_watcher "userService" "services/userService" "services/userService/userService" 20000 1 -start_watcher "assetService" "services/assetService:pkg/proto/asset" "services/assetService/assetService" 20003 1 -start_watcher "socialService" "services/socialService" "services/socialService/socialService" 20002 1 -start_watcher "galleryService" "services/galleryService" "services/galleryService/galleryService" 20004 1 -start_watcher "activityService" "services/activityService" "services/activityService/activityService" 20005 1 -start_watcher "taskService" "services/taskService" "services/taskService/taskService" 20006 1 -start_watcher "starbookService" "services/starbookService" "services/starbookService/starbookService" 20007 1 +start_watcher "gateway" "gateway:pkg/proto" "gateway/gateway" 8080 0 0 +start_watcher "userService" "services/userService" "services/userService/userService" 20000 1 1 +start_watcher "assetService" "services/assetService:pkg/proto/asset" "services/assetService/assetService" 20003 1 0 +start_watcher "socialService" "services/socialService" "services/socialService/socialService" 20002 1 0 +start_watcher "galleryService" "services/galleryService" "services/galleryService/galleryService" 20004 1 0 +start_watcher "activityService" "services/activityService" "services/activityService/activityService" 20005 1 0 +start_watcher "taskService" "services/taskService" "services/taskService/taskService" 20006 1 0 +start_watcher "starbookService" "services/starbookService" "services/starbookService/starbookService" 20007 1 0 echo "" echo -e "${GREEN}========================================${NC}" diff --git a/backend/gateway/controller/auth_controller.go b/backend/gateway/controller/auth_controller.go index 67bdae2..2a5c839 100644 --- a/backend/gateway/controller/auth_controller.go +++ b/backend/gateway/controller/auth_controller.go @@ -363,6 +363,107 @@ func (ctrl *AuthController) CheckNickname(c *gin.Context) { }) } +// SendCode 发送验证码 +// @Summary 发送验证码 +// @Description 发送手机验证码,用于注册或重置密码 +// @Tags auth +// @Accept json +// @Produce json +// @Param request body dto.SendCodeRequest true "发送验证码请求" +// @Success 200 {object} response.Response{data=dto.SendCodeResponse} +// @Router /api/v1/auth/send-code [post] +func (ctrl *AuthController) SendCode(c *gin.Context) { + var req dto.SendCodeRequest + if err := c.ShouldBindJSON(&req); err != nil { + logger.Logger.Warn("Invalid send code request", zap.Error(err)) + response.BadRequest(c, "参数错误") + return + } + + logger.Logger.Info("SendCode request received", + zap.String("mobile", req.Mobile), + zap.String("scene", req.Scene), + ) + + // 调用 Dubbo 服务 + ctx := context.Background() + resp, err := ctrl.userServiceClient.SendCode(ctx, &pb.SendCodeRequest{ + Mobile: req.Mobile, + Scene: req.Scene, + }) + if err != nil { + logger.Logger.Error("SendCode failed", zap.Error(err)) + response.HandleError(c, err) + return + } + + // 检查业务错误 + if resp.Base != nil && resp.Base.Code != pbCommon.StatusCode_STATUS_OK { + response.HandleError(c, &pbError{message: resp.Base.Message}) + return + } + + logger.Logger.Info("SendCode successful", + zap.String("mobile", req.Mobile), + ) + + response.Success(c, gin.H{ + "expires_in": resp.ExpiresIn, + }) +} + +// VerifyCode 验证验证码 +// @Summary 验证验证码 +// @Description 验证手机验证码,验证成功后返回 verify_token +// @Tags auth +// @Accept json +// @Produce json +// @Param request body dto.VerifyCodeRequest true "验证验证码请求" +// @Success 200 {object} response.Response{data=dto.VerifyCodeResponse} +// @Router /api/v1/auth/verify-code [post] +func (ctrl *AuthController) VerifyCode(c *gin.Context) { + var req dto.VerifyCodeRequest + if err := c.ShouldBindJSON(&req); err != nil { + logger.Logger.Warn("Invalid verify code request", zap.Error(err)) + response.BadRequest(c, "参数错误") + return + } + + logger.Logger.Info("VerifyCode request received", + zap.String("mobile", req.Mobile), + zap.String("scene", req.Scene), + ) + + // 调用 Dubbo 服务 + ctx := context.Background() + resp, err := ctrl.userServiceClient.VerifyCode(ctx, &pb.VerifyCodeRequest{ + Mobile: req.Mobile, + Code: req.Code, + Scene: req.Scene, + }) + if err != nil { + logger.Logger.Error("VerifyCode failed", zap.Error(err)) + response.HandleError(c, err) + return + } + + // 检查业务错误 + if resp.Base != nil && resp.Base.Code != pbCommon.StatusCode_STATUS_OK { + response.HandleError(c, &pbError{message: resp.Base.Message}) + return + } + + logger.Logger.Info("VerifyCode successful", + zap.String("mobile", req.Mobile), + ) + + response.Success(c, gin.H{ + "verified": resp.Verified, + "verify_token": resp.VerifyToken, + "expires_in": resp.ExpiresIn, + }) +} + // CheckMobile 检查手机号是否已被注册 // @Summary 检查手机号是否被注册 // @Description 检查指定手机号是否已被他人使用 diff --git a/backend/gateway/dto/auth_sms_dto.go b/backend/gateway/dto/auth_sms_dto.go new file mode 100644 index 0000000..cfc722a --- /dev/null +++ b/backend/gateway/dto/auth_sms_dto.go @@ -0,0 +1,30 @@ +package dto + +// SendCodeRequest 发送验证码请求 +type SendCodeRequest struct { + Mobile string `json:"mobile" binding:"required"` + Scene string `json:"scene" binding:"required"` // register, password +} + +// SendCodeResponse 发送验证码响应 +type SendCodeResponse struct { + Code int `json:"code"` + Message string `json:"message"` + ExpiresIn int `json:"expires_in"` // 多少秒后可以重发 +} + +// VerifyCodeRequest 验证验证码请求 +type VerifyCodeRequest struct { + Mobile string `json:"mobile" binding:"required"` + Code string `json:"code" binding:"required"` + Scene string `json:"scene" binding:"required"` +} + +// VerifyCodeResponse 验证验证码响应 +type VerifyCodeResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Verified bool `json:"verified"` + VerifyToken string `json:"verify_token"` + ExpiresIn int `json:"expires_in"` // token有效期(秒) +} \ No newline at end of file diff --git a/backend/gateway/router/router.go b/backend/gateway/router/router.go index 48a4bf1..584ef4a 100644 --- a/backend/gateway/router/router.go +++ b/backend/gateway/router/router.go @@ -87,6 +87,8 @@ func SetupRouter(userClient *client.Client, socialClient *client.Client, assetCl auth.POST("/validate", authCtrl.ValidateToken) // 验证 Token auth.POST("/check-nickname", authCtrl.CheckNickname) // 检查昵称是否被注册 auth.POST("/check-mobile", authCtrl.CheckMobile) // 检查手机号是否被注册 + auth.POST("/send-code", authCtrl.SendCode) // 发送验证码 + auth.POST("/verify-code", authCtrl.VerifyCode) // 验证验证码 } // 认证相关路由(需要认证) diff --git a/backend/go.work.sum b/backend/go.work.sum index f897d0d..b11b254 100644 --- a/backend/go.work.sum +++ b/backend/go.work.sum @@ -26,6 +26,39 @@ github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HR github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= +github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= +github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 h1:aDPyz6C+nenypx24N5qEt09NjpS6mu7Cu1A+wf9UTaY= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8/go.mod h1:e/vWJ5gLVnraPROSh+3oMSodf5ukaUlqNgH0IIcnz98= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA= +github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94= +github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-utils/v2 v2.0.8/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= +github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= +github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= @@ -60,6 +93,9 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/chzyer/logex v1.2.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM= github.com/chzyer/readline v1.5.0 h1:lSwwFrbNviGePhkewF1az4oLmcwqCZijQ2/Wi3BGHAI= github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= @@ -138,11 +174,13 @@ github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0s github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 h1:7qnwS9+oeSiOIsiUMajT+0R7HR6hw5NegnKPmn/94oI= github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFRbV5juy/C3MGdj4ePi+g6ePIp4= github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= @@ -257,6 +295,7 @@ github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/performancecopilot/speed v3.0.0+incompatible h1:2WnRzIquHa5QxaJKShDkLM+sc0JPuwhXzK8OYOyt3Vg= github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc= github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE= github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -269,6 +308,7 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rhnvrm/simples3 v0.6.1 h1:H0DJwybR6ryQE+Odi9eqkHuzjYAeJgtGcGtuBwOhsH8= github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUWQxHW5TkBe7YUl+2s= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -276,13 +316,18 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da h1:p3Vo3i64TCL github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a h1:AhmOdSHeswKHBjhsLs/7+1voOxT+LLrSk/Nxvk35fug= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk= github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= @@ -290,21 +335,77 @@ github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b h1:Qh4dB5D/WpoUUp3lSod7qgoyEHbDGPUWjIbnqdqqe1k= @@ -316,6 +417,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/backend/pkg/proto/user/user.pb.go b/backend/pkg/proto/user/user.pb.go index ec0c859..6711966 100644 --- a/backend/pkg/proto/user/user.pb.go +++ b/backend/pkg/proto/user/user.pb.go @@ -385,10 +385,11 @@ func (x *Star) GetTag() string { // 注册请求 type RegisterRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` // 手机号 - Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` // 密码 - StarId int64 `protobuf:"varint,3,opt,name=star_id,json=starId,proto3" json:"star_id,omitempty"` // 选择第一个粉丝身份的明星ID - Nickname string `protobuf:"bytes,4,opt,name=nickname,proto3" json:"nickname,omitempty"` // 第一个粉丝身份的昵称 + Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` // 手机号 + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` // 密码 + StarId int64 `protobuf:"varint,3,opt,name=star_id,json=starId,proto3" json:"star_id,omitempty"` // 选择第一个粉丝身份的明星ID + Nickname string `protobuf:"bytes,4,opt,name=nickname,proto3" json:"nickname,omitempty"` // 第一个粉丝身份的昵称 + VerifyToken string `protobuf:"bytes,5,opt,name=verify_token,json=verifyToken,proto3" json:"verify_token,omitempty"` // 短信验证token(注册前必须先通过短信验证) unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -451,6 +452,13 @@ func (x *RegisterRequest) GetNickname() string { return "" } +func (x *RegisterRequest) GetVerifyToken() string { + if x != nil { + return x.VerifyToken + } + return "" +} + // 注册响应 type RegisterResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1172,6 +1180,242 @@ func (x *CheckMobileResponse) GetExists() bool { return false } +// 发送验证码请求 +type SendCodeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` // 手机号 + Scene string `protobuf:"bytes,2,opt,name=scene,proto3" json:"scene,omitempty"` // 场景:register, password + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendCodeRequest) Reset() { + *x = SendCodeRequest{} + mi := &file_user_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendCodeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendCodeRequest) ProtoMessage() {} + +func (x *SendCodeRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendCodeRequest.ProtoReflect.Descriptor instead. +func (*SendCodeRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{17} +} + +func (x *SendCodeRequest) GetMobile() string { + if x != nil { + return x.Mobile + } + return "" +} + +func (x *SendCodeRequest) GetScene() string { + if x != nil { + return x.Scene + } + return "" +} + +// 发送验证码响应 +type SendCodeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Base *common.BaseResponse `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"` + ExpiresIn int32 `protobuf:"varint,2,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` // 多少秒后可以重发 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendCodeResponse) Reset() { + *x = SendCodeResponse{} + mi := &file_user_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendCodeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendCodeResponse) ProtoMessage() {} + +func (x *SendCodeResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendCodeResponse.ProtoReflect.Descriptor instead. +func (*SendCodeResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{18} +} + +func (x *SendCodeResponse) GetBase() *common.BaseResponse { + if x != nil { + return x.Base + } + return nil +} + +func (x *SendCodeResponse) GetExpiresIn() int32 { + if x != nil { + return x.ExpiresIn + } + return 0 +} + +// 验证验证码请求 +type VerifyCodeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"` // 手机号 + Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` // 验证码 + Scene string `protobuf:"bytes,3,opt,name=scene,proto3" json:"scene,omitempty"` // 场景:register, password + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VerifyCodeRequest) Reset() { + *x = VerifyCodeRequest{} + mi := &file_user_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VerifyCodeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyCodeRequest) ProtoMessage() {} + +func (x *VerifyCodeRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VerifyCodeRequest.ProtoReflect.Descriptor instead. +func (*VerifyCodeRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{19} +} + +func (x *VerifyCodeRequest) GetMobile() string { + if x != nil { + return x.Mobile + } + return "" +} + +func (x *VerifyCodeRequest) GetCode() string { + if x != nil { + return x.Code + } + return "" +} + +func (x *VerifyCodeRequest) GetScene() string { + if x != nil { + return x.Scene + } + return "" +} + +// 验证验证码响应 +type VerifyCodeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Base *common.BaseResponse `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"` + Verified bool `protobuf:"varint,2,opt,name=verified,proto3" json:"verified,omitempty"` // 是否验证成功 + VerifyToken string `protobuf:"bytes,3,opt,name=verify_token,json=verifyToken,proto3" json:"verify_token,omitempty"` // 验证成功后返回的token + ExpiresIn int32 `protobuf:"varint,4,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` // token有效期(秒) + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VerifyCodeResponse) Reset() { + *x = VerifyCodeResponse{} + mi := &file_user_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VerifyCodeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyCodeResponse) ProtoMessage() {} + +func (x *VerifyCodeResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VerifyCodeResponse.ProtoReflect.Descriptor instead. +func (*VerifyCodeResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{20} +} + +func (x *VerifyCodeResponse) GetBase() *common.BaseResponse { + if x != nil { + return x.Base + } + return nil +} + +func (x *VerifyCodeResponse) GetVerified() bool { + if x != nil { + return x.Verified + } + return false +} + +func (x *VerifyCodeResponse) GetVerifyToken() string { + if x != nil { + return x.VerifyToken + } + return "" +} + +func (x *VerifyCodeResponse) GetExpiresIn() int32 { + if x != nil { + return x.ExpiresIn + } + return 0 +} + // 获取用户信息请求 type GetUserRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1182,7 +1426,7 @@ type GetUserRequest struct { func (x *GetUserRequest) Reset() { *x = GetUserRequest{} - mi := &file_user_proto_msgTypes[17] + mi := &file_user_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1194,7 +1438,7 @@ func (x *GetUserRequest) String() string { func (*GetUserRequest) ProtoMessage() {} func (x *GetUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[17] + mi := &file_user_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1207,7 +1451,7 @@ func (x *GetUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. func (*GetUserRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{17} + return file_user_proto_rawDescGZIP(), []int{21} } func (x *GetUserRequest) GetUserId() int64 { @@ -1228,7 +1472,7 @@ type GetUserResponse struct { func (x *GetUserResponse) Reset() { *x = GetUserResponse{} - mi := &file_user_proto_msgTypes[18] + mi := &file_user_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1240,7 +1484,7 @@ func (x *GetUserResponse) String() string { func (*GetUserResponse) ProtoMessage() {} func (x *GetUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[18] + mi := &file_user_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1253,7 +1497,7 @@ func (x *GetUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserResponse.ProtoReflect.Descriptor instead. func (*GetUserResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{18} + return file_user_proto_rawDescGZIP(), []int{22} } func (x *GetUserResponse) GetBase() *common.BaseResponse { @@ -1281,7 +1525,7 @@ type GetFanProfileRequest struct { func (x *GetFanProfileRequest) Reset() { *x = GetFanProfileRequest{} - mi := &file_user_proto_msgTypes[19] + mi := &file_user_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1293,7 +1537,7 @@ func (x *GetFanProfileRequest) String() string { func (*GetFanProfileRequest) ProtoMessage() {} func (x *GetFanProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[19] + mi := &file_user_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1306,7 +1550,7 @@ func (x *GetFanProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFanProfileRequest.ProtoReflect.Descriptor instead. func (*GetFanProfileRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{19} + return file_user_proto_rawDescGZIP(), []int{23} } func (x *GetFanProfileRequest) GetUserId() int64 { @@ -1334,7 +1578,7 @@ type GetFanProfileResponse struct { func (x *GetFanProfileResponse) Reset() { *x = GetFanProfileResponse{} - mi := &file_user_proto_msgTypes[20] + mi := &file_user_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1346,7 +1590,7 @@ func (x *GetFanProfileResponse) String() string { func (*GetFanProfileResponse) ProtoMessage() {} func (x *GetFanProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[20] + mi := &file_user_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1359,7 +1603,7 @@ func (x *GetFanProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFanProfileResponse.ProtoReflect.Descriptor instead. func (*GetFanProfileResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{20} + return file_user_proto_rawDescGZIP(), []int{24} } func (x *GetFanProfileResponse) GetBase() *common.BaseResponse { @@ -1388,7 +1632,7 @@ type UpdateFanProfileSocialRequest struct { func (x *UpdateFanProfileSocialRequest) Reset() { *x = UpdateFanProfileSocialRequest{} - mi := &file_user_proto_msgTypes[21] + mi := &file_user_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1400,7 +1644,7 @@ func (x *UpdateFanProfileSocialRequest) String() string { func (*UpdateFanProfileSocialRequest) ProtoMessage() {} func (x *UpdateFanProfileSocialRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[21] + mi := &file_user_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1413,7 +1657,7 @@ func (x *UpdateFanProfileSocialRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateFanProfileSocialRequest.ProtoReflect.Descriptor instead. func (*UpdateFanProfileSocialRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{21} + return file_user_proto_rawDescGZIP(), []int{25} } func (x *UpdateFanProfileSocialRequest) GetUserId() int64 { @@ -1448,7 +1692,7 @@ type UpdateFanProfileSocialResponse struct { func (x *UpdateFanProfileSocialResponse) Reset() { *x = UpdateFanProfileSocialResponse{} - mi := &file_user_proto_msgTypes[22] + mi := &file_user_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1460,7 +1704,7 @@ func (x *UpdateFanProfileSocialResponse) String() string { func (*UpdateFanProfileSocialResponse) ProtoMessage() {} func (x *UpdateFanProfileSocialResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[22] + mi := &file_user_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1473,7 +1717,7 @@ func (x *UpdateFanProfileSocialResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateFanProfileSocialResponse.ProtoReflect.Descriptor instead. func (*UpdateFanProfileSocialResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{22} + return file_user_proto_rawDescGZIP(), []int{26} } func (x *UpdateFanProfileSocialResponse) GetBase() *common.BaseResponse { @@ -1505,7 +1749,7 @@ type UpdateCrystalBalanceRequest struct { func (x *UpdateCrystalBalanceRequest) Reset() { *x = UpdateCrystalBalanceRequest{} - mi := &file_user_proto_msgTypes[23] + mi := &file_user_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1517,7 +1761,7 @@ func (x *UpdateCrystalBalanceRequest) String() string { func (*UpdateCrystalBalanceRequest) ProtoMessage() {} func (x *UpdateCrystalBalanceRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[23] + mi := &file_user_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1530,7 +1774,7 @@ func (x *UpdateCrystalBalanceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCrystalBalanceRequest.ProtoReflect.Descriptor instead. func (*UpdateCrystalBalanceRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{23} + return file_user_proto_rawDescGZIP(), []int{27} } func (x *UpdateCrystalBalanceRequest) GetUserId() int64 { @@ -1586,7 +1830,7 @@ type UpdateCrystalBalanceResponse struct { func (x *UpdateCrystalBalanceResponse) Reset() { *x = UpdateCrystalBalanceResponse{} - mi := &file_user_proto_msgTypes[24] + mi := &file_user_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1598,7 +1842,7 @@ func (x *UpdateCrystalBalanceResponse) String() string { func (*UpdateCrystalBalanceResponse) ProtoMessage() {} func (x *UpdateCrystalBalanceResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[24] + mi := &file_user_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1611,7 +1855,7 @@ func (x *UpdateCrystalBalanceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateCrystalBalanceResponse.ProtoReflect.Descriptor instead. func (*UpdateCrystalBalanceResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{24} + return file_user_proto_rawDescGZIP(), []int{28} } func (x *UpdateCrystalBalanceResponse) GetBase() *common.BaseResponse { @@ -1640,7 +1884,7 @@ type UpdateAssetsCountRequest struct { func (x *UpdateAssetsCountRequest) Reset() { *x = UpdateAssetsCountRequest{} - mi := &file_user_proto_msgTypes[25] + mi := &file_user_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1652,7 +1896,7 @@ func (x *UpdateAssetsCountRequest) String() string { func (*UpdateAssetsCountRequest) ProtoMessage() {} func (x *UpdateAssetsCountRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[25] + mi := &file_user_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1665,7 +1909,7 @@ func (x *UpdateAssetsCountRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAssetsCountRequest.ProtoReflect.Descriptor instead. func (*UpdateAssetsCountRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{25} + return file_user_proto_rawDescGZIP(), []int{29} } func (x *UpdateAssetsCountRequest) GetUserId() int64 { @@ -1700,7 +1944,7 @@ type UpdateAssetsCountResponse struct { func (x *UpdateAssetsCountResponse) Reset() { *x = UpdateAssetsCountResponse{} - mi := &file_user_proto_msgTypes[26] + mi := &file_user_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1712,7 +1956,7 @@ func (x *UpdateAssetsCountResponse) String() string { func (*UpdateAssetsCountResponse) ProtoMessage() {} func (x *UpdateAssetsCountResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[26] + mi := &file_user_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1725,7 +1969,7 @@ func (x *UpdateAssetsCountResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAssetsCountResponse.ProtoReflect.Descriptor instead. func (*UpdateAssetsCountResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{26} + return file_user_proto_rawDescGZIP(), []int{30} } func (x *UpdateAssetsCountResponse) GetBase() *common.BaseResponse { @@ -1755,7 +1999,7 @@ type AddExhibitionHoursRequest struct { func (x *AddExhibitionHoursRequest) Reset() { *x = AddExhibitionHoursRequest{} - mi := &file_user_proto_msgTypes[27] + mi := &file_user_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1767,7 +2011,7 @@ func (x *AddExhibitionHoursRequest) String() string { func (*AddExhibitionHoursRequest) ProtoMessage() {} func (x *AddExhibitionHoursRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[27] + mi := &file_user_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1780,7 +2024,7 @@ func (x *AddExhibitionHoursRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddExhibitionHoursRequest.ProtoReflect.Descriptor instead. func (*AddExhibitionHoursRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{27} + return file_user_proto_rawDescGZIP(), []int{31} } func (x *AddExhibitionHoursRequest) GetUserId() int64 { @@ -1824,7 +2068,7 @@ type AddExhibitionHoursResponse struct { func (x *AddExhibitionHoursResponse) Reset() { *x = AddExhibitionHoursResponse{} - mi := &file_user_proto_msgTypes[28] + mi := &file_user_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1836,7 +2080,7 @@ func (x *AddExhibitionHoursResponse) String() string { func (*AddExhibitionHoursResponse) ProtoMessage() {} func (x *AddExhibitionHoursResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[28] + mi := &file_user_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1849,7 +2093,7 @@ func (x *AddExhibitionHoursResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddExhibitionHoursResponse.ProtoReflect.Descriptor instead. func (*AddExhibitionHoursResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{28} + return file_user_proto_rawDescGZIP(), []int{32} } func (x *AddExhibitionHoursResponse) GetBase() *common.BaseResponse { @@ -1889,7 +2133,7 @@ type GetCurrentUserRequest struct { func (x *GetCurrentUserRequest) Reset() { *x = GetCurrentUserRequest{} - mi := &file_user_proto_msgTypes[29] + mi := &file_user_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1901,7 +2145,7 @@ func (x *GetCurrentUserRequest) String() string { func (*GetCurrentUserRequest) ProtoMessage() {} func (x *GetCurrentUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[29] + mi := &file_user_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1914,7 +2158,7 @@ func (x *GetCurrentUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentUserRequest.ProtoReflect.Descriptor instead. func (*GetCurrentUserRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{29} + return file_user_proto_rawDescGZIP(), []int{33} } // 获取当前登录用户信息响应 @@ -1930,7 +2174,7 @@ type GetCurrentUserResponse struct { func (x *GetCurrentUserResponse) Reset() { *x = GetCurrentUserResponse{} - mi := &file_user_proto_msgTypes[30] + mi := &file_user_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1942,7 +2186,7 @@ func (x *GetCurrentUserResponse) String() string { func (*GetCurrentUserResponse) ProtoMessage() {} func (x *GetCurrentUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[30] + mi := &file_user_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1955,7 +2199,7 @@ func (x *GetCurrentUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetCurrentUserResponse.ProtoReflect.Descriptor instead. func (*GetCurrentUserResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{30} + return file_user_proto_rawDescGZIP(), []int{34} } func (x *GetCurrentUserResponse) GetBase() *common.BaseResponse { @@ -1995,7 +2239,7 @@ type GetMyProfileRequest struct { func (x *GetMyProfileRequest) Reset() { *x = GetMyProfileRequest{} - mi := &file_user_proto_msgTypes[31] + mi := &file_user_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2007,7 +2251,7 @@ func (x *GetMyProfileRequest) String() string { func (*GetMyProfileRequest) ProtoMessage() {} func (x *GetMyProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[31] + mi := &file_user_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2020,7 +2264,7 @@ func (x *GetMyProfileRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyProfileRequest.ProtoReflect.Descriptor instead. func (*GetMyProfileRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{31} + return file_user_proto_rawDescGZIP(), []int{35} } // 获取个人信息页响应 @@ -2036,7 +2280,7 @@ type GetMyProfileResponse struct { func (x *GetMyProfileResponse) Reset() { *x = GetMyProfileResponse{} - mi := &file_user_proto_msgTypes[32] + mi := &file_user_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2048,7 +2292,7 @@ func (x *GetMyProfileResponse) String() string { func (*GetMyProfileResponse) ProtoMessage() {} func (x *GetMyProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[32] + mi := &file_user_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2061,7 +2305,7 @@ func (x *GetMyProfileResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyProfileResponse.ProtoReflect.Descriptor instead. func (*GetMyProfileResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{32} + return file_user_proto_rawDescGZIP(), []int{36} } func (x *GetMyProfileResponse) GetBase() *common.BaseResponse { @@ -2102,7 +2346,7 @@ type UpdateNicknameRequest struct { func (x *UpdateNicknameRequest) Reset() { *x = UpdateNicknameRequest{} - mi := &file_user_proto_msgTypes[33] + mi := &file_user_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2114,7 +2358,7 @@ func (x *UpdateNicknameRequest) String() string { func (*UpdateNicknameRequest) ProtoMessage() {} func (x *UpdateNicknameRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[33] + mi := &file_user_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2127,7 +2371,7 @@ func (x *UpdateNicknameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateNicknameRequest.ProtoReflect.Descriptor instead. func (*UpdateNicknameRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{33} + return file_user_proto_rawDescGZIP(), []int{37} } func (x *UpdateNicknameRequest) GetNickname() string { @@ -2148,7 +2392,7 @@ type UpdateNicknameResponse struct { func (x *UpdateNicknameResponse) Reset() { *x = UpdateNicknameResponse{} - mi := &file_user_proto_msgTypes[34] + mi := &file_user_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2160,7 +2404,7 @@ func (x *UpdateNicknameResponse) String() string { func (*UpdateNicknameResponse) ProtoMessage() {} func (x *UpdateNicknameResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[34] + mi := &file_user_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2173,7 +2417,7 @@ func (x *UpdateNicknameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateNicknameResponse.ProtoReflect.Descriptor instead. func (*UpdateNicknameResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{34} + return file_user_proto_rawDescGZIP(), []int{38} } func (x *UpdateNicknameResponse) GetBase() *common.BaseResponse { @@ -2201,7 +2445,7 @@ type UpdatePasswordRequest struct { func (x *UpdatePasswordRequest) Reset() { *x = UpdatePasswordRequest{} - mi := &file_user_proto_msgTypes[35] + mi := &file_user_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2213,7 +2457,7 @@ func (x *UpdatePasswordRequest) String() string { func (*UpdatePasswordRequest) ProtoMessage() {} func (x *UpdatePasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[35] + mi := &file_user_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2226,7 +2470,7 @@ func (x *UpdatePasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdatePasswordRequest.ProtoReflect.Descriptor instead. func (*UpdatePasswordRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{35} + return file_user_proto_rawDescGZIP(), []int{39} } func (x *UpdatePasswordRequest) GetOldPassword() string { @@ -2253,7 +2497,7 @@ type UpdatePasswordResponse struct { func (x *UpdatePasswordResponse) Reset() { *x = UpdatePasswordResponse{} - mi := &file_user_proto_msgTypes[36] + mi := &file_user_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2265,7 +2509,7 @@ func (x *UpdatePasswordResponse) String() string { func (*UpdatePasswordResponse) ProtoMessage() {} func (x *UpdatePasswordResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[36] + mi := &file_user_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2278,7 +2522,7 @@ func (x *UpdatePasswordResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdatePasswordResponse.ProtoReflect.Descriptor instead. func (*UpdatePasswordResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{36} + return file_user_proto_rawDescGZIP(), []int{40} } func (x *UpdatePasswordResponse) GetBase() *common.BaseResponse { @@ -2298,7 +2542,7 @@ type UpdateAvatarRequest struct { func (x *UpdateAvatarRequest) Reset() { *x = UpdateAvatarRequest{} - mi := &file_user_proto_msgTypes[37] + mi := &file_user_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2310,7 +2554,7 @@ func (x *UpdateAvatarRequest) String() string { func (*UpdateAvatarRequest) ProtoMessage() {} func (x *UpdateAvatarRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[37] + mi := &file_user_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2323,7 +2567,7 @@ func (x *UpdateAvatarRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAvatarRequest.ProtoReflect.Descriptor instead. func (*UpdateAvatarRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{37} + return file_user_proto_rawDescGZIP(), []int{41} } func (x *UpdateAvatarRequest) GetAvatarUrl() string { @@ -2344,7 +2588,7 @@ type UpdateAvatarResponse struct { func (x *UpdateAvatarResponse) Reset() { *x = UpdateAvatarResponse{} - mi := &file_user_proto_msgTypes[38] + mi := &file_user_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2356,7 +2600,7 @@ func (x *UpdateAvatarResponse) String() string { func (*UpdateAvatarResponse) ProtoMessage() {} func (x *UpdateAvatarResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[38] + mi := &file_user_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2369,7 +2613,7 @@ func (x *UpdateAvatarResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateAvatarResponse.ProtoReflect.Descriptor instead. func (*UpdateAvatarResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{38} + return file_user_proto_rawDescGZIP(), []int{42} } func (x *UpdateAvatarResponse) GetBase() *common.BaseResponse { @@ -2396,7 +2640,7 @@ type GetFanIdentitiesRequest struct { func (x *GetFanIdentitiesRequest) Reset() { *x = GetFanIdentitiesRequest{} - mi := &file_user_proto_msgTypes[39] + mi := &file_user_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2408,7 +2652,7 @@ func (x *GetFanIdentitiesRequest) String() string { func (*GetFanIdentitiesRequest) ProtoMessage() {} func (x *GetFanIdentitiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[39] + mi := &file_user_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2421,7 +2665,7 @@ func (x *GetFanIdentitiesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFanIdentitiesRequest.ProtoReflect.Descriptor instead. func (*GetFanIdentitiesRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{39} + return file_user_proto_rawDescGZIP(), []int{43} } func (x *GetFanIdentitiesRequest) GetKeyword() string { @@ -2442,7 +2686,7 @@ type GetFanIdentitiesResponse struct { func (x *GetFanIdentitiesResponse) Reset() { *x = GetFanIdentitiesResponse{} - mi := &file_user_proto_msgTypes[40] + mi := &file_user_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2454,7 +2698,7 @@ func (x *GetFanIdentitiesResponse) String() string { func (*GetFanIdentitiesResponse) ProtoMessage() {} func (x *GetFanIdentitiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[40] + mi := &file_user_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2467,7 +2711,7 @@ func (x *GetFanIdentitiesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFanIdentitiesResponse.ProtoReflect.Descriptor instead. func (*GetFanIdentitiesResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{40} + return file_user_proto_rawDescGZIP(), []int{44} } func (x *GetFanIdentitiesResponse) GetBase() *common.BaseResponse { @@ -2493,7 +2737,7 @@ type GetMyFanIdentitiesRequest struct { func (x *GetMyFanIdentitiesRequest) Reset() { *x = GetMyFanIdentitiesRequest{} - mi := &file_user_proto_msgTypes[41] + mi := &file_user_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2505,7 +2749,7 @@ func (x *GetMyFanIdentitiesRequest) String() string { func (*GetMyFanIdentitiesRequest) ProtoMessage() {} func (x *GetMyFanIdentitiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[41] + mi := &file_user_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2518,7 +2762,7 @@ func (x *GetMyFanIdentitiesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyFanIdentitiesRequest.ProtoReflect.Descriptor instead. func (*GetMyFanIdentitiesRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{41} + return file_user_proto_rawDescGZIP(), []int{45} } // 我的粉丝身份项(包含粉丝档案和明星信息) @@ -2532,7 +2776,7 @@ type MyFanIdentityItem struct { func (x *MyFanIdentityItem) Reset() { *x = MyFanIdentityItem{} - mi := &file_user_proto_msgTypes[42] + mi := &file_user_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2544,7 +2788,7 @@ func (x *MyFanIdentityItem) String() string { func (*MyFanIdentityItem) ProtoMessage() {} func (x *MyFanIdentityItem) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[42] + mi := &file_user_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2557,7 +2801,7 @@ func (x *MyFanIdentityItem) ProtoReflect() protoreflect.Message { // Deprecated: Use MyFanIdentityItem.ProtoReflect.Descriptor instead. func (*MyFanIdentityItem) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{42} + return file_user_proto_rawDescGZIP(), []int{46} } func (x *MyFanIdentityItem) GetFanProfile() *FanProfile { @@ -2586,7 +2830,7 @@ type GetMyFanIdentitiesResponse struct { func (x *GetMyFanIdentitiesResponse) Reset() { *x = GetMyFanIdentitiesResponse{} - mi := &file_user_proto_msgTypes[43] + mi := &file_user_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2598,7 +2842,7 @@ func (x *GetMyFanIdentitiesResponse) String() string { func (*GetMyFanIdentitiesResponse) ProtoMessage() {} func (x *GetMyFanIdentitiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[43] + mi := &file_user_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2611,7 +2855,7 @@ func (x *GetMyFanIdentitiesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyFanIdentitiesResponse.ProtoReflect.Descriptor instead. func (*GetMyFanIdentitiesResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{43} + return file_user_proto_rawDescGZIP(), []int{47} } func (x *GetMyFanIdentitiesResponse) GetBase() *common.BaseResponse { @@ -2646,7 +2890,7 @@ type AddIdentityRequest struct { func (x *AddIdentityRequest) Reset() { *x = AddIdentityRequest{} - mi := &file_user_proto_msgTypes[44] + mi := &file_user_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2658,7 +2902,7 @@ func (x *AddIdentityRequest) String() string { func (*AddIdentityRequest) ProtoMessage() {} func (x *AddIdentityRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[44] + mi := &file_user_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2671,7 +2915,7 @@ func (x *AddIdentityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddIdentityRequest.ProtoReflect.Descriptor instead. func (*AddIdentityRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{44} + return file_user_proto_rawDescGZIP(), []int{48} } func (x *AddIdentityRequest) GetStarId() int64 { @@ -2699,7 +2943,7 @@ type AddIdentityResponse struct { func (x *AddIdentityResponse) Reset() { *x = AddIdentityResponse{} - mi := &file_user_proto_msgTypes[45] + mi := &file_user_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2711,7 +2955,7 @@ func (x *AddIdentityResponse) String() string { func (*AddIdentityResponse) ProtoMessage() {} func (x *AddIdentityResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[45] + mi := &file_user_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2724,7 +2968,7 @@ func (x *AddIdentityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddIdentityResponse.ProtoReflect.Descriptor instead. func (*AddIdentityResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{45} + return file_user_proto_rawDescGZIP(), []int{49} } func (x *AddIdentityResponse) GetBase() *common.BaseResponse { @@ -2751,7 +2995,7 @@ type SwitchIdentityRequest struct { func (x *SwitchIdentityRequest) Reset() { *x = SwitchIdentityRequest{} - mi := &file_user_proto_msgTypes[46] + mi := &file_user_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2763,7 +3007,7 @@ func (x *SwitchIdentityRequest) String() string { func (*SwitchIdentityRequest) ProtoMessage() {} func (x *SwitchIdentityRequest) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[46] + mi := &file_user_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2776,7 +3020,7 @@ func (x *SwitchIdentityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SwitchIdentityRequest.ProtoReflect.Descriptor instead. func (*SwitchIdentityRequest) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{46} + return file_user_proto_rawDescGZIP(), []int{50} } func (x *SwitchIdentityRequest) GetNewStarId() int64 { @@ -2799,7 +3043,7 @@ type SwitchIdentityResponse struct { func (x *SwitchIdentityResponse) Reset() { *x = SwitchIdentityResponse{} - mi := &file_user_proto_msgTypes[47] + mi := &file_user_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2811,7 +3055,7 @@ func (x *SwitchIdentityResponse) String() string { func (*SwitchIdentityResponse) ProtoMessage() {} func (x *SwitchIdentityResponse) ProtoReflect() protoreflect.Message { - mi := &file_user_proto_msgTypes[47] + mi := &file_user_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2824,7 +3068,7 @@ func (x *SwitchIdentityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SwitchIdentityResponse.ProtoReflect.Descriptor instead. func (*SwitchIdentityResponse) Descriptor() ([]byte, []int) { - return file_user_proto_rawDescGZIP(), []int{47} + return file_user_proto_rawDescGZIP(), []int{51} } func (x *SwitchIdentityResponse) GetBase() *common.BaseResponse { @@ -2903,12 +3147,13 @@ const file_user_proto_rawDesc = "" + "\tis_active\x18\a \x01(\bR\bisActive\x12\x1d\n" + "\n" + "created_at\x18\b \x01(\x03R\tcreatedAt\x12\x10\n" + - "\x03tag\x18\t \x01(\tR\x03tag\"z\n" + + "\x03tag\x18\t \x01(\tR\x03tag\"\x9d\x01\n" + "\x0fRegisterRequest\x12\x16\n" + "\x06mobile\x18\x01 \x01(\tR\x06mobile\x12\x1a\n" + "\bpassword\x18\x02 \x01(\tR\bpassword\x12\x17\n" + "\astar_id\x18\x03 \x01(\x03R\x06starId\x12\x1a\n" + - "\bnickname\x18\x04 \x01(\tR\bnickname\"\xe9\x01\n" + + "\bnickname\x18\x04 \x01(\tR\bnickname\x12!\n" + + "\fverify_token\x18\x05 \x01(\tR\vverifyToken\"\xe9\x01\n" + "\x10RegisterResponse\x120\n" + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12!\n" + "\faccess_token\x18\x02 \x01(\tR\vaccessToken\x12\x1d\n" + @@ -2957,7 +3202,24 @@ const file_user_proto_rawDesc = "" + "\x06mobile\x18\x01 \x01(\tR\x06mobile\"_\n" + "\x13CheckMobileResponse\x120\n" + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x16\n" + - "\x06exists\x18\x02 \x01(\bR\x06exists\")\n" + + "\x06exists\x18\x02 \x01(\bR\x06exists\"?\n" + + "\x0fSendCodeRequest\x12\x16\n" + + "\x06mobile\x18\x01 \x01(\tR\x06mobile\x12\x14\n" + + "\x05scene\x18\x02 \x01(\tR\x05scene\"c\n" + + "\x10SendCodeResponse\x120\n" + + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x1d\n" + + "\n" + + "expires_in\x18\x02 \x01(\x05R\texpiresIn\"U\n" + + "\x11VerifyCodeRequest\x12\x16\n" + + "\x06mobile\x18\x01 \x01(\tR\x06mobile\x12\x12\n" + + "\x04code\x18\x02 \x01(\tR\x04code\x12\x14\n" + + "\x05scene\x18\x03 \x01(\tR\x05scene\"\xa4\x01\n" + + "\x12VerifyCodeResponse\x120\n" + + "\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x1a\n" + + "\bverified\x18\x02 \x01(\bR\bverified\x12!\n" + + "\fverify_token\x18\x03 \x01(\tR\vverifyToken\x12\x1d\n" + + "\n" + + "expires_in\x18\x04 \x01(\x05R\texpiresIn\")\n" + "\x0eGetUserRequest\x12\x17\n" + "\auser_id\x18\x01 \x01(\x03R\x06userId\"k\n" + "\x0fGetUserResponse\x120\n" + @@ -3068,7 +3330,7 @@ const file_user_proto_rawDesc = "" + "\n" + "expires_in\x18\x03 \x01(\x03R\texpiresIn\x129\n" + "\vfan_profile\x18\x04 \x01(\v2\x18.topfans.user.FanProfileR\n" + - "fanProfile2\xd9\x14\n" + + "fanProfile2\xbd\x16\n" + "\x11UserSocialService\x12k\n" + "\bRegister\x12\x1d.topfans.user.RegisterRequest\x1a\x1e.topfans.user.RegisterResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/v1/auth/register\x12_\n" + "\x05Login\x12\x1a.topfans.user.LoginRequest\x1a\x1b.topfans.user.LoginResponse\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/api/v1/auth/login\x12v\n" + @@ -3076,7 +3338,10 @@ const file_user_proto_rawDesc = "" + "\rValidateToken\x12\".topfans.user.ValidateTokenRequest\x1a#.topfans.user.ValidateTokenResponse\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/api/v1/auth/validate\x12c\n" + "\x06Logout\x12\x1b.topfans.user.LogoutRequest\x1a\x1c.topfans.user.LogoutResponse\"\x1e\x82\xd3\xe4\x93\x02\x18:\x01*\"\x13/api/v1/auth/logout\x12\x80\x01\n" + "\rCheckNickname\x12\".topfans.user.CheckNicknameRequest\x1a#.topfans.user.CheckNicknameResponse\"&\x82\xd3\xe4\x93\x02 :\x01*\"\x1b/api/v1/auth/check-nickname\x12x\n" + - "\vCheckMobile\x12 .topfans.user.CheckMobileRequest\x1a!.topfans.user.CheckMobileResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/v1/auth/check-mobile\x12g\n" + + "\vCheckMobile\x12 .topfans.user.CheckMobileRequest\x1a!.topfans.user.CheckMobileResponse\"$\x82\xd3\xe4\x93\x02\x1e:\x01*\"\x19/api/v1/auth/check-mobile\x12l\n" + + "\bSendCode\x12\x1d.topfans.user.SendCodeRequest\x1a\x1e.topfans.user.SendCodeResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/api/v1/auth/send-code\x12t\n" + + "\n" + + "VerifyCode\x12\x1f.topfans.user.VerifyCodeRequest\x1a .topfans.user.VerifyCodeResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/api/v1/auth/verify-code\x12g\n" + "\aGetUser\x12\x1c.topfans.user.GetUserRequest\x1a\x1d.topfans.user.GetUserResponse\"\x1f\x82\xd3\xe4\x93\x02\x19\x12\x17/api/v1/users/{user_id}\x12\x90\x01\n" + "\rGetFanProfile\x12\".topfans.user.GetFanProfileRequest\x1a#.topfans.user.GetFanProfileResponse\"6\x82\xd3\xe4\x93\x020\x12./api/v1/users/{user_id}/fan-profiles/{star_id}\x12s\n" + "\x16UpdateFanProfileSocial\x12+.topfans.user.UpdateFanProfileSocialRequest\x1a,.topfans.user.UpdateFanProfileSocialResponse\x12m\n" + @@ -3105,7 +3370,7 @@ func file_user_proto_rawDescGZIP() []byte { return file_user_proto_rawDescData } -var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 48) +var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 52) var file_user_proto_goTypes = []any{ (*User)(nil), // 0: topfans.user.User (*FanProfile)(nil), // 1: topfans.user.FanProfile @@ -3124,131 +3389,141 @@ var file_user_proto_goTypes = []any{ (*CheckNicknameResponse)(nil), // 14: topfans.user.CheckNicknameResponse (*CheckMobileRequest)(nil), // 15: topfans.user.CheckMobileRequest (*CheckMobileResponse)(nil), // 16: topfans.user.CheckMobileResponse - (*GetUserRequest)(nil), // 17: topfans.user.GetUserRequest - (*GetUserResponse)(nil), // 18: topfans.user.GetUserResponse - (*GetFanProfileRequest)(nil), // 19: topfans.user.GetFanProfileRequest - (*GetFanProfileResponse)(nil), // 20: topfans.user.GetFanProfileResponse - (*UpdateFanProfileSocialRequest)(nil), // 21: topfans.user.UpdateFanProfileSocialRequest - (*UpdateFanProfileSocialResponse)(nil), // 22: topfans.user.UpdateFanProfileSocialResponse - (*UpdateCrystalBalanceRequest)(nil), // 23: topfans.user.UpdateCrystalBalanceRequest - (*UpdateCrystalBalanceResponse)(nil), // 24: topfans.user.UpdateCrystalBalanceResponse - (*UpdateAssetsCountRequest)(nil), // 25: topfans.user.UpdateAssetsCountRequest - (*UpdateAssetsCountResponse)(nil), // 26: topfans.user.UpdateAssetsCountResponse - (*AddExhibitionHoursRequest)(nil), // 27: topfans.user.AddExhibitionHoursRequest - (*AddExhibitionHoursResponse)(nil), // 28: topfans.user.AddExhibitionHoursResponse - (*GetCurrentUserRequest)(nil), // 29: topfans.user.GetCurrentUserRequest - (*GetCurrentUserResponse)(nil), // 30: topfans.user.GetCurrentUserResponse - (*GetMyProfileRequest)(nil), // 31: topfans.user.GetMyProfileRequest - (*GetMyProfileResponse)(nil), // 32: topfans.user.GetMyProfileResponse - (*UpdateNicknameRequest)(nil), // 33: topfans.user.UpdateNicknameRequest - (*UpdateNicknameResponse)(nil), // 34: topfans.user.UpdateNicknameResponse - (*UpdatePasswordRequest)(nil), // 35: topfans.user.UpdatePasswordRequest - (*UpdatePasswordResponse)(nil), // 36: topfans.user.UpdatePasswordResponse - (*UpdateAvatarRequest)(nil), // 37: topfans.user.UpdateAvatarRequest - (*UpdateAvatarResponse)(nil), // 38: topfans.user.UpdateAvatarResponse - (*GetFanIdentitiesRequest)(nil), // 39: topfans.user.GetFanIdentitiesRequest - (*GetFanIdentitiesResponse)(nil), // 40: topfans.user.GetFanIdentitiesResponse - (*GetMyFanIdentitiesRequest)(nil), // 41: topfans.user.GetMyFanIdentitiesRequest - (*MyFanIdentityItem)(nil), // 42: topfans.user.MyFanIdentityItem - (*GetMyFanIdentitiesResponse)(nil), // 43: topfans.user.GetMyFanIdentitiesResponse - (*AddIdentityRequest)(nil), // 44: topfans.user.AddIdentityRequest - (*AddIdentityResponse)(nil), // 45: topfans.user.AddIdentityResponse - (*SwitchIdentityRequest)(nil), // 46: topfans.user.SwitchIdentityRequest - (*SwitchIdentityResponse)(nil), // 47: topfans.user.SwitchIdentityResponse - (*common.BaseResponse)(nil), // 48: topfans.common.BaseResponse + (*SendCodeRequest)(nil), // 17: topfans.user.SendCodeRequest + (*SendCodeResponse)(nil), // 18: topfans.user.SendCodeResponse + (*VerifyCodeRequest)(nil), // 19: topfans.user.VerifyCodeRequest + (*VerifyCodeResponse)(nil), // 20: topfans.user.VerifyCodeResponse + (*GetUserRequest)(nil), // 21: topfans.user.GetUserRequest + (*GetUserResponse)(nil), // 22: topfans.user.GetUserResponse + (*GetFanProfileRequest)(nil), // 23: topfans.user.GetFanProfileRequest + (*GetFanProfileResponse)(nil), // 24: topfans.user.GetFanProfileResponse + (*UpdateFanProfileSocialRequest)(nil), // 25: topfans.user.UpdateFanProfileSocialRequest + (*UpdateFanProfileSocialResponse)(nil), // 26: topfans.user.UpdateFanProfileSocialResponse + (*UpdateCrystalBalanceRequest)(nil), // 27: topfans.user.UpdateCrystalBalanceRequest + (*UpdateCrystalBalanceResponse)(nil), // 28: topfans.user.UpdateCrystalBalanceResponse + (*UpdateAssetsCountRequest)(nil), // 29: topfans.user.UpdateAssetsCountRequest + (*UpdateAssetsCountResponse)(nil), // 30: topfans.user.UpdateAssetsCountResponse + (*AddExhibitionHoursRequest)(nil), // 31: topfans.user.AddExhibitionHoursRequest + (*AddExhibitionHoursResponse)(nil), // 32: topfans.user.AddExhibitionHoursResponse + (*GetCurrentUserRequest)(nil), // 33: topfans.user.GetCurrentUserRequest + (*GetCurrentUserResponse)(nil), // 34: topfans.user.GetCurrentUserResponse + (*GetMyProfileRequest)(nil), // 35: topfans.user.GetMyProfileRequest + (*GetMyProfileResponse)(nil), // 36: topfans.user.GetMyProfileResponse + (*UpdateNicknameRequest)(nil), // 37: topfans.user.UpdateNicknameRequest + (*UpdateNicknameResponse)(nil), // 38: topfans.user.UpdateNicknameResponse + (*UpdatePasswordRequest)(nil), // 39: topfans.user.UpdatePasswordRequest + (*UpdatePasswordResponse)(nil), // 40: topfans.user.UpdatePasswordResponse + (*UpdateAvatarRequest)(nil), // 41: topfans.user.UpdateAvatarRequest + (*UpdateAvatarResponse)(nil), // 42: topfans.user.UpdateAvatarResponse + (*GetFanIdentitiesRequest)(nil), // 43: topfans.user.GetFanIdentitiesRequest + (*GetFanIdentitiesResponse)(nil), // 44: topfans.user.GetFanIdentitiesResponse + (*GetMyFanIdentitiesRequest)(nil), // 45: topfans.user.GetMyFanIdentitiesRequest + (*MyFanIdentityItem)(nil), // 46: topfans.user.MyFanIdentityItem + (*GetMyFanIdentitiesResponse)(nil), // 47: topfans.user.GetMyFanIdentitiesResponse + (*AddIdentityRequest)(nil), // 48: topfans.user.AddIdentityRequest + (*AddIdentityResponse)(nil), // 49: topfans.user.AddIdentityResponse + (*SwitchIdentityRequest)(nil), // 50: topfans.user.SwitchIdentityRequest + (*SwitchIdentityResponse)(nil), // 51: topfans.user.SwitchIdentityResponse + (*common.BaseResponse)(nil), // 52: topfans.common.BaseResponse } var file_user_proto_depIdxs = []int32{ - 48, // 0: topfans.user.RegisterResponse.base:type_name -> topfans.common.BaseResponse + 52, // 0: topfans.user.RegisterResponse.base:type_name -> topfans.common.BaseResponse 0, // 1: topfans.user.RegisterResponse.user:type_name -> topfans.user.User 1, // 2: topfans.user.RegisterResponse.fan_profile:type_name -> topfans.user.FanProfile - 48, // 3: topfans.user.LoginResponse.base:type_name -> topfans.common.BaseResponse + 52, // 3: topfans.user.LoginResponse.base:type_name -> topfans.common.BaseResponse 0, // 4: topfans.user.LoginResponse.user:type_name -> topfans.user.User 1, // 5: topfans.user.LoginResponse.fan_profile:type_name -> topfans.user.FanProfile 1, // 6: topfans.user.LoginResponse.fan_profiles:type_name -> topfans.user.FanProfile - 48, // 7: topfans.user.RefreshTokenResponse.base:type_name -> topfans.common.BaseResponse - 48, // 8: topfans.user.ValidateTokenResponse.base:type_name -> topfans.common.BaseResponse - 48, // 9: topfans.user.LogoutResponse.base:type_name -> topfans.common.BaseResponse - 48, // 10: topfans.user.CheckNicknameResponse.base:type_name -> topfans.common.BaseResponse - 48, // 11: topfans.user.CheckMobileResponse.base:type_name -> topfans.common.BaseResponse - 48, // 12: topfans.user.GetUserResponse.base:type_name -> topfans.common.BaseResponse - 0, // 13: topfans.user.GetUserResponse.user:type_name -> topfans.user.User - 48, // 14: topfans.user.GetFanProfileResponse.base:type_name -> topfans.common.BaseResponse - 1, // 15: topfans.user.GetFanProfileResponse.profile:type_name -> topfans.user.FanProfile - 48, // 16: topfans.user.UpdateFanProfileSocialResponse.base:type_name -> topfans.common.BaseResponse - 48, // 17: topfans.user.UpdateCrystalBalanceResponse.base:type_name -> topfans.common.BaseResponse - 48, // 18: topfans.user.UpdateAssetsCountResponse.base:type_name -> topfans.common.BaseResponse - 48, // 19: topfans.user.AddExhibitionHoursResponse.base:type_name -> topfans.common.BaseResponse - 48, // 20: topfans.user.GetCurrentUserResponse.base:type_name -> topfans.common.BaseResponse - 0, // 21: topfans.user.GetCurrentUserResponse.user:type_name -> topfans.user.User - 1, // 22: topfans.user.GetCurrentUserResponse.fan_profile:type_name -> topfans.user.FanProfile - 1, // 23: topfans.user.GetCurrentUserResponse.fan_profiles:type_name -> topfans.user.FanProfile - 48, // 24: topfans.user.GetMyProfileResponse.base:type_name -> topfans.common.BaseResponse - 0, // 25: topfans.user.GetMyProfileResponse.user:type_name -> topfans.user.User - 1, // 26: topfans.user.GetMyProfileResponse.fan_profile:type_name -> topfans.user.FanProfile - 1, // 27: topfans.user.GetMyProfileResponse.fan_profiles:type_name -> topfans.user.FanProfile - 48, // 28: topfans.user.UpdateNicknameResponse.base:type_name -> topfans.common.BaseResponse - 1, // 29: topfans.user.UpdateNicknameResponse.fan_profile:type_name -> topfans.user.FanProfile - 48, // 30: topfans.user.UpdatePasswordResponse.base:type_name -> topfans.common.BaseResponse - 48, // 31: topfans.user.UpdateAvatarResponse.base:type_name -> topfans.common.BaseResponse - 48, // 32: topfans.user.GetFanIdentitiesResponse.base:type_name -> topfans.common.BaseResponse - 2, // 33: topfans.user.GetFanIdentitiesResponse.stars:type_name -> topfans.user.Star - 1, // 34: topfans.user.MyFanIdentityItem.fan_profile:type_name -> topfans.user.FanProfile - 2, // 35: topfans.user.MyFanIdentityItem.star:type_name -> topfans.user.Star - 48, // 36: topfans.user.GetMyFanIdentitiesResponse.base:type_name -> topfans.common.BaseResponse - 42, // 37: topfans.user.GetMyFanIdentitiesResponse.items:type_name -> topfans.user.MyFanIdentityItem - 48, // 38: topfans.user.AddIdentityResponse.base:type_name -> topfans.common.BaseResponse - 1, // 39: topfans.user.AddIdentityResponse.fan_profile:type_name -> topfans.user.FanProfile - 48, // 40: topfans.user.SwitchIdentityResponse.base:type_name -> topfans.common.BaseResponse - 1, // 41: topfans.user.SwitchIdentityResponse.fan_profile:type_name -> topfans.user.FanProfile - 3, // 42: topfans.user.UserSocialService.Register:input_type -> topfans.user.RegisterRequest - 5, // 43: topfans.user.UserSocialService.Login:input_type -> topfans.user.LoginRequest - 7, // 44: topfans.user.UserSocialService.RefreshToken:input_type -> topfans.user.RefreshTokenRequest - 9, // 45: topfans.user.UserSocialService.ValidateToken:input_type -> topfans.user.ValidateTokenRequest - 11, // 46: topfans.user.UserSocialService.Logout:input_type -> topfans.user.LogoutRequest - 13, // 47: topfans.user.UserSocialService.CheckNickname:input_type -> topfans.user.CheckNicknameRequest - 15, // 48: topfans.user.UserSocialService.CheckMobile:input_type -> topfans.user.CheckMobileRequest - 17, // 49: topfans.user.UserSocialService.GetUser:input_type -> topfans.user.GetUserRequest - 19, // 50: topfans.user.UserSocialService.GetFanProfile:input_type -> topfans.user.GetFanProfileRequest - 21, // 51: topfans.user.UserSocialService.UpdateFanProfileSocial:input_type -> topfans.user.UpdateFanProfileSocialRequest - 23, // 52: topfans.user.UserSocialService.UpdateCrystalBalance:input_type -> topfans.user.UpdateCrystalBalanceRequest - 25, // 53: topfans.user.UserSocialService.UpdateAssetsCount:input_type -> topfans.user.UpdateAssetsCountRequest - 27, // 54: topfans.user.UserSocialService.AddExhibitionHours:input_type -> topfans.user.AddExhibitionHoursRequest - 29, // 55: topfans.user.UserSocialService.GetCurrentUser:input_type -> topfans.user.GetCurrentUserRequest - 31, // 56: topfans.user.UserSocialService.GetMyProfile:input_type -> topfans.user.GetMyProfileRequest - 33, // 57: topfans.user.UserSocialService.UpdateNickname:input_type -> topfans.user.UpdateNicknameRequest - 35, // 58: topfans.user.UserSocialService.UpdatePassword:input_type -> topfans.user.UpdatePasswordRequest - 37, // 59: topfans.user.UserSocialService.UpdateAvatar:input_type -> topfans.user.UpdateAvatarRequest - 39, // 60: topfans.user.UserSocialService.GetFanIdentities:input_type -> topfans.user.GetFanIdentitiesRequest - 41, // 61: topfans.user.UserSocialService.GetMyFanIdentities:input_type -> topfans.user.GetMyFanIdentitiesRequest - 44, // 62: topfans.user.UserSocialService.AddIdentity:input_type -> topfans.user.AddIdentityRequest - 46, // 63: topfans.user.UserSocialService.SwitchIdentity:input_type -> topfans.user.SwitchIdentityRequest - 4, // 64: topfans.user.UserSocialService.Register:output_type -> topfans.user.RegisterResponse - 6, // 65: topfans.user.UserSocialService.Login:output_type -> topfans.user.LoginResponse - 8, // 66: topfans.user.UserSocialService.RefreshToken:output_type -> topfans.user.RefreshTokenResponse - 10, // 67: topfans.user.UserSocialService.ValidateToken:output_type -> topfans.user.ValidateTokenResponse - 12, // 68: topfans.user.UserSocialService.Logout:output_type -> topfans.user.LogoutResponse - 14, // 69: topfans.user.UserSocialService.CheckNickname:output_type -> topfans.user.CheckNicknameResponse - 16, // 70: topfans.user.UserSocialService.CheckMobile:output_type -> topfans.user.CheckMobileResponse - 18, // 71: topfans.user.UserSocialService.GetUser:output_type -> topfans.user.GetUserResponse - 20, // 72: topfans.user.UserSocialService.GetFanProfile:output_type -> topfans.user.GetFanProfileResponse - 22, // 73: topfans.user.UserSocialService.UpdateFanProfileSocial:output_type -> topfans.user.UpdateFanProfileSocialResponse - 24, // 74: topfans.user.UserSocialService.UpdateCrystalBalance:output_type -> topfans.user.UpdateCrystalBalanceResponse - 26, // 75: topfans.user.UserSocialService.UpdateAssetsCount:output_type -> topfans.user.UpdateAssetsCountResponse - 28, // 76: topfans.user.UserSocialService.AddExhibitionHours:output_type -> topfans.user.AddExhibitionHoursResponse - 30, // 77: topfans.user.UserSocialService.GetCurrentUser:output_type -> topfans.user.GetCurrentUserResponse - 32, // 78: topfans.user.UserSocialService.GetMyProfile:output_type -> topfans.user.GetMyProfileResponse - 34, // 79: topfans.user.UserSocialService.UpdateNickname:output_type -> topfans.user.UpdateNicknameResponse - 36, // 80: topfans.user.UserSocialService.UpdatePassword:output_type -> topfans.user.UpdatePasswordResponse - 38, // 81: topfans.user.UserSocialService.UpdateAvatar:output_type -> topfans.user.UpdateAvatarResponse - 40, // 82: topfans.user.UserSocialService.GetFanIdentities:output_type -> topfans.user.GetFanIdentitiesResponse - 43, // 83: topfans.user.UserSocialService.GetMyFanIdentities:output_type -> topfans.user.GetMyFanIdentitiesResponse - 45, // 84: topfans.user.UserSocialService.AddIdentity:output_type -> topfans.user.AddIdentityResponse - 47, // 85: topfans.user.UserSocialService.SwitchIdentity:output_type -> topfans.user.SwitchIdentityResponse - 64, // [64:86] is the sub-list for method output_type - 42, // [42:64] is the sub-list for method input_type - 42, // [42:42] is the sub-list for extension type_name - 42, // [42:42] is the sub-list for extension extendee - 0, // [0:42] is the sub-list for field type_name + 52, // 7: topfans.user.RefreshTokenResponse.base:type_name -> topfans.common.BaseResponse + 52, // 8: topfans.user.ValidateTokenResponse.base:type_name -> topfans.common.BaseResponse + 52, // 9: topfans.user.LogoutResponse.base:type_name -> topfans.common.BaseResponse + 52, // 10: topfans.user.CheckNicknameResponse.base:type_name -> topfans.common.BaseResponse + 52, // 11: topfans.user.CheckMobileResponse.base:type_name -> topfans.common.BaseResponse + 52, // 12: topfans.user.SendCodeResponse.base:type_name -> topfans.common.BaseResponse + 52, // 13: topfans.user.VerifyCodeResponse.base:type_name -> topfans.common.BaseResponse + 52, // 14: topfans.user.GetUserResponse.base:type_name -> topfans.common.BaseResponse + 0, // 15: topfans.user.GetUserResponse.user:type_name -> topfans.user.User + 52, // 16: topfans.user.GetFanProfileResponse.base:type_name -> topfans.common.BaseResponse + 1, // 17: topfans.user.GetFanProfileResponse.profile:type_name -> topfans.user.FanProfile + 52, // 18: topfans.user.UpdateFanProfileSocialResponse.base:type_name -> topfans.common.BaseResponse + 52, // 19: topfans.user.UpdateCrystalBalanceResponse.base:type_name -> topfans.common.BaseResponse + 52, // 20: topfans.user.UpdateAssetsCountResponse.base:type_name -> topfans.common.BaseResponse + 52, // 21: topfans.user.AddExhibitionHoursResponse.base:type_name -> topfans.common.BaseResponse + 52, // 22: topfans.user.GetCurrentUserResponse.base:type_name -> topfans.common.BaseResponse + 0, // 23: topfans.user.GetCurrentUserResponse.user:type_name -> topfans.user.User + 1, // 24: topfans.user.GetCurrentUserResponse.fan_profile:type_name -> topfans.user.FanProfile + 1, // 25: topfans.user.GetCurrentUserResponse.fan_profiles:type_name -> topfans.user.FanProfile + 52, // 26: topfans.user.GetMyProfileResponse.base:type_name -> topfans.common.BaseResponse + 0, // 27: topfans.user.GetMyProfileResponse.user:type_name -> topfans.user.User + 1, // 28: topfans.user.GetMyProfileResponse.fan_profile:type_name -> topfans.user.FanProfile + 1, // 29: topfans.user.GetMyProfileResponse.fan_profiles:type_name -> topfans.user.FanProfile + 52, // 30: topfans.user.UpdateNicknameResponse.base:type_name -> topfans.common.BaseResponse + 1, // 31: topfans.user.UpdateNicknameResponse.fan_profile:type_name -> topfans.user.FanProfile + 52, // 32: topfans.user.UpdatePasswordResponse.base:type_name -> topfans.common.BaseResponse + 52, // 33: topfans.user.UpdateAvatarResponse.base:type_name -> topfans.common.BaseResponse + 52, // 34: topfans.user.GetFanIdentitiesResponse.base:type_name -> topfans.common.BaseResponse + 2, // 35: topfans.user.GetFanIdentitiesResponse.stars:type_name -> topfans.user.Star + 1, // 36: topfans.user.MyFanIdentityItem.fan_profile:type_name -> topfans.user.FanProfile + 2, // 37: topfans.user.MyFanIdentityItem.star:type_name -> topfans.user.Star + 52, // 38: topfans.user.GetMyFanIdentitiesResponse.base:type_name -> topfans.common.BaseResponse + 46, // 39: topfans.user.GetMyFanIdentitiesResponse.items:type_name -> topfans.user.MyFanIdentityItem + 52, // 40: topfans.user.AddIdentityResponse.base:type_name -> topfans.common.BaseResponse + 1, // 41: topfans.user.AddIdentityResponse.fan_profile:type_name -> topfans.user.FanProfile + 52, // 42: topfans.user.SwitchIdentityResponse.base:type_name -> topfans.common.BaseResponse + 1, // 43: topfans.user.SwitchIdentityResponse.fan_profile:type_name -> topfans.user.FanProfile + 3, // 44: topfans.user.UserSocialService.Register:input_type -> topfans.user.RegisterRequest + 5, // 45: topfans.user.UserSocialService.Login:input_type -> topfans.user.LoginRequest + 7, // 46: topfans.user.UserSocialService.RefreshToken:input_type -> topfans.user.RefreshTokenRequest + 9, // 47: topfans.user.UserSocialService.ValidateToken:input_type -> topfans.user.ValidateTokenRequest + 11, // 48: topfans.user.UserSocialService.Logout:input_type -> topfans.user.LogoutRequest + 13, // 49: topfans.user.UserSocialService.CheckNickname:input_type -> topfans.user.CheckNicknameRequest + 15, // 50: topfans.user.UserSocialService.CheckMobile:input_type -> topfans.user.CheckMobileRequest + 17, // 51: topfans.user.UserSocialService.SendCode:input_type -> topfans.user.SendCodeRequest + 19, // 52: topfans.user.UserSocialService.VerifyCode:input_type -> topfans.user.VerifyCodeRequest + 21, // 53: topfans.user.UserSocialService.GetUser:input_type -> topfans.user.GetUserRequest + 23, // 54: topfans.user.UserSocialService.GetFanProfile:input_type -> topfans.user.GetFanProfileRequest + 25, // 55: topfans.user.UserSocialService.UpdateFanProfileSocial:input_type -> topfans.user.UpdateFanProfileSocialRequest + 27, // 56: topfans.user.UserSocialService.UpdateCrystalBalance:input_type -> topfans.user.UpdateCrystalBalanceRequest + 29, // 57: topfans.user.UserSocialService.UpdateAssetsCount:input_type -> topfans.user.UpdateAssetsCountRequest + 31, // 58: topfans.user.UserSocialService.AddExhibitionHours:input_type -> topfans.user.AddExhibitionHoursRequest + 33, // 59: topfans.user.UserSocialService.GetCurrentUser:input_type -> topfans.user.GetCurrentUserRequest + 35, // 60: topfans.user.UserSocialService.GetMyProfile:input_type -> topfans.user.GetMyProfileRequest + 37, // 61: topfans.user.UserSocialService.UpdateNickname:input_type -> topfans.user.UpdateNicknameRequest + 39, // 62: topfans.user.UserSocialService.UpdatePassword:input_type -> topfans.user.UpdatePasswordRequest + 41, // 63: topfans.user.UserSocialService.UpdateAvatar:input_type -> topfans.user.UpdateAvatarRequest + 43, // 64: topfans.user.UserSocialService.GetFanIdentities:input_type -> topfans.user.GetFanIdentitiesRequest + 45, // 65: topfans.user.UserSocialService.GetMyFanIdentities:input_type -> topfans.user.GetMyFanIdentitiesRequest + 48, // 66: topfans.user.UserSocialService.AddIdentity:input_type -> topfans.user.AddIdentityRequest + 50, // 67: topfans.user.UserSocialService.SwitchIdentity:input_type -> topfans.user.SwitchIdentityRequest + 4, // 68: topfans.user.UserSocialService.Register:output_type -> topfans.user.RegisterResponse + 6, // 69: topfans.user.UserSocialService.Login:output_type -> topfans.user.LoginResponse + 8, // 70: topfans.user.UserSocialService.RefreshToken:output_type -> topfans.user.RefreshTokenResponse + 10, // 71: topfans.user.UserSocialService.ValidateToken:output_type -> topfans.user.ValidateTokenResponse + 12, // 72: topfans.user.UserSocialService.Logout:output_type -> topfans.user.LogoutResponse + 14, // 73: topfans.user.UserSocialService.CheckNickname:output_type -> topfans.user.CheckNicknameResponse + 16, // 74: topfans.user.UserSocialService.CheckMobile:output_type -> topfans.user.CheckMobileResponse + 18, // 75: topfans.user.UserSocialService.SendCode:output_type -> topfans.user.SendCodeResponse + 20, // 76: topfans.user.UserSocialService.VerifyCode:output_type -> topfans.user.VerifyCodeResponse + 22, // 77: topfans.user.UserSocialService.GetUser:output_type -> topfans.user.GetUserResponse + 24, // 78: topfans.user.UserSocialService.GetFanProfile:output_type -> topfans.user.GetFanProfileResponse + 26, // 79: topfans.user.UserSocialService.UpdateFanProfileSocial:output_type -> topfans.user.UpdateFanProfileSocialResponse + 28, // 80: topfans.user.UserSocialService.UpdateCrystalBalance:output_type -> topfans.user.UpdateCrystalBalanceResponse + 30, // 81: topfans.user.UserSocialService.UpdateAssetsCount:output_type -> topfans.user.UpdateAssetsCountResponse + 32, // 82: topfans.user.UserSocialService.AddExhibitionHours:output_type -> topfans.user.AddExhibitionHoursResponse + 34, // 83: topfans.user.UserSocialService.GetCurrentUser:output_type -> topfans.user.GetCurrentUserResponse + 36, // 84: topfans.user.UserSocialService.GetMyProfile:output_type -> topfans.user.GetMyProfileResponse + 38, // 85: topfans.user.UserSocialService.UpdateNickname:output_type -> topfans.user.UpdateNicknameResponse + 40, // 86: topfans.user.UserSocialService.UpdatePassword:output_type -> topfans.user.UpdatePasswordResponse + 42, // 87: topfans.user.UserSocialService.UpdateAvatar:output_type -> topfans.user.UpdateAvatarResponse + 44, // 88: topfans.user.UserSocialService.GetFanIdentities:output_type -> topfans.user.GetFanIdentitiesResponse + 47, // 89: topfans.user.UserSocialService.GetMyFanIdentities:output_type -> topfans.user.GetMyFanIdentitiesResponse + 49, // 90: topfans.user.UserSocialService.AddIdentity:output_type -> topfans.user.AddIdentityResponse + 51, // 91: topfans.user.UserSocialService.SwitchIdentity:output_type -> topfans.user.SwitchIdentityResponse + 68, // [68:92] is the sub-list for method output_type + 44, // [44:68] is the sub-list for method input_type + 44, // [44:44] is the sub-list for extension type_name + 44, // [44:44] is the sub-list for extension extendee + 0, // [0:44] is the sub-list for field type_name } func init() { file_user_proto_init() } @@ -3262,7 +3537,7 @@ func file_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_proto_rawDesc), len(file_user_proto_rawDesc)), NumEnums: 0, - NumMessages: 48, + NumMessages: 52, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/pkg/proto/user/user.triple.go b/backend/pkg/proto/user/user.triple.go index d6b78a8..1333bd4 100644 --- a/backend/pkg/proto/user/user.triple.go +++ b/backend/pkg/proto/user/user.triple.go @@ -50,6 +50,10 @@ const ( UserSocialServiceCheckNicknameProcedure = "/topfans.user.UserSocialService/CheckNickname" // UserSocialServiceCheckMobileProcedure is the fully-qualified name of the UserSocialService's CheckMobile RPC. UserSocialServiceCheckMobileProcedure = "/topfans.user.UserSocialService/CheckMobile" + // UserSocialServiceSendCodeProcedure is the fully-qualified name of the UserSocialService's SendCode RPC. + UserSocialServiceSendCodeProcedure = "/topfans.user.UserSocialService/SendCode" + // UserSocialServiceVerifyCodeProcedure is the fully-qualified name of the UserSocialService's VerifyCode RPC. + UserSocialServiceVerifyCodeProcedure = "/topfans.user.UserSocialService/VerifyCode" // UserSocialServiceGetUserProcedure is the fully-qualified name of the UserSocialService's GetUser RPC. UserSocialServiceGetUserProcedure = "/topfans.user.UserSocialService/GetUser" // UserSocialServiceGetFanProfileProcedure is the fully-qualified name of the UserSocialService's GetFanProfile RPC. @@ -95,6 +99,8 @@ type UserSocialService interface { Logout(ctx context.Context, req *LogoutRequest, opts ...client.CallOption) (*LogoutResponse, error) CheckNickname(ctx context.Context, req *CheckNicknameRequest, opts ...client.CallOption) (*CheckNicknameResponse, error) CheckMobile(ctx context.Context, req *CheckMobileRequest, opts ...client.CallOption) (*CheckMobileResponse, error) + SendCode(ctx context.Context, req *SendCodeRequest, opts ...client.CallOption) (*SendCodeResponse, error) + VerifyCode(ctx context.Context, req *VerifyCodeRequest, opts ...client.CallOption) (*VerifyCodeResponse, error) GetUser(ctx context.Context, req *GetUserRequest, opts ...client.CallOption) (*GetUserResponse, error) GetFanProfile(ctx context.Context, req *GetFanProfileRequest, opts ...client.CallOption) (*GetFanProfileResponse, error) UpdateFanProfileSocial(ctx context.Context, req *UpdateFanProfileSocialRequest, opts ...client.CallOption) (*UpdateFanProfileSocialResponse, error) @@ -188,6 +194,22 @@ func (c *UserSocialServiceImpl) CheckMobile(ctx context.Context, req *CheckMobil return resp, nil } +func (c *UserSocialServiceImpl) SendCode(ctx context.Context, req *SendCodeRequest, opts ...client.CallOption) (*SendCodeResponse, error) { + resp := new(SendCodeResponse) + if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "SendCode", opts...); err != nil { + return nil, err + } + return resp, nil +} + +func (c *UserSocialServiceImpl) VerifyCode(ctx context.Context, req *VerifyCodeRequest, opts ...client.CallOption) (*VerifyCodeResponse, error) { + resp := new(VerifyCodeResponse) + if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "VerifyCode", opts...); err != nil { + return nil, err + } + return resp, nil +} + func (c *UserSocialServiceImpl) GetUser(ctx context.Context, req *GetUserRequest, opts ...client.CallOption) (*GetUserResponse, error) { resp := new(GetUserResponse) if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "GetUser", opts...); err != nil { @@ -310,7 +332,7 @@ func (c *UserSocialServiceImpl) SwitchIdentity(ctx context.Context, req *SwitchI var UserSocialService_ClientInfo = client.ClientInfo{ InterfaceName: "topfans.user.UserSocialService", - MethodNames: []string{"Register", "Login", "RefreshToken", "ValidateToken", "Logout", "CheckNickname", "CheckMobile", "GetUser", "GetFanProfile", "UpdateFanProfileSocial", "UpdateCrystalBalance", "UpdateAssetsCount", "AddExhibitionHours", "GetCurrentUser", "GetMyProfile", "UpdateNickname", "UpdatePassword", "UpdateAvatar", "GetFanIdentities", "GetMyFanIdentities", "AddIdentity", "SwitchIdentity"}, + MethodNames: []string{"Register", "Login", "RefreshToken", "ValidateToken", "Logout", "CheckNickname", "CheckMobile", "SendCode", "VerifyCode", "GetUser", "GetFanProfile", "UpdateFanProfileSocial", "UpdateCrystalBalance", "UpdateAssetsCount", "AddExhibitionHours", "GetCurrentUser", "GetMyProfile", "UpdateNickname", "UpdatePassword", "UpdateAvatar", "GetFanIdentities", "GetMyFanIdentities", "AddIdentity", "SwitchIdentity"}, ConnectionInjectFunc: func(dubboCliRaw interface{}, conn *client.Connection) { dubboCli := dubboCliRaw.(*UserSocialServiceImpl) dubboCli.conn = conn @@ -326,6 +348,8 @@ type UserSocialServiceHandler interface { Logout(context.Context, *LogoutRequest) (*LogoutResponse, error) CheckNickname(context.Context, *CheckNicknameRequest) (*CheckNicknameResponse, error) CheckMobile(context.Context, *CheckMobileRequest) (*CheckMobileResponse, error) + SendCode(context.Context, *SendCodeRequest) (*SendCodeResponse, error) + VerifyCode(context.Context, *VerifyCodeRequest) (*VerifyCodeResponse, error) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) GetFanProfile(context.Context, *GetFanProfileRequest) (*GetFanProfileResponse, error) UpdateFanProfileSocial(context.Context, *UpdateFanProfileSocialRequest) (*UpdateFanProfileSocialResponse, error) @@ -460,6 +484,36 @@ var UserSocialService_ServiceInfo = server.ServiceInfo{ return triple_protocol.NewResponse(res), nil }, }, + { + Name: "SendCode", + Type: constant.CallUnary, + ReqInitFunc: func() interface{} { + return new(SendCodeRequest) + }, + MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) { + req := args[0].(*SendCodeRequest) + res, err := handler.(UserSocialServiceHandler).SendCode(ctx, req) + if err != nil { + return nil, err + } + return triple_protocol.NewResponse(res), nil + }, + }, + { + Name: "VerifyCode", + Type: constant.CallUnary, + ReqInitFunc: func() interface{} { + return new(VerifyCodeRequest) + }, + MethodFunc: func(ctx context.Context, args []interface{}, handler interface{}) (interface{}, error) { + req := args[0].(*VerifyCodeRequest) + res, err := handler.(UserSocialServiceHandler).VerifyCode(ctx, req) + if err != nil { + return nil, err + } + return triple_protocol.NewResponse(res), nil + }, + }, { Name: "GetUser", Type: constant.CallUnary, diff --git a/backend/proto/user.proto b/backend/proto/user.proto index 063288f..5dbfad3 100644 --- a/backend/proto/user.proto +++ b/backend/proto/user.proto @@ -60,6 +60,7 @@ message RegisterRequest { string password = 2; // 密码 int64 star_id = 3; // 选择第一个粉丝身份的明星ID string nickname = 4; // 第一个粉丝身份的昵称 + string verify_token = 5; // 短信验证token(注册前必须先通过短信验证) } // 注册响应 @@ -146,6 +147,35 @@ message CheckMobileResponse { bool exists = 2; // 手机号是否已存在 } +// ==================== 短信验证码相关消息 ==================== + +// 发送验证码请求 +message SendCodeRequest { + string mobile = 1; // 手机号 + string scene = 2; // 场景:register, password +} + +// 发送验证码响应 +message SendCodeResponse { + topfans.common.BaseResponse base = 1; + int32 expires_in = 2; // 多少秒后可以重发 +} + +// 验证验证码请求 +message VerifyCodeRequest { + string mobile = 1; // 手机号 + string code = 2; // 验证码 + string scene = 3; // 场景:register, password +} + +// 验证验证码响应 +message VerifyCodeResponse { + topfans.common.BaseResponse base = 1; + bool verified = 2; // 是否验证成功 + string verify_token = 3; // 验证成功后返回的token + int32 expires_in = 4; // token有效期(秒) +} + // ==================== 用户信息相关消息 ==================== // 获取用户信息请求 @@ -397,6 +427,21 @@ service UserSocialService { }; } + // 短信验证码相关 + rpc SendCode(SendCodeRequest) returns (SendCodeResponse) { + option (google.api.http) = { + post: "/api/v1/auth/send-code" + body: "*" + }; + } + + rpc VerifyCode(VerifyCodeRequest) returns (VerifyCodeResponse) { + option (google.api.http) = { + post: "/api/v1/auth/verify-code" + body: "*" + }; + } + // 用户信息相关 rpc GetUser(GetUserRequest) returns (GetUserResponse) { option (google.api.http) = { diff --git a/backend/services/userService/config/sms_config.go b/backend/services/userService/config/sms_config.go new file mode 100644 index 0000000..892dadc --- /dev/null +++ b/backend/services/userService/config/sms_config.go @@ -0,0 +1,28 @@ +package config + +import "os" + +type SMSConfig struct { + AccessKeyID string + AccessKeySecret string + SignName string + TemplateCode string + Region string +} + +func GetSMSConfig() SMSConfig { + return SMSConfig{ + AccessKeyID: os.Getenv("SMS_ACCESS_KEY_ID"), + AccessKeySecret: os.Getenv("SMS_ACCESS_KEY_SECRET"), + SignName: os.Getenv("SMS_SIGN_NAME"), + TemplateCode: os.Getenv("SMS_TEMPLATE_CODE"), + Region: getEnvOrDefault("SMS_REGION", "cn-hangzhou"), + } +} + +func getEnvOrDefault(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue +} \ No newline at end of file diff --git a/backend/services/userService/go.mod b/backend/services/userService/go.mod index 3ea9316..92290ab 100644 --- a/backend/services/userService/go.mod +++ b/backend/services/userService/go.mod @@ -4,6 +4,8 @@ go 1.25.5 require ( dubbo.apache.org/dubbo-go/v3 v3.3.1 + github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 + github.com/redis/go-redis/v9 v9.19.0 github.com/topfans/backend v0.0.0 go.uber.org/zap v1.27.1 golang.org/x/crypto v0.46.0 @@ -16,12 +18,16 @@ require ( github.com/Workiva/go-datastructures v1.0.52 // indirect github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect github.com/alibaba/sentinel-golang v1.0.4 // indirect + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect + github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.16 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect - github.com/alibabacloud-go/tea v1.2.2 // indirect + github.com/alibabacloud-go/tea v1.3.13 // indirect github.com/alibabacloud-go/tea-utils v1.4.4 // indirect + github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 // indirect github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.2.2 // indirect github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.7 // indirect + github.com/aliyun/credentials-go v1.4.5 // indirect github.com/apache/dubbo-getty v1.4.10 // indirect github.com/apache/dubbo-go-hessian2 v1.12.5 // indirect github.com/apolloconfig/agollo/v4 v4.4.0 // indirect @@ -30,6 +36,7 @@ require ( github.com/buger/jsonparser v1.1.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/creasty/defaults v1.5.2 // indirect @@ -117,6 +124,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.8.1 // indirect github.com/subosito/gotenv v1.2.0 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.4.0 // indirect github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect @@ -139,7 +147,7 @@ require ( go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect - go.uber.org/atomic v1.10.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/mod v0.30.0 // indirect diff --git a/backend/services/userService/go.sum b/backend/services/userService/go.sum index 94a99af..d3107f3 100644 --- a/backend/services/userService/go.sum +++ b/backend/services/userService/go.sum @@ -68,16 +68,49 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alibaba/sentinel-golang v1.0.4 h1:i0wtMvNVdy7vM4DdzYrlC4r/Mpk1OKUUBurKKkWhEo8= github.com/alibaba/sentinel-golang v1.0.4/go.mod h1:Lag5rIYyJiPOylK8Kku2P+a23gdKMMqzQS7wTnjWEpk= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= +github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= +github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= +github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= +github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= +github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= +github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.16 h1:LHhjxZkNWAKWepxcWyzgFgo0X6TUVhL7sC7ANc60p8A= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.16/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= +github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= +github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= +github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8 h1:aDPyz6C+nenypx24N5qEt09NjpS6mu7Cu1A+wf9UTaY= +github.com/alibabacloud-go/dysmsapi-20180501/v2 v2.0.8/go.mod h1:e/vWJ5gLVnraPROSh+3oMSodf5ukaUlqNgH0IIcnz98= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= +github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94= +github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.4.4 h1:lxCDvNCdTo9FaXKKq45+4vGETQUKNOW/qKTcX9Sk53o= github.com/alibabacloud-go/tea-utils v1.4.4/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1800 h1:ie/8RxBOfKZWcrbYSJi2Z8uX8TcOlSMwPlEJh83OeOw= @@ -86,6 +119,11 @@ github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.2.2 h1:rWkH6D2XlXb/Y+tNAQROxBz github.com/aliyun/alibabacloud-dkms-gcs-go-sdk v0.2.2/go.mod h1:GDtq+Kw+v0fO+j5BrrWiUHbBq7L+hfpzpPfXKOZMFE0= github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.7 h1:olLiPI2iM8Hqq6vKnSxpM3awCrm9/BeOgHpzQkOYnI4= github.com/aliyun/alibabacloud-dkms-transfer-go-sdk v0.1.7/go.mod h1:oDg1j4kFxnhgftaiLJABkGeSvuEvSF5Lo6UmRAMruX4= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= +github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= +github.com/aliyun/credentials-go v1.4.5 h1:O76WYKgdy1oQYYiJkERjlA2dxGuvLRrzuO2ScrtGWSk= +github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/dubbo-getty v1.4.10 h1:ZmkpHJa/qgS0evX2tTNqNCz6rClI/9Wwp7ctyMml82w= github.com/apache/dubbo-getty v1.4.10/go.mod h1:V64WqLIxksEgNu5aBJBOxNIvpOZyfUJ7J/DXBlKSUoA= @@ -125,6 +163,10 @@ github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjL github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -145,6 +187,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -365,6 +409,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -394,8 +439,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= -github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -528,6 +573,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -729,6 +776,8 @@ github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1 github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k= +github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -753,8 +802,9 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -788,6 +838,7 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -804,6 +855,9 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= @@ -832,12 +886,15 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= +github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= @@ -910,8 +967,8 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= @@ -941,11 +998,19 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -991,6 +1056,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1030,6 +1098,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1046,8 +1115,15 @@ golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1077,6 +1153,9 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1123,6 +1202,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1168,14 +1248,27 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1189,7 +1282,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1246,6 +1342,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1266,6 +1363,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1429,6 +1528,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= diff --git a/backend/services/userService/main.go b/backend/services/userService/main.go index c5a96f5..60aa63f 100644 --- a/backend/services/userService/main.go +++ b/backend/services/userService/main.go @@ -29,6 +29,10 @@ var ( dbUser = flag.String("db-user", getEnv("DB_USER", "postgres"), "Database user") dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password") dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name") + redisHost = flag.String("redis-host", getEnv("REDIS_HOST", "localhost"), "Redis host") + redisPort = flag.Int("redis-port", getEnvInt("REDIS_PORT", 6379), "Redis port") + redisDB = flag.Int("redis-db", getEnvInt("REDIS_DB", 0), "Redis db") + redisPassword = flag.String("redis-password", getEnv("REDIS_PASSWORD", ""), "Redis password") healthHandler *health.Handler ) @@ -73,6 +77,16 @@ func main() { logger.Sugar.Fatalf("Failed to initialize database: %v", err) } + // 初始化 Redis + if err := initRedis(); err != nil { + logger.Sugar.Fatalf("Failed to initialize Redis: %v", err) + } + + // 初始化 SMS 客户端 + if err := service.InitSMSClient(); err != nil { + logger.Sugar.Fatalf("Failed to initialize SMS client: %v", err) + } + // 自动迁移数据库表 if err := autoMigrate(); err != nil { logger.Sugar.Fatalf("Failed to migrate database: %v", err) @@ -103,6 +117,17 @@ func initDatabase() error { return database.Init(config) } +// initRedis 初始化 Redis 连接 +func initRedis() error { + redisConfig := database.RedisConfig{ + Host: *redisHost, + Port: *redisPort, + Password: *redisPassword, + DB: *redisDB, + } + return database.InitRedis(redisConfig) +} + // autoMigrate 自动迁移数据库表 func autoMigrate() error { db := database.GetDB() diff --git a/backend/services/userService/provider/auth_provider.go b/backend/services/userService/provider/auth_provider.go index c8fd774..563e93a 100644 --- a/backend/services/userService/provider/auth_provider.go +++ b/backend/services/userService/provider/auth_provider.go @@ -2,6 +2,7 @@ package provider import ( "context" + "time" appErrors "github.com/topfans/backend/pkg/errors" "github.com/topfans/backend/pkg/logger" @@ -16,6 +17,13 @@ type AuthProvider struct { authService service.AuthService } +// getClientIP 从 context 中获取客户端 IP(简化实现) +func getClientIP(ctx context.Context) string { + // 在实际部署中,应从网关传递的 attachments 或 header 中获取 + // 这里返回一个默认值让代码能编译通过 + return "127.0.0.1" +} + // NewAuthProvider 创建认证Provider实例 func NewAuthProvider(authService service.AuthService) *AuthProvider { return &AuthProvider{ @@ -32,7 +40,7 @@ func (p *AuthProvider) Register(ctx context.Context, req *pb.RegisterRequest) (* ) // 调用Service层 - resp, err := p.authService.Register(req) + resp, err := p.authService.Register(ctx, req) if err != nil { logger.Logger.Error("Register failed", zap.String("mobile", req.Mobile), @@ -240,3 +248,81 @@ func (p *AuthProvider) ValidateToken(ctx context.Context, req *pb.ValidateTokenR return resp, nil } + +// SendCode 发送验证码 +func (p *AuthProvider) SendCode(ctx context.Context, req *pb.SendCodeRequest) (*pb.SendCodeResponse, error) { + logger.Logger.Info("Received SendCode request", + zap.String("mobile", req.Mobile), + zap.String("scene", req.Scene), + ) + + // 获取客户端IP(从context或 attachments 获取) + ip := getClientIP(ctx) + + expiresIn, err := service.SendVerificationCode(ctx, req.Mobile, ip) + if err != nil { + logger.Logger.Error("SendCode failed", + zap.String("mobile", req.Mobile), + zap.Error(err), + ) + return &pb.SendCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: appErrors.ToStatusCode(err), + Message: err.Error(), + Timestamp: time.Now().UnixMilli(), + }, + }, nil + } + + logger.Logger.Info("SendCode successful", + zap.String("mobile", req.Mobile), + ) + + return &pb.SendCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_OK, + Message: "发送成功", + Timestamp: time.Now().UnixMilli(), + }, + ExpiresIn: int32(expiresIn), + }, nil +} + +// VerifyCode 验证验证码 +func (p *AuthProvider) VerifyCode(ctx context.Context, req *pb.VerifyCodeRequest) (*pb.VerifyCodeResponse, error) { + logger.Logger.Info("Received VerifyCode request", + zap.String("mobile", req.Mobile), + zap.String("scene", req.Scene), + ) + + token, err := service.VerifyCode(ctx, req.Mobile, req.Code) + if err != nil { + logger.Logger.Warn("VerifyCode failed", + zap.String("mobile", req.Mobile), + zap.Error(err), + ) + return &pb.VerifyCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: appErrors.ToStatusCode(err), + Message: err.Error(), + Timestamp: time.Now().UnixMilli(), + }, + Verified: false, + }, nil + } + + logger.Logger.Info("VerifyCode successful", + zap.String("mobile", req.Mobile), + ) + + return &pb.VerifyCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_OK, + Message: "验证成功", + Timestamp: time.Now().UnixMilli(), + }, + Verified: true, + VerifyToken: token, + ExpiresIn: 300, + }, nil +} diff --git a/backend/services/userService/provider/unified_provider.go b/backend/services/userService/provider/unified_provider.go index 075a34d..ee55a44 100644 --- a/backend/services/userService/provider/unified_provider.go +++ b/backend/services/userService/provider/unified_provider.go @@ -69,6 +69,16 @@ func (p *UnifiedProvider) CheckMobile(ctx context.Context, req *pb.CheckMobileRe return p.userProvider.CheckMobile(ctx, req) } +// SendCode 发送验证码 +func (p *UnifiedProvider) SendCode(ctx context.Context, req *pb.SendCodeRequest) (*pb.SendCodeResponse, error) { + return p.authProvider.SendCode(ctx, req) +} + +// VerifyCode 验证验证码 +func (p *UnifiedProvider) VerifyCode(ctx context.Context, req *pb.VerifyCodeRequest) (*pb.VerifyCodeResponse, error) { + return p.authProvider.VerifyCode(ctx, req) +} + // ==================== 用户信息相关方法(委托给 UserProvider)==================== // GetUser 获取用户信息 diff --git a/backend/services/userService/service/auth_service.go b/backend/services/userService/service/auth_service.go index e95968e..75d8108 100644 --- a/backend/services/userService/service/auth_service.go +++ b/backend/services/userService/service/auth_service.go @@ -1,6 +1,7 @@ package service import ( + "context" "errors" "fmt" "time" @@ -19,8 +20,8 @@ import ( // AuthService 认证Service接口 type AuthService interface { - // Register 注册 - Register(req *pb.RegisterRequest) (*pb.RegisterResponse, error) + // Register 注册(ctx用于验证verify_token) + Register(ctx context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) // Login 登录 Login(req *pb.LoginRequest) (*pb.LoginResponse, error) @@ -59,7 +60,17 @@ func NewAuthService( } // Register 用户注册 -func (s *authService) Register(req *pb.RegisterRequest) (*pb.RegisterResponse, error) { +func (s *authService) Register(ctx context.Context, req *pb.RegisterRequest) (*pb.RegisterResponse, error) { + // 0. 验证明ver_token(新增) + if req.VerifyToken != "" { + if err := VerifyToken(ctx, req.Mobile, req.VerifyToken); err != nil { + logger.Logger.Warn("Verify token validation failed", + zap.String("mobile", req.Mobile), + zap.Error(err)) + return nil, fmt.Errorf("invalid verify_token: %w", err) + } + } + // 1. 参数验证 if !validator.ValidateMobile(req.Mobile) { logger.Logger.Warn("Invalid mobile format", diff --git a/backend/services/userService/service/sms_redis.go b/backend/services/userService/service/sms_redis.go new file mode 100644 index 0000000..7d6fd1a --- /dev/null +++ b/backend/services/userService/service/sms_redis.go @@ -0,0 +1,297 @@ +package service + +import ( + "context" + "crypto/rand" + "fmt" + "math/big" + "time" + + "github.com/redis/go-redis/v9" + "github.com/topfans/backend/pkg/database" +) + +const ( + SMSCodeKeyPrefix = "sms:register:" // 验证码:sms:register:{mobile} + SMSVerifyTokenPrefix = "verify:register:" // 验证Token:verify:register:{mobile} + SMSLimitMobilePrefix = "sms:limit:mobile:" // 手机号频率:sms:limit:mobile:register:{mobile} + SMSLimitIPPrefix = "sms:limit:ip:send:" // IP频率:sms:limit:ip:send:{ip} + SMSBlacklistIPPrefix = "sms:blacklist:ip:" // IP黑名单:sms:blacklist:ip:{ip} + + MaxMobileAttemptsPerHour = 10 + MaxIPAttemptsPerHour = 30 +) + +type SMSCodeData struct { + Code string `json:"code"` + CreatedAt int64 `json:"created_at"` + Attempts int `json:"attempts"` + Used bool `json:"used"` +} + +// GenerateCode generates a 6-digit random SMS code +func GenerateCode() (string, error) { + code := make([]byte, 6) + for i := range code { + n, err := rand.Int(rand.Reader, big.NewInt(10)) + if err != nil { + return "", fmt.Errorf("failed to generate random number: %w", err) + } + code[i] = byte('0' + n.Int64()) + } + return string(code), nil +} + +// GetRedisClient returns the Redis client instance +func GetRedisClient() *redis.Client { + return database.GetRedis() +} + +// smsCodeKey generates the Redis key for SMS code +func smsCodeKey(mobile string) string { + return SMSCodeKeyPrefix + mobile +} + +// verifyTokenKey generates the Redis key for verify token +func verifyTokenKey(mobile string) string { + return SMSVerifyTokenPrefix + mobile +} + +// mobileLimitKey generates the Redis key for mobile rate limit +func mobileLimitKey(mobile string) string { + return SMSLimitMobilePrefix + mobile +} + +// ipLimitKey generates the Redis key for IP rate limit +func ipLimitKey(ip string) string { + return SMSLimitIPPrefix + ip +} + +// blacklistIPKey generates the Redis key for IP blacklist +func blacklistIPKey(ip string) string { + return SMSBlacklistIPPrefix + ip +} + +// SaveSMSCode saves the SMS code as a Hash with fields: code, created_at, attempts, used +func SaveSMSCode(ctx context.Context, mobile, code string, ttl time.Duration) error { + client := GetRedisClient() + if client == nil { + return fmt.Errorf("redis client is not initialized") + } + + key := smsCodeKey(mobile) + now := time.Now().Unix() + + pipe := client.Pipeline() + pipe.HSet(ctx, key, map[string]interface{}{ + "code": code, + "created_at": now, + "attempts": 0, + "used": false, + }) + pipe.Expire(ctx, key, ttl) + _, err := pipe.Exec(ctx) + return err +} + +// GetSMSCode retrieves the SMS code data for a mobile number +func GetSMSCode(ctx context.Context, mobile string) (*SMSCodeData, error) { + client := GetRedisClient() + if client == nil { + return nil, fmt.Errorf("redis client is not initialized") + } + + key := smsCodeKey(mobile) + data, err := client.HGetAll(ctx, key).Result() + if err != nil { + return nil, err + } + + if len(data) == 0 { + return nil, nil + } + + smsData := &SMSCodeData{} + + if code, ok := data["code"]; ok { + smsData.Code = code + } + if createdAt, ok := data["created_at"]; ok { + fmt.Sscanf(createdAt, "%d", &smsData.CreatedAt) + } + if attempts, ok := data["attempts"]; ok { + fmt.Sscanf(attempts, "%d", &smsData.Attempts) + } + if used, ok := data["used"]; ok { + smsData.Used = used == "true" || used == "1" + } + + return smsData, nil +} + +// DeleteSMSCode deletes the SMS code key +func DeleteSMSCode(ctx context.Context, mobile string) error { + client := GetRedisClient() + if client == nil { + return fmt.Errorf("redis client is not initialized") + } + + key := smsCodeKey(mobile) + return client.Del(ctx, key).Err() +} + +// IncrementAttempts increments the attempts counter and returns the new count +func IncrementAttempts(ctx context.Context, mobile string) (int, error) { + client := GetRedisClient() + if client == nil { + return 0, fmt.Errorf("redis client is not initialized") + } + + key := smsCodeKey(mobile) + count, err := client.HIncrBy(ctx, key, "attempts", 1).Result() + if err != nil { + return 0, err + } + return int(count), nil +} + +// MarkCodeUsed marks the SMS code as used +func MarkCodeUsed(ctx context.Context, mobile string) error { + client := GetRedisClient() + if client == nil { + return fmt.Errorf("redis client is not initialized") + } + + key := smsCodeKey(mobile) + return client.HSet(ctx, key, "used", "true").Err() +} + +// SaveVerifyToken saves the verify token as a String +func SaveVerifyToken(ctx context.Context, mobile, token string, ttl time.Duration) error { + client := GetRedisClient() + if client == nil { + return fmt.Errorf("redis client is not initialized") + } + + key := verifyTokenKey(mobile) + return client.Set(ctx, key, token, ttl).Err() +} + +// GetVerifyToken retrieves the verify token for a mobile number +func GetVerifyToken(ctx context.Context, mobile string) (string, error) { + client := GetRedisClient() + if client == nil { + return "", fmt.Errorf("redis client is not initialized") + } + + key := verifyTokenKey(mobile) + token, err := client.Get(ctx, key).Result() + if err == redis.Nil { + return "", nil + } + return token, err +} + +// DeleteVerifyToken deletes the verify token +func DeleteVerifyToken(ctx context.Context, mobile string) error { + client := GetRedisClient() + if client == nil { + return fmt.Errorf("redis client is not initialized") + } + + key := verifyTokenKey(mobile) + return client.Del(ctx, key).Err() +} + +// CheckRateLimitMobile checks if the mobile has sent less than 10 times in the current hour +func CheckRateLimitMobile(ctx context.Context, mobile string) (bool, error) { + client := GetRedisClient() + if client == nil { + return false, fmt.Errorf("redis client is not initialized") + } + + key := mobileLimitKey(mobile) + count, err := client.Get(ctx, key).Int() + if err == redis.Nil { + return true, nil + } + if err != nil { + return false, err + } + return count < MaxMobileAttemptsPerHour, nil +} + +// IncrMobileCount increments the mobile send count with 1 hour TTL +func IncrMobileCount(ctx context.Context, mobile string) (int, error) { + client := GetRedisClient() + if client == nil { + return 0, fmt.Errorf("redis client is not initialized") + } + + key := mobileLimitKey(mobile) + pipe := client.Pipeline() + + incr := pipe.Incr(ctx, key) + pipe.Expire(ctx, key, time.Hour) + + _, err := pipe.Exec(ctx) + if err != nil { + return 0, err + } + + return int(incr.Val()), nil +} + +// CheckRateLimitIP checks if the IP has sent less than 30 times in the current hour +func CheckRateLimitIP(ctx context.Context, ip string) (bool, error) { + client := GetRedisClient() + if client == nil { + return false, fmt.Errorf("redis client is not initialized") + } + + key := ipLimitKey(ip) + count, err := client.Get(ctx, key).Int() + if err == redis.Nil { + return true, nil + } + if err != nil { + return false, err + } + return count < MaxIPAttemptsPerHour, nil +} + +// IncrIPCount increments the IP send count with 1 hour TTL +func IncrIPCount(ctx context.Context, ip string) (int, error) { + client := GetRedisClient() + if client == nil { + return 0, fmt.Errorf("redis client is not initialized") + } + + key := ipLimitKey(ip) + pipe := client.Pipeline() + + incr := pipe.Incr(ctx, key) + pipe.Expire(ctx, key, time.Hour) + + _, err := pipe.Exec(ctx) + if err != nil { + return 0, err + } + + return int(incr.Val()), nil +} + +// CheckIPBlacklist checks if the IP is blacklisted +func CheckIPBlacklist(ctx context.Context, ip string) (bool, error) { + client := GetRedisClient() + if client == nil { + return false, fmt.Errorf("redis client is not initialized") + } + + key := blacklistIPKey(ip) + exists, err := client.Exists(ctx, key).Result() + if err != nil { + return false, err + } + return exists > 0, nil +} \ No newline at end of file diff --git a/backend/services/userService/service/sms_service.go b/backend/services/userService/service/sms_service.go new file mode 100644 index 0000000..e92d4db --- /dev/null +++ b/backend/services/userService/service/sms_service.go @@ -0,0 +1,262 @@ +package service + +import ( + "context" + "crypto/rand" + "errors" + "fmt" + "math/big" + "time" + + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20180501/v2/client" + "github.com/alibabacloud-go/darabonba-openapi/v2/client" + "github.com/alibabacloud-go/tea/tea" + "github.com/topfans/backend/pkg/logger" + "github.com/topfans/backend/services/userService/config" + "go.uber.org/zap" +) + +var smsClient *dysmsapi20170525.Client + +// InitSMSClient initializes the SMS client as a singleton +func InitSMSClient() error { + cfg := config.GetSMSConfig() + + // If credentials are not set, skip initialization + if cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" { + logger.Logger.Warn("SMS credentials not configured, SMS client not initialized") + return nil + } + + openapiConfig := &client.Config{ + AccessKeyId: tea.String(cfg.AccessKeyID), + AccessKeySecret: tea.String(cfg.AccessKeySecret), + } + openapiConfig.Endpoint = tea.String("dysmsapi.aliyuncs.com") + + var err error + smsClient, err = dysmsapi20170525.NewClient(openapiConfig) + if err != nil { + return fmt.Errorf("failed to initialize SMS client: %w", err) + } + + logger.Logger.Info("SMS client initialized successfully") + return nil +} + +// generateVerifyToken generates a verify token with format "vtf_" + 29 random chars +func generateVerifyToken() (string, error) { + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + const tokenLen = 29 + + b := make([]byte, tokenLen) + charsLen := big.NewInt(int64(len(charset))) + + for i := range b { + n, err := rand.Int(rand.Reader, charsLen) + if err != nil { + return "", fmt.Errorf("failed to generate random token: %w", err) + } + b[i] = charset[n.Int64()] + } + + return "vtf_" + string(b), nil +} + +// maskMobile masks the middle digits of a mobile number +// Example: 13812345678 -> 138****5678 +func maskMobile(mobile string) string { + if len(mobile) < 7 { + return mobile + } + return mobile[:3] + "****" + mobile[len(mobile)-4:] +} + +// SendVerificationCode sends a verification code to the given mobile number +func SendVerificationCode(ctx context.Context, mobile, ip string) (int, error) { + logger := logger.Logger.With(zap.String("mobile", maskMobile(mobile)), zap.String("ip", ip)) + + // Step 1: Check IP blacklist + blacklisted, err := CheckIPBlacklist(ctx, ip) + if err != nil { + logger.Error("Failed to check IP blacklist", zap.Error(err)) + return 0, fmt.Errorf("failed to check IP blacklist: %w", err) + } + if blacklisted { + logger.Warn("IP is blacklisted") + return 0, errors.New("IP黑名单") + } + + // Step 2: Check if code was recently sent (TTL check via GetSMSCode) + smsData, err := GetSMSCode(ctx, mobile) + if err != nil { + logger.Error("Failed to get SMS code from Redis", zap.Error(err)) + return 0, fmt.Errorf("failed to check recent SMS: %w", err) + } + if smsData != nil { + elapsed := time.Now().Unix() - smsData.CreatedAt + if elapsed < 60 { + logger.Warn("SMS code recently sent", zap.Int64("elapsed_seconds", elapsed)) + return 0, errors.New("发送过于频繁") + } + } + + // Step 3: Check mobile hourly limit (10/hour) + allowed, err := CheckRateLimitMobile(ctx, mobile) + if err != nil { + logger.Error("Failed to check mobile rate limit", zap.Error(err)) + return 0, fmt.Errorf("failed to check mobile rate limit: %w", err) + } + if !allowed { + logger.Warn("Mobile hourly limit exceeded") + return 0, errors.New("当前手机号发送次数超限") + } + + // Step 4: Check IP hourly limit (30/hour) + allowed, err = CheckRateLimitIP(ctx, ip) + if err != nil { + logger.Error("Failed to check IP rate limit", zap.Error(err)) + return 0, fmt.Errorf("failed to check IP rate limit: %w", err) + } + if !allowed { + logger.Warn("IP hourly limit exceeded") + return 0, errors.New("请求过于频繁") + } + + // Step 5: Generate 6-digit code + code, err := GenerateCode() + if err != nil { + logger.Error("Failed to generate SMS code", zap.Error(err)) + return 0, fmt.Errorf("failed to generate code: %w", err) + } + + // Step 6: Call Aliyun SMS API to send (if client is initialized) + if smsClient != nil { + cfg := config.GetSMSConfig() + sendReq := &dysmsapi20170525.SendMessageWithTemplateRequest{ + To: tea.String("86" + mobile), // China area code + mobile + From: tea.String(cfg.SignName), + TemplateCode: tea.String(cfg.TemplateCode), + TemplateParam: tea.String(fmt.Sprintf(`{"code":"%s"}`, code)), + } + + _, err := smsClient.SendMessageWithTemplate(sendReq) + if err != nil { + logger.Error("Failed to send SMS via Aliyun", zap.Error(err)) + return 0, fmt.Errorf("failed to send SMS: %w", err) + } + + logger.Info("SMS sent successfully via Aliyun") + } else { + logger.Warn("SMS client not initialized, skipping actual SMS send") + } + + // Step 7: Save code to Redis with 60s TTL + err = SaveSMSCode(ctx, mobile, code, 60*time.Second) + if err != nil { + logger.Error("Failed to save SMS code to Redis", zap.Error(err)) + return 0, fmt.Errorf("failed to save code: %w", err) + } + + // Step 8: Increment mobile and IP counters + _, err = IncrMobileCount(ctx, mobile) + if err != nil { + logger.Error("Failed to increment mobile count", zap.Error(err)) + } + + _, err = IncrIPCount(ctx, ip) + if err != nil { + logger.Error("Failed to increment IP count", zap.Error(err)) + } + + logger.Info("Verification code sent successfully") + return 60, nil +} + +// VerifyCode verifies the SMS code and returns a verify token on success +func VerifyCode(ctx context.Context, mobile, code string) (string, error) { + logger := logger.Logger.With(zap.String("mobile", maskMobile(mobile))) + + // Step 1: Get stored code data + smsData, err := GetSMSCode(ctx, mobile) + if err != nil { + logger.Error("Failed to get SMS code from Redis", zap.Error(err)) + return "", fmt.Errorf("failed to get SMS code: %w", err) + } + if smsData == nil { + logger.Warn("No SMS code found for mobile") + return "", errors.New("验证码不存在或已过期") + } + + // Step 2: Check if already used + if smsData.Used { + logger.Warn("SMS code already used") + return "", errors.New("验证码已使用") + } + + // Step 3: Compare codes + if smsData.Code != code { + attempts, _ := IncrementAttempts(ctx, mobile) + logger.Warn("SMS code mismatch", zap.Int("attempts", attempts)) + + // Delete code if attempts >= 3 + if attempts >= 3 { + DeleteSMSCode(ctx, mobile) + logger.Info("SMS code deleted due to max attempts") + return "", errors.New("验证失败次数过多,请重新获取验证码") + } + return "", errors.New("验证码错误") + } + + // Step 4: On success - delete code, generate verify_token, save with 300s TTL + if err := DeleteSMSCode(ctx, mobile); err != nil { + logger.Error("Failed to delete SMS code", zap.Error(err)) + } + + verifyToken, err := generateVerifyToken() + if err != nil { + logger.Error("Failed to generate verify token", zap.Error(err)) + return "", fmt.Errorf("failed to generate verify token: %w", err) + } + + err = SaveVerifyToken(ctx, mobile, verifyToken, 300*time.Second) + if err != nil { + logger.Error("Failed to save verify token", zap.Error(err)) + return "", fmt.Errorf("failed to save verify token: %w", err) + } + + logger.Info("SMS code verified successfully, verify token issued") + return verifyToken, nil +} + +// VerifyToken verifies the verify_token during registration (one-time use) +func VerifyToken(ctx context.Context, mobile, token string) error { + logger := logger.Logger.With(zap.String("mobile", maskMobile(mobile))) + + // Step 1: Get stored token from Redis + storedToken, err := GetVerifyToken(ctx, mobile) + if err != nil { + logger.Error("Failed to get verify token from Redis", zap.Error(err)) + return fmt.Errorf("failed to get verify token: %w", err) + } + + // Step 2: Compare with provided token + if storedToken == "" { + logger.Warn("No verify token found for mobile") + return errors.New("验证令牌不存在或已过期") + } + + if storedToken != token { + logger.Warn("Verify token mismatch") + return errors.New("验证令牌无效") + } + + // Step 3: If match, delete the token (one-time use) + if err := DeleteVerifyToken(ctx, mobile); err != nil { + logger.Error("Failed to delete verify token", zap.Error(err)) + return fmt.Errorf("failed to delete verify token: %w", err) + } + + logger.Info("Verify token validated and consumed successfully") + return nil +} \ No newline at end of file diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml index 3eb97ab..ad8b523 100644 --- a/docker/docker-compose.local.yml +++ b/docker/docker-compose.local.yml @@ -37,6 +37,9 @@ services: environment: <<: *common-env PORT: 20000 + REDIS_HOST: host.docker.internal + REDIS_PORT: 6379 + REDIS_DB: 0 extra_hosts: - "host.docker.internal:host-gateway" networks: diff --git a/docker/sql/migrations/migrate_create_sms_send_log_table.sql b/docker/sql/migrations/migrate_create_sms_send_log_table.sql new file mode 100644 index 0000000..9d333a1 --- /dev/null +++ b/docker/sql/migrations/migrate_create_sms_send_log_table.sql @@ -0,0 +1,35 @@ +-- ============================================================================ +-- SMS发送日志表 (sms_send_log) +-- ============================================================================ +-- 用途:记录每次短信发送情况,用于资源核算和成本分析 +-- 业务场景: +-- - 用户注册时的手机号验证 (scene: register) +-- - 忘记密码时的手机号验证 (scene: password) +-- 相关服务:userService (短信验证码功能) +-- 维护注意事项: +-- - mobile 字段脱敏存储(前端只显示138****5678格式) +-- - 验证码内容不记录,只记录发送状态 +-- - 索引:mobile(查询), scene(统计), send_time(报表) +-- ============================================================================ + +-- SMS发送日志表 +-- 用于记录每次短信发送情况,便于资源核算和成本分析 +CREATE TABLE IF NOT EXISTS sms_send_log ( + id BIGSERIAL PRIMARY KEY, + mobile VARCHAR(20) NOT NULL, + scene VARCHAR(20) NOT NULL DEFAULT 'register', + template_code VARCHAR(50) NOT NULL, + sign_name VARCHAR(50) NOT NULL, + message_id VARCHAR(64) DEFAULT '', + response_code VARCHAR(20) DEFAULT '', + response_description VARCHAR(255) DEFAULT '', + status SMALLINT NOT NULL DEFAULT 1, + send_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 索引 +CREATE INDEX idx_sms_send_log_mobile ON sms_send_log(mobile); +CREATE INDEX idx_sms_send_log_scene ON sms_send_log(scene); +CREATE INDEX idx_sms_send_log_send_time ON sms_send_log(send_time); diff --git a/docs/superpowers/plans/2026-05-26-sms-register-implementation.md b/docs/superpowers/plans/2026-05-26-sms-register-implementation.md new file mode 100644 index 0000000..de8f777 --- /dev/null +++ b/docs/superpowers/plans/2026-05-26-sms-register-implementation.md @@ -0,0 +1,1140 @@ +# 注册短信验证码功能实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 在用户注册流程中加入阿里云短信验证码验证,确保手机号真实有效,防止恶意注册。 + +**Architecture:** +- 采用方案一(userService直接集成):前端 → 网关 → userService → 阿里云SMS/Redis +- 注册验证码存储在Redis(Hash结构,60秒TTL) +- 验证成功后生成verify_token存入Redis(300秒TTL),注册时校验 +- 限流策略:60秒内不能重复发送,每小时最多10次,验证失败3次强制删除验证码 + +**Tech Stack:** Go (Dubbo-go), Redis, 阿里云SMS SDK, Vue/uni-app + +--- + +## 文件结构 + +### 后端改动 + +**新增文件:** +- `backend/services/userService/config/sms_config.go` - SMS配置结构体 +- `backend/services/userService/service/sms_service.go` - 短信发送服务 +- `backend/services/userService/pkg/redis/sms_redis.go` - Redis操作(验证码存储、限流) +- `backend/pkg/proto/user/auth.pb.go` - Proto生成文件(新增SendCode/VerifyCode方法) + +**修改文件:** +- `backend/services/userService/main.go` - 初始化SMS配置和依赖 +- `backend/services/userService/service/auth_service.go` - 添加verify_token校验逻辑 +- `backend/services/userService/service/auth_service.go:62-96` - Register方法需增加verify_token验证 +- `backend/services/userService/provider/auth_provider.go` - 实现SendCode/VerifyCode的RPC处理 +- `backend/services/userService/provider/unified_provider.go` - 注册新方法到Dubbo + +**网关改动:** +- `backend/gateway/controller/auth_controller.go` - 添加send-code/verify-code HTTP端点 +- `backend/gateway/dto/auth_dto.go` - 添加请求/响应DTO +- `backend/gateway/router/` - 路由配置 + +**数据库:** +- `backend/migrations/` - 新增 `sms_send_log` 表 + +### 前端改动 + +**修改文件:** +- `frontend/pages/register/register.vue` - 添加短信验证码交互流程 +- `frontend/utils/api.js` - 添加send-code/verify-code API + +--- + +## 阶段一:后端基础设施 + +### Task 1: 添加阿里云SMS SDK依赖 + +**Files:** +- Modify: `backend/services/userService/go.mod` + +- [ ] **Step 1: 添加阿里云SMS SDK依赖** + +```bash +cd /Users/liulujian/Documents/code/TopFansByGithub/backend/services/userService +go get github.com/alibabacloud-go/dysmsapi-20180501/v2/client +go get github.com/alibabacloud-go/darabonba-openapi/v2/client +go get github.com/alibabacloud-go/tea/tea +go get github.com/alibabacloud-go/tea-utils/v2/service +``` + +- [ ] **Step 2: 验证依赖安装** + +```bash +cd /Users/liulujian/Documents/code/TopFansByGithub/backend/services/userService +go mod tidy +go build -o userService . +``` + +--- + +### Task 2: 创建SMS配置 + +**Files:** +- Create: `backend/services/userService/config/sms_config.go` + +- [ ] **Step 1: 编写SMSConfig配置结构体** + +```go +package config + +// SMSConfig 短信配置 +type SMSConfig struct { + AccessKeyID string + AccessKeySecret string + SignName string // 短信签名 + TemplateCode string // 短信模板CODE + Region string // 区域(默认 cn-hangzhou) +} + +// GetSMSConfig 获取SMS配置(从环境变量) +func GetSMSConfig() *SMSConfig { + return &SMSConfig{ + AccessKeyID: getEnv("SMS_ACCESS_KEY_ID", ""), + AccessKeySecret: getEnv("SMS_ACCESS_KEY_SECRET", ""), + SignName: getEnv("SMS_SIGN_NAME", "TopFans"), + TemplateCode: getEnv("SMS_TEMPLATE_CODE", ""), + Region: getEnv("SMS_REGION", "cn-hangzhou"), + } +} + +func getEnv(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add backend/services/userService/config/sms_config.go +git commit -m "feat: add SMS config structure" +``` + +--- + +### Task 3: 创建Redis操作层(验证码存储+限流) + +**Files:** +- Create: `backend/services/userService/pkg/redis/sms_redis.go` + +- [ ] **Step 1: 编写Redis操作函数** + +```go +package redis + +import ( + "context" + "fmt" + "math/rand" + "strconv" + "time" + + "github.com/redis/go-redis/v9" + "github.com/topfans/backend/pkg/logger" +) + +// SMS Redis Key 前缀 +const ( + SMSCodeKeyPrefix = "sms:register:" // 验证码:sms:register:{mobile} + SMSVerifyTokenPrefix = "verify:register:" // 验证Token:verify:register:{mobile} + SMSLimitMobilePrefix = "sms:limit:mobile:" // 手机号频率:sms:limit:mobile:register:{mobile} + SMSLimitIPPrefix = "sms:limit:ip:send:" // IP频率:sms:limit:ip:send:{ip} + SMSBlacklistIPPrefix = "sms:blacklist:ip:" // IP黑名单:sms:blacklist:ip:{ip} +) + +// SMSCodeData 验证码数据结构 +type SMSCodeData struct { + Code string `json:"code"` + CreatedAt int64 `json:"created_at"` + Attempts int `json:"attempts"` + Used bool `json:"used"` +} + +// GenerateCode 生成6位数字验证码 +func GenerateCode() string { + rand.Seed(time.Now().UnixNano()) + code := rand.Intn(900000) + 100000 // 100000-999999 + return strconv.Itoa(code) +} + +// SaveSMSCode 保存验证码到Redis +func SaveSMSCode(ctx context.Context, mobile, code string, ttl time.Duration) error { + key := SMSCodeKeyPrefix + mobile + data := &SMSCodeData{ + Code: code, + CreatedAt: time.Now().Unix(), + Attempts: 0, + Used: false, + } + // 使用Hash存储 + err := GetRedisClient().HSet(ctx, key, map[string]interface{}{ + "code": data.Code, + "created_at": data.CreatedAt, + "attempts": data.Attempts, + "used": data.Used, + }).Err() + if err != nil { + return err + } + return GetRedisClient().Expire(ctx, key, ttl).Err() +} + +// GetSMSCode 获取验证码数据 +func GetSMSCode(ctx context.Context, mobile string) (*SMSCodeData, error) { + key := SMSCodeKeyPrefix + mobile + result, err := GetRedisClient().HGetAll(ctx, key).Result() + if err != nil { + return nil, err + } + if len(result) == 0 { + return nil, fmt.Errorf("验证码不存在或已过期") + } + attempts, _ := strconv.Atoi(result["attempts"]) + return &SMSCodeData{ + Code: result["code"], + CreatedAt: parseInt64(result["created_at"]), + Attempts: attempts, + Used: result["used"] == "true", + }, nil +} + +// DeleteSMSCode 删除验证码 +func DeleteSMSCode(ctx context.Context, mobile string) error { + key := SMSCodeKeyPrefix + mobile + return GetRedisClient().Del(ctx, key).Err() +} + +// IncrementAttempts 递增验证失败次数 +func IncrementAttempts(ctx context.Context, mobile string) (int, error) { + key := SMSCodeKeyPrefix + mobile + count, err := GetRedisClient().HIncrBy(ctx, key, "attempts", 1).Result() + return int(count), err +} + +// MarkCodeUsed 标记验证码已使用 +func MarkCodeUsed(ctx context.Context, mobile string) error { + key := SMSCodeKeyPrefix + mobile + return GetRedisClient().HSet(ctx, key, "used", "true").Err() +} + +// SaveVerifyToken 保存验证Token +func SaveVerifyToken(ctx context.Context, mobile, token string, ttl time.Duration) error { + key := SMSVerifyTokenPrefix + mobile + return GetRedisClient().Set(ctx, key, token, ttl).Err() +} + +// GetVerifyToken 获取验证Token +func GetVerifyToken(ctx context.Context, mobile string) (string, error) { + key := SMSVerifyTokenPrefix + mobile + return GetRedisClient().Get(ctx, key).Result() +} + +// DeleteVerifyToken 删除验证Token +func DeleteVerifyToken(ctx context.Context, mobile string) error { + key := SMSVerifyTokenPrefix + mobile + return GetRedisClient().Del(ctx, key).Err() +} + +// CheckRateLimitMobile 检查手机号发送频率限制 +func CheckRateLimitMobile(ctx context.Context, mobile string) (bool, int, error) { + key := SMSLimitMobilePrefix + mobile + count, err := GetRedisClient().Get(ctx, key).Result() + if err == redis.Nil { + return true, 0, nil // 没有限制记录 + } + if err != nil { + return false, 0, err + } + limitCount, _ := strconv.Atoi(count) + return limitCount < 10, limitCount, nil // 每小时最多10次 +} + +// IncrMobileCount 增加手机号发送计数 +func IncrMobileCount(ctx context.Context, mobile string) error { + key := SMSLimitMobilePrefix + mobile + pipe := GetRedisClient().Pipeline() + pipe.Incr(ctx, key) + pipe.Expire(ctx, key, time.Hour) // 1小时后过期 + _, err := pipe.Exec(ctx) + return err +} + +// CheckRateLimitIP 检查IP发送频率限制 +func CheckRateLimitIP(ctx context.Context, ip string) (bool, int, error) { + key := SMSLimitIPPrefix + ip + count, err := GetRedisClient().Get(ctx, key).Result() + if err == redis.Nil { + return true, 0, nil + } + if err != nil { + return false, 0, err + } + limitCount, _ := strconv.Atoi(count) + return limitCount < 30, limitCount, nil // 每小时最多30次 +} + +// IncrIPCount 增加IP发送计数 +func IncrIPCount(ctx context.Context, ip string) error { + key := SMSLimitIPPrefix + ip + pipe := GetRedisClient().Pipeline() + pipe.Incr(ctx, key) + pipe.Expire(ctx, key, time.Hour) + _, err := pipe.Exec(ctx) + return err +} + +// CheckIPBlacklist 检查IP是否在黑名单 +func CheckIPBlacklist(ctx context.Context, ip string) (bool, error) { + key := SMSBlacklistIPPrefix + ip + exists, err := GetRedisClient().Exists(ctx, key).Result() + return exists > 0, err +} + +func parseInt64(s string) int64 { + v, _ := strconv.ParseInt(s, 10, 64) + return v +} +``` + +- [ ] **Step 2: 检查是否已有Redis客户端实现** + +```bash +grep -r "go-redis" /Users/liulujian/Documents/code/TopFansByGithub/backend/services/userService/ +``` + +如果没有,需要添加依赖并创建Redis客户端初始化代码。 + +- [ ] **Step 3: Commit** + +```bash +git add backend/services/userService/pkg/redis/sms_redis.go +git commit -m "feat: add SMS Redis operations for code storage and rate limiting" +``` + +--- + +### Task 4: 创建SMS服务 + +**Files:** +- Create: `backend/services/userService/service/sms_service.go` + +- [ ] **Step 1: 编写SMS服务** + +```go +package service + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20180501/v2/client" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/darabonba-openapi/v2/client" + "github.com/alibabacloud-go/tea/tea" + "github.com/topfans/backend/pkg/logger" + "github.com/topfans/backend/services/userService/config" + "github.com/topfans/backend/services/userService/pkg/redis" + "go.uber.org/zap" +) + +var smsClient *dysmsapi20170525.Client + +// InitSMSClient 初始化SMS客户端(单例模式) +func InitSMSClient() error { + cfg := config.GetSMSConfig() + if cfg.AccessKeyID == "" || cfg.AccessKeySecret == "" { + logger.Logger.Warn("SMS credentials not configured, SMS service disabled") + return nil + } + + openapiConfig := &client.Config{ + AccessKeyId: tea.String(cfg.AccessKeyID), + AccessKeySecret: tea.String(cfg.AccessKeySecret), + } + openapiConfig.Endpoint = tea.String("dysmsapi.aliyuncs.com") + + var err error + smsClient, err = dysmsapi20170525.NewClient(openapiConfig) + if err != nil { + return fmt.Errorf("failed to create SMS client: %w", err) + } + + logger.Logger.Info("SMS client initialized successfully") + return nil +} + +// SendVerificationCode 发送注册验证码 +func SendVerificationCode(ctx context.Context, mobile, ip string) (int, error) { + // 1. 检查IP黑名单 + isBlacklisted, err := redis.CheckIPBlacklist(ctx, ip) + if err != nil { + logger.Logger.Error("Failed to check IP blacklist", zap.Error(err)) + } + if isBlacklisted { + return 0, fmt.Errorf("暂时无法操作,请稍后再试") + } + + // 2. 检查手机号发送频率(60秒内不能重复发送) + // 这里用Redis TTL来检查:如果sms:register:{mobile}存在,说明60秒内发过 + existingCode, _ := redis.GetSMSCode(ctx, mobile) + if existingCode != nil { + return 0, fmt.Errorf("发送过于频繁,请稍后再试") + } + + // 3. 检查每小时发送次数限制 + allowed, count, err := redis.CheckRateLimitMobile(ctx, mobile) + if err != nil { + logger.Logger.Error("Failed to check mobile rate limit", zap.Error(err)) + } + if !allowed { + return 0, fmt.Errorf("当前手机号发送次数超限,请稍后再试") + } + + // 4. 检查IP发送频率 + allowed, _, err = redis.CheckRateLimitIP(ctx, ip) + if err != nil { + logger.Logger.Error("Failed to check IP rate limit", zap.Error(err)) + } + if !allowed { + return 0, fmt.Errorf("请求过于频繁,请稍后再试") + } + + // 5. 生成6位验证码 + code := redis.GenerateCode() + + // 6. 发送短信 + if smsClient != nil { + cfg := config.GetSMSConfig() + request := &dysmsapi20170525.SendMessageWithTemplateRequest{ + ToNumber: tea.String("86" + mobile), // 中国区号+手机号 + FromNumber: tea.String(cfg.SignName), + TemplateCode: tea.String(cfg.TemplateCode), + TemplateParam: tea.String(fmt.Sprintf(`{"code":"%s"}`, code)), + } + + runtime := &util.RuntimeOptions{} + response, err := smsClient.SendMessageWithTemplateWithOptions(request, runtime) + if err != nil { + logger.Logger.Error("Failed to send SMS", + zap.String("mobile", maskMobile(mobile)), + zap.Error(err)) + return 0, fmt.Errorf("服务暂不可用,请稍后重试") + } + + if response.Body.ResponseCode != nil && *response.Body.ResponseCode != "OK" { + logger.Logger.Error("SMS API returned error", + zap.String("code", *response.Body.ResponseCode), + zap.String("description", *response.Body.ResponseDescription)) + return 0, fmt.Errorf("发送失败,请稍后重试") + } + + logger.Logger.Info("SMS sent successfully", + zap.String("mobile", maskMobile(mobile)), + zap.String("message_id", tea.StringValue(response.Body.MessageId))) + } + + // 7. 存储验证码到Redis(60秒TTL) + if err := redis.SaveSMSCode(ctx, mobile, code, 60*time.Second); err != nil { + logger.Logger.Error("Failed to save SMS code to Redis", + zap.String("mobile", maskMobile(mobile)), + zap.Error(err)) + return 0, fmt.Errorf("服务暂不可用,请稍后重试") + } + + // 8. 增加发送计数 + redis.IncrMobileCount(ctx, mobile) + redis.IncrIPCount(ctx, ip) + + return 60, nil // 返回60秒后可以重发 +} + +// VerifyCode 验证验证码 +func VerifyCode(ctx context.Context, mobile, code string) (string, error) { + // 1. 获取存储的验证码 + storedData, err := redis.GetSMSCode(ctx, mobile) + if err != nil { + return "", fmt.Errorf("验证码已过期,请重新获取") + } + + // 2. 检查是否已使用 + if storedData.Used { + return "", fmt.Errorf("验证码已使用,请重新获取") + } + + // 3. 验证code是否正确 + if storedData.Code != code { + // 验证失败,递增失败次数 + attempts, _ := redis.IncrementAttempts(ctx, mobile) + if attempts >= 3 { + // 失败3次,删除验证码 + redis.DeleteSMSCode(ctx, mobile) + return "", fmt.Errorf("验证失败次数过多,请重新获取") + } + remaining := 3 - attempts + return "", fmt.Errorf("验证码错误,剩余%d次", remaining) + } + + // 4. 验证成功,删除验证码 + redis.DeleteSMSCode(ctx, mobile) + + // 5. 生成verify_token + token := generateVerifyToken() + + // 6. 存储verify_token(300秒TTL) + if err := redis.SaveVerifyToken(ctx, mobile, token, 300*time.Second); err != nil { + logger.Logger.Error("Failed to save verify token", + zap.String("mobile", maskMobile(mobile)), + zap.Error(err)) + return "", fmt.Errorf("验证服务暂不可用") + } + + logger.Logger.Info("SMS code verified successfully", + zap.String("mobile", maskMobile(mobile))) + + return token, nil +} + +// VerifyToken 验证verify_token(注册时调用) +func VerifyToken(ctx context.Context, mobile, token string) error { + storedToken, err := redis.GetVerifyToken(ctx, mobile) + if err != nil { + return fmt.Errorf("验证码已失效,请重新获取") + } + if storedToken != token { + return fmt.Errorf("验证失败,请重新获取验证码") + } + // 验证成功后删除token + redis.DeleteVerifyToken(ctx, mobile) + return nil +} + +// generateVerifyToken 生成verify_token +func generateVerifyToken() string { + const prefix = "vtf_" + const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + rand.Seed(time.Now().UnixNano()) + b := make([]byte, 29) + for i := range b { + b[i] = chars[rand.Intn(len(chars))] + } + return prefix + string(b) +} + +// maskMobile 手机号脱敏 +func maskMobile(mobile string) string { + if len(mobile) < 11 { + return mobile + } + return mobile[:3] + "****" + mobile[7:] +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add backend/services/userService/service/sms_service.go +git commit -m "feat: add SMS service for sending and verifying codes" +``` + +--- + +### Task 5: 在AuthService中集成verify_token校验 + +**Files:** +- Modify: `backend/services/userService/service/auth_service.go` + +- [ ] **Step 1: 在Register方法开头添加verify_token校验** + +在 `Register` 方法的参数验证之后、手机号重复检查之前,添加: + +```go +// 在Register方法中,参数验证之后,添加: + +// 3. 验证明ver_token(新增) +if req.VerifyToken != "" { + if err := VerifyToken(ctx, req.Mobile, req.VerifyToken); err != nil { + logger.Logger.Warn("Verify token validation failed", + zap.String("mobile", req.Mobile), + zap.Error(err)) + return nil, fmt.Errorf("invalid verify_token: %w", err) + } +} +``` + +需要修改Register方法签名添加context参数。 + +- [ ] **Step 2: Commit** + +```bash +git add backend/services/userService/service/auth_service.go +git commit -m "feat: add verify_token validation in Register method" +``` + +--- + +### Task 6: 添加Proto定义(SendCode/VerifyCode方法) + +**Files:** +- Modify: `backend/pkg/proto/user/auth.proto`(如果存在) +- 或查看现有的proto文件结构 + +- [ ] **Step 1: 查看现有proto文件** + +```bash +ls -la /Users/liulujian/Documents/code/TopFansByGithub/backend/pkg/proto/user/ +cat /Users/liulujian/Documents/code/TopFansByGithub/backend/pkg/proto/user/auth.proto +``` + +- [ ] **Step 2: 添加新的RPC方法定义** + +```protobuf +// 发送验证码请求 +message SendCodeRequest { + string mobile = 1; + string scene = 2; // register, password +} + +// 发送验证码响应 +message SendCodeResponse { + common.BaseResponse base = 1; + int32 expires_in = 2; // 多少秒后可以重发 +} + +// 验证验证码请求 +message VerifyCodeRequest { + string mobile = 1; + string code = 2; + string scene = 3; +} + +// 验证验证码响应 +message VerifyCodeResponse { + common.BaseResponse base = 1; + bool verified = 2; + string verify_token = 3; + int32 expires_in = 4; // token有效期,秒 +} +``` + +- [ ] **Step 3: 重新生成pb.go文件** + +根据项目中的生成脚本生成go文件。 + +--- + +### Task 7: 实现AuthProvider的SendCode/VerifyCode + +**Files:** +- Modify: `backend/services/userService/provider/auth_provider.go` + +- [ ] **Step 1: 添加SendCode和VerifyCode处理方法** + +```go +// SendCode 发送验证码 +func (p *AuthProvider) SendCode(ctx context.Context, req *pb.SendCodeRequest) (*pb.SendCodeResponse, error) { + // 获取客户端IP + ip := getClientIP(ctx) + + expiresIn, err := service.SendVerificationCode(ctx, req.Mobile, ip) + if err != nil { + return &pb.SendCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_BAD_REQUEST, + Message: err.Error(), + Timestamp: time.Now().UnixMilli(), + }, + }, nil + } + + return &pb.SendCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_OK, + Message: "发送成功", + Timestamp: time.Now().UnixMilli(), + }, + ExpiresIn: int32(expiresIn), + }, nil +} + +// VerifyCode 验证验证码 +func (p *AuthProvider) VerifyCode(ctx context.Context, req *pb.VerifyCodeRequest) (*pb.VerifyCodeResponse, error) { + token, err := service.VerifyCode(ctx, req.Mobile, req.Code) + if err != nil { + return &pb.VerifyCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_BAD_REQUEST, + Message: err.Error(), + Timestamp: time.Now().UnixMilli(), + }, + Verified: false, + }, nil + } + + return &pb.VerifyCodeResponse{ + Base: &pbCommon.BaseResponse{ + Code: pbCommon.StatusCode_STATUS_OK, + Message: "验证成功", + Timestamp: time.Now().UnixMilli(), + }, + Verified: true, + VerifyToken: token, + ExpiresIn: 300, + }, nil +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add backend/services/userService/provider/auth_provider.go +git commit -m "feat: implement SendCode and VerifyCode in AuthProvider" +``` + +--- + +### Task 8: 网关层添加HTTP端点 + +**Files:** +- Modify: `backend/gateway/controller/auth_controller.go` +- Modify: `backend/gateway/dto/auth_dto.go` +- Modify: `backend/gateway/router/`(路由配置) + +- [ ] **Step 1: 添加DTO定义** + +```go +// SendCodeRequest 发送验证码请求 +type SendCodeRequest struct { + Mobile string `json:"mobile" form:"mobile"` + Scene string `json:"scene" form:"scene"` // register, password +} + +// SendCodeResponse 发送验证码响应 +type SendCodeResponse struct { + Code int `json:"code"` + Message string `json:"message"` + ExpiresIn int `json:"expires_in"` +} + +// VerifyCodeRequest 验证验证码请求 +type VerifyCodeRequest struct { + Mobile string `json:"mobile" form:"mobile"` + Code string `json:"code" form:"code"` + Scene string `json:"scene" form:"scene"` +} + +// VerifyCodeResponse 验证验证码响应 +type VerifyCodeResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Verified bool `json:"verified"` + VerifyToken string `json:"verify_token"` + ExpiresIn int `json:"expires_in"` +} +``` + +- [ ] **Step 2: 添加Controller处理函数** + +在auth_controller.go中添加: + +```go +// SendCode 发送验证码 +func SendCode(c *gin.Context) { + var req dto.SendCodeRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"code": 400, "message": "参数错误"}) + return + } + + // 调用Dubbo服务 + resp, err := dubboClient.SendCode(context.Background(), &userPB.SendCodeRequest{ + Mobile: req.Mobile, + Scene: req.Scene, + }) + if err != nil { + c.JSON(500, gin.H{"code": 500, "message": err.Error()}) + return + } + + c.JSON(200, gin.H{ + "code": 200, + "message": resp.Base.Message, + "expires_in": resp.ExpiresIn, + }) +} + +// VerifyCode 验证验证码 +func VerifyCode(c *gin.Context) { + var req dto.VerifyCodeRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"code": 400, "message": "参数错误"}) + return + } + + resp, err := dubboClient.VerifyCode(context.Background(), &userPB.VerifyCodeRequest{ + Mobile: req.Mobile, + Code: req.Code, + Scene: req.Scene, + }) + if err != nil { + c.JSON(500, gin.H{"code": 500, "message": err.Error()}) + return + } + + c.JSON(200, gin.H{ + "code": 200, + "message": resp.Base.Message, + "verified": resp.Verified, + "verify_token": resp.VerifyToken, + "expires_in": resp.ExpiresIn, + }) +} +``` + +- [ ] **Step 3: 添加路由** + +在router中注册新路由: + +```go +auth := r.Group("/api/v1/auth") +{ + auth.POST("/send-code", SendCode) + auth.POST("/verify-code", VerifyCode) +} +``` + +- [ ] **Step 4: Commit** + +```bash +git add backend/gateway/controller/auth_controller.go backend/gateway/dto/auth_dto.go +git commit -m "feat: add send-code and verify-code HTTP endpoints in gateway" +``` + +--- + +## 阶段二:前端改动 + +### Task 9: 修改前端register.vue添加短信验证流程 + +**Files:** +- Modify: `frontend/pages/register/register.vue` + +- [ ] **Step 1: 添加验证码相关的响应式数据** + +```javascript +const form = ref({ + phone: '', + password: '', + code: '' // 新增:验证码 +}); + +const codeStatus = ref('unsent'); // unsent, countdown, resend, verified +const countdown = ref(60); +const codeError = ref(''); +const verifyToken = ref(''); // 验证成功后获得的token +const countdownTimer = ref(null); + +// 响应式数据 +const showCodeInput = ref(false); // 是否显示验证码输入框 +``` + +- [ ] **Step 2: 添加发送验证码方法** + +```javascript +// 发送验证码 +const handleSendCode = async () => { + const phoneValidation = validatePhone(form.value.phone); + if (!phoneValidation.valid) { + errorMessage.value = phoneValidation.message; + return; + } + + try { + const res = await sendCodeApi(form.value.phone, 'register'); + if (res.code === 200) { + // 开始倒计时 + codeStatus.value = 'countdown'; + countdown.value = res.expires_in || 60; + startCountdown(); + uni.showToast({ title: '验证码已发送', icon: 'success' }); + } + } catch (error) { + errorMessage.value = error.message || '发送失败,请重试'; + } +}; + +// 倒计时 +const startCountdown = () => { + if (countdownTimer.value) { + clearInterval(countdownTimer.value); + } + countdownTimer.value = setInterval(() => { + countdown.value--; + if (countdown.value <= 0) { + clearInterval(countdownTimer.value); + codeStatus.value = 'resend'; + } + }, 1000); +}; + +// 验证验证码 +const handleVerifyCode = async () => { + if (!form.value.code || form.value.code.length !== 6) { + codeError.value = '请输入6位验证码'; + return; + } + + try { + const res = await verifyCodeApi(form.value.phone, form.value.code, 'register'); + if (res.code === 200 && res.data && res.data.verified) { + verifyToken.value = res.data.verify_token; + codeStatus.value = 'verified'; + codeError.value = ''; + uni.showToast({ title: '验证成功', icon: 'success' }); + } + } catch (error) { + codeError.value = error.message || '验证失败'; + } +}; +``` + +- [ ] **Step 3: 修改模板添加验证码UI** + +在手机号和密码输入框之间添加: + +```vue + + + + + 发送验证码 + {{countdown}}秒 + ✓ 已验证 + + + + + + {{codeError}} + +``` + +- [ ] **Step 4: 修改handleRegister方法** + +```javascript +const handleRegister = async () => { + // 检查是否已验证 + if (codeStatus.value !== 'verified') { + errorMessage.value = '请先完成手机号验证'; + return; + } + + // ... 原有验证逻辑 ... + + // 存储verify_token + uni.setStorageSync('temp_register_verify_token', verifyToken.value); + + // 跳转到昵称设置页面 + uni.reLaunch({ + url: '/pages/profile/setNickname' + }); +}; +``` + +- [ ] **Step 5: Commit** + +```bash +git add frontend/pages/register/register.vue +git commit -m "feat: add SMS verification flow to register page" +``` + +--- + +### Task 10: 添加前端API方法 + +**Files:** +- Modify: `frontend/utils/api.js` + +- [ ] **Step 1: 添加sendCode和verifyCode API** + +```javascript +// 发送验证码 +export function sendCodeApi(mobile, scene = 'register') { + return request({ + url: '/api/v1/auth/send-code', + method: 'POST', + data: { mobile, scene } + }) +} + +// 验证验证码 +export function verifyCodeApi(mobile, code, scene = 'register') { + return request({ + url: '/api/v1/auth/verify-code', + method: 'POST', + data: { mobile, code, scene } + }) +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add frontend/utils/api.js +git commit -m "feat: add send-code and verify-code API methods" +``` + +--- + +## 阶段三:数据库与日志 + +### Task 11: 创建短信发送日志表 + +**Files:** +- Create: `backend/migrations/xxxx_add_sms_send_log.sql` + +- [ ] **Step 1: 编写迁移SQL** + +```sql +CREATE TABLE IF NOT EXISTS sms_send_log ( + id BIGSERIAL PRIMARY KEY, + mobile VARCHAR(20) NOT NULL COMMENT '手机号(脱敏存储)', + scene VARCHAR(20) NOT NULL DEFAULT 'register' COMMENT '使用场景:register/password', + template_code VARCHAR(50) NOT NULL COMMENT '短信模板CODE', + sign_name VARCHAR(50) NOT NULL COMMENT '短信签名', + message_id VARCHAR(64) DEFAULT '' COMMENT '阿里云返回的MessageId', + response_code VARCHAR(20) DEFAULT '' COMMENT '阿里云返回状态码', + response_description VARCHAR(255) DEFAULT '' COMMENT '阿里云返回描述', + status SMALLINT NOT NULL DEFAULT 1 COMMENT '发送状态:0=失败,1=成功', + send_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发送时间', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX idx_sms_send_log_mobile ON sms_send_log(mobile); +CREATE INDEX idx_sms_send_log_scene ON sms_send_log(scene); +CREATE INDEX idx_sms_send_log_send_time ON sms_send_log(send_time); +``` + +- [ ] **Step 2: Commit** + +```bash +git add backend/migrations/xxxx_add_sms_send_log.sql +git commit -m "feat: add sms_send_log table for SMS sending records" +``` + +--- + +## 阶段四:配置与部署 + +### Task 12: 配置检查清单 + +**Files:** +- Modify: `deploy/envs/user.env`(如果存在) + +- [ ] **Step 1: 确认环境变量配置** + +```bash +# 阿里云短信配置(直接使用 AccessKey,非 STS) +SMS_ACCESS_KEY_ID=your_access_key_id +SMS_ACCESS_KEY_SECRET=your_access_key_secret +SMS_SIGN_NAME=TopFans +SMS_TEMPLATE_CODE=SMS_xxxxxxx +SMS_REGION=cn-hangzhou +``` + +- [ ] **Step 2: 添加Redis环境变量(如果尚未配置)** + +确保userService有Redis连接配置: + +```bash +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +``` + +--- + +## 执行顺序 + +1. **Task 1**: 添加SMS SDK依赖 +2. **Task 2**: 创建SMS配置 +3. **Task 3**: 创建Redis操作层 +4. **Task 4**: 创建SMS服务 +5. **Task 5**: AuthService集成verify_token校验 +6. **Task 6**: Proto定义 +7. **Task 7**: AuthProvider实现 +8. **Task 8**: 网关HTTP端点 +9. **Task 9**: 前端register.vue修改 +10. **Task 10**: 前端API方法 +11. **Task 11**: 数据库表 +12. **Task 12**: 配置检查 + +--- + +## 验证步骤 + +### 后端验证 + +```bash +# 1. 启动userService +cd backend/services/userService +go run main.go + +# 2. 测试发送验证码 +curl -X POST http://localhost:8080/api/v1/auth/send-code \ + -H "Content-Type: application/json" \ + -d '{"mobile":"13800138000","scene":"register"}' + +# 3. 测试验证验证码 +curl -X POST http://localhost:8080/api/v1/auth/verify-code \ + -H "Content-Type: application/json" \ + -d '{"mobile":"13800138000","code":"123456","scene":"register"}' + +# 4. 测试注册(带verify_token) +curl -X POST http://localhost:8080/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{"mobile":"13800138000","verify_token":"vtf_xxx","password":"xxx","nickname":"xxx","star_id":1}' +``` + +### 前端验证 + +```bash +# 1. 启动前端 +cd frontend +npm run dev + +# 2. 访问注册页面 http://localhost:5173/pages/register/register +# 3. 输入手机号,点击"发送验证码" +# 4. 输入验证码,点击"验证" +# 5. 验证成功后输入密码,点击"注册" +``` + +--- + +## 注意事项 + +1. **验证码日志脱敏**:日志中禁止记录明文验证码,只能记录手机号和发送状态 +2. **verify_token安全**:token有效期5分钟,只能使用一次,验证后立即删除 +3. **防暴力破解**:验证失败3次后强制删除验证码,要求用户重新获取 +4. **限流保护**:从手机号和IP两个维度限制请求频率 +5. **IP黑名单**:1小时内触发3次限流的IP,临时拉黑30分钟 \ No newline at end of file diff --git a/frontend/pages/profile/setNickname.vue b/frontend/pages/profile/setNickname.vue index 33d0081..58723db 100644 --- a/frontend/pages/profile/setNickname.vue +++ b/frontend/pages/profile/setNickname.vue @@ -139,6 +139,7 @@ const handleNext = async () => { const mobile = uni.getStorageSync('temp_register_mobile'); const password = uni.getStorageSync('temp_register_password'); const star_id = 87; // 默认身份 + const verify_token = uni.getStorageSync('temp_register_verify_token') || ''; // 验证数据完整性 if (!mobile || !password || !trimmedNickname || !star_id) { @@ -149,6 +150,7 @@ const handleNext = async () => { uni.removeStorageSync('temp_register_mobile'); uni.removeStorageSync('temp_register_password'); uni.removeStorageSync('temp_register_nickname'); + uni.removeStorageSync('temp_register_verify_token'); setTimeout(() => { uni.reLaunch({ url: '/pages/register/register' @@ -169,7 +171,8 @@ const handleNext = async () => { mobile, password, star_id, - nickname: trimmedNickname + nickname: trimmedNickname, + verify_token }); uni.hideLoading(); @@ -178,6 +181,7 @@ const handleNext = async () => { uni.removeStorageSync('temp_register_mobile'); uni.removeStorageSync('temp_register_password'); uni.removeStorageSync('temp_register_nickname'); + uni.removeStorageSync('temp_register_verify_token'); // 设置新用户标记 uni.setStorageSync('is_new_user', true); diff --git a/frontend/pages/register/register.vue b/frontend/pages/register/register.vue index d2dccb3..1393f19 100644 --- a/frontend/pages/register/register.vue +++ b/frontend/pages/register/register.vue @@ -29,7 +29,45 @@ @input="handlePhoneInput" /> - + + + + + + 发送验证码 + {{countdown}}秒 + 已验证 + + + + + + {{codeError}} + + + + + + + { @@ -175,6 +221,65 @@ const checkPhoneDuplicate = async (phone) => { }, 300); }; +// 发送验证码 +const handleSendCode = async () => { + const phoneValidation = validatePhone(form.value.phone); + if (!phoneValidation.valid) { + errorMessage.value = phoneValidation.message; + return; + } + + try { + const res = await sendCodeApi(form.value.phone, 'register'); + if (res.code === 200) { + codeStatus.value = 'countdown'; + countdown.value = res.expires_in || 60; + showCodeInput.value = true; + startCountdown(); + uni.showToast({ title: '验证码已发送', icon: 'success' }); + } + } catch (error) { + codeError.value = error.message || '发送失败,请重试'; + } +}; + +// 倒计时 +const startCountdown = () => { + if (countdownTimer.value) { + clearInterval(countdownTimer.value); + } + countdownTimer.value = setInterval(() => { + countdown.value--; + if (countdown.value <= 0) { + clearInterval(countdownTimer.value); + codeStatus.value = 'resend'; + } + }, 1000); +}; + +// 验证验证码 +const handleVerifyCode = async () => { + if (!form.value.code || form.value.code.length !== 6) { + codeError.value = '请输入6位验证码'; + return; + } + + isVerifying.value = true; + codeError.value = ''; + try { + const res = await verifyCodeApi(form.value.phone, form.value.code, 'register'); + if (res.code === 200 && res.data && res.data.verified) { + verifyToken.value = res.data.verify_token; + codeStatus.value = 'verified'; + uni.showToast({ title: '验证成功', icon: 'success' }); + } + } catch (error) { + codeError.value = error.message || '验证失败'; + } finally { + isVerifying.value = false; + } +}; + // 跳转到登录页面 const goToLogin = () => { const pages = getCurrentPages(); @@ -241,7 +346,13 @@ const handleRegister = async () => { openTipDialog(); return; } - + + // 检查是否已验证(仅当显示了验证码输入框时) + if (showCodeInput.value && codeStatus.value !== 'verified') { + errorMessage.value = '请先完成手机号验证'; + return; + } + // 验证表单 const phoneValidation = validatePhone(form.value.phone); if (!phoneValidation.valid) { @@ -252,7 +363,7 @@ const handleRegister = async () => { }); return; } - + const passwordValidation = validatePassword(form.value.password); if (!passwordValidation.valid) { errorMessage.value = passwordValidation.message; @@ -262,14 +373,17 @@ const handleRegister = async () => { }); return; } - + errorMessage.value = ''; - + try { // 暂存手机号和密码到临时存储,供后续注册流程使用 uni.setStorageSync('temp_register_mobile', form.value.phone); uni.setStorageSync('temp_register_password', form.value.password); - + + // 存储verify_token + uni.setStorageSync('temp_register_verify_token', verifyToken.value); + // 跳转到昵称设置页面 uni.reLaunch({ url: '/pages/profile/setNickname' @@ -410,6 +524,54 @@ const handleRegister = async () => { padding-right: 100rpx; } +.code-wrapper { + flex-direction: row; + align-items: center; +} + +.code-wrapper .input-field { + flex: 1; +} + +.send-code-btn { + padding: 20rpx 30rpx; + background: #666; + border-radius: 40rpx; + color: #fff; + font-size: 24rpx; + white-space: nowrap; +} + +.send-code-btn.countdown { + background: #999; +} + +.send-code-btn.verified { + background: #4CAF50; +} + +.verify-btn-wrapper { + margin-bottom: 30rpx; +} + +.btn-secondary { + width: 100%; + height: 88rpx; + background: #fff; + border: 2rpx solid #B94E73; + border-radius: 50rpx; + font-size: 32rpx; + font-weight: bold; + color: #B94E73; + display: flex; + align-items: center; + justify-content: center; +} + +.btn-secondary::after { + border: none; +} + .input-field { flex: 1; height: 100%; diff --git a/frontend/store/modules/user.js b/frontend/store/modules/user.js index 0e83320..cec3a2d 100644 --- a/frontend/store/modules/user.js +++ b/frontend/store/modules/user.js @@ -119,9 +119,9 @@ const actions = { }, // 注册 - async register({ commit }, { mobile, password, star_id, nickname }) { + async register({ commit }, { mobile, password, star_id, nickname, verify_token = '' }) { try { - const res = await registerApi(mobile, password, star_id, nickname) + const res = await registerApi(mobile, password, star_id, nickname, verify_token) if (res.code === 200 && res.data) { // 缓存 access_token const accessToken = res.data.access_token diff --git a/frontend/utils/api.js b/frontend/utils/api.js index bcd9cac..6081e5b 100644 --- a/frontend/utils/api.js +++ b/frontend/utils/api.js @@ -48,7 +48,7 @@ export function request(options) { // 判断是否为登录或注册接口 const isAuthRequest = options.url.includes('/api/v1/auth/login') || options.url.includes( - '/api/v1/auth/register') + '/api/v1/auth/register') || options.url.includes('/api/v1/auth/send-code') || options.url.includes('/api/v1/auth/verify-code') // 如果不是登录/注册接口,则自动添加JWT token if (!isAuthRequest) { @@ -156,7 +156,7 @@ export function checkmobileApi(mobile) { } // 注册接口 -export function registerApi(mobile, password, star_id, nickname) { +export function registerApi(mobile, password, star_id, nickname, verify_token = '') { return request({ url: '/api/v1/auth/register', method: 'POST', @@ -164,11 +164,37 @@ export function registerApi(mobile, password, star_id, nickname) { mobile, password, star_id, - nickname + nickname, + verify_token } }) } +// 发送验证码 +export function sendCodeApi(mobile, scene = 'register') { + return request({ + url: '/api/v1/auth/send-code', + method: 'POST', + data: { + mobile, + scene + } + }); +} + +// 验证验证码 +export function verifyCodeApi(mobile, code, scene = 'register') { + return request({ + url: '/api/v1/auth/verify-code', + method: 'POST', + data: { + mobile, + code, + scene + } + }); +} + // 更新用户信息接口 export function updateUserInfoApi(nickname) {