From d351b5006663580cab340af36d51fa2795d71dd1 Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Wed, 27 May 2026 07:13:05 -0700 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(#71):=20=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=91=98=E8=AE=A2=E9=98=85=E9=99=90=E9=80=9F=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=8B=92=E7=BB=9D=E8=B4=9F=E6=95=B0=E9=99=90=E9=80=9F=E5=92=8C?= =?UTF-8?q?=E9=9D=9E=E6=B3=95=20traffic=5Flimit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SpeedLimit 加 validate:"gte=0" 校验 - TrafficLimit 加 JSON 格式校验 - 新增单测覆盖负数限速和非法 JSON 场景 Co-authored-by: multica-agent --- apis/admin/user.api | 2 +- .../admin/user/updateUserSubscribeHandler.go | 19 ++++++ .../user/updateUserSubscribeHandler_test.go | 59 +++++++++++++++++++ .../admin/user/updateUserSubscribeLogic.go | 38 +++++++----- internal/types/types.go | 2 +- 5 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 internal/handler/admin/user/updateUserSubscribeHandler_test.go diff --git a/apis/admin/user.api b/apis/admin/user.api index 3b3b069..7bf6e87 100644 --- a/apis/admin/user.api +++ b/apis/admin/user.api @@ -149,7 +149,7 @@ type ( ExpiredAt int64 `json:"expired_at"` Upload int64 `json:"upload"` Download int64 `json:"download"` - SpeedLimit *int64 `json:"speed_limit,omitempty"` + SpeedLimit *int64 `json:"speed_limit,omitempty" validate:"omitempty,gte=0"` TrafficLimit *string `json:"traffic_limit,omitempty"` } GetUserLoginLogsRequest { diff --git a/internal/handler/admin/user/updateUserSubscribeHandler.go b/internal/handler/admin/user/updateUserSubscribeHandler.go index 3e111d3..4d0c573 100644 --- a/internal/handler/admin/user/updateUserSubscribeHandler.go +++ b/internal/handler/admin/user/updateUserSubscribeHandler.go @@ -1,6 +1,9 @@ package user import ( + "encoding/json" + "errors" + "github.com/gin-gonic/gin" "github.com/perfect-panel/server/internal/logic/admin/user" "github.com/perfect-panel/server/internal/svc" @@ -18,9 +21,25 @@ func UpdateUserSubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) result.ParamErrorResult(c, validateErr) return } + if err := validateUpdateUserSubscribeTrafficLimit(&req); err != nil { + result.ParamErrorResult(c, err) + return + } l := user.NewUpdateUserSubscribeLogic(c.Request.Context(), svcCtx) err := l.UpdateUserSubscribe(&req) result.HttpResult(c, nil, err) } } + +func validateUpdateUserSubscribeTrafficLimit(req *types.UpdateUserSubscribeRequest) error { + if req.TrafficLimit == nil || *req.TrafficLimit == "" { + return nil + } + + var rules []types.TrafficLimit + if err := json.Unmarshal([]byte(*req.TrafficLimit), &rules); err != nil { + return errors.New("traffic_limit must be a valid JSON array") + } + return nil +} diff --git a/internal/handler/admin/user/updateUserSubscribeHandler_test.go b/internal/handler/admin/user/updateUserSubscribeHandler_test.go new file mode 100644 index 0000000..1e7affd --- /dev/null +++ b/internal/handler/admin/user/updateUserSubscribeHandler_test.go @@ -0,0 +1,59 @@ +package user + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/pkg/xerr" +) + +func TestUpdateUserSubscribeHandlerRejectsInvalidLimits(t *testing.T) { + gin.SetMode(gin.TestMode) + + tests := []struct { + name string + body string + }{ + { + name: "negative speed limit", + body: `{"user_subscribe_id":1,"subscribe_id":1,"traffic":0,"expired_at":4102444800000,"upload":0,"download":0,"speed_limit":-1}`, + }, + { + name: "invalid traffic limit json", + body: `{"user_subscribe_id":1,"subscribe_id":1,"traffic":0,"expired_at":4102444800000,"upload":0,"download":0,"traffic_limit":"not-json"}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + router := gin.New() + router.PUT("/v1/admin/user/subscribe", UpdateUserSubscribeHandler(&svc.ServiceContext{})) + + req := httptest.NewRequest(http.MethodPut, "/v1/admin/user/subscribe", bytes.NewBufferString(tt.body)) + req.Header.Set("Content-Type", "application/json") + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected HTTP 200, got %d", rec.Code) + } + + var resp struct { + Code uint32 `json:"code"` + Msg string `json:"msg"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { + t.Fatalf("unmarshal response: %v", err) + } + if resp.Code != xerr.InvalidParams { + t.Fatalf("expected code %d, got %d (%s)", xerr.InvalidParams, resp.Code, resp.Msg) + } + }) + } +} diff --git a/internal/logic/admin/user/updateUserSubscribeLogic.go b/internal/logic/admin/user/updateUserSubscribeLogic.go index 6ec2267..ca03c22 100644 --- a/internal/logic/admin/user/updateUserSubscribeLogic.go +++ b/internal/logic/admin/user/updateUserSubscribeLogic.go @@ -39,22 +39,32 @@ func (l *UpdateUserSubscribeLogic) UpdateUserSubscribe(req *types.UpdateUserSubs } else { userSub.Status = 1 } + speedLimit := userSub.SpeedLimit + if req.SpeedLimit != nil { + speedLimit = *req.SpeedLimit + } + trafficLimit := userSub.TrafficLimit + if req.TrafficLimit != nil { + trafficLimit = *req.TrafficLimit + } err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, &user.Subscribe{ - Id: userSub.Id, - UserId: userSub.UserId, - OrderId: userSub.OrderId, - SubscribeId: req.SubscribeId, - StartTime: userSub.StartTime, - ExpireTime: time.UnixMilli(req.ExpiredAt), - Traffic: req.Traffic, - Download: req.Download, - Upload: req.Upload, - Token: userSub.Token, - UUID: userSub.UUID, - Status: userSub.Status, - NodeGroupId: userSub.NodeGroupId, - GroupLocked: userSub.GroupLocked, + Id: userSub.Id, + UserId: userSub.UserId, + OrderId: userSub.OrderId, + SubscribeId: req.SubscribeId, + StartTime: userSub.StartTime, + ExpireTime: time.UnixMilli(req.ExpiredAt), + Traffic: req.Traffic, + Download: req.Download, + Upload: req.Upload, + SpeedLimit: speedLimit, + TrafficLimit: trafficLimit, + Token: userSub.Token, + UUID: userSub.UUID, + Status: userSub.Status, + NodeGroupId: userSub.NodeGroupId, + GroupLocked: userSub.GroupLocked, }) if err != nil { diff --git a/internal/types/types.go b/internal/types/types.go index cce77d7..c1a599a 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -3442,7 +3442,7 @@ type UpdateUserSubscribeRequest struct { ExpiredAt int64 `json:"expired_at"` Upload int64 `json:"upload"` Download int64 `json:"download"` - SpeedLimit *int64 `json:"speed_limit,omitempty"` + SpeedLimit *int64 `json:"speed_limit,omitempty" validate:"omitempty,gte=0"` TrafficLimit *string `json:"traffic_limit,omitempty"` }