diff --git a/backend/cmd/server/VERSION b/backend/cmd/server/VERSION
index 01eddd222f..9a57afb880 100644
--- a/backend/cmd/server/VERSION
+++ b/backend/cmd/server/VERSION
@@ -1 +1 @@
-0.1.110.34
+0.1.110.35
diff --git a/backend/internal/service/payment_config_plans.go b/backend/internal/service/payment_config_plans.go
index 8a3e2950cc..ca8b32d270 100644
--- a/backend/internal/service/payment_config_plans.go
+++ b/backend/internal/service/payment_config_plans.go
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
+ "strings"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/ent/group"
@@ -10,6 +11,26 @@ import (
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
)
+// validatePlanRequired checks that all required fields for a plan are provided.
+func validatePlanRequired(name string, groupID int64, price float64, validityDays int, validityUnit string) error {
+ if strings.TrimSpace(name) == "" {
+ return infraerrors.BadRequest("PLAN_NAME_REQUIRED", "plan name is required")
+ }
+ if groupID <= 0 {
+ return infraerrors.BadRequest("PLAN_GROUP_REQUIRED", "group is required")
+ }
+ if price < 0 {
+ return infraerrors.BadRequest("PLAN_PRICE_INVALID", "price must be >= 0")
+ }
+ if validityDays <= 0 {
+ return infraerrors.BadRequest("PLAN_VALIDITY_REQUIRED", "validity days must be > 0")
+ }
+ if strings.TrimSpace(validityUnit) == "" {
+ return infraerrors.BadRequest("PLAN_VALIDITY_UNIT_REQUIRED", "validity unit is required")
+ }
+ return nil
+}
+
// --- Plan CRUD ---
// PlanGroupInfo holds the group details needed for subscription plan display.
@@ -66,14 +87,17 @@ func (s *PaymentConfigService) GetGroupInfoMap(ctx context.Context, plans []*dbe
}
func (s *PaymentConfigService) ListPlans(ctx context.Context) ([]*dbent.SubscriptionPlan, error) {
- return s.entClient.SubscriptionPlan.Query().Order(subscriptionplan.BySortOrder()).All(ctx)
+ return s.entClient.SubscriptionPlan.Query().Order(subscriptionplan.ByCreatedAt()).All(ctx)
}
func (s *PaymentConfigService) ListPlansForSale(ctx context.Context) ([]*dbent.SubscriptionPlan, error) {
- return s.entClient.SubscriptionPlan.Query().Where(subscriptionplan.ForSaleEQ(true)).Order(subscriptionplan.BySortOrder()).All(ctx)
+ return s.entClient.SubscriptionPlan.Query().Where(subscriptionplan.ForSaleEQ(true)).Order(subscriptionplan.ByCreatedAt()).All(ctx)
}
func (s *PaymentConfigService) CreatePlan(ctx context.Context, req CreatePlanRequest) (*dbent.SubscriptionPlan, error) {
+ if err := validatePlanRequired(req.Name, req.GroupID, req.Price, req.ValidityDays, req.ValidityUnit); err != nil {
+ return nil, err
+ }
b := s.entClient.SubscriptionPlan.Create().
SetGroupID(req.GroupID).SetName(req.Name).SetDescription(req.Description).
SetPrice(req.Price).SetValidityDays(req.ValidityDays).SetValidityUnit(req.ValidityUnit).
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index a21082690f..9986c42260 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -5412,6 +5412,7 @@ export default {
tabPlanConfig: 'Plan Configuration',
tabUserSubs: 'User Subscriptions',
selectGroup: 'Select a group',
+ groupRequired: 'Please select a subscription group',
groupMissing: 'Missing',
groupInfo: 'Group Info',
platform: 'Platform',
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index f8d6b8588f..60bc6b1dc1 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -5607,6 +5607,7 @@ export default {
tabPlanConfig: '套餐配置',
tabUserSubs: '用户订阅',
selectGroup: '请选择分组',
+ groupRequired: '请选择订阅分组',
groupMissing: '缺失',
groupInfo: '分组信息',
platform: '平台',
diff --git a/frontend/src/views/admin/orders/AdminPaymentPlansView.vue b/frontend/src/views/admin/orders/AdminPaymentPlansView.vue
index 639b4f666e..fc7bff44aa 100644
--- a/frontend/src/views/admin/orders/AdminPaymentPlansView.vue
+++ b/frontend/src/views/admin/orders/AdminPaymentPlansView.vue
@@ -29,7 +29,7 @@