margin_handler.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package admin
  2. import (
  3. "errors"
  4. "strconv"
  5. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  6. "github.com/Wei-Shaw/sub2api/internal/service"
  7. "github.com/gin-gonic/gin"
  8. )
  9. // MarginHandler 毛利看板与熔断:收入取用户实扣,成本按目录价估算。
  10. type MarginHandler struct {
  11. service *service.MarginService
  12. }
  13. func NewMarginHandler(svc *service.MarginService) *MarginHandler {
  14. return &MarginHandler{service: svc}
  15. }
  16. func (h *MarginHandler) requireService(c *gin.Context) bool {
  17. if h == nil || h.service == nil {
  18. response.ErrorFrom(c, errors.New("margin service unavailable"))
  19. return false
  20. }
  21. return true
  22. }
  23. // Summary 毛利汇总:?hours=24&group_id=。
  24. // GET /api/v1/admin/margins
  25. func (h *MarginHandler) Summary(c *gin.Context) {
  26. if !h.requireService(c) {
  27. return
  28. }
  29. hours := 24
  30. if raw := c.Query("hours"); raw != "" {
  31. if v, err := strconv.Atoi(raw); err == nil && v > 0 && v <= 24*90 {
  32. hours = v
  33. }
  34. }
  35. var groupID *int64
  36. if raw := c.Query("group_id"); raw != "" {
  37. if v, err := strconv.ParseInt(raw, 10, 64); err == nil && v > 0 {
  38. groupID = &v
  39. }
  40. }
  41. rows, err := h.service.Summary(c.Request.Context(), hours, groupID)
  42. if err != nil {
  43. response.ErrorFrom(c, err)
  44. return
  45. }
  46. if rows == nil {
  47. rows = []service.MarginRow{}
  48. }
  49. response.Success(c, gin.H{"items": rows, "count": len(rows), "hours": hours})
  50. }
  51. // GetSettings 读取熔断配置。
  52. // GET /api/v1/admin/margins/fuse-settings
  53. func (h *MarginHandler) GetSettings(c *gin.Context) {
  54. if !h.requireService(c) {
  55. return
  56. }
  57. response.Success(c, h.service.GetSettings(c.Request.Context()))
  58. }
  59. // UpdateSettings 更新熔断配置。
  60. // PUT /api/v1/admin/margins/fuse-settings
  61. func (h *MarginHandler) UpdateSettings(c *gin.Context) {
  62. if !h.requireService(c) {
  63. return
  64. }
  65. var req service.MarginFuseSettings
  66. if err := c.ShouldBindJSON(&req); err != nil {
  67. response.BadRequest(c, "Invalid request: "+err.Error())
  68. return
  69. }
  70. out, err := h.service.UpdateSettings(c.Request.Context(), req)
  71. if err != nil {
  72. response.ErrorFrom(c, err)
  73. return
  74. }
  75. response.Success(c, out)
  76. }
  77. // Events 近期熔断/恢复事件。
  78. // GET /api/v1/admin/margins/events
  79. func (h *MarginHandler) Events(c *gin.Context) {
  80. if !h.requireService(c) {
  81. return
  82. }
  83. events := h.service.Events()
  84. if events == nil {
  85. events = []service.MarginFuseEvent{}
  86. }
  87. response.Success(c, gin.H{"items": events, "count": len(events)})
  88. }
  89. // Unfuse 手动恢复渠道并清除熔断跟踪。
  90. // POST /api/v1/admin/margins/channels/:id/unfuse
  91. func (h *MarginHandler) Unfuse(c *gin.Context) {
  92. if !h.requireService(c) {
  93. return
  94. }
  95. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  96. if err != nil || id <= 0 {
  97. response.BadRequest(c, "Invalid channel ID")
  98. return
  99. }
  100. if err := h.service.Unfuse(c.Request.Context(), id); err != nil {
  101. response.ErrorFrom(c, err)
  102. return
  103. }
  104. response.Success(c, gin.H{"message": "Channel recovered successfully"})
  105. }