package userapi import ( "net/http" "strings" userv1 "hyapp.local/api/proto/user/v1" "hyapp/services/gateway-service/internal/auth" "hyapp/services/gateway-service/internal/transport/http/httpkit" ) type cpUserProfileData struct { UserID string `json:"user_id"` DisplayUserID string `json:"display_user_id"` Username string `json:"username"` Avatar string `json:"avatar"` } type cpGiftSnapshotData struct { GiftID string `json:"gift_id"` GiftName string `json:"gift_name"` GiftIconURL string `json:"gift_icon_url"` GiftAnimationURL string `json:"gift_animation_url"` GiftCount int32 `json:"gift_count"` GiftValue int64 `json:"gift_value"` BillingReceiptID string `json:"billing_receipt_id"` } type cpApplicationData struct { ApplicationID string `json:"application_id"` RelationType string `json:"relation_type"` Status string `json:"status"` Requester *cpUserProfileData `json:"requester,omitempty"` Target *cpUserProfileData `json:"target,omitempty"` RoomID string `json:"room_id"` RoomRegionID int64 `json:"room_region_id"` Gift *cpGiftSnapshotData `json:"gift,omitempty"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` ExpiresAtMS int64 `json:"expires_at_ms"` DecidedAtMS int64 `json:"decided_at_ms"` } type cpRelationshipData struct { RelationshipID string `json:"relationship_id"` RelationType string `json:"relation_type"` Status string `json:"status"` Me *cpUserProfileData `json:"me,omitempty"` Partner *cpUserProfileData `json:"partner,omitempty"` IntimacyValue int64 `json:"intimacy_value"` Level int32 `json:"level"` FormedAtMS int64 `json:"formed_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } type cpRejectRequest struct { Reason string `json:"reason"` } // listCPApplications 返回当前用户收到或发出的 CP/兄弟/姐妹申请。 func (h *Handler) listCPApplications(writer http.ResponseWriter, request *http.Request) { if h.userCPClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } page, pageSize, ok := socialPage(request) if !ok { httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument") return } resp, err := h.userCPClient.ListCPApplications(request.Context(), &userv1.ListCPApplicationsRequest{ Meta: httpkit.UserMeta(request, ""), UserId: auth.UserIDFromContext(request.Context()), Direction: strings.TrimSpace(request.URL.Query().Get("direction")), Status: strings.TrimSpace(request.URL.Query().Get("status")), Page: page, PageSize: pageSize, }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } applications := make([]cpApplicationData, 0, len(resp.GetApplications())) for _, item := range resp.GetApplications() { applications = append(applications, cpApplicationFromProto(item)) } httpkit.WriteOK(writer, request, map[string]any{ "applications": applications, "total": resp.GetTotal(), "server_time_ms": resp.GetServerTimeMs(), }) } // acceptCPApplication 同意一条申请;同意后同一对用户其他 pending 类型由 user-service 阻断。 func (h *Handler) acceptCPApplication(writer http.ResponseWriter, request *http.Request) { if h.userCPClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } applicationID := strings.TrimSpace(request.PathValue("application_id")) if applicationID == "" { httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found") return } resp, err := h.userCPClient.AcceptCPApplication(request.Context(), &userv1.AcceptCPApplicationRequest{ Meta: httpkit.UserMeta(request, ""), UserId: auth.UserIDFromContext(request.Context()), ApplicationId: applicationID, }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } httpkit.WriteOK(writer, request, map[string]any{ "application": cpApplicationFromProto(resp.GetApplication()), "relationship": cpRelationshipFromProto(resp.GetRelationship()), }) } // rejectCPApplication 拒绝一条申请;拒绝不会删除历史申请,客户端仍可看到 decided 状态。 func (h *Handler) rejectCPApplication(writer http.ResponseWriter, request *http.Request) { if h.userCPClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } applicationID := strings.TrimSpace(request.PathValue("application_id")) if applicationID == "" { httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found") return } var body cpRejectRequest if request.Body != nil && request.ContentLength != 0 && !httpkit.Decode(writer, request, &body) { return } resp, err := h.userCPClient.RejectCPApplication(request.Context(), &userv1.RejectCPApplicationRequest{ Meta: httpkit.UserMeta(request, ""), UserId: auth.UserIDFromContext(request.Context()), ApplicationId: applicationID, Reason: body.Reason, }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } httpkit.WriteOK(writer, request, map[string]any{"application": cpApplicationFromProto(resp.GetApplication())}) } // listCPRelationships 返回当前用户 active 关系;服务端固定限制一个用户只能同时存在一条关系。 func (h *Handler) listCPRelationships(writer http.ResponseWriter, request *http.Request) { if h.userCPClient == nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") return } page, pageSize, ok := socialPage(request) if !ok { httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument") return } resp, err := h.userCPClient.ListCPRelationships(request.Context(), &userv1.ListCPRelationshipsRequest{ Meta: httpkit.UserMeta(request, ""), UserId: auth.UserIDFromContext(request.Context()), RelationType: strings.TrimSpace(request.URL.Query().Get("relation_type")), Page: page, PageSize: pageSize, }) if err != nil { httpkit.WriteRPCError(writer, request, err) return } relationships := make([]cpRelationshipData, 0, len(resp.GetRelationships())) for _, item := range resp.GetRelationships() { relationships = append(relationships, cpRelationshipFromProto(item)) } httpkit.WriteOK(writer, request, map[string]any{ "relationships": relationships, "total": resp.GetTotal(), }) } func cpApplicationFromProto(item *userv1.CPApplication) cpApplicationData { if item == nil { return cpApplicationData{} } return cpApplicationData{ ApplicationID: item.GetApplicationId(), RelationType: item.GetRelationType(), Status: item.GetStatus(), Requester: cpUserProfileFromProto(item.GetRequester()), Target: cpUserProfileFromProto(item.GetTarget()), RoomID: item.GetRoomId(), RoomRegionID: item.GetRoomRegionId(), Gift: cpGiftSnapshotFromProto(item.GetGift()), CreatedAtMS: item.GetCreatedAtMs(), UpdatedAtMS: item.GetUpdatedAtMs(), ExpiresAtMS: item.GetExpiresAtMs(), DecidedAtMS: item.GetDecidedAtMs(), } } func cpRelationshipFromProto(item *userv1.CPRelationship) cpRelationshipData { if item == nil { return cpRelationshipData{} } return cpRelationshipData{ RelationshipID: item.GetRelationshipId(), RelationType: item.GetRelationType(), Status: item.GetStatus(), Me: cpUserProfileFromProto(item.GetMe()), Partner: cpUserProfileFromProto(item.GetPartner()), IntimacyValue: item.GetIntimacyValue(), Level: item.GetLevel(), FormedAtMS: item.GetFormedAtMs(), UpdatedAtMS: item.GetUpdatedAtMs(), } } func cpUserProfileFromProto(item *userv1.CPUserProfile) *cpUserProfileData { if item == nil || item.GetUserId() <= 0 { return nil } return &cpUserProfileData{ UserID: userIDString(item.GetUserId()), DisplayUserID: item.GetDisplayUserId(), Username: item.GetUsername(), Avatar: item.GetAvatar(), } } func cpGiftSnapshotFromProto(item *userv1.CPGiftSnapshot) *cpGiftSnapshotData { if item == nil { return nil } return &cpGiftSnapshotData{ GiftID: item.GetGiftId(), GiftName: item.GetGiftName(), GiftIconURL: item.GetGiftIconUrl(), GiftAnimationURL: item.GetGiftAnimationUrl(), GiftCount: item.GetGiftCount(), GiftValue: item.GetGiftValue(), BillingReceiptID: item.GetBillingReceiptId(), } }