2026-04-29 12:43:05 +08:00

244 lines
8.8 KiB
Go

package http
import (
"net/http"
"strings"
userv1 "hyapp/api/proto/user/v1"
"hyapp/services/gateway-service/internal/auth"
)
// userIdentityData 是 gateway 对外返回的当前短号投影。
// `display_user_id` 永远表示当前有效展示号,临时靓号过期后由 user-service 恢复为默认短号。
type userIdentityData struct {
UserID int64 `json:"user_id"`
DisplayUserID string `json:"display_user_id"`
Status string `json:"status"`
DefaultDisplayUserID string `json:"default_display_user_id"`
DisplayUserIDKind string `json:"display_user_id_kind"`
DisplayUserIDExpiresAtMs int64 `json:"display_user_id_expires_at_ms"`
LeaseID string `json:"lease_id,omitempty"`
}
// userProfileData 是 gateway 对外返回的用户基础资料投影。
// 它不包含注册来源、三方身份或 refresh session 等服务端内部字段。
type userProfileData struct {
UserID int64 `json:"user_id"`
DisplayUserID string `json:"display_user_id"`
DefaultDisplayUserID string `json:"default_display_user_id"`
DisplayUserIDKind string `json:"display_user_id_kind"`
DisplayUserIDExpiresAtMs int64 `json:"display_user_id_expires_at_ms"`
Username string `json:"username"`
Gender string `json:"gender,omitempty"`
Country string `json:"country,omitempty"`
CountryID int64 `json:"country_id,omitempty"`
CountryName string `json:"country_name,omitempty"`
CountryDisplayName string `json:"country_display_name,omitempty"`
RegionID int64 `json:"region_id,omitempty"`
RegionCode string `json:"region_code,omitempty"`
RegionName string `json:"region_name,omitempty"`
Avatar string `json:"avatar,omitempty"`
Birth string `json:"birth,omitempty"`
Language string `json:"language,omitempty"`
UpdatedAtMs int64 `json:"updated_at_ms"`
NextCountryChangeAtMs int64 `json:"next_country_change_allowed_at_ms,omitempty"`
}
// getMyIdentity 返回当前登录用户的短号状态。
func (h *Handler) getMyIdentity(writer http.ResponseWriter, request *http.Request) {
if h.userIdentityClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
resp, err := h.userIdentityClient.GetUserIdentity(request.Context(), &userv1.GetUserIdentityRequest{
Meta: authRequestMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, identityData(resp.GetIdentity(), ""))
}
// resolveDisplayUserID 通过当前有效展示号解析用户。
func (h *Handler) resolveDisplayUserID(writer http.ResponseWriter, request *http.Request) {
if h.userIdentityClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
displayUserID := strings.TrimPrefix(request.URL.Path, apiV1Prefix+"/users/by-display-user-id/")
resp, err := h.userIdentityClient.ResolveDisplayUserID(request.Context(), &userv1.ResolveDisplayUserIDRequest{
Meta: authRequestMeta(request, ""),
DisplayUserId: displayUserID,
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, identityData(resp.GetIdentity(), ""))
}
// updateMyProfile 修改当前用户的用户名、头像和生日。
func (h *Handler) updateMyProfile(writer http.ResponseWriter, request *http.Request) {
var body struct {
Username *string `json:"username"`
Avatar *string `json:"avatar"`
Birth *string `json:"birth"`
}
if !decode(writer, request, &body) {
return
}
if h.userProfileClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
resp, err := h.userProfileClient.UpdateUserProfile(request.Context(), &userv1.UpdateUserProfileRequest{
Meta: authRequestMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
Username: body.Username,
Avatar: body.Avatar,
Birth: body.Birth,
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, profileData(resp.GetUser(), 0))
}
// changeMyCountry 单独修改当前用户国家;冷却期和审计由 user-service 保证。
func (h *Handler) changeMyCountry(writer http.ResponseWriter, request *http.Request) {
var body struct {
Country string `json:"country"`
}
if !decode(writer, request, &body) {
return
}
if h.userProfileClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
resp, err := h.userProfileClient.ChangeUserCountry(request.Context(), &userv1.ChangeUserCountryRequest{
Meta: authRequestMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
Country: body.Country,
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, profileData(resp.GetUser(), resp.GetNextChangeAllowedAtMs()))
}
// changeMyDisplayUserID 修改当前登录用户的默认短号。
func (h *Handler) changeMyDisplayUserID(writer http.ResponseWriter, request *http.Request) {
var body struct {
NewDisplayUserID string `json:"new_display_user_id"`
Reason string `json:"reason"`
}
if !decode(writer, request, &body) {
return
}
if h.userIdentityClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
resp, err := h.userIdentityClient.ChangeDisplayUserID(request.Context(), &userv1.ChangeDisplayUserIDRequest{
Meta: authRequestMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
NewDisplayUserId: body.NewDisplayUserID,
Reason: body.Reason,
OperatorUserId: auth.UserIDFromContext(request.Context()),
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, identityData(resp.GetIdentity(), ""))
}
// applyMyPrettyDisplayUserID 为当前登录用户申请临时靓号。
func (h *Handler) applyMyPrettyDisplayUserID(writer http.ResponseWriter, request *http.Request) {
var body struct {
PrettyDisplayUserID string `json:"pretty_display_user_id"`
LeaseDurationMs int64 `json:"lease_duration_ms"`
PaymentReceiptID string `json:"payment_receipt_id"`
}
if !decode(writer, request, &body) {
return
}
if h.userIdentityClient == nil {
writeError(writer, request, http.StatusBadGateway, codeUpstreamError, "upstream service error")
return
}
resp, err := h.userIdentityClient.ApplyPrettyDisplayUserID(request.Context(), &userv1.ApplyPrettyDisplayUserIDRequest{
Meta: authRequestMeta(request, ""),
UserId: auth.UserIDFromContext(request.Context()),
PrettyDisplayUserId: body.PrettyDisplayUserID,
LeaseDurationMs: body.LeaseDurationMs,
PaymentReceiptId: body.PaymentReceiptID,
})
if err != nil {
writeRPCError(writer, request, err)
return
}
writeOK(writer, request, identityData(resp.GetIdentity(), resp.GetLeaseId()))
}
func identityData(identity *userv1.UserIdentity, leaseID string) userIdentityData {
if identity == nil {
return userIdentityData{LeaseID: leaseID}
}
return userIdentityData{
UserID: identity.GetUserId(),
DisplayUserID: identity.GetDisplayUserId(),
Status: identity.GetStatus(),
DefaultDisplayUserID: identity.GetDefaultDisplayUserId(),
DisplayUserIDKind: identity.GetDisplayUserIdKind(),
DisplayUserIDExpiresAtMs: identity.GetDisplayUserIdExpiresAtMs(),
LeaseID: leaseID,
}
}
func profileData(user *userv1.User, nextCountryChangeAtMs int64) userProfileData {
if user == nil {
return userProfileData{NextCountryChangeAtMs: nextCountryChangeAtMs}
}
return userProfileData{
UserID: user.GetUserId(),
DisplayUserID: user.GetDisplayUserId(),
DefaultDisplayUserID: user.GetDefaultDisplayUserId(),
DisplayUserIDKind: user.GetDisplayUserIdKind(),
DisplayUserIDExpiresAtMs: user.GetDisplayUserIdExpiresAtMs(),
Username: user.GetUsername(),
Gender: user.GetGender(),
Country: user.GetCountry(),
CountryID: user.GetCountryId(),
CountryName: user.GetCountryName(),
CountryDisplayName: user.GetCountryDisplayName(),
RegionID: user.GetRegionId(),
RegionCode: user.GetRegionCode(),
RegionName: user.GetRegionName(),
Avatar: user.GetAvatar(),
Birth: user.GetBirth(),
Language: user.GetLanguage(),
UpdatedAtMs: user.GetUpdatedAtMs(),
NextCountryChangeAtMs: nextCountryChangeAtMs,
}
}