init: 1.0.0

This commit is contained in:
Chang lue Tsen
2025-04-25 12:08:29 +09:00
commit 8addcc584b
1031 changed files with 76472 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
package svc
import (
"github.com/hibiken/asynq"
"github.com/perfect-panel/ppanel-server/internal/config"
)
func NewAsynqClient(c config.Config) *asynq.Client {
return asynq.NewClient(asynq.RedisClientOpt{Addr: c.Redis.Host, Password: c.Redis.Pass, DB: 5})
}
+129
View File
@@ -0,0 +1,129 @@
package svc
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/model/user"
"github.com/perfect-panel/ppanel-server/pkg/device"
"github.com/perfect-panel/ppanel-server/pkg/logger"
"github.com/pkg/errors"
"gorm.io/gorm"
)
func NewDeviceManager(srv *ServiceContext) *device.DeviceManager {
ctx := context.Background()
manager := device.NewDeviceManager(30, 30)
//设备离线处理
manager.OnDeviceOffline = func(userID int64, deviceID, session string, createAt time.Time) {
oneDevice, err := srv.UserModel.FindOneDeviceByIdentifier(ctx, deviceID)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.Errorw("failed to find device", logger.Field("error", err.Error()), logger.Field("device_id", deviceID))
}
return
}
//更新设备状态为离线
oneDevice.Online = false
err = srv.UserModel.UpdateDevice(ctx, oneDevice)
if err != nil {
logger.Errorw("[DeviceManager] failed to update device", logger.Field("error", err.Error()), logger.Field("device_id", deviceID))
}
//当前时间为设备离线时间
currentTime := time.Now()
endTime := currentTime.Format("2006-01-02 00:00:00")
parseStart, _ := time.Parse(time.DateTime, endTime)
startTime := parseStart.Add(time.Hour * 24).Format(time.DateTime)
deviceOnlineRecord := user.DeviceOnlineRecord{
UserId: userID,
Identifier: deviceID,
OnlineTime: createAt,
OfflineTime: currentTime,
OnlineSeconds: int64(currentTime.Sub(createAt).Seconds()),
}
//获取设备昨日在线记录
var onlineRecord user.DeviceOnlineRecord
if err := srv.DB.Model(&onlineRecord).Where("user_id = ? and create_at >= ? and create_at < ?", userID, startTime, endTime).First(&onlineRecord).Error; err != nil {
//昨日未在线,连续在线天数为1
deviceOnlineRecord.DurationDays = 1
} else {
//昨日在线,连续在线天数为,昨天连续在线天数+1,等于当前连续在线天数
deviceOnlineRecord.DurationDays = onlineRecord.DurationDays + 1
}
if err := srv.DB.Create(&deviceOnlineRecord).Error; err != nil {
logger.Errorw("[DeviceOnlineRecord] failed to DeviceOnlineRecord", logger.Field("error", err.Error()), logger.Field("device_id", deviceID))
}
}
//设备上线处理
manager.OnDeviceOnline = func(userID int64, deviceID, session string) {
oneDevice, err := srv.UserModel.FindOneDeviceByIdentifier(ctx, deviceID)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.Errorw("failed to find device", logger.Field("error", err.Error()), logger.Field("device_id", deviceID))
}
return
}
oneDevice.Online = true
err = srv.UserModel.UpdateDevice(ctx, oneDevice)
if err != nil {
logger.Errorw("[DeviceManager] failed to update device", logger.Field("error", err.Error()), logger.Field("device_id", deviceID))
return
}
}
manager.OnDeviceKicked = func(userID int64, deviceID, session string, operator device.Operator) {
//管理员踢下线
if operator == device.Admin {
message := DeviceMessage{Method: DeviceKickedAdmin}
_ = manager.SendToDevice(userID, deviceID, message.Json())
//将登陆凭证从缓存中删除
srv.Redis.Del(ctx, fmt.Sprintf("%v:%v", config.SessionIdKey, session))
return
}
//登陆设备超过限制踢下线
if operator == device.MaxDevices {
message := DeviceMessage{Method: DeviceKickedMax}
_ = manager.SendToDevice(userID, deviceID, message.Json())
//将登陆凭证从缓存中删除
srv.Redis.Del(ctx, fmt.Sprintf("%v:%v", config.SessionIdKey, session))
return
}
}
manager.OnMessage = func(userID int64, deviceID, session string, message string) {
logger.Infof("userid: %d ,device_number: %s,session: %s, message: %v", userID, deviceID, session, message)
}
return manager
}
type DeviceMessage struct {
Method DeviceMessageMethod `json:"method"`
}
func (dm *DeviceMessage) Json() string {
jsonData, _ := json.Marshal(dm)
return string(jsonData)
}
type DeviceMessageMethod string
const (
// DeviceKickedMax 设备数量超出限制
DeviceKickedMax DeviceMessageMethod = "kicked_device"
// DeviceKickedAdmin 管理员踢下线
DeviceKickedAdmin DeviceMessageMethod = "kicked_admin"
// SubscribeUpdate 订阅有更新
SubscribeUpdate DeviceMessageMethod = "subscribe_update"
)
+14
View File
@@ -0,0 +1,14 @@
package svc
import (
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/pkg/logger"
)
func NewLogger(c config.Config) *logger.Logger {
//log := logger.New(c.Logger)
//// replace the default logger
//logger = log
//return log
return nil
}
+111
View File
@@ -0,0 +1,111 @@
package svc
import (
"context"
"github.com/perfect-panel/ppanel-server/pkg/device"
"github.com/perfect-panel/ppanel-server/internal/model/ads"
"github.com/perfect-panel/ppanel-server/internal/model/cache"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/hibiken/asynq"
"github.com/perfect-panel/ppanel-server/internal/config"
"github.com/perfect-panel/ppanel-server/internal/model/announcement"
"github.com/perfect-panel/ppanel-server/internal/model/application"
"github.com/perfect-panel/ppanel-server/internal/model/auth"
"github.com/perfect-panel/ppanel-server/internal/model/coupon"
"github.com/perfect-panel/ppanel-server/internal/model/document"
"github.com/perfect-panel/ppanel-server/internal/model/log"
"github.com/perfect-panel/ppanel-server/internal/model/order"
"github.com/perfect-panel/ppanel-server/internal/model/payment"
"github.com/perfect-panel/ppanel-server/internal/model/server"
"github.com/perfect-panel/ppanel-server/internal/model/subscribe"
"github.com/perfect-panel/ppanel-server/internal/model/subscribeType"
"github.com/perfect-panel/ppanel-server/internal/model/system"
"github.com/perfect-panel/ppanel-server/internal/model/ticket"
"github.com/perfect-panel/ppanel-server/internal/model/traffic"
"github.com/perfect-panel/ppanel-server/internal/model/user"
"github.com/perfect-panel/ppanel-server/pkg/limit"
"github.com/perfect-panel/ppanel-server/pkg/nodeMultiplier"
"github.com/perfect-panel/ppanel-server/pkg/orm"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
type ServiceContext struct {
DB *gorm.DB
Redis *redis.Client
Config config.Config
Queue *asynq.Client
NodeCache *cache.NodeCacheClient
AuthModel auth.Model
AdsModel ads.Model
LogModel log.Model
UserModel user.Model
OrderModel order.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
ApplicationModel application.Model
AnnouncementModel announcement.Model
SubscribeTypeModel subscribeType.Model
Restart func() error
TelegramBot *tgbotapi.BotAPI
NodeMultiplierManager *nodeMultiplier.Manager
AuthLimiter *limit.PeriodLimit
DeviceManager *device.DeviceManager
}
func NewServiceContext(c config.Config) *ServiceContext {
// gorm initialize
db, err := orm.ConnectMysql(orm.Mysql{
Config: c.MySQL,
})
if err != nil {
panic(err.Error())
}
rds := redis.NewClient(&redis.Options{
Addr: c.Redis.Host,
Password: c.Redis.Pass,
DB: c.Redis.DB,
})
err = rds.Ping(context.Background()).Err()
if err != nil {
panic(err.Error())
} else {
_ = rds.FlushAll(context.Background()).Err()
}
authLimiter := limit.NewPeriodLimit(86400, 15, rds, config.SendCountLimitKeyPrefix, limit.Align())
srv := &ServiceContext{
DB: db,
Redis: rds,
Config: c,
Queue: NewAsynqClient(c),
NodeCache: cache.NewNodeCacheClient(rds),
AuthLimiter: authLimiter,
AdsModel: ads.NewModel(db, rds),
LogModel: log.NewModel(db),
AuthModel: auth.NewModel(db, rds),
UserModel: user.NewModel(db, rds),
OrderModel: order.NewModel(db, rds),
TicketModel: ticket.NewModel(db, rds),
ServerModel: server.NewModel(db, rds),
SystemModel: system.NewModel(db, rds),
CouponModel: coupon.NewModel(db, rds),
PaymentModel: payment.NewModel(db, rds),
DocumentModel: document.NewModel(db, rds),
SubscribeModel: subscribe.NewModel(db, rds),
TrafficLogModel: traffic.NewModel(db),
ApplicationModel: application.NewModel(db, rds),
AnnouncementModel: announcement.NewModel(db, rds),
}
srv.DeviceManager = NewDeviceManager(srv)
return srv
}
+33
View File
@@ -0,0 +1,33 @@
package svc
import (
"reflect"
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
enTranslations "github.com/go-playground/validator/v10/translations/en"
"github.com/pkg/errors"
)
func (svc ServiceContext) Validate(dataStruct interface{}) error {
enUs := en.New()
validate := validator.New()
// RegisterTagNameFunc registers a function to get field name for error messages
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := fld.Tag.Get("label")
return name
})
uni := ut.New(enUs)
trans, _ := uni.GetTranslator("en")
// RegisterDefaultTranslations registers the default translations
_ = enTranslations.RegisterDefaultTranslations(validate, trans)
err := validate.Struct(dataStruct)
if err != nil {
for _, err := range err.(validator.ValidationErrors) {
return errors.New(err.Translate(trans))
}
}
return nil
}