feat(site): 官网定价页 —— 套餐由后台配置驱动,公开可见

官网加「定价」菜单与页面。数据来自 GET /api/v1/pricing(**不挂鉴权**):
运营改价、上下架、调发放节奏都在管理端完成,官网跟着变,不用发版;
未登录就能看到价格,这是转化的前提,也是官网存在的意义。

接口只吐在售项与展示字段,成本、权重、租户信息一律不出去。

结账仍在 Web 面完成(那里有登录态、租户上下文与支付轮询),官网只负责
「看价 → 去买」。同一套支付流程不在两处各实现一遍——这也是本次订阅开发
一直遵循的那条线:积分包与订阅共用一个支付弹窗,桌面端不重复购买入口。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-20 16:59:58 +08:00
parent a34d7045f1
commit b1fea23a0c
5 changed files with 167 additions and 0 deletions
@@ -36,6 +36,18 @@ func (h *Handler) MySubscription(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"subscription": sub, "plan": h.db.GetSubPlan(ctx, sub.PlanID)})
}
// PublicPricing: GET /api/v1/pricing —— **公开**(无需登录):官网定价页用。
// 官网要在用户登录前就能看到价格,所以这条不挂鉴权;只暴露在售项,且不含任何
// 内部字段(成本、权重、租户信息一概不出去)。
func (h *Handler) PublicPricing(c *gin.Context) {
ctx := c.Request.Context()
packs, _ := h.db.ActivePacks(ctx)
c.JSON(http.StatusOK, gin.H{
"plans": h.db.ListSubPlans(ctx, true),
"packs": packs,
})
}
// ---- 管理端 ----
// AdminSubPlans: GET /api/v1/admin/sub-plans —— 全部套餐(含下架)。
@@ -50,6 +50,7 @@ func New(db *store.Postgres, cache *store.Redis, bus *nats.Bus, blobStore *blob.
api := r.Group("/api/v1")
{
// —— 公开:鉴权端点 / 健康 / 按 task_id 寻址的 SSE 与导出(EventSource/下载无法带 Bearer)——
api.GET("/pricing", h.PublicPricing) // 公开定价(官网未登录也要能看价,故不挂鉴权)
api.POST("/auth/register", h.Register) // 注册 + 签发 JWT
api.POST("/auth/login", h.Login) // 登录 + 签发 JWT
api.GET("/auth/me", h.Me) // 当前登录用户(无效令牌 → 401)