package http import ( "hyapp/services/gateway-service/internal/transport/http/httpkit" "net/http" "strings" userv1 "hyapp.local/api/proto/user/v1" "hyapp/services/gateway-service/internal/auth" ) // handlePushToken 分发 push token 绑定和失效。 // 设备 token 属于 user-service 主数据扩展,gateway 只做鉴权、字段透传和 HTTP method 约束。 func (h *Handler) handlePushToken(writer http.ResponseWriter, request *http.Request) { switch request.Method { case http.MethodPost: h.bindPushToken(writer, request) case http.MethodDelete: h.deletePushToken(writer, request) default: httpkit.WriteError(writer, request, http.StatusMethodNotAllowed, httpkit.CodeInvalidArgument, "invalid argument") } } func (h *Handler) bindPushToken(writer http.ResponseWriter, request *http.Request) { if h.userDeviceClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } var body struct { DeviceID string `json:"device_id"` PushToken string `json:"push_token"` Provider string `json:"provider"` Platform string `json:"platform"` AppVersion string `json:"app_version"` Language string `json:"language"` Timezone string `json:"timezone"` } if !decode(writer, request, &body) { return } body.DeviceID = pushDeviceID(request, body.DeviceID) body.Platform = pushPlatform(request, body.Platform) resp, err := h.userDeviceClient.BindPushToken(request.Context(), &userv1.BindPushTokenRequest{ Meta: authRequestMeta(request, body.DeviceID), UserId: auth.UserIDFromContext(request.Context()), DeviceId: body.DeviceID, PushToken: strings.TrimSpace(body.PushToken), Provider: body.Provider, Platform: body.Platform, AppVersion: body.AppVersion, Language: body.Language, Timezone: body.Timezone, }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } httpkit.WriteOK(writer, request, bindPushTokenDataFromProto(resp)) } func (h *Handler) deletePushToken(writer http.ResponseWriter, request *http.Request) { if h.userDeviceClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } var body struct { DeviceID string `json:"device_id"` PushToken string `json:"push_token"` } if !decode(writer, request, &body) { return } body.DeviceID = pushDeviceID(request, body.DeviceID) resp, err := h.userDeviceClient.DeletePushToken(request.Context(), &userv1.DeletePushTokenRequest{ Meta: authRequestMeta(request, body.DeviceID), UserId: auth.UserIDFromContext(request.Context()), DeviceId: body.DeviceID, PushToken: strings.TrimSpace(body.PushToken), }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } httpkit.WriteOK(writer, request, deletePushTokenDataFromProto(resp)) } // bindPushTokenData/deletePushTokenData 固定外部 JSON 字段,避免 proto3 false 被省略。 type bindPushTokenData struct { Bound bool `json:"bound"` UpdatedAtMS int64 `json:"updated_at_ms"` } type deletePushTokenData struct { Deleted bool `json:"deleted"` UpdatedAtMS int64 `json:"updated_at_ms"` } func bindPushTokenDataFromProto(resp *userv1.BindPushTokenResponse) bindPushTokenData { if resp == nil { return bindPushTokenData{} } return bindPushTokenData{Bound: resp.GetBound(), UpdatedAtMS: resp.GetUpdatedAtMs()} } func deletePushTokenDataFromProto(resp *userv1.DeletePushTokenResponse) deletePushTokenData { if resp == nil { return deletePushTokenData{} } return deletePushTokenData{Deleted: resp.GetDeleted(), UpdatedAtMS: resp.GetUpdatedAtMs()} } func pushDeviceID(request *http.Request, bodyValue string) string { if value := strings.TrimSpace(bodyValue); value != "" { return value } return firstHeader(request, "X-Device-ID", "X-HY-Device-ID") } func pushPlatform(request *http.Request, bodyValue string) string { if value := strings.TrimSpace(bodyValue); value != "" { return value } return firstHeader(request, "X-App-Platform", "X-Platform") }