diff --git a/initialize/migrate/database/02119_user_subscribe_note.down.sql b/initialize/migrate/database/02119_user_subscribe_note.down.sql new file mode 100644 index 0000000..60cc0e8 --- /dev/null +++ b/initialize/migrate/database/02119_user_subscribe_note.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE `user_subscribe` +DROP COLUMN `note`; diff --git a/initialize/migrate/database/02119_user_subscribe_note.up.sql b/initialize/migrate/database/02119_user_subscribe_note.up.sql new file mode 100644 index 0000000..b8b6983 --- /dev/null +++ b/initialize/migrate/database/02119_user_subscribe_note.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE `user_subscribe` +ADD COLUMN `note` VARCHAR(500) NOT NULL DEFAULT '' + COMMENT 'User note for subscription' + AFTER `status`; diff --git a/internal/handler/public/user/updateUserSubscribeNoteHandler.go b/internal/handler/public/user/updateUserSubscribeNoteHandler.go new file mode 100644 index 0000000..17b77bf --- /dev/null +++ b/internal/handler/public/user/updateUserSubscribeNoteHandler.go @@ -0,0 +1,26 @@ +package user + +import ( + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/logic/public/user" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/result" +) + +// Update User Subscribe Note +func UpdateUserSubscribeNoteHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) { + return func(c *gin.Context) { + var req types.UpdateUserSubscribeNoteRequest + _ = c.ShouldBind(&req) + validateErr := svcCtx.Validate(&req) + if validateErr != nil { + result.ParamErrorResult(c, validateErr) + return + } + + l := user.NewUpdateUserSubscribeNoteLogic(c.Request.Context(), svcCtx) + err := l.UpdateUserSubscribeNote(&req) + result.HttpResult(c, nil, err) + } +} diff --git a/internal/handler/routes.go b/internal/handler/routes.go index d42ac7e..943ba84 100644 --- a/internal/handler/routes.go +++ b/internal/handler/routes.go @@ -822,6 +822,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { // Reset User Subscribe Token publicUserGroupRouter.PUT("/subscribe_token", publicUser.ResetUserSubscribeTokenHandler(serverCtx)) + // Update User Subscribe Note + publicUserGroupRouter.PUT("/subscribe_note", publicUser.UpdateUserSubscribeNoteHandler(serverCtx)) + // Unbind Device publicUserGroupRouter.PUT("/unbind_device", publicUser.UnbindDeviceHandler(serverCtx)) diff --git a/internal/logic/public/user/updateUserSubscribeNoteLogic.go b/internal/logic/public/user/updateUserSubscribeNoteLogic.go new file mode 100644 index 0000000..3c43a8d --- /dev/null +++ b/internal/logic/public/user/updateUserSubscribeNoteLogic.go @@ -0,0 +1,73 @@ +package user + +import ( + "context" + + "github.com/perfect-panel/server/pkg/constant" + + "github.com/perfect-panel/server/internal/model/user" + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/internal/types" + "github.com/perfect-panel/server/pkg/logger" + "github.com/perfect-panel/server/pkg/tool" + "github.com/perfect-panel/server/pkg/xerr" + "github.com/pkg/errors" +) + +type UpdateUserSubscribeNoteLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// NewUpdateUserSubscribeNoteLogic Update User Subscribe Note +func NewUpdateUserSubscribeNoteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserSubscribeNoteLogic { + return &UpdateUserSubscribeNoteLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *UpdateUserSubscribeNoteLogic) UpdateUserSubscribeNote(req *types.UpdateUserSubscribeNoteRequest) error { + u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User) + if !ok { + logger.Error("current user is not found in context") + return errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access") + } + + userSub, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeId) + if err != nil { + l.Errorw("FindOneUserSubscribe failed:", logger.Field("error", err.Error())) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "FindOneUserSubscribe failed: %v", err.Error()) + } + + if userSub.UserId != u.Id { + l.Errorw("UserSubscribeId does not belong to the current user") + return errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "UserSubscribeId does not belong to the current user") + } + + userSub.Note = req.Note + var newSub user.Subscribe + tool.DeepCopy(&newSub, userSub) + + err = l.svcCtx.UserModel.UpdateSubscribe(l.ctx, &newSub) + if err != nil { + l.Errorw("UpdateSubscribe failed:", logger.Field("error", err.Error())) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "UpdateSubscribe failed: %v", err.Error()) + } + + // Clear user subscription cache + if err = l.svcCtx.UserModel.ClearSubscribeCache(l.ctx, &newSub); err != nil { + l.Errorw("ClearSubscribeCache failed", logger.Field("error", err.Error()), logger.Field("userSubscribeId", userSub.Id)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error()) + } + + // Clear subscription cache + if err = l.svcCtx.SubscribeModel.ClearCache(l.ctx, userSub.SubscribeId); err != nil { + l.Errorw("ClearSubscribeCache failed", logger.Field("error", err.Error()), logger.Field("subscribeId", userSub.SubscribeId)) + return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "ClearSubscribeCache failed: %v", err.Error()) + } + + return nil +} diff --git a/internal/model/user/model.go b/internal/model/user/model.go index ffc5020..86caa0d 100644 --- a/internal/model/user/model.go +++ b/internal/model/user/model.go @@ -36,6 +36,7 @@ type SubscribeDetails struct { Token string `gorm:"index:idx_token;unique;type:varchar(255);default:'';comment:Token"` UUID string `gorm:"type:varchar(255);unique;index:idx_uuid;default:'';comment:UUID"` Status uint8 `gorm:"type:tinyint(1);default:0;comment:Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired; 4: Cancelled"` + Note string `gorm:"type:varchar(500);default:'';comment:User note for subscription"` CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } diff --git a/internal/model/user/user.go b/internal/model/user/user.go index 98bfbcb..3344745 100644 --- a/internal/model/user/user.go +++ b/internal/model/user/user.go @@ -48,6 +48,7 @@ type Subscribe struct { Token string `gorm:"index:idx_token;unique;type:varchar(255);default:'';comment:Token"` UUID string `gorm:"type:varchar(255);unique;index:idx_uuid;default:'';comment:UUID"` Status uint8 `gorm:"type:tinyint(1);default:0;comment:Subscription Status: 0: Pending 1: Active 2: Finished 3: Expired 4: Deducted"` + Note string `gorm:"type:varchar(500);default:'';comment:User note for subscription"` CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } diff --git a/internal/types/types.go b/internal/types/types.go index 4e481ff..9611bac 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1804,6 +1804,11 @@ type ResetUserSubscribeTokenRequest struct { UserSubscribeId int64 `json:"user_subscribe_id"` } +type UpdateUserSubscribeNoteRequest struct { + UserSubscribeId int64 `json:"user_subscribe_id" validate:"required"` + Note string `json:"note" validate:"max=500"` +} + type RevenueStatisticsResponse struct { Today OrdersStatistics `json:"today"` Monthly OrdersStatistics `json:"monthly"` @@ -2576,6 +2581,7 @@ type UserSubscribe struct { Upload int64 `json:"upload"` Token string `json:"token"` Status uint8 `json:"status"` + Note string `json:"note"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } @@ -2595,6 +2601,7 @@ type UserSubscribeDetail struct { Upload int64 `json:"upload"` Token string `json:"token"` Status uint8 `json:"status"` + Note string `json:"note"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` } @@ -2613,6 +2620,7 @@ type UserSubscribeInfo struct { Upload int64 `json:"upload"` Token string `json:"token"` Status uint8 `json:"status"` + Note string `json:"note"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` IsTryOut bool `json:"is_try_out"`