2025-11-03 17:35:03 +08:00
|
|
|
// Package handler provides HTTP handlers for various endpoints.
|
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-04 17:12:15 +08:00
|
|
|
"context"
|
2025-11-03 17:35:03 +08:00
|
|
|
"fmt"
|
|
|
|
|
"net/http"
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
"modelRT/constants"
|
2025-11-05 18:20:54 +08:00
|
|
|
"modelRT/database"
|
2025-11-03 17:35:03 +08:00
|
|
|
"modelRT/logger"
|
|
|
|
|
"modelRT/network"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/gofrs/uuid"
|
2025-11-05 18:20:54 +08:00
|
|
|
"gorm.io/gorm"
|
2025-11-03 17:35:03 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var globalMonitorState *SharedMonitorState
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
globalMonitorState = NewSharedMonitorState()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RealTimeMonitorHandler define real time data monitor process API
|
|
|
|
|
// @Summary 开始或结束实时数据监控
|
|
|
|
|
// @Description 根据用户输入的组件token,从 modelRT 服务中开始或结束对于实时数据的监控
|
|
|
|
|
// @Tags RealTime Component
|
|
|
|
|
// @Accept json
|
|
|
|
|
// @Produce json
|
|
|
|
|
// @Router /data/realtime [get]
|
|
|
|
|
func RealTimeMonitorHandler(c *gin.Context) {
|
|
|
|
|
var request network.RealTimeQueryRequest
|
|
|
|
|
var monitorAction string
|
|
|
|
|
var monitorID string
|
|
|
|
|
|
|
|
|
|
if err := c.ShouldBindJSON(&request); err != nil {
|
|
|
|
|
logger.Error(c, "failed to unmarshal real time query request", "error", err)
|
|
|
|
|
c.JSON(http.StatusOK, network.FailureResponse{
|
|
|
|
|
Code: http.StatusBadRequest,
|
|
|
|
|
Msg: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if request.Action == constants.MonitorStartAction && request.MonitorID == "" {
|
|
|
|
|
monitorAction = request.Action
|
|
|
|
|
id, err := uuid.NewV4()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(c, "failed to generate monitor id", "error", err)
|
|
|
|
|
c.JSON(http.StatusOK, network.FailureResponse{
|
|
|
|
|
Code: http.StatusBadRequest,
|
|
|
|
|
Msg: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
monitorID = id.String()
|
|
|
|
|
} else if request.Action == constants.MonitorStartAction && request.MonitorID != "" {
|
|
|
|
|
monitorAction = constants.MonitorAppendAction
|
|
|
|
|
monitorID = request.MonitorID
|
|
|
|
|
} else if request.Action == constants.MonitorStopAction && request.MonitorID != "" {
|
|
|
|
|
monitorAction = request.Action
|
|
|
|
|
monitorID = request.MonitorID
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 18:20:54 +08:00
|
|
|
pgClient := database.GetPostgresDBClient()
|
|
|
|
|
// open transaction
|
|
|
|
|
tx := pgClient.Begin()
|
|
|
|
|
defer tx.Commit()
|
|
|
|
|
|
2025-11-03 17:35:03 +08:00
|
|
|
switch monitorAction {
|
|
|
|
|
case constants.MonitorStartAction:
|
2025-11-05 18:20:54 +08:00
|
|
|
results, err := globalMonitorState.CreateConfig(c, tx, monitorID, request.Components)
|
2025-11-04 17:12:15 +08:00
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(c, "create real time data monitor config failed", "error", err)
|
|
|
|
|
c.JSON(http.StatusOK, network.FailureResponse{
|
|
|
|
|
Code: http.StatusBadRequest,
|
|
|
|
|
Msg: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
2025-11-03 17:35:03 +08:00
|
|
|
}
|
2025-11-04 17:12:15 +08:00
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, network.SuccessResponse{
|
|
|
|
|
Code: http.StatusOK,
|
|
|
|
|
Msg: "success",
|
2025-11-05 18:20:54 +08:00
|
|
|
PayLoad: network.RealTimeQueryPayload{
|
|
|
|
|
TargetResults: results,
|
2025-11-04 17:12:15 +08:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
return
|
2025-11-03 17:35:03 +08:00
|
|
|
case constants.MonitorStopAction:
|
2025-11-04 17:12:15 +08:00
|
|
|
globalMonitorState.RemoveTargets(c, monitorID, request.Components)
|
2025-11-03 17:35:03 +08:00
|
|
|
case constants.MonitorAppendAction:
|
2025-11-04 17:12:15 +08:00
|
|
|
err := globalMonitorState.AppendTargets(monitorID, request.Components)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(c, "append target to real time data monitor config failed", "error", err)
|
|
|
|
|
c.JSON(http.StatusOK, network.FailureResponse{
|
|
|
|
|
Code: http.StatusBadRequest,
|
|
|
|
|
Msg: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
2025-11-03 17:35:03 +08:00
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
err := fmt.Errorf("%w: %s", constants.ErrUnsupportedAction, request.Action)
|
|
|
|
|
logger.Error(c, "unsupported action of real time data monitor request", "error", err)
|
|
|
|
|
c.JSON(http.StatusOK, network.FailureResponse{
|
|
|
|
|
Code: http.StatusBadRequest,
|
|
|
|
|
Msg: err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RealTimeMonitorComponent define struct of real time monitor component
|
|
|
|
|
type RealTimeMonitorComponent struct {
|
2025-11-04 17:12:15 +08:00
|
|
|
targets []string
|
2025-11-03 17:35:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RealTimeMonitorConfig define struct of real time monitor config
|
|
|
|
|
type RealTimeMonitorConfig struct {
|
|
|
|
|
noticeChan chan struct{}
|
|
|
|
|
components map[string]*RealTimeMonitorComponent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SharedMonitorState define struct of shared monitor state with mutex
|
|
|
|
|
type SharedMonitorState struct {
|
|
|
|
|
monitorMap map[string]*RealTimeMonitorConfig
|
|
|
|
|
mutex sync.RWMutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewSharedMonitorState define function to create new SharedMonitorState
|
|
|
|
|
func NewSharedMonitorState() *SharedMonitorState {
|
|
|
|
|
return &SharedMonitorState{
|
|
|
|
|
monitorMap: make(map[string]*RealTimeMonitorConfig),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 17:12:15 +08:00
|
|
|
// CreateConfig define function to create config in SharedMonitorState
|
2025-11-05 18:20:54 +08:00
|
|
|
func (s *SharedMonitorState) CreateConfig(ctx context.Context, tx *gorm.DB, monitorID string, components []network.RealTimeComponentItem) ([]network.TargetResult, error) {
|
2025-11-03 17:35:03 +08:00
|
|
|
s.mutex.Lock()
|
|
|
|
|
defer s.mutex.Unlock()
|
2025-11-04 17:12:15 +08:00
|
|
|
|
|
|
|
|
if _, exist := s.monitorMap[monitorID]; exist {
|
2025-11-05 18:20:54 +08:00
|
|
|
return nil, fmt.Errorf("monitorID %s already exists. Use AppendTargets to modify existing config", monitorID)
|
2025-11-04 17:12:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config := &RealTimeMonitorConfig{
|
|
|
|
|
noticeChan: make(chan struct{}),
|
|
|
|
|
components: make(map[string]*RealTimeMonitorComponent),
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 18:20:54 +08:00
|
|
|
targetProcessResults := make([]network.TargetResult, 0, processRealTimeRequestCount(components))
|
|
|
|
|
for _, componentItem := range components {
|
|
|
|
|
interval := componentItem.Interval
|
|
|
|
|
for _, target := range componentItem.Targets {
|
|
|
|
|
targetModel, err := database.ParseDataIdentifierToken(ctx, tx, target)
|
|
|
|
|
|
|
|
|
|
var targetResult network.TargetResult
|
|
|
|
|
targetResult.ID = target
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(ctx, "parse data indentity token failed", "error", err, "identity_token", target)
|
|
|
|
|
targetResult.Code = constants.SubFailedCode
|
|
|
|
|
targetResult.Msg = fmt.Sprintf("%s: %s", constants.SubFailedMsg, err.Error())
|
|
|
|
|
targetProcessResults = append(targetProcessResults, targetResult)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetResult.Code = constants.SubSuccessCode
|
|
|
|
|
targetResult.Msg = constants.SubSuccessMsg
|
|
|
|
|
if _, ok := config.components[componentItem.Interval]; !ok {
|
|
|
|
|
targets := make([]string, 0, len(componentItem.Targets))
|
|
|
|
|
config.components[interval] = &RealTimeMonitorComponent{
|
|
|
|
|
targets: append(targets, target),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
component := config.components[interval]
|
|
|
|
|
component.targets = append(component.targets, target)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println(targetModel.GetMeasurementInfo().Size)
|
2025-11-04 17:12:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.monitorMap[monitorID] = config
|
2025-11-05 18:20:54 +08:00
|
|
|
return targetProcessResults, nil
|
2025-11-03 17:35:03 +08:00
|
|
|
}
|
|
|
|
|
|
2025-11-04 17:12:15 +08:00
|
|
|
// AppendTargets define function to append targets in SharedMonitorState
|
2025-11-05 18:20:54 +08:00
|
|
|
// TODO 增加targetsResults的返回
|
2025-11-04 17:12:15 +08:00
|
|
|
func (s *SharedMonitorState) AppendTargets(monitorID string, components []network.RealTimeComponentItem) error {
|
|
|
|
|
s.mutex.Lock()
|
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
config, exist := s.monitorMap[monitorID]
|
|
|
|
|
if !exist {
|
|
|
|
|
return fmt.Errorf("monitorID %s not found. Use CreateConfig to start a new config", monitorID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, compent := range components {
|
|
|
|
|
interval := compent.Interval
|
|
|
|
|
comp, compExist := config.components[interval]
|
|
|
|
|
if !compExist {
|
|
|
|
|
comp = &RealTimeMonitorComponent{
|
2025-11-05 18:20:54 +08:00
|
|
|
targets: compent.Targets,
|
2025-11-04 17:12:15 +08:00
|
|
|
}
|
|
|
|
|
config.components[interval] = comp
|
|
|
|
|
} else {
|
2025-11-05 18:20:54 +08:00
|
|
|
comp.targets = append(comp.targets, compent.Targets...)
|
2025-11-04 17:12:15 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.noticeChan <- struct{}{}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpsertTargets define function to upsert targets in SharedMonitorState
|
2025-11-05 18:20:54 +08:00
|
|
|
// TODO 增加targetsResults的返回
|
2025-11-04 17:12:15 +08:00
|
|
|
func (s *SharedMonitorState) UpsertTargets(monitorID string, interval string, newTargets []string) (isNewMonitor bool, err error) {
|
|
|
|
|
s.mutex.Lock()
|
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
config, exist := s.monitorMap[monitorID]
|
|
|
|
|
if !exist {
|
|
|
|
|
config = &RealTimeMonitorConfig{
|
|
|
|
|
noticeChan: make(chan struct{}),
|
|
|
|
|
components: make(map[string]*RealTimeMonitorComponent),
|
|
|
|
|
}
|
|
|
|
|
config.components[interval] = &RealTimeMonitorComponent{
|
|
|
|
|
targets: newTargets,
|
|
|
|
|
}
|
|
|
|
|
s.monitorMap[monitorID] = config
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
comp, compExist := config.components[interval]
|
|
|
|
|
if !compExist {
|
|
|
|
|
comp = &RealTimeMonitorComponent{
|
|
|
|
|
targets: newTargets,
|
|
|
|
|
}
|
|
|
|
|
config.components[interval] = comp
|
|
|
|
|
} else {
|
|
|
|
|
comp.targets = append(comp.targets, newTargets...)
|
|
|
|
|
}
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get define function to get value from SharedMonitorState
|
2025-11-03 17:35:03 +08:00
|
|
|
func (s *SharedMonitorState) Get(monitorID, interval string) ([]string, bool) {
|
|
|
|
|
s.mutex.RLock()
|
|
|
|
|
defer s.mutex.RUnlock()
|
|
|
|
|
|
|
|
|
|
config, ok := s.monitorMap[monitorID]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
component, ok := config.components[interval]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, false
|
|
|
|
|
}
|
|
|
|
|
return component.targets, true
|
|
|
|
|
}
|
2025-11-04 17:12:15 +08:00
|
|
|
|
|
|
|
|
// RemoveTargets define function to remove targets in SharedMonitorState
|
2025-11-05 18:20:54 +08:00
|
|
|
// TODO 增加targetsResults的返回
|
2025-11-04 17:12:15 +08:00
|
|
|
func (s *SharedMonitorState) RemoveTargets(ctx context.Context, monitorID string, components []network.RealTimeComponentItem) error {
|
|
|
|
|
s.mutex.Lock()
|
|
|
|
|
defer s.mutex.Unlock()
|
|
|
|
|
|
|
|
|
|
config, exist := s.monitorMap[monitorID]
|
|
|
|
|
if !exist {
|
|
|
|
|
return fmt.Errorf("monitorID %s not found", monitorID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, compent := range components {
|
|
|
|
|
interval := compent.Interval
|
|
|
|
|
comp, compExist := config.components[interval]
|
|
|
|
|
if !compExist {
|
|
|
|
|
logger.Error(ctx, fmt.Sprintf("component with interval %s not found under monitorID %s", interval, monitorID), "monitorID", monitorID, "interval", interval)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetsToRemoveMap := make(map[string]struct{})
|
|
|
|
|
for _, target := range compent.Targets {
|
|
|
|
|
targetsToRemoveMap[target] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var newTargets []string
|
|
|
|
|
for _, existingTarget := range comp.targets {
|
|
|
|
|
if _, found := targetsToRemoveMap[existingTarget]; !found {
|
|
|
|
|
newTargets = append(newTargets, existingTarget)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
comp.targets = newTargets
|
|
|
|
|
|
|
|
|
|
if len(comp.targets) == 0 {
|
|
|
|
|
delete(config.components, interval)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(config.components) == 0 {
|
|
|
|
|
delete(s.monitorMap, monitorID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.noticeChan <- struct{}{}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-11-05 18:20:54 +08:00
|
|
|
|
|
|
|
|
func processRealTimeRequestCount(components []network.RealTimeComponentItem) int {
|
|
|
|
|
totalTargetsCount := 0
|
|
|
|
|
for _, compItem := range components {
|
|
|
|
|
totalTargetsCount += len(compItem.Targets)
|
|
|
|
|
}
|
|
|
|
|
return totalTargetsCount
|
|
|
|
|
}
|