easyai-ai-gateway/apps/api/internal/httpapi/wallet_handlers.go
chengcheng a81a7b5200 fix: 修复 OIDC 用户预配与跨标签页登录态
增加受控 JIT 本地用户投影,并使用 HttpOnly Cookie 在新标签页恢复认证中心登录态。补充错误语义、安全校验、自动化测试与回滚配置。
2026-07-13 17:07:52 +08:00

110 lines
3.8 KiB
Go

package httpapi
import (
"net/http"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)
// getWallet godoc
// @Summary 获取钱包摘要
// @Description 返回当前用户的钱包账户、余额和最近消费摘要,可按 currency 过滤。
// @Tags wallet
// @Produce json
// @Security BearerAuth
// @Param currency query string false "币种" default(USD)
// @Success 200 {object} store.WalletSummary
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 503 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/workspace/wallet [get]
func (s *Server) getWallet(w http.ResponseWriter, r *http.Request) {
user, ok := auth.UserFromContext(r.Context())
if !ok {
writeError(w, http.StatusUnauthorized, "unauthorized")
return
}
summary, err := s.store.GetWalletSummary(r.Context(), user, r.URL.Query().Get("currency"))
if err != nil {
s.logger.Error("get wallet failed", "error", err)
writeError(w, http.StatusInternalServerError, "get wallet failed")
return
}
writeJSON(w, http.StatusOK, summary)
}
// listWalletTransactions godoc
// @Summary 列出钱包交易
// @Description 返回当前用户的钱包交易流水,支持关键字、方向、交易类型、时间范围和分页过滤。
// @Tags wallet
// @Produce json
// @Security BearerAuth
// @Param q query string false "搜索关键字,别名 query"
// @Param direction query string false "交易方向"
// @Param transactionType query string false "交易类型"
// @Param createdFrom query string false "创建时间起点,别名 from"
// @Param createdTo query string false "创建时间终点,别名 to"
// @Param page query int false "页码" default(1)
// @Param pageSize query int false "每页数量,别名 limit" default(50)
// @Success 200 {object} WalletTransactionListResponse
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 503 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/workspace/wallet/transactions [get]
func (s *Server) listWalletTransactions(w http.ResponseWriter, r *http.Request) {
user, ok := auth.UserFromContext(r.Context())
if !ok {
writeError(w, http.StatusUnauthorized, "unauthorized")
return
}
query := r.URL.Query()
page, err := positiveQueryInt(query.Get("page"), 1)
if err != nil {
writeError(w, http.StatusBadRequest, "invalid page")
return
}
pageSizeRaw := query.Get("pageSize")
if pageSizeRaw == "" {
pageSizeRaw = query.Get("limit")
}
pageSize, err := positiveQueryInt(pageSizeRaw, 50)
if err != nil {
writeError(w, http.StatusBadRequest, "invalid pageSize")
return
}
createdFrom, err := parseTaskListTime(query.Get("createdFrom"), query.Get("from"))
if err != nil {
writeError(w, http.StatusBadRequest, "invalid createdFrom")
return
}
createdTo, err := parseTaskListTime(query.Get("createdTo"), query.Get("to"))
if err != nil {
writeError(w, http.StatusBadRequest, "invalid createdTo")
return
}
result, err := s.store.ListWalletTransactions(r.Context(), user, store.WalletTransactionListFilter{
Query: firstNonEmpty(query.Get("q"), query.Get("query")),
Direction: query.Get("direction"),
TransactionType: query.Get("transactionType"),
CreatedFrom: createdFrom,
CreatedTo: createdTo,
Page: page,
PageSize: pageSize,
})
if err != nil {
s.logger.Error("list wallet transactions failed", "error", err)
writeError(w, http.StatusInternalServerError, "list wallet transactions failed")
return
}
writeJSON(w, http.StatusOK, map[string]any{
"items": result.Items,
"total": result.Total,
"page": result.Page,
"pageSize": result.PageSize,
})
}