diff --git a/internal/logic/public/user/deviceOnlineStatisticsLogic.go b/internal/logic/public/user/deviceOnlineStatisticsLogic.go new file mode 100644 index 0000000..27ca6dc --- /dev/null +++ b/internal/logic/public/user/deviceOnlineStatisticsLogic.go @@ -0,0 +1,115 @@ +package user + +import ( + "context" + "sort" + "time" + + "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/constant" + "github.com/perfect-panel/server/pkg/logger" +) + +type DeviceOnlineStatisticsLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// Device Online Statistics +func NewDeviceOnlineStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceOnlineStatisticsLogic { + return &DeviceOnlineStatisticsLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DeviceOnlineStatisticsLogic) DeviceOnlineStatistics() (resp *types.GetDeviceOnlineStatsResponse, err error) { + u := l.ctx.Value(constant.CtxKeyUser).(*user.User) + //获取历史最长在线时间 + var OnlineSeconds int64 + if err := l.svcCtx.DB.Model(user.DeviceOnlineRecord{}).Where("user_id = ?", u.Id).Select("online_seconds").Order("online_seconds desc").Limit(1).Scan(&OnlineSeconds).Error; err != nil { + l.Logger.Error(err) + } + + //获取历史连续最长在线天数 + var DurationDays int64 + if err := l.svcCtx.DB.Model(user.DeviceOnlineRecord{}).Where("user_id = ?", u.Id).Select("duration_days").Order("duration_days desc").Limit(1).Scan(&DurationDays).Error; err != nil { + l.Logger.Error(err) + } + + //获取近七天在线情况 + var userOnlineRecord []user.DeviceOnlineRecord + if err := l.svcCtx.DB.Model(&userOnlineRecord).Where("user_id = ? and created_at >= ?", u.Id, time.Now().AddDate(0, 0, -7).Format(time.DateTime)).Order("created_at desc").Find(&userOnlineRecord).Error; err != nil { + l.Logger.Error(err) + } + + //获取当前连续在线天数 + var currentContinuousDays int64 + if len(userOnlineRecord) > 0 { + currentContinuousDays = userOnlineRecord[0].DurationDays + } else { + currentContinuousDays = 1 + } + + var dates []string + for i := 0; i < 7; i++ { + date := time.Now().AddDate(0, 0, -i).Format(time.DateOnly) + dates = append(dates, date) + } + + onlineDays := make(map[string]types.WeeklyStat) + for _, record := range userOnlineRecord { + //获取近七天在线情况 + onlineTime := record.OnlineTime.Format(time.DateOnly) + if weeklyStat, ok := onlineDays[onlineTime]; ok { + weeklyStat.Hours += float64(record.OnlineSeconds) + onlineDays[onlineTime] = weeklyStat + } else { + onlineDays[onlineTime] = types.WeeklyStat{ + Hours: float64(record.OnlineSeconds), + //根据日期获取周几 + DayName: record.OnlineTime.Weekday().String(), + } + } + } + + //补全不存在的日期 + for _, date := range dates { + if _, ok := onlineDays[date]; !ok { + onlineTime, _ := time.Parse(time.DateOnly, date) + onlineDays[date] = types.WeeklyStat{ + DayName: onlineTime.Weekday().String(), + } + } + } + + var keys []string + for key := range onlineDays { + keys = append(keys, key) + } + + //排序 + sort.Strings(keys) + + var weeklyStats []types.WeeklyStat + for index, key := range keys { + weeklyStat := onlineDays[key] + weeklyStat.Day = index + 1 + weeklyStat.Hours = weeklyStat.Hours / float64(3600) + weeklyStats = append(weeklyStats, weeklyStat) + } + + resp = &types.GetDeviceOnlineStatsResponse{ + WeeklyStats: weeklyStats, + ConnectionRecords: types.ConnectionRecords{ + CurrentContinuousDays: currentContinuousDays, + HistoryContinuousDays: DurationDays, + LongestSingleConnection: OnlineSeconds / 60, + }, + } + return +} diff --git a/internal/logic/public/user/ws/deviceWsConnectLogic.go b/internal/logic/public/user/ws/deviceWsConnectLogic.go new file mode 100644 index 0000000..a52bef4 --- /dev/null +++ b/internal/logic/public/user/ws/deviceWsConnectLogic.go @@ -0,0 +1,84 @@ +package ws + +import ( + "context" + sysErr "errors" + "time" + + "github.com/gin-gonic/gin" + "github.com/perfect-panel/server/internal/model/user" + "github.com/perfect-panel/server/pkg/constant" + "github.com/perfect-panel/server/pkg/xerr" + "github.com/pkg/errors" + "gorm.io/gorm" + + "github.com/perfect-panel/server/internal/svc" + "github.com/perfect-panel/server/pkg/logger" +) + +type DeviceWsConnectLogic struct { + logger.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +// Webosocket Device Connect +func NewDeviceWsConnectLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeviceWsConnectLogic { + return &DeviceWsConnectLogic{ + Logger: logger.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *DeviceWsConnectLogic) DeviceWsConnect(c *gin.Context) error { + + value := l.ctx.Value(constant.CtxKeyIdentifier) + if value == nil || value.(string) == "" { + l.Errorf("DeviceWsConnectLogic DeviceWsConnect identifier is empty") + return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "identifier is empty") + } + identifier := value.(string) + _, err := l.svcCtx.UserModel.FindOneDeviceByIdentifier(l.ctx, identifier) + if err != nil && !sysErr.Is(err, gorm.ErrRecordNotFound) { + l.Errorf("DeviceWsConnectLogic DeviceWsConnect FindOneDeviceByIdentifier err: %v", err) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), err.Error()) + } + + value = l.ctx.Value(constant.CtxKeyUser) + if value == nil { + l.Errorf("DeviceWsConnectLogic DeviceWsConnect value is nil") + return nil + } + userInfo := value.(*user.User) + if sysErr.Is(err, gorm.ErrRecordNotFound) { + device := user.Device{ + Identifier: identifier, + UserId: userInfo.Id, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + Online: true, + Enabled: true, + } + err := l.svcCtx.UserModel.InsertDevice(l.ctx, &device) + if err != nil { + l.Errorf("DeviceWsConnectLogic DeviceWsConnect InsertDevice err: %v", err) + return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), err.Error()) + } + } + //默认在线设备1 + maxDevice := 3 + subscribe, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, userInfo.Id, 1, 2) + if err == nil { + for _, sub := range subscribe { + if time.Now().Before(sub.ExpireTime) { + deviceLimit := int(sub.Subscribe.DeviceLimit) + if deviceLimit > maxDevice { + maxDevice = deviceLimit + } + } + } + } + l.svcCtx.DeviceManager.AddDevice(c.Writer, c.Request, l.ctx.Value(constant.CtxKeySessionID).(string), userInfo.Id, identifier, maxDevice) + return nil +}