refactor: 更新项目引用路径从perfect-panel/ppanel-server到perfect-panel/server
feat: 添加版本和构建时间变量 fix: 修正短信队列类型注释错误 style: 清理未使用的代码和测试文件 docs: 更新安装文档中的下载链接 chore: 迁移数据库脚本添加日志和订阅配置
This commit is contained in:
@@ -4,8 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/cache"
|
||||
"github.com/perfect-panel/server/internal/model/node"
|
||||
"github.com/perfect-panel/server/pkg/cache"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -57,11 +60,34 @@ func (m *defaultSubscribeModel) getCacheKeys(data *Subscribe) []string {
|
||||
if data == nil {
|
||||
return []string{}
|
||||
}
|
||||
SubscribeIdKey := fmt.Sprintf("%s%v", cacheSubscribeIdPrefix, data.Id)
|
||||
cacheKeys := []string{
|
||||
SubscribeIdKey,
|
||||
var keys []string
|
||||
if data.Nodes != "" {
|
||||
var nodes []*node.Node
|
||||
ids := strings.Split(data.Nodes, ",")
|
||||
|
||||
err := m.QueryNoCacheCtx(context.Background(), &nodes, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&node.Node{}).Where("id IN (?)", tool.StringSliceToInt64Slice(ids)).Find(&nodes).Error
|
||||
})
|
||||
if err == nil {
|
||||
for _, n := range nodes {
|
||||
keys = append(keys, fmt.Sprintf("%s%d", node.ServerUserListCacheKey, n.ServerId))
|
||||
}
|
||||
}
|
||||
}
|
||||
return cacheKeys
|
||||
if data.NodeTags != "" {
|
||||
var nodes []*node.Node
|
||||
tags := tool.RemoveDuplicateElements(strings.Split(data.NodeTags, ",")...)
|
||||
err := m.QueryNoCacheCtx(context.Background(), &nodes, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&node.Node{}).Scopes(InSet("tags", tags)).Find(&nodes).Error
|
||||
})
|
||||
if err == nil {
|
||||
for _, n := range nodes {
|
||||
keys = append(keys, fmt.Sprintf("%s%d", node.ServerUserListCacheKey, n.ServerId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return append(keys, fmt.Sprintf("%s%v", cacheSubscribeIdPrefix, data.Id))
|
||||
}
|
||||
|
||||
func (m *defaultSubscribeModel) Insert(ctx context.Context, data *Subscribe, tx ...*gorm.DB) error {
|
||||
|
||||
@@ -3,38 +3,37 @@ package subscribe
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// type Details struct {
|
||||
// Id int64 `gorm:"primaryKey"`
|
||||
// Name string `gorm:"type:varchar(255);not null;default:'';comment:Subscribe Name"`
|
||||
// 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"`
|
||||
// Discount string `gorm:"type:text;comment:Discount"`
|
||||
// Replacement int64 `gorm:"type:int;not null;default:0;comment:Replacement"`
|
||||
// Inventory int64 `gorm:"type:int;not null;default:0;comment:Inventory"`
|
||||
// Traffic int64 `gorm:"type:int;not null;default:0;comment:Traffic"`
|
||||
// SpeedLimit int64 `gorm:"type:int;not null;default:0;comment:Speed Limit"`
|
||||
// DeviceLimit int64 `gorm:"type:int;not null;default:0;comment:Device Limit"`
|
||||
// GroupId int64 `gorm:"type:bigint;comment:Group Id"`
|
||||
// Quota int64 `gorm:"type:int;not null;default:0;comment:Quota"`
|
||||
// Show *bool `gorm:"type:tinyint(1);not null;default:0;comment:Show"`
|
||||
// Sell *bool `gorm:"type:tinyint(1);not null;default:0;comment:Sell"`
|
||||
// DeductionRatio int64 `gorm:"type:int;default:0;comment:Deduction Ratio"`
|
||||
// PurchaseWithDiscount bool `gorm:"type:tinyint(1);default:0;comment:PurchaseWithDiscount"`
|
||||
// ResetCycle int64 `gorm:"type:int;default:0;comment:Reset Cycle"`
|
||||
// RenewalReset bool `gorm:"type:tinyint(1);default:0;comment:Renew Reset"`
|
||||
// }
|
||||
type FilterParams struct {
|
||||
Page int // Page Number
|
||||
Size int // Page Size
|
||||
Ids []int64 // Subscribe IDs
|
||||
Node []int64 // Node IDs
|
||||
Tags []string // Node Tags
|
||||
Show bool // Show Portal Page
|
||||
Sell bool // Sell
|
||||
Language string // Language
|
||||
DefaultLanguage bool // Default Subscribe Language Data
|
||||
Search string // Search Keywords
|
||||
}
|
||||
|
||||
func (p *FilterParams) Normalize() {
|
||||
if p.Page <= 0 {
|
||||
p.Page = 1
|
||||
}
|
||||
if p.Size <= 0 {
|
||||
p.Size = 10
|
||||
}
|
||||
}
|
||||
|
||||
type customSubscribeLogicModel interface {
|
||||
QuerySubscribeListByPage(ctx context.Context, page, size int, group int64, search string) (total int64, list []*Subscribe, err error)
|
||||
QuerySubscribeList(ctx context.Context) ([]*Subscribe, error)
|
||||
QuerySubscribeListByShow(ctx context.Context) ([]*Subscribe, error)
|
||||
QuerySubscribeIdsByServerIdAndServerGroupId(ctx context.Context, serverId, serverGroupId int64) ([]*Subscribe, error)
|
||||
FilterList(ctx context.Context, params *FilterParams) (int64, []*Subscribe, error)
|
||||
ClearCache(ctx context.Context, id ...int64) error
|
||||
QuerySubscribeMinSortByIds(ctx context.Context, ids []int64) (int64, error)
|
||||
QuerySubscribeListByIds(ctx context.Context, ids []int64) ([]*Subscribe, error)
|
||||
}
|
||||
|
||||
// NewModel returns a model for the database table.
|
||||
@@ -44,54 +43,6 @@ 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) {
|
||||
err = m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
// About to be abandoned
|
||||
_ = conn.Model(&Subscribe{}).
|
||||
Where("sort = ?", 0).
|
||||
Update("sort", gorm.Expr("id"))
|
||||
|
||||
conn = conn.Model(&Subscribe{})
|
||||
if group > 0 {
|
||||
conn = conn.Where("group_id = ?", group)
|
||||
}
|
||||
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
|
||||
})
|
||||
return total, list, err
|
||||
}
|
||||
|
||||
// QuerySubscribeList Get Subscribe List
|
||||
func (m *customSubscribeModel) QuerySubscribeList(ctx context.Context) ([]*Subscribe, error) {
|
||||
var list []*Subscribe
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
conn = conn.Model(&Subscribe{})
|
||||
return conn.Where("`sell` = true").Order("sort ").Find(v).Error
|
||||
})
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (m *customSubscribeModel) QuerySubscribeIdsByServerIdAndServerGroupId(ctx context.Context, serverId, serverGroupId int64) ([]*Subscribe, error) {
|
||||
var data []*Subscribe
|
||||
err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&Subscribe{}).Where("FIND_IN_SET(?, server)", serverId).Or("FIND_IN_SET(?, server_group)", serverGroupId).Find(v).Error
|
||||
})
|
||||
return data, err
|
||||
}
|
||||
|
||||
// QuerySubscribeListByShow Get Subscribe List By Show
|
||||
func (m *customSubscribeModel) QuerySubscribeListByShow(ctx context.Context) ([]*Subscribe, error) {
|
||||
var list []*Subscribe
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
conn = conn.Model(&Subscribe{})
|
||||
return conn.Where("`show` = true").Find(v).Error
|
||||
})
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (m *customSubscribeModel) QuerySubscribeMinSortByIds(ctx context.Context, ids []int64) (int64, error) {
|
||||
var minSort int64
|
||||
err := m.QueryNoCacheCtx(ctx, &minSort, func(conn *gorm.DB, v interface{}) error {
|
||||
@@ -100,10 +51,106 @@ func (m *customSubscribeModel) QuerySubscribeMinSortByIds(ctx context.Context, i
|
||||
return minSort, err
|
||||
}
|
||||
|
||||
func (m *customSubscribeModel) QuerySubscribeListByIds(ctx context.Context, ids []int64) ([]*Subscribe, error) {
|
||||
var list []*Subscribe
|
||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Model(&Subscribe{}).Where("id IN ?", ids).Find(v).Error
|
||||
})
|
||||
return list, err
|
||||
func (m *customSubscribeModel) ClearCache(ctx context.Context, ids ...int64) error {
|
||||
if len(ids) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cacheKeys []string
|
||||
for _, id := range ids {
|
||||
data, err := m.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheKeys = append(cacheKeys, m.getCacheKeys(data)...)
|
||||
}
|
||||
return m.CachedConn.DelCacheCtx(ctx, cacheKeys...)
|
||||
}
|
||||
|
||||
// FilterList Filter Subscribe List
|
||||
func (m *customSubscribeModel) FilterList(ctx context.Context, params *FilterParams) (int64, []*Subscribe, error) {
|
||||
if params == nil {
|
||||
params = &FilterParams{}
|
||||
}
|
||||
params.Normalize()
|
||||
|
||||
var list []*Subscribe
|
||||
var total int64
|
||||
|
||||
// 构建查询函数
|
||||
buildQuery := func(conn *gorm.DB, lang string) *gorm.DB {
|
||||
query := conn.Model(&Subscribe{})
|
||||
|
||||
if params.Search != "" {
|
||||
s := "%" + params.Search + "%"
|
||||
query = query.Where("`name` LIKE ? OR `description` LIKE ?", s, s)
|
||||
}
|
||||
if params.Show {
|
||||
query = query.Where("`show` = true")
|
||||
}
|
||||
if params.Sell {
|
||||
query = query.Where("`sell` = true")
|
||||
}
|
||||
|
||||
if len(params.Ids) > 0 {
|
||||
query = query.Where("id IN ?", params.Ids)
|
||||
}
|
||||
if len(params.Node) > 0 {
|
||||
query = query.Scopes(InSet("nodes", tool.Int64SliceToStringSlice(params.Node)))
|
||||
}
|
||||
|
||||
if len(params.Tags) > 0 {
|
||||
query = query.Scopes(InSet("node_tags", params.Tags))
|
||||
}
|
||||
if lang != "" {
|
||||
query = query.Where("language = ?", lang)
|
||||
} else if params.DefaultLanguage {
|
||||
query = query.Where("language = ''")
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
// 查询数据
|
||||
queryFunc := func(lang string) error {
|
||||
return m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||
query := buildQuery(conn, lang)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return query.Order("sort ASC").
|
||||
Limit(params.Size).
|
||||
Offset((params.Page - 1) * params.Size).
|
||||
Find(v).Error
|
||||
})
|
||||
}
|
||||
|
||||
err := queryFunc(params.Language)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// fallback 默认语言
|
||||
if params.DefaultLanguage && total == 0 {
|
||||
err = queryFunc("")
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return total, list, nil
|
||||
}
|
||||
|
||||
func InSet(field string, values []string) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
if len(values) == 0 {
|
||||
return db
|
||||
}
|
||||
|
||||
query := db.Where("1=0")
|
||||
for _, v := range values {
|
||||
query = query.Or("FIND_IN_SET(?, "+field+")", v)
|
||||
}
|
||||
return query
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +20,8 @@ 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"`
|
||||
ServerGroup string `gorm:"type:varchar(255);comment:Server Group"`
|
||||
Server string `gorm:"type:varchar(255);comment:Server"`
|
||||
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"`
|
||||
Sell *bool `gorm:"type:tinyint(1);not null;default:0;comment:Sell"`
|
||||
Sort int64 `gorm:"type:int;not null;default:0;comment:Sort"`
|
||||
@@ -48,6 +48,28 @@ func (s *Subscribe) BeforeCreate(tx *gorm.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Subscribe) BeforeDelete(tx *gorm.DB) error {
|
||||
if err := tx.Exec("UPDATE `subscribe` SET sort = sort - 1 WHERE sort > ?", s.Sort).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *Subscribe) BeforeUpdate(tx *gorm.DB) error {
|
||||
var count int64
|
||||
if err := tx.Set("gorm:query_option", "FOR UPDATE").Model(&Subscribe{}).
|
||||
Where("sort = ? AND id != ?", s.Sort, s.Id).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
var maxSort int64
|
||||
if err := tx.Model(&Subscribe{}).Select("MAX(sort)").Scan(&maxSort).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
s.Sort = maxSort + 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Discount struct {
|
||||
Months int64 `json:"months"`
|
||||
Discount int64 `json:"discount"`
|
||||
|
||||
Reference in New Issue
Block a user