feat(subscription): add admin quota reset actions (#5952)

* feat(subscription): add admin quota reset actions

* fix(subscription): keep quota reset in plan row actions

* refactor(subscription): move user subscription actions into menu
This commit is contained in:
Seefs
2026-07-07 12:47:41 +08:00
committed by GitHub
parent d1abf78ec0
commit 9b93d61b7f
17 changed files with 729 additions and 26 deletions
+3
View File
@@ -46,6 +46,9 @@ var auditContentTemplates = map[string]string{
"channel.upstream_apply_all": "Applied upstream model changes to ${count} channels",
"redemption.create": "Created ${count} redemption codes named ${name} (${quota} each)",
"subscription.plan_reset": "Reset active subscriptions for plan ${plan_id}",
"subscription.user_plan_reset": "Reset active plan ${plan_id} subscriptions for user ${target_user_id}",
}
// auditContentEN 按 action 模板渲染英文兜底文本;未登记的 action 退回 action 本身。
+86
View File
@@ -1,6 +1,7 @@
package controller
import (
"fmt"
"strconv"
"strings"
@@ -394,6 +395,28 @@ type AdminCreateUserSubscriptionRequest struct {
PlanId int `json:"plan_id"`
}
type AdminResetSubscriptionRequest struct {
PlanId int `json:"plan_id"`
AdvanceResetTime *bool `json:"advance_reset_time"`
}
func resolveAdvanceResetTime(value *bool) bool {
if value == nil {
return true
}
return *value
}
func recordSubscriptionResetUserLogs(result *model.SubscriptionResetResult, adminInfo map[string]interface{}) {
if result == nil || result.ResetCount == 0 {
return
}
content := fmt.Sprintf("管理员重置订阅套餐 %sID: %d)额度", result.PlanTitle, result.PlanId)
for _, userId := range result.AffectedUserIds {
model.RecordLogWithAdminInfo(userId, model.LogTypeManage, content, adminInfo)
}
}
// AdminCreateUserSubscription creates a new user subscription from a plan (no payment).
func AdminCreateUserSubscription(c *gin.Context) {
if !requirePaymentCompliance(c) {
@@ -422,6 +445,69 @@ func AdminCreateUserSubscription(c *gin.Context) {
common.ApiSuccess(c, nil)
}
func AdminResetUserSubscriptionsByPlan(c *gin.Context) {
userId, _ := strconv.Atoi(c.Param("id"))
if userId <= 0 {
common.ApiErrorMsg(c, "无效的用户ID")
return
}
var req AdminResetSubscriptionRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ApiErrorMsg(c, "参数错误")
return
}
if req.PlanId <= 0 {
common.ApiErrorMsg(c, "参数错误")
return
}
advanceResetTime := resolveAdvanceResetTime(req.AdvanceResetTime)
result, err := model.AdminResetUserSubscriptionsByPlan(userId, req.PlanId, advanceResetTime)
if err != nil {
common.ApiError(c, err)
return
}
recordSubscriptionResetUserLogs(result, auditOperatorInfo(c))
recordManageAuditFor(c, userId, "subscription.user_plan_reset", map[string]interface{}{
"target_user_id": userId,
"plan_id": result.PlanId,
"plan_title": result.PlanTitle,
"reset_count": result.ResetCount,
"user_count": result.UserCount,
"advance_reset_time": result.AdvanceResetTime,
})
common.ApiSuccess(c, result)
}
func AdminResetPlanSubscriptions(c *gin.Context) {
planId, _ := strconv.Atoi(c.Param("id"))
if planId <= 0 {
common.ApiErrorMsg(c, "无效的ID")
return
}
var req AdminResetSubscriptionRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.ApiErrorMsg(c, "参数错误")
return
}
advanceResetTime := resolveAdvanceResetTime(req.AdvanceResetTime)
result, err := model.AdminResetPlanSubscriptions(planId, advanceResetTime)
if err != nil {
common.ApiError(c, err)
return
}
recordSubscriptionResetUserLogs(result, auditOperatorInfo(c))
common.SysLog(fmt.Sprintf("admin reset subscription plan %d quota: reset_count=%d user_count=%d advance_reset_time=%t",
result.PlanId, result.ResetCount, result.UserCount, result.AdvanceResetTime))
recordManageAudit(c, "subscription.plan_reset", map[string]interface{}{
"plan_id": result.PlanId,
"plan_title": result.PlanTitle,
"reset_count": result.ResetCount,
"user_count": result.UserCount,
"advance_reset_time": result.AdvanceResetTime,
})
common.ApiSuccess(c, result)
}
// AdminInvalidateUserSubscription cancels a user subscription immediately.
func AdminInvalidateUserSubscription(c *gin.Context) {
subId, _ := strconv.Atoi(c.Param("id"))