Compare commits

...

9 Commits

Author SHA1 Message Date
Chang lue Tsen
4ad18a8b70 fix(model): update save method to use specific ID for record updates 2025-09-16 11:20:48 -04:00
Chang lue Tsen
596434454c feat(client): add additional options for Shadowsocks and Vless protocols 2025-09-16 11:20:39 -04:00
Chang lue Tsen
b5c756af35 feat(subscribe): update subscribe path 2025-09-16 10:39:19 -04:00
Chang lue Tsen
621eda41c2 fix(client): Shadowrocket template 2025-09-16 10:26:39 -04:00
Chang lue Tsen
900096ed12 fix(coupon): add check for zero count in coupon usage validation 2025-09-16 10:17:18 -04:00
Chang lue Tsen
26fe430d7c fix(subscribe): update status filter in subscription count query 2025-09-16 10:16:56 -04:00
Chang lue Tsen
0c55d173cb fix(model): add sorting to server and node queries 2025-09-16 10:10:11 -04:00
Chang lue Tsen
537dab1c3b fix(quota): simplify gift amount calculation 2025-09-16 09:53:03 -04:00
Leif Draven
4b2976a670
fix(schema): remove unnecessary AUTO_INCREMENT values from initial schema (#79)
Co-authored-by: Chang lue Tsen <tension@ppanel.dev>
2025-09-16 09:33:28 -04:00
16 changed files with 167 additions and 162 deletions

View File

@ -34,10 +34,9 @@ type Proxy struct {
Path string // For HTTP/HTTPS Path string // For HTTP/HTTPS
ServiceName string // For gRPC ServiceName string // For gRPC
// Shadowsocks Options // Shadowsocks Options
Method string Method string
ServerKey string // For Shadowsocks 2022 ServerKey string // For Shadowsocks 2022
Plugin string // Plugin for Shadowsocks
PluginOptions string // Plugin options for Shadowsocks
// Vmess/Vless/Trojan Options // Vmess/Vless/Trojan Options
Flow string // Flow for Vmess/Vless/Trojan Flow string // Flow for Vmess/Vless/Trojan
// Hysteria2 Options // Hysteria2 Options
@ -58,6 +57,25 @@ type Proxy struct {
// Mieru // Mieru
Multiplex string Multiplex string
// Obfs
Obfs string // obfs, 'none', 'http', 'tls'
ObfsHost string // obfs host
ObfsPath string // obfs path
// Vless
XhttpMode string // xhttp mode
XhttpExtra string // xhttp path
// encryption
Encryption string // encryption'none', 'mlkem768x25519plus'
EncryptionMode string // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string // encryption rtt'0rtt', '1rtt'
EncryptionTicket string // encryption ticket
EncryptionServerPadding string // encryption server padding
EncryptionPrivateKey string // encryption private key
EncryptionClientPadding string // encryption client padding
EncryptionPassword string // encryption password
} }
type User struct { type User struct {

View File

@ -770,37 +770,49 @@ type (
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
} }
Protocol { Protocol {
Type string `json:"type"` Type string `json:"type"`
Port uint16 `json:"port"` Port uint16 `json:"port"`
Security string `json:"security,omitempty"` Enable bool `json:"enable"`
SNI string `json:"sni,omitempty"` Security string `json:"security,omitempty"`
AllowInsecure bool `json:"allow_insecure,omitempty"` SNI string `json:"sni,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"` AllowInsecure bool `json:"allow_insecure,omitempty"`
RealityServerAddr string `json:"reality_server_addr,omitempty"` Fingerprint string `json:"fingerprint,omitempty"`
RealityServerPort int `json:"reality_server_port,omitempty"` RealityServerAddr string `json:"reality_server_addr,omitempty"`
RealityPrivateKey string `json:"reality_private_key,omitempty"` RealityServerPort int `json:"reality_server_port,omitempty"`
RealityPublicKey string `json:"reality_public_key,omitempty"` RealityPrivateKey string `json:"reality_private_key,omitempty"`
RealityShortId string `json:"reality_short_id,omitempty"` RealityPublicKey string `json:"reality_public_key,omitempty"`
Transport string `json:"transport,omitempty"` RealityShortId string `json:"reality_short_id,omitempty"`
Host string `json:"host,omitempty"` Transport string `json:"transport,omitempty"`
Path string `json:"path,omitempty"` Host string `json:"host,omitempty"`
ServiceName string `json:"service_name,omitempty"` Path string `json:"path,omitempty"`
Cipher string `json:"cipher,omitempty"` ServiceName string `json:"service_name,omitempty"`
ServerKey string `json:"server_key,omitempty"` Cipher string `json:"cipher,omitempty"`
Flow string `json:"flow,omitempty"` ServerKey string `json:"server_key,omitempty"`
HopPorts string `json:"hop_ports,omitempty"` Flow string `json:"flow,omitempty"`
HopInterval int `json:"hop_interval,omitempty"` HopPorts string `json:"hop_ports,omitempty"`
ObfsPassword string `json:"obfs_password,omitempty"` HopInterval int `json:"hop_interval,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"` ObfsPassword string `json:"obfs_password,omitempty"`
ReduceRtt bool `json:"reduce_rtt,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"`
UDPRelayMode string `json:"udp_relay_mode,omitempty"` ReduceRtt bool `json:"reduce_rtt,omitempty"`
CongestionController string `json:"congestion_controller,omitempty"` UDPRelayMode string `json:"udp_relay_mode,omitempty"`
Plugin string `json:"plugin,omitempty"` // obfs, none,v2ray-plugin, simple-obfs CongestionController string `json:"congestion_controller,omitempty"`
PluginOptions string `json:"plugin_options,omitempty"` // plugin options, eg: obfs=http;obfs-host=www.bing.com Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high
Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme
PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme UpMbps int `json:"up_mbps,omitempty"` // upload speed limit
UpMbps int `json:"up_mbps,omitempty"` // upload speed limit DownMbps int `json:"down_mbps,omitempty"` // download speed limit
DownMbps int `json:"down_mbps,omitempty"` // download speed limit Obfs string `json:"obfs,omitempty"` // obfs, 'none', 'http', 'tls'
ObfsHost string `json:"obfs_host,omitempty"` // obfs host
ObfsPath string `json:"obfs_path,omitempty"` // obfs path
XhttpMode string `json:"xhttp_mode,omitempty"` // xhttp mode
XhttpExtra string `json:"xhttp_extra,omitempty"` // xhttp extra path
Encryption string `json:"encryption,omitempty"` // encryption'none', 'mlkem768x25519plus'
EncryptionMode string `json:"encryption_mode,omitempty"` // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string `json:"encryption_rtt,omitempty"` // encryption rtt'0rtt', '1rtt'
EncryptionTicket string `json:"encryption_ticket,omitempty"` // encryption ticket
EncryptionServerPadding string `json:"encryption_server_padding,omitempty"` // encryption server padding
EncryptionPrivateKey string `json:"encryption_private_key,omitempty"` // encryption private key
EncryptionClientPadding string `json:"encryption_client_padding,omitempty"` // encryption client padding
EncryptionPassword string `json:"encryption_password,omitempty"` // encryption password
} }
) )

View File

@ -91,7 +91,6 @@ CREATE TABLE IF NOT EXISTS `auth_method`
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `uni_auth_method` (`method`) UNIQUE KEY `uni_auth_method` (`method`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 9
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -305,7 +304,6 @@ CREATE TABLE IF NOT EXISTS `subscribe_type`
`updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间', `updated_at` datetime(3) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 15
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -323,7 +321,6 @@ CREATE TABLE IF NOT EXISTS `system`
UNIQUE KEY `uni_system_key` (`key`), UNIQUE KEY `uni_system_key` (`key`),
KEY `index_key` (`key`) KEY `index_key` (`key`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 42
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -398,7 +395,6 @@ CREATE TABLE IF NOT EXISTS `user`
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `idx_referer` (`referer_id`) KEY `idx_referer` (`referer_id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;
@ -415,7 +411,6 @@ CREATE TABLE IF NOT EXISTS `user_auth_methods`
UNIQUE KEY `idx_auth_identifier` (`auth_identifier`), UNIQUE KEY `idx_auth_identifier` (`auth_identifier`),
KEY `idx_user_id` (`user_id`) KEY `idx_user_id` (`user_id`)
) ENGINE = InnoDB ) ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARSET = utf8mb4 DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci; COLLATE = utf8mb4_general_ci;

File diff suppressed because one or more lines are too long

View File

@ -53,7 +53,7 @@ type Verify struct {
type SubscribeConfig struct { type SubscribeConfig struct {
SingleModel bool `yaml:"SingleModel" default:"false"` SingleModel bool `yaml:"SingleModel" default:"false"`
SubscribePath string `yaml:"SubscribePath" default:"/api/subscribe"` SubscribePath string `yaml:"SubscribePath" default:"/v1/subscribe/config"`
SubscribeDomain string `yaml:"SubscribeDomain" default:""` SubscribeDomain string `yaml:"SubscribeDomain" default:""`
PanDomain bool `yaml:"PanDomain" default:"false"` PanDomain bool `yaml:"PanDomain" default:"false"`
UserAgentLimit bool `yaml:"UserAgentLimit" default:"false"` UserAgentLimit bool `yaml:"UserAgentLimit" default:"false"`

View File

@ -74,7 +74,7 @@ func SubscribeHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
func RegisterSubscribeHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) { func RegisterSubscribeHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
path := serverCtx.Config.Subscribe.SubscribePath path := serverCtx.Config.Subscribe.SubscribePath
if path == "" { if path == "" {
path = "/api/subscribe" path = "/v1/subscribe/config"
} }
router.GET(path, SubscribeHandler(serverCtx)) router.GET(path, SubscribeHandler(serverCtx))
} }

View File

@ -114,7 +114,7 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error()) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
} }
if couponInfo.Count <= couponInfo.UsedCount { if couponInfo.Count != 0 && couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used") return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
} }
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe) couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)

View File

@ -84,7 +84,7 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error()) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
} }
if couponInfo.Count <= couponInfo.UsedCount { if couponInfo.Count != 0 && couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used") return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
} }
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe) couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)

View File

@ -54,7 +54,7 @@ func (l *PrePurchaseOrderLogic) PrePurchaseOrder(req *types.PrePurchaseOrderRequ
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error()) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
} }
if couponInfo.Count <= couponInfo.UsedCount { if couponInfo.Count != 0 && couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used") return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
} }
subs := tool.StringToInt64Slice(couponInfo.Subscribe) subs := tool.StringToInt64Slice(couponInfo.Subscribe)

View File

@ -80,7 +80,7 @@ func (l *PurchaseLogic) Purchase(req *types.PortalPurchaseRequest) (resp *types.
} }
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error()) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find coupon error: %v", err.Error())
} }
if couponInfo.Count <= couponInfo.UsedCount { if couponInfo.Count != 0 && couponInfo.Count <= couponInfo.UsedCount {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used") return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
} }
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe) couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)

View File

@ -79,7 +79,7 @@ func (m *defaultServerModel) UpdateServer(ctx context.Context, data *Server, tx
if len(tx) > 0 { if len(tx) > 0 {
db = tx[0] db = tx[0]
} }
return db.WithContext(ctx).Save(data).Error return db.WithContext(ctx).Where("`id` = ?", data.Id).Save(data).Error
} }
@ -115,7 +115,7 @@ func (m *defaultServerModel) UpdateNode(ctx context.Context, data *Node, tx ...*
if len(tx) > 0 { if len(tx) > 0 {
db = tx[0] db = tx[0]
} }
return db.WithContext(ctx).Save(data).Error return db.WithContext(ctx).Where("`id` = ?", data.Id).Save(data).Error
} }
func (m *defaultServerModel) DeleteNode(ctx context.Context, id int64, tx ...*gorm.DB) error { func (m *defaultServerModel) DeleteNode(ctx context.Context, id int64, tx ...*gorm.DB) error {

View File

@ -57,7 +57,7 @@ func (m *customServerModel) FilterServerList(ctx context.Context, params *Filter
if len(params.Ids) > 0 { if len(params.Ids) > 0 {
query = query.Where("id IN ?", params.Ids) query = query.Where("id IN ?", params.Ids)
} }
err := query.Count(&total).Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(&servers).Error err := query.Count(&total).Order("sort ASC").Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(&servers).Error
return total, servers, err return total, servers, err
} }
@ -92,7 +92,7 @@ func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNo
query = query.Preload("Server") query = query.Preload("Server")
} }
err := query.Count(&total).Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(&nodes).Error err := query.Count(&total).Order("sort ASC").Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(&nodes).Error
return total, nodes, err return total, nodes, err
} }

View File

@ -101,37 +101,49 @@ func (m *Server) UnmarshalProtocols() ([]Protocol, error) {
} }
type Protocol struct { type Protocol struct {
Type string `json:"type"` Type string `json:"type"`
Port uint16 `json:"port"` Port uint16 `json:"port"`
Security string `json:"security,omitempty"` Enable bool `json:"enable"`
SNI string `json:"sni,omitempty"` Security string `json:"security,omitempty"`
AllowInsecure bool `json:"allow_insecure,omitempty"` SNI string `json:"sni,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"` AllowInsecure bool `json:"allow_insecure,omitempty"`
RealityServerAddr string `json:"reality_server_addr,omitempty"` Fingerprint string `json:"fingerprint,omitempty"`
RealityServerPort int `json:"reality_server_port,omitempty"` RealityServerAddr string `json:"reality_server_addr,omitempty"`
RealityPrivateKey string `json:"reality_private_key,omitempty"` RealityServerPort int `json:"reality_server_port,omitempty"`
RealityPublicKey string `json:"reality_public_key,omitempty"` RealityPrivateKey string `json:"reality_private_key,omitempty"`
RealityShortId string `json:"reality_short_id,omitempty"` RealityPublicKey string `json:"reality_public_key,omitempty"`
Transport string `json:"transport,omitempty"` RealityShortId string `json:"reality_short_id,omitempty"`
Host string `json:"host,omitempty"` Transport string `json:"transport,omitempty"`
Path string `json:"path,omitempty"` Host string `json:"host,omitempty"`
ServiceName string `json:"service_name,omitempty"` Path string `json:"path,omitempty"`
Cipher string `json:"cipher,omitempty"` ServiceName string `json:"service_name,omitempty"`
ServerKey string `json:"server_key,omitempty"` Cipher string `json:"cipher,omitempty"`
Flow string `json:"flow,omitempty"` ServerKey string `json:"server_key,omitempty"`
HopPorts string `json:"hop_ports,omitempty"` Flow string `json:"flow,omitempty"`
HopInterval int `json:"hop_interval,omitempty"` HopPorts string `json:"hop_ports,omitempty"`
ObfsPassword string `json:"obfs_password,omitempty"` HopInterval int `json:"hop_interval,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"` ObfsPassword string `json:"obfs_password,omitempty"`
ReduceRtt bool `json:"reduce_rtt,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"`
UDPRelayMode string `json:"udp_relay_mode,omitempty"` ReduceRtt bool `json:"reduce_rtt,omitempty"`
CongestionController string `json:"congestion_controller,omitempty"` UDPRelayMode string `json:"udp_relay_mode,omitempty"`
Plugin string `json:"plugin,omitempty"` // obfs, v2ray-plugin, simple-obfs CongestionController string `json:"congestion_controller,omitempty"`
PluginOptions string `json:"plugin_options,omitempty"` // plugin options, eg: obfs=http;obfs-host=www.bing.com Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high
Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme
PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme UpMbps int `json:"up_mbps,omitempty"` // upload speed limit
UpMbps int `json:"up_mbps,omitempty"` // upload speed limit DownMbps int `json:"down_mbps,omitempty"` // download speed limit
DownMbps int `json:"down_mbps,omitempty"` // download speed limit Obfs string `json:"obfs,omitempty"` // obfs, 'none', 'http', 'tls'
ObfsHost string `json:"obfs_host,omitempty"` // obfs host
ObfsPath string `json:"obfs_path,omitempty"` // obfs path
XhttpMode string `json:"xhttp_mode,omitempty"` // xhttp mode
XhttpExtra string `json:"xhttp_extra,omitempty"` // xhttp extra path
Encryption string `json:"encryption,omitempty"` // encryption'none', 'mlkem768x25519plus'
EncryptionMode string `json:"encryption_mode,omitempty"` // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string `json:"encryption_rtt,omitempty"` // encryption rtt'0rtt', '1rtt'
EncryptionTicket string `json:"encryption_ticket,omitempty"` // encryption ticket
EncryptionServerPadding string `json:"encryption_server_padding,omitempty"` // encryption server padding
EncryptionPrivateKey string `json:"encryption_private_key,omitempty"` // encryption private key
EncryptionClientPadding string `json:"encryption_client_padding,omitempty"` // encryption client padding
EncryptionPassword string `json:"encryption_password,omitempty"` // encryption password
} }
// Marshal protocol to json // Marshal protocol to json

View File

@ -21,7 +21,7 @@ func (m *defaultUserModel) QueryActiveSubscriptions(ctx context.Context, subscri
var result []SubscriptionCount var result []SubscriptionCount
err := m.QueryNoCacheCtx(ctx, &result, func(conn *gorm.DB, v interface{}) error { err := m.QueryNoCacheCtx(ctx, &result, func(conn *gorm.DB, v interface{}) error {
return conn.Model(&Subscribe{}). return conn.Model(&Subscribe{}).
Where("subscribe_id IN ? AND `status` IN ?", subscribeId, []int64{1, 0, 3}). Where("subscribe_id IN ? AND `status` IN ?", subscribeId, []int64{1, 0}).
Select("subscribe_id, COUNT(id) as total"). Select("subscribe_id, COUNT(id) as total").
Group("subscribe_id"). Group("subscribe_id").
Scan(&result). Scan(&result).

View File

@ -1423,37 +1423,48 @@ type PrivacyPolicyConfig struct {
} }
type Protocol struct { type Protocol struct {
Type string `json:"type"` Type string `json:"type"`
Port uint16 `json:"port"` Port uint16 `json:"port"`
Security string `json:"security,omitempty"` Security string `json:"security,omitempty"`
SNI string `json:"sni,omitempty"` SNI string `json:"sni,omitempty"`
AllowInsecure bool `json:"allow_insecure,omitempty"` AllowInsecure bool `json:"allow_insecure,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"` Fingerprint string `json:"fingerprint,omitempty"`
RealityServerAddr string `json:"reality_server_addr,omitempty"` RealityServerAddr string `json:"reality_server_addr,omitempty"`
RealityServerPort int `json:"reality_server_port,omitempty"` RealityServerPort int `json:"reality_server_port,omitempty"`
RealityPrivateKey string `json:"reality_private_key,omitempty"` RealityPrivateKey string `json:"reality_private_key,omitempty"`
RealityPublicKey string `json:"reality_public_key,omitempty"` RealityPublicKey string `json:"reality_public_key,omitempty"`
RealityShortId string `json:"reality_short_id,omitempty"` RealityShortId string `json:"reality_short_id,omitempty"`
Transport string `json:"transport,omitempty"` Transport string `json:"transport,omitempty"`
Host string `json:"host,omitempty"` Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
ServiceName string `json:"service_name,omitempty"` ServiceName string `json:"service_name,omitempty"`
Cipher string `json:"cipher,omitempty"` Cipher string `json:"cipher,omitempty"`
ServerKey string `json:"server_key,omitempty"` ServerKey string `json:"server_key,omitempty"`
Flow string `json:"flow,omitempty"` Flow string `json:"flow,omitempty"`
HopPorts string `json:"hop_ports,omitempty"` HopPorts string `json:"hop_ports,omitempty"`
HopInterval int `json:"hop_interval,omitempty"` HopInterval int `json:"hop_interval,omitempty"`
ObfsPassword string `json:"obfs_password,omitempty"` ObfsPassword string `json:"obfs_password,omitempty"`
DisableSNI bool `json:"disable_sni,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"`
ReduceRtt bool `json:"reduce_rtt,omitempty"` ReduceRtt bool `json:"reduce_rtt,omitempty"`
UDPRelayMode string `json:"udp_relay_mode,omitempty"` UDPRelayMode string `json:"udp_relay_mode,omitempty"`
CongestionController string `json:"congestion_controller,omitempty"` CongestionController string `json:"congestion_controller,omitempty"`
Plugin string `json:"plugin,omitempty"` // obfs, v2ray-plugin, simple-obfs Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high
PluginOptions string `json:"plugin_options,omitempty"` // plugin options, eg: obfs=http;obfs-host=www.bing.com PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme
Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high UpMbps int `json:"up_mbps,omitempty"` // upload speed limit
PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme DownMbps int `json:"down_mbps,omitempty"` // download speed limit
UpMbps int `json:"up_mbps,omitempty"` // upload speed limit Obfs string `json:"obfs,omitempty"` // obfs, 'none', 'http', 'tls'
DownMbps int `json:"down_mbps,omitempty"` // download speed limit ObfsHost string `json:"obfs_host,omitempty"` // obfs host
ObfsPath string `json:"obfs_path,omitempty"` // obfs path
XhttpMode string `json:"xhttp_mode,omitempty"` // xhttp mode
XhttpExtra string `json:"xhttp_extra,omitempty"` // xhttp extra path
Encryption string `json:"encryption,omitempty"` // encryption'none', 'mlkem768x25519plus'
EncryptionMode string `json:"encryption_mode,omitempty"` // encryption mode'native', 'xorpub', 'random'
EncryptionRtt string `json:"encryption_rtt,omitempty"` // encryption rtt'0rtt', '1rtt'
EncryptionTicket string `json:"encryption_ticket,omitempty"` // encryption ticket
EncryptionServerPadding string `json:"encryption_server_padding,omitempty"` // encryption server padding
EncryptionPrivateKey string `json:"encryption_private_key,omitempty"` // encryption private key
EncryptionClientPadding string `json:"encryption_client_padding,omitempty"` // encryption client padding
EncryptionPassword string `json:"encryption_password,omitempty"` // encryption password
} }
type PubilcRegisterConfig struct { type PubilcRegisterConfig struct {

View File

@ -9,8 +9,6 @@ import (
"github.com/hibiken/asynq" "github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/log" "github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/task" "github.com/perfect-panel/server/internal/model/task"
"github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
@ -318,16 +316,17 @@ func (l *QuotaTaskLogic) processGift(tx *gorm.DB, sub *user.Subscribe, content t
case 1: case 1:
giftAmount = int64(content.GiftValue) giftAmount = int64(content.GiftValue)
case 2: case 2:
orderAmount, err := l.calculateOrderAmount(tx, sub, now) // 获取订阅对应的套餐信息
subscribeInfo, err := l.svcCtx.SubscribeModel.FindOne(context.Background(), sub.SubscribeId)
if err != nil { if err != nil {
*errors = append(*errors, ErrorInfo{ *errors = append(*errors, ErrorInfo{
UserSubscribeId: sub.Id, UserSubscribeId: sub.Id,
Error: err.Error(), Error: "find subscribe error: " + err.Error(),
}) })
return nil return nil
} }
if orderAmount > 0 { if subscribeInfo.UnitPrice > 0 {
giftAmount = int64(float64(orderAmount) * (float64(content.GiftValue) / 100)) giftAmount = int64(float64(subscribeInfo.UnitPrice) * (float64(content.GiftValue) / 100))
} }
} }
@ -364,48 +363,6 @@ func (l *QuotaTaskLogic) getStartTime(sub *user.Subscribe, now time.Time) time.T
return sub.StartTime return sub.StartTime
} }
func (l *QuotaTaskLogic) calculateOrderAmount(tx *gorm.DB, sub *user.Subscribe, now time.Time) (int64, error) {
if sub.OrderId != 0 {
var orderInfo *order.Order
if err := tx.Model(&order.Order{}).Where("id = ?", sub.OrderId).First(&orderInfo).Error; err != nil {
return 0, fmt.Errorf("find order error: %v", err)
}
return orderInfo.Amount + orderInfo.GiftAmount, nil
}
var subInfo *subscribe.Subscribe
if err := tx.Model(&subscribe.Subscribe{}).Where("id = ?", sub.SubscribeId).First(&subInfo).Error; err != nil {
return 0, fmt.Errorf("find subscribe error: %v", err)
}
startTime := l.getStartTime(sub, now)
if sub.ExpireTime.Before(startTime) {
return subInfo.UnitPrice, nil
}
switch subInfo.UnitTime {
case UnitTimeNoLimit:
return subInfo.UnitPrice, nil
case UnitTimeYear:
days := tool.DayDiff(startTime, sub.ExpireTime)
return subInfo.UnitPrice / 365 * days, nil
case UnitTimeMonth:
days := tool.DayDiff(startTime, sub.ExpireTime)
return subInfo.UnitPrice / 30 * days, nil
case UnitTimeDay:
days := tool.DayDiff(startTime, sub.ExpireTime)
return subInfo.UnitPrice * days, nil
case UnitTimeHour:
hours := int(tool.HourDiff(startTime, sub.ExpireTime))
return subInfo.UnitPrice * int64(hours), nil
case UnitTimeMinute:
minutes := tool.HourDiff(startTime, sub.ExpireTime) * 60
return subInfo.UnitPrice * minutes, nil
default:
return subInfo.UnitPrice, nil
}
}
func (l *QuotaTaskLogic) createGiftLog(tx *gorm.DB, subscribeId, userId, amount, balance int64, now time.Time) error { func (l *QuotaTaskLogic) createGiftLog(tx *gorm.DB, subscribeId, userId, amount, balance int64, now time.Time) error {
giftLog := &log.Gift{ giftLog := &log.Gift{
Type: log.GiftTypeIncrease, Type: log.GiftTypeIncrease,