From d23f7bca8f2a7db02173519f8003cc7cff8625e1 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 13 Jul 2026 07:46:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(server/reward):=20GET=20/v1/invite=20?= =?UTF-8?q?=E2=80=94=20=E9=82=80=E8=AF=B7=E7=A0=81/=E9=93=BE=E6=8E=A5/?= =?UTF-8?q?=E6=88=98=E7=BB=A9/TG=20=E4=BB=BB=E5=8A=A1=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/internal/reward/handler.go | 55 +++++++++++++++++++ server/internal/reward/handler_invite_test.go | 39 +++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 server/internal/reward/handler.go create mode 100644 server/internal/reward/handler_invite_test.go diff --git a/server/internal/reward/handler.go b/server/internal/reward/handler.go new file mode 100644 index 0000000..ce2d892 --- /dev/null +++ b/server/internal/reward/handler.go @@ -0,0 +1,55 @@ +package reward + +import ( + "encoding/json" + "net/http" + + "github.com/wangjia/pangolin/server/internal/apierr" + "github.com/wangjia/pangolin/server/internal/auth" +) + +const inviteLinkBase = "https://pangolin.yanmeiai.com/i/" + +type Handler struct { + svc *Service + st *Store + tgEnabled bool + channel string +} + +func NewHandler(s *Service, st *Store, tgEnabled bool, channel string) *Handler { + return &Handler{svc: s, st: st, tgEnabled: tgEnabled, channel: channel} +} + +func (h *Handler) GetInvite(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + uid, ok := auth.UserIDFromContext(ctx) + if !ok { + apierr.WriteJSON(w, http.StatusUnauthorized, apierr.ErrUnauthorized) + return + } + code, err := h.svc.EnsureCode(ctx, uid) + if err != nil { + apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal) + return + } + invited, converted, earned, err := h.st.Summary(ctx, uid) + if err != nil { + apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal) + return + } + joined, _ := h.st.TelegramClaimed(ctx, uid) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + _ = json.NewEncoder(w).Encode(map[string]any{ + "invite_code": code, + "invite_link": inviteLinkBase + code, + "invited": invited, + "converted": converted, + "earned_days": earned, + "telegram": map[string]any{ + "enabled": h.tgEnabled, + "joined": joined, + "channel": h.channel, + }, + }) +} diff --git a/server/internal/reward/handler_invite_test.go b/server/internal/reward/handler_invite_test.go new file mode 100644 index 0000000..f9cd688 --- /dev/null +++ b/server/internal/reward/handler_invite_test.go @@ -0,0 +1,39 @@ +package reward + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/wangjia/pangolin/server/internal/codes" +) + +func TestGetInvite_ReturnsCodeAndSummary(t *testing.T) { + db := openDB(t) + seedU(t, db, 1, "u1") + st := NewStore(db) + g := codes.NewService(codes.NewStore(db), nil, 5, time.Hour) + svc := NewService(db, st, g, Config{RegDays: 3, PaidDays: 7, TgDays: 3, RegMonthlyCap: 10}, time.Now) + h := NewHandler(svc, st, true, "@pangolin_app") + + r := httptest.NewRequest(http.MethodGet, "/v1/invite", nil) + r = r.WithContext(context.WithValue(r.Context(), codes.CtxKeyUserID, int64(1))) + w := httptest.NewRecorder() + h.GetInvite(w, r) // 直接调 handler,不经 router + + if w.Code != 200 { + t.Fatalf("code=%d body=%s", w.Code, w.Body) + } + var got map[string]any + json.Unmarshal(w.Body.Bytes(), &got) + if got["invite_code"] == "" || got["invite_code"] == nil { + t.Fatalf("no invite_code: %v", got) + } + tg, _ := got["telegram"].(map[string]any) + if tg == nil || tg["enabled"] != true { + t.Fatalf("telegram block wrong: %v", got["telegram"]) + } +}