feat(subscribe): add Language field to subscription models and update query logic

This commit is contained in:
Chang lue Tsen 2025-09-03 15:44:04 -04:00
parent f8e6cf515e
commit f632ea2c89
24 changed files with 75 additions and 259 deletions

View File

@ -35,6 +35,7 @@ type (
}
CreateSubscribeRequest {
Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -45,7 +46,6 @@ type (
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show *bool `json:"show"`
@ -58,6 +58,7 @@ type (
UpdateSubscribeRequest {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -68,7 +69,6 @@ type (
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show *bool `json:"show"`
@ -83,10 +83,10 @@ type (
Sort []SortItem `json:"sort"`
}
GetSubscribeListRequest {
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
GroupId int64 `form:"group_id,omitempty"`
Search string `form:"search,omitempty"`
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
Language string `form:"language,omitempty"`
Search string `form:"search,omitempty"`
}
SubscribeItem {
Subscribe

View File

@ -42,10 +42,6 @@ service ppanel {
@handler UpdateSubscribeConfig
put /subscribe_config (SubscribeConfig)
@doc "Get subscribe type"
@handler GetSubscribeType
get /subscribe_type returns (SubscribeType)
@doc "Get register config"
@handler GetRegisterConfig
get /register_config returns (RegisterConfig)

View File

@ -10,6 +10,12 @@ info (
import "../types.api"
type (
QuerySubscribeListRequest {
Language string `form:"language"`
}
)
@server (
prefix: v1/public/subscribe
group: public/subscribe
@ -18,10 +24,6 @@ import "../types.api"
service ppanel {
@doc "Get subscribe list"
@handler QuerySubscribeList
get /list returns (QuerySubscribeListResponse)
@doc "Get subscribe group list"
@handler QuerySubscribeGroupList
get /group/list returns (QuerySubscribeGroupListResponse)
get /list (QuerySubscribeListRequest) returns (QuerySubscribeListResponse)
}

View File

@ -185,6 +185,7 @@ type (
Subscribe {
Id int64 `json:"id"`
Name string `json:"name"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -195,7 +196,6 @@ type (
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show bool `json:"show"`

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `subscribe_type`;
DROP TABLE IF EXISTS `sms`;

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `subscribe_type`;
DROP TABLE IF EXISTS `sms`;

View File

@ -0,0 +1,7 @@
ALTER TABLE `subscribe`
DROP COLUMN `group_id`,
ADD COLUMN `language` VARCHAR(255) NOT NULL DEFAULT ''
COMMENT 'Language'
AFTER `name`;
DROP TABLE IF EXISTS `subscribe_group`;

View File

@ -1,18 +0,0 @@
package system
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/admin/system"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
// Get subscribe type
func GetSubscribeTypeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
l := system.NewGetSubscribeTypeLogic(c.Request.Context(), svcCtx)
resp, err := l.GetSubscribeType()
result.HttpResult(c, resp, err)
}
}

View File

@ -4,15 +4,23 @@ import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/subscribe"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/result"
)
// Get subscribe list
// QuerySubscribeListHandler Get subscribe list
func QuerySubscribeListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
var req types.QuerySubscribeListRequest
_ = c.ShouldBind(&req)
validateErr := svcCtx.Validate(&req)
if validateErr != nil {
result.ParamErrorResult(c, validateErr)
return
}
l := subscribe.NewQuerySubscribeListLogic(c.Request.Context(), svcCtx)
resp, err := l.QuerySubscribeList()
resp, err := l.QuerySubscribeList(&req)
result.HttpResult(c, resp, err)
}
}

View File

@ -435,9 +435,6 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Update subscribe config
adminSystemGroupRouter.PUT("/subscribe_config", adminSystem.UpdateSubscribeConfigHandler(serverCtx))
// Get subscribe type
adminSystemGroupRouter.GET("/subscribe_type", adminSystem.GetSubscribeTypeHandler(serverCtx))
// Get Team of Service Config
adminSystemGroupRouter.GET("/tos_config", adminSystem.GetTosConfigHandler(serverCtx))
@ -722,9 +719,6 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
publicSubscribeGroupRouter.Use(middleware.AuthMiddleware(serverCtx))
{
// Get subscribe group list
publicSubscribeGroupRouter.GET("/group/list", publicSubscribe.QuerySubscribeGroupListHandler(serverCtx))
// Get subscribe list
publicSubscribeGroupRouter.GET("/list", publicSubscribe.QuerySubscribeListHandler(serverCtx))
}

View File

@ -37,6 +37,7 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
sub := &subscribe.Subscribe{
Id: 0,
Name: req.Name,
Language: req.Language,
Description: req.Description,
UnitPrice: req.UnitPrice,
UnitTime: req.UnitTime,
@ -47,7 +48,6 @@ func (l *CreateSubscribeLogic) CreateSubscribe(req *types.CreateSubscribeRequest
SpeedLimit: req.SpeedLimit,
DeviceLimit: req.DeviceLimit,
Quota: req.Quota,
GroupId: req.GroupId,
Nodes: tool.Int64SliceToString(req.Nodes),
NodeTags: tool.StringSliceToString(req.NodeTags),
Show: req.Show,

View File

@ -29,7 +29,7 @@ func NewGetSubscribeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *
}
func (l *GetSubscribeListLogic) GetSubscribeList(req *types.GetSubscribeListRequest) (resp *types.GetSubscribeListResponse, err error) {
total, list, err := l.svcCtx.SubscribeModel.QuerySubscribeListByPage(l.ctx, int(req.Page), int(req.Size), req.GroupId, req.Search)
total, list, err := l.svcCtx.SubscribeModel.QuerySubscribeListByPage(l.ctx, int(req.Page), int(req.Size), req.Language, req.Search)
if err != nil {
l.Logger.Error("[GetSubscribeListLogic] get subscribe list failed: ", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe list failed: %v", err.Error())

View File

@ -45,6 +45,7 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
sub := &subscribe.Subscribe{
Id: req.Id,
Name: req.Name,
Language: req.Language,
Description: req.Description,
UnitPrice: req.UnitPrice,
UnitTime: req.UnitTime,
@ -55,7 +56,6 @@ func (l *UpdateSubscribeLogic) UpdateSubscribe(req *types.UpdateSubscribeRequest
SpeedLimit: req.SpeedLimit,
DeviceLimit: req.DeviceLimit,
Quota: req.Quota,
GroupId: req.GroupId,
Nodes: tool.Int64SliceToString(req.Nodes),
NodeTags: tool.StringSliceToString(req.NodeTags),
Show: req.Show,

View File

@ -1,42 +0,0 @@
package system
import (
"context"
"github.com/perfect-panel/server/internal/model/subscribeType"
"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/xerr"
"github.com/pkg/errors"
)
type GetSubscribeTypeLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logger.Logger
}
func NewGetSubscribeTypeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSubscribeTypeLogic {
return &GetSubscribeTypeLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logger.WithContext(ctx),
}
}
func (l *GetSubscribeTypeLogic) GetSubscribeType() (resp *types.SubscribeType, err error) {
var list []*subscribeType.SubscribeType
err = l.svcCtx.DB.Model(&subscribeType.SubscribeType{}).Find(&list).Error
if err != nil {
l.Errorw("[GetSubscribeType] get subscribe type failed", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "get subscribe type failed: %v", err)
}
typeList := make([]string, 0)
for _, item := range list {
typeList = append(typeList, item.Name)
}
return &types.SubscribeType{
SubscribeTypes: typeList,
}, nil
}

View File

@ -27,13 +27,22 @@ func NewQuerySubscribeListLogic(ctx context.Context, svcCtx *svc.ServiceContext)
}
}
func (l *QuerySubscribeListLogic) QuerySubscribeList() (resp *types.QuerySubscribeListResponse, err error) {
func (l *QuerySubscribeListLogic) QuerySubscribeList(req *types.QuerySubscribeListRequest) (resp *types.QuerySubscribeListResponse, err error) {
data, err := l.svcCtx.SubscribeModel.QuerySubscribeList(l.ctx)
total, data, err := l.svcCtx.SubscribeModel.QuerySubscribeListByPage(l.ctx, 1, 1000, req.Language, "")
if err != nil {
l.Errorw("[QuerySubscribeListLogic] Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QuerySubscribeList error: %v", err.Error())
}
// If no data is found for the specified language, query default language data
if total == 0 && req.Language != "" {
total, data, err = l.svcCtx.SubscribeModel.QuerySubscribeListByPage(l.ctx, 1, 1000, "", "")
if err != nil {
l.Errorw("[QuerySubscribeListLogic] Database Error", logger.Field("error", err.Error()))
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "QuerySubscribeList error: %v", err.Error())
}
}
resp = &types.QuerySubscribeListResponse{
Total: int64(len(data)),
}

View File

@ -26,6 +26,7 @@ type ServerFilter struct {
Size int
}
// Deprecated: use internal/model/node/server.go
type Server struct {
Id int64 `gorm:"primary_key"`
Name string `gorm:"type:varchar(100);not null;default:'';comment:Node Name"`

View File

@ -8,7 +8,7 @@ import (
)
type customSubscribeLogicModel interface {
QuerySubscribeListByPage(ctx context.Context, page, size int, group int64, search string) (total int64, list []*Subscribe, err error)
QuerySubscribeListByPage(ctx context.Context, page, size int, lang string, search string) (total int64, list []*Subscribe, err error)
QuerySubscribeList(ctx context.Context) ([]*Subscribe, error)
QuerySubscribeListByShow(ctx context.Context) ([]*Subscribe, error)
QuerySubscribeIdsByNodeIdAndNodeTag(ctx context.Context, node []int64, tags []string) ([]*Subscribe, error)
@ -25,7 +25,7 @@ func NewModel(conn *gorm.DB, c *redis.Client) Model {
}
// QuerySubscribeListByPage Get Subscribe List
func (m *customSubscribeModel) QuerySubscribeListByPage(ctx context.Context, page, size int, group int64, search string) (total int64, list []*Subscribe, err error) {
func (m *customSubscribeModel) QuerySubscribeListByPage(ctx context.Context, page, size int, lang string, search string) (total int64, list []*Subscribe, err error) {
err = m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
// About to be abandoned
_ = conn.Model(&Subscribe{}).
@ -33,13 +33,14 @@ func (m *customSubscribeModel) QuerySubscribeListByPage(ctx context.Context, pag
Update("sort", gorm.Expr("id"))
conn = conn.Model(&Subscribe{})
if group > 0 {
conn = conn.Where("group_id = ?", group)
if lang != "" {
conn = conn.Where("`language` = ?", lang)
}
if search != "" {
conn = conn.Where("`name` like ? or `description` like ?", "%"+search+"%", "%"+search+"%")
}
return conn.Count(&total).Order("sort ASC").Limit(size).Offset((page - 1) * size).Find(v).Error
err = conn.Count(&total).Order("sort ASC").Limit(size).Offset((page - 1) * size).Find(v).Error
return nil
})
return total, list, err
}

View File

@ -9,6 +9,7 @@ import (
type Subscribe struct {
Id int64 `gorm:"primaryKey"`
Name string `gorm:"type:varchar(255);not null;default:'';comment:Subscribe Name"`
Language string `gorm:"type:varchar(255);not null;default:'';comment:Language"`
Description string `gorm:"type:text;comment:Subscribe Description"`
UnitPrice int64 `gorm:"type:int;not null;default:0;comment:Unit Price"`
UnitTime string `gorm:"type:varchar(255);not null;default:'';comment:Unit Time"`
@ -19,7 +20,6 @@ type Subscribe struct {
SpeedLimit int64 `gorm:"type:int;not null;default:0;comment:Speed Limit"`
DeviceLimit int64 `gorm:"type:int;not null;default:0;comment:Device Limit"`
Quota int64 `gorm:"type:int;not null;default:0;comment:Quota"`
GroupId int64 `gorm:"type:bigint;comment:Group Id"`
Nodes string `gorm:"type:varchar(255);comment:Node Ids"`
NodeTags string `gorm:"type:varchar(255);comment:Node Tags"`
Show *bool `gorm:"type:tinyint(1);not null;default:0;comment:Show portal page"`

View File

@ -1,117 +0,0 @@
package subscribeType
import (
"context"
"errors"
"fmt"
"github.com/perfect-panel/server/pkg/cache"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
var _ Model = (*customSubscribeTypeModel)(nil)
var (
cacheSubscribeTypeIdPrefix = "cache:subscribeType:id:"
)
type (
Model interface {
subscribeTypeModel
customSubscribeTypeLogicModel
}
subscribeTypeModel interface {
Insert(ctx context.Context, data *SubscribeType) error
FindOne(ctx context.Context, id int64) (*SubscribeType, error)
Update(ctx context.Context, data *SubscribeType) error
Delete(ctx context.Context, id int64) error
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
}
customSubscribeTypeModel struct {
*defaultSubscribeTypeModel
}
defaultSubscribeTypeModel struct {
cache.CachedConn
table string
}
)
func newSubscribeTypeModel(db *gorm.DB, c *redis.Client) *defaultSubscribeTypeModel {
return &defaultSubscribeTypeModel{
CachedConn: cache.NewConn(db, c),
table: "`SubscribeType`",
}
}
//nolint:unused
func (m *defaultSubscribeTypeModel) batchGetCacheKeys(SubscribeTypes ...*SubscribeType) []string {
var keys []string
for _, subscribeType := range SubscribeTypes {
keys = append(keys, m.getCacheKeys(subscribeType)...)
}
return keys
}
func (m *defaultSubscribeTypeModel) getCacheKeys(data *SubscribeType) []string {
if data == nil {
return []string{}
}
SubscribeTypeIdKey := fmt.Sprintf("%s%v", cacheSubscribeTypeIdPrefix, data.Id)
cacheKeys := []string{
SubscribeTypeIdKey,
}
return cacheKeys
}
func (m *defaultSubscribeTypeModel) Insert(ctx context.Context, data *SubscribeType) error {
err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
return conn.Create(&data).Error
}, m.getCacheKeys(data)...)
return err
}
func (m *defaultSubscribeTypeModel) FindOne(ctx context.Context, id int64) (*SubscribeType, error) {
SubscribeTypeIdKey := fmt.Sprintf("%s%v", cacheSubscribeTypeIdPrefix, id)
var resp SubscribeType
err := m.QueryCtx(ctx, &resp, SubscribeTypeIdKey, func(conn *gorm.DB, v interface{}) error {
return conn.Model(&SubscribeType{}).Where("`id` = ?", id).First(&resp).Error
})
switch {
case err == nil:
return &resp, nil
default:
return nil, err
}
}
func (m *defaultSubscribeTypeModel) Update(ctx context.Context, data *SubscribeType) error {
old, err := m.FindOne(ctx, data.Id)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
db := conn
return db.Save(data).Error
}, m.getCacheKeys(old)...)
return err
}
func (m *defaultSubscribeTypeModel) Delete(ctx context.Context, id int64) error {
data, err := m.FindOne(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}
err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
db := conn
return db.Delete(&SubscribeType{}, id).Error
}, m.getCacheKeys(data)...)
return err
}
func (m *defaultSubscribeTypeModel) Transaction(ctx context.Context, fn func(db *gorm.DB) error) error {
return m.TransactCtx(ctx, fn)
}

View File

@ -1,16 +0,0 @@
package subscribeType
import (
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
type customSubscribeTypeLogicModel interface {
}
// NewModel returns a model for the database table.
func NewModel(conn *gorm.DB, c *redis.Client) Model {
return &customSubscribeTypeModel{
defaultSubscribeTypeModel: newSubscribeTypeModel(conn, c),
}
}

View File

@ -1,15 +0,0 @@
package subscribeType
import "time"
type SubscribeType struct {
Id int64 `gorm:"primary_key"`
Name string `gorm:"type:varchar(50);default:'';not null;comment:订阅类型"`
Mark string `gorm:"type:varchar(255);default:'';not null;comment:订阅标识"`
CreatedAt time.Time `gorm:"<-:create;comment:创建时间"`
UpdatedAt time.Time `gorm:"comment:更新时间"`
}
func (SubscribeType) TableName() string {
return "subscribe_type"
}

View File

@ -17,7 +17,6 @@ import (
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/payment"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/subscribeType"
"github.com/perfect-panel/server/internal/model/system"
"github.com/perfect-panel/server/internal/model/ticket"
"github.com/perfect-panel/server/internal/model/traffic"
@ -47,14 +46,13 @@ type ServiceContext struct {
ClientModel client.Model
TicketModel ticket.Model
//ServerModel server.Model
SystemModel system.Model
CouponModel coupon.Model
PaymentModel payment.Model
DocumentModel document.Model
SubscribeModel subscribe.Model
TrafficLogModel traffic.Model
AnnouncementModel announcement.Model
SubscribeTypeModel subscribeType.Model
SystemModel system.Model
CouponModel coupon.Model
PaymentModel payment.Model
DocumentModel document.Model
SubscribeModel subscribe.Model
TrafficLogModel traffic.Model
AnnouncementModel announcement.Model
Restart func() error
TelegramBot *tgbotapi.BotAPI

View File

@ -373,6 +373,7 @@ type CreateSubscribeGroupRequest struct {
type CreateSubscribeRequest struct {
Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -383,7 +384,6 @@ type CreateSubscribeRequest struct {
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show *bool `json:"show"`
@ -944,10 +944,10 @@ type GetSubscribeGroupListResponse struct {
}
type GetSubscribeListRequest struct {
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
GroupId int64 `form:"group_id,omitempty"`
Search string `form:"search,omitempty"`
Page int64 `form:"page" validate:"required"`
Size int64 `form:"size" validate:"required"`
Language string `form:"language,omitempty"`
Search string `form:"search,omitempty"`
}
type GetSubscribeListResponse struct {
@ -1522,6 +1522,10 @@ type QuerySubscribeGroupListResponse struct {
Total int64 `json:"total"`
}
type QuerySubscribeListRequest struct {
Language string `form:"language"`
}
type QuerySubscribeListResponse struct {
List []Subscribe `json:"list"`
Total int64 `json:"total"`
@ -1847,6 +1851,7 @@ type StripePayment struct {
type Subscribe struct {
Id int64 `json:"id"`
Name string `json:"name"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -1857,7 +1862,6 @@ type Subscribe struct {
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show bool `json:"show"`
@ -2217,6 +2221,7 @@ type UpdateSubscribeGroupRequest struct {
type UpdateSubscribeRequest struct {
Id int64 `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
Language string `json:"language"`
Description string `json:"description"`
UnitPrice int64 `json:"unit_price"`
UnitTime string `json:"unit_time"`
@ -2227,7 +2232,6 @@ type UpdateSubscribeRequest struct {
SpeedLimit int64 `json:"speed_limit"`
DeviceLimit int64 `json:"device_limit"`
Quota int64 `json:"quota"`
GroupId int64 `json:"group_id"`
Nodes []int64 `json:"nodes"`
NodeTags []string `json:"node_tags"`
Show *bool `json:"show"`