global_pricing_handler.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package admin
  2. import (
  3. "errors"
  4. "net/http"
  5. "strconv"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  7. "github.com/Wei-Shaw/sub2api/internal/service"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // GlobalPricingHandler 管理全站模型定价覆盖:命中模型的请求在全部分组/
  11. // 账号强制使用此处价格,未命中的模型走原定价链路。
  12. type GlobalPricingHandler struct {
  13. service *service.GlobalModelPricingService
  14. }
  15. func NewGlobalPricingHandler(svc *service.GlobalModelPricingService) *GlobalPricingHandler {
  16. return &GlobalPricingHandler{service: svc}
  17. }
  18. func (h *GlobalPricingHandler) requireService(c *gin.Context) bool {
  19. if h == nil || h.service == nil {
  20. response.ErrorFrom(c, errors.New("global pricing service unavailable"))
  21. return false
  22. }
  23. return true
  24. }
  25. func globalPricingNotFound(c *gin.Context, err error) bool {
  26. if errors.Is(err, service.ErrGlobalModelPricingNotFound) {
  27. response.NotFound(c, "Global pricing entry not found")
  28. return true
  29. }
  30. return false
  31. }
  32. func globalPricingConflict(c *gin.Context, err error) bool {
  33. if errors.Is(err, service.ErrGlobalModelPricingDuplicate) {
  34. response.Error(c, http.StatusConflict, "This model already has a global pricing entry")
  35. return true
  36. }
  37. return false
  38. }
  39. // List 返回全部全站定价(含禁用)。
  40. // GET /api/v1/admin/global-pricing
  41. func (h *GlobalPricingHandler) List(c *gin.Context) {
  42. if !h.requireService(c) {
  43. return
  44. }
  45. items, err := h.service.List(c.Request.Context())
  46. if err != nil {
  47. response.ErrorFrom(c, err)
  48. return
  49. }
  50. response.Success(c, gin.H{"items": items, "count": len(items)})
  51. }
  52. type globalPricingUpsertRequest struct {
  53. ModelPattern string `json:"model_pattern"`
  54. BillingMode service.BillingMode `json:"billing_mode"`
  55. InputPrice *float64 `json:"input_price"`
  56. OutputPrice *float64 `json:"output_price"`
  57. CacheWritePrice *float64 `json:"cache_write_price"`
  58. CacheWrite1hPrice *float64 `json:"cache_write_1h_price"`
  59. CacheReadPrice *float64 `json:"cache_read_price"`
  60. PerRequestPrice *float64 `json:"per_request_price"`
  61. Enabled *bool `json:"enabled"`
  62. }
  63. func (r globalPricingUpsertRequest) toInput(defaultEnabled bool) service.GlobalModelPriceInput {
  64. enabled := defaultEnabled
  65. if r.Enabled != nil {
  66. enabled = *r.Enabled
  67. }
  68. return service.GlobalModelPriceInput{
  69. ModelPattern: r.ModelPattern,
  70. BillingMode: r.BillingMode,
  71. InputPrice: r.InputPrice,
  72. OutputPrice: r.OutputPrice,
  73. CacheWritePrice: r.CacheWritePrice,
  74. CacheWrite1hPrice: r.CacheWrite1hPrice,
  75. CacheReadPrice: r.CacheReadPrice,
  76. PerRequestPrice: r.PerRequestPrice,
  77. Enabled: enabled,
  78. }
  79. }
  80. // Create 新建全站定价(默认启用)。
  81. // POST /api/v1/admin/global-pricing
  82. func (h *GlobalPricingHandler) Create(c *gin.Context) {
  83. if !h.requireService(c) {
  84. return
  85. }
  86. var req globalPricingUpsertRequest
  87. if err := c.ShouldBindJSON(&req); err != nil {
  88. response.BadRequest(c, "Invalid request: "+err.Error())
  89. return
  90. }
  91. item, err := h.service.Create(c.Request.Context(), req.toInput(true))
  92. if err != nil {
  93. if globalPricingConflict(c, err) {
  94. return
  95. }
  96. response.ErrorFrom(c, err)
  97. return
  98. }
  99. response.Success(c, item)
  100. }
  101. // Update 更新全站定价(enabled 缺省保持原值)。
  102. // PUT /api/v1/admin/global-pricing/:id
  103. func (h *GlobalPricingHandler) Update(c *gin.Context) {
  104. if !h.requireService(c) {
  105. return
  106. }
  107. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  108. if err != nil || id <= 0 {
  109. response.BadRequest(c, "Invalid pricing ID")
  110. return
  111. }
  112. var req globalPricingUpsertRequest
  113. if err := c.ShouldBindJSON(&req); err != nil {
  114. response.BadRequest(c, "Invalid request: "+err.Error())
  115. return
  116. }
  117. input := req.toInput(true)
  118. if req.Enabled == nil {
  119. existing, err := h.service.Get(c.Request.Context(), id)
  120. if err != nil {
  121. if globalPricingNotFound(c, err) {
  122. return
  123. }
  124. response.ErrorFrom(c, err)
  125. return
  126. }
  127. input.Enabled = existing.Enabled
  128. }
  129. item, err := h.service.Update(c.Request.Context(), id, input)
  130. if err != nil {
  131. if globalPricingNotFound(c, err) {
  132. return
  133. }
  134. if globalPricingConflict(c, err) {
  135. return
  136. }
  137. response.ErrorFrom(c, err)
  138. return
  139. }
  140. response.Success(c, item)
  141. }
  142. // Delete 删除全站定价(对应模型恢复原定价)。
  143. // DELETE /api/v1/admin/global-pricing/:id
  144. func (h *GlobalPricingHandler) Delete(c *gin.Context) {
  145. if !h.requireService(c) {
  146. return
  147. }
  148. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  149. if err != nil || id <= 0 {
  150. response.BadRequest(c, "Invalid pricing ID")
  151. return
  152. }
  153. if err := h.service.Delete(c.Request.Context(), id); err != nil {
  154. if globalPricingNotFound(c, err) {
  155. return
  156. }
  157. response.ErrorFrom(c, err)
  158. return
  159. }
  160. response.Success(c, gin.H{"message": "Global pricing deleted successfully"})
  161. }
  162. type globalPricingEnableRequest struct {
  163. Enabled *bool `json:"enabled"`
  164. }
  165. // SetEnabled 启停全站定价(停用后对应模型恢复原定价)。
  166. // POST /api/v1/admin/global-pricing/:id/enable
  167. func (h *GlobalPricingHandler) SetEnabled(c *gin.Context) {
  168. if !h.requireService(c) {
  169. return
  170. }
  171. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  172. if err != nil || id <= 0 {
  173. response.BadRequest(c, "Invalid pricing ID")
  174. return
  175. }
  176. var req globalPricingEnableRequest
  177. if err := c.ShouldBindJSON(&req); err != nil {
  178. response.BadRequest(c, "Invalid request: "+err.Error())
  179. return
  180. }
  181. if req.Enabled == nil {
  182. response.BadRequest(c, "Invalid request: enabled is required")
  183. return
  184. }
  185. item, err := h.service.SetEnabled(c.Request.Context(), id, *req.Enabled)
  186. if err != nil {
  187. if globalPricingNotFound(c, err) {
  188. return
  189. }
  190. response.ErrorFrom(c, err)
  191. return
  192. }
  193. response.Success(c, item)
  194. }