285 lines
9.8 KiB
Go
285 lines
9.8 KiB
Go
package http
|
|
|
|
import (
|
|
"hyapp/services/gateway-service/internal/transport/http/httpkit"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
activityv1 "hyapp.local/api/proto/activity/v1"
|
|
userv1 "hyapp.local/api/proto/user/v1"
|
|
"hyapp/pkg/appcode"
|
|
"hyapp/services/gateway-service/internal/auth"
|
|
)
|
|
|
|
type messageTabSectionData struct {
|
|
Section string `json:"section"`
|
|
Title string `json:"title"`
|
|
UnreadCount int64 `json:"unread_count"`
|
|
Source string `json:"source"`
|
|
}
|
|
|
|
type inboxMessageData struct {
|
|
MessageID string `json:"message_id"`
|
|
Title string `json:"title"`
|
|
Summary string `json:"summary"`
|
|
Body string `json:"body"`
|
|
IconURL string `json:"icon_url"`
|
|
ImageURL string `json:"image_url"`
|
|
ActionType string `json:"action_type"`
|
|
ActionParam string `json:"action_param"`
|
|
Read bool `json:"read"`
|
|
SentAtMS int64 `json:"sent_at_ms"`
|
|
}
|
|
|
|
type userProfileBatchData struct {
|
|
UserID string `json:"user_id"`
|
|
DisplayUserID string `json:"display_user_id"`
|
|
Username string `json:"username"`
|
|
Avatar string `json:"avatar,omitempty"`
|
|
Gender string `json:"gender,omitempty"`
|
|
Country string `json:"country,omitempty"`
|
|
RegionID int64 `json:"region_id,omitempty"`
|
|
AppCode string `json:"app_code"`
|
|
}
|
|
|
|
// listMessageTabs returns backend unread summary and declares user conversations as Tencent IM SDK owned.
|
|
func (h *Handler) listMessageTabs(writer http.ResponseWriter, request *http.Request) {
|
|
if h.messageClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
resp, err := h.messageClient.ListMessageTabs(request.Context(), &activityv1.ListMessageTabsRequest{
|
|
Meta: activityMeta(request),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
sections := make([]messageTabSectionData, 0, len(resp.GetSections()))
|
|
for _, section := range resp.GetSections() {
|
|
sections = append(sections, messageTabSectionData{
|
|
Section: section.GetSection(),
|
|
Title: section.GetTitle(),
|
|
UnreadCount: section.GetUnreadCount(),
|
|
Source: section.GetSource(),
|
|
})
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"sections": sections})
|
|
}
|
|
|
|
// listInboxMessages reads system/activity inbox list; private user conversations stay in Tencent IM SDK.
|
|
func (h *Handler) listInboxMessages(writer http.ResponseWriter, request *http.Request) {
|
|
if h.messageClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
pageSize, ok := parseMessagePageSize(request.URL.Query().Get("page_size"))
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
section := strings.TrimSpace(request.URL.Query().Get("section"))
|
|
resp, err := h.messageClient.ListInboxMessages(request.Context(), &activityv1.ListInboxMessagesRequest{
|
|
Meta: activityMeta(request),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
Section: section,
|
|
PageSize: pageSize,
|
|
PageToken: strings.TrimSpace(request.URL.Query().Get("page_token")),
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
items := make([]inboxMessageData, 0, len(resp.GetItems()))
|
|
for _, item := range resp.GetItems() {
|
|
items = append(items, inboxMessageData{
|
|
MessageID: item.GetMessageId(),
|
|
Title: item.GetTitle(),
|
|
Summary: item.GetSummary(),
|
|
Body: item.GetBody(),
|
|
IconURL: item.GetIconUrl(),
|
|
ImageURL: item.GetImageUrl(),
|
|
ActionType: item.GetActionType(),
|
|
ActionParam: item.GetActionParam(),
|
|
Read: item.GetRead(),
|
|
SentAtMS: item.GetSentAtMs(),
|
|
})
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"section": resp.GetSection(), "items": items, "next_page_token": resp.GetNextPageToken()})
|
|
}
|
|
|
|
// markInboxSectionRead marks all current visible unread messages in one backend-owned section.
|
|
func (h *Handler) markInboxSectionRead(writer http.ResponseWriter, request *http.Request) {
|
|
if h.messageClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
var body struct {
|
|
Section string `json:"section"`
|
|
}
|
|
if !decode(writer, request, &body) {
|
|
return
|
|
}
|
|
resp, err := h.messageClient.MarkInboxSectionRead(request.Context(), &activityv1.MarkInboxSectionReadRequest{
|
|
Meta: activityMeta(request),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
Section: body.Section,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"section": resp.GetSection(), "read_count": resp.GetReadCount()})
|
|
}
|
|
|
|
// markInboxMessageReadByPath handles /api/v1/messages/{message_id}/read.
|
|
func (h *Handler) markInboxMessageReadByPath(writer http.ResponseWriter, request *http.Request) {
|
|
if h.messageClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
messageID := strings.TrimSpace(request.PathValue("message_id"))
|
|
if messageID == "" {
|
|
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
|
return
|
|
}
|
|
h.markInboxMessageRead(writer, request, messageID)
|
|
}
|
|
|
|
// deleteInboxMessageByPath handles /api/v1/messages/{message_id}.
|
|
func (h *Handler) deleteInboxMessageByPath(writer http.ResponseWriter, request *http.Request) {
|
|
if h.messageClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
messageID := strings.TrimSpace(request.PathValue("message_id"))
|
|
if messageID == "" {
|
|
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
|
return
|
|
}
|
|
if request.Method == http.MethodDelete {
|
|
h.deleteInboxMessage(writer, request, messageID)
|
|
return
|
|
}
|
|
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
|
}
|
|
|
|
func (h *Handler) markInboxMessageRead(writer http.ResponseWriter, request *http.Request, messageID string) {
|
|
if request.Method != http.MethodPost {
|
|
httpkit.WriteError(writer, request, http.StatusNotFound, httpkit.CodeNotFound, "not found")
|
|
return
|
|
}
|
|
resp, err := h.messageClient.MarkInboxMessageRead(request.Context(), &activityv1.MarkInboxMessageReadRequest{
|
|
Meta: activityMeta(request),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
MessageId: messageID,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"message_id": resp.GetMessageId(), "read": resp.GetRead(), "read_at_ms": resp.GetReadAtMs()})
|
|
}
|
|
|
|
func (h *Handler) deleteInboxMessage(writer http.ResponseWriter, request *http.Request, messageID string) {
|
|
resp, err := h.messageClient.DeleteInboxMessage(request.Context(), &activityv1.DeleteInboxMessageRequest{
|
|
Meta: activityMeta(request),
|
|
UserId: auth.UserIDFromContext(request.Context()),
|
|
MessageId: messageID,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"message_id": resp.GetMessageId(), "deleted": resp.GetDeleted()})
|
|
}
|
|
|
|
// batchUserProfiles returns only safe profile fields needed to render Tencent IM conversation rows.
|
|
func (h *Handler) batchUserProfiles(writer http.ResponseWriter, request *http.Request) {
|
|
if h.userProfileClient == nil {
|
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
|
return
|
|
}
|
|
userIDs, ok := parseBatchUserIDs(request)
|
|
if !ok {
|
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
|
return
|
|
}
|
|
resp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{
|
|
Meta: authRequestMeta(request, ""),
|
|
UserIds: userIDs,
|
|
})
|
|
if err != nil {
|
|
httpkit.WriteRPCError(writer, request, err)
|
|
return
|
|
}
|
|
items := make([]userProfileBatchData, 0, len(resp.GetUsers()))
|
|
for _, userID := range userIDs {
|
|
if user := resp.GetUsers()[userID]; user != nil {
|
|
items = append(items, userProfileBatchData{
|
|
UserID: strconv.FormatInt(user.GetUserId(), 10),
|
|
DisplayUserID: user.GetDisplayUserId(),
|
|
Username: user.GetUsername(),
|
|
Avatar: user.GetAvatar(),
|
|
Gender: user.GetGender(),
|
|
Country: user.GetCountry(),
|
|
RegionID: user.GetRegionId(),
|
|
AppCode: user.GetAppCode(),
|
|
})
|
|
}
|
|
}
|
|
httpkit.WriteOK(writer, request, map[string]any{"profiles": items})
|
|
}
|
|
|
|
func activityMeta(request *http.Request) *activityv1.RequestMeta {
|
|
return &activityv1.RequestMeta{
|
|
RequestId: httpkit.RequestIDFromContext(request.Context()),
|
|
Caller: "gateway-service",
|
|
GatewayNodeId: "gateway-local",
|
|
SentAtMs: time.Now().UnixMilli(),
|
|
AppCode: appcode.FromContext(request.Context()),
|
|
}
|
|
}
|
|
|
|
func parseMessagePageSize(raw string) (int32, bool) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return 0, true
|
|
}
|
|
value, err := strconv.Atoi(raw)
|
|
if err != nil || value < 0 || value > 1000 {
|
|
return 0, false
|
|
}
|
|
return int32(value), true
|
|
}
|
|
|
|
func parseBatchUserIDs(request *http.Request) ([]int64, bool) {
|
|
rawValues := append([]string(nil), request.URL.Query()["user_ids"]...)
|
|
rawValues = append(rawValues, request.URL.Query()["user_id"]...)
|
|
seen := make(map[int64]bool)
|
|
result := make([]int64, 0, len(rawValues))
|
|
for _, raw := range rawValues {
|
|
for piece := range strings.SplitSeq(raw, ",") {
|
|
piece = strings.TrimSpace(piece)
|
|
if piece == "" {
|
|
continue
|
|
}
|
|
userID, err := strconv.ParseInt(piece, 10, 64)
|
|
if err != nil || userID <= 0 {
|
|
return nil, false
|
|
}
|
|
if !seen[userID] {
|
|
seen[userID] = true
|
|
result = append(result, userID)
|
|
}
|
|
}
|
|
}
|
|
if len(result) == 0 || len(result) > 100 {
|
|
return nil, false
|
|
}
|
|
return result, true
|
|
}
|