Add: Add a WebSocket connection to monitor the app's online status.

This commit is contained in:
EUForest 2025-11-06 15:34:51 +08:00
parent 066f5d6538
commit 8cce9b95b4
6 changed files with 109 additions and 1 deletions

View File

@ -106,6 +106,22 @@ type (
UnbindDeviceRequest {
Id int64 `json:"id" validate:"required"`
}
GetDeviceOnlineStatsResponse {
WeeklyStats []WeeklyStat `json:"weekly_stats"`
ConnectionRecords ConnectionRecords `json:"connection_records"`
}
WeeklyStat {
Day int `json:"day"`
DayName string `json:"day_name"`
Hours float64 `json:"hours"`
}
ConnectionRecords {
CurrentContinuousDays int64 `json:"current_continuous_days"`
HistoryContinuousDays int64 `json:"history_continuous_days"`
LongestSingleConnection int64 `json:"longest_single_connection"`
}
)
@server (
@ -209,5 +225,21 @@ service ppanel {
@doc "Unbind Device"
@handler UnbindDevice
put /unbind_device (UnbindDeviceRequest)
@doc "Device Online Statistics"
@handler DeviceOnlineStatistics
get /device_online_statistics returns (GetDeviceOnlineStatsResponse)
}
@server(
prefix: v1/public/user
group: public/user/ws
middleware: AuthMiddleware
)
service ppanel {
@doc "Webosocket Device Connect"
@handler DeviceWsConnect
get /device_ws_connect
}

View File

@ -0,0 +1,18 @@
package user
import (
"github.com/gin-gonic/gin"
"github.com/perfect-panel/server/internal/logic/public/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
// Device Online Statistics
func DeviceOnlineStatisticsHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
l := user.NewDeviceOnlineStatisticsLogic(c.Request.Context(), svcCtx)
resp, err := l.DeviceOnlineStatistics()
result.HttpResult(c, resp, err)
}
}

View File

@ -0,0 +1,29 @@
package ws
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
wslogic "github.com/perfect-panel/server/internal/logic/public/user/ws"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/result"
)
var upGrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // 允许所有来源,生产环境中应该根据需求限制
},
}
// Webosocket Device Connect
func DeviceWsConnectHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
return func(c *gin.Context) {
l := wslogic.NewDeviceWsConnectLogic(c.Request.Context(), svcCtx)
err := l.DeviceWsConnect(c)
result.HttpResult(c, nil, err)
}
}

View File

@ -33,6 +33,7 @@ import (
publicSubscribe "github.com/perfect-panel/server/internal/handler/public/subscribe"
publicTicket "github.com/perfect-panel/server/internal/handler/public/ticket"
publicUser "github.com/perfect-panel/server/internal/handler/public/user"
publicUserWs "github.com/perfect-panel/server/internal/handler/public/user/ws"
server "github.com/perfect-panel/server/internal/handler/server"
"github.com/perfect-panel/server/internal/middleware"
"github.com/perfect-panel/server/internal/svc"
@ -795,6 +796,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
// Query User Commission Log
publicUserGroupRouter.GET("/commission_log", publicUser.QueryUserCommissionLogHandler(serverCtx))
// Device Online Statistics
publicUserGroupRouter.GET("/device_online_statistics", publicUser.DeviceOnlineStatisticsHandler(serverCtx))
// Get Device List
publicUserGroupRouter.GET("/devices", publicUser.GetDeviceListHandler(serverCtx))
@ -841,6 +845,14 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
publicUserGroupRouter.POST("/verify_email", publicUser.VerifyEmailHandler(serverCtx))
}
publicUserWsGroupRouter := router.Group("/v1/public/user")
publicUserWsGroupRouter.Use(middleware.AuthMiddleware(serverCtx))
{
// Webosocket Device Connect
publicUserWsGroupRouter.GET("/device_ws_connect", publicUserWs.DeviceWsConnectHandler(serverCtx))
}
serverGroupRouter := router.Group("/v1/server")
serverGroupRouter.Use(middleware.ServerMiddleware(serverCtx))

View File

@ -51,7 +51,7 @@ func NewDeviceManager(srv *ServiceContext) *device.DeviceManager {
//获取设备昨日在线记录
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 {
if err := srv.DB.Model(&onlineRecord).Where("user_id = ? and created_at >= ? and created_at < ?", userID, startTime, endTime).First(&onlineRecord).Error; err != nil {
//昨日未在线连续在线天数为1
deviceOnlineRecord.DurationDays = 1
} else {

View File

@ -239,6 +239,12 @@ type CommissionLog struct {
Timestamp int64 `json:"timestamp"`
}
type ConnectionRecords struct {
CurrentContinuousDays int64 `json:"current_continuous_days"`
HistoryContinuousDays int64 `json:"history_continuous_days"`
LongestSingleConnection int64 `json:"longest_single_connection"`
}
type Coupon struct {
Id int64 `json:"id"`
Name string `json:"name"`
@ -822,6 +828,11 @@ type GetDeviceListResponse struct {
Total int64 `json:"total"`
}
type GetDeviceOnlineStatsResponse struct {
WeeklyStats []WeeklyStat `json:"weekly_stats"`
ConnectionRecords ConnectionRecords `json:"connection_records"`
}
type GetDocumentDetailRequest struct {
Id int64 `json:"id" validate:"required"`
}
@ -2734,3 +2745,9 @@ type VmessProtocol struct {
Network string `json:"network"`
Transport string `json:"transport"`
}
type WeeklyStat struct {
Day int `json:"day"`
DayName string `json:"day_name"`
Hours float64 `json:"hours"`
}