| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- package admin
- import (
- "errors"
- "strconv"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/pkg/response"
- middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/gin-gonic/gin"
- )
- // BillingExportHandler 账单:用户月账单与用量 CSV 导出,管理员可查任意用户。
- type BillingExportHandler struct {
- service *service.BillingExportService
- }
- func NewBillingExportHandler(svc *service.BillingExportService) *BillingExportHandler {
- return &BillingExportHandler{service: svc}
- }
- func (h *BillingExportHandler) requireService(c *gin.Context) bool {
- if h == nil || h.service == nil {
- response.ErrorFrom(c, errors.New("billing export service unavailable"))
- return false
- }
- return true
- }
- // Statement 用户月账单:?year=&month=。
- // GET /billing/statement
- func (h *BillingExportHandler) Statement(c *gin.Context) {
- if !h.requireService(c) {
- return
- }
- subject, ok := middleware2.GetAuthSubjectFromContext(c)
- if !ok {
- response.Unauthorized(c, "Unauthorized")
- return
- }
- now := time.Now().UTC()
- year, month := now.Year(), int(now.Month())
- if raw := c.Query("year"); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil {
- year = v
- }
- }
- if raw := c.Query("month"); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil {
- month = v
- }
- }
- stmt, err := h.service.Statement(c.Request.Context(), subject.UserID, year, month)
- if err != nil {
- response.ErrorFrom(c, err)
- return
- }
- response.Success(c, stmt)
- }
- // Export 用户用量 CSV:?start=RFC3339&end=RFC3339(≤31 天)。
- // GET /billing/export
- func (h *BillingExportHandler) Export(c *gin.Context) {
- if !h.requireService(c) {
- return
- }
- subject, ok := middleware2.GetAuthSubjectFromContext(c)
- if !ok {
- response.Unauthorized(c, "Unauthorized")
- return
- }
- start, end, ok := billingExportRange(c)
- if !ok {
- return
- }
- data, filename, err := h.service.ExportCSV(c.Request.Context(), subject.UserID, start, end)
- if err != nil {
- response.ErrorFrom(c, err)
- return
- }
- c.Header("Content-Disposition", "attachment; filename="+filename)
- c.Data(200, "text/csv; charset=utf-8", data)
- }
- // AdminStatement 管理员查任意用户月账单。
- // GET /api/v1/admin/billing/users/:userId/statement?year=&month=
- func (h *BillingExportHandler) AdminStatement(c *gin.Context) {
- if !h.requireService(c) {
- return
- }
- userID, err := strconv.ParseInt(c.Param("userId"), 10, 64)
- if err != nil || userID <= 0 {
- response.BadRequest(c, "Invalid user ID")
- return
- }
- now := time.Now().UTC()
- year, month := now.Year(), int(now.Month())
- if raw := c.Query("year"); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil {
- year = v
- }
- }
- if raw := c.Query("month"); raw != "" {
- if v, err := strconv.Atoi(raw); err == nil {
- month = v
- }
- }
- stmt, err := h.service.Statement(c.Request.Context(), userID, year, month)
- if err != nil {
- response.ErrorFrom(c, err)
- return
- }
- response.Success(c, stmt)
- }
- // AdminExport 管理员导出任意用户用量 CSV。
- // GET /api/v1/admin/billing/users/:userId/export?start=&end=
- func (h *BillingExportHandler) AdminExport(c *gin.Context) {
- if !h.requireService(c) {
- return
- }
- userID, err := strconv.ParseInt(c.Param("userId"), 10, 64)
- if err != nil || userID <= 0 {
- response.BadRequest(c, "Invalid user ID")
- return
- }
- start, end, ok := billingExportRange(c)
- if !ok {
- return
- }
- data, filename, err := h.service.ExportCSV(c.Request.Context(), userID, start, end)
- if err != nil {
- response.ErrorFrom(c, err)
- return
- }
- c.Header("Content-Disposition", "attachment; filename="+filename)
- c.Data(200, "text/csv; charset=utf-8", data)
- }
- func billingExportRange(c *gin.Context) (time.Time, time.Time, bool) {
- end := time.Now().UTC()
- start := end.AddDate(0, 0, -7)
- if raw := c.Query("start"); raw != "" {
- v, err := time.Parse(time.RFC3339, raw)
- if err != nil {
- response.BadRequest(c, "Invalid start, use RFC3339")
- return time.Time{}, time.Time{}, false
- }
- start = v
- }
- if raw := c.Query("end"); raw != "" {
- v, err := time.Parse(time.RFC3339, raw)
- if err != nil {
- response.BadRequest(c, "Invalid end, use RFC3339")
- return time.Time{}, time.Time{}, false
- }
- end = v
- }
- return start, end, true
- }
|