feat: merge bay-realtime-data-calc into develop
This commit is contained in:
commit
c17ddb80b9
|
|
@ -27,3 +27,16 @@ go.work
|
|||
/log/
|
||||
# Shield config files in the configs folder
|
||||
/configs/**/*.yaml
|
||||
/configs/**/*.pem
|
||||
|
||||
# ai config
|
||||
.cursor/
|
||||
.claude/
|
||||
.cursorrules
|
||||
.copilot/
|
||||
.chatgpt/
|
||||
.ai_history/
|
||||
.vector_cache/
|
||||
ai-debug.log
|
||||
*.patch
|
||||
*.diff
|
||||
|
|
@ -16,6 +16,12 @@ var (
|
|||
|
||||
// ErrFoundTargetFailed define variable to returned when the specific database table cannot be identified using the provided token info.
|
||||
ErrFoundTargetFailed = newError(40004, "found target table by token failed")
|
||||
// ErrSubTargetRepeat define variable to indicates subscription target already exist in list
|
||||
ErrSubTargetRepeat = newError(40005, "subscription target already exist in list")
|
||||
// ErrSubTargetNotFound define variable to indicates can not find measurement by subscription target
|
||||
ErrSubTargetNotFound = newError(40006, "found measuremnet by subscription target failed")
|
||||
// ErrCancelSubTargetMissing define variable to indicates cancel a not exist subscription target
|
||||
ErrCancelSubTargetMissing = newError(40007, "cancel a not exist subscription target")
|
||||
|
||||
// ErrDBQueryFailed define variable to represents a generic failure during a PostgreSQL SELECT or SCAN operation.
|
||||
ErrDBQueryFailed = newError(50001, "query postgres database data failed")
|
||||
|
|
@ -40,4 +46,10 @@ var (
|
|||
|
||||
// ErrCacheQueryFailed define variable to indicates query cached data by token failed.
|
||||
ErrCacheQueryFailed = newError(60003, "query cached data by token failed")
|
||||
|
||||
// ErrTaskNotFound indicates the async task with the given ID does not exist.
|
||||
ErrTaskNotFound = newError(40008, "async task not found")
|
||||
|
||||
// ErrTaskCannotCancel indicates the task is already running or completed and cannot be cancelled.
|
||||
ErrTaskCannotCancel = newError(40009, "task cannot be cancelled, already running or completed")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -139,10 +139,10 @@ func (e *AppError) SetMsg(msg string) *AppError {
|
|||
}
|
||||
|
||||
type formattedErr struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Cause interface{} `json:"cause"`
|
||||
Occurred string `json:"occurred"`
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Cause any `json:"cause"`
|
||||
Occurred string `json:"occurred"`
|
||||
}
|
||||
|
||||
// toStructuredError define func convert AppError to structured error for better readability
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
// Package common define common error variables
|
||||
package common
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrUnknowEventActionCommand define error of unknown event action command
|
||||
var ErrUnknowEventActionCommand = errors.New("unknown action command")
|
||||
|
||||
// ErrExecEventActionFailed define error of execute event action failed
|
||||
var ErrExecEventActionFailed = errors.New("exec event action func failed")
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
// Package common define common error variables
|
||||
package common
|
||||
|
||||
import "errors"
|
||||
|
||||
|
|
@ -42,12 +42,13 @@ var baseCurrentFunc = func(archorValue float64, args ...float64) float64 {
|
|||
}
|
||||
|
||||
// SelectAnchorCalculateFuncAndParams define select anchor func and anchor calculate value by component type 、 anchor name and component data
|
||||
func SelectAnchorCalculateFuncAndParams(componentType int, anchorName string, componentData map[string]interface{}) (func(archorValue float64, args ...float64) float64, []float64) {
|
||||
func SelectAnchorCalculateFuncAndParams(componentType int, anchorName string, componentData map[string]any) (func(archorValue float64, args ...float64) float64, []float64) {
|
||||
if componentType == constants.DemoType {
|
||||
if anchorName == "voltage" {
|
||||
switch anchorName {
|
||||
case "voltage":
|
||||
resistance := componentData["resistance"].(float64)
|
||||
return baseVoltageFunc, []float64{resistance}
|
||||
} else if anchorName == "current" {
|
||||
case "current":
|
||||
resistance := componentData["resistance"].(float64)
|
||||
return baseCurrentFunc, []float64{resistance}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package config
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
|
@ -19,6 +20,21 @@ type ServiceConfig struct {
|
|||
ServiceAddr string `mapstructure:"service_addr"`
|
||||
ServiceName string `mapstructure:"service_name"`
|
||||
SecretKey string `mapstructure:"secret_key"`
|
||||
DeployEnv string `mapstructure:"deploy_env"`
|
||||
}
|
||||
|
||||
// RabbitMQConfig define config struct of RabbitMQ config
|
||||
type RabbitMQConfig struct {
|
||||
CACertPath string `mapstructure:"ca_cert_path"`
|
||||
ClientKeyPath string `mapstructure:"client_key_path"`
|
||||
ClientKeyPassword string `mapstructure:"client_key_password"`
|
||||
ClientCertPath string `mapstructure:"client_cert_path"`
|
||||
InsecureSkipVerify bool `mapstructure:"insecure_skip_verify"`
|
||||
ServerName string `mapstructure:"server_name"`
|
||||
User string `mapstructure:"user"`
|
||||
Password string `mapstructure:"password"`
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
}
|
||||
|
||||
// KafkaConfig define config struct of kafka config
|
||||
|
|
@ -40,24 +56,33 @@ type PostgresConfig struct {
|
|||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
// LokiConfig define config struct of loki direct-push (used in development mode)
|
||||
type LokiConfig struct {
|
||||
Endpoint string `mapstructure:"endpoint"` // empty disables direct push
|
||||
Labels map[string]string `mapstructure:"labels"`
|
||||
}
|
||||
|
||||
// LoggerConfig define config struct of zap logger config
|
||||
type LoggerConfig struct {
|
||||
Mode string `mapstructure:"mode"`
|
||||
Level string `mapstructure:"level"`
|
||||
FilePath string `mapstructure:"filepath"`
|
||||
MaxSize int `mapstructure:"maxsize"`
|
||||
MaxBackups int `mapstructure:"maxbackups"`
|
||||
MaxAge int `mapstructure:"maxage"`
|
||||
Compress bool `mapstructure:"compress"`
|
||||
Mode string `mapstructure:"mode"`
|
||||
Level string `mapstructure:"level"`
|
||||
FilePath string `mapstructure:"filepath"` // empty disables file rotation in container modes
|
||||
MaxSize int `mapstructure:"maxsize"`
|
||||
MaxBackups int `mapstructure:"maxbackups"`
|
||||
MaxAge int `mapstructure:"maxage"`
|
||||
Compress bool `mapstructure:"compress"`
|
||||
Loki LokiConfig `mapstructure:"loki"`
|
||||
}
|
||||
|
||||
// RedisConfig define config struct of redis config
|
||||
type RedisConfig struct {
|
||||
Addr string `mapstructure:"addr"`
|
||||
Password string `mapstructure:"password"`
|
||||
DB int `mapstructure:"db"`
|
||||
PoolSize int `mapstructure:"poolsize"`
|
||||
Timeout int `mapstructure:"timeout"`
|
||||
Addr string `mapstructure:"addr"`
|
||||
Password string `mapstructure:"password"`
|
||||
DB int `mapstructure:"db"`
|
||||
PoolSize int `mapstructure:"poolsize"`
|
||||
DialTimeout int `mapstructure:"dial_timeout"`
|
||||
ReadTimeout int `mapstructure:"read_timeout"`
|
||||
WriteTimeout int `mapstructure:"write_timeout"`
|
||||
}
|
||||
|
||||
// AntsConfig define config struct of ants pool config
|
||||
|
|
@ -74,18 +99,37 @@ type DataRTConfig struct {
|
|||
Method string `mapstructure:"polling_api_method"`
|
||||
}
|
||||
|
||||
// OtelConfig define config struct of OpenTelemetry tracing
|
||||
type OtelConfig struct {
|
||||
Endpoint string `mapstructure:"endpoint"` // e.g. "localhost:4318"
|
||||
Insecure bool `mapstructure:"insecure"`
|
||||
}
|
||||
|
||||
// AsyncTaskConfig define config struct of asynchronous task system
|
||||
type AsyncTaskConfig struct {
|
||||
WorkerPoolSize int `mapstructure:"worker_pool_size"`
|
||||
QueueConsumerCount int `mapstructure:"queue_consumer_count"`
|
||||
MaxRetryCount int `mapstructure:"max_retry_count"`
|
||||
RetryInitialDelay time.Duration `mapstructure:"retry_initial_delay"`
|
||||
RetryMaxDelay time.Duration `mapstructure:"retry_max_delay"`
|
||||
HealthCheckInterval time.Duration `mapstructure:"health_check_interval"`
|
||||
}
|
||||
|
||||
// ModelRTConfig define config struct of model runtime server
|
||||
type ModelRTConfig struct {
|
||||
BaseConfig `mapstructure:"base"`
|
||||
ServiceConfig `mapstructure:"service"`
|
||||
PostgresConfig `mapstructure:"postgres"`
|
||||
RabbitMQConfig `mapstructure:"rabbitmq"`
|
||||
KafkaConfig `mapstructure:"kafka"`
|
||||
LoggerConfig `mapstructure:"logger"`
|
||||
AntsConfig `mapstructure:"ants"`
|
||||
DataRTConfig `mapstructure:"dataRT"`
|
||||
LockerRedisConfig RedisConfig `mapstructure:"locker_redis"`
|
||||
StorageRedisConfig RedisConfig `mapstructure:"storage_redis"`
|
||||
PostgresDBURI string `mapstructure:"-"`
|
||||
AsyncTaskConfig AsyncTaskConfig `mapstructure:"async_task"`
|
||||
OtelConfig OtelConfig `mapstructure:"otel"`
|
||||
PostgresDBURI string `mapstructure:"-"`
|
||||
}
|
||||
|
||||
// ReadAndInitConfig return modelRT project config struct
|
||||
|
|
@ -101,6 +145,9 @@ func ReadAndInitConfig(configDir, configName, configType string) (modelRTConfig
|
|||
panic(err)
|
||||
}
|
||||
|
||||
config.BindEnv("postgres.password", "POSTGRES_PASSWORD")
|
||||
config.BindEnv("service.secret_key", "SERVICE_SECRET_KEY")
|
||||
|
||||
if err := config.Unmarshal(&modelRTConfig); err != nil {
|
||||
panic(fmt.Sprintf("unmarshal modelRT config failed:%s\n", err.Error()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// CodeSuccess define constant to indicates that the API was successfully processed
|
||||
CodeSuccess = 20000
|
||||
// CodeInvalidParamFailed define constant to indicates request parameter parsing failed
|
||||
CodeInvalidParamFailed = 40001
|
||||
// CodeDBQueryFailed define constant to indicates database query operation failed
|
||||
CodeDBQueryFailed = 50001
|
||||
// CodeDBUpdateailed define constant to indicates database update operation failed
|
||||
CodeDBUpdateailed = 50002
|
||||
// CodeRedisQueryFailed define constant to indicates redis query operation failed
|
||||
CodeRedisQueryFailed = 60001
|
||||
// CodeRedisUpdateFailed define constant to indicates redis update operation failed
|
||||
CodeRedisUpdateFailed = 60002
|
||||
)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// CodeSuccess define constant to indicates that the API was successfully processed
|
||||
CodeSuccess = 20000
|
||||
// CodeInvalidParamFailed define constant to indicates request parameter parsing failed
|
||||
CodeInvalidParamFailed = 40001
|
||||
// CodeFoundTargetFailed define variable to returned when the specific database table cannot be identified using the provided token info.
|
||||
CodeFoundTargetFailed = 40004
|
||||
// CodeSubTargetRepeat define variable to indicates subscription target already exist in list
|
||||
CodeSubTargetRepeat = 40005
|
||||
// CodeSubTargetNotFound define variable to indicates can not find measurement by subscription target
|
||||
CodeSubTargetNotFound = 40006
|
||||
// CodeCancelSubTargetMissing define variable to indicates cancel a not exist subscription target
|
||||
CodeCancelSubTargetMissing = 40007
|
||||
// CodeUpdateSubTargetMissing define variable to indicates update a not exist subscription target
|
||||
CodeUpdateSubTargetMissing = 40008
|
||||
// CodeAppendSubTargetMissing define variable to indicates append a not exist subscription target
|
||||
CodeAppendSubTargetMissing = 40009
|
||||
// CodeUnsupportSubOperation define variable to indicates append a not exist subscription target
|
||||
CodeUnsupportSubOperation = 40010
|
||||
// CodeDBQueryFailed define constant to indicates database query operation failed
|
||||
CodeDBQueryFailed = 50001
|
||||
// CodeDBUpdateailed define constant to indicates database update operation failed
|
||||
CodeDBUpdateailed = 50002
|
||||
// CodeRedisQueryFailed define constant to indicates redis query operation failed
|
||||
CodeRedisQueryFailed = 60001
|
||||
// CodeRedisUpdateFailed define constant to indicates redis update operation failed
|
||||
CodeRedisUpdateFailed = 60002
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// DevelopmentDeployMode define development operator environment for modelRT project
|
||||
DevelopmentDeployMode = "development"
|
||||
// DebugDeployMode define debug operator environment for modelRT project
|
||||
DebugDeployMode = "debug"
|
||||
// ProductionDeployMode define production operator environment for modelRT project
|
||||
ProductionDeployMode = "production"
|
||||
)
|
||||
|
|
@ -1,31 +1,97 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
// EvenvtType define event type
|
||||
type EvenvtType int
|
||||
|
||||
const (
|
||||
// TIBreachTriggerType define out of bounds type constant
|
||||
TIBreachTriggerType = "trigger"
|
||||
// EventGeneralHard define gereral hard event type
|
||||
EventGeneralHard EvenvtType = iota
|
||||
// EventGeneralPlatformSoft define gereral platform soft event type
|
||||
EventGeneralPlatformSoft
|
||||
// EventGeneralApplicationSoft define gereral application soft event type
|
||||
EventGeneralApplicationSoft
|
||||
// EventWarnHard define warn hard event type
|
||||
EventWarnHard
|
||||
// EventWarnPlatformSoft define warn platform soft event type
|
||||
EventWarnPlatformSoft
|
||||
// EventWarnApplicationSoft define warn application soft event type
|
||||
EventWarnApplicationSoft
|
||||
// EventCriticalHard define critical hard event type
|
||||
EventCriticalHard
|
||||
// EventCriticalPlatformSoft define critical platform soft event type
|
||||
EventCriticalPlatformSoft
|
||||
// EventCriticalApplicationSoft define critical application soft event type
|
||||
EventCriticalApplicationSoft
|
||||
)
|
||||
|
||||
// IsGeneral define fucn to check event type is general
|
||||
func IsGeneral(eventType EvenvtType) bool {
|
||||
return eventType < 3
|
||||
}
|
||||
|
||||
// IsWarning define fucn to check event type is warn
|
||||
func IsWarning(eventType EvenvtType) bool {
|
||||
return eventType >= 3 && eventType <= 5
|
||||
}
|
||||
|
||||
// IsCritical define fucn to check event type is critical
|
||||
func IsCritical(eventType EvenvtType) bool {
|
||||
return eventType >= 6
|
||||
}
|
||||
|
||||
const (
|
||||
// EventFromStation define event from station type
|
||||
EventFromStation = "station"
|
||||
// EventFromPlatform define event from platform type
|
||||
EventFromPlatform = "platform"
|
||||
// EventFromOthers define event from others type
|
||||
EventFromOthers = "others"
|
||||
)
|
||||
|
||||
const (
|
||||
// TelemetryUpLimit define telemetry upper limit
|
||||
TelemetryUpLimit = "up"
|
||||
// TelemetryUpUpLimit define telemetry upper upper limit
|
||||
TelemetryUpUpLimit = "upup"
|
||||
|
||||
// TelemetryDownLimit define telemetry limit
|
||||
TelemetryDownLimit = "down"
|
||||
// TelemetryDownDownLimit define telemetry lower lower limit
|
||||
TelemetryDownDownLimit = "downdown"
|
||||
// EventStatusHappended define status for event record when event just happened, no data attached yet
|
||||
EventStatusHappended = iota
|
||||
// EventStatusDataAttached define status for event record when event data attached, ready to be sent
|
||||
EventStatusDataAttached
|
||||
// EventStatusReported define status for event record when event reported to downstream, no matter it's successful or failed
|
||||
EventStatusReported
|
||||
// EventStatusConfirmed define status for event record when event confirmed by operator or CIM
|
||||
EventStatusConfirmed
|
||||
// EventStatusClosed define status for event record when event closed due to condition recovery or manual close
|
||||
EventStatusClosed
|
||||
)
|
||||
|
||||
const (
|
||||
// TelesignalRaising define telesignal raising edge
|
||||
TelesignalRaising = "raising"
|
||||
// TelesignalFalling define telesignal falling edge
|
||||
TelesignalFalling = "falling"
|
||||
// EventExchangeName define exchange name for event alarm message
|
||||
EventExchangeName = "event-exchange"
|
||||
// EventDeadExchangeName define dead letter exchange name for event alarm message
|
||||
EventDeadExchangeName = "event-dead-letter-exchange"
|
||||
)
|
||||
|
||||
const (
|
||||
// MinBreachCount define min breach count of real time data
|
||||
MinBreachCount = 10
|
||||
// EventUpDownRoutingKey define routing key for up or down limit event alarm message
|
||||
EventUpDownRoutingKey = "event.#"
|
||||
// EventUpDownDeadRoutingKey define dead letter routing key for up or down limit event alarm message
|
||||
EventUpDownDeadRoutingKey = "event.#"
|
||||
// EventUpDownQueueName define queue name for up or down limit event alarm message
|
||||
EventUpDownQueueName = "event-up-down-queue"
|
||||
// EventUpDownDeadQueueName define dead letter queue name for event alarm message
|
||||
EventUpDownDeadQueueName = "event-dead-letter-queue"
|
||||
)
|
||||
|
||||
const (
|
||||
// EventGeneralUpDownLimitCategroy define category for general up and down limit event
|
||||
EventGeneralUpDownLimitCategroy = "event.general.updown.limit"
|
||||
// EventWarnUpDownLimitCategroy define category for warn up and down limit event
|
||||
EventWarnUpDownLimitCategroy = "event.warn.updown.limit"
|
||||
// EventCriticalUpDownLimitCategroy define category for critical up and down limit event
|
||||
EventCriticalUpDownLimitCategroy = "event.critical.updown.limit"
|
||||
)
|
||||
|
||||
const (
|
||||
// EventTaskGeneralTestCategory define category for test task event
|
||||
EventTaskGeneralTestCategory = "event.general.task.test"
|
||||
// EventTaskGeneralTopologyAnalyzeCategory define category for topology analyze task event
|
||||
EventTaskGeneralTopologyAnalyzeCategory = "event.general.task.topology_analyze"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// MessageExchangeName define exchange name for message
|
||||
MessageExchangeName = "message-exchange"
|
||||
// MessageDeadExchangeName define dead letter exchange name for message
|
||||
MessageDeadExchangeName = "message-dead-letter-exchange"
|
||||
)
|
||||
|
||||
const (
|
||||
// MessageRoutingKey define binding routing key pattern for the message queue (matches all message.* categories)
|
||||
MessageRoutingKey = "message.#"
|
||||
// MessageDeadRoutingKey define binding routing key for the message dead letter queue
|
||||
MessageDeadRoutingKey = "#"
|
||||
// MessageQueueName define queue name for message
|
||||
MessageQueueName = "message-queue"
|
||||
// MessageDeadQueueName define dead letter queue name for message
|
||||
MessageDeadQueueName = "message-dead-letter-queue"
|
||||
)
|
||||
|
||||
const (
|
||||
// MessageTaskSubmittedCategory define category for task submitted message
|
||||
MessageTaskSubmittedCategory = "message.task.submitted"
|
||||
// MessageTaskRunningCategory define category for task running message
|
||||
MessageTaskRunningCategory = "message.task.running"
|
||||
// MessageTaskCompletedCategory define category for task completed message
|
||||
MessageTaskCompletedCategory = "message.task.completed"
|
||||
// MessageTaskFailedCategory define category for task failed message
|
||||
MessageTaskFailedCategory = "message.task.failed"
|
||||
// MessageTaskCancelledCategory define category for task cancelled message
|
||||
MessageTaskCancelledCategory = "message.task.cancelled"
|
||||
)
|
||||
|
|
@ -12,29 +12,6 @@ const (
|
|||
SubUpdateAction string = "update"
|
||||
)
|
||||
|
||||
// 定义状态常量
|
||||
// TODO 从4位格式修改为5位格式
|
||||
const (
|
||||
// SubSuccessCode define subscription success code
|
||||
SubSuccessCode = "1001"
|
||||
// SubFailedCode define subscription failed code
|
||||
SubFailedCode = "1002"
|
||||
// RTDSuccessCode define real time data return success code
|
||||
RTDSuccessCode = "1003"
|
||||
// RTDFailedCode define real time data return failed code
|
||||
RTDFailedCode = "1004"
|
||||
// CancelSubSuccessCode define cancel subscription success code
|
||||
CancelSubSuccessCode = "1005"
|
||||
// CancelSubFailedCode define cancel subscription failed code
|
||||
CancelSubFailedCode = "1006"
|
||||
// SubRepeatCode define subscription repeat code
|
||||
SubRepeatCode = "1007"
|
||||
// UpdateSubSuccessCode define update subscription success code
|
||||
UpdateSubSuccessCode = "1008"
|
||||
// UpdateSubFailedCode define update subscription failed code
|
||||
UpdateSubFailedCode = "1009"
|
||||
)
|
||||
|
||||
const (
|
||||
// SysCtrlPrefix define to indicates the prefix for all system control directives,facilitating unified parsing within the sendDataStream goroutine
|
||||
SysCtrlPrefix = "SYS_CTRL_"
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Package constants defines task-related constants for the async task system
|
||||
package constants
|
||||
|
||||
import "time"
|
||||
|
||||
// Task priority levels
|
||||
const (
|
||||
// TaskPriorityDefault is the default priority level for tasks
|
||||
TaskPriorityDefault = 5
|
||||
// TaskPriorityHigh represents high priority tasks
|
||||
TaskPriorityHigh = 10
|
||||
// TaskPriorityLow represents low priority tasks
|
||||
TaskPriorityLow = 1
|
||||
)
|
||||
|
||||
// Task queue configuration
|
||||
const (
|
||||
// TaskExchangeName is the name of the exchange for task routing
|
||||
TaskExchangeName = "modelrt.tasks.exchange"
|
||||
// TaskQueueName is the name of the main task queue
|
||||
TaskQueueName = "modelrt.tasks.queue"
|
||||
// TaskRoutingKey is the routing key for task messages
|
||||
TaskRoutingKey = "modelrt.task"
|
||||
)
|
||||
|
||||
// Task message settings
|
||||
const (
|
||||
// TaskMaxPriority is the maximum priority level for tasks (0-10)
|
||||
TaskMaxPriority = 10
|
||||
// TaskDefaultMessageTTL is the default time-to-live for task messages (24 hours)
|
||||
TaskDefaultMessageTTL = 24 * time.Hour
|
||||
)
|
||||
|
||||
// Task retry settings
|
||||
const (
|
||||
// TaskRetryMaxDefault is the default maximum number of retry attempts
|
||||
TaskRetryMaxDefault = 3
|
||||
// TaskRetryInitialDelayDefault is the default initial delay for exponential backoff
|
||||
TaskRetryInitialDelayDefault = 1 * time.Second
|
||||
// TaskRetryMaxDelayDefault is the default maximum delay for exponential backoff
|
||||
TaskRetryMaxDelayDefault = 5 * time.Minute
|
||||
// TaskRetryRandomFactorDefault is the default random factor for jitter (10%)
|
||||
TaskRetryRandomFactorDefault = 0.1
|
||||
// TaskRetryFixedDelayDefault is the default delay for fixed retry strategy
|
||||
TaskRetryFixedDelayDefault = 5 * time.Second
|
||||
)
|
||||
|
||||
// Test task settings
|
||||
const (
|
||||
// TestTaskSleepDurationDefault is the default sleep duration for test tasks (60 seconds)
|
||||
TestTaskSleepDurationDefault = 60
|
||||
// TestTaskSleepDurationMax is the maximum allowed sleep duration for test tasks (1 hour)
|
||||
TestTaskSleepDurationMax = 3600
|
||||
)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// TIBreachTriggerType define out of bounds type constant
|
||||
TIBreachTriggerType = "trigger"
|
||||
)
|
||||
|
||||
const (
|
||||
// TelemetryUpLimit define telemetry upper limit
|
||||
TelemetryUpLimit = "up"
|
||||
// TelemetryUpUpLimit define telemetry upper upper limit
|
||||
TelemetryUpUpLimit = "upup"
|
||||
|
||||
// TelemetryDownLimit define telemetry limit
|
||||
TelemetryDownLimit = "down"
|
||||
// TelemetryDownDownLimit define telemetry lower lower limit
|
||||
TelemetryDownDownLimit = "downdown"
|
||||
)
|
||||
|
||||
const (
|
||||
// TelesignalRaising define telesignal raising edge
|
||||
TelesignalRaising = "raising"
|
||||
// TelesignalFalling define telesignal falling edge
|
||||
TelesignalFalling = "falling"
|
||||
)
|
||||
|
||||
const (
|
||||
// MinBreachCount define min breach count of real time data
|
||||
MinBreachCount = 10
|
||||
)
|
||||
|
|
@ -1,9 +1,21 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
// Assuming the B3 specification
|
||||
// Internal context keys for trace values set by StartTrace middleware.
|
||||
// These are gin/stdlib context keys only — actual W3C header propagation
|
||||
// (traceparent / tracestate) is handled automatically by the OTel propagator.
|
||||
const (
|
||||
HeaderTraceID = "X-B3-TraceId"
|
||||
HeaderSpanID = "X-B3-SpanId"
|
||||
HeaderParentSpanID = "X-B3-ParentSpanId"
|
||||
HeaderTraceID = "trace-id"
|
||||
HeaderSpanID = "span-id"
|
||||
HeaderParentSpanID = "parent-span-id"
|
||||
)
|
||||
|
||||
// traceCtxKey is an unexported type for context keys to avoid collisions with other packages.
|
||||
type traceCtxKey string
|
||||
|
||||
// Typed context keys for trace values — use these with context.WithValue / ctx.Value.
|
||||
var (
|
||||
CtxKeyTraceID = traceCtxKey(HeaderTraceID)
|
||||
CtxKeySpanID = traceCtxKey(HeaderSpanID)
|
||||
CtxKeyParentSpanID = traceCtxKey(HeaderParentSpanID)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
// Package database define database operation functions
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UpdateTaskStarted updates task start time and status to running
|
||||
func UpdateTaskStarted(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, startedAt int64) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"status": orm.AsyncTaskStatusRunning,
|
||||
"started_at": startedAt,
|
||||
})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskRetryInfo updates task retry information
|
||||
func UpdateTaskRetryInfo(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, retryCount int, nextRetryTime int64) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
updateData := map[string]any{
|
||||
"retry_count": retryCount,
|
||||
}
|
||||
if nextRetryTime <= 0 {
|
||||
updateData["next_retry_time"] = nil
|
||||
} else {
|
||||
updateData["next_retry_time"] = nextRetryTime
|
||||
}
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(updateData)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskErrorInfo updates task error information
|
||||
func UpdateTaskErrorInfo(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, errorMsg, stackTrace string) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"failure_reason": errorMsg,
|
||||
"stack_trace": stackTrace,
|
||||
"status": orm.AsyncTaskStatusFailed,
|
||||
})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskExecutionTime updates task execution time
|
||||
func UpdateTaskExecutionTime(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, executionTime int64) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("execution_time", executionTime)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskWorkerID updates the worker ID that is processing the task
|
||||
func UpdateTaskWorkerID(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, workerID string) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("worker_id", workerID)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskPriority updates task priority
|
||||
func UpdateTaskPriority(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, priority int) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("priority", priority)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskQueueName updates task queue name
|
||||
func UpdateTaskQueueName(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, queueName string) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("queue_name", queueName)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskCreatedBy updates task creator information
|
||||
func UpdateTaskCreatedBy(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, createdBy string) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("created_by", createdBy)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateTaskResultWithMetrics updates task result with execution metrics
|
||||
func UpdateTaskResultWithMetrics(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, executionTime int64, memoryUsage *int64, cpuUsage *float64, retryCount int, completedAt int64) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTaskResult{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"execution_time": executionTime,
|
||||
"memory_usage": memoryUsage,
|
||||
"cpu_usage": cpuUsage,
|
||||
"retry_count": retryCount,
|
||||
"completed_at": completedAt,
|
||||
})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// GetTasksForRetry retrieves tasks that are due for retry
|
||||
func GetTasksForRetry(ctx context.Context, tx *gorm.DB, limit int) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
now := time.Now().Unix()
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("status = ? AND next_retry_time IS NOT NULL AND next_retry_time <= ?", orm.AsyncTaskStatusFailed, now).
|
||||
Order("next_retry_time ASC").
|
||||
Limit(limit).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// GetTasksByPriority retrieves tasks by priority order
|
||||
func GetTasksByPriority(ctx context.Context, tx *gorm.DB, status orm.AsyncTaskStatus, limit int) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("status = ?", status).
|
||||
Order("priority DESC, created_at ASC").
|
||||
Limit(limit).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// GetTasksByWorkerID retrieves tasks being processed by a specific worker
|
||||
func GetTasksByWorkerID(ctx context.Context, tx *gorm.DB, workerID string) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("worker_id = ? AND status = ?", workerID, orm.AsyncTaskStatusRunning).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// CleanupStaleTasks marks tasks as failed if they have been running for too long
|
||||
func CleanupStaleTasks(ctx context.Context, tx *gorm.DB, timeoutSeconds int64) (int64, error) {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
threshold := time.Now().Unix() - timeoutSeconds
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("status = ? AND started_at IS NOT NULL AND started_at < ?", orm.AsyncTaskStatusRunning, threshold).
|
||||
Updates(map[string]any{
|
||||
"status": orm.AsyncTaskStatusFailed,
|
||||
"failure_reason": "task timeout",
|
||||
"finished_at": time.Now().Unix(),
|
||||
})
|
||||
|
||||
return result.RowsAffected, result.Error
|
||||
}
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
// Package database define database operation functions
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// CreateAsyncTask creates a new async task in the database
|
||||
func CreateAsyncTask(ctx context.Context, tx *gorm.DB, taskType orm.AsyncTaskType, params orm.JSONMap) (*orm.AsyncTask, error) {
|
||||
taskID, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task := &orm.AsyncTask{
|
||||
TaskID: taskID,
|
||||
TaskType: taskType,
|
||||
Status: orm.AsyncTaskStatusSubmitted,
|
||||
Params: params,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).Create(task)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// GetAsyncTaskByID retrieves an async task by its ID
|
||||
func GetAsyncTaskByID(ctx context.Context, tx *gorm.DB, taskID uuid.UUID) (*orm.AsyncTask, error) {
|
||||
var task orm.AsyncTask
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("task_id = ?", taskID).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
First(&task)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return &task, nil
|
||||
}
|
||||
|
||||
// GetAsyncTasksByIDs retrieves multiple async tasks by their IDs
|
||||
func GetAsyncTasksByIDs(ctx context.Context, tx *gorm.DB, taskIDs []uuid.UUID) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
if len(taskIDs) == 0 {
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("task_id IN ?", taskIDs).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// UpdateAsyncTaskStatus updates the status of an async task
|
||||
func UpdateAsyncTaskStatus(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, status orm.AsyncTaskStatus) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("status", status)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// UpdateAsyncTaskProgress updates the progress of an async task
|
||||
func UpdateAsyncTaskProgress(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, progress int) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Update("progress", progress)
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// CompleteAsyncTask marks an async task as completed with timestamp
|
||||
func CompleteAsyncTask(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, timestamp int64) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"status": orm.AsyncTaskStatusCompleted,
|
||||
"finished_at": timestamp,
|
||||
"progress": 100,
|
||||
})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// FailAsyncTask marks an async task as failed with timestamp
|
||||
func FailAsyncTask(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, timestamp int64) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTask{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"status": orm.AsyncTaskStatusFailed,
|
||||
"finished_at": timestamp,
|
||||
})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// CreateAsyncTaskResult creates a result record for an async task
|
||||
func CreateAsyncTaskResult(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, result orm.JSONMap) error {
|
||||
taskResult := &orm.AsyncTaskResult{
|
||||
TaskID: taskID,
|
||||
Result: result,
|
||||
}
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resultOp := tx.WithContext(cancelCtx).Create(taskResult)
|
||||
return resultOp.Error
|
||||
}
|
||||
|
||||
// UpdateAsyncTaskResultWithError upserts a task result with error information.
|
||||
func UpdateAsyncTaskResultWithError(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, code int, message string, detail orm.JSONMap) error {
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := tx.WithContext(cancelCtx).
|
||||
Where("task_id = ?", taskID).
|
||||
FirstOrCreate(&orm.AsyncTaskResult{TaskID: taskID}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTaskResult{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"error_code": code,
|
||||
"error_message": message,
|
||||
"error_detail": detail,
|
||||
"result": nil,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// UpdateAsyncTaskResultWithSuccess updates a task result with success information
|
||||
func UpdateAsyncTaskResultWithSuccess(ctx context.Context, tx *gorm.DB, taskID uuid.UUID, result orm.JSONMap) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// First try to update existing record, if not found create new one
|
||||
existingResult := tx.WithContext(cancelCtx).
|
||||
Where("task_id = ?", taskID).
|
||||
FirstOrCreate(&orm.AsyncTaskResult{TaskID: taskID})
|
||||
|
||||
if existingResult.Error != nil {
|
||||
return existingResult.Error
|
||||
}
|
||||
|
||||
// Update with success information
|
||||
updateResult := tx.WithContext(cancelCtx).
|
||||
Model(&orm.AsyncTaskResult{}).
|
||||
Where("task_id = ?", taskID).
|
||||
Updates(map[string]any{
|
||||
"result": result,
|
||||
"error_code": nil,
|
||||
"error_message": nil,
|
||||
"error_detail": nil,
|
||||
})
|
||||
|
||||
return updateResult.Error
|
||||
}
|
||||
|
||||
// GetAsyncTaskResult retrieves the result of an async task
|
||||
func GetAsyncTaskResult(ctx context.Context, tx *gorm.DB, taskID uuid.UUID) (*orm.AsyncTaskResult, error) {
|
||||
var taskResult orm.AsyncTaskResult
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("task_id = ?", taskID).
|
||||
First(&taskResult)
|
||||
|
||||
if result.Error != nil {
|
||||
if result.Error == gorm.ErrRecordNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return &taskResult, nil
|
||||
}
|
||||
|
||||
// GetAsyncTaskResults retrieves multiple task results by task IDs
|
||||
func GetAsyncTaskResults(ctx context.Context, tx *gorm.DB, taskIDs []uuid.UUID) ([]orm.AsyncTaskResult, error) {
|
||||
var taskResults []orm.AsyncTaskResult
|
||||
|
||||
if len(taskIDs) == 0 {
|
||||
return taskResults, nil
|
||||
}
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("task_id IN ?", taskIDs).
|
||||
Find(&taskResults)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return taskResults, nil
|
||||
}
|
||||
|
||||
// GetPendingTasks retrieves pending tasks (submitted but not yet running/completed)
|
||||
func GetPendingTasks(ctx context.Context, tx *gorm.DB, limit int) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("status = ?", orm.AsyncTaskStatusSubmitted).
|
||||
Order("created_at ASC").
|
||||
Limit(limit).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// GetTasksByStatus retrieves tasks by status
|
||||
func GetTasksByStatus(ctx context.Context, tx *gorm.DB, status orm.AsyncTaskStatus, limit int) ([]orm.AsyncTask, error) {
|
||||
var tasks []orm.AsyncTask
|
||||
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("status = ?", status).
|
||||
Order("created_at ASC").
|
||||
Limit(limit).
|
||||
Find(&tasks)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// DeleteOldTasks deletes tasks older than the specified timestamp
|
||||
func DeleteOldTasks(ctx context.Context, tx *gorm.DB, olderThan int64) error {
|
||||
// ctx timeout judgment
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// First delete task results
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("task_id IN (SELECT task_id FROM async_task WHERE created_at < ?)", olderThan).
|
||||
Delete(&orm.AsyncTaskResult{})
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// Then delete tasks
|
||||
result = tx.WithContext(cancelCtx).
|
||||
Where("created_at < ?", olderThan).
|
||||
Delete(&orm.AsyncTask{})
|
||||
|
||||
return result.Error
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ func CreateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo netwo
|
|||
Name: componentInfo.Name,
|
||||
Context: componentInfo.Context,
|
||||
Op: componentInfo.Op,
|
||||
Ts: time.Now(),
|
||||
TS: time.Now(),
|
||||
}
|
||||
|
||||
result := tx.WithContext(cancelCtx).Create(&component)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ func CreateMeasurement(ctx context.Context, tx *gorm.DB, measurementInfo network
|
|||
BayUUID: globalUUID,
|
||||
ComponentUUID: globalUUID,
|
||||
Op: -1,
|
||||
Ts: time.Now(),
|
||||
TS: time.Now(),
|
||||
}
|
||||
|
||||
result := tx.WithContext(cancelCtx).Create(&measurement)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ func FillingLongTokenModel(ctx context.Context, tx *gorm.DB, identModel *model.L
|
|||
func ParseDataIdentifierToken(ctx context.Context, tx *gorm.DB, identToken string) (model.IndentityTokenModelInterface, error) {
|
||||
identSlice := strings.Split(identToken, ".")
|
||||
identSliceLen := len(identSlice)
|
||||
if identSliceLen == 4 {
|
||||
switch identSliceLen {
|
||||
case 4:
|
||||
// token1.token2.token3.token4.token7
|
||||
shortIndentModel := &model.ShortIdentityTokenModel{
|
||||
GridTag: identSlice[0],
|
||||
|
|
@ -67,7 +68,7 @@ func ParseDataIdentifierToken(ctx context.Context, tx *gorm.DB, identToken strin
|
|||
return nil, err
|
||||
}
|
||||
return shortIndentModel, nil
|
||||
} else if identSliceLen == 7 {
|
||||
case 7:
|
||||
// token1.token2.token3.token4.token5.token6.token7
|
||||
longIndentModel := &model.LongIdentityTokenModel{
|
||||
GridTag: identSlice[0],
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ func ParseAttrToken(ctx context.Context, tx *gorm.DB, attrToken, clientToken str
|
|||
|
||||
attrSlice := strings.Split(attrToken, ".")
|
||||
attrLen := len(attrSlice)
|
||||
if attrLen == 4 {
|
||||
switch attrLen {
|
||||
case 4:
|
||||
short := &model.ShortAttrInfo{
|
||||
AttrGroupName: attrSlice[2],
|
||||
AttrKey: attrSlice[3],
|
||||
|
|
@ -35,7 +36,7 @@ func ParseAttrToken(ctx context.Context, tx *gorm.DB, attrToken, clientToken str
|
|||
}
|
||||
short.AttrValue = attrValue
|
||||
return short, nil
|
||||
} else if attrLen == 7 {
|
||||
case 7:
|
||||
long := &model.LongAttrInfo{
|
||||
AttrGroupName: attrSlice[5],
|
||||
AttrKey: attrSlice[6],
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ package database
|
|||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"modelRT/logger"
|
||||
"modelRT/orm"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
|
@ -15,15 +15,11 @@ import (
|
|||
var (
|
||||
postgresOnce sync.Once
|
||||
_globalPostgresClient *gorm.DB
|
||||
_globalPostgresMu sync.RWMutex
|
||||
)
|
||||
|
||||
// GetPostgresDBClient returns the global PostgresDB client.It's safe for concurrent use.
|
||||
func GetPostgresDBClient() *gorm.DB {
|
||||
_globalPostgresMu.RLock()
|
||||
client := _globalPostgresClient
|
||||
_globalPostgresMu.RUnlock()
|
||||
return client
|
||||
return _globalPostgresClient
|
||||
}
|
||||
|
||||
// InitPostgresDBInstance return instance of PostgresDB client
|
||||
|
|
@ -36,11 +32,19 @@ func InitPostgresDBInstance(ctx context.Context, PostgresDBURI string) *gorm.DB
|
|||
|
||||
// initPostgresDBClient return successfully initialized PostgresDB client
|
||||
func initPostgresDBClient(ctx context.Context, PostgresDBURI string) *gorm.DB {
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
db, err := gorm.Open(postgres.Open(PostgresDBURI), &gorm.Config{Logger: logger.NewGormLogger()})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Auto migrate async task tables
|
||||
err = db.WithContext(ctx).AutoMigrate(
|
||||
&orm.AsyncTask{},
|
||||
&orm.AsyncTaskResult{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
// Package database define database operation functions
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"modelRT/logger"
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// QueryBayByUUID returns the Bay record matching bayUUID.
|
||||
func QueryBayByUUID(ctx context.Context, tx *gorm.DB, bayUUID uuid.UUID) (*orm.Bay, error) {
|
||||
var bay orm.Bay
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("bay_uuid = ?", bayUUID).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
First(&bay)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
return &bay, nil
|
||||
}
|
||||
|
||||
// QueryBaysByUUIDs returns Bay records matching the given UUIDs in a single query.
|
||||
// The returned slice preserves database order; unmatched UUIDs are silently omitted.
|
||||
func QueryBaysByUUIDs(ctx context.Context, tx *gorm.DB, bayUUIDs []uuid.UUID) ([]orm.Bay, error) {
|
||||
if len(bayUUIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var bays []orm.Bay
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Where("bay_uuid IN ?", bayUUIDs).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Find(&bays)
|
||||
|
||||
if result.Error != nil {
|
||||
logger.Error(ctx, "query bays by uuids failed", "error", result.Error)
|
||||
return nil, result.Error
|
||||
}
|
||||
return bays, nil
|
||||
}
|
||||
|
|
@ -148,6 +148,39 @@ func QueryLongIdentModelInfoByToken(ctx context.Context, tx *gorm.DB, measTag st
|
|||
return &resultComp, &meauserment, nil
|
||||
}
|
||||
|
||||
// QueryComponentsInServiceByUUIDs returns a map of global_uuid → in_service for the
|
||||
// given UUIDs. Only global_uuid and in_service columns are selected for efficiency.
|
||||
func QueryComponentsInServiceByUUIDs(ctx context.Context, tx *gorm.DB, uuids []uuid.UUID) (map[uuid.UUID]bool, error) {
|
||||
if len(uuids) == 0 {
|
||||
return make(map[uuid.UUID]bool), nil
|
||||
}
|
||||
|
||||
type row struct {
|
||||
GlobalUUID uuid.UUID `gorm:"column:global_uuid"`
|
||||
InService bool `gorm:"column:in_service"`
|
||||
}
|
||||
|
||||
var rows []row
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Model(&orm.Component{}).
|
||||
Select("global_uuid, in_service").
|
||||
Where("global_uuid IN ?", uuids).
|
||||
Scan(&rows)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
m := make(map[uuid.UUID]bool, len(rows))
|
||||
for _, r := range rows {
|
||||
m[r.GlobalUUID] = r.InService
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// QueryShortIdentModelInfoByToken define func to query short identity model info by short token
|
||||
func QueryShortIdentModelInfoByToken(ctx context.Context, tx *gorm.DB, measTag string, condition *orm.Component) (*orm.Component, *orm.Measurement, error) {
|
||||
var resultComp orm.Component
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"modelRT/orm"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ type StationWithParent struct {
|
|||
ZoneTag string `gorm:"column:zone_tag"`
|
||||
}
|
||||
|
||||
func GetFullMeasurementSet(db *gorm.DB) (*orm.MeasurementSet, error) {
|
||||
func GetFullMeasurementSet(ctx context.Context, db *gorm.DB) (*orm.MeasurementSet, error) {
|
||||
mSet := &orm.MeasurementSet{
|
||||
GridToZoneTags: make(map[string][]string),
|
||||
ZoneToStationTags: make(map[string][]string),
|
||||
|
|
@ -26,85 +30,110 @@ func GetFullMeasurementSet(db *gorm.DB) (*orm.MeasurementSet, error) {
|
|||
CompTagToMeasTags: make(map[string][]string),
|
||||
}
|
||||
|
||||
var grids []orm.Grid
|
||||
if err := db.Table("grid").Select("tagname").Scan(&grids).Error; err == nil {
|
||||
for _, g := range grids {
|
||||
if g.TAGNAME != "" {
|
||||
mSet.AllGridTags = append(mSet.AllGridTags, g.TAGNAME)
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
db = db.WithContext(gctx)
|
||||
|
||||
g.Go(func() error {
|
||||
var grids []orm.Grid
|
||||
if err := db.Table("grid").Select("tagname").Scan(&grids).Error; err != nil {
|
||||
return fmt.Errorf("query grids: %w", err)
|
||||
}
|
||||
for _, grid := range grids {
|
||||
if grid.TAGNAME != "" {
|
||||
mSet.AllGridTags = append(mSet.AllGridTags, grid.TAGNAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var zones []struct {
|
||||
orm.Zone
|
||||
GridTag string `gorm:"column:grid_tag"`
|
||||
}
|
||||
if err := db.Table("zone").
|
||||
Select("zone.*, grid.tagname as grid_tag").
|
||||
Joins("left join grid on zone.grid_id = grid.id").
|
||||
Scan(&zones).Error; err == nil {
|
||||
g.Go(func() error {
|
||||
var zones []struct {
|
||||
orm.Zone
|
||||
GridTag string `gorm:"column:grid_tag"`
|
||||
}
|
||||
if err := db.Table("zone").
|
||||
Select("zone.*, grid.tagname as grid_tag").
|
||||
Joins("left join grid on zone.grid_id = grid.id").
|
||||
Scan(&zones).Error; err != nil {
|
||||
return fmt.Errorf("query zones: %w", err)
|
||||
}
|
||||
for _, z := range zones {
|
||||
mSet.AllZoneTags = append(mSet.AllZoneTags, z.TAGNAME)
|
||||
if z.GridTag != "" {
|
||||
mSet.GridToZoneTags[z.GridTag] = append(mSet.GridToZoneTags[z.GridTag], z.TAGNAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var stations []struct {
|
||||
orm.Station
|
||||
ZoneTag string `gorm:"column:zone_tag"`
|
||||
}
|
||||
if err := db.Table("station").
|
||||
Select("station.*, zone.tagname as zone_tag").
|
||||
Joins("left join zone on station.zone_id = zone.id").
|
||||
Scan(&stations).Error; err == nil {
|
||||
g.Go(func() error {
|
||||
var stations []struct {
|
||||
orm.Station
|
||||
ZoneTag string `gorm:"column:zone_tag"`
|
||||
}
|
||||
if err := db.Table("station").
|
||||
Select("station.*, zone.tagname as zone_tag").
|
||||
Joins("left join zone on station.zone_id = zone.id").
|
||||
Scan(&stations).Error; err != nil {
|
||||
return fmt.Errorf("query stations: %w", err)
|
||||
}
|
||||
for _, s := range stations {
|
||||
mSet.AllStationTags = append(mSet.AllStationTags, s.TAGNAME)
|
||||
if s.ZoneTag != "" {
|
||||
mSet.ZoneToStationTags[s.ZoneTag] = append(mSet.ZoneToStationTags[s.ZoneTag], s.TAGNAME)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
var comps []struct {
|
||||
orm.Component
|
||||
StationTag string `gorm:"column:station_tag"`
|
||||
}
|
||||
if err := db.Table("component").
|
||||
Select("component.*, station.tagname as station_tag").
|
||||
Joins("left join station on component.station_id = station.id").
|
||||
Scan(&comps).Error; err == nil {
|
||||
g.Go(func() error {
|
||||
var comps []struct {
|
||||
orm.Component
|
||||
StationTag string `gorm:"column:station_tag"`
|
||||
}
|
||||
if err := db.Table("component").
|
||||
Select("component.*, station.tagname as station_tag").
|
||||
Joins("left join station on component.station_id = station.id").
|
||||
Scan(&comps).Error; err != nil {
|
||||
return fmt.Errorf("query components: %w", err)
|
||||
}
|
||||
for _, c := range comps {
|
||||
mSet.AllCompNSPaths = append(mSet.AllCompNSPaths, c.NSPath)
|
||||
mSet.AllCompTags = append(mSet.AllCompTags, c.Tag)
|
||||
|
||||
if c.StationTag != "" {
|
||||
mSet.StationToCompNSPaths[c.StationTag] = append(mSet.StationToCompNSPaths[c.StationTag], c.NSPath)
|
||||
}
|
||||
|
||||
if c.NSPath != "" {
|
||||
mSet.CompNSPathToCompTags[c.NSPath] = append(mSet.CompNSPathToCompTags[c.NSPath], c.Tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
mSet.AllConfigTags = append(mSet.AllConfigTags, "bay")
|
||||
|
||||
var measurements []struct {
|
||||
orm.Measurement
|
||||
CompTag string `gorm:"column:comp_tag"`
|
||||
}
|
||||
if err := db.Table("measurement").
|
||||
Select("measurement.*, component.tag as comp_tag").
|
||||
Joins("left join component on measurement.component_uuid = component.global_uuid").
|
||||
Scan(&measurements).Error; err == nil {
|
||||
g.Go(func() error {
|
||||
var measurements []struct {
|
||||
orm.Measurement
|
||||
CompTag string `gorm:"column:comp_tag"`
|
||||
}
|
||||
if err := db.Table("measurement").
|
||||
Select("measurement.*, component.tag as comp_tag").
|
||||
Joins("left join component on measurement.component_uuid = component.global_uuid").
|
||||
Scan(&measurements).Error; err != nil {
|
||||
return fmt.Errorf("query measurements: %w", err)
|
||||
}
|
||||
for _, m := range measurements {
|
||||
mSet.AllMeasTags = append(mSet.AllMeasTags, m.Tag)
|
||||
if m.CompTag != "" {
|
||||
mSet.CompTagToMeasTags[m.CompTag] = append(mSet.CompTagToMeasTags[m.CompTag], m.Tag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mSet.AllConfigTags = append(mSet.AllConfigTags, "bay")
|
||||
return mSet, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,71 +32,51 @@ func QueryTopologic(ctx context.Context, tx *gorm.DB) ([]orm.Topologic, error) {
|
|||
return topologics, nil
|
||||
}
|
||||
|
||||
// QueryTopologicFromDB return the result of query topologic info from DB
|
||||
func QueryTopologicFromDB(ctx context.Context, tx *gorm.DB) (*diagram.MultiBranchTreeNode, error) {
|
||||
// QueryTopologicByStartUUID returns all edges reachable from startUUID following
|
||||
// directed uuid_from → uuid_to edges in the topologic table.
|
||||
func QueryTopologicByStartUUID(ctx context.Context, tx *gorm.DB, startUUID uuid.UUID) ([]orm.Topologic, error) {
|
||||
var topologics []orm.Topologic
|
||||
|
||||
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
result := tx.WithContext(cancelCtx).
|
||||
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Raw(sql.RecursiveSQL, startUUID).
|
||||
Scan(&topologics)
|
||||
if result.Error != nil {
|
||||
logger.Error(ctx, "query topologic by start uuid failed", "start_uuid", startUUID, "error", result.Error)
|
||||
return nil, result.Error
|
||||
}
|
||||
return topologics, nil
|
||||
}
|
||||
|
||||
// QueryTopologicFromDB return the result of query topologic info from DB.
|
||||
// Returns the root node and a flat nodeMap for O(1) lookup by UUID.
|
||||
func QueryTopologicFromDB(ctx context.Context, tx *gorm.DB) (*diagram.MultiBranchTreeNode, map[uuid.UUID]*diagram.MultiBranchTreeNode, error) {
|
||||
topologicInfos, err := QueryTopologic(ctx, tx)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "query topologic info failed", "error", err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
tree, err := BuildMultiBranchTree(topologicInfos)
|
||||
tree, nodeMap, err := BuildMultiBranchTree(topologicInfos)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "init topologic failed", "error", err)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
return tree, nil
|
||||
return tree, nodeMap, nil
|
||||
}
|
||||
|
||||
// InitCircuitDiagramTopologic return circuit diagram topologic info from postgres
|
||||
func InitCircuitDiagramTopologic(topologicNodes []orm.Topologic) error {
|
||||
var rootVertex *diagram.MultiBranchTreeNode
|
||||
for _, node := range topologicNodes {
|
||||
if node.UUIDFrom == constants.UUIDNil {
|
||||
rootVertex = diagram.NewMultiBranchTree(node.UUIDFrom)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rootVertex == nil {
|
||||
return fmt.Errorf("root vertex is nil")
|
||||
}
|
||||
|
||||
for _, node := range topologicNodes {
|
||||
if node.UUIDFrom == constants.UUIDNil {
|
||||
nodeVertex := diagram.NewMultiBranchTree(node.UUIDTo)
|
||||
rootVertex.AddChild(nodeVertex)
|
||||
}
|
||||
}
|
||||
|
||||
node := rootVertex
|
||||
for _, nodeVertex := range node.Children {
|
||||
nextVertexs := make([]*diagram.MultiBranchTreeNode, 0)
|
||||
nextVertexs = append(nextVertexs, nodeVertex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO 电流互感器不单独划分间隔,以母线、浇筑母线、变压器为间隔原件
|
||||
func IntervalBoundaryDetermine(uuid uuid.UUID) bool {
|
||||
diagram.GetComponentMap(uuid.String())
|
||||
// TODO 判断 component 的类型是否为间隔
|
||||
// TODO 0xA1B2C3D4,高四位表示可以成为间隔的compoent类型的值为FFFF,普通 component 类型的值为 0000。低四位中前二位表示component的一级类型,例如母线 PT、母联/母分、进线等,低四位中后二位表示一级类型中包含的具体类型,例如母线 PT中包含的电压互感器、隔离开关、接地开关、避雷器、带电显示器等。
|
||||
num := uint32(0xA1B2C3D4) // 八位16进制数
|
||||
high16 := uint16(num >> 16)
|
||||
fmt.Printf("原始值: 0x%X\n", num) // 输出: 0xA1B2C3D4
|
||||
fmt.Printf("高十六位: 0x%X\n", high16) // 输出: 0xA1B2
|
||||
return true
|
||||
}
|
||||
|
||||
// BuildMultiBranchTree return the multi branch tree by topologic info and component type map
|
||||
func BuildMultiBranchTree(topologics []orm.Topologic) (*diagram.MultiBranchTreeNode, error) {
|
||||
// BuildMultiBranchTree return the multi branch tree by topologic info.
|
||||
// Returns the root node and a flat nodeMap for O(1) lookup by UUID.
|
||||
func BuildMultiBranchTree(topologics []orm.Topologic) (*diagram.MultiBranchTreeNode, map[uuid.UUID]*diagram.MultiBranchTreeNode, error) {
|
||||
nodeMap := make(map[uuid.UUID]*diagram.MultiBranchTreeNode, len(topologics)*2)
|
||||
|
||||
for _, topo := range topologics {
|
||||
if _, exists := nodeMap[topo.UUIDFrom]; !exists {
|
||||
// skip special uuid
|
||||
if topo.UUIDTo != constants.UUIDNil {
|
||||
// UUIDNil is the virtual root sentinel — skip creating a regular node for it
|
||||
if topo.UUIDFrom != constants.UUIDNil {
|
||||
nodeMap[topo.UUIDFrom] = &diagram.MultiBranchTreeNode{
|
||||
ID: topo.UUIDFrom,
|
||||
Children: make([]*diagram.MultiBranchTreeNode, 0),
|
||||
|
|
@ -105,7 +85,6 @@ func BuildMultiBranchTree(topologics []orm.Topologic) (*diagram.MultiBranchTreeN
|
|||
}
|
||||
|
||||
if _, exists := nodeMap[topo.UUIDTo]; !exists {
|
||||
// skip special uuid
|
||||
if topo.UUIDTo != constants.UUIDNil {
|
||||
nodeMap[topo.UUIDTo] = &diagram.MultiBranchTreeNode{
|
||||
ID: topo.UUIDTo,
|
||||
|
|
@ -118,10 +97,13 @@ func BuildMultiBranchTree(topologics []orm.Topologic) (*diagram.MultiBranchTreeN
|
|||
for _, topo := range topologics {
|
||||
var parent *diagram.MultiBranchTreeNode
|
||||
if topo.UUIDFrom == constants.UUIDNil {
|
||||
parent = &diagram.MultiBranchTreeNode{
|
||||
ID: constants.UUIDNil,
|
||||
if _, exists := nodeMap[constants.UUIDNil]; !exists {
|
||||
nodeMap[constants.UUIDNil] = &diagram.MultiBranchTreeNode{
|
||||
ID: constants.UUIDNil,
|
||||
Children: make([]*diagram.MultiBranchTreeNode, 0),
|
||||
}
|
||||
}
|
||||
nodeMap[constants.UUIDNil] = parent
|
||||
parent = nodeMap[constants.UUIDNil]
|
||||
} else {
|
||||
parent = nodeMap[topo.UUIDFrom]
|
||||
}
|
||||
|
|
@ -141,7 +123,7 @@ func BuildMultiBranchTree(topologics []orm.Topologic) (*diagram.MultiBranchTreeN
|
|||
// return root vertex
|
||||
root, exists := nodeMap[constants.UUIDNil]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("root node not found")
|
||||
return nil, nil, fmt.Errorf("root node not found")
|
||||
}
|
||||
return root, nil
|
||||
return root, nodeMap, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func UpdateComponentIntoDB(ctx context.Context, tx *gorm.DB, componentInfo netwo
|
|||
Name: componentInfo.Name,
|
||||
Context: componentInfo.Context,
|
||||
Op: componentInfo.Op,
|
||||
Ts: time.Now(),
|
||||
TS: time.Now(),
|
||||
}
|
||||
|
||||
result = tx.Model(&orm.Component{}).WithContext(cancelCtx).Where("GLOBAL_UUID = ?", component.GlobalUUID).Updates(&updateParams)
|
||||
|
|
|
|||
749
deploy/deploy.md
749
deploy/deploy.md
|
|
@ -1,12 +1,12 @@
|
|||
# 项目依赖服务部署指南
|
||||
# 项目服务部署指南
|
||||
|
||||
本项目依赖于 $\text{PostgreSQL}$ 数据库和 $\text{Redis Stack Server}$(包含 $\text{Redisearch}$ 等模块)部署文档将使用 $\text{Docker}$ 容器化技术部署这两个依赖服务
|
||||
本项目依赖于 `PostgreSQL` 数据库和 `Redis Stack Server`(包含 `Redisearch` 等模块)部署文档将使用 `Docker` 容器化技术部署这两个依赖服务
|
||||
|
||||
## 前提条件
|
||||
|
||||
1. 已安装 $\text{Docker}$
|
||||
1. 已安装 `Docker`
|
||||
2. 下载相关容器镜像
|
||||
3. 确保主机的 $\text{5432}$ 端口($\text{Postgres}$)和 $\text{6379}$ 端口($\text{Redis}$)未被占用
|
||||
3. 确保主机的 `5432` 端口(`Postgres`)和 `6379` 端口(`Redis`)未被占用
|
||||
|
||||
### 1\. 部署 PostgreSQL 数据库
|
||||
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#### 1.1 部署命令
|
||||
|
||||
运行以下命令启动 $\text{PostgreSQL}$ 容器
|
||||
运行以下命令启动 `PostgreSQL` 容器
|
||||
|
||||
```bash
|
||||
docker run --name postgres \
|
||||
|
|
@ -45,13 +45,75 @@ docker ps -a |grep postgres
|
|||
docker logs postgres
|
||||
```
|
||||
|
||||
#### 1.4 初始化异步任务表
|
||||
|
||||
`PostgreSQL` 启动后执行以下建表语句,创建异步任务系统所需的两张表:
|
||||
|
||||
```sql
|
||||
-- ==========================================
|
||||
-- 表: async_task
|
||||
-- 说明: 存储异步任务的生命周期跟踪信息
|
||||
-- ==========================================
|
||||
CREATE TABLE IF NOT EXISTS async_task (
|
||||
task_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
task_type VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL,
|
||||
params JSONB,
|
||||
created_at BIGINT NOT NULL,
|
||||
finished_at BIGINT,
|
||||
started_at BIGINT,
|
||||
execution_time BIGINT,
|
||||
progress INTEGER,
|
||||
retry_count INTEGER DEFAULT 0,
|
||||
max_retry_count INTEGER DEFAULT 3,
|
||||
next_retry_time BIGINT,
|
||||
retry_delay INTEGER DEFAULT 5000,
|
||||
priority INTEGER DEFAULT 5,
|
||||
queue_name VARCHAR(100) DEFAULT 'default',
|
||||
worker_id VARCHAR(50),
|
||||
failure_reason TEXT,
|
||||
stack_trace TEXT,
|
||||
created_by VARCHAR(100)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_task_type ON async_task(task_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_status ON async_task(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_created_at ON async_task(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_finished_at ON async_task(finished_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_started_at ON async_task(started_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_next_retry_time ON async_task(next_retry_time);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_priority ON async_task(priority);
|
||||
CREATE INDEX IF NOT EXISTS idx_async_task_status_retry ON async_task(status, next_retry_time)
|
||||
WHERE status = 'FAILED' AND next_retry_time IS NOT NULL;
|
||||
|
||||
-- ==========================================
|
||||
-- 表: async_task_result
|
||||
-- 说明: 存储异步任务的执行结果
|
||||
-- ==========================================
|
||||
CREATE TABLE IF NOT EXISTS async_task_result (
|
||||
task_id UUID PRIMARY KEY,
|
||||
result JSONB,
|
||||
error_code INTEGER,
|
||||
error_message TEXT,
|
||||
error_detail JSONB,
|
||||
execution_time BIGINT NOT NULL DEFAULT 0,
|
||||
memory_usage BIGINT,
|
||||
cpu_usage DOUBLE PRECISION,
|
||||
retry_count INTEGER DEFAULT 0,
|
||||
completed_at BIGINT NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE async_task IS '异步任务生命周期跟踪表';
|
||||
COMMENT ON TABLE async_task_result IS '异步任务执行结果表';
|
||||
```
|
||||
|
||||
### 2\. 部署 Redis Stack Server
|
||||
|
||||
我们将使用 `redis/redis-stack-server:latest` 镜像该镜像内置了 $\text{Redisearch}$ 模块,用于 $\text{ModelRT}$ 项目中补全功能
|
||||
我们将使用 `redis/redis-stack-server:latest` 镜像该镜像内置了 `Redisearch` 模块,用于 `ModelRT` 项目中补全功能
|
||||
|
||||
#### 2.1 部署命令
|
||||
|
||||
运行以下命令启动 $\text{Redis Stack Server}$ 容器
|
||||
运行以下命令启动 `Redis Stack Server` 容器
|
||||
|
||||
```bash
|
||||
docker run --name redis -p 6379:6379 \
|
||||
|
|
@ -68,7 +130,7 @@ docker run --name redis -p 6379:6379 \
|
|||
| **地址** | `localhost:6379` | |
|
||||
| **密码** | **无** | 默认未设置密码 |
|
||||
|
||||
> **注意:** 生产环境中建议使用 `-e REDIS_PASSWORD=<your_secure_password>` 参数来设置 $\text{Redis}$ 访问密码
|
||||
> **注意:** 生产环境中建议使用 `-e REDIS_PASSWORD=<your_secure_password>` 参数来设置 `Redis` 访问密码
|
||||
|
||||
#### 2.3 状态检查
|
||||
|
||||
|
|
@ -136,7 +198,7 @@ VALUES
|
|||
'ns1', 'tag1', 'component1', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
false,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
|
|
@ -149,7 +211,7 @@ VALUES
|
|||
'ns2', 'tag2', 'component2', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
false,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
|
|
@ -162,13 +224,78 @@ VALUES
|
|||
'ns3', 'tag3', 'component3', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station2', 2,
|
||||
-1,
|
||||
false,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
),
|
||||
(
|
||||
'70c190f2-8a60-42a9-b143-ec5f87e0aa6b',
|
||||
'ns4', 'tag4', 'component4', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
),
|
||||
(
|
||||
'10f155cf-bd27-4557-85b2-d126b6e2657f',
|
||||
'ns5', 'tag5', 'component5', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
),
|
||||
(
|
||||
'e32bc0be-67f4-4d79-a5da-eaa40a5bd77d',
|
||||
'ns6', 'tag6', 'component6', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
),
|
||||
(
|
||||
'70c190f2-8a75-42a9-b166-ec5f87e0aa6b',
|
||||
'ns7', 'tag7', 'component7', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
),
|
||||
(
|
||||
'70c200f2-8a75-42a9-c166-bf5f87e0aa6b',
|
||||
'ns8', 'tag8', 'component8', 'bus_1', '',
|
||||
'grid1', 'zone1', 'station1', 1,
|
||||
-1,
|
||||
true,
|
||||
-1, -1,
|
||||
'{}',
|
||||
'{}',
|
||||
'{}',
|
||||
-1,
|
||||
CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO public.measurement (id, tag, name, type, size, data_source, event_plan, bay_uuid, component_uuid, op, ts)
|
||||
|
|
@ -276,46 +403,46 @@ go run deploy/redis-test-data/measurments-recommend/measurement_injection.go
|
|||
|
||||
| 类别 | 参数名 | 作用描述 | 示例值 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Postgres** | `host` | PostgreSQL 数据库服务器的 $\text{IP}$ 地址或域名。 | `"192.168.1.101"` |
|
||||
| **Postgres** | `host` | PostgreSQL 数据库服务器的 `IP` 地址或域名。 | `"192.168.1.101"` |
|
||||
| | `port` | PostgreSQL 数据库服务器的端口号。 | `5432` |
|
||||
| | `database` | 连接的数据库名称。 | `"demo"` |
|
||||
| | `user` | 连接数据库所使用的用户名。 | `"postgres"` |
|
||||
| | `password` | 连接数据库所使用的密码。 | `"coslight"` |
|
||||
| **Kafka** | `servers` | Kafka 集群的 $\text{Bootstrap Server}$ 地址列表(通常是 $\text{host:port}$ 形式,多个地址用逗号分隔)。 | `"localhost:9092"` |
|
||||
| **Kafka** | `servers` | Kafka 集群的 `Bootstrap Server` 地址列表(通常是 `host:port` 形式,多个地址用逗号分隔)。 | `"localhost:9092"` |
|
||||
| | `port` | Kafka 服务器的端口号。 | `9092` |
|
||||
| | `group_id` | 消费者组 $\text{ID}$,用于标识和管理一组相关的消费者。 | `"modelRT"` |
|
||||
| | `group_id` | 消费者组 `ID`,用于标识和管理一组相关的消费者。 | `"modelRT"` |
|
||||
| | `topic` | Kafka 消息的主题名称。 | `""` |
|
||||
| | `auto_offset_reset` | 消费者首次启动或 $\text{Offset}$ 无效时,从哪个位置开始消费(如 `earliest` 或 `latest`)。 | `"earliest"` |
|
||||
| | `enable_auto_commit` | 是否自动提交 $\text{Offset}$。设为 $\text{false}$ 通常用于手动控制 $\text{Offset}$ 提交。 | `"false"` |
|
||||
| | `auto_offset_reset` | 消费者首次启动或 `Offset` 无效时,从哪个位置开始消费(如 `earliest` 或 `latest`)。 | `"earliest"` |
|
||||
| | `enable_auto_commit` | 是否自动提交 `Offset`。设为 `false` 通常用于手动控制 `Offset` 提交。 | `"false"` |
|
||||
| | `read_message_time_duration` | 读取消息时的超时或等待时间。 | `”0.5s"` |
|
||||
| **Logger (Zap)** | `mode` | 日志模式,通常为 `development`(开发)或 `production`(生产)。影响日志格式。 | `"development"` |
|
||||
| | `level` | 最低日志级别(如 $\text{debug, info, warn, error}$)。 | `"debug"` |
|
||||
| | `level` | 最低日志级别(如 `debug`, `info`, `warn`, `error`)。 | `"debug"` |
|
||||
| | `filepath` | 日志文件的输出路径和名称格式(`%s` 会被替换为日期等)。 | `"/Users/douxu/Workspace/coslight/modelRT/modelRT-%s.log"` |
|
||||
| | `maxsize` | 单个日志文件最大大小(单位:$\text{MB}$)。 | `1` |
|
||||
| | `maxsize` | 单个日志文件最大大小(单位:`MB`)。 | `1` |
|
||||
| | `maxbackups` | 保留旧日志文件的最大个数。 | `5` |
|
||||
| | `maxage` | 保留旧日志文件的最大天数。 | `30` |
|
||||
| | `compress` | 是否压缩备份的日志文件。 | `false` |
|
||||
| **Ants Pool** | `parse_concurrent_quantity` | 用于解析任务的协程池最大并发数量。 | `10` |
|
||||
| | `rtd_receive_concurrent_quantity` | 用于实时数据接收任务的协程池最大并发数量。 | `10` |
|
||||
| **Locker Redis** | `addr` | 分布式锁服务所使用的 $\text{Redis}$ 地址。 | `"127.0.0.1:6379"` |
|
||||
| | `password` | $\text{Locker Redis}$ 的密码。 | `""` |
|
||||
| | `db` | $\text{Locker Redis}$ 使用的数据库编号。 | `1` |
|
||||
| | `poolsize` | $\text{Locker Redis}$ 连接池的最大连接数。 | `50` |
|
||||
| | `timeout` | $\text{Locker Redis}$ 连接操作的超时时间(单位:毫秒)。 | `10` |
|
||||
| **Storage Redis** | `addr` | 数据存储服务所使用的 $\text{Redis}$ 地址(例如 $\text{Redisearch}$)。 | `"127.0.0.1:6379"` |
|
||||
| | `password` | $\text{Storage Redis}$ 的密码。 | `""` |
|
||||
| | `db` | $\text{Storage Redis}$ 使用的数据库编号。 | `0` |
|
||||
| | `poolsize` | $\text{Storage Redis}$ 连接池的最大连接数。 | `50` |
|
||||
| | `timeout` | $\text{Storage Redis}$ 连接操作的超时时间(单位:毫秒)。 | `10` |
|
||||
| **Base Config** | `grid_id` | 项目所操作的默认电网 $\text{ID}$。 | `1` |
|
||||
| | `zone_id` | 项目所操作的默认区域 $\text{ID}$。 | `1` |
|
||||
| | `station_id` | 项目所操作的默认变电站 $\text{ID}$。 | `1` |
|
||||
| **Locker Redis** | `addr` | 分布式锁服务所使用的 `Redis` 地址。 | `"127.0.0.1:6379"` |
|
||||
| | `password` | `Locker Redis` 的密码。 | `""` |
|
||||
| | `db` | `Locker Redis` 使用的数据库编号。 | `1` |
|
||||
| | `poolsize` | `Locker Redis` 连接池的最大连接数。 | `50` |
|
||||
| | `timeout` | `Locker Redis` 连接操作的超时时间(单位:毫秒)。 | `10` |
|
||||
| **Storage Redis** | `addr` | 数据存储服务所使用的 `Redis` 地址(例如 `Redisearch`)。 | `"127.0.0.1:6379"` |
|
||||
| | `password` | `Storage Redis` 的密码。 | `""` |
|
||||
| | `db` | `Storage Redis` 使用的数据库编号。 | `0` |
|
||||
| | `poolsize` | `Storage Redis` 连接池的最大连接数。 | `50` |
|
||||
| | `timeout` | `Storage Redis` 连接操作的超时时间(单位:毫秒)。 | `10` |
|
||||
| **Base Config** | `grid_id` | 项目所操作的默认电网 `ID`。 | `1` |
|
||||
| | `zone_id` | 项目所操作的默认区域 `ID`。 | `1` |
|
||||
| | `station_id` | 项目所操作的默认变电站 `ID`。 | `1` |
|
||||
| **Service Config** | `service_name` | 服务名称,用于日志、监控等标识。 | `"modelRT"` |
|
||||
| | `secret_key` | 服务内部使用的秘钥,用于签名或认证。 | `"modelrt_key"` |
|
||||
| **DataRT API** | `host` | 外部 $\text{DataRT}$ 服务的主机地址。 | `"http://127.0.0.1"` |
|
||||
| | `port` | $\text{DataRT}$ 服务的端口号。 | `8888` |
|
||||
| | `polling_api` | 轮询数据的 $\text{API}$ 路径。 | `"datart/getPointData"` |
|
||||
| | `polling_api_method` | 调用该 $\text{API}$ 使用的 $\text{HTTP}$ 方法。 | `"GET"` |
|
||||
| **DataRT API** | `host` | 外部 `DataRT` 服务的主机地址。 | `"http://127.0.0.1"` |
|
||||
| | `port` | `DataRT` 服务的端口号。 | `8888` |
|
||||
| | `polling_api` | 轮询数据的 `API` 路径。 | `"datart/getPointData"` |
|
||||
| | `polling_api_method` | 调用该 `API` 使用的 `HTTP` 方法。 | `"GET"` |
|
||||
|
||||
#### 3.2 编译 ModelRT 服务
|
||||
|
||||
|
|
@ -336,15 +463,561 @@ go build -o model-rt main.go
|
|||
在发现控制台输出如下信息`starting ModelRT server`
|
||||
后即代表服务启动成功
|
||||
|
||||
### 4\. 后续操作(停止与清理)
|
||||
### 4\. 部署基础依赖(Kubernetes)
|
||||
|
||||
#### 4.1 停止容器
|
||||
Redis 和 RabbitMQ 部署在 Minikube 中,YAML 文件位于 `deploy/k8s/`。RabbitMQ 启用双向 TLS(mTLS),客户端以 X.509 证书的 CN 字段作为用户名进行认证。
|
||||
|
||||
#### 4.1 部署 Redis
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/redis-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/redis-service.yaml
|
||||
```
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **镜像** | `redis/redis-stack-server:latest` | 内置 Redisearch 模块 |
|
||||
| **NodePort** | `30001` | 集群外访问端口 |
|
||||
|
||||
#### 4.2 RabbitMQ TLS 证书生成
|
||||
|
||||
RabbitMQ 配置为仅允许 TLS 连接(`listeners.tcp = none`),所有客户端须持有由同一 CA 签发的证书。
|
||||
|
||||
##### 4.2.1 生成根 CA
|
||||
|
||||
```bash
|
||||
# 克隆 tls-gen 工具
|
||||
git clone https://github.com/rabbitmq/tls-gen.git
|
||||
cd tls-gen/basic
|
||||
|
||||
# 生成根 CA(结果在 result/ 目录)
|
||||
make CN=rabbitmq-server
|
||||
# ca_certificate.pem 和 ca_key.pem 生成于 result/
|
||||
```
|
||||
|
||||
##### 4.2.2 生成服务器证书
|
||||
|
||||
服务器证书需包含 SAN(Subject Alternative Name),使其同时匹配集群内 DNS 和 Minikube IP。
|
||||
|
||||
创建 `server.cnf`:
|
||||
|
||||
```text
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = CN
|
||||
ST = Beijing
|
||||
L = Beijing
|
||||
O = coslight
|
||||
CN = rabbitmq-server
|
||||
|
||||
[v3_server]
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = rabbitmq-server
|
||||
DNS.2 = rabbitmq-service.default.svc.cluster.local
|
||||
DNS.3 = localhost
|
||||
IP.1 = 192.168.49.2
|
||||
IP.2 = 127.0.0.1
|
||||
```
|
||||
|
||||
生成证书:
|
||||
|
||||
```bash
|
||||
# 将 ca_certificate.pem 和 ca_key.pem(即 cakey.pem)放在当前目录
|
||||
openssl genrsa -out server_key.pem 2048
|
||||
|
||||
openssl req -new -key server_key.pem -out server_cert.csr -config server.cnf
|
||||
|
||||
openssl x509 -req -in server_cert.csr \
|
||||
-CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
|
||||
-out server_certificate.pem -days 730 -sha256 \
|
||||
-extfile server.cnf -extensions v3_server
|
||||
|
||||
rm server_cert.csr
|
||||
```
|
||||
|
||||
##### 4.2.3 生成 ModelRT 客户端证书
|
||||
|
||||
CN 必须与 RabbitMQ 中注册的用户名一致(`modelrt-client`)。
|
||||
|
||||
创建 `modelrt.cnf`:
|
||||
|
||||
```text
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = CN
|
||||
ST = Beijing
|
||||
L = Beijing
|
||||
O = coslight
|
||||
CN = modelrt-client
|
||||
|
||||
[v3_client]
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth
|
||||
```
|
||||
|
||||
生成证书:
|
||||
|
||||
```bash
|
||||
openssl genrsa -out modelrt_client_key.pem 2048
|
||||
|
||||
openssl req -new -key modelrt_client_key.pem \
|
||||
-out modelrt_client.csr -config modelrt.cnf
|
||||
|
||||
openssl x509 -req -in modelrt_client.csr \
|
||||
-CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
|
||||
-out modelrt_client_cert.pem -days 365 \
|
||||
-extensions v3_client -extfile modelrt.cnf
|
||||
|
||||
rm modelrt_client.csr
|
||||
```
|
||||
|
||||
##### 4.2.4 生成 EventRT 客户端证书
|
||||
|
||||
创建 `eventrt.cnf`(CN 改为 `eventrt-client`):
|
||||
|
||||
```text
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = CN
|
||||
ST = Beijing
|
||||
L = Beijing
|
||||
O = coslight
|
||||
CN = eventrt-client
|
||||
|
||||
[v3_client]
|
||||
keyUsage = critical, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth
|
||||
```
|
||||
|
||||
生成证书:
|
||||
|
||||
```bash
|
||||
openssl genrsa -out eventrt_client_key.pem 2048
|
||||
|
||||
openssl req -new -key eventrt_client_key.pem \
|
||||
-out eventrt_client.csr -config eventrt.cnf
|
||||
|
||||
openssl x509 -req -in eventrt_client.csr \
|
||||
-CA ca_certificate.pem -CAkey cakey.pem -CAcreateserial \
|
||||
-out eventrt_client_cert.pem -days 365 \
|
||||
-extensions v3_client -extfile eventrt.cnf
|
||||
|
||||
rm eventrt_client.csr
|
||||
```
|
||||
|
||||
##### 4.2.5 验证证书
|
||||
|
||||
```bash
|
||||
# 验证服务器证书
|
||||
openssl verify -CAfile ca_certificate.pem server_certificate.pem
|
||||
|
||||
# 验证客户端证书
|
||||
openssl verify -CAfile ca_certificate.pem modelrt_client_cert.pem
|
||||
openssl verify -CAfile ca_certificate.pem eventrt_client_cert.pem
|
||||
|
||||
# 查看证书详情(确认 CN 和 SAN)
|
||||
openssl x509 -in server_certificate.pem -noout -subject -ext subjectAltName
|
||||
openssl x509 -in modelrt_client_cert.pem -noout -subject
|
||||
openssl x509 -in eventrt_client_cert.pem -noout -subject
|
||||
```
|
||||
|
||||
#### 4.3 部署 RabbitMQ
|
||||
|
||||
##### 4.3.1 创建证书 Secret
|
||||
|
||||
将服务器端三个证书文件打包为 K8s Secret(在证书文件所在目录执行):
|
||||
|
||||
```bash
|
||||
kubectl create secret generic rabbitmq-certs \
|
||||
--from-file=ca_certificate.pem=./ca_certificate.pem \
|
||||
--from-file=server_certificate.pem=./server_certificate.pem \
|
||||
--from-file=server_key.pem=./server_key.pem
|
||||
```
|
||||
|
||||
##### 4.3.2 部署
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/rabbitmq-secret.yaml
|
||||
kubectl apply -f deploy/k8s/rabbitmq-config.yaml
|
||||
kubectl apply -f deploy/k8s/rabbitmq-users-config.yaml
|
||||
kubectl apply -f deploy/k8s/rabbitmq-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/rabbitmq-service.yaml
|
||||
```
|
||||
|
||||
##### 4.3.3 端口汇总
|
||||
|
||||
| 端口 | NodePort | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `5671` | `30671` | AMQP over TLS(客户端连接) |
|
||||
| `5672` | `30672` | AMQP 明文(内部备用,生产禁用) |
|
||||
| `15671` | `31671` | Management UI over TLS |
|
||||
| `15672` | `31672` | Management UI 明文(内部备用) |
|
||||
|
||||
##### 4.3.4 用户与权限说明
|
||||
|
||||
用户定义在 `rabbitmq-users-config.yaml` 的 `definitions.json` 中,通过 `load_definitions` 启动时自动加载:
|
||||
|
||||
| 用户 | 认证方式 | 权限 | 说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `coslight` | 密码 | administrator | 管理员,密码在 rabbitmq-secret.yaml |
|
||||
| `modelrt-client` | X.509 证书(CN) | configure/read/write | ModelRT 服务专用 |
|
||||
| `eventrt-client` | X.509 证书(CN) | configure/read/write | EventRT 服务专用 |
|
||||
| `web-client` | X.509 证书(CN) | read/write | Web 客户端 |
|
||||
|
||||
> **注意:** 证书认证用户的 `password_hash` 留空;RabbitMQ 通过 `ssl_cert_login_from = common_name` 将证书 CN 映射为用户名。
|
||||
|
||||
#### 4.4 部署 PostgreSQL
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/pg-configmap.yaml
|
||||
kubectl apply -f deploy/k8s/pg-pvc.yaml
|
||||
kubectl apply -f deploy/k8s/pg-statefulset.yaml
|
||||
kubectl apply -f deploy/k8s/pg-service.yaml
|
||||
```
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **镜像** | `postgres:13.16` | PostgreSQL 13.16 |
|
||||
| **NodePort** | `30432` | 集群外访问端口 |
|
||||
| **数据库** | `demo` | ConfigMap 中 `POSTGRES_DB` |
|
||||
| **用户名** | `postgres` | ConfigMap 中 `POSTGRES_USER` |
|
||||
| **密码** | `coslight` | ConfigMap `postgres-config` 中配置,生产环境迁移至 Secret |
|
||||
| **存储** | `2Gi` | PVC `postgres-data` |
|
||||
|
||||
##### 4.4.1 等待 Pod 就绪
|
||||
|
||||
```bash
|
||||
kubectl wait --for=condition=ready pod -l app=postgres --timeout=120s
|
||||
```
|
||||
|
||||
##### 4.4.2 初始化异步任务表
|
||||
|
||||
PostgreSQL 就绪后执行 1.4 节的建表 SQL,可通过以下方式进入容器执行:
|
||||
|
||||
```bash
|
||||
# 交互式 psql
|
||||
kubectl exec -it $(kubectl get pod -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
|
||||
-- psql -U postgres -d demo
|
||||
|
||||
# 或将 SQL 文件通过管道一次性执行
|
||||
kubectl exec -i $(kubectl get pod -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
|
||||
-- psql -U postgres -d demo < /path/to/init.sql
|
||||
```
|
||||
|
||||
##### 4.4.3 状态检查
|
||||
|
||||
```bash
|
||||
kubectl get pods -l app=postgres
|
||||
kubectl logs -l app=postgres --tail=30
|
||||
```
|
||||
|
||||
##### 4.4.4 清理
|
||||
|
||||
```bash
|
||||
kubectl delete -f deploy/k8s/pg-service.yaml \
|
||||
-f deploy/k8s/pg-statefulset.yaml \
|
||||
-f deploy/k8s/pg-pvc.yaml \
|
||||
-f deploy/k8s/pg-configmap.yaml
|
||||
```
|
||||
|
||||
#### 4.5 部署 MongoDB
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/mongodb-secret.yaml
|
||||
kubectl apply -f deploy/k8s/mongodb-pvc.yaml
|
||||
kubectl apply -f deploy/k8s/mongodb-statefulset.yaml
|
||||
kubectl apply -f deploy/k8s/mongodb-service.yaml
|
||||
```
|
||||
|
||||
| 参数 | 值 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **镜像** | `mongo:7.0` | MongoDB 7.0 |
|
||||
| **NodePort** | `30017` | 集群外访问端口 |
|
||||
| **用户名** | `admin` | Root 管理员 |
|
||||
| **密码** | `coslight` | Secret `mongodb-secret` 中配置,生产环境请替换强密码 |
|
||||
| **存储** | `2Gi` | PVC `mongodb-data` |
|
||||
|
||||
> **注意:** 密码存储在 `mongodb-secret.yaml` 的 `stringData` 中,生产环境应替换为强密码,并避免将明文密码提交至版本库。
|
||||
|
||||
##### 4.5.1 等待 Pod 就绪
|
||||
|
||||
```bash
|
||||
kubectl wait --for=condition=ready pod -l app=mongodb --timeout=120s
|
||||
```
|
||||
|
||||
##### 4.5.2 连接验证
|
||||
|
||||
```bash
|
||||
kubectl exec -it $(kubectl get pod -l app=mongodb -o jsonpath='{.items[0].metadata.name}') \
|
||||
-- mongosh -u admin -p coslight --authenticationDatabase admin
|
||||
```
|
||||
|
||||
##### 4.5.3 状态检查
|
||||
|
||||
```bash
|
||||
kubectl get pods -l app=mongodb
|
||||
kubectl logs -l app=mongodb --tail=30
|
||||
```
|
||||
|
||||
##### 4.5.4 清理
|
||||
|
||||
```bash
|
||||
kubectl delete -f deploy/k8s/mongodb-service.yaml \
|
||||
-f deploy/k8s/mongodb-statefulset.yaml \
|
||||
-f deploy/k8s/mongodb-pvc.yaml \
|
||||
-f deploy/k8s/mongodb-secret.yaml
|
||||
```
|
||||
|
||||
### 5\. 部署 ModelRT(Kubernetes)
|
||||
|
||||
所有资源部署在 `default` 命名空间,YAML 文件位于 `deploy/k8s/`。
|
||||
|
||||
#### 5.1 构建并推送镜像
|
||||
|
||||
```bash
|
||||
# 在项目根目录执行
|
||||
docker build -f deploy/dockerfile/modelrt.Dockerfile -t coslight/modelrt:latest .
|
||||
|
||||
# 推送到镜像仓库(或直接加载到 Minikube)
|
||||
minikube image load coslight/modelrt:latest
|
||||
```
|
||||
|
||||
#### 5.2 创建客户端证书 Secret
|
||||
|
||||
在 RabbitMQ TLS 证书生成完成后(见 4.2),进入证书文件所在目录执行:
|
||||
|
||||
```bash
|
||||
sh deploy/k8s/modelrt-certs-secret.sh
|
||||
```
|
||||
|
||||
该脚本等价于:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic modelrt-certs \
|
||||
--from-file=ca_certificate.pem=./ca_certificate.pem \
|
||||
--from-file=modelrt_client_cert.pem=./modelrt_client_cert.pem \
|
||||
--from-file=modelrt_client_key.pem=./modelrt_client_key.pem
|
||||
```
|
||||
|
||||
#### 5.3 部署
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/modelrt-secret.yaml
|
||||
kubectl apply -f deploy/k8s/modelrt-configmap.yaml
|
||||
kubectl apply -f deploy/k8s/modelrt-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/modelrt-service.yaml
|
||||
```
|
||||
|
||||
#### 5.4 配置说明
|
||||
|
||||
| 配置项 | 方式 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `postgres.password` | Secret `modelrt-secret` | 不写入 ConfigMap |
|
||||
| `service.secret_key` | Secret `modelrt-secret` | 不写入 ConfigMap |
|
||||
| RabbitMQ 客户端证书 | Secret `modelrt-certs` | 挂载至 `/app/configs/certs/` |
|
||||
| `config.yaml` 其余配置 | ConfigMap `modelrt-config` | 所有 host 已替换为 K8s service 名 |
|
||||
| `K8S_NAMESPACE` / `K8S_NODE_NAME` | Downward API | 注入至日志全局字段 |
|
||||
|
||||
> **注意:** `modelrt-configmap.yaml` 中 `postgres.password` 和 `service.secret_key` 留空,实际值由容器启动时的环境变量 `POSTGRES_PASSWORD` / `SERVICE_SECRET_KEY` 注入,应用需读取这两个环境变量覆盖 config 中的空值。若应用当前仅读取文件配置,可直接将值填入 `modelrt-secret.yaml` 并在 ConfigMap 中引用,或在 ConfigMap 中直接填写。
|
||||
|
||||
#### 5.5 状态检查
|
||||
|
||||
```bash
|
||||
# 查看 Pod 状态
|
||||
kubectl get pods -l app=modelrt
|
||||
|
||||
# 查看启动日志
|
||||
kubectl logs -l app=modelrt --tail=50
|
||||
|
||||
# 查看 Service
|
||||
kubectl get svc modelrt-service
|
||||
```
|
||||
|
||||
#### 5.6 端口汇总
|
||||
|
||||
| NodePort | 说明 |
|
||||
| :--- | :--- |
|
||||
| `30080` | ModelRT HTTP API,SSH 隧道本地端口 `8080` |
|
||||
|
||||
#### 5.7 清理
|
||||
|
||||
```bash
|
||||
kubectl delete -f deploy/k8s/modelrt-service.yaml \
|
||||
-f deploy/k8s/modelrt-deployment.yaml \
|
||||
-f deploy/k8s/modelrt-configmap.yaml \
|
||||
-f deploy/k8s/modelrt-secret.yaml
|
||||
kubectl delete secret modelrt-certs
|
||||
```
|
||||
|
||||
### 6\. 部署可观测性栈(Kubernetes)
|
||||
|
||||
在 `Kubernetes` 集群中部署 `Jaeger`(链路追踪)+ `Loki + Promtail + Grafana`(日志可视化)。所有资源部署在 `default` 命名空间,`YAML` 文件位于 `deploy/k8s/`。
|
||||
|
||||
#### 6.1 部署 Jaeger
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/jaeger-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/jaeger-service.yaml
|
||||
```
|
||||
|
||||
#### 6.2 部署 Loki
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/loki-configmap.yaml
|
||||
kubectl apply -f deploy/k8s/loki-pvc.yaml
|
||||
kubectl apply -f deploy/k8s/loki-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/loki-service.yaml
|
||||
```
|
||||
|
||||
#### 6.3 部署 Promtail
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/promtail-rbac.yaml
|
||||
kubectl apply -f deploy/k8s/promtail-configmap.yaml
|
||||
kubectl apply -f deploy/k8s/promtail-daemonset.yaml
|
||||
```
|
||||
|
||||
#### 6.4 部署 Grafana
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/grafana-configmap.yaml
|
||||
kubectl apply -f deploy/k8s/grafana-deployment.yaml
|
||||
kubectl apply -f deploy/k8s/grafana-service.yaml
|
||||
```
|
||||
|
||||
#### 6.5 一键部署
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/jaeger-deployment.yaml \
|
||||
-f deploy/k8s/jaeger-service.yaml \
|
||||
-f deploy/k8s/loki-configmap.yaml \
|
||||
-f deploy/k8s/loki-pvc.yaml \
|
||||
-f deploy/k8s/loki-deployment.yaml \
|
||||
-f deploy/k8s/loki-service.yaml \
|
||||
-f deploy/k8s/promtail-rbac.yaml \
|
||||
-f deploy/k8s/promtail-configmap.yaml \
|
||||
-f deploy/k8s/promtail-daemonset.yaml \
|
||||
-f deploy/k8s/grafana-configmap.yaml \
|
||||
-f deploy/k8s/grafana-deployment.yaml \
|
||||
-f deploy/k8s/grafana-service.yaml
|
||||
```
|
||||
|
||||
#### 6.6 状态检查
|
||||
|
||||
```bash
|
||||
# 查看所有 Pod 状态
|
||||
kubectl get pods
|
||||
|
||||
# 查看所有 Service 及 NodePort
|
||||
kubectl get svc
|
||||
```
|
||||
|
||||
#### 6.7 端口汇总
|
||||
|
||||
| 服务 | NodePort | 访问地址 | 说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Jaeger UI** | `31686` | `http://<NodeIP>:31686` | 链路追踪查询界面 |
|
||||
| **Loki** | `31100` | `http://<NodeIP>:31100` | 日志 HTTP API |
|
||||
| **Grafana** | `31000` | `http://<NodeIP>:31000` | 可视化界面,账号 `admin / coslight` |
|
||||
| **OTLP gRPC** | `31317` | `<NodeIP>:31317` | ModelRT OTel 上报地址(gRPC) |
|
||||
| **OTLP HTTP** | `31318` | `http://<NodeIP>:31318` | ModelRT OTel 上报地址(HTTP) |
|
||||
|
||||
#### 6.8 清理
|
||||
|
||||
```bash
|
||||
kubectl delete -f deploy/k8s/
|
||||
```
|
||||
|
||||
### 7\. Mac 本地访问(SSH 隧道)
|
||||
|
||||
`ModelRT / EventRT` 在 `Mac` 本地运行时,依赖的 `RabbitMQ`、`Redis`、`Jaeger`、`Loki`、`Grafana` 均部署在 `Ubuntu` 宿主机(`192.168.1.101`)上的 `Minikube`(`192.168.49.2`)中。由于 `Minikube` 网络不直接对外暴露,需通过 `SSH` 本地端口转发建立访问隧道。
|
||||
|
||||
#### 7.1 网络拓扑
|
||||
|
||||
``` text
|
||||
Mac 本地端口 ──SSH隧道──▶ Ubuntu 宿主机 (192.168.1.101) ──▶ Minikube NodePort (192.168.49.2)
|
||||
```
|
||||
|
||||
#### 7.2 建立隧道
|
||||
|
||||
```bash
|
||||
ssh -L 5432:192.168.49.2:30432 \
|
||||
-L 27017:192.168.49.2:30017 \
|
||||
-L 5671:192.168.49.2:30671 \
|
||||
-L 15671:192.168.49.2:31671 \
|
||||
-L 6379:192.168.49.2:30001 \
|
||||
-L 4318:192.168.49.2:31318 \
|
||||
-L 16686:192.168.49.2:31686 \
|
||||
-L 3100:192.168.49.2:31100 \
|
||||
-L 3000:192.168.49.2:31000 \
|
||||
douxu@192.168.1.101
|
||||
```
|
||||
|
||||
如需后台静默运行(不占用终端):
|
||||
|
||||
```bash
|
||||
ssh -fN \
|
||||
-L 5432:192.168.49.2:30432 \
|
||||
-L 27017:192.168.49.2:30017 \
|
||||
-L 5671:192.168.49.2:30671 \
|
||||
-L 15671:192.168.49.2:31671 \
|
||||
-L 6379:192.168.49.2:30001 \
|
||||
-L 4318:192.168.49.2:31318 \
|
||||
-L 16686:192.168.49.2:31686 \
|
||||
-L 3100:192.168.49.2:31100 \
|
||||
-L 3000:192.168.49.2:31000 \
|
||||
douxu@192.168.1.101
|
||||
```
|
||||
|
||||
#### 7.3 端口映射说明
|
||||
|
||||
| Mac 本地端口 | Minikube NodePort | 服务 | 说明 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `5432` | `30432` | PostgreSQL | 数据库连接 `localhost:5432` |
|
||||
| `27017` | `30017` | MongoDB | 数据库连接 `localhost:27017` |
|
||||
| `5671` | `30671` | RabbitMQ AMQP | ModelRT / EventRT 消息队列连接 |
|
||||
| `15671` | `31671` | RabbitMQ Management | RabbitMQ 管理界面 `http://localhost:15671` |
|
||||
| `6379` | `30001` | Redis | 分布式锁 / 数据存储 |
|
||||
| `4318` | `31318` | OTLP HTTP | OTel Trace 上报(Jaeger Collector) |
|
||||
| `16686` | `31686` | Jaeger UI | 链路追踪查询 `http://localhost:16686` |
|
||||
| `3100` | `31100` | Loki | 日志查询 API |
|
||||
| `3000` | `31000` | Grafana | 可视化界面 `http://localhost:3000` |
|
||||
|
||||
> **注意:** 隧道建立后,本地配置文件中所有服务地址均填 `localhost:<本地端口>`,无需修改即可在 `Mac` 上直接运行服务。
|
||||
|
||||
#### 7.4 关闭隧道
|
||||
|
||||
前台运行时直接 `Ctrl+C`;后台运行时查找并终止进程:
|
||||
|
||||
```bash
|
||||
# 找到 ssh 隧道进程
|
||||
ps aux | grep "ssh -fN"
|
||||
# 终止(替换为实际 PID)
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
### 8\. 后续操作(停止与清理)
|
||||
|
||||
#### 8.1 停止容器
|
||||
|
||||
```bash
|
||||
docker stop postgres redis
|
||||
```
|
||||
|
||||
#### 4.2 删除容器(删除后数据将丢失)
|
||||
#### 8.2 删除容器(删除后数据将丢失)
|
||||
|
||||
```bash
|
||||
docker rm postgres redis
|
||||
|
|
|
|||
|
|
@ -1,19 +1,35 @@
|
|||
FROM golang:1.24-alpine AS builder
|
||||
FROM golang:1.25-alpine AS builder
|
||||
RUN apk --no-cache upgrade
|
||||
|
||||
WORKDIR /app
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
COPY go.mod go.sum ./
|
||||
RUN GOPROXY="https://goproxy.cn,direct" go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o modelrt main.go
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
-ldflags="-s -w" \
|
||||
-trimpath \
|
||||
-mod=readonly \
|
||||
-o modelrt main.go
|
||||
|
||||
FROM alpine:latest
|
||||
WORKDIR /app
|
||||
# Prepare runtime dependencies in a pinned Alpine stage so they can be
|
||||
# copied into scratch without pulling any vulnerable OS packages at run time.
|
||||
FROM alpine:3.21 AS certs
|
||||
ARG USER_ID=1000
|
||||
RUN adduser -D -u ${USER_ID} modelrt
|
||||
RUN apk --no-cache add ca-certificates tzdata && \
|
||||
adduser -D -u ${USER_ID} modelrt
|
||||
|
||||
FROM scratch
|
||||
# CA certificates required for TLS connections (RabbitMQ amqps://)
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
# Timezone data
|
||||
COPY --from=certs /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
# Non-root user/group definitions
|
||||
COPY --from=certs /etc/passwd /etc/passwd
|
||||
COPY --from=certs /etc/group /etc/group
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/modelrt ./modelrt
|
||||
COPY configs/config.example.yaml ./configs/config.example.yaml
|
||||
RUN chown -R modelrt:modelrt /app
|
||||
RUN chmod +x /app/modelrt
|
||||
|
||||
USER modelrt
|
||||
CMD ["/app/modelrt", "-modelRT_config_dir=/app/configs"]
|
||||
CMD ["/app/modelrt", "-modelRT_config_dir=/app/configs"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: grafana-datasources
|
||||
namespace: default
|
||||
data:
|
||||
datasources.yaml: |
|
||||
apiVersion: 1
|
||||
datasources:
|
||||
- name: Loki
|
||||
type: loki
|
||||
access: proxy
|
||||
url: http://loki:3100
|
||||
isDefault: true
|
||||
jsonData:
|
||||
# derivedFields: 从日志的 traceID 字段生成跳转链接到 Jaeger
|
||||
derivedFields:
|
||||
- matcherRegex: '"traceID":\s*"([a-f0-9]+)"'
|
||||
name: TraceID
|
||||
url: http://127.0.0.1:16686/trace/$${__value.raw}
|
||||
targetBlank: true
|
||||
- name: Jaeger
|
||||
type: jaeger
|
||||
uid: jaeger
|
||||
access: proxy
|
||||
url: http://jaeger:16686
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: grafana
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: grafana
|
||||
spec:
|
||||
containers:
|
||||
- name: grafana
|
||||
image: grafana/grafana:10.4.2
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: GF_SECURITY_ADMIN_USER
|
||||
value: "coslight"
|
||||
- name: GF_SECURITY_ADMIN_PASSWORD
|
||||
value: "coslight@tj"
|
||||
- name: GF_AUTH_ANONYMOUS_ENABLED
|
||||
value: "false"
|
||||
volumeMounts:
|
||||
- name: datasources
|
||||
mountPath: /etc/grafana/provisioning/datasources
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
volumes:
|
||||
- name: datasources
|
||||
configMap:
|
||||
name: grafana-datasources
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
nodePort: 31000 # Grafana UI: http://<NodeIP>:31000
|
||||
selector:
|
||||
app: grafana
|
||||
type: NodePort
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jaeger
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jaeger
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: jaeger
|
||||
spec:
|
||||
containers:
|
||||
- name: jaeger
|
||||
image: jaegertracing/all-in-one:1.56
|
||||
env:
|
||||
- name: COLLECTOR_OTLP_ENABLED
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 16686 # UI
|
||||
- containerPort: 14268 # Jaeger Collector
|
||||
- containerPort: 4317 # OTLP gRPC
|
||||
- containerPort: 4318 # OTLP HTTP
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jaeger
|
||||
labels:
|
||||
app: jaeger
|
||||
spec:
|
||||
ports:
|
||||
- name: ui
|
||||
port: 16686
|
||||
targetPort: 16686
|
||||
nodePort: 31686 # Jaeger UI,浏览器访问 http://<NodeIP>:31686
|
||||
- name: collector-http
|
||||
port: 14268
|
||||
targetPort: 14268
|
||||
nodePort: 31268 # Jaeger 原生 HTTP collector(非 OTel)
|
||||
- name: otlp-http
|
||||
port: 4318
|
||||
targetPort: 4318
|
||||
nodePort: 31318 # OTLP HTTP,集群外使用 <NodeIP>:31318
|
||||
- name: otlp-grpc
|
||||
port: 4317
|
||||
targetPort: 4317
|
||||
nodePort: 31317 # OTLP gRPC,集群外使用 <NodeIP>:31317
|
||||
selector:
|
||||
app: jaeger
|
||||
type: NodePort
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: loki-config
|
||||
namespace: default
|
||||
data:
|
||||
loki.yaml: |
|
||||
auth_enabled: false
|
||||
|
||||
server:
|
||||
http_listen_port: 3100
|
||||
|
||||
ingester:
|
||||
wal:
|
||||
enabled: true
|
||||
dir: /loki/wal # 指向 PVC 挂载路径,避免在容器根目录创建 /wal 时 permission denied
|
||||
lifecycler:
|
||||
ring:
|
||||
kvstore:
|
||||
store: inmemory
|
||||
replication_factor: 1
|
||||
chunk_idle_period: 5m
|
||||
chunk_retain_period: 30s
|
||||
|
||||
schema_config:
|
||||
configs:
|
||||
- from: 2024-01-01
|
||||
store: boltdb-shipper
|
||||
object_store: filesystem
|
||||
schema: v11
|
||||
index:
|
||||
prefix: index_
|
||||
period: 24h
|
||||
|
||||
storage_config:
|
||||
boltdb_shipper:
|
||||
active_index_directory: /loki/index
|
||||
cache_location: /loki/cache
|
||||
shared_store: filesystem
|
||||
filesystem:
|
||||
directory: /loki/chunks
|
||||
|
||||
limits_config:
|
||||
reject_old_samples: true
|
||||
reject_old_samples_max_age: 168h
|
||||
|
||||
compactor:
|
||||
working_directory: /loki/compactor
|
||||
shared_store: filesystem
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: loki
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: loki
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: loki
|
||||
spec:
|
||||
securityContext:
|
||||
fsGroup: 10001 # 使 PVC 挂载目录对 Loki 默认用户(UID 10001)可写
|
||||
runAsUser: 10001
|
||||
runAsGroup: 10001
|
||||
containers:
|
||||
- name: loki
|
||||
image: grafana/loki:2.9.4
|
||||
args:
|
||||
- -config.file=/etc/loki/loki.yaml
|
||||
ports:
|
||||
- containerPort: 3100
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/loki
|
||||
- name: storage
|
||||
mountPath: /loki
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: loki-config
|
||||
- name: storage
|
||||
persistentVolumeClaim:
|
||||
claimName: loki-pvc
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: loki-pvc
|
||||
namespace: default
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: loki
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 3100
|
||||
targetPort: 3100
|
||||
nodePort: 31100 # 集群外访问: http://<NodeIP>:31100
|
||||
selector:
|
||||
app: loki
|
||||
type: NodePort
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
# Create the modelrt client certificate secret.
|
||||
# Run this script from the directory that contains the three cert files,
|
||||
# or adjust the paths below to point at the actual files.
|
||||
#
|
||||
# Expected files (generated during RabbitMQ TLS setup):
|
||||
# ca_certificate.pem
|
||||
# modelrt_client_cert.pem
|
||||
# modelrt_client_key.pem
|
||||
|
||||
kubectl create secret generic modelrt-certs \
|
||||
--from-file=ca_certificate.pem=./ca_certificate.pem \
|
||||
--from-file=modelrt_client_cert.pem=./modelrt_client_cert.pem \
|
||||
--from-file=modelrt_client_key.pem=./modelrt_client_key.pem
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: modelrt-config
|
||||
data:
|
||||
config.yaml: |
|
||||
postgres:
|
||||
host: "192.168.1.101"
|
||||
port: 5432
|
||||
database: "demo"
|
||||
user: "postgres"
|
||||
password: "" # injected via env POSTGRES_PASSWORD
|
||||
|
||||
rabbitmq:
|
||||
ca_cert_path: "/app/configs/certs/ca_certificate.pem"
|
||||
client_key_path: "/app/configs/certs/modelrt_client_key.pem"
|
||||
client_key_password: ""
|
||||
client_cert_path: "/app/configs/certs/modelrt_client_cert.pem"
|
||||
insecure_skip_verify: false
|
||||
server_name: "rabbitmq-server"
|
||||
user: ""
|
||||
password: ""
|
||||
host: "rabbitmq-service"
|
||||
port: 5671
|
||||
|
||||
logger:
|
||||
mode: "production"
|
||||
level: "info"
|
||||
filepath: ""
|
||||
maxsize: 100
|
||||
maxbackups: 5
|
||||
maxage: 30
|
||||
compress: false
|
||||
loki:
|
||||
endpoint: "" # Promtail handles log collection in K8s, direct push disabled
|
||||
|
||||
otel:
|
||||
endpoint: "jaeger:4318"
|
||||
insecure: true
|
||||
|
||||
ants:
|
||||
parse_concurrent_quantity: 10
|
||||
rtd_receive_concurrent_quantity: 10
|
||||
|
||||
async_task:
|
||||
worker_pool_size: 10
|
||||
queue_consumer_count: 2
|
||||
max_retry_count: 3
|
||||
retry_initial_delay: 1s
|
||||
retry_max_delay: 5m
|
||||
health_check_interval: 30s
|
||||
|
||||
locker_redis:
|
||||
addr: "redis-service:6379"
|
||||
password: ""
|
||||
db: 1
|
||||
poolsize: 50
|
||||
dial_timeout: 10
|
||||
read_timeout: 10
|
||||
write_timeout: 10
|
||||
|
||||
storage_redis:
|
||||
addr: "redis-service:6379"
|
||||
password: ""
|
||||
db: 0
|
||||
poolsize: 50
|
||||
dial_timeout: 10
|
||||
read_timeout: 10
|
||||
write_timeout: 10
|
||||
|
||||
base:
|
||||
grid_id: 1
|
||||
zone_id: 1
|
||||
station_id: 1
|
||||
|
||||
service:
|
||||
service_addr: ":8080"
|
||||
service_name: "modelRT"
|
||||
secret_key: "" # injected via env SERVICE_SECRET_KEY
|
||||
deploy_env: "production"
|
||||
|
||||
dataRT:
|
||||
host: "http://127.0.0.1"
|
||||
port: 8888
|
||||
polling_api: "datart/getPointData"
|
||||
polling_api_method: "GET"
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: modelrt
|
||||
labels:
|
||||
app: modelrt
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: modelrt
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: modelrt
|
||||
spec:
|
||||
containers:
|
||||
- name: modelrt
|
||||
image: coslight/modelrt:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- "-modelRT_config_dir=/app/configs"
|
||||
- "-modelRT_config_name=config"
|
||||
- "-modelRT_config_type=yaml"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
# Downward API — injected into every log line by logger/zap.go containerFields()
|
||||
- name: K8S_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: K8S_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
# HOSTNAME is set automatically by K8s to the pod name
|
||||
# Sensitive values injected from Secret so they stay out of ConfigMap
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: modelrt-secret
|
||||
key: postgres-password
|
||||
- name: SERVICE_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: modelrt-secret
|
||||
key: secret-key
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /app/configs/config.yaml
|
||||
subPath: config.yaml
|
||||
readOnly: true
|
||||
- name: certs
|
||||
mountPath: /app/configs/certs
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsNonRoot: true
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: modelrt-config
|
||||
- name: certs
|
||||
secret:
|
||||
secretName: modelrt-certs
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: modelrt-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
postgres-password: "coslight"
|
||||
secret-key: "modelrt_key"
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: modelrt-service
|
||||
labels:
|
||||
app: modelrt
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: modelrt
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
nodePort: 30080
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mongodb-data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mongodb-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
MONGO_INITDB_ROOT_USERNAME: admin
|
||||
MONGO_INITDB_ROOT_PASSWORD: coslight
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mongodb
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mongodb
|
||||
ports:
|
||||
- name: mongodb
|
||||
port: 27017
|
||||
targetPort: 27017
|
||||
nodePort: 30017
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: mongodb
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
serviceName: mongodb
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mongodb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mongodb
|
||||
spec:
|
||||
containers:
|
||||
- name: mongodb
|
||||
image: mongo:7.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: mongodb
|
||||
containerPort: 27017
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: mongodb-secret
|
||||
volumeMounts:
|
||||
- name: mongodb-data
|
||||
mountPath: /data/db
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- mongosh
|
||||
- --eval
|
||||
- "db.adminCommand('ping')"
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 12
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- mongosh
|
||||
- --eval
|
||||
- "db.adminCommand('ping')"
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
- name: mongodb-data
|
||||
persistentVolumeClaim:
|
||||
claimName: mongodb-data
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: postgres-config
|
||||
data:
|
||||
POSTGRES_DB: demo
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: coslight
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: postgres
|
||||
ports:
|
||||
- name: postgres
|
||||
port: 5432
|
||||
targetPort: 5432
|
||||
nodePort: 30432
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgres
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
serviceName: postgres
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:13.16
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: postgres
|
||||
containerPort: 5432
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: postgres-config
|
||||
volumeMounts:
|
||||
- name: postgres-data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
initialDelaySeconds: 8
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 12
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
- name: postgres-data
|
||||
persistentVolumeClaim:
|
||||
claimName: postgres-data
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: promtail-config
|
||||
namespace: default
|
||||
data:
|
||||
promtail.yaml: |
|
||||
server:
|
||||
http_listen_port: 9080
|
||||
grpc_listen_port: 0
|
||||
|
||||
positions:
|
||||
filename: /tmp/positions.yaml
|
||||
|
||||
clients:
|
||||
- url: http://loki:3100/loki/api/v1/push
|
||||
|
||||
scrape_configs:
|
||||
- job_name: kubernetes-pods
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
pipeline_stages:
|
||||
# 解析 zap 输出的 JSON 日志,提取结构化字段
|
||||
- json:
|
||||
expressions:
|
||||
level: level
|
||||
traceID: traceID
|
||||
spanID: spanID
|
||||
caller: caller
|
||||
pod: pod
|
||||
namespace: namespace
|
||||
node: node
|
||||
# 将关键字段提升为 Loki Label,支持在 Grafana 中按实例/Trace 过滤
|
||||
- labels:
|
||||
level:
|
||||
traceID:
|
||||
pod:
|
||||
namespace:
|
||||
node:
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
target_label: namespace
|
||||
- source_labels: [__meta_kubernetes_pod_name]
|
||||
target_label: pod
|
||||
- source_labels: [__meta_kubernetes_pod_container_name]
|
||||
target_label: container
|
||||
- source_labels: [__meta_kubernetes_pod_label_app]
|
||||
target_label: app
|
||||
# 只采集有 app label 的 Pod
|
||||
- source_labels: [__meta_kubernetes_pod_label_app]
|
||||
action: keep
|
||||
regex: .+
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: promtail
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: promtail
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: promtail
|
||||
spec:
|
||||
serviceAccountName: promtail
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
effect: NoSchedule
|
||||
containers:
|
||||
- name: promtail
|
||||
image: grafana/promtail:2.9.4
|
||||
args:
|
||||
- -config.file=/etc/promtail/promtail.yaml
|
||||
ports:
|
||||
- containerPort: 9080
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/promtail
|
||||
- name: varlog
|
||||
mountPath: /var/log
|
||||
readOnly: true
|
||||
- name: varlibdockercontainers
|
||||
mountPath: /var/lib/docker/containers
|
||||
readOnly: true
|
||||
resources:
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: promtail-config
|
||||
- name: varlog
|
||||
hostPath:
|
||||
path: /var/log
|
||||
- name: varlibdockercontainers
|
||||
hostPath:
|
||||
path: /var/lib/docker/containers
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: promtail
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: promtail
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes", "nodes/proxy", "services", "endpoints", "pods"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: promtail
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: promtail
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: promtail
|
||||
namespace: default
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: rabbitmq-config
|
||||
data:
|
||||
rabbitmq.conf: |
|
||||
# 确保允许PLAIN认证
|
||||
auth_mechanisms.1 = PLAIN
|
||||
auth_mechanisms.2 = AMQPLAIN
|
||||
auth_mechanisms.3 = EXTERNAL
|
||||
# 允许admin用户通过远程方式连接
|
||||
loopback_users.admin = false
|
||||
# 默认心跳和监听配置可在此扩展
|
||||
# 确定 ssl 连接时验证使用的用户名
|
||||
ssl_cert_login_from = common_name
|
||||
# 开启此项配置会导致只能通过TLS端口访问
|
||||
listeners.tcp = none
|
||||
listeners.ssl.default = 5671
|
||||
# default user config
|
||||
load_definitions = /etc/rabbitmq/definitions.json
|
||||
# ssl config
|
||||
ssl_options.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
|
||||
ssl_options.certfile = /etc/rabbitmq/certs/server_certificate.pem
|
||||
ssl_options.keyfile = /etc/rabbitmq/certs/server_key.pem
|
||||
ssl_options.verify = verify_peer
|
||||
ssl_options.fail_if_no_peer_cert = true
|
||||
# management config
|
||||
management.ssl.port = 15671
|
||||
management.ssl.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem
|
||||
management.ssl.certfile = /etc/rabbitmq/certs/server_certificate.pem
|
||||
management.ssl.keyfile = /etc/rabbitmq/certs/server_key.pem
|
||||
management.ssl.verify = verify_peer
|
||||
management.ssl.fail_if_no_peer_cert = true
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: eventrt-rabbitmq
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rabbitmq
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rabbitmq
|
||||
spec:
|
||||
containers:
|
||||
- name: rabbitmq
|
||||
image: rabbitmq:4.1.1-management-alpine
|
||||
ports:
|
||||
- containerPort: 4369
|
||||
- containerPort: 5671
|
||||
- containerPort: 5672 # AMQP
|
||||
- containerPort: 15671
|
||||
- containerPort: 15672 # Management UI
|
||||
- containerPort: 15691
|
||||
- containerPort: 15692
|
||||
- containerPort: 25672
|
||||
env:
|
||||
- name: RABBITMQ_DEFAULT_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: rabbitmq-secret
|
||||
key: rabbitmq-user
|
||||
- name: RABBITMQ_DEFAULT_PASS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: rabbitmq-secret
|
||||
key: rabbitmq-pass
|
||||
- name: RABBITMQ_ERLANG_COOKIE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: rabbitmq-secret
|
||||
key: erlang-cookie
|
||||
- name: RABBITMQ_DEFAULT_VHOST
|
||||
value: "/"
|
||||
volumeMounts:
|
||||
- name: rabbitmq-certs-volume
|
||||
mountPath: /etc/rabbitmq/certs
|
||||
readOnly: true
|
||||
- name: rabbitmq-config-volume
|
||||
mountPath: /etc/rabbitmq/rabbitmq.conf
|
||||
subPath: rabbitmq.conf
|
||||
- name: rabbitmq-config-volume
|
||||
mountPath: /etc/rabbitmq/advanced.config
|
||||
subPath: advanced.config
|
||||
readOnly: true
|
||||
- name: plugins-config-volume
|
||||
mountPath: /etc/rabbitmq/enabled_plugins
|
||||
subPath: enabled_plugins
|
||||
- name: users-config-volume
|
||||
mountPath: /etc/rabbitmq/definitions.json
|
||||
subPath: definitions.json
|
||||
- name: rabbitmq-data
|
||||
mountPath: /var/lib/rabbitmq
|
||||
volumes:
|
||||
- name: rabbitmq-certs-volume
|
||||
secret:
|
||||
secretName: rabbitmq-certs
|
||||
- name: rabbitmq-config-volume
|
||||
configMap:
|
||||
name: rabbitmq-config
|
||||
- name: rabbitmq-advanced-config-volume
|
||||
configMap:
|
||||
name: rabbitmq-config
|
||||
- name: plugins-config-volume
|
||||
configMap:
|
||||
name: rabbit-plugins-conf
|
||||
- name: users-config-volume
|
||||
configMap:
|
||||
name: rabbitmq-users-definitions
|
||||
- name: rabbitmq-data
|
||||
emptyDir: {}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: rabbitmq-secret
|
||||
type: Opaque
|
||||
stringData:
|
||||
rabbitmq-user: "coslight"
|
||||
rabbitmq-pass: "coslight@tj"
|
||||
erlang-cookie: "secret-erlang-cookie"
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: rabbitmq-service
|
||||
spec:
|
||||
type: NodePort # 在 Minikube 中使用 NodePort 方便外部访问
|
||||
selector:
|
||||
app: rabbitmq
|
||||
ports:
|
||||
- name: amqp-ssl
|
||||
protocol: TCP
|
||||
port: 5671
|
||||
targetPort: 5671
|
||||
nodePort: 30671
|
||||
- name: amqp
|
||||
protocol: TCP
|
||||
port: 5672
|
||||
targetPort: 5672
|
||||
nodePort: 30672
|
||||
- name: management-ssl
|
||||
protocol: TCP
|
||||
port: 15671
|
||||
targetPort: 15671
|
||||
nodePort: 31671
|
||||
- name: management
|
||||
protocol: TCP
|
||||
port: 15672
|
||||
targetPort: 15672
|
||||
nodePort: 31672
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: rabbitmq-users-definitions
|
||||
data:
|
||||
definitions.json: |
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"name": "coslight",
|
||||
"password_hash": "Gl2XVEJwPwDZQF8ZhsYnvm83wMkdftY3/raxyntdZueyx/Uv",
|
||||
"hashing_algorithm": "rabbit_password_hashing_sha256",
|
||||
"tags": ["administrator"]
|
||||
},
|
||||
{
|
||||
"name": "web-client",
|
||||
"password_hash": "",
|
||||
"hashing_algorithm": "rabbit_password_hashing_sha256",
|
||||
"tags": ["management"]
|
||||
},
|
||||
{
|
||||
"name": "modelrt-client",
|
||||
"password_hash": "",
|
||||
"hashing_algorithm": "rabbit_password_hashing_sha256",
|
||||
"tags": ["management"]
|
||||
},
|
||||
{
|
||||
"name": "eventrt-client",
|
||||
"password_hash": "",
|
||||
"hashing_algorithm": "rabbit_password_hashing_sha256",
|
||||
"tags": ["management"]
|
||||
}
|
||||
],
|
||||
"vhosts": [ { "name": "/" } ],
|
||||
"permissions": [
|
||||
{
|
||||
"user": "coslight",
|
||||
"vhost": "/",
|
||||
"configure": ".*",
|
||||
"write": ".*",
|
||||
"read": ".*"
|
||||
},
|
||||
{
|
||||
"user": "web-client",
|
||||
"vhost": "/",
|
||||
"configure": "^$",
|
||||
"write": ".*",
|
||||
"read": ".*"
|
||||
},
|
||||
{
|
||||
"user": "modelrt-client",
|
||||
"vhost": "/",
|
||||
"configure": ".*",
|
||||
"write": ".*",
|
||||
"read": ".*"
|
||||
},
|
||||
{
|
||||
"user": "eventrt-client",
|
||||
"vhost": "/",
|
||||
"configure": ".*",
|
||||
"write": ".*",
|
||||
"read": ".*"
|
||||
}
|
||||
],
|
||||
"topic_permissions": [],
|
||||
"parameters": [],
|
||||
"global_parameters": [
|
||||
{
|
||||
"name": "cluster_name",
|
||||
"value": "evnetrt-rabbitmq-cluster"
|
||||
}
|
||||
],
|
||||
"policies": [],
|
||||
"queues": [],
|
||||
"exchanges": [],
|
||||
"bindings": []
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis/redis-stack-server:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-service
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: redis
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
nodePort: 30001
|
||||
|
||||
|
|
@ -129,9 +129,9 @@ func generateOutlierSegments(totalSize, minLength, maxLength, count int, distrib
|
|||
segments := make([]OutlierSegment, 0, count)
|
||||
usedPositions := make(map[int]bool)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
for range count {
|
||||
// 尝试多次寻找合适的位置
|
||||
for attempt := 0; attempt < 10; attempt++ {
|
||||
for range 10 {
|
||||
length := rand.Intn(maxLength-minLength+1) + minLength
|
||||
start := rand.Intn(totalSize - length)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package diagram provide diagram data structure and operation
|
||||
package diagram
|
||||
|
||||
import (
|
||||
|
|
@ -31,11 +32,9 @@ func UpdateAnchorValue(componentUUID string, anchorValue string) bool {
|
|||
// StoreAnchorValue define func of store anchor value with componentUUID and anchor name
|
||||
func StoreAnchorValue(componentUUID string, anchorValue string) {
|
||||
anchorValueOverview.Store(componentUUID, anchorValue)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteAnchorValue define func of delete anchor value with componentUUID
|
||||
func DeleteAnchorValue(componentUUID string) {
|
||||
anchorValueOverview.Delete(componentUUID)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package diagram provide diagram data structure and operation
|
||||
package diagram
|
||||
|
||||
import (
|
||||
|
|
@ -33,11 +34,9 @@ func UpdateComponentMap(componentID int64, componentInfo *orm.Component) bool {
|
|||
// StoreComponentMap define func of store circuit diagram data with component uuid and component info
|
||||
func StoreComponentMap(componentUUID string, componentInfo *orm.Component) {
|
||||
diagramsOverview.Store(componentUUID, componentInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteComponentMap define func of delete circuit diagram data with component uuid
|
||||
func DeleteComponentMap(componentUUID string) {
|
||||
diagramsOverview.Delete(componentUUID)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ func (g *Graph) DelEdge(from, to uuid.UUID) error {
|
|||
return fmt.Errorf("delete edge failed: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("fromKeys:", fromKeys)
|
||||
for _, fromUUID := range fromKeys {
|
||||
fromKey := fromUUID.String()
|
||||
var delIndex int
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package diagram provide diagram data structure and operation
|
||||
package diagram
|
||||
|
||||
import (
|
||||
|
|
@ -17,7 +18,7 @@ func TestHMSet(t *testing.T) {
|
|||
PoolSize: 50,
|
||||
DialTimeout: 10 * time.Second,
|
||||
})
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"field1": "Hello1",
|
||||
"field2": "World1",
|
||||
"field3": 11,
|
||||
|
|
@ -29,5 +30,4 @@ func TestHMSet(t *testing.T) {
|
|||
fmt.Printf("err:%v\n", err)
|
||||
}
|
||||
fmt.Printf("res:%v\n", res)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package diagram provide diagram data structure and operation
|
||||
package diagram
|
||||
|
||||
import (
|
||||
|
|
@ -52,7 +53,7 @@ func (n *MultiBranchTreeNode) FindNodeByID(id uuid.UUID) *MultiBranchTreeNode {
|
|||
}
|
||||
|
||||
func (n *MultiBranchTreeNode) PrintTree(level int) {
|
||||
for i := 0; i < level; i++ {
|
||||
for range level {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
|
||||
|
|
@ -62,3 +63,63 @@ func (n *MultiBranchTreeNode) PrintTree(level int) {
|
|||
child.PrintTree(level + 1)
|
||||
}
|
||||
}
|
||||
|
||||
// FindPath returns the ordered node sequence from startID to endID using the
|
||||
// supplied nodeMap for O(1) lookup. It walks each node up to the root to find
|
||||
// the LCA, then stitches the two half-paths together.
|
||||
// Returns nil when either node is absent from nodeMap or no path exists.
|
||||
func FindPath(startID, endID uuid.UUID, nodeMap map[uuid.UUID]*MultiBranchTreeNode) []*MultiBranchTreeNode {
|
||||
startNode, ok := nodeMap[startID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
endNode, ok := nodeMap[endID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// collect ancestors (inclusive) from a node up to the root sentinel
|
||||
ancestors := func(n *MultiBranchTreeNode) []*MultiBranchTreeNode {
|
||||
var chain []*MultiBranchTreeNode
|
||||
for n != nil {
|
||||
chain = append(chain, n)
|
||||
n = n.Parent
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
startChain := ancestors(startNode) // [start, ..., root]
|
||||
endChain := ancestors(endNode) // [end, ..., root]
|
||||
|
||||
// index startChain by ID for fast LCA detection
|
||||
startIdx := make(map[uuid.UUID]int, len(startChain))
|
||||
for i, node := range startChain {
|
||||
startIdx[node.ID] = i
|
||||
}
|
||||
|
||||
// find LCA: first node in endChain that also appears in startChain
|
||||
lcaEndPos := -1
|
||||
lcaStartPos := -1
|
||||
for i, node := range endChain {
|
||||
if j, found := startIdx[node.ID]; found {
|
||||
lcaEndPos = i
|
||||
lcaStartPos = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if lcaEndPos < 0 {
|
||||
return nil // disconnected
|
||||
}
|
||||
|
||||
// path = startChain[0..lcaStartPos] reversed + endChain[lcaEndPos..0] reversed
|
||||
path := make([]*MultiBranchTreeNode, 0, lcaStartPos+lcaEndPos+1)
|
||||
for i := 0; i <= lcaStartPos; i++ {
|
||||
path = append(path, startChain[i])
|
||||
}
|
||||
// append end-side (skip LCA to avoid duplication), reversed
|
||||
for i := lcaEndPos - 1; i >= 0; i-- {
|
||||
path = append(path, endChain[i])
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ func NewRedisClient() *RedisClient {
|
|||
}
|
||||
}
|
||||
|
||||
// QueryByZRangeByLex define func to query real time data from redis zset
|
||||
func (rc *RedisClient) QueryByZRangeByLex(ctx context.Context, key string, size int64) ([]redis.Z, error) {
|
||||
// QueryByZRange define func to query real time data from redis zset
|
||||
func (rc *RedisClient) QueryByZRange(ctx context.Context, key string, size int64) ([]redis.Z, error) {
|
||||
client := rc.Client
|
||||
args := redis.ZRangeArgs{
|
||||
Key: key,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func NewRedisHash(ctx context.Context, hashKey string, lockLeaseTime uint64, nee
|
|||
}
|
||||
|
||||
// SetRedisHashByMap define func of set redis hash by map struct
|
||||
func (rh *RedisHash) SetRedisHashByMap(fields map[string]interface{}) error {
|
||||
func (rh *RedisHash) SetRedisHashByMap(fields map[string]any) error {
|
||||
err := rh.rwLocker.WLock(rh.ctx)
|
||||
if err != nil {
|
||||
logger.Error(rh.ctx, "lock wLock by hash_key failed", "hash_key", rh.hashKey, "error", err)
|
||||
|
|
@ -46,7 +46,7 @@ func (rh *RedisHash) SetRedisHashByMap(fields map[string]interface{}) error {
|
|||
}
|
||||
|
||||
// SetRedisHashByKV define func of set redis hash by kv struct
|
||||
func (rh *RedisHash) SetRedisHashByKV(field string, value interface{}) error {
|
||||
func (rh *RedisHash) SetRedisHashByKV(field string, value any) error {
|
||||
err := rh.rwLocker.WLock(rh.ctx)
|
||||
if err != nil {
|
||||
logger.Error(rh.ctx, "lock wLock by hash_key failed", "hash_key", rh.hashKey, "error", err)
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@ var (
|
|||
)
|
||||
|
||||
// initClient define func of return successfully initialized redis client
|
||||
func initClient(rCfg config.RedisConfig) *redis.Client {
|
||||
func initClient(rCfg config.RedisConfig, deployEnv string) *redis.Client {
|
||||
client, err := util.NewRedisClient(
|
||||
rCfg.Addr,
|
||||
util.WithPassword(rCfg.Password),
|
||||
util.WithPassword(rCfg.Password, deployEnv),
|
||||
util.WithDB(rCfg.DB),
|
||||
util.WithPoolSize(rCfg.PoolSize),
|
||||
util.WithTimeout(time.Duration(rCfg.Timeout)*time.Second),
|
||||
util.WithConnectTimeout(time.Duration(rCfg.DialTimeout)*time.Second),
|
||||
util.WithReadTimeout(time.Duration(rCfg.ReadTimeout)*time.Second),
|
||||
util.WithWriteTimeout(time.Duration(rCfg.WriteTimeout)*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -31,9 +33,9 @@ func initClient(rCfg config.RedisConfig) *redis.Client {
|
|||
}
|
||||
|
||||
// InitRedisClientInstance define func of return instance of redis client
|
||||
func InitRedisClientInstance(rCfg config.RedisConfig) *redis.Client {
|
||||
func InitRedisClientInstance(rCfg config.RedisConfig, deployEnv string) *redis.Client {
|
||||
once.Do(func() {
|
||||
_globalStorageClient = initClient(rCfg)
|
||||
_globalStorageClient = initClient(rCfg, deployEnv)
|
||||
})
|
||||
return _globalStorageClient
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func (rs *RedisString) Get(stringKey string) (string, error) {
|
|||
}
|
||||
|
||||
// Set define func of set the value of key
|
||||
func (rs *RedisString) Set(stringKey string, value interface{}) error {
|
||||
func (rs *RedisString) Set(stringKey string, value any) error {
|
||||
err := rs.rwLocker.WLock(rs.ctx)
|
||||
if err != nil {
|
||||
logger.Error(rs.ctx, "lock wLock by stringKey failed", "string_key", stringKey, "error", err)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func NewRedisZSet(ctx context.Context, key string, lockLeaseTime uint64, needRef
|
|||
}
|
||||
|
||||
// ZADD define func of add redis zset by members
|
||||
func (rs *RedisZSet) ZADD(setKey string, score float64, member interface{}) error {
|
||||
func (rs *RedisZSet) ZADD(setKey string, score float64, member any) error {
|
||||
err := rs.rwLocker.WLock(rs.ctx)
|
||||
if err != nil {
|
||||
logger.Error(rs.ctx, "lock wLock by setKey failed", "set_key", setKey, "error", err)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package diagram provide diagram data structure and operation
|
||||
package diagram
|
||||
|
||||
import (
|
||||
|
|
@ -11,7 +12,7 @@ var graphOverview sync.Map
|
|||
|
||||
// PrintGrapMap define func of print circuit diagram topologic info data
|
||||
func PrintGrapMap() {
|
||||
graphOverview.Range(func(key, value interface{}) bool {
|
||||
graphOverview.Range(func(key, value any) bool {
|
||||
fmt.Println(key, value)
|
||||
return true
|
||||
})
|
||||
|
|
@ -39,11 +40,9 @@ func UpdateGrapMap(pageID int64, graphInfo *Graph) bool {
|
|||
// StoreGraphMap define func of store circuit diagram topologic data with pageID and topologic info
|
||||
func StoreGraphMap(pageID int64, graphInfo *Graph) {
|
||||
graphOverview.Store(pageID, graphInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteGraphMap define func of delete circuit diagram topologic data with pageID
|
||||
func DeleteGraphMap(pageID int64) {
|
||||
graphOverview.Delete(pageID)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,13 +16,15 @@ var (
|
|||
)
|
||||
|
||||
// initClient define func of return successfully initialized redis client
|
||||
func initClient(rCfg config.RedisConfig) *redis.Client {
|
||||
func initClient(rCfg config.RedisConfig, deployEnv string) *redis.Client {
|
||||
client, err := util.NewRedisClient(
|
||||
rCfg.Addr,
|
||||
util.WithPassword(rCfg.Password),
|
||||
util.WithPassword(rCfg.Password, deployEnv),
|
||||
util.WithDB(rCfg.DB),
|
||||
util.WithPoolSize(rCfg.PoolSize),
|
||||
util.WithTimeout(time.Duration(rCfg.Timeout)*time.Second),
|
||||
util.WithConnectTimeout(time.Duration(rCfg.DialTimeout)*time.Second),
|
||||
util.WithReadTimeout(time.Duration(rCfg.ReadTimeout)*time.Second),
|
||||
util.WithWriteTimeout(time.Duration(rCfg.WriteTimeout)*time.Second),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -31,9 +33,9 @@ func initClient(rCfg config.RedisConfig) *redis.Client {
|
|||
}
|
||||
|
||||
// InitClientInstance define func of return instance of redis client
|
||||
func InitClientInstance(rCfg config.RedisConfig) *redis.Client {
|
||||
func InitClientInstance(rCfg config.RedisConfig, deployEnv string) *redis.Client {
|
||||
once.Do(func() {
|
||||
_globalLockerClient = initClient(rCfg)
|
||||
_globalLockerClient = initClient(rCfg, deployEnv)
|
||||
})
|
||||
return _globalLockerClient
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,539 @@
|
|||
# ModelRT 异步任务 API 文档
|
||||
|
||||
## 1. 概述
|
||||
|
||||
ModelRT 异步任务系统基于 RabbitMQ 消息驱动,提供完整的任务生命周期管理。任务创建后进入队列,由后台 Worker 消费并执行,调用方可通过轮询接口获取进度和结果。
|
||||
|
||||
**Base URL**: `http://{host}:{port}`(默认 `localhost:8080`)
|
||||
|
||||
**鉴权**: 公开接口需在 Header 中携带 `X-Service-Token`(由服务端启动时生成)。
|
||||
|
||||
---
|
||||
|
||||
## 2. API 端点总览
|
||||
|
||||
| 方法 | 路径 | 描述 | 权限 |
|
||||
| :----- | :--------------------------------- | :--------------- | :----- |
|
||||
| POST | `/task/async` | 创建异步任务 | 公开 |
|
||||
| GET | `/task/async/results` | 批量查询任务结果 | 公开 |
|
||||
| GET | `/task/async/{task_id}` | 查询单个任务详情 | 公开 |
|
||||
| POST | `/task/async/{task_id}/cancel` | 取消异步任务 | 公开 |
|
||||
| POST | `/task/internal/async/progress` | 更新任务进度 | 内部 |
|
||||
| POST | `/task/internal/async/status` | 更新任务状态 | 内部 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 通用响应结构
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "success message",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
### 失败响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 4001,
|
||||
"msg": "error description",
|
||||
"payload": null
|
||||
}
|
||||
```
|
||||
|
||||
### 响应码说明
|
||||
|
||||
| code | 含义 |
|
||||
| :--- | :--------------------- |
|
||||
| 2000 | 成功 |
|
||||
| 3000 | 处理失败 |
|
||||
| 4001 | 请求参数无效 |
|
||||
| 4002 | 未授权(Token 无效) |
|
||||
| 5000 | 服务器内部错误 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 详细接口说明
|
||||
|
||||
### 4.1 创建异步任务
|
||||
|
||||
**POST** `/task/async`
|
||||
|
||||
创建新的异步任务,任务进入队列等待 Worker 消费。返回 `task_id` 用于后续查询。
|
||||
|
||||
**请求头**
|
||||
|
||||
```
|
||||
Content-Type: application/json
|
||||
X-Service-Token: <token>
|
||||
```
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"task_type": "TOPOLOGY_ANALYSIS",
|
||||
"params": { }
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :---------- | :----- | :--- | :-------------------------------------------------------------------------------- |
|
||||
| `task_type` | string | 是 | 任务类型,枚举值见 §5.1 |
|
||||
| `params` | object | 是 | 任务参数,不同任务类型的参数结构见 §5.3 |
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "task created successfully",
|
||||
"payload": {
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**
|
||||
|
||||
| 场景 | code | msg |
|
||||
| :----------------- | :--- | :-------------------------- |
|
||||
| 参数格式错误 | 4001 | invalid request parameters |
|
||||
| task_type 不合法 | 4001 | invalid task type |
|
||||
| params 内容不合法 | 4001 | invalid task parameters |
|
||||
| 数据库连接失败 | 5000 | database connection error |
|
||||
| 任务写库失败 | 5000 | failed to create task |
|
||||
|
||||
**curl 示例**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8080/task/async" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Service-Token: <token>" \
|
||||
-d '{
|
||||
"task_type": "TOPOLOGY_ANALYSIS",
|
||||
"params": {
|
||||
"start_component_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"end_component_uuid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"check_in_service": true
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 批量查询任务结果
|
||||
|
||||
**GET** `/task/async/results`
|
||||
|
||||
根据一组任务 ID 批量查询状态和结果,适用于轮询多个任务。
|
||||
|
||||
**Query 参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| :--------- | :----- | :--- | :----------------------------------- |
|
||||
| `task_ids` | string | 是 | 逗号分隔的 UUID 列表,最少 1 个 |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```
|
||||
GET /task/async/results?task_ids=123e4567-e89b-12d3-a456-426614174000,223e4567-e89b-12d3-a456-426614174001
|
||||
```
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "query completed",
|
||||
"payload": {
|
||||
"total": 2,
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"task_type": "TOPOLOGY_ANALYSIS",
|
||||
"status": "RUNNING",
|
||||
"progress": 50,
|
||||
"created_at": 1741846200
|
||||
},
|
||||
{
|
||||
"task_id": "223e4567-e89b-12d3-a456-426614174001",
|
||||
"task_type": "PERFORMANCE_ANALYSIS",
|
||||
"status": "COMPLETED",
|
||||
"progress": 100,
|
||||
"created_at": 1741846200,
|
||||
"finished_at": 1741846260,
|
||||
"result": {
|
||||
"components_analyzed": 5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**
|
||||
|
||||
| 场景 | code | msg |
|
||||
| :----------------- | :--- | :-------------------------- |
|
||||
| 缺少 task_ids | 4001 | task_ids parameter is required |
|
||||
| UUID 格式不合法 | 4001 | invalid task ID format |
|
||||
| 数据库连接失败 | 5000 | database connection error |
|
||||
| 查询失败 | 5000 | failed to query tasks |
|
||||
|
||||
**curl 示例**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:8080/task/async/results?task_ids=123e4567-e89b-12d3-a456-426614174000"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 查询单个任务详情
|
||||
|
||||
**GET** `/task/async/{task_id}`
|
||||
|
||||
查询单个任务的完整信息,包含结果或错误详情。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| :-------- | :----- | :--- | :-------------- |
|
||||
| `task_id` | string | 是 | 任务 UUID |
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "query completed",
|
||||
"payload": {
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"task_type": "TOPOLOGY_ANALYSIS",
|
||||
"status": "COMPLETED",
|
||||
"progress": 100,
|
||||
"created_at": 1741846200,
|
||||
"finished_at": 1741846260,
|
||||
"result": {
|
||||
"path_exists": true,
|
||||
"path_length": 3,
|
||||
"path_nodes": ["comp-001", "comp-005", "comp-999"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
任务失败时 payload 附带错误信息:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "query completed",
|
||||
"payload": {
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"task_type": "TOPOLOGY_ANALYSIS",
|
||||
"status": "FAILED",
|
||||
"created_at": 1741846200,
|
||||
"finished_at": 1741846210,
|
||||
"error_code": 400102,
|
||||
"error_message": "Component UUID not found",
|
||||
"error_detail": {
|
||||
"component_uuid": "550e8400-0000-0000-0000-000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**
|
||||
|
||||
| 场景 | code | msg |
|
||||
| :----------------- | :--- | :-------------------------- |
|
||||
| 缺少 task_id | 4001 | task_id parameter is required |
|
||||
| UUID 格式不合法 | 4001 | invalid task ID format |
|
||||
| 任务不存在 | 404 | task not found |
|
||||
| 数据库连接失败 | 5000 | database connection error |
|
||||
|
||||
**curl 示例**
|
||||
|
||||
```bash
|
||||
curl "http://localhost:8080/task/async/123e4567-e89b-12d3-a456-426614174000"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 取消异步任务
|
||||
|
||||
**POST** `/task/async/{task_id}/cancel`
|
||||
|
||||
取消指定任务。**仅 `SUBMITTED`(排队中)状态的任务可以被取消**,已开始执行(`RUNNING`)或已结束的任务无法取消。
|
||||
|
||||
取消后任务状态变为 `FAILED`,错误码 `40003`,错误信息 `task cancelled by user`。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| :-------- | :----- | :--- | :-------- |
|
||||
| `task_id` | string | 是 | 任务 UUID |
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "task cancelled successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**失败响应**
|
||||
|
||||
| 场景 | code | msg |
|
||||
| :----------------------- | :--- | :--------------------------------------------------------- |
|
||||
| 缺少 task_id | 400 | task_id parameter is required |
|
||||
| UUID 格式不合法 | 400 | invalid task ID format |
|
||||
| 任务不存在 | 404 | task not found |
|
||||
| 任务已执行或已完成 | 400 | task cannot be cancelled (already running or completed) |
|
||||
| 数据库连接失败 | 500 | database connection error |
|
||||
| 取消操作失败 | 500 | failed to cancel task |
|
||||
|
||||
**curl 示例**
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8080/task/async/123e4567-e89b-12d3-a456-426614174000/cancel"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 内部接口:更新任务进度
|
||||
|
||||
**POST** `/task/internal/async/progress`
|
||||
|
||||
由 Worker 内部调用,更新任务执行进度(0-100)。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"progress": 75
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :--------- | :----- | :--- | :---------------- |
|
||||
| `task_id` | string | 是 | 任务 UUID |
|
||||
| `progress` | int | 是 | 进度值,范围 0-100 |
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "task progress updated successfully",
|
||||
"payload": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 内部接口:更新任务状态
|
||||
|
||||
**POST** `/task/internal/async/status`
|
||||
|
||||
由 Worker 内部调用,更新任务状态。当状态更新为 `COMPLETED` 或 `FAILED` 时,同步写入 `finished_at` 时间戳。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"status": "RUNNING",
|
||||
"timestamp": 1741846205
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :---------- | :----- | :--- | :--------------------------------------- |
|
||||
| `task_id` | string | 是 | 任务 UUID |
|
||||
| `status` | string | 是 | 目标状态,枚举值见 §5.2 |
|
||||
| `timestamp` | int64 | 是 | 状态变更时间戳(Unix 秒) |
|
||||
|
||||
**成功响应** `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 2000,
|
||||
"msg": "task status updated successfully",
|
||||
"payload": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据结构参考
|
||||
|
||||
### 5.1 任务类型(task_type)
|
||||
|
||||
| 枚举值 | 描述 |
|
||||
| :--------------------- | :----------- |
|
||||
| `TOPOLOGY_ANALYSIS` | 拓扑连通性分析 |
|
||||
| `PERFORMANCE_ANALYSIS` | 性能分析 |
|
||||
| `EVENT_ANALYSIS` | 事件分析 |
|
||||
| `BATCH_IMPORT` | 批量数据导入 |
|
||||
| `TEST` | 测试任务(系统验证用) |
|
||||
|
||||
### 5.2 任务状态(status)
|
||||
|
||||
| 枚举值 | 描述 | 可转换至 |
|
||||
| :---------- | :----------------- | :------------------------------ |
|
||||
| `SUBMITTED` | 已提交至队列 | `RUNNING`, `FAILED`(取消) |
|
||||
| `RUNNING` | 正在执行 | `COMPLETED`, `FAILED` |
|
||||
| `COMPLETED` | 执行成功 | — |
|
||||
| `FAILED` | 执行失败或被取消 | — |
|
||||
|
||||
### 5.3 各任务类型的 params 结构
|
||||
|
||||
#### TOPOLOGY_ANALYSIS — 拓扑连通性分析
|
||||
|
||||
分析两个元件之间是否存在连通路径。
|
||||
|
||||
```json
|
||||
{
|
||||
"start_component_uuid": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"end_component_uuid": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"check_in_service": true
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :--------------------- | :------ | :--- | :------------------------------------- |
|
||||
| `start_component_uuid` | string | 是 | 起始元件 UUID |
|
||||
| `end_component_uuid` | string | 是 | 目标元件 UUID |
|
||||
| `check_in_service` | boolean | 否 | 是否只检查投运状态元件,默认 `true` |
|
||||
|
||||
#### PERFORMANCE_ANALYSIS — 性能分析
|
||||
|
||||
```json
|
||||
{
|
||||
"component_ids": ["comp-001", "comp-002"],
|
||||
"time_range": {
|
||||
"start": "2026-03-01T00:00:00Z",
|
||||
"end": "2026-03-02T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :--------------- | :------- | :--- | :------------------ |
|
||||
| `component_ids` | []string | 是 | 待分析的元件 ID 列表(至少 1 个) |
|
||||
| `time_range` | object | 否 | 分析时间范围 |
|
||||
|
||||
#### EVENT_ANALYSIS — 事件分析
|
||||
|
||||
```json
|
||||
{
|
||||
"event_type": "MOTOR_START",
|
||||
"start_time": "2026-03-01T00:00:00Z",
|
||||
"end_time": "2026-03-02T00:00:00Z",
|
||||
"components": ["comp-001", "comp-002"]
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :------------ | :------- | :--- | :------------------ |
|
||||
| `event_type` | string | 是 | 事件类型 |
|
||||
| `start_time` | string | 否 | 事件起始时间(RFC3339) |
|
||||
| `end_time` | string | 否 | 事件截止时间(RFC3339) |
|
||||
| `components` | []string | 否 | 关联的元件列表 |
|
||||
|
||||
#### BATCH_IMPORT — 批量导入
|
||||
|
||||
```json
|
||||
{
|
||||
"file_path": "/data/import/model.csv",
|
||||
"file_type": "CSV",
|
||||
"options": {
|
||||
"overwrite": false,
|
||||
"validate": true,
|
||||
"notify_user": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :--------------------- | :------ | :--- | :-------------------------------- |
|
||||
| `file_path` | string | 是 | 导入文件路径 |
|
||||
| `file_type` | string | 否 | 文件类型:`CSV`, `JSON`, `XML` |
|
||||
| `options.overwrite` | boolean | 否 | 是否覆盖已有数据,默认 `false` |
|
||||
| `options.validate` | boolean | 否 | 是否校验数据,默认 `true` |
|
||||
| `options.notify_user` | boolean | 否 | 完成后是否通知用户,默认 `true` |
|
||||
|
||||
#### TEST — 测试任务
|
||||
|
||||
```json
|
||||
{
|
||||
"sleep_duration": 30
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| :--------------- | :--- | :--- | :---------------------------------------------- |
|
||||
| `sleep_duration` | int | 否 | 模拟执行耗时(秒),默认 60,最大 3600 |
|
||||
|
||||
### 5.4 任务结果对象(AsyncTaskResult)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| :-------------- | :----- | :--------------------------------------------------- |
|
||||
| `task_id` | string | 任务 UUID |
|
||||
| `task_type` | string | 任务类型 |
|
||||
| `status` | string | 当前状态 |
|
||||
| `progress` | int | 进度(0-100),仅 `RUNNING` 时返回 |
|
||||
| `created_at` | int64 | 创建时间戳(Unix 秒) |
|
||||
| `finished_at` | int64 | 完成时间戳(Unix 秒),仅 `COMPLETED`/`FAILED` 返回 |
|
||||
| `result` | object | 任务结果,仅 `COMPLETED` 时返回 |
|
||||
| `error_code` | int | 错误码,仅 `FAILED` 时返回 |
|
||||
| `error_message` | string | 错误描述,仅 `FAILED` 时返回 |
|
||||
| `error_detail` | object | 错误详情,仅 `FAILED` 时返回 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 典型调用流程
|
||||
|
||||
```
|
||||
创建任务
|
||||
└─ POST /task/async
|
||||
└─ 返回 task_id
|
||||
|
||||
轮询状态(建议间隔 2-5 秒)
|
||||
└─ GET /task/async/{task_id}
|
||||
├─ status=SUBMITTED → 继续等待
|
||||
├─ status=RUNNING → 查看 progress
|
||||
├─ status=COMPLETED → 读取 result 字段
|
||||
└─ status=FAILED → 读取 error_code / error_message
|
||||
|
||||
如需中止(仅 SUBMITTED 状态有效)
|
||||
└─ POST /task/async/{task_id}/cancel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 队列配置参考
|
||||
|
||||
| 配置项 | 值 |
|
||||
| :------------- | :-------------------------- |
|
||||
| Exchange | `modelrt.tasks.exchange` |
|
||||
| Queue | `modelrt.tasks.queue` |
|
||||
| Routing Key | `modelrt.task` |
|
||||
| 优先级范围 | 0–10(默认 5) |
|
||||
| 消息 TTL | 24 小时 |
|
||||
| 最大重试次数 | 3 次 |
|
||||
| 重试初始延迟 | 1 秒(指数退避,最大 5 分钟)|
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: 1.1
|
||||
**最后更新**: 2026-04-28
|
||||
**相关文档**: [异步任务系统设计文档](./async_task_system.md)
|
||||
485
docs/docs.go
485
docs/docs.go
|
|
@ -102,13 +102,12 @@ const docTemplate = `{
|
|||
"summary": "测量点推荐(搜索框自动补全)",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "查询输入参数,例如 'trans' 或 'transformfeeder1_220.'",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.MeasurementRecommendRequest"
|
||||
}
|
||||
"type": "string",
|
||||
"example": "\"grid1\"",
|
||||
"description": "推荐关键词,例如 'grid1' 或 'grid1.'",
|
||||
"name": "input",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
|
@ -176,19 +175,400 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/monitors/data/realtime/stream/:clientID": {
|
||||
"get": {
|
||||
"description": "根据用户输入的clientID拉取对应的实时数据",
|
||||
"tags": [
|
||||
"RealTime Component Websocket"
|
||||
],
|
||||
"summary": "实时数据拉取 websocket api",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/monitors/data/subscriptions": {
|
||||
"post": {
|
||||
"description": "根据用户输入的组件token,从 modelRT 服务中开始或结束对于量测节点的实时数据的订阅",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"RealTime Component"
|
||||
],
|
||||
"summary": "开始或结束订阅实时数据",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "量测节点实时数据订阅",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.RealTimeSubRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"2000": {
|
||||
"description": "订阅实时数据结果列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.RealTimeSubPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"3000": {
|
||||
"description": "订阅实时数据结果列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.RealTimeSubPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async": {
|
||||
"post": {
|
||||
"description": "创建新的异步任务并返回任务ID,任务将被提交到队列等待处理",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "创建异步任务",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "任务创建请求",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.AsyncTaskCreateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "任务创建成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskCreateResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/results": {
|
||||
"get": {
|
||||
"description": "根据任务ID列表查询异步任务的状态和结果",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "查询异步任务结果",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID列表,用逗号分隔",
|
||||
"name": "task_ids",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "查询成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResultQueryResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/{task_id}": {
|
||||
"get": {
|
||||
"description": "根据任务ID查询异步任务的详细状态和结果",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "查询异步任务详情",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID",
|
||||
"name": "task_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "查询成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "任务不存在",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/{task_id}/cancel": {
|
||||
"post": {
|
||||
"description": "取消指定ID的异步任务(如果任务尚未开始执行)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "取消异步任务",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID",
|
||||
"name": "task_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "任务取消成功",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误或任务无法取消",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "任务不存在",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"network.AsyncTaskCreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"params": {
|
||||
"description": "required: true",
|
||||
"type": "object"
|
||||
},
|
||||
"task_type": {
|
||||
"description": "required: true\nenum: TOPOLOGY_ANALYSIS, PERFORMANCE_ANALYSIS, EVENT_ANALYSIS, BATCH_IMPORT",
|
||||
"type": "string",
|
||||
"example": "TOPOLOGY_ANALYSIS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskCreateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "integer",
|
||||
"example": 1741846200
|
||||
},
|
||||
"error_code": {
|
||||
"type": "integer",
|
||||
"example": 400102
|
||||
},
|
||||
"error_detail": {
|
||||
"type": "object"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"example": "Component UUID not found"
|
||||
},
|
||||
"finished_at": {
|
||||
"type": "integer",
|
||||
"example": 1741846205
|
||||
},
|
||||
"progress": {
|
||||
"type": "integer",
|
||||
"example": 65
|
||||
},
|
||||
"result": {
|
||||
"type": "object"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "COMPLETED"
|
||||
},
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
||||
},
|
||||
"task_type": {
|
||||
"type": "string",
|
||||
"example": "TOPOLOGY_ANALYSIS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskResultQueryResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResult"
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"example": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.FailureResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 500
|
||||
"example": 3000
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "failed to get recommend data from redis"
|
||||
"example": "process completed with partial failures"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object"
|
||||
|
|
@ -216,15 +596,10 @@ const docTemplate = `{
|
|||
" \"I_B_rms\"",
|
||||
"\"I_C_rms\"]"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.MeasurementRecommendRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
},
|
||||
"recommended_type": {
|
||||
"type": "string",
|
||||
"example": "trans"
|
||||
"example": "grid_tag"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -237,21 +612,93 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeMeasurementItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"example": "1"
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": [
|
||||
"[\"grid1.zone1.station1.ns1.tag1.bay.I11_A_rms\"",
|
||||
"\"grid1.zone1.station1.ns1.tag1.tag1.bay.I11_B_rms\"]"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeSubPayload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"example": "5d72f2d9-e33a-4f1b-9c76-88a44b9a953e"
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.TargetResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeSubRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "required: true\nenum: [start, stop]",
|
||||
"type": "string",
|
||||
"example": "start"
|
||||
},
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"example": "5d72f2d9-e33a-4f1b-9c76-88a44b9a953e"
|
||||
},
|
||||
"measurements": {
|
||||
"description": "required: true",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.RealTimeMeasurementItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.SuccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 200
|
||||
"example": 2000
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "success"
|
||||
"example": "process completed"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.TargetResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 20000
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "subscription success"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
|
|
|||
|
|
@ -96,13 +96,12 @@
|
|||
"summary": "测量点推荐(搜索框自动补全)",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "查询输入参数,例如 'trans' 或 'transformfeeder1_220.'",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.MeasurementRecommendRequest"
|
||||
}
|
||||
"type": "string",
|
||||
"example": "\"grid1\"",
|
||||
"description": "推荐关键词,例如 'grid1' 或 'grid1.'",
|
||||
"name": "input",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
|
@ -170,19 +169,400 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/monitors/data/realtime/stream/:clientID": {
|
||||
"get": {
|
||||
"description": "根据用户输入的clientID拉取对应的实时数据",
|
||||
"tags": [
|
||||
"RealTime Component Websocket"
|
||||
],
|
||||
"summary": "实时数据拉取 websocket api",
|
||||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/monitors/data/subscriptions": {
|
||||
"post": {
|
||||
"description": "根据用户输入的组件token,从 modelRT 服务中开始或结束对于量测节点的实时数据的订阅",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"RealTime Component"
|
||||
],
|
||||
"summary": "开始或结束订阅实时数据",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "量测节点实时数据订阅",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.RealTimeSubRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"2000": {
|
||||
"description": "订阅实时数据结果列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.RealTimeSubPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"3000": {
|
||||
"description": "订阅实时数据结果列表",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.RealTimeSubPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async": {
|
||||
"post": {
|
||||
"description": "创建新的异步任务并返回任务ID,任务将被提交到队列等待处理",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "创建异步任务",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "任务创建请求",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.AsyncTaskCreateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "任务创建成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskCreateResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/results": {
|
||||
"get": {
|
||||
"description": "根据任务ID列表查询异步任务的状态和结果",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "查询异步任务结果",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID列表,用逗号分隔",
|
||||
"name": "task_ids",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "查询成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResultQueryResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/{task_id}": {
|
||||
"get": {
|
||||
"description": "根据任务ID查询异步任务的详细状态和结果",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "查询异步任务详情",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID",
|
||||
"name": "task_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "查询成功",
|
||||
"schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"payload": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "任务不存在",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/task/async/{task_id}/cancel": {
|
||||
"post": {
|
||||
"description": "取消指定ID的异步任务(如果任务尚未开始执行)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"AsyncTask"
|
||||
],
|
||||
"summary": "取消异步任务",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "任务ID",
|
||||
"name": "task_id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "任务取消成功",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.SuccessResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "请求参数错误或任务无法取消",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "任务不存在",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "服务器内部错误",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/network.FailureResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"network.AsyncTaskCreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"params": {
|
||||
"description": "required: true",
|
||||
"type": "object"
|
||||
},
|
||||
"task_type": {
|
||||
"description": "required: true\nenum: TOPOLOGY_ANALYSIS, PERFORMANCE_ANALYSIS, EVENT_ANALYSIS, BATCH_IMPORT",
|
||||
"type": "string",
|
||||
"example": "TOPOLOGY_ANALYSIS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskCreateResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "integer",
|
||||
"example": 1741846200
|
||||
},
|
||||
"error_code": {
|
||||
"type": "integer",
|
||||
"example": 400102
|
||||
},
|
||||
"error_detail": {
|
||||
"type": "object"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"example": "Component UUID not found"
|
||||
},
|
||||
"finished_at": {
|
||||
"type": "integer",
|
||||
"example": 1741846205
|
||||
},
|
||||
"progress": {
|
||||
"type": "integer",
|
||||
"example": 65
|
||||
},
|
||||
"result": {
|
||||
"type": "object"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"example": "COMPLETED"
|
||||
},
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
||||
},
|
||||
"task_type": {
|
||||
"type": "string",
|
||||
"example": "TOPOLOGY_ANALYSIS"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.AsyncTaskResultQueryResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.AsyncTaskResult"
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"example": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.FailureResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 500
|
||||
"example": 3000
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "failed to get recommend data from redis"
|
||||
"example": "process completed with partial failures"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object"
|
||||
|
|
@ -210,15 +590,10 @@
|
|||
" \"I_B_rms\"",
|
||||
"\"I_C_rms\"]"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.MeasurementRecommendRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {
|
||||
},
|
||||
"recommended_type": {
|
||||
"type": "string",
|
||||
"example": "trans"
|
||||
"example": "grid_tag"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -231,21 +606,93 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeMeasurementItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"example": "1"
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": [
|
||||
"[\"grid1.zone1.station1.ns1.tag1.bay.I11_A_rms\"",
|
||||
"\"grid1.zone1.station1.ns1.tag1.tag1.bay.I11_B_rms\"]"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeSubPayload": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"example": "5d72f2d9-e33a-4f1b-9c76-88a44b9a953e"
|
||||
},
|
||||
"targets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.TargetResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.RealTimeSubRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "required: true\nenum: [start, stop]",
|
||||
"type": "string",
|
||||
"example": "start"
|
||||
},
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"example": "5d72f2d9-e33a-4f1b-9c76-88a44b9a953e"
|
||||
},
|
||||
"measurements": {
|
||||
"description": "required: true",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/network.RealTimeMeasurementItem"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.SuccessResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 200
|
||||
"example": 2000
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "success"
|
||||
"example": "process completed"
|
||||
},
|
||||
"payload": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"network.TargetResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"example": 20000
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"example": "grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"example": "subscription success"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,71 @@
|
|||
basePath: /api/v1
|
||||
definitions:
|
||||
network.AsyncTaskCreateRequest:
|
||||
properties:
|
||||
params:
|
||||
description: 'required: true'
|
||||
type: object
|
||||
task_type:
|
||||
description: |-
|
||||
required: true
|
||||
enum: TOPOLOGY_ANALYSIS, PERFORMANCE_ANALYSIS, EVENT_ANALYSIS, BATCH_IMPORT
|
||||
example: TOPOLOGY_ANALYSIS
|
||||
type: string
|
||||
type: object
|
||||
network.AsyncTaskCreateResponse:
|
||||
properties:
|
||||
task_id:
|
||||
example: 123e4567-e89b-12d3-a456-426614174000
|
||||
type: string
|
||||
type: object
|
||||
network.AsyncTaskResult:
|
||||
properties:
|
||||
created_at:
|
||||
example: 1741846200
|
||||
type: integer
|
||||
error_code:
|
||||
example: 400102
|
||||
type: integer
|
||||
error_detail:
|
||||
type: object
|
||||
error_message:
|
||||
example: Component UUID not found
|
||||
type: string
|
||||
finished_at:
|
||||
example: 1741846205
|
||||
type: integer
|
||||
progress:
|
||||
example: 65
|
||||
type: integer
|
||||
result:
|
||||
type: object
|
||||
status:
|
||||
example: COMPLETED
|
||||
type: string
|
||||
task_id:
|
||||
example: 123e4567-e89b-12d3-a456-426614174000
|
||||
type: string
|
||||
task_type:
|
||||
example: TOPOLOGY_ANALYSIS
|
||||
type: string
|
||||
type: object
|
||||
network.AsyncTaskResultQueryResponse:
|
||||
properties:
|
||||
tasks:
|
||||
items:
|
||||
$ref: '#/definitions/network.AsyncTaskResult'
|
||||
type: array
|
||||
total:
|
||||
example: 3
|
||||
type: integer
|
||||
type: object
|
||||
network.FailureResponse:
|
||||
properties:
|
||||
code:
|
||||
example: 500
|
||||
example: 3000
|
||||
type: integer
|
||||
msg:
|
||||
example: failed to get recommend data from redis
|
||||
example: process completed with partial failures
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
|
|
@ -27,11 +86,8 @@ definitions:
|
|||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
network.MeasurementRecommendRequest:
|
||||
properties:
|
||||
input:
|
||||
example: trans
|
||||
recommended_type:
|
||||
example: grid_tag
|
||||
type: string
|
||||
type: object
|
||||
network.RealTimeDataPayload:
|
||||
|
|
@ -40,17 +96,69 @@ definitions:
|
|||
description: TODO 增加example tag
|
||||
type: object
|
||||
type: object
|
||||
network.RealTimeMeasurementItem:
|
||||
properties:
|
||||
interval:
|
||||
example: "1"
|
||||
type: string
|
||||
targets:
|
||||
example:
|
||||
- '["grid1.zone1.station1.ns1.tag1.bay.I11_A_rms"'
|
||||
- '"grid1.zone1.station1.ns1.tag1.tag1.bay.I11_B_rms"]'
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
network.RealTimeSubPayload:
|
||||
properties:
|
||||
client_id:
|
||||
example: 5d72f2d9-e33a-4f1b-9c76-88a44b9a953e
|
||||
type: string
|
||||
targets:
|
||||
items:
|
||||
$ref: '#/definitions/network.TargetResult'
|
||||
type: array
|
||||
type: object
|
||||
network.RealTimeSubRequest:
|
||||
properties:
|
||||
action:
|
||||
description: |-
|
||||
required: true
|
||||
enum: [start, stop]
|
||||
example: start
|
||||
type: string
|
||||
client_id:
|
||||
example: 5d72f2d9-e33a-4f1b-9c76-88a44b9a953e
|
||||
type: string
|
||||
measurements:
|
||||
description: 'required: true'
|
||||
items:
|
||||
$ref: '#/definitions/network.RealTimeMeasurementItem'
|
||||
type: array
|
||||
type: object
|
||||
network.SuccessResponse:
|
||||
properties:
|
||||
code:
|
||||
example: 200
|
||||
example: 2000
|
||||
type: integer
|
||||
msg:
|
||||
example: success
|
||||
example: process completed
|
||||
type: string
|
||||
payload:
|
||||
type: object
|
||||
type: object
|
||||
network.TargetResult:
|
||||
properties:
|
||||
code:
|
||||
example: 20000
|
||||
type: integer
|
||||
id:
|
||||
example: grid1.zone1.station1.ns1.tag1.transformfeeder1_220.I_A_rms
|
||||
type: string
|
||||
msg:
|
||||
example: subscription success
|
||||
type: string
|
||||
type: object
|
||||
host: localhost:8080
|
||||
info:
|
||||
contact:
|
||||
|
|
@ -110,12 +218,12 @@ paths:
|
|||
- application/json
|
||||
description: 根据用户输入的字符串,从 Redis 中查询可能的测量点或结构路径,并提供推荐列表。
|
||||
parameters:
|
||||
- description: 查询输入参数,例如 'trans' 或 'transformfeeder1_220.'
|
||||
in: body
|
||||
name: request
|
||||
- description: 推荐关键词,例如 'grid1' 或 'grid1.'
|
||||
example: '"grid1"'
|
||||
in: query
|
||||
name: input
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/network.MeasurementRecommendRequest'
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
@ -160,4 +268,187 @@ paths:
|
|||
summary: load circuit diagram info
|
||||
tags:
|
||||
- load circuit_diagram
|
||||
/monitors/data/realtime/stream/:clientID:
|
||||
get:
|
||||
description: 根据用户输入的clientID拉取对应的实时数据
|
||||
responses: {}
|
||||
summary: 实时数据拉取 websocket api
|
||||
tags:
|
||||
- RealTime Component Websocket
|
||||
/monitors/data/subscriptions:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据用户输入的组件token,从 modelRT 服务中开始或结束对于量测节点的实时数据的订阅
|
||||
parameters:
|
||||
- description: 量测节点实时数据订阅
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/network.RealTimeSubRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"2000":
|
||||
description: 订阅实时数据结果列表
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/network.SuccessResponse'
|
||||
- properties:
|
||||
payload:
|
||||
$ref: '#/definitions/network.RealTimeSubPayload'
|
||||
type: object
|
||||
"3000":
|
||||
description: 订阅实时数据结果列表
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/network.FailureResponse'
|
||||
- properties:
|
||||
payload:
|
||||
$ref: '#/definitions/network.RealTimeSubPayload'
|
||||
type: object
|
||||
summary: 开始或结束订阅实时数据
|
||||
tags:
|
||||
- RealTime Component
|
||||
/task/async:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 创建新的异步任务并返回任务ID,任务将被提交到队列等待处理
|
||||
parameters:
|
||||
- description: 任务创建请求
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/network.AsyncTaskCreateRequest'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 任务创建成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/network.SuccessResponse'
|
||||
- properties:
|
||||
payload:
|
||||
$ref: '#/definitions/network.AsyncTaskCreateResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
summary: 创建异步任务
|
||||
tags:
|
||||
- AsyncTask
|
||||
/task/async/{task_id}:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据任务ID查询异步任务的详细状态和结果
|
||||
parameters:
|
||||
- description: 任务ID
|
||||
in: path
|
||||
name: task_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 查询成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/network.SuccessResponse'
|
||||
- properties:
|
||||
payload:
|
||||
$ref: '#/definitions/network.AsyncTaskResult'
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"404":
|
||||
description: 任务不存在
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
summary: 查询异步任务详情
|
||||
tags:
|
||||
- AsyncTask
|
||||
/task/async/{task_id}/cancel:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 取消指定ID的异步任务(如果任务尚未开始执行)
|
||||
parameters:
|
||||
- description: 任务ID
|
||||
in: path
|
||||
name: task_id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 任务取消成功
|
||||
schema:
|
||||
$ref: '#/definitions/network.SuccessResponse'
|
||||
"400":
|
||||
description: 请求参数错误或任务无法取消
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"404":
|
||||
description: 任务不存在
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
summary: 取消异步任务
|
||||
tags:
|
||||
- AsyncTask
|
||||
/task/async/results:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 根据任务ID列表查询异步任务的状态和结果
|
||||
parameters:
|
||||
- description: 任务ID列表,用逗号分隔
|
||||
in: query
|
||||
name: task_ids
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: 查询成功
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/network.SuccessResponse'
|
||||
- properties:
|
||||
payload:
|
||||
$ref: '#/definitions/network.AsyncTaskResultQueryResponse'
|
||||
type: object
|
||||
"400":
|
||||
description: 请求参数错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
"500":
|
||||
description: 服务器内部错误
|
||||
schema:
|
||||
$ref: '#/definitions/network.FailureResponse'
|
||||
summary: 查询异步任务结果
|
||||
tags:
|
||||
- AsyncTask
|
||||
swagger: "2.0"
|
||||
|
|
|
|||
64
go.mod
64
go.mod
|
|
@ -1,26 +1,33 @@
|
|||
module modelRT
|
||||
|
||||
go 1.24
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/RediSearch/redisearch-go/v2 v2.1.1
|
||||
github.com/bitly/go-simplejson v0.5.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/gomodule/redigo v1.8.9
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
github.com/panjf2000/ants/v2 v2.10.0
|
||||
github.com/rabbitmq/amqp091-go v1.10.0
|
||||
github.com/redis/go-redis/v9 v9.7.3
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.4
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
go.opentelemetry.io/otel/trace v1.43.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/sync v0.20.0
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/gorm v1.25.12
|
||||
|
|
@ -29,25 +36,29 @@ require (
|
|||
require (
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.5 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/bytedance/sonic v1.13.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
|
|
@ -56,7 +67,7 @@ require (
|
|||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
|
|
@ -64,7 +75,7 @@ require (
|
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
|
|
@ -74,16 +85,23 @@ require (
|
|||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/arch v0.12.0 // indirect
|
||||
golang.org/x/crypto v0.30.0 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
|
|||
142
go.sum
142
go.sum
|
|
@ -12,16 +12,17 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
|
||||
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -33,14 +34,21 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
|||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
|
|
@ -55,21 +63,27 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
|
|
@ -90,8 +104,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
|
|
@ -116,15 +130,17 @@ github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4
|
|||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
|
||||
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
|
|
@ -148,8 +164,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
|
|
@ -160,37 +176,57 @@ github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
|||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -198,8 +234,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
|
@ -207,16 +243,24 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"modelRT/alert"
|
||||
"modelRT/constants"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
"modelRT/real-time-data/alert"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func ComponentAnchorReplaceHandler(c *gin.Context) {
|
|||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": request.UUID,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/mq"
|
||||
"modelRT/mq/event"
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AsyncTaskCancelHandler handles cancellation of an async task
|
||||
// @Summary 取消异步任务
|
||||
// @Description 取消指定ID的异步任务(如果任务尚未开始执行)
|
||||
// @Tags AsyncTask
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param task_id path string true "任务ID"
|
||||
// @Success 200 {object} network.SuccessResponse "任务取消成功"
|
||||
// @Failure 200 {object} network.FailureResponse "请求参数错误或任务无法取消"
|
||||
// @Router /task/async/{task_id}/cancel [post]
|
||||
func AsyncTaskCancelHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
taskIDStr := c.Param("task_id")
|
||||
if taskIDStr == "" {
|
||||
logger.Error(ctx, "task_id parameter is required")
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task_id parameter is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
taskID, err := uuid.FromString(taskIDStr)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "invalid task ID format", "task_id", taskIDStr, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task ID format", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
asyncTask, err := database.GetAsyncTaskByID(ctx, pgClient, taskID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Error(ctx, "async task not found", "task_id", taskID)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task not found", nil)
|
||||
return
|
||||
}
|
||||
logger.Error(ctx, "failed to query async task from database", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to query task", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if asyncTask.Status != orm.AsyncTaskStatusSubmitted {
|
||||
logger.Error(ctx, "task cannot be cancelled", "task_id", taskID, "status", asyncTask.Status)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task cannot be cancelled, already running or completed", nil)
|
||||
return
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
err = database.FailAsyncTask(ctx, pgClient, taskID, timestamp)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to cancel async task", "task_id", taskID, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to cancel task", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if record, evtErr := event.NewTaskCancelledMessage(taskID.String(), string(asyncTask.TaskType)); evtErr == nil {
|
||||
mq.TryEmitMessage(ctx, record)
|
||||
}
|
||||
|
||||
err = database.UpdateAsyncTaskResultWithError(ctx, pgClient, taskID, 40009, "task cancelled by user", orm.JSONMap{
|
||||
"cancelled_at": timestamp,
|
||||
"cancelled_by": "user",
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to update task result with cancellation error", "task_id", taskID, "error", err)
|
||||
}
|
||||
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "task cancelled successfully", nil)
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/mq"
|
||||
"modelRT/mq/event"
|
||||
"modelRT/network"
|
||||
"modelRT/orm"
|
||||
"modelRT/task"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
)
|
||||
|
||||
// AsyncTaskCreateHandler handles creation of asynchronous tasks
|
||||
// @Summary 创建异步任务
|
||||
// @Description 创建新的异步任务并返回任务ID,任务将被提交到队列等待处理
|
||||
// @Tags AsyncTask
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body network.AsyncTaskCreateRequest true "任务创建请求"
|
||||
// @Success 200 {object} network.SuccessResponse{payload=network.AsyncTaskCreateResponse} "任务创建成功"
|
||||
// @Failure 400 {object} network.FailureResponse "请求参数错误"
|
||||
// @Failure 500 {object} network.FailureResponse "服务器内部错误"
|
||||
// @Router /task/async [post]
|
||||
func AsyncTaskCreateHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
var request network.AsyncTaskCreateRequest
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
logger.Error(ctx, "unmarshal async task create request failed", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid request parameters", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// validate task type
|
||||
if !orm.IsValidAsyncTaskType(request.TaskType) {
|
||||
logger.Error(ctx, "check task type invalid", "task_type", request.TaskType)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task type", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// validate task parameters based on task type
|
||||
if !validateTaskParams(request.TaskType, request.Params) {
|
||||
logger.Error(ctx, "check task parameters invalid", "task_type", request.TaskType, "params", request.Params)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task parameters", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// create task in database
|
||||
taskType := orm.AsyncTaskType(request.TaskType)
|
||||
params := orm.JSONMap(request.Params)
|
||||
|
||||
asyncTask, err := database.CreateAsyncTask(ctx, pgClient, taskType, params)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "create async task in database failed", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to create task", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// enqueue task to channel for async publishing to RabbitMQ
|
||||
msg := task.NewTaskQueueMessageWithPriority(asyncTask.TaskID, task.TaskType(request.TaskType), 5)
|
||||
// propagate the current OTel span context so the async chain stays on the same trace
|
||||
carrier := make(map[string]string)
|
||||
otel.GetTextMapPropagator().Inject(ctx, propagation.MapCarrier(carrier))
|
||||
msg.TraceCarrier = carrier
|
||||
msg.Params = request.Params
|
||||
task.TaskMsgChan <- msg
|
||||
logger.Info(ctx, "task enqueued to channel", "task_id", asyncTask.TaskID, "queue", constants.TaskQueueName)
|
||||
|
||||
if record, err := event.NewTaskSubmittedMessage(asyncTask.TaskID.String(), request.TaskType, 5); err == nil {
|
||||
mq.TryEmitMessage(ctx, record)
|
||||
}
|
||||
|
||||
logger.Info(ctx, "async task created success", "task_id", asyncTask.TaskID, "task_type", request.TaskType)
|
||||
|
||||
// return success response
|
||||
payload := genAsyncTaskCreatePayload(asyncTask.TaskID.String())
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "task created successfully", payload)
|
||||
}
|
||||
|
||||
func validateTaskParams(taskType string, params map[string]any) bool {
|
||||
switch taskType {
|
||||
case string(orm.AsyncTaskTypeTopologyAnalysis):
|
||||
return validateTopologyAnalysisParams(params)
|
||||
case string(orm.AsyncTaskTypePerformanceAnalysis):
|
||||
return validatePerformanceAnalysisParams(params)
|
||||
case string(orm.AsyncTaskTypeEventAnalysis):
|
||||
return validateEventAnalysisParams(params)
|
||||
case string(orm.AsyncTaskTypeBatchImport):
|
||||
return validateBatchImportParams(params)
|
||||
case string(orm.AsyncTaskTypeTest):
|
||||
return validateTestTaskParams(params)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validateTopologyAnalysisParams(params map[string]any) bool {
|
||||
if v, ok := params["start_component_uuid"]; !ok || v == "" {
|
||||
return false
|
||||
}
|
||||
if v, ok := params["end_component_uuid"]; !ok || v == "" {
|
||||
return false
|
||||
}
|
||||
// check_in_service is optional; validate type when present
|
||||
if v, exists := params["check_in_service"]; exists {
|
||||
if _, isBool := v.(bool); !isBool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validatePerformanceAnalysisParams(params map[string]any) bool {
|
||||
// Check required parameters for performance analysis
|
||||
if componentIDs, ok := params["component_ids"]; !ok {
|
||||
return false
|
||||
} else if ids, isSlice := componentIDs.([]any); !isSlice || len(ids) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateEventAnalysisParams(params map[string]any) bool {
|
||||
// Check required parameters for event analysis
|
||||
if eventType, ok := params["event_type"]; !ok || eventType == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateBatchImportParams(params map[string]any) bool {
|
||||
// Check required parameters for batch import
|
||||
if filePath, ok := params["file_path"]; !ok || filePath == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validateTestTaskParams(params map[string]any) bool {
|
||||
// Test task has optional parameters, all are valid
|
||||
// sleep_duration defaults to 60 seconds if not provided
|
||||
return true
|
||||
}
|
||||
|
||||
func genAsyncTaskCreatePayload(taskID string) map[string]any {
|
||||
payload := map[string]any{
|
||||
"task_id": taskID,
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AsyncTaskProgressUpdateHandler handles updating task progress (internal use, not exposed via API)
|
||||
func AsyncTaskProgressUpdateHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
var request network.AsyncTaskProgressUpdate
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
logger.Error(ctx, "failed to unmarshal async task progress update request", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid request parameters", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
err := database.UpdateAsyncTaskProgress(ctx, pgClient, request.TaskID, request.Progress)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to update async task progress", "task_id", request.TaskID, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to update task progress", nil)
|
||||
return
|
||||
}
|
||||
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "task progress updated successfully", nil)
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AsyncTaskResultDetailHandler handles detailed query of a single async task result
|
||||
// @Summary 查询异步任务详情
|
||||
// @Description 根据任务ID查询异步任务的详细状态和结果
|
||||
// @Tags AsyncTask
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param task_id path string true "任务ID"
|
||||
// @Success 200 {object} network.SuccessResponse{payload=network.AsyncTaskResult} "查询成功"
|
||||
// @Failure 200 {object} network.FailureResponse "请求参数错误"
|
||||
// @Router /task/async/{task_id} [get]
|
||||
func AsyncTaskResultDetailHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
taskIDStr := c.Param("task_id")
|
||||
if taskIDStr == "" {
|
||||
logger.Error(ctx, "task_id parameter is required")
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task_id parameter is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
taskID, err := uuid.FromString(taskIDStr)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "invalid task ID format", "task_id", taskIDStr, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task ID format", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
asyncTask, err := database.GetAsyncTaskByID(ctx, pgClient, taskID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
logger.Error(ctx, "async task not found", "task_id", taskID)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task not found", nil)
|
||||
return
|
||||
}
|
||||
logger.Error(ctx, "failed to query async task from database", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to query task", nil)
|
||||
return
|
||||
}
|
||||
|
||||
taskResult, err := database.GetAsyncTaskResult(ctx, pgClient, taskID)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to query async task result from database", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to query task result", nil)
|
||||
return
|
||||
}
|
||||
|
||||
responseTask := network.AsyncTaskResult{
|
||||
TaskID: asyncTask.TaskID,
|
||||
TaskType: string(asyncTask.TaskType),
|
||||
Status: string(asyncTask.Status),
|
||||
CreatedAt: asyncTask.CreatedAt,
|
||||
FinishedAt: asyncTask.FinishedAt,
|
||||
Progress: asyncTask.Progress,
|
||||
}
|
||||
|
||||
if taskResult != nil {
|
||||
if taskResult.Result != nil {
|
||||
responseTask.Result = map[string]any(taskResult.Result)
|
||||
}
|
||||
if taskResult.ErrorCode != nil {
|
||||
responseTask.ErrorCode = taskResult.ErrorCode
|
||||
}
|
||||
if taskResult.ErrorMessage != nil {
|
||||
responseTask.ErrorMessage = taskResult.ErrorMessage
|
||||
}
|
||||
if taskResult.ErrorDetail != nil {
|
||||
responseTask.ErrorDetail = map[string]any(taskResult.ErrorDetail)
|
||||
}
|
||||
}
|
||||
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "query completed", responseTask)
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
// AsyncTaskResultQueryHandler handles querying of asynchronous task results
|
||||
// @Summary 查询异步任务结果
|
||||
// @Description 根据任务ID列表查询异步任务的状态和结果
|
||||
// @Tags AsyncTask
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param task_ids query string true "任务ID列表,用逗号分隔"
|
||||
// @Success 200 {object} network.SuccessResponse{payload=network.AsyncTaskResultQueryResponse} "查询成功"
|
||||
// @Failure 200 {object} network.FailureResponse "请求参数错误"
|
||||
// @Router /task/async/results [get]
|
||||
func AsyncTaskResultQueryHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
taskIDsParam := c.Query("task_ids")
|
||||
if taskIDsParam == "" {
|
||||
logger.Error(ctx, "task_ids parameter is required")
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "task_ids parameter is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var taskIDs []uuid.UUID
|
||||
taskIDStrs := splitCommaSeparated(taskIDsParam)
|
||||
for _, taskIDStr := range taskIDStrs {
|
||||
taskID, err := uuid.FromString(taskIDStr)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "invalid task ID format", "task_id", taskIDStr, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task ID format", nil)
|
||||
return
|
||||
}
|
||||
taskIDs = append(taskIDs, taskID)
|
||||
}
|
||||
|
||||
if len(taskIDs) == 0 {
|
||||
logger.Error(ctx, "no valid task IDs provided")
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "no valid task IDs provided", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
asyncTasks, err := database.GetAsyncTasksByIDs(ctx, pgClient, taskIDs)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to query async tasks from database", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to query tasks", nil)
|
||||
return
|
||||
}
|
||||
|
||||
taskResults, err := database.GetAsyncTaskResults(ctx, pgClient, taskIDs)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to query async task results from database", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to query task results", nil)
|
||||
return
|
||||
}
|
||||
|
||||
taskResultMap := make(map[uuid.UUID]orm.AsyncTaskResult)
|
||||
for _, result := range taskResults {
|
||||
taskResultMap[result.TaskID] = result
|
||||
}
|
||||
|
||||
var responseTasks []network.AsyncTaskResult
|
||||
for _, asyncTask := range asyncTasks {
|
||||
taskResult := network.AsyncTaskResult{
|
||||
TaskID: asyncTask.TaskID,
|
||||
TaskType: string(asyncTask.TaskType),
|
||||
Status: string(asyncTask.Status),
|
||||
CreatedAt: asyncTask.CreatedAt,
|
||||
FinishedAt: asyncTask.FinishedAt,
|
||||
Progress: asyncTask.Progress,
|
||||
}
|
||||
|
||||
if result, exists := taskResultMap[asyncTask.TaskID]; exists {
|
||||
if result.Result != nil {
|
||||
taskResult.Result = map[string]any(result.Result)
|
||||
}
|
||||
if result.ErrorCode != nil {
|
||||
taskResult.ErrorCode = result.ErrorCode
|
||||
}
|
||||
if result.ErrorMessage != nil {
|
||||
taskResult.ErrorMessage = result.ErrorMessage
|
||||
}
|
||||
if result.ErrorDetail != nil {
|
||||
taskResult.ErrorDetail = map[string]any(result.ErrorDetail)
|
||||
}
|
||||
}
|
||||
|
||||
responseTasks = append(responseTasks, taskResult)
|
||||
}
|
||||
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "query completed", network.AsyncTaskResultQueryResponse{
|
||||
Total: len(responseTasks),
|
||||
Tasks: responseTasks,
|
||||
})
|
||||
}
|
||||
|
||||
func splitCommaSeparated(s string) []string {
|
||||
var result []string
|
||||
var current strings.Builder
|
||||
inQuotes := false
|
||||
escape := false
|
||||
|
||||
for _, ch := range s {
|
||||
if escape {
|
||||
current.WriteRune(ch)
|
||||
escape = false
|
||||
continue
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case '\\':
|
||||
escape = true
|
||||
case '"':
|
||||
inQuotes = !inQuotes
|
||||
case ',':
|
||||
if !inQuotes {
|
||||
result = append(result, strings.TrimSpace(current.String()))
|
||||
current.Reset()
|
||||
} else {
|
||||
current.WriteRune(ch)
|
||||
}
|
||||
default:
|
||||
current.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
if current.Len() > 0 {
|
||||
result = append(result, strings.TrimSpace(current.String()))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
"modelRT/orm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AsyncTaskStatusUpdateHandler handles updating task status (internal use, not exposed via API)
|
||||
func AsyncTaskStatusUpdateHandler(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
var request network.AsyncTaskStatusUpdate
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
logger.Error(ctx, "failed to unmarshal async task status update request", "error", err)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid request parameters", nil)
|
||||
return
|
||||
}
|
||||
|
||||
validStatus := map[string]bool{
|
||||
string(orm.AsyncTaskStatusSubmitted): true,
|
||||
string(orm.AsyncTaskStatusRunning): true,
|
||||
string(orm.AsyncTaskStatusCompleted): true,
|
||||
string(orm.AsyncTaskStatusFailed): true,
|
||||
}
|
||||
|
||||
if !validStatus[request.Status] {
|
||||
logger.Error(ctx, "invalid task status", "status", request.Status)
|
||||
renderRespFailure(c, constants.RespCodeInvalidParams, "invalid task status", nil)
|
||||
return
|
||||
}
|
||||
|
||||
pgClient := database.GetPostgresDBClient()
|
||||
if pgClient == nil {
|
||||
logger.Error(ctx, "database connection not found in context")
|
||||
renderRespFailure(c, constants.RespCodeServerError, "database connection error", nil)
|
||||
return
|
||||
}
|
||||
|
||||
status := orm.AsyncTaskStatus(request.Status)
|
||||
err := database.UpdateAsyncTaskStatus(ctx, pgClient, request.TaskID, status)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to update async task status", "task_id", request.TaskID, "status", request.Status, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to update task status", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if request.Status == string(orm.AsyncTaskStatusCompleted) {
|
||||
err = database.CompleteAsyncTask(ctx, pgClient, request.TaskID, request.Timestamp)
|
||||
} else if request.Status == string(orm.AsyncTaskStatusFailed) {
|
||||
err = database.FailAsyncTask(ctx, pgClient, request.TaskID, request.Timestamp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Error(ctx, "failed to update async task completion timestamp", "task_id", request.TaskID, "error", err)
|
||||
renderRespFailure(c, constants.RespCodeServerError, "failed to update task completion timestamp", nil)
|
||||
return
|
||||
}
|
||||
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "task status updated successfully", nil)
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/common"
|
||||
"modelRT/diagram"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
|
@ -16,7 +16,7 @@ func AttrDeleteHandler(c *gin.Context) {
|
|||
var request network.AttrDeleteRequest
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
err := common.ErrGetClientToken
|
||||
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
|
|
@ -41,7 +41,7 @@ func AttrDeleteHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{"attr_token": request.AttrToken},
|
||||
Payload: map[string]any{"attr_token": request.AttrToken},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ func AttrDeleteHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"attr_token": request.AttrToken,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/common"
|
||||
"modelRT/database"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
|
@ -17,7 +17,7 @@ func AttrGetHandler(c *gin.Context) {
|
|||
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
err := common.ErrGetClientToken
|
||||
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
|
|
@ -46,7 +46,7 @@ func AttrGetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{"attr_token": request.AttrToken},
|
||||
Payload: map[string]any{"attr_token": request.AttrToken},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func AttrGetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"attr_token": request.AttrToken,
|
||||
"attr_value": attrValue,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/common"
|
||||
"modelRT/diagram"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
|
|
@ -17,7 +17,7 @@ func AttrSetHandler(c *gin.Context) {
|
|||
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
err := common.ErrGetClientToken
|
||||
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
|
|
@ -43,7 +43,7 @@ func AttrSetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{"attr_token": request.AttrToken},
|
||||
Payload: map[string]any{"attr_token": request.AttrToken},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ func AttrSetHandler(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"attr_token": request.AttrToken,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicLink,
|
||||
},
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_infos": topologicCreateInfos,
|
||||
},
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"component_infos": request.ComponentInfos,
|
||||
},
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": info.UUID,
|
||||
"component_params": info.Params,
|
||||
},
|
||||
|
|
@ -152,7 +152,7 @@ func CircuitDiagramCreateHandler(c *gin.Context) {
|
|||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicLink,
|
||||
},
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicDelInfo,
|
||||
},
|
||||
}
|
||||
|
|
@ -112,7 +112,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicDelInfo,
|
||||
},
|
||||
}
|
||||
|
|
@ -138,7 +138,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": componentInfo.UUID,
|
||||
},
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": componentInfo.UUID,
|
||||
},
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": componentInfo.UUID,
|
||||
},
|
||||
}
|
||||
|
|
@ -205,7 +205,7 @@ func CircuitDiagramDeleteHandler(c *gin.Context) {
|
|||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": pageID,
|
||||
},
|
||||
}
|
||||
|
|
@ -48,14 +48,14 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": pageID,
|
||||
},
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
return
|
||||
}
|
||||
payload := make(map[string]interface{})
|
||||
payload := make(map[string]any)
|
||||
payload["root_vertex"] = topologicInfo.RootVertex
|
||||
payload["topologic"] = topologicInfo.VerticeLinks
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": componentUUID,
|
||||
},
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": componentUUID,
|
||||
},
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": topologicInfo.RootVertex,
|
||||
},
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ func CircuitDiagramLoadHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": rootVertexUUID,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicLink,
|
||||
},
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicChangeInfo,
|
||||
},
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"topologic_info": topologicChangeInfo,
|
||||
},
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
"component_info": request.ComponentInfos,
|
||||
},
|
||||
|
|
@ -129,7 +129,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"uuid": info.UUID,
|
||||
"component_params": info.Params,
|
||||
},
|
||||
|
|
@ -152,7 +152,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
|
|||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"page_id": request.PageID,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"modelRT/common"
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/diagram"
|
||||
|
|
@ -43,7 +44,7 @@ func DiagramNodeLinkHandler(c *gin.Context) {
|
|||
var request network.DiagramNodeLinkRequest
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
err := common.ErrGetClientToken
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
|
@ -167,7 +168,7 @@ func processLinkSetData(ctx context.Context, action string, level int, prevLinkS
|
|||
err2 = prevLinkSet.SREM(prevMember)
|
||||
}
|
||||
default:
|
||||
err := constants.ErrUnsupportedLinkAction
|
||||
err := common.ErrUnsupportedLinkAction
|
||||
logger.Error(ctx, "unsupport diagram node link process action", "action", action, "error", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,3 +30,14 @@ func renderRespSuccess(c *gin.Context, code int, msg string, payload any) {
|
|||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func renderWSRespFailure(c *gin.Context, code int, msg string, payload any) {
|
||||
resp := network.WSResponse{
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
}
|
||||
if payload != nil {
|
||||
resp.Payload = payload
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"modelRT/alert"
|
||||
"modelRT/constants"
|
||||
"modelRT/logger"
|
||||
"modelRT/network"
|
||||
"modelRT/real-time-data/alert"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
|
@ -50,7 +50,7 @@ func QueryHistoryDataHandler(c *gin.Context) {
|
|||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
Payload: map[string]any{
|
||||
"events": events,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ package handler
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/constants"
|
||||
"modelRT/common"
|
||||
"modelRT/database"
|
||||
"modelRT/diagram"
|
||||
"modelRT/logger"
|
||||
|
|
@ -19,7 +19,7 @@ func MeasurementGetHandler(c *gin.Context) {
|
|||
|
||||
clientToken := c.GetString("client_token")
|
||||
if clientToken == "" {
|
||||
err := constants.ErrGetClientToken
|
||||
err := common.ErrGetClientToken
|
||||
|
||||
logger.Error(c, "failed to get client token from context", "error", err)
|
||||
c.JSON(http.StatusOK, network.FailureResponse{
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue