├── shutdown.bat ├── shutdown.sh ├── resource ├── excel │ └── 字典_20211201154616.xlsx ├── rbac_model.conf ├── template │ ├── js │ │ └── api.template │ └── go │ │ └── entity.template ├── default.pem └── default.key ├── pkg ├── config │ ├── ys.go │ ├── casbin.go │ ├── queue.go │ ├── redis.go │ ├── app.go │ ├── oss.go │ ├── taos.go │ ├── gen.go │ ├── jwt.go │ ├── mqtt.go │ ├── log.go │ ├── server.go │ ├── db.go │ └── config.go ├── transport │ ├── transport.go │ ├── grpc_server.go │ └── http_server.go ├── global │ ├── global.go │ ├── model │ │ ├── global_model.go │ │ └── device_auth_model.go │ └── global_const_device.go ├── tool │ ├── base_test.go │ ├── base.go │ └── excel.go ├── rule_engine │ ├── nodes │ │ ├── template_engine.go │ │ ├── input_node.go │ │ ├── filter_message_type_switch_node.go │ │ ├── init.go │ │ ├── filter_switch_node.go │ │ ├── transform_script_node.go │ │ ├── filter_device_type_switch_node.go │ │ ├── external_rule_chain_node.go │ │ ├── external_send_sms_node.go │ │ ├── filter_message_type_node.go │ │ ├── properties.go │ │ ├── filter_script_node.go │ │ ├── action_switch_meta_node.go │ │ ├── external_nats_node.go │ │ ├── factory.go │ │ ├── action_log_node.go │ │ ├── transform_delete_key_node.go │ │ ├── action_clear_alarm_node.go │ │ ├── transform_rename_key_node.go │ │ ├── external_wechat_node.go │ │ └── action_delay_node.go │ ├── instance.go │ ├── instance_test.go │ └── manifest │ │ └── manifest.go ├── middleware │ ├── escape_html.go │ ├── cors.go │ ├── rate.go │ ├── oper.go │ ├── swagger.go │ ├── permission.go │ └── log.go ├── cache │ ├── product_rule.go │ └── device_etoken.go ├── tdengine │ ├── tdengine_rule_debug.go │ ├── TDengineModel.go │ ├── tdengine_event.go │ └── tdengine_log.go ├── shadow │ └── device.go ├── device_rpc │ └── rpc.go ├── websocket │ └── socket_server_pool.go └── initialize │ ├── event.go │ └── table.go ├── apps ├── job │ ├── api │ │ ├── from │ │ │ └── job.go │ │ └── log_job.go │ ├── jobs │ │ ├── type.go │ │ └── examples.go │ ├── entity │ │ ├── log_job.go │ │ └── job.go │ ├── services │ │ └── log_job.go │ └── router │ │ └── job_log.go ├── system │ ├── entity │ │ ├── role_organization.go │ │ ├── role_menu.go │ │ ├── api.go │ │ ├── tenant.go │ │ ├── notice.go │ │ ├── post.go │ │ ├── config.go │ │ ├── dict.go │ │ ├── organization.go │ │ ├── role.go │ │ ├── menu.go │ │ └── user.go │ ├── router │ │ ├── system.go │ │ ├── upload.go │ │ └── notice.go │ ├── api │ │ ├── form │ │ │ ├── role.go │ │ │ └── user.go │ │ ├── vo │ │ │ ├── metaVo.go │ │ │ ├── routerVo.go │ │ │ └── systemVo.go │ │ ├── tenant.go │ │ ├── notice.go │ │ ├── upload.go │ │ ├── config.go │ │ └── api.go │ └── services │ │ ├── role_organization.go │ │ └── notice.go ├── develop │ ├── api │ │ ├── vo │ │ │ └── tableVo.go │ │ └── gen.go │ ├── router │ │ └── gen.go │ └── entity │ │ └── dev_gen_table.go ├── rule │ ├── entity │ │ ├── rulechain_data.go │ │ └── rulechain.go │ ├── api │ │ └── rulechain_log.go │ ├── router │ │ └── rulechain_log.go │ └── services │ │ └── rulechain_log.go ├── log │ ├── entity │ │ ├── log_oper.go │ │ └── log_login.go │ ├── api │ │ ├── log_oper.go │ │ └── log_login.go │ ├── services │ │ ├── log_oper.go │ │ └── log_login.go │ └── router │ │ └── oper_log.go └── device │ ├── entity │ └── device_vo.go │ ├── api │ ├── device_alarm.go │ └── device_cmd.go │ ├── router │ ├── device_cmd.go │ └── device_alarm.go │ ├── util │ └── device_rpc.go │ └── tsl │ └── tsl.go ├── uploads └── file │ ├── 0ab71ae4a9b9b6f045d43be8de89344d_20231005105659.zip │ ├── 1df420e901be965018e95bac136ec17f_20231012191851.jpg │ ├── 626213519d0907c2f2ab12919fbd7779_20230414142538.png │ ├── 9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png │ ├── dc49aad85da7ae4c37374bf79e29134c_20230414144346.png │ └── visual │ └── e9cc37b4b094c1510624894650b2dca7_20230913093840.gif ├── iothub ├── server │ ├── httpserver │ │ ├── const.go │ │ └── http_server.go │ ├── udpserver │ │ └── udp_server.go │ ├── emqxserver │ │ ├── grpc_server.go │ │ └── const.go │ └── tcpserver │ │ └── tcp_server.go ├── netbase │ └── iothub_session.go ├── iothub.go ├── client │ ├── mqttclient │ │ └── rpc.go │ ├── tcpclient │ │ └── tcp.go │ ├── udpclient │ │ └── udp.go │ └── updclient │ │ └── udp.go ├── hook_message_work │ └── hook_service.go └── README.md ├── deploy ├── manifest-server │ ├── namespace.yaml │ ├── kustomization.yaml │ ├── rbac.yaml │ ├── tdengine │ │ └── deployment-service.yaml │ ├── mysql │ │ └── deployment-service.yaml │ ├── redis │ │ └── deployment-service.yaml │ └── emqx │ │ └── deployment-service.yaml ├── manifest │ ├── kustomization.yaml │ └── pandax │ │ ├── pandaxweb-deployment-service.yaml │ │ ├── pandaxrule-deployment-service.yaml │ │ ├── pandaxscreen-deployment-service.yaml │ │ └── pandax-deployment-service.yaml ├── configmap.yaml ├── README.md └── docker-compose.yaml ├── .gitignore ├── Dockerfile ├── startup.sh └── config.yml /shutdown.bat: -------------------------------------------------------------------------------- 1 | taskkill /im /f /t pandax.exe 2 | taskkill /im /f /t go.exe -------------------------------------------------------------------------------- /shutdown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pkill pandax 3 | pkill go 4 | pkill debug__ 5 | -------------------------------------------------------------------------------- /resource/excel/字典_20211201154616.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/resource/excel/字典_20211201154616.xlsx -------------------------------------------------------------------------------- /pkg/config/ys.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Ys struct { 4 | AppKey string `yaml:"appKey"` 5 | Secret string `yaml:"secret"` 6 | } 7 | -------------------------------------------------------------------------------- /apps/job/api/from/job.go: -------------------------------------------------------------------------------- 1 | package from 2 | 3 | type JobStatus struct { 4 | JobId string `json:"jobId"` 5 | Status string `json:"status"` 6 | } 7 | -------------------------------------------------------------------------------- /pkg/config/casbin.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Casbin struct { 4 | ModelPath string `mapstructure:"model-path" json:"model-path" yaml:"model-path"` 5 | } 6 | -------------------------------------------------------------------------------- /uploads/file/0ab71ae4a9b9b6f045d43be8de89344d_20231005105659.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/0ab71ae4a9b9b6f045d43be8de89344d_20231005105659.zip -------------------------------------------------------------------------------- /uploads/file/1df420e901be965018e95bac136ec17f_20231012191851.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/1df420e901be965018e95bac136ec17f_20231012191851.jpg -------------------------------------------------------------------------------- /uploads/file/626213519d0907c2f2ab12919fbd7779_20230414142538.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/626213519d0907c2f2ab12919fbd7779_20230414142538.png -------------------------------------------------------------------------------- /uploads/file/9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png -------------------------------------------------------------------------------- /uploads/file/dc49aad85da7ae4c37374bf79e29134c_20230414144346.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/dc49aad85da7ae4c37374bf79e29134c_20230414144346.png -------------------------------------------------------------------------------- /iothub/server/httpserver/const.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | const ( 4 | Row = `row` 5 | Telemetry = `telemetry` 6 | Attributes = `attributes` 7 | Rpc = `rpc` 8 | ) 9 | -------------------------------------------------------------------------------- /uploads/file/visual/e9cc37b4b094c1510624894650b2dca7_20230913093840.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/PandaX/master/uploads/file/visual/e9cc37b4b094c1510624894650b2dca7_20230913093840.gif -------------------------------------------------------------------------------- /deploy/manifest-server/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: pandax 6 | app.kubernetes.io/version: master 7 | name: pandax -------------------------------------------------------------------------------- /pkg/config/queue.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Queue struct { 4 | QueuePool int64 `yaml:"queue-pool"` //消息队列池 5 | TaskNum int64 `yaml:"task-num"` //任务队列数 6 | ChNum int64 `yaml:"ch-num"` //并发数 7 | } 8 | -------------------------------------------------------------------------------- /pkg/config/redis.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Redis struct { 4 | Host string `yaml:"host"` 5 | Port int `yaml:"port"` 6 | Password string `yaml:"password"` 7 | Db int `yaml:"db"` 8 | } 9 | -------------------------------------------------------------------------------- /pkg/config/app.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | type App struct { 6 | Name string `yaml:"name"` 7 | Version string `yaml:"version"` 8 | } 9 | 10 | func (a *App) GetAppInfo() string { 11 | return fmt.Sprintf("[%s:%s]", a.Name, a.Version) 12 | } 13 | -------------------------------------------------------------------------------- /apps/system/entity/role_organization.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type SysRoleOrganization struct { 4 | RoleId int64 `gorm:"type:int"` 5 | OrganizationId int64 `gorm:"type:int"` 6 | Id int64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id" form:"id"` 7 | } 8 | -------------------------------------------------------------------------------- /pkg/config/oss.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Oss struct { 4 | Endpoint string `yaml:"endpoint"` 5 | AccessKey string `yaml:"accessKey"` 6 | SecretKey string `yaml:"secretKey"` 7 | BucketName string `yaml:"bucketName"` 8 | UseSSL bool `yaml:"useSSL"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/config/taos.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Taos struct { 4 | Host string `yaml:"host"` // 服务器地址:端口 5 | Username string `yaml:"username"` // 数据库用户名 6 | Password string `yaml:"password"` // 数据库密码 7 | Database string `yaml:"database"` 8 | Config string `yaml:"config"` 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.idea 8 | # Test binary, built with `go test -c` 9 | *.test 10 | .idea 11 | pandax 12 | static 13 | 14 | *.git 15 | 16 | /fatal 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out -------------------------------------------------------------------------------- /pkg/config/gen.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | /** 4 | * @Description 5 | * @Author Panda 6 | * @Date 2021/12/31 15:13 7 | **/ 8 | type Gen struct { 9 | Dbname string `mapstructure:"dbname" json:"dbname" yaml:"dbname"` 10 | Frontpath string `mapstructure:"frontpath" json:"frontpath" yaml:"frontpath"` 11 | } 12 | -------------------------------------------------------------------------------- /resource/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [policy_effect] 8 | e = some(where (p.eft == allow)) 9 | 10 | [matchers] 11 | m = r.sub == p.sub && (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) && ( r.act == p.act || p.act == '*') -------------------------------------------------------------------------------- /deploy/manifest/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - pandax/pandax-deployment-service.yaml 3 | - pandax/pandaxweb-deployment-service.yaml 4 | - pandax/pandaxrule-deployment-service.yaml 5 | - pandax/pandaxscreen-deployment-service.yaml 6 | 7 | commonLabels: 8 | app.kubernetes.io/version: 1.0.0 9 | namespace: pandax 10 | -------------------------------------------------------------------------------- /apps/develop/api/vo/tableVo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "pandax/apps/develop/entity" 4 | 5 | /** 6 | * @Description 7 | * @Author 熊猫 8 | * @Date 2022/8/4 15:52 9 | **/ 10 | 11 | type TableInfoVo struct { 12 | List []entity.DevGenTableColumn `json:"list"` 13 | Info entity.DevGenTable `json:"info"` 14 | } 15 | -------------------------------------------------------------------------------- /deploy/manifest-server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - namespace.yaml 3 | - rbac.yaml 4 | - mysql/deployment-service.yaml 5 | - redis/deployment-service.yaml 6 | - emqx/deployment-service.yaml 7 | - tdengine/deployment-service.yaml 8 | 9 | commonLabels: 10 | app.kubernetes.io/version: 1.0.0 11 | namespace: pandax 12 | -------------------------------------------------------------------------------- /apps/job/jobs/type.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import "github.com/robfig/cron/v3" 4 | 5 | type Job interface { 6 | Run() 7 | addJob(*cron.Cron) (int, error) 8 | } 9 | 10 | type JobsExec interface { 11 | Exec(arg any, content any) error 12 | } 13 | 14 | func CallExec(e JobsExec, arg any, content any) error { 15 | return e.Exec(arg, content) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/transport/transport.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Type string 8 | 9 | const ( 10 | TypeHTTP Type = "HTTP" 11 | TypeGRPC Type = "GRPC" 12 | ) 13 | 14 | // Server is transport server. 15 | type Server interface { 16 | Type() Type 17 | Start(context.Context) error 18 | Stop(context.Context) error 19 | } 20 | -------------------------------------------------------------------------------- /apps/system/entity/role_menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type SysRoleMenu struct { 4 | RoleId int64 `gorm:"type:int"` 5 | MenuId int64 `gorm:"type:int"` 6 | RoleName string `gorm:"type:varchar(128)"` 7 | Id int64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id" form:"id"` 8 | } 9 | 10 | type MenuPath struct { 11 | Path string `json:"path"` 12 | } 13 | -------------------------------------------------------------------------------- /pkg/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "gorm.io/gorm" 6 | "pandax/pkg/config" 7 | "pandax/pkg/events" 8 | "pandax/pkg/tdengine" 9 | ) 10 | 11 | var ( 12 | Log *logrus.Logger // 日志 13 | Db *gorm.DB // gorm 14 | TdDb *tdengine.TdEngine 15 | Conf *config.Config 16 | ) 17 | var EventEmitter = events.EventEmitter{} 18 | -------------------------------------------------------------------------------- /deploy/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: emqx-deployment 5 | data: 6 | NAME: "emqx" 7 | CLUSTER__DISCOVERY: "k8s" 8 | CLUSTER__K8S__ADDRESS_TYPE: "ip" 9 | CLUSTER__K8S__APISERVER: "https://IP:PORT" 10 | CLUSTER__K8S__NAMESPACE: "default" 11 | CLUSTER__K8S__SERVICE_NAME: "emqx-deployment" 12 | CLUSTER__K8S__APP_NAME: "emqx" -------------------------------------------------------------------------------- /pkg/config/jwt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | ) 6 | 7 | type Jwt struct { 8 | Key string `yaml:"key"` 9 | ExpireTime int64 `yaml:"expire-time"` // 过期时间,单位分钟 10 | } 11 | 12 | func (j *Jwt) Valid() { 13 | biz.IsTrue(j.Key != "", "config.yml之 [jwt.key] 不能为空") 14 | biz.IsTrue(j.ExpireTime != 0, "config.yml之 [jwt.expire-time] 不能为空") 15 | } 16 | -------------------------------------------------------------------------------- /apps/system/router/system.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/emicklei/go-restful/v3" 5 | "pandax/apps/system/api" 6 | ) 7 | 8 | func InitSystemRouter(container *restful.Container) { 9 | s := &api.System{} 10 | ws := new(restful.WebService) 11 | ws.Path("/system").Produces(restful.MIME_JSON) 12 | ws.Route(ws.GET("/").To(s.ConnectWs)) 13 | ws.Route(ws.GET("/server").To(s.ServerInfo)) 14 | container.Add(ws) 15 | } 16 | -------------------------------------------------------------------------------- /apps/job/jobs/examples.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | type CronDeviceHandle struct { 8 | } 9 | 10 | func (t CronDeviceHandle) Exec(arg any, content any) error { 11 | log.Println("执行设备任务", arg, content) 12 | 13 | return nil 14 | } 15 | 16 | type CronProductHandle struct { 17 | } 18 | 19 | func (t CronProductHandle) Exec(arg any, content any) error { 20 | log.Println("执行产品任务", arg, content) 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /pkg/config/mqtt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Mqtt struct { 4 | Broker string `mapstructure:"broker" json:"broker" yaml:"broker"` 5 | HttpBroker string `mapstructure:"httpBroker" json:"httpBroker" yaml:"httpBroker"` 6 | Qos int `mapstructure:"qos" json:"qos" yaml:"qos"` 7 | Username string `mapstructure:"username" json:"username" yaml:"username"` 8 | Password string `mapstructure:"password" json:"password" yaml:"password"` 9 | } 10 | -------------------------------------------------------------------------------- /pkg/tool/base_test.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/utils" 5 | "testing" 6 | ) 7 | 8 | func TestToCamelCase(t *testing.T) { 9 | camelCase := ToCamelCase("hello_world") 10 | t.Log(camelCase) 11 | } 12 | 13 | func TestGenerateID(t *testing.T) { 14 | id := utils.GenerateID("") 15 | t.Log(id) 16 | } 17 | 18 | func TestGetInterfaceType(t *testing.T) { 19 | id := GetInterfaceType(`{"aa": 23}`) 20 | t.Log(id) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/template_engine.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | func ParseTemplate(content string, data map[string]interface{}) (string, error) { 9 | tmpl, err := template.New("template").Parse(content) 10 | if err != nil { 11 | return "", err 12 | } 13 | buffer := &bytes.Buffer{} 14 | err = tmpl.Execute(buffer, data) 15 | if err != nil { 16 | return "", err 17 | } 18 | return buffer.String(), nil 19 | } 20 | -------------------------------------------------------------------------------- /pkg/middleware/escape_html.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/emicklei/go-restful/v3" 5 | "html" 6 | ) 7 | 8 | // 防止XSS攻击 9 | func EscapeHTML(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { 10 | // 获取请求参数中的HTML标签 11 | for _, p := range req.Request.URL.Query() { 12 | escaped := html.EscapeString(p[0]) 13 | // 将转义后的参数重新设置到请求参数中 14 | req.Request.URL.Query().Set(p[0], escaped) 15 | } 16 | chain.ProcessFilter(req, resp) 17 | } 18 | -------------------------------------------------------------------------------- /deploy/manifest-server/rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: pandax 5 | labels: 6 | app.kubernetes.io/name: pandax 7 | --- 8 | kind: ClusterRoleBinding 9 | apiVersion: rbac.authorization.k8s.io/v1 10 | metadata: 11 | name: pandax-rolebinding 12 | labels: 13 | app.kubernetes.io/name: pandax 14 | roleRef: 15 | apiGroup: rbac.authorization.k8s.io 16 | kind: ClusterRole 17 | name: cluster-admin 18 | subjects: 19 | - kind: ServiceAccount 20 | name: pandax 21 | -------------------------------------------------------------------------------- /apps/system/api/form/role.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | // 分配角色资源表单信息 4 | type RoleResourceForm struct { 5 | Id int 6 | ResourceIds string 7 | } 8 | 9 | // 保存角色信息表单 10 | type RoleForm struct { 11 | Id int 12 | Status int `json:"status"` // 1:可用;-1:不可用 13 | Name string `binding:"required"` 14 | Code string `binding:"required"` 15 | Remark string `json:"remark"` 16 | } 17 | 18 | // 账号分配角色表单 19 | type AccountRoleForm struct { 20 | Id int `binding:"required"` 21 | RoleIds string 22 | } 23 | -------------------------------------------------------------------------------- /apps/system/entity/api.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysApi struct { 6 | model.BaseAutoModel 7 | Path string `json:"path" gorm:"comment:api路径" binding:"required"` // api路径 8 | Description string `json:"description" gorm:"comment:api中文描述"` // api中文描述 9 | ApiGroup string `json:"apiGroup" gorm:"comment:api组"` // api组 10 | Method string `json:"method" gorm:"comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE 11 | } 12 | -------------------------------------------------------------------------------- /apps/job/entity/log_job.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "pandax/pkg/global/model" 5 | ) 6 | 7 | type JobLog struct { 8 | model.BaseAuthModel 9 | Name string `json:"name" gorm:"type:varchar(128);comment:任务名称"` 10 | EntryId int `json:"entryId" gorm:"type:int;comment:任务id"` 11 | TargetInvoke string `json:"targetInvoke" gorm:"type:varchar(128);comment:调用方法"` 12 | LogInfo string `json:"logInfo" gorm:"type:varchar(255);comment:日志信息"` 13 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"` 14 | } 15 | -------------------------------------------------------------------------------- /apps/system/entity/tenant.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "time" 6 | ) 7 | 8 | /** 9 | * @Description 10 | * @Author 熊猫 11 | * @Date 2022/7/14 16:14 12 | **/ 13 | 14 | type SysTenants struct { 15 | model.BaseAutoModel 16 | TenantName string `json:"tenantName" gorm:"type:varchar(255);comment:租户名"` 17 | Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` 18 | ExpireTime time.Time `json:"expireTime" gorm:"comment:过期时间"` 19 | } 20 | 21 | func (SysTenants) TableName() string { 22 | return "sys_tenants" 23 | } 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23.0-alpine AS builder 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | RUN go env -w GOPROXY=https://goproxy.cn,direct && go mod download 7 | RUN go build -o pandax 8 | 9 | FROM alpine:latest 10 | LABEL MAINTAINER="PandaX" 11 | 12 | WORKDIR /pandax 13 | COPY --from=builder /app/pandax ./ 14 | COPY --from=builder /app/config.yml ./ 15 | COPY --from=builder /app/resource ./resource 16 | COPY --from=builder /app/uploads ./uploads 17 | 18 | RUN chmod 755 ./pandax 19 | 20 | EXPOSE 7788 21 | EXPOSE 9001 22 | EXPOSE 9002 23 | EXPOSE 9003 24 | EXPOSE 9003/udp 25 | 26 | ENTRYPOINT ./pandax -------------------------------------------------------------------------------- /pkg/cache/product_rule.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/cache" 5 | "time" 6 | ) 7 | 8 | var ProductCache = cache.NewTimedCache(cache.NoExpiration, 24*time.Hour) 9 | 10 | func ComputeIfAbsentProductRule(key string, fun func(any) (any, error)) (any, error) { 11 | return ProductCache.ComputeIfAbsent(key, fun) 12 | } 13 | 14 | func GetProductRule(key string) (any, bool) { 15 | return ProductCache.Get(key) 16 | } 17 | 18 | func DelProductRule(key string) { 19 | ProductCache.Delete(key) 20 | } 21 | 22 | func PutProductRule(key string, data any) { 23 | ProductCache.Put(key, data) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/tdengine/tdengine_rule_debug.go: -------------------------------------------------------------------------------- 1 | package tdengine 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const debugTableName = "device_rule_debug" 8 | 9 | func (s *TdEngine) CreateDeviceRuleDebugTable() (err error) { 10 | sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.%s (ts TIMESTAMP,nodeId NCHAR(64),msgd NCHAR(64),debugType NCHAR(64), 11 | deviceName NCHAR(64),msgType NCHAR(64),msg VARCHAR,metadata VARCHAR,error VARCHAR)`, s.dbName, debugTableName) 12 | _, err = s.db.Exec(sql) 13 | return 14 | } 15 | 16 | func (s *TdEngine) InsertRuleDebug(data map[string]any) (err error) { 17 | return s.InsertDevice(debugTableName, data) 18 | } 19 | -------------------------------------------------------------------------------- /apps/rule/entity/rulechain_data.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type RuleDataJson struct { 8 | Id string 9 | GlobalColor string `json:"globalColor"` 10 | DataCode map[string]interface{} `json:"dataCode"` 11 | OpenRule bool `json:"openRule"` 12 | Setting map[string]interface{} `json:"setting"` 13 | } 14 | 15 | // 序列化 16 | func (m *RuleDataJson) MarshalBinary() (data []byte, err error) { 17 | return json.Marshal(m) 18 | } 19 | 20 | // 反序列化 21 | func (m *RuleDataJson) UnmarshalBinary(data []byte) error { 22 | return json.Unmarshal(data, m) 23 | } 24 | -------------------------------------------------------------------------------- /iothub/netbase/iothub_session.go: -------------------------------------------------------------------------------- 1 | package netbase 2 | 3 | import ( 4 | "encoding/json" 5 | "pandax/pkg/global/model" 6 | ) 7 | 8 | type DeviceEventInfo struct { 9 | DeviceId string `json:"deviceId"` 10 | DeviceAuth *model.DeviceAuth `json:"deviceAuth"` 11 | Datas string `json:"datas"` 12 | Type string `json:"type"` 13 | RequestId string `json:"requestId"` // rpc 请求ID 14 | Identifier string `json:"identifier"` //事件标识 15 | } 16 | 17 | func (j *DeviceEventInfo) Bytes() []byte { 18 | b, err := json.Marshal(j) 19 | if err != nil { 20 | return nil 21 | } 22 | return b 23 | } 24 | -------------------------------------------------------------------------------- /pkg/config/log.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "path" 4 | 5 | type Log struct { 6 | Level string `yaml:"level"` 7 | File *LogFile `yaml:"file"` 8 | } 9 | 10 | type LogFile struct { 11 | Name string `yaml:"name"` 12 | Path string `yaml:"path"` 13 | } 14 | 15 | // 获取完整路径文件名 16 | func (l *LogFile) GetFilename() string { 17 | var filepath, filename string 18 | if l == nil { 19 | return "" 20 | } 21 | if fp := l.Path; fp == "" { 22 | filepath = "./" 23 | } else { 24 | filepath = fp 25 | } 26 | if fn := l.Name; fn == "" { 27 | filename = "default.log" 28 | } else { 29 | filename = fn 30 | } 31 | 32 | return path.Join(filepath, filename) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/emicklei/go-restful/v3" 5 | ) 6 | 7 | // 处理跨域请求,支持options访问 8 | func Cors(wsContainer *restful.Container) restful.CrossOriginResourceSharing { 9 | cors := restful.CrossOriginResourceSharing{ 10 | ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"}, 11 | AllowedHeaders: []string{"Content-Type", "AccessToken", "X-CSRF-Token", "Authorization", "Token", "X-Token", "X-User-Id"}, 12 | AllowedMethods: []string{"POST", "GET", "OPTIONS", "DELETE", "PUT"}, 13 | CookiesAllowed: false, 14 | Container: wsContainer} 15 | 16 | return cors 17 | } 18 | -------------------------------------------------------------------------------- /apps/system/entity/notice.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysNotice struct { 6 | NoticeId int64 `json:"noticeId" gorm:"primary_key;AUTO_INCREMENT"` 7 | Title string `json:"title" gorm:"type:varchar(128);comment:标题"` 8 | Content string `json:"content" gorm:"type:text;comment:标题"` 9 | NoticeType string `json:"noticeType" gorm:"type:varchar(1);comment:通知类型"` 10 | OrganizationId int64 `json:"organizationId" gorm:"type:int;comment:组织Id,组织及子组织"` 11 | UserName string `json:"userName" gorm:"type:varchar(64);comment:发布人"` 12 | 13 | OrganizationIds []int64 `json:"organizationIds" gorm:"-"` 14 | model.BaseModel 15 | } 16 | -------------------------------------------------------------------------------- /apps/system/entity/post.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysPost struct { 6 | PostId int64 `gorm:"primary_key;AUTO_INCREMENT" json:"postId"` 7 | PostName string `gorm:"type:varchar(128);comment:岗位名称" json:"postName"` 8 | PostCode string `gorm:"type:varchar(128);comment:岗位代码" json:"postCode"` 9 | Sort int64 `gorm:"type:int;comment:岗位排序" json:"sort"` 10 | Status string `gorm:"type:varchar(1);comment:状态" json:"status"` 11 | Remark string `gorm:"type:varchar(255);comment:描述" json:"remark"` 12 | CreateBy string `gorm:"type:varchar(128);" json:"createBy"` 13 | UpdateBy string `gorm:"type:varchar(128);" json:"updateBy"` 14 | model.BaseModel 15 | } 16 | -------------------------------------------------------------------------------- /pkg/shadow/device.go: -------------------------------------------------------------------------------- 1 | package shadow 2 | 3 | import ( 4 | "pandax/apps/device/services" 5 | "pandax/pkg/global" 6 | "time" 7 | ) 8 | 9 | // Device 设备结构 10 | type Device struct { 11 | Name string // 设备名称 12 | online bool // 在线状态 13 | updatedAt time.Time // 更新时间 14 | } 15 | 16 | func NewDevice(deviceName string) Device { 17 | return Device{ 18 | Name: deviceName, 19 | online: false, 20 | updatedAt: time.Now(), 21 | } 22 | } 23 | 24 | func deviceHandler(deviceName string, online bool) { 25 | if online { 26 | services.DeviceModelDao.UpdateStatus(deviceName, global.ONLINE) 27 | } else { 28 | services.DeviceModelDao.UpdateStatus(deviceName, global.OFFLINE) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/middleware/rate.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/didip/tollbooth" 5 | "github.com/emicklei/go-restful/v3" 6 | "pandax/pkg/global" 7 | ) 8 | 9 | /** 10 | * @Description 添加qq群467890197 交流学习 11 | * @Author 熊猫 12 | * @Date 2022/1/19 8:28 13 | **/ 14 | 15 | //Rate 限流中间件 16 | func Rate(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { 17 | lmt := tollbooth.NewLimiter(global.Conf.Server.Rate.RateNum, nil) 18 | lmt.SetMessage("已经超出接口请求限制,稍后再试.") 19 | httpError := tollbooth.LimitByRequest(lmt, resp, req.Request) 20 | if httpError != nil { 21 | resp.WriteErrorString(httpError.StatusCode, httpError.Message) 22 | return 23 | } 24 | chain.ProcessFilter(req, resp) 25 | } 26 | -------------------------------------------------------------------------------- /apps/system/entity/config.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysConfig struct { 6 | ConfigId int64 `json:"configId" gorm:"primaryKey;AUTO_INCREMENT;comment:主键编码"` 7 | ConfigName string `json:"configName" gorm:"type:varchar(128);comment:ConfigName"` 8 | ConfigKey string `json:"configKey" gorm:"type:varchar(128);comment:ConfigKey"` 9 | ConfigValue string `json:"configValue" gorm:"type:varchar(255);comment:ConfigValue"` 10 | ConfigType string `json:"configType" gorm:"type:varchar(64);comment:是否系统内置0,1"` 11 | IsFrontend string `json:"isFrontend" gorm:"type:varchar(1);comment:是否前台"` 12 | Remark string `json:"remark" gorm:"type:varchar(128);comment:Remark"` 13 | model.BaseModel 14 | } 15 | -------------------------------------------------------------------------------- /pkg/device_rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package devicerpc 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/kakuilan/kgo" 9 | ) 10 | 11 | type RpcPayload struct { 12 | Method string `json:"method"` 13 | Params any `json:"params"` 14 | } 15 | 16 | func (rp *RpcPayload) ToMap() map[string]any { 17 | data, err := kgo.KConv.Struct2Map(rp, "") 18 | if err != nil { 19 | return nil 20 | } 21 | return data 22 | } 23 | 24 | // GetRequestResult 处理设备端请求服务端方法 25 | func (rpc RpcPayload) GetRequestResult() (string, error) { 26 | //TODO 此处处理设备的请求参数逻辑 27 | //自己定义请求逻辑 28 | if rpc.Params == "getCurrentTime" { 29 | unix := time.Now().Unix() 30 | msg := fmt.Sprintf("%d", unix) 31 | return msg, nil 32 | } 33 | // 获取属性 ... 34 | return "", errors.New("未获取到请求方法") 35 | } 36 | -------------------------------------------------------------------------------- /pkg/cache/device_etoken.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "context" 5 | "github.com/PandaXGO/PandaKit/rediscli" 6 | "time" 7 | ) 8 | 9 | var RedisDb *rediscli.RedisDB 10 | 11 | // SetDeviceEtoken key 是设备的时候为token, 是子设备的时候为设备编码 12 | func SetDeviceEtoken(key string, value any, duration time.Duration) error { 13 | return RedisDb.Set(key, value, duration) 14 | } 15 | 16 | // GetDeviceEtoken value 是参数指针 17 | func GetDeviceEtoken(key string, value interface{}) error { 18 | return RedisDb.Get(key, value) 19 | } 20 | 21 | // DelDeviceEtoken 删除指定的key 22 | func DelDeviceEtoken(key string) error { 23 | return RedisDb.Del(context.Background(), key).Err() 24 | } 25 | 26 | func ExistsDeviceEtoken(key string) bool { 27 | exists, _ := RedisDb.Exists(RedisDb.Context(), key).Result() 28 | return exists == 1 29 | } 30 | -------------------------------------------------------------------------------- /iothub/iothub.go: -------------------------------------------------------------------------------- 1 | package iothub 2 | 3 | import ( 4 | "fmt" 5 | "pandax/iothub/hook_message_work" 6 | "pandax/iothub/server/emqxserver" 7 | "pandax/iothub/server/httpserver" 8 | "pandax/iothub/server/tcpserver" 9 | updserver "pandax/iothub/server/udpserver" 10 | "pandax/pkg/global" 11 | ) 12 | 13 | func InitIothub() { 14 | service := hook_message_work.NewHookService() 15 | // 初始化EMQX 16 | go emqxserver.InitEmqxHook(fmt.Sprintf(":%d", global.Conf.Server.GrpcPort), service) 17 | // 初始化HTTP 18 | go httpserver.InitHttpHook(fmt.Sprintf(":%d", global.Conf.Server.HttpPort), service) 19 | //初始化TCP 20 | go tcpserver.InitTcpHook(fmt.Sprintf(":%d", global.Conf.Server.TcpPort), service) 21 | 22 | go updserver.InitUdpHook(fmt.Sprintf(":%d", global.Conf.Server.TcpPort), service) 23 | // 开启线程处理消息 24 | go service.MessageWork() 25 | } 26 | -------------------------------------------------------------------------------- /pkg/global/model/global_model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type BaseModel struct { 8 | Id string `json:"id" gorm:"primary_key;"` 9 | CreatedAt time.Time `gorm:"column:create_time" json:"createTime" form:"create_time"` 10 | UpdatedAt time.Time `gorm:"column:update_time" json:"updateTime" form:"update_time"` 11 | } 12 | 13 | type BaseAuthModel struct { 14 | Id string `json:"id" gorm:"primary_key;"` 15 | Owner string `json:"owner" gorm:"type:varchar(64);comment:创建者,所有者"` 16 | OrgId int64 `json:"orgId" gorm:"type:int;comment:机构ID"` 17 | CreatedAt time.Time `gorm:"column:create_time" json:"createTime" form:"create_time"` 18 | UpdatedAt time.Time `gorm:"column:update_time" json:"updateTime" form:"update_time"` 19 | 20 | RoleId int64 `gorm:"-"` // 角色数据权限 21 | } 22 | -------------------------------------------------------------------------------- /apps/develop/api/gen.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/restfulx" 5 | "pandax/apps/develop/gen" 6 | "pandax/apps/develop/services" 7 | ) 8 | 9 | type GenApi struct { 10 | GenTableApp services.SysGenTableModel 11 | } 12 | 13 | // Preview 代码视图 14 | func (e *GenApi) Preview(rc *restfulx.ReqCtx) { 15 | tableId := restfulx.PathParamInt(rc, "tableId") 16 | rc.ResData = gen.Preview(int64(tableId)) 17 | } 18 | 19 | // GenCode 代码生成 20 | func (e *GenApi) GenCode(rc *restfulx.ReqCtx) { 21 | tableId := restfulx.PathParamInt(rc, "tableId") 22 | gen.GenCode(int64(tableId)) 23 | } 24 | 25 | // GenConfigure 配置生成 26 | func (e *GenApi) GenConfigure(rc *restfulx.ReqCtx) { 27 | tableId := restfulx.PathParamInt(rc, "tableId") 28 | menuId := restfulx.QueryInt(rc, "menuId", 0) 29 | gen.GenConfigure(tableId, menuId) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/tdengine/TDengineModel.go: -------------------------------------------------------------------------------- 1 | package tdengine 2 | 3 | import "time" 4 | 5 | type TDEngineTablesList struct { 6 | TableName string `json:"tableName" description:"表名"` 7 | DbName string `json:"dbName" description:"数据库名"` 8 | StableName string `json:"stableName" description:"超级表名"` 9 | CreateTime *time.Time `json:"createTime" description:"创建时间"` 10 | } 11 | 12 | type TDEngineTableInfo struct { 13 | Field string `json:"field" description:"字段名"` 14 | Type string `json:"type" description:"类型"` 15 | Length int `json:"length" description:"长度"` 16 | Note string `json:"note" description:"note"` 17 | } 18 | 19 | type TableDataInfo struct { 20 | Filed []string `json:"filed" description:"字段"` 21 | Info []map[string]interface{} `json:"info" description:"数据"` 22 | } 23 | -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo snap install docker go 3 | sudo systemctl start docker 4 | sudo systemctl enable docker 5 | 6 | docker run -itd --name mysql --restart=always -p 3306:3306 -e MYSQL_ROOT_PASSWORD=!MyEMS1 mysql:8.0.23 7 | docker run -itd --name tdengine --restart=always -p 6030:6030 -p 6041:6041 -p 6043-6049:6043-6049 -p 6043-6049:6043-6049/udp tdengine/tdengine:3.0.4.2 8 | docker run -itd -p 6379:6379 --restart=always --name redis redis:7.0.4 --requirepass root 9 | docker run -d --name emqx --restart=always -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:5.1.0 10 | 11 | docker cp resource/pandax_iot.sql mysql:/root/pandax_iot.sql 12 | docker exec mysql mysql -uroot -p!MyEMS1 pandax_iot < /root/pandax_iot.sql 13 | 14 | go env -w GO111MODULE=on 15 | go env -w GOPROXY=https://goproxy.cn,direct 16 | 17 | go mod tidy 18 | go run main.go -------------------------------------------------------------------------------- /apps/job/entity/job.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "pandax/pkg/global/model" 5 | ) 6 | 7 | type SysJob struct { 8 | model.BaseAuthModel 9 | JobName string `json:"jobName" gorm:"type:varchar(255);"` // 名称 10 | TargetInvoke string `json:"targetInvoke" gorm:"type:varchar(64);comment:目标类型"` //调用目标 设备还是产品 11 | TargetArgs string `json:"targetArgs" gorm:"type:varchar(64);comment:目标Id"` //目标传参 设备或者产品id 12 | JobContent string `json:"jobContent" gorm:"type:json;comment:任务内容"` //目标传参 要执行的内容 13 | CronExpression string `json:"cronExpression" gorm:"type:varchar(255);"` // cron表达式 14 | MisfirePolicy string `json:"misfirePolicy" gorm:"type:varchar(1);"` // 执行策略 15 | Status string `json:"status" gorm:"type:varchar(1);"` // 状态 16 | EntryId int `json:"entryId" gorm:"type:int;"` // job启动时返回的id 17 | 18 | } 19 | -------------------------------------------------------------------------------- /apps/system/api/vo/metaVo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | /**s 4 | * 路由meta对象参数说明 5 | * meta: { 6 | * title: 菜单栏及 tagsView 栏、菜单搜索名称 7 | * isLink: 是否超链接菜单,开启外链条件,`1、isLink:true 2、链接地址不为空` 8 | * isHide: 是否隐藏此路由 9 | * isKeepAlive: 是否缓存组件状态 10 | * isAffix: 是否固定在 tagsView 栏上 11 | * isFrame: 是否内嵌窗口,,开启条件,`1、isFrame:true 2、链接地址不为空` 12 | * auth: 当前路由权限标识(多个请用逗号隔开),最后转成数组格式,用于与当前用户权限进行对比,控制路由显示、隐藏 13 | * icon: 菜单、tagsView 图标,阿里:加 `iconfont xxx`,fontawesome:加 `fa xxx` 14 | * } 15 | */ 16 | type MetaVo struct { 17 | Title string `json:"title"` 18 | IsLink string `json:"isLink"` 19 | IsHide bool `json:"isHide"` 20 | IsKeepAlive bool `json:"isKeepAlive"` 21 | IsAffix bool `json:"isAffix"` 22 | IsIframe bool `json:"isIframe"` 23 | Auth []string `json:"auth"` 24 | Icon string `json:"icon"` 25 | } 26 | -------------------------------------------------------------------------------- /deploy/manifest-server/tdengine/deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: tdengine-service 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: 6030 9 | targetPort: 6030 10 | protocol: TCP 11 | - port: 6041 12 | targetPort: 6041 13 | protocol: TCP 14 | selector: 15 | app: tdengine 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: tdengine 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: tdengine 26 | strategy: 27 | type: Recreate 28 | template: 29 | metadata: 30 | labels: 31 | app: tdengine 32 | spec: 33 | containers: 34 | - image: tdengine/tdengine:3.0.4.2 35 | name: tdengine 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 6030 39 | protocol: TCP 40 | - containerPort: 6041 41 | protocol: TCP -------------------------------------------------------------------------------- /apps/log/entity/log_oper.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | ) 6 | 7 | type LogOper struct { 8 | OperId int64 `json:"operId" gorm:"primary_key;AUTO_INCREMENT"` //主键 9 | Title string `json:"title" gorm:"type:varchar(128);comment:操作的模块"` 10 | BusinessType string `json:"businessType" gorm:"type:varchar(1);comment:0其它 1新增 2修改 3删除"` 11 | Method string `json:"method" gorm:"type:varchar(255);comment:请求方法"` 12 | OperName string `json:"operName" gorm:"type:varchar(255);comment:操作人员"` 13 | OperUrl string `json:"operUrl" gorm:"type:varchar(255);comment:操作url"` 14 | OperIp string `json:"operIp" gorm:"type:varchar(255);comment:操作IP"` 15 | OperLocation string `json:"operLocation" gorm:"type:varchar(255);comment:操作地点"` 16 | OperParam string `json:"operParam" gorm:"type:varchar(255);comment:请求参数"` // 17 | Status string `json:"status" gorm:"type:varchar(1);comment:0=正常,1=异常"` 18 | model.BaseModel 19 | } 20 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/input_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "pandax/pkg/rule_engine/message" 6 | ) 7 | 8 | const InputNodeName = "InputNode" 9 | 10 | type inputNode struct { 11 | bareNode 12 | } 13 | 14 | type inputNodeFactory struct{} 15 | 16 | func (f inputNodeFactory) Name() string { return "InputNode" } 17 | func (f inputNodeFactory) Category() string { return NODE_CATEGORY_OTHERS } 18 | func (f inputNodeFactory) Labels() []string { return []string{"True"} } 19 | func (f inputNodeFactory) Create(id string, meta Properties) (Node, error) { 20 | node := &inputNode{ 21 | bareNode: newBareNode(InputNodeName, id, meta, f.Labels()), 22 | } 23 | return node, nil 24 | } 25 | 26 | func (n *inputNode) Handle(msg *message.Message) error { 27 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType) 28 | 29 | nodes := n.GetLinkedNodes() 30 | for _, node := range nodes { 31 | return node.Handle(msg) 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /iothub/server/udpserver/udp_server.go: -------------------------------------------------------------------------------- 1 | package updserver 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "pandax/pkg/global" 7 | ) 8 | 9 | const DefaultPort = ":9003" 10 | 11 | type UdpServer struct { 12 | Addr string 13 | listener *net.UDPConn 14 | } 15 | 16 | func NewUdpServer(addr string) *UdpServer { 17 | if addr == "" { 18 | addr = DefaultPort 19 | } 20 | return &UdpServer{ 21 | Addr: addr, 22 | } 23 | } 24 | 25 | func (s *UdpServer) GetServe() *net.UDPConn { 26 | return s.listener 27 | } 28 | 29 | func (s *UdpServer) Type() string { 30 | return "UDP" 31 | } 32 | 33 | func (s *UdpServer) Start(ctx context.Context) error { 34 | addr, _ := net.ResolveUDPAddr("udp", s.Addr) 35 | listener, err := net.ListenUDP("udp", addr) 36 | if err != nil { 37 | global.Log.Errorf("error http serve: %s", err) 38 | return err 39 | } 40 | s.listener = listener 41 | return nil 42 | } 43 | 44 | func (s *UdpServer) Stop(ctx context.Context) error { 45 | s.listener.Close() 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/websocket/socket_server_pool.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | "pandax/pkg/global" 6 | ) 7 | 8 | var Wsp = make(map[string]*Websocket) 9 | 10 | // GetWebSocketByScreenId 获取WebSocket 11 | func GetWebSocketByScreenId(screenId string) *Websocket { 12 | return Wsp[screenId] 13 | } 14 | 15 | // AddWebSocketByScreenId 添加WebSocket 16 | func AddWebSocketByScreenId(screenId string, webs *Websocket) { 17 | Wsp[screenId] = webs 18 | } 19 | 20 | // RemoveWebSocket 移除WebSocket 21 | func RemoveWebSocket(screenId string) bool { 22 | if ws, ok := Wsp[screenId]; ok { 23 | ws.Conn.Close() 24 | delete(Wsp, screenId) 25 | global.Log.Info("已经断开websocket:" + screenId) 26 | return true 27 | } 28 | return false 29 | } 30 | 31 | // SendMessage 向特定场景id发送消息,同一场景代码有可能在多台客户机上连接 ,这时就会在多台客户机接受到了数据 32 | func SendMessage(message, screenId string) { 33 | ws := GetWebSocketByScreenId(screenId) 34 | if ws != nil { 35 | ws.Conn.WriteMessage(websocket.TextMessage, []byte(message)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /deploy/manifest/pandax/pandaxweb-deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pandaxweb 5 | labels: 6 | app.kubernetes.io/name: pandaxweb 7 | spec: 8 | type: LoadBalancer 9 | ports: 10 | - port: 7788 11 | targetPort: 7789 12 | protocol: TCP 13 | selector: 14 | app.kubernetes.io/name: pandaxweb 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: pandaxweb 20 | labels: 21 | app.kubernetes.io/name: pandaxweb 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app.kubernetes.io/name: pandaxweb 27 | template: 28 | metadata: 29 | labels: 30 | app.kubernetes.io/name: pandaxweb 31 | spec: 32 | serviceAccountName: pandaxweb 33 | containers: 34 | - name: pandaxweb 35 | image: ccr.ccs.tencentyun.com/pandax/pandaxweb:v1.9.1 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 7789 39 | protocol: TCP 40 | 41 | -------------------------------------------------------------------------------- /deploy/manifest/pandax/pandaxrule-deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pandaxrule 5 | labels: 6 | app.kubernetes.io/name: pandaxrule 7 | spec: 8 | type: LoadBalancer 9 | ports: 10 | - port: 7791 11 | targetPort: 7791 12 | protocol: TCP 13 | selector: 14 | app.kubernetes.io/name: pandaxrule 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: pandaxrule 20 | labels: 21 | app.kubernetes.io/name: pandaxrule 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app.kubernetes.io/name: pandaxrule 27 | template: 28 | metadata: 29 | labels: 30 | app.kubernetes.io/name: pandaxrule 31 | spec: 32 | serviceAccountName: pandaxrule 33 | containers: 34 | - name: pandaxrule 35 | image: ccr.ccs.tencentyun.com/pandax/pandaxrule:v1.8 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 7791 39 | protocol: TCP 40 | 41 | -------------------------------------------------------------------------------- /pkg/transport/grpc_server.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "google.golang.org/grpc" 7 | "net" 8 | "pandax/pkg/global" 9 | ) 10 | 11 | type GrpcServer struct { 12 | Addr string 13 | srv *grpc.Server 14 | } 15 | 16 | func NewServer(addr string) *GrpcServer { 17 | return &GrpcServer{ 18 | Addr: addr, 19 | srv: grpc.NewServer(), 20 | } 21 | } 22 | 23 | func (s *GrpcServer) GetServe() *grpc.Server { 24 | return s.srv 25 | } 26 | 27 | func (s *GrpcServer) Type() Type { 28 | return TypeGRPC 29 | } 30 | 31 | func (s *GrpcServer) Start(ctx context.Context) error { 32 | l, err := net.Listen("tcp", s.Addr) 33 | if err != nil { 34 | return fmt.Errorf("error listen addr: %w", err) 35 | } 36 | global.Log.Debugf("GRPC Server listen: %s", s.Addr) 37 | go func() { 38 | if err := s.srv.Serve(l); err != nil { 39 | global.Log.Errorf("error http serve: %s", err) 40 | } 41 | }() 42 | return nil 43 | } 44 | 45 | func (s *GrpcServer) Stop(ctx context.Context) error { 46 | s.srv.Stop() 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /deploy/manifest/pandax/pandaxscreen-deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pandaxscreen 5 | labels: 6 | app.kubernetes.io/name: pandaxscreen 7 | spec: 8 | type: LoadBalancer 9 | ports: 10 | - port: 7790 11 | targetPort: 7790 12 | protocol: TCP 13 | selector: 14 | app.kubernetes.io/name: pandaxscreen 15 | --- 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: pandaxscreen 20 | labels: 21 | app.kubernetes.io/name: pandaxscreen 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app.kubernetes.io/name: pandaxscreen 27 | template: 28 | metadata: 29 | labels: 30 | app.kubernetes.io/name: pandaxscreen 31 | spec: 32 | serviceAccountName: pandaxscreen 33 | containers: 34 | - name: pandaxscreen 35 | image: ccr.ccs.tencentyun.com/pandax/pandaxscreen:v1.9.1 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 7790 39 | protocol: TCP 40 | 41 | -------------------------------------------------------------------------------- /iothub/client/mqttclient/rpc.go: -------------------------------------------------------------------------------- 1 | package mqttclient 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | const ( 9 | RpcRespTopic = `v1/devices/me/rpc/response/%s` 10 | RpcReqTopic = `v1/devices/me/rpc/request/%s` 11 | ) 12 | 13 | const ( 14 | SingleMode = "single" 15 | DoubleMode = "double" 16 | ) 17 | 18 | type RpcRequest struct { 19 | RequestId string 20 | Mode string //单向、双向 单项只发送不等待响应 双向需要等到响应 21 | Timeout int // 设置双向时,等待的超时时间 22 | } 23 | 24 | // RequestCmd 下发指令 25 | func (rpc RpcRequest) RequestCmd(deviceId, rpcPayload string) error { 26 | topic := fmt.Sprintf(RpcReqTopic, rpc.RequestId) 27 | value, ok := Session.Load(deviceId) 28 | if !ok { 29 | return errors.New("未获取到设备的MQTT连接") 30 | } 31 | return Publish(topic, value.(string), rpcPayload) 32 | } 33 | 34 | func (rpc RpcRequest) Pub(deviceId, reqPayload string) error { 35 | topic := fmt.Sprintf(RpcRespTopic, rpc.RequestId) 36 | value, ok := Session.Load(deviceId) 37 | if !ok { 38 | return errors.New("未获取到设备的MQTT连接") 39 | } 40 | return Publish(topic, value.(string), reqPayload) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/rule_engine/instance.go: -------------------------------------------------------------------------------- 1 | package rule_engine 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/manifest" 5 | "pandax/pkg/rule_engine/nodes" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | type RuleChainInstance struct { 11 | ruleId string 12 | firstRuleNodeID string 13 | nodes map[string]nodes.Node 14 | } 15 | 16 | func NewRuleChainInstance(ruleId string, data []byte) (*RuleChainInstance, error) { 17 | manifest, err := manifest.New(data) 18 | if err != nil { 19 | logrus.WithError(err).Errorf("invalid manifest file") 20 | return nil, err 21 | } 22 | withManifest, err := newInstanceWithManifest(manifest) 23 | if err != nil { 24 | return nil, err 25 | } 26 | withManifest.ruleId = ruleId 27 | return withManifest, nil 28 | } 29 | 30 | func newInstanceWithManifest(m *manifest.Manifest) (*RuleChainInstance, error) { 31 | nodes, err := nodes.GetNodes(m) 32 | if err != nil { 33 | return nil, err 34 | } 35 | r := &RuleChainInstance{ 36 | firstRuleNodeID: m.FirstRuleNodeId, 37 | nodes: nodes, 38 | } 39 | return r, nil 40 | } 41 | -------------------------------------------------------------------------------- /iothub/client/tcpclient/tcp.go: -------------------------------------------------------------------------------- 1 | package tcpclient 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "net" 7 | "pandax/pkg/global" 8 | "sync" 9 | ) 10 | 11 | var TcpClient sync.Map 12 | 13 | func Send(deviceId, msg string) error { 14 | if conn, ok := TcpClient.Load(deviceId); ok { 15 | conn := conn.(*net.TCPConn) 16 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 17 | _, err := conn.Write([]byte(msg)) 18 | if err != nil { 19 | return err 20 | } 21 | } else { 22 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 23 | return errors.New("未获取到设备的MQTT连接") 24 | } 25 | return nil 26 | } 27 | 28 | func SendHex(deviceId, msg string) error { 29 | 30 | if conn, ok := TcpClient.Load(deviceId); ok { 31 | conn := conn.(*net.TCPConn) 32 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 33 | b, err := hex.DecodeString(msg) 34 | if err != nil { 35 | return err 36 | } 37 | _, err = conn.Write(b) 38 | if err != nil { 39 | return err 40 | } 41 | } else { 42 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /iothub/server/emqxserver/grpc_server.go: -------------------------------------------------------------------------------- 1 | package emqxserver 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "google.golang.org/grpc" 7 | "net" 8 | "pandax/pkg/global" 9 | ) 10 | 11 | const DefaultPort = ":9001" 12 | 13 | type Server struct { 14 | Addr string 15 | srv *grpc.Server 16 | } 17 | 18 | func NewServer(addr string) *Server { 19 | if addr == "" { 20 | addr = DefaultPort 21 | } 22 | return &Server{ 23 | Addr: addr, 24 | srv: grpc.NewServer(), 25 | } 26 | } 27 | 28 | func (s *Server) GetServe() *grpc.Server { 29 | return s.srv 30 | } 31 | 32 | func (s *Server) Type() string { 33 | return "GRPC" 34 | } 35 | 36 | func (s *Server) Start(ctx context.Context) error { 37 | l, err := net.Listen("tcp", s.Addr) 38 | if err != nil { 39 | return fmt.Errorf("error listen addr: %w", err) 40 | } 41 | go func() { 42 | if err := s.srv.Serve(l); err != nil { 43 | global.Log.Error(fmt.Sprintf("error http serve: %s", err)) 44 | } 45 | }() 46 | return nil 47 | } 48 | 49 | func (s *Server) Stop(ctx context.Context) error { 50 | s.srv.Stop() 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /apps/system/api/vo/routerVo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | /** 4 | * 路由对象参数说明 5 | * { 6 | * component: 组件地址 7 | * redirect: 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 8 | * path: 路由地址 9 | * name: 路由名字 10 | * // 路由meta对象参数说明 11 | * meta: { 12 | * title: 菜单栏及 tagsView 栏、菜单搜索名称(国际化) 13 | * isLink: 是否超链接菜单,开启外链条件,`1、isLink:true 2、链接地址不为空` 14 | * isHide: 是否隐藏此路由 15 | * isKeepAlive: 是否缓存组件状态 16 | * isAffix: 是否固定在 tagsView 栏上 17 | * isFrame: 是否内嵌窗口,,开启条件,`1、isIframe:true 2、链接地址不为空` 18 | * auth: 当前路由权限标识(多个请用逗号隔开),最后转成数组格式,用于与当前用户权限进行对比,控制路由显示、隐藏 19 | * icon: 菜单、tagsView 图标,阿里:加 `iconfont xxx`,fontawesome:加 `fa xxx` 20 | * } 21 | * } 22 | * 23 | */ 24 | type RouterVo struct { 25 | Name string `json:"name"` 26 | Path string `json:"path"` 27 | Redirect string `json:"redirect"` 28 | Component string `json:"component"` 29 | Meta MetaVo `json:"meta"` 30 | Children []RouterVo `json:"children"` 31 | } 32 | -------------------------------------------------------------------------------- /iothub/hook_message_work/hook_service.go: -------------------------------------------------------------------------------- 1 | package hook_message_work 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "github.com/golang-queue/queue" 7 | "github.com/golang-queue/queue/core" 8 | "pandax/iothub/netbase" 9 | "pandax/pkg/global" 10 | "sync" 11 | ) 12 | 13 | type HookService struct { 14 | Cache sync.Map 15 | Queue *queue.Queue 16 | Ch chan struct{} // 并发限制 17 | Wg sync.WaitGroup // 优雅关闭 18 | MessageCh chan *netbase.DeviceEventInfo 19 | } 20 | 21 | func NewHookService() *HookService { 22 | hs := &HookService{ 23 | Cache: sync.Map{}, 24 | Ch: make(chan struct{}, global.Conf.Queue.ChNum), 25 | MessageCh: make(chan *netbase.DeviceEventInfo, global.Conf.Queue.TaskNum), 26 | } 27 | pool := queue.NewPool(int(global.Conf.Queue.QueuePool), queue.WithFn(func(ctx context.Context, m core.QueuedMessage) error { 28 | v, ok := m.(*netbase.DeviceEventInfo) 29 | if !ok { 30 | if err := json.Unmarshal(m.Bytes(), &v); err != nil { 31 | return err 32 | } 33 | } 34 | hs.MessageCh <- v 35 | return nil 36 | })) 37 | hs.Queue = pool 38 | return hs 39 | } 40 | -------------------------------------------------------------------------------- /iothub/client/udpclient/udp.go: -------------------------------------------------------------------------------- 1 | package udpclient 2 | 3 | import ( 4 | "encoding/hex" 5 | "net" 6 | "pandax/pkg/global" 7 | "sync" 8 | ) 9 | 10 | type UdpClientT struct { 11 | Conn *net.UDPConn 12 | Addr *net.UDPAddr 13 | } 14 | 15 | var UdpClient sync.Map 16 | 17 | func Send(deviceId, msg string) error { 18 | if conn, ok := UdpClient.Load(deviceId); ok { 19 | conn := conn.(*UdpClientT) 20 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 21 | _, err := conn.Conn.WriteToUDP([]byte(msg), conn.Addr) 22 | if err != nil { 23 | return err 24 | } 25 | } else { 26 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 27 | } 28 | return nil 29 | } 30 | 31 | func SendHex(deviceId, msg string) error { 32 | if conn, ok := UdpClient.Load(deviceId); ok { 33 | conn := conn.(*UdpClientT) 34 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 35 | b, err := hex.DecodeString(msg) 36 | if err != nil { 37 | return err 38 | } 39 | _, err = conn.Conn.WriteToUDP(b, conn.Addr) 40 | if err != nil { 41 | return err 42 | } 43 | } else { 44 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /iothub/client/updclient/udp.go: -------------------------------------------------------------------------------- 1 | package udpclient 2 | 3 | import ( 4 | "encoding/hex" 5 | "net" 6 | "pandax/pkg/global" 7 | "sync" 8 | ) 9 | 10 | type UdpClientT struct { 11 | Conn *net.UDPConn 12 | Addr *net.UDPAddr 13 | } 14 | 15 | var UdpClient sync.Map 16 | 17 | func Send(deviceId, msg string) error { 18 | if conn, ok := UdpClient.Load(deviceId); ok { 19 | conn := conn.(*UdpClientT) 20 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 21 | _, err := conn.Conn.WriteToUDP([]byte(msg), conn.Addr) 22 | if err != nil { 23 | return err 24 | } 25 | } else { 26 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 27 | } 28 | return nil 29 | } 30 | 31 | func SendHex(deviceId, msg string) error { 32 | if conn, ok := UdpClient.Load(deviceId); ok { 33 | conn := conn.(*UdpClientT) 34 | global.Log.Infof("设备%s, 发送指令%s", deviceId, msg) 35 | b, err := hex.DecodeString(msg) 36 | if err != nil { 37 | return err 38 | } 39 | _, err = conn.Conn.WriteToUDP(b, conn.Addr) 40 | if err != nil { 41 | return err 42 | } 43 | } else { 44 | global.Log.Infof("设备%s TCP连接不存在, 发送指令失败", deviceId) 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /deploy/manifest-server/mysql/deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mysql-service 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: 3306 9 | targetPort: mysql 10 | protocol: TCP 11 | selector: 12 | app: mysql 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: mysql 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: mysql 23 | strategy: 24 | type: Recreate 25 | template: 26 | metadata: 27 | labels: 28 | app: mysql 29 | spec: 30 | containers: 31 | - image: mysql:8.0.23 32 | name: mysql 33 | imagePullPolicy: IfNotPresent 34 | env: 35 | - name: MYSQL_ROOT_PASSWORD 36 | value: pandax 37 | ports: 38 | - containerPort: 3306 39 | name: mysql 40 | protocol: TCP 41 | volumeMounts: 42 | - name: mysql-data 43 | mountPath: /var/lib/mysql 44 | volumes: 45 | - hostPath: 46 | path: ./mysql/data 47 | name: mysql-data -------------------------------------------------------------------------------- /resource/template/js/api.template: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | // 查询{{.FunctionName}}列表 4 | export function list{{.FunctionName}}(query:any) { 5 | return request({ 6 | url: '/{{.PackageName}}/{{.BusinessName}}/list', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | // 查询{{.FunctionName}}详细 13 | export function get{{.FunctionName}}({{.PkJsonField}}:{{.PkGoType}}) { 14 | return request({ 15 | url: '/{{.PackageName}}/{{.BusinessName}}/' + {{.PkJsonField}}, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | // 新增{{.FunctionName}} 21 | export function add{{.FunctionName}}(data:any) { 22 | return request({ 23 | url: '/{{.PackageName}}/{{.BusinessName}}', 24 | method: 'post', 25 | data: data 26 | }) 27 | } 28 | 29 | // 修改{{.FunctionName}} 30 | export function update{{.FunctionName}}(data:any) { 31 | return request({ 32 | url: '/{{.PackageName}}/{{.BusinessName}}', 33 | method: 'put', 34 | data: data 35 | }) 36 | } 37 | 38 | // 删除{{.FunctionName}} 39 | export function del{{.FunctionName}}({{.PkJsonField}}: {{.PkGoType}}) { 40 | return request({ 41 | url: '/{{.PackageName}}/{{.BusinessName}}/' + {{.PkJsonField}}, 42 | method: 'delete' 43 | }) 44 | } -------------------------------------------------------------------------------- /pkg/initialize/event.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "encoding/json" 5 | "pandax/apps/device/services" 6 | ruleEntity "pandax/apps/rule/entity" 7 | "pandax/pkg/events" 8 | "pandax/pkg/global" 9 | "pandax/pkg/rule_engine" 10 | "pandax/pkg/tool" 11 | ) 12 | 13 | // 初始化事件监听 14 | func InitEvents() { 15 | // 监听规则链改变 更新所有绑定改规则链的产品 16 | global.EventEmitter.On(events.ProductChainRuleEvent, func(ruleId string, codeData string) { 17 | global.Log.Infof("规则链%s变更", ruleId) 18 | list, _ := services.ProductModelDao.FindListByRule(ruleId) 19 | if list != nil { 20 | var lfData ruleEntity.RuleDataJson 21 | err := tool.StringToStruct(codeData, &lfData) 22 | if err != nil { 23 | global.Log.Error("规则链序列化失败", err) 24 | return 25 | } 26 | code, err := json.Marshal(lfData.DataCode) 27 | if err != nil { 28 | global.Log.Error("规则链序列化失败", err) 29 | return 30 | } 31 | //新建规则链实体 32 | instance, err := rule_engine.NewRuleChainInstance(ruleId, code) 33 | if err != nil { 34 | global.Log.Error("规则链初始化失败", err) 35 | return 36 | } 37 | for _, product := range *list { 38 | rule_engine.RuleEngine.SaveRuleInstance(product.Id, instance) 39 | } 40 | } 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/global/global_const_device.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | const ( 4 | TslAttributesType = "attributes" 5 | TslTelemetryType = "telemetry" 6 | TslCommandsType = "commands" 7 | TslEventType = "events" 8 | ) 9 | 10 | // 告警等级 11 | const ( 12 | CRITICAL = "CRITICAL" // 危险 13 | MAJOR = "MAJOR" // 重要 14 | MINOR = "MINOR" // 次要 15 | WARNING = "WARNING" // 警告 16 | INDETERMINATE = "INDETERMINATE" // 不确定 17 | ) 18 | 19 | // 告警状态 20 | const ( 21 | ALARMING = "0" // 告警中 22 | CONFIRMED = "1" // 已确认 23 | CLEARED = "2" // 已清除 24 | CLOSED = "3" // 已关闭 25 | ) 26 | 27 | // 设备状态 28 | const ( 29 | INACTIVE = "inactive" //未激活 30 | ONLINE = "online" //在线 31 | OFFLINE = "offline" // 离线 32 | ) 33 | 34 | // 设备类型 35 | const ( 36 | DIRECT = "direct" //直连设备 37 | GATEWAY = "gateway" //网关设备 38 | GATEWAYS = "gatewayS" // 网关子设备 39 | ) 40 | 41 | // 设备命令状态 42 | const ( 43 | CMDSUCCESS = "0" //执行成功 44 | CMDFAIL = "1" //执行失败 45 | CMDRUNNING = "2" // 执行中 46 | ) 47 | 48 | const ( 49 | MQTTProtocol = "MQTT" 50 | CoAPProtocol = "CoAP" 51 | TCPProtocol = "TCP" 52 | UDPProtocol = "UDP" 53 | HTTPProtocol = "HTTP" 54 | LwM2MProtocol = "LwM2M" 55 | ) 56 | -------------------------------------------------------------------------------- /pkg/initialize/table.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | devEntity "pandax/apps/develop/entity" 6 | jobEntity "pandax/apps/job/entity" 7 | logEntity "pandax/apps/log/entity" 8 | systemEntity "pandax/apps/system/entity" 9 | "pandax/pkg/global" 10 | ) 11 | 12 | // 初始化时如果没有表创建表 13 | func InitTable() { 14 | m := global.Conf.Server 15 | if m.IsInitTable { 16 | biz.ErrIsNil( 17 | global.Db.AutoMigrate( 18 | //casbin.CasbinRule{}, 19 | systemEntity.SysOrganization{}, 20 | systemEntity.SysApi{}, 21 | systemEntity.SysConfig{}, 22 | systemEntity.SysDictType{}, 23 | systemEntity.SysDictData{}, 24 | systemEntity.SysUser{}, 25 | systemEntity.SysTenants{}, 26 | systemEntity.SysRole{}, 27 | systemEntity.SysMenu{}, 28 | systemEntity.SysPost{}, 29 | systemEntity.SysRoleMenu{}, 30 | systemEntity.SysRoleOrganization{}, 31 | systemEntity.SysNotice{}, 32 | 33 | logEntity.LogLogin{}, 34 | logEntity.LogOper{}, 35 | jobEntity.SysJob{}, 36 | devEntity.DevGenTable{}, 37 | devEntity.DevGenTableColumn{}, 38 | ), 39 | "初始化表失败") 40 | } 41 | err := global.TdDb.CreateEventTable() 42 | if err != nil { 43 | global.Log.Panic(err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/middleware/oper.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/restfulx" 5 | "github.com/PandaXGO/PandaKit/utils" 6 | "net/http" 7 | "pandax/apps/log/entity" 8 | "pandax/apps/log/services" 9 | ) 10 | 11 | func OperationHandler(rc *restfulx.ReqCtx) error { 12 | c := rc.Request 13 | // 请求操作不做记录 14 | if c.Request.Method == http.MethodGet || rc.LoginAccount == nil { 15 | return nil 16 | } 17 | if rc.RequiredPermission == nil || !rc.RequiredPermission.NeedToken { 18 | return nil 19 | } 20 | go func() { 21 | oper := entity.LogOper{ 22 | Title: rc.LogInfo.Description, 23 | BusinessType: "0", 24 | Method: c.Request.Method, 25 | OperName: rc.LoginAccount.UserName, 26 | OperUrl: c.Request.URL.Path, 27 | OperIp: c.Request.RemoteAddr, 28 | OperLocation: utils.GetRealAddressByIP(c.Request.RemoteAddr), 29 | OperParam: "", 30 | Status: "0", 31 | } 32 | if c.Request.Method == "POST" { 33 | oper.BusinessType = "1" 34 | } else if c.Request.Method == "PUT" { 35 | oper.BusinessType = "2" 36 | } else if c.Request.Method == "DELETE" { 37 | oper.BusinessType = "3" 38 | } 39 | services.LogOperModelDao.Insert(oper) 40 | }() 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /apps/log/entity/log_login.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "time" 6 | ) 7 | 8 | type LogLogin struct { 9 | InfoId int64 `json:"infoId" gorm:"primary_key;AUTO_INCREMENT"` //主键 10 | Username string `json:"username" gorm:"type:varchar(128);comment:用户名"` 11 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"` 12 | Ipaddr string `json:"ipaddr" gorm:"type:varchar(255);comment:ip地址"` 13 | LoginLocation string `json:"loginLocation" gorm:"type:varchar(255);comment:归属地"` 14 | Browser string `json:"browser" gorm:"type:varchar(255);comment:浏览器"` 15 | Os string `json:"os" gorm:"type:varchar(255);comment:系统"` 16 | Platform string `json:"platform" gorm:"type:varchar(255);comment:固件"` 17 | LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;comment:登录时间"` 18 | CreateBy string `json:"createBy" gorm:"type:varchar(128);comment:创建人"` 19 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);comment:更新者"` 20 | Params string `json:"params" gorm:"-"` 21 | Remark string `json:"remark" gorm:"type:varchar(255);"` //备注 22 | Msg string `json:"msg" gorm:"type:varchar(255);"` 23 | model.BaseModel 24 | } 25 | -------------------------------------------------------------------------------- /pkg/middleware/swagger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 5 | "github.com/emicklei/go-restful/v3" 6 | "github.com/go-openapi/spec" 7 | ) 8 | 9 | /** 10 | * @Description 11 | * @Author 熊猫 12 | * @Date 2022/8/3 9:16 13 | **/ 14 | 15 | func SwaggerConfig(wsContainer *restful.Container) { 16 | config := restfulspec.Config{ 17 | WebServices: wsContainer.RegisteredWebServices(), 18 | APIPath: "/apidocs.json", 19 | PostBuildSwaggerObjectHandler: enrichSwaggerObject} 20 | wsContainer.Add(restfulspec.NewOpenAPIService(config)) 21 | } 22 | 23 | func enrichSwaggerObject(swo *spec.Swagger) { 24 | swo.Info = &spec.Info{ 25 | InfoProps: spec.InfoProps{ 26 | Title: "PandaX 框架的API文档", 27 | Description: "这是PandaX框架文档,根据文档调用API", 28 | Contact: &spec.ContactInfo{ 29 | ContactInfoProps: spec.ContactInfoProps{ 30 | Name: "PandaX 熊猫", 31 | Email: "2417920382@qq.com", 32 | URL: "https://github.com/XM-GO/PandaX", 33 | }, 34 | }, 35 | License: &spec.License{ 36 | LicenseProps: spec.LicenseProps{ 37 | Name: "MIT", 38 | URL: "https://github.com/XM-GO/PandaX", 39 | }, 40 | }, 41 | Version: "1.0.0", 42 | }, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /deploy/README.md: -------------------------------------------------------------------------------- 1 | # K3S安装 2 | ## K3S安装及卸载 3 | 1. 安装 4 | curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION="v1.23.1+k3s1" sh - 5 | 2. 卸载 6 | sudo /usr/local/bin/k3s-uninstall.sh 7 | 8 | ## 服务安装 安装 9 | 1. kubectl kustomize deploy/manifest-server -o deploy/deploy-server.yaml 10 | 2. kubectl apply -f ./deploy/deploy-server.yaml 11 | 12 | ## k3s部署流程 13 | 1. 设置打包环境 14 | go env -w GOOS=linux 15 | go env -w GOARCH=amd64 16 | 2. 构建Linux执行命令 17 | go build -o pandax . 18 | 4. 构建docker镜像 (修改版本号 xmadmin/pandax:v1.0) 19 | docker build -t pandax:v1.4 --rm . 20 | 5. 上传daocker镜像 21 | docker push pandax:v1.4 22 | 6. 生成 deploy.yaml 23 | kubectl kustomize deploy/manifest -o deploy/deploy.yaml 24 | 7. k8s安装yaml 25 | kubectl apply -f deploy/deploy.yaml 26 | 27 | ## 查看部署状态 28 | 8. 查看 yaml 的安装状态 29 | kubectl get pods -n pandax 30 | kubectl get services -n pandax 31 | 32 | 33 | # docker-compose安装 34 | ## 安装 docker-compose 35 | ```text 36 | curl -L "https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 37 | ``` 38 | ## 赋予权限 39 | ```text 40 | sudo chmod +x /usr/local/bin/docker-compose 41 | ``` 42 | 43 | ## 执行创建命令 44 | ```text 45 | docker-compose up 46 | ``` -------------------------------------------------------------------------------- /deploy/manifest-server/redis/deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: redis-service 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: 6379 9 | targetPort: redis 10 | protocol: TCP 11 | selector: 12 | app: redis 13 | --- 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | metadata: 17 | name: redis 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: redis 23 | strategy: 24 | type: Recreate 25 | template: 26 | metadata: 27 | labels: 28 | app: redis 29 | spec: 30 | containers: 31 | - image: redis:7.0.12 32 | name: redis 33 | imagePullPolicy: IfNotPresent 34 | env: 35 | - name: requirepass 36 | value: pandax 37 | ports: 38 | - containerPort: 3306 39 | name: redis 40 | protocol: TCP 41 | volumeMounts: 42 | - name: redis-data 43 | mountPath: /data 44 | - name: mysql-log 45 | mountPath: /logs 46 | volumes: 47 | - hostPath: 48 | path: ./redis/data 49 | name: redis-data 50 | - hostPath: 51 | path: ./redis/logs 52 | name: redis-log -------------------------------------------------------------------------------- /apps/system/api/form/user.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | // User register structure 4 | type Register struct { 5 | Username string `json:"userName"` 6 | Password string `json:"passWord"` 7 | NickName string `json:"nickName" gorm:"default:'QMPlusUser'"` 8 | HeaderImg string `json:"headerImg" gorm:"default:'http://www.henrongyi.top/avatar/lufu.jpg'"` 9 | AuthorityId string `json:"authorityId" gorm:"default:888"` 10 | } 11 | 12 | // User login structure 13 | type Login struct { 14 | Username string `json:"username" ` // 用户名 15 | Password string `json:"password"` // 密码 16 | Captcha string `json:"captcha"` // 验证码 17 | CaptchaId string `json:"captchaId"` // 验证码ID 18 | } 19 | 20 | // Modify password structure 21 | type ChangePasswordStruct struct { 22 | Username string `json:"username"` // 用户名 23 | Password string `json:"password"` // 密码 24 | NewPassword string `json:"newPassword"` // 新密码 25 | } 26 | 27 | type UserSearch struct { 28 | Username string `json:"username"` // 用户UUID 29 | NickName string `json:"nickName"` // 角色ID 30 | Status int64 `json:"status"` // 角色ID 31 | Phone string `json:"phone"` // 角色ID 32 | PostId int64 `json:"postId"` // 角色ID 33 | OrganizationId int64 `json:"organizationId"` // 角色ID 34 | } 35 | -------------------------------------------------------------------------------- /apps/device/entity/device_vo.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type DeviceTotalOutput struct { 4 | DeviceInfo DeviceCount `json:"deviceInfo"` 5 | DeviceLinkStatusInfo []DeviceCountLinkStatus `json:"deviceLinkStatusInfo"` 6 | DeviceCountType []DeviceCountType `json:"deviceCountType"` 7 | ProductInfo DeviceCount `json:"productInfo"` 8 | AlarmInfo DeviceCount `json:"alarmInfo"` 9 | } 10 | 11 | type DeviceStatusVo struct { 12 | Name string `json:"name"` 13 | Key string `json:"key"` 14 | Type string `json:"type"` 15 | Define any `json:"define"` 16 | Value any `json:"value"` 17 | Time any `json:"time"` 18 | } 19 | 20 | type VisualClass struct { 21 | ClassId string `json:"classId"` 22 | Name string `json:"name"` 23 | Attrs []VisualTwinAttr `json:"attrs"` 24 | } 25 | 26 | type VisualTwinAttr struct { 27 | Key string `json:"key"` 28 | Name string `json:"name"` 29 | Type string `json:"type"` 30 | Rw string `json:"rw"` //属性的操作权限 31 | } 32 | 33 | type VisualTwin struct { 34 | TwinId string `json:"twinId"` 35 | Name string `json:"name"` 36 | } 37 | 38 | // 发送数据 39 | type VisualTwinSendAttrs struct { 40 | TwinId string `json:"twinId"` 41 | Attrs map[string]interface{} `json:"attrs"` 42 | } 43 | -------------------------------------------------------------------------------- /resource/default.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDejCCAmICCQDQU4ZRt2G46TANBgkqhkiG9w0BAQsFADB/MQswCQYDVQQGEwJ6 3 | aDEPMA0GA1UECAwGbWF5Zmx5MQswCQYDVQQHDAJ4bTEPMA0GA1UECgwGbWF5Zmx5 4 | MQ8wDQYDVQQLDAZtYXlmbHkxDzANBgNVBAMMBm1heWZseTEfMB0GCSqGSIb3DQEJ 5 | ARYQOTU0NTM3NDczQHFxLmNvbTAeFw0yMTA2MjQwMzI2MzBaFw0zMTA2MjIwMzI2 6 | MzBaMH8xCzAJBgNVBAYTAnpoMQ8wDQYDVQQIDAZtYXlmbHkxCzAJBgNVBAcMAnht 7 | MQ8wDQYDVQQKDAZtYXlmbHkxDzANBgNVBAsMBm1heWZseTEPMA0GA1UEAwwGbWF5 8 | Zmx5MR8wHQYJKoZIhvcNAQkBFhA5NTQ1Mzc0NzNAcXEuY29tMIIBIjANBgkqhkiG 9 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CsawvEZl42Vf+0BlTuZ3Dp10yW8Oty1tjim 10 | xUj3s0WPeKil6+TehnQELS8vGJfek+yT99nyrt+bkRmg1kxZ57FtQFEuthG4OQZo 11 | aMDUz6Ab+8P1PQ9VH0XimnnYabxztJiQjl8HdJt6N4WP35kGlcul7qQ+Qc7iwjhS 12 | adfAhXVycqVIcGQyHiPPfbmYRjueAIC4czmMUxwFKCwjepGYkwzWuGkpMD0hg/SI 13 | XpFJE2dcqYPR2nCah1gxZZG00lHU1X2pehNmmgeHRkB5S7mrsCdyyV/33SAYk6T6 14 | PT7dOqY54bfnh3C0k+T7IzvKTXKG76eG63STmxVa6luVoKMvxwIDAQABMA0GCSqG 15 | SIb3DQEBCwUAA4IBAQB/e8EO2XEtkYBxebR1w6i50vaegLsxQJR3l5qm7rsHu3Cr 16 | smJXGsc56axKCAqJ4XvSI65BT51FghAoGn62QNyiQgc0YoS99nwCCGFtnhZ2lmSe 17 | pfhUHegN/Qo4I8FemEMD+o9kGeAzwrnaIVIT/cNOEQgm+RzrgHHJh5QBn2XgJalU 18 | NeFTWaimyefwSezSa/vPbyMoAl9HkT6kdvnms/yOth4AOle6+5pM2StWjmMi4yx4 19 | 16y3NvLTku6nAUazaHTOOu/MCqLWL2/qYTk3r7OCop2jr9Rp+HLbg5AfKLUIVXjG 20 | /1fnXJIuD+2u9qgDLN5PZNgz4MlU86ugtmYPFkVt 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /deploy/manifest-server/emqx/deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: emqx-service 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - port: 1883 9 | targetPort: 1883 10 | protocol: TCP 11 | - port: 8083 12 | targetPort: 8083 13 | protocol: TCP 14 | - port: 8084 15 | targetPort: 8084 16 | protocol: TCP 17 | - port: 8883 18 | targetPort: 8883 19 | protocol: TCP 20 | - port: 18083 21 | targetPort: 18083 22 | protocol: TCP 23 | selector: 24 | app: emqx 25 | --- 26 | apiVersion: apps/v1 27 | kind: Deployment 28 | metadata: 29 | name: emqx 30 | spec: 31 | replicas: 1 32 | selector: 33 | matchLabels: 34 | app: emqx 35 | strategy: 36 | type: Recreate 37 | template: 38 | metadata: 39 | labels: 40 | app: emqx 41 | spec: 42 | containers: 43 | - image: emqx/emqx:5.1.0 44 | name: emqx 45 | imagePullPolicy: IfNotPresent 46 | ports: 47 | - containerPort: 1883 48 | protocol: TCP 49 | - containerPort: 8083 50 | protocol: TCP 51 | - containerPort: 8084 52 | protocol: TCP 53 | - containerPort: 8883 54 | protocol: TCP 55 | - containerPort: 18083 56 | protocol: TCP -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/filter_message_type_switch_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type messageTypeSwitchNode struct { 8 | bareNode 9 | } 10 | type messageTypeSwitchNodeFactory struct{} 11 | 12 | func (f messageTypeSwitchNodeFactory) Name() string { return "MessageTypeSwitchNode" } 13 | func (f messageTypeSwitchNodeFactory) Category() string { return NODE_CATEGORY_FILTER } 14 | func (f messageTypeSwitchNodeFactory) Labels() []string { 15 | return []string{ 16 | message.RowMes, 17 | message.AttributesMes, 18 | message.TelemetryMes, 19 | message.RpcRequestFromDevice, 20 | message.RpcRequestToDevice, 21 | message.UpEventMes, 22 | message.ConnectMes, 23 | message.DisConnectMes, 24 | } 25 | } 26 | func (f messageTypeSwitchNodeFactory) Create(id string, meta Properties) (Node, error) { 27 | node := &messageTypeSwitchNode{ 28 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 29 | } 30 | return decodePath(meta, node) 31 | } 32 | 33 | func (n *messageTypeSwitchNode) Handle(msg *message.Message) error { 34 | n.Debug(msg, message.DEBUGIN, "") 35 | nodes := n.GetLinkedNodes() 36 | messageType := msg.MsgType 37 | for label, node := range nodes { 38 | if messageType == label { 39 | return node.Handle(msg) 40 | } 41 | } 42 | n.Debug(msg, message.DEBUGOUT, "消息类型不正确") 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /pkg/tdengine/tdengine_event.go: -------------------------------------------------------------------------------- 1 | package tdengine 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const connectTableName = "device_events" 8 | 9 | type Events struct { 10 | Ts string `json:"ts"` 11 | Name string `json:"name"` //标识 connet 12 | Type string `json:"type"` // 事件类型 info alarm fault 13 | Content string `json:"content"` // 事件描述 14 | DeviceId string `json:"deviceId"` 15 | } 16 | 17 | // CreateEventTable 创建设备连接事件表 18 | func (s *TdEngine) CreateEventTable() (err error) { 19 | sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.%s (ts TIMESTAMP,deviceId NCHAR(64),name NCHAR(64), 20 | type NCHAR(64),content NCHAR(255))`, s.dbName, connectTableName) 21 | _, err = s.db.Exec(sql) 22 | return 23 | } 24 | 25 | func (s *TdEngine) InsertEvent(data map[string]any) (err error) { 26 | return s.InsertDevice(connectTableName, data) 27 | } 28 | 29 | func (s *TdEngine) GetAllEvents(sql string, args ...any) (list []Events, err error) { 30 | rows, err := s.db.Query(sql, args...) 31 | if err != nil { 32 | return nil, err 33 | } 34 | defer rows.Close() 35 | 36 | for rows.Next() { 37 | var event Events 38 | 39 | err = rows.Scan(&event.Ts, &event.DeviceId, &event.Name, &event.Type, &event.Content) 40 | if err != nil { 41 | return nil, err 42 | } 43 | event.Ts = s.Time(event.Ts) 44 | 45 | list = append(list, event) 46 | } 47 | 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /pkg/config/server.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | type Server struct { 6 | Port int `yaml:"port"` 7 | GrpcPort int `yaml:"grpc-port"` 8 | TcpPort int `yaml:"tcp-port"` 9 | HttpPort int `yaml:"http-port"` 10 | Model string `yaml:"model"` 11 | Cors bool `yaml:"cors"` 12 | Rate *Rate `yaml:"rate"` 13 | IsInitTable bool `yaml:"isInitTable"` 14 | DbType string `yaml:"db-type"` 15 | ExcelDir string `yaml:"excel-dir"` 16 | Tls *Tls `yaml:"tls"` 17 | Static *[]*Static `yaml:"static"` 18 | StaticFile *[]*StaticFile `yaml:"static-file"` 19 | } 20 | 21 | func (s *Server) GetPort() string { 22 | return fmt.Sprintf(":%d", s.Port) 23 | } 24 | 25 | type Static struct { 26 | RelativePath string `yaml:"relative-path"` 27 | Root string `yaml:"root"` 28 | } 29 | 30 | type StaticFile struct { 31 | RelativePath string `yaml:"relative-path"` 32 | Filepath string `yaml:"filepath"` 33 | } 34 | 35 | type Tls struct { 36 | Enable bool `yaml:"enable"` // 是否启用tls 37 | KeyFile string `yaml:"key-file"` // 私钥文件路径 38 | CertFile string `yaml:"cert-file"` // 证书文件路径 39 | } 40 | 41 | type Rate struct { 42 | Enable bool `yaml:"enable"` // 是否限流 43 | RateNum float64 `yaml:"rate-num"` // 限流数量 44 | } 45 | -------------------------------------------------------------------------------- /iothub/server/httpserver/http_server.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "context" 5 | "github.com/emicklei/go-restful/v3" 6 | "net/http" 7 | "pandax/pkg/global" 8 | ) 9 | 10 | const DefaultPort = ":9002" 11 | 12 | type HttpServer struct { 13 | Addr string 14 | srv *http.Server 15 | Container *restful.Container 16 | } 17 | 18 | func NewHttpServer(addr string) *HttpServer { 19 | if addr == "" { 20 | addr = DefaultPort 21 | } 22 | c := restful.NewContainer() 23 | c.EnableContentEncoding(true) 24 | return &HttpServer{ 25 | Addr: addr, 26 | Container: c, 27 | srv: &http.Server{ 28 | Addr: addr, 29 | Handler: c, 30 | }, 31 | } 32 | } 33 | 34 | func (s *HttpServer) GetServe() *http.Server { 35 | return s.srv 36 | } 37 | 38 | func (s *HttpServer) Type() string { 39 | return "HTTP" 40 | } 41 | 42 | func (s *HttpServer) Start(ctx context.Context) error { 43 | if global.Conf.Server.Tls.Enable { 44 | if err := s.srv.ListenAndServeTLS(global.Conf.Server.Tls.CertFile, global.Conf.Server.Tls.KeyFile); err != nil { 45 | global.Log.Errorf("error http serve: %s", err) 46 | } 47 | } else { 48 | if err := s.srv.ListenAndServe(); err != nil { 49 | global.Log.Errorf("error http serve: %s", err) 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func (s *HttpServer) Stop(ctx context.Context) error { 56 | s.srv.Shutdown(ctx) 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/init.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | // init register all node's factory 4 | func init() { 5 | RegisterFactory(inputNodeFactory{}) 6 | RegisterFactory(switchFilterNodeFactory{}) 7 | RegisterFactory(scriptFilterNodeFactory{}) 8 | RegisterFactory(messageTypeFilterNodeFactory{}) 9 | RegisterFactory(messageTypeSwitchNodeFactory{}) 10 | 11 | RegisterFactory(transformDeleteKeyNodeFactory{}) 12 | RegisterFactory(transformRenameKeyNodeFactory{}) 13 | RegisterFactory(transformScriptNodeFactory{}) 14 | 15 | RegisterFactory(createAlarmNodeFactory{}) 16 | RegisterFactory(clearAlarmNodeFactory{}) 17 | RegisterFactory(logNodeFactory{}) 18 | RegisterFactory(saveAttributesNodeFactory{}) 19 | RegisterFactory(saveTimeSeriesNodeFactory{}) 20 | RegisterFactory(delayNodeFactory{}) 21 | 22 | RegisterFactory(externalDingNodeFactory{}) 23 | RegisterFactory(externalWechatNodeFactory{}) 24 | RegisterFactory(externalKafkaNodeFactory{}) 25 | RegisterFactory(externalNatsNodeFactory{}) 26 | RegisterFactory(externalMqttNodeFactory{}) 27 | RegisterFactory(externalRestapiNodeFactory{}) 28 | RegisterFactory(externalSendEmailNodeFactory{}) 29 | RegisterFactory(externalSendSmsNodeFactory{}) 30 | RegisterFactory(externalRuleChainNodeFactory{}) 31 | RegisterFactory(rpcRequestFromDeviceFactory{}) 32 | RegisterFactory(rpcRequestToDeviceNodeFactory{}) 33 | RegisterFactory(switchMetaNodeFactory{}) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/middleware/permission.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/casbin" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "github.com/PandaXGO/PandaKit/token" 8 | "pandax/pkg/global" 9 | 10 | "github.com/golang-jwt/jwt/v5" 11 | ) 12 | 13 | func PermissionHandler(rc *restfulx.ReqCtx) error { 14 | permission := rc.RequiredPermission 15 | // 如果需要的权限信息不为空,并且不需要token,则不返回错误,继续后续逻辑 16 | if permission != nil && !permission.NeedToken { 17 | return nil 18 | } 19 | tokenStr := rc.Request.Request.Header.Get("X-TOKEN") 20 | // header不存在则从查询参数token中获取 21 | if tokenStr == "" { 22 | tokenStr = rc.Request.QueryParameter("token") 23 | } 24 | if tokenStr == "" { 25 | return biz.PermissionErr 26 | } 27 | j := token.NewJWT("", []byte(global.Conf.Jwt.Key), jwt.SigningMethodHS256) 28 | loginAccount, err := j.ParseToken(tokenStr) 29 | if err != nil || loginAccount == nil { 30 | return biz.PermissionErr 31 | } 32 | rc.LoginAccount = loginAccount 33 | 34 | if !permission.NeedCasbin { 35 | return nil 36 | } 37 | 38 | ca := casbin.CasbinService{ModelPath: global.Conf.Casbin.ModelPath} 39 | e := ca.GetCasbinEnforcer() 40 | // 判断策略中是否存在 41 | success, err := e.Enforce(loginAccount.RoleKey, rc.Request.Request.URL.Path, rc.Request.Request.Method) 42 | if !success || err != nil { 43 | return biz.CasbinErr 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /apps/job/api/log_job.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "pandax/apps/job/entity" 8 | "pandax/apps/job/services" 9 | "strings" 10 | ) 11 | 12 | type JobLogApi struct { 13 | JobLogApp services.JobLogModel 14 | } 15 | 16 | // GetJobLogList Job日志列表 17 | func (l *JobLogApi) GetJobLogList(rc *restfulx.ReqCtx) { 18 | job := entity.JobLog{} 19 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 20 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 21 | job.Name = restfulx.QueryParam(rc, "name") 22 | job.Status = restfulx.QueryParam(rc, "status") 23 | 24 | job.RoleId = rc.LoginAccount.RoleId 25 | job.Owner = rc.LoginAccount.UserName 26 | 27 | list, total, err := l.JobLogApp.FindListPage(pageNum, pageSize, job) 28 | biz.ErrIsNil(err, "查询任务列表失败") 29 | rc.ResData = model.ResultPage{ 30 | Total: total, 31 | PageNum: int64(pageNum), 32 | PageSize: int64(pageSize), 33 | Data: list, 34 | } 35 | } 36 | 37 | // DeleteJobLog 批量删除登录日志 38 | func (l *JobLogApi) DeleteJobLog(rc *restfulx.ReqCtx) { 39 | logIds := restfulx.PathParam(rc, "id") 40 | group := strings.Split(logIds, ",") 41 | err := l.JobLogApp.Delete(group) 42 | biz.ErrIsNil(err, "删除失败") 43 | } 44 | 45 | // DeleteAll 清空登录日志 46 | func (l *JobLogApi) DeleteAll(rc *restfulx.ReqCtx) { 47 | err := l.JobLogApp.DeleteAll() 48 | biz.ErrIsNil(err, "清空失败") 49 | } 50 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/filter_switch_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type switchFilterNode struct { 8 | bareNode 9 | Script string `json:"script" yaml:"script"` 10 | } 11 | 12 | type switchFilterNodeFactory struct{} 13 | 14 | func (f switchFilterNodeFactory) Name() string { return "SwitchNode" } 15 | func (f switchFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER } 16 | func (f switchFilterNodeFactory) Labels() []string { 17 | return []string{"Failure"} 18 | } 19 | func (f switchFilterNodeFactory) Create(id string, meta Properties) (Node, error) { 20 | node := &switchFilterNode{ 21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 22 | } 23 | return decodePath(meta, node) 24 | } 25 | 26 | func (n *switchFilterNode) Handle(msg *message.Message) error { 27 | n.Debug(msg, message.DEBUGIN, "") 28 | 29 | failureLabelNode := n.GetLinkedNode("Failure") 30 | 31 | scriptEngine := NewScriptEngine(*msg, "Switch", n.Script) 32 | SwitchResults, err := scriptEngine.ScriptOnSwitch() 33 | if err != nil { 34 | n.Debug(msg, message.DEBUGOUT, err.Error()) 35 | return failureLabelNode.Handle(msg) 36 | } 37 | nodes := n.GetLinkedNodes() 38 | for label, node := range nodes { 39 | for _, switchresult := range SwitchResults { 40 | if label == switchresult { 41 | go node.Handle(msg) 42 | } 43 | } 44 | } 45 | n.Debug(msg, message.DEBUGOUT, "") 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /iothub/server/emqxserver/const.go: -------------------------------------------------------------------------------- 1 | package emqxserver 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | // Topic 上行 10 | RowTopic = `v1/devices/me/row` 11 | TelemetryTopic = `v1/devices/me/telemetry` 12 | AttributesTopic = `v1/devices/me/attributes` 13 | 14 | //网关子设备 15 | AttributesGatewayTopic = "v1/gateway/attributes" 16 | TelemetryGatewayTopic = "v1/gateway/telemetry" 17 | ConnectGatewayTopic = "v1/gateway/connect" 18 | 19 | RpcReq = `v1/devices/me/rpc/(.*?)/(.*?)$` 20 | 21 | EventReq = `v1/devices/event/(.*?)$` 22 | ) 23 | 24 | var IotHubTopic = NewIotHubTopic() 25 | 26 | type TopicMeg map[string]string 27 | 28 | // 消息的来源类型 29 | func NewIotHubTopic() TopicMeg { 30 | return map[string]string{ 31 | AttributesTopic: message.AttributesMes, 32 | RowTopic: message.RowMes, 33 | TelemetryTopic: message.TelemetryMes, 34 | AttributesGatewayTopic: message.GATEWAY, 35 | TelemetryGatewayTopic: message.GATEWAY, 36 | ConnectGatewayTopic: message.GATEWAY, 37 | } 38 | } 39 | 40 | func (iht TopicMeg) GetMessageType(topic string) string { 41 | if meg, ok := iht[topic]; ok { 42 | return meg 43 | } 44 | if strings.Contains(topic, "v1/devices/me/rpc/request") || strings.Contains(topic, "v1/devices/me/rpc/response") { 45 | return message.RpcRequestFromDevice 46 | } 47 | if strings.Contains(topic, "v1/devices/event") { 48 | return message.UpEventMes 49 | } 50 | return "" 51 | } 52 | -------------------------------------------------------------------------------- /apps/system/entity/dict.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysDictData struct { 6 | DictCode int64 `json:"dictCode" gorm:"primary_key;AUTO_INCREMENT"` 7 | DictSort int `json:"dictSort" gorm:"type:int;comment:排序"` 8 | DictLabel string `json:"dictLabel" gorm:"type:varchar(64);comment:标签"` 9 | DictValue string `json:"dictValue" gorm:"type:varchar(64);comment:值"` 10 | DictType string `json:"dictType" gorm:"type:varchar(64);comment:字典类型"` 11 | Status string `json:"status" gorm:"type:varchar(1);comment:状态(0正常 1停用)"` 12 | CssClass string `json:"cssClass" gorm:"type:varchar(128);comment:CssClass"` 13 | ListClass string `json:"listClass" gorm:"type:varchar(128);comment:ListClass"` 14 | IsDefault string `json:"isDefault" gorm:"type:varchar(8);comment:IsDefault"` 15 | CreateBy string `json:"createBy"` 16 | UpdateBy string `json:"updateBy"` 17 | Remark string `json:"remark" gorm:"type:varchar(256);comment:备注"` 18 | model.BaseModel 19 | } 20 | 21 | type SysDictType struct { 22 | DictId int64 `json:"dictId" gorm:"primary_key;AUTO_INCREMENT"` 23 | DictName string `json:"dictName" gorm:"type:varchar(64);comment:名称"` 24 | DictType string `json:"dictType" gorm:"type:varchar(64);comment:类型"` 25 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"` 26 | CreateBy string `json:"createBy"` 27 | UpdateBy string `json:"updateBy"` 28 | Remark string `json:"remark" gorm:"type:varchar(256);comment:备注"` 29 | model.BaseModel 30 | } 31 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/transform_script_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type transformScriptNode struct { 8 | bareNode 9 | Script string `json:"script" yaml:"script"` 10 | } 11 | 12 | type transformScriptNodeFactory struct{} 13 | 14 | func (f transformScriptNodeFactory) Name() string { return "ScriptKeyNode" } 15 | func (f transformScriptNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM } 16 | func (f transformScriptNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 17 | func (f transformScriptNodeFactory) Create(id string, meta Properties) (Node, error) { 18 | node := &transformScriptNode{ 19 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 20 | } 21 | return decodePath(meta, node) 22 | } 23 | 24 | func (n *transformScriptNode) Handle(msg *message.Message) error { 25 | n.Debug(msg, message.DEBUGIN, "") 26 | 27 | successLabelNode := n.GetLinkedNode("Success") 28 | failureLabelNode := n.GetLinkedNode("Failure") 29 | 30 | scriptEngine := NewScriptEngine(*msg, "Transform", n.Script) 31 | newMessage, err := scriptEngine.ScriptOnMessage() 32 | if err != nil { 33 | n.Debug(msg, message.DEBUGOUT, err.Error()) 34 | if failureLabelNode != nil { 35 | return failureLabelNode.Handle(msg) 36 | } else { 37 | return err 38 | } 39 | } 40 | if successLabelNode != nil { 41 | n.Debug(msg, message.DEBUGOUT, "") 42 | return successLabelNode.Handle(newMessage) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /apps/system/entity/organization.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | // 组织组织 6 | type SysOrganization struct { 7 | OrganizationId int64 `json:"organizationId" gorm:"primary_key;AUTO_INCREMENT"` //组织编码 8 | ParentId int64 `json:"parentId" gorm:"type:int;comment:上级组织"` 9 | OrganizationPath string `json:"organizationPath" gorm:"type:varchar(255);comment:组织路径"` 10 | OrganizationName string `json:"organizationName" gorm:"type:varchar(128);comment:组织名称"` 11 | Sort int64 `json:"sort" gorm:"type:int;comment:排序"` 12 | Leader string `json:"leader" gorm:"type:varchar(64);comment:负责人"` // userId 13 | Phone string `json:"phone" gorm:"type:varchar(11);comment:手机"` 14 | Email string `json:"email" gorm:"type:varchar(64);comment:邮箱"` 15 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"` 16 | CreateBy string `json:"createBy" gorm:"type:varchar(64);comment:创建人"` 17 | UpdateBy string `json:"updateBy" gorm:"type:varchar(64);comment:修改人"` 18 | Children []SysOrganization `json:"children" gorm:"-"` 19 | model.BaseModel 20 | } 21 | 22 | type OrganizationLable struct { 23 | OrganizationId int64 `gorm:"-" json:"organizationId"` 24 | OrganizationName string `gorm:"-" json:"organizationName"` 25 | Children []OrganizationLable `gorm:"-" json:"children"` 26 | } 27 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/filter_device_type_switch_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | // 检查关联关系 8 | // 该消息来自与哪个实体或到那个实体 9 | type deviceTypeSwitchNode struct { 10 | bareNode 11 | } 12 | 13 | type deviceTypeSwitchNodeFactory struct{} 14 | 15 | func (f deviceTypeSwitchNodeFactory) Name() string { return "DeviceTypeSwitchNode" } 16 | func (f deviceTypeSwitchNodeFactory) Category() string { return NODE_CATEGORY_FILTER } 17 | func (f deviceTypeSwitchNodeFactory) Labels() []string { 18 | return []string{message.DEVICE, message.GATEWAY} 19 | } 20 | func (f deviceTypeSwitchNodeFactory) Create(id string, meta Properties) (Node, error) { 21 | node := &deviceTypeSwitchNode{ 22 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 23 | } 24 | return decodePath(meta, node) 25 | } 26 | 27 | func (n *deviceTypeSwitchNode) Handle(msg *message.Message) error { 28 | n.Debug(msg, message.DEBUGIN, "") 29 | 30 | deviceLabelNode := n.GetLinkedNode(message.DEVICE) 31 | gatewayLabelNode := n.GetLinkedNode(message.GATEWAY) 32 | deviceType := msg.Metadata.GetStringValue("deviceType") 33 | if deviceType == message.DEVICE { 34 | if deviceLabelNode != nil { 35 | n.Debug(msg, message.DEBUGOUT, "") 36 | return deviceLabelNode.Handle(msg) 37 | } 38 | } else if deviceType == message.GATEWAY { 39 | if gatewayLabelNode != nil { 40 | n.Debug(msg, message.DEBUGOUT, "") 41 | return gatewayLabelNode.Handle(msg) 42 | } 43 | } 44 | n.Debug(msg, message.DEBUGOUT, "没有匹配的设备类型") 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /apps/system/services/role_organization.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "fmt" 5 | "pandax/apps/system/entity" 6 | "pandax/pkg/global" 7 | ) 8 | 9 | type ( 10 | SysRoleOrganizationModel interface { 11 | Insert(roleId int64, organizationIds []int64) error 12 | FindOrganizationsByRoleId(roleId int64) ([]int64, error) 13 | Delete(rm entity.SysRoleOrganization) error 14 | } 15 | 16 | sysRoleOrganizationImpl struct { 17 | table string 18 | } 19 | ) 20 | 21 | var SysRoleOrganizationModelDao SysRoleOrganizationModel = &sysRoleOrganizationImpl{ 22 | table: `sys_role_organizations`, 23 | } 24 | 25 | func (m *sysRoleOrganizationImpl) Insert(roleId int64, organizationIds []int64) error { 26 | sql := "INSERT INTO sys_role_organizations (role_id, organization_id) VALUES " 27 | 28 | for i := 0; i < len(organizationIds); i++ { 29 | if len(organizationIds)-1 == i { 30 | //最后一条数据 以分号结尾 31 | sql += fmt.Sprintf("(%d,%d);", roleId, organizationIds[i]) 32 | } else { 33 | sql += fmt.Sprintf("(%d,%d),", roleId, organizationIds[i]) 34 | } 35 | } 36 | 37 | return global.Db.Exec(sql).Error 38 | } 39 | 40 | func (m *sysRoleOrganizationImpl) FindOrganizationsByRoleId(roleId int64) ([]int64, error) { 41 | var result []int64 42 | err := global.Db.Table(m.table).Where("role_id = ?", roleId).Pluck("organization_id", &result).Error 43 | return result, err 44 | } 45 | 46 | func (m *sysRoleOrganizationImpl) Delete(rm entity.SysRoleOrganization) error { 47 | return global.Db.Table(m.table).Where("role_id = ?", rm.RoleId).Delete(&rm).Error 48 | } 49 | -------------------------------------------------------------------------------- /iothub/server/tcpserver/tcp_server.go: -------------------------------------------------------------------------------- 1 | package tcpserver 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "fmt" 7 | "net" 8 | "pandax/pkg/global" 9 | ) 10 | 11 | const DefaultPort = ":9003" 12 | 13 | type TcpServer struct { 14 | Addr string 15 | listener *net.TCPListener 16 | } 17 | 18 | func NewTcpServer(addr string) *TcpServer { 19 | if addr == "" { 20 | addr = DefaultPort 21 | } 22 | return &TcpServer{ 23 | Addr: addr, 24 | } 25 | } 26 | 27 | func (s *TcpServer) GetServe() *net.TCPListener { 28 | return s.listener 29 | } 30 | 31 | func (s *TcpServer) Type() string { 32 | return "TCP" 33 | } 34 | 35 | func (s *TcpServer) Start(ctx context.Context) error { 36 | addr, _ := net.ResolveTCPAddr("tcp", s.Addr) 37 | listener, err := net.ListenTCP("tcp", addr) 38 | if err != nil { 39 | global.Log.Errorf("error http serve: %s", err) 40 | return err 41 | } 42 | s.listener = listener 43 | return nil 44 | } 45 | 46 | func (s *TcpServer) Stop(ctx context.Context) error { 47 | s.listener.Close() 48 | return nil 49 | } 50 | 51 | func (s *TcpServer) TlsConfig() (*tls.Config, error) { 52 | var certificates []tls.Certificate 53 | cert, err := tls.LoadX509KeyPair(global.Conf.Server.Tls.CertFile, global.Conf.Server.Tls.KeyFile) 54 | if err != nil { 55 | return nil, fmt.Errorf("generate x509 key pair failed: %s ", err) 56 | } 57 | certificates = append(certificates, cert) 58 | 59 | if len(certificates) == 0 { 60 | return nil, fmt.Errorf("none valid certs and secret") 61 | } 62 | 63 | return &tls.Config{Certificates: certificates}, nil 64 | } 65 | -------------------------------------------------------------------------------- /apps/rule/api/rulechain_log.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "pandax/apps/rule/entity" 8 | "pandax/apps/rule/services" 9 | "pandax/pkg/rule_engine/nodes" 10 | ) 11 | 12 | type RuleChainMsgLogApi struct { 13 | RuleChainMsgLogApp services.RuleChainMsgLogModel 14 | } 15 | 16 | func (r *RuleChainMsgLogApi) GetNodeLabels(rc *restfulx.ReqCtx) { 17 | rc.ResData = nodes.GetCategory() 18 | } 19 | 20 | // GetRuleChainMsgLogList 列表数据 21 | func (p *RuleChainMsgLogApi) GetRuleChainMsgLogList(rc *restfulx.ReqCtx) { 22 | data := entity.RuleChainMsgLog{} 23 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 24 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 25 | data.DeviceName = restfulx.QueryParam(rc, "deviceName") 26 | data.MsgType = restfulx.QueryParam(rc, "msgType") 27 | 28 | data.RoleId = rc.LoginAccount.RoleId 29 | data.Owner = rc.LoginAccount.UserName 30 | 31 | list, total, err := p.RuleChainMsgLogApp.FindListPage(pageNum, pageSize, data) 32 | biz.ErrIsNil(err, "查询规则链日志列表失败") 33 | rc.ResData = model.ResultPage{ 34 | Total: total, 35 | PageNum: int64(pageNum), 36 | PageSize: int64(pageSize), 37 | Data: list, 38 | } 39 | } 40 | 41 | // DeleteRuleChainMsgLog 删除规则链 42 | func (p *RuleChainMsgLogApi) DeleteRuleChainMsgLog(rc *restfulx.ReqCtx) { 43 | data := entity.RuleChainMsgLog{} 44 | data.DeviceName = restfulx.QueryParam(rc, "deviceName") 45 | data.MsgType = restfulx.QueryParam(rc, "msgType") 46 | biz.ErrIsNil(p.RuleChainMsgLogApp.Delete(data), "删除规则链日志失败") 47 | } 48 | -------------------------------------------------------------------------------- /apps/log/api/log_oper.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "github.com/PandaXGO/PandaKit/utils" 8 | "pandax/apps/log/entity" 9 | "pandax/apps/log/services" 10 | ) 11 | 12 | type LogOperApi struct { 13 | LogOperApp services.LogOperModel 14 | } 15 | 16 | func (l *LogOperApi) GetOperLogList(rc *restfulx.ReqCtx) { 17 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 18 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 19 | businessType := restfulx.QueryParam(rc, "businessType") 20 | operName := restfulx.QueryParam(rc, "operName") 21 | title := restfulx.QueryParam(rc, "title") 22 | list, total, err := l.LogOperApp.FindListPage(pageNum, pageSize, entity.LogOper{BusinessType: businessType, OperName: operName, Title: title}) 23 | biz.ErrIsNil(err, "查询操作日志列表失败") 24 | rc.ResData = model.ResultPage{ 25 | Total: total, 26 | PageNum: int64(pageNum), 27 | PageSize: int64(pageSize), 28 | Data: list, 29 | } 30 | } 31 | 32 | func (l *LogOperApi) GetOperLog(rc *restfulx.ReqCtx) { 33 | operId := restfulx.PathParamInt(rc, "operId") 34 | data, err := l.LogOperApp.FindOne(int64(operId)) 35 | biz.ErrIsNil(err, "查询操作日志失败") 36 | rc.ResData = data 37 | } 38 | 39 | func (l *LogOperApi) DeleteOperLog(rc *restfulx.ReqCtx) { 40 | operIds := restfulx.PathParam(rc, "operId") 41 | group := utils.IdsStrToIdsIntGroup(operIds) 42 | biz.ErrIsNil(l.LogOperApp.Delete(group), "删除操作日志失败") 43 | } 44 | 45 | func (l *LogOperApi) DeleteAll(rc *restfulx.ReqCtx) { 46 | biz.ErrIsNil(l.LogOperApp.DeleteAll(), "清空操作日志失败") 47 | } 48 | -------------------------------------------------------------------------------- /pkg/transport/http_server.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "pandax/pkg/global" 7 | 8 | "github.com/emicklei/go-restful/v3" 9 | ) 10 | 11 | type HttpServer struct { 12 | Addr string 13 | srv *http.Server 14 | 15 | Container *restful.Container 16 | } 17 | 18 | func NewHttpServer(addr string) *HttpServer { 19 | c := restful.NewContainer() 20 | c.EnableContentEncoding(true) 21 | restful.TraceLogger(&httpLog{}) 22 | restful.SetLogger(&httpLog{}) 23 | return &HttpServer{ 24 | Addr: addr, 25 | Container: c, 26 | srv: &http.Server{ 27 | Addr: addr, 28 | Handler: c, 29 | }, 30 | } 31 | } 32 | 33 | func (s *HttpServer) Type() Type { 34 | return TypeHTTP 35 | } 36 | 37 | func (s *HttpServer) Start(ctx context.Context) error { 38 | go func() { 39 | if global.Conf.Server.Tls.Enable { 40 | global.Log.Infof("HTTPS Server listen: %s", s.Addr) 41 | if err := s.srv.ListenAndServeTLS(global.Conf.Server.Tls.CertFile, global.Conf.Server.Tls.KeyFile); err != nil { 42 | global.Log.Errorf("error http serve: %s", err) 43 | } 44 | } else { 45 | global.Log.Infof("HTTP Server listen: %s", s.Addr) 46 | if err := s.srv.ListenAndServe(); err != nil { 47 | global.Log.Errorf("error http serve: %s", err) 48 | } 49 | } 50 | }() 51 | return nil 52 | } 53 | 54 | func (s *HttpServer) Stop(ctx context.Context) error { 55 | return s.srv.Shutdown(ctx) 56 | } 57 | 58 | type httpLog struct{} 59 | 60 | func (t *httpLog) Print(v ...any) { 61 | global.Log.Debug(v...) 62 | } 63 | 64 | func (t *httpLog) Printf(format string, v ...any) { 65 | global.Log.Debugf(format, v...) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/external_rule_chain_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/apps/rule/services" 5 | "pandax/pkg/rule_engine/message" 6 | 7 | "errors" 8 | "fmt" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | type externalRuleChainNode struct { 13 | bareNode 14 | RuleId string `json:"ruleId" yaml:"ruleId"` 15 | } 16 | 17 | type externalRuleChainNodeFactory struct{} 18 | 19 | func (f externalRuleChainNodeFactory) Name() string { return "RuleChainNode" } 20 | func (f externalRuleChainNodeFactory) Category() string { return NODE_CATEGORY_FLOWS } 21 | func (f externalRuleChainNodeFactory) Labels() []string { return []string{} } 22 | func (f externalRuleChainNodeFactory) Create(id string, meta Properties) (Node, error) { 23 | node := &externalRuleChainNode{ 24 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 25 | } 26 | 27 | return decodePath(meta, node) 28 | } 29 | 30 | func (n *externalRuleChainNode) Handle(msg *message.Message) error { 31 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType) 32 | _, err := services.RuleChainModelDao.FindOne(n.RuleId) 33 | if err != nil { 34 | return errors.New(fmt.Sprintf("节点 %s ,获取规则链失败", n.Name())) 35 | } 36 | 37 | /*code, _ := json.Marshal(data.RuleDataJson.LfData.DataCode) 38 | m, err := manifest.New(code) 39 | if err != nil { 40 | logrus.WithError(err).Errorf("invalidi manifest file") 41 | return err 42 | } 43 | nodes, err := GetNodes(m) 44 | if err != nil { 45 | return errors.New(fmt.Sprintf("节点 %s ,构建节点失败", n.Name())) 46 | } 47 | if node, found := nodes[m.FirstRuleNodeId]; found { 48 | go node.Handle(msg) 49 | }*/ 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/external_send_sms_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type externalSendSmsNode struct { 8 | bareNode 9 | SecretId string `json:"secretId" yaml:"secretId"` 10 | SecretKey string `json:"secretKey" yaml:"secretKey"` 11 | SdkAppId string `json:"sdkAppId" yaml:"sdkAppId"` //应用Id(腾讯) 或 签名名称(阿里) 12 | PhoneNumber string `json:"phoneNumber" yaml:"phoneNumber"` //发送到手机号 13 | TemplateId string `json:"templateId" yaml:"templateId"` //短信模板Id 14 | TemplateParam map[string]interface{} `json:"templateParam" yaml:"templateParam"` //模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空*/ 15 | } 16 | 17 | type externalSendSmsNodeFactory struct{} 18 | 19 | func (f externalSendSmsNodeFactory) Name() string { return "SendSmsNode" } 20 | func (f externalSendSmsNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL } 21 | func (f externalSendSmsNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 22 | func (f externalSendSmsNodeFactory) Create(id string, meta Properties) (Node, error) { 23 | node := &externalSendSmsNode{ 24 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 25 | } 26 | return decodePath(meta, node) 27 | } 28 | 29 | func (n *externalSendSmsNode) Handle(msg *message.Message) error { 30 | n.Debug(msg, message.DEBUGIN, "") 31 | 32 | successLabelNode := n.GetLinkedNode("Success") 33 | //failureLabelNode := n.GetLinkedNode("Failure") 34 | 35 | n.Debug(msg, message.DEBUGOUT, "") 36 | return successLabelNode.Handle(msg) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/filter_message_type_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type messageTypeFilterNode struct { 8 | bareNode 9 | MessageTypes []string `json:"messageTypes" yaml:"messageTypes"` 10 | } 11 | 12 | type messageTypeFilterNodeFactory struct{} 13 | 14 | func (f messageTypeFilterNodeFactory) Name() string { return "MessageTypeNode" } 15 | func (f messageTypeFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER } 16 | func (f messageTypeFilterNodeFactory) Labels() []string { return []string{"True", "False"} } 17 | 18 | func (f messageTypeFilterNodeFactory) Create(id string, meta Properties) (Node, error) { 19 | node := &messageTypeFilterNode{ 20 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 21 | MessageTypes: []string{}, 22 | } 23 | return decodePath(meta, node) 24 | } 25 | 26 | func (n *messageTypeFilterNode) Handle(msg *message.Message) error { 27 | n.Debug(msg, message.DEBUGIN, "") 28 | 29 | trueLabelNode := n.GetLinkedNode("True") 30 | falseLabelNode := n.GetLinkedNode("False") 31 | 32 | if n.containsType(msg.MsgType) { 33 | if trueLabelNode != nil { 34 | n.Debug(msg, message.DEBUGOUT, "") 35 | return trueLabelNode.Handle(msg) 36 | } 37 | } else { 38 | if falseLabelNode != nil { 39 | n.Debug(msg, message.DEBUGOUT, "不包含消息类型") 40 | return falseLabelNode.Handle(msg) 41 | } 42 | } 43 | return nil 44 | } 45 | 46 | func (n *messageTypeFilterNode) containsType(messageType string) bool { 47 | for _, filterType := range n.MessageTypes { 48 | if filterType == messageType { 49 | return true 50 | } 51 | } 52 | return false 53 | } 54 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/properties.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mitchellh/mapstructure" 6 | ) 7 | 8 | const ( 9 | NODE_CONFIG_MESSAGE_TYPE_KEY = "messageTypeKey" 10 | NODE_CONFIG_ORIGINATOR_TYPE_KEY = "originatorTypeKey" 11 | ) 12 | 13 | // Properties 前端 参数 Properties 14 | type Properties interface { 15 | Keys() []string 16 | With(key string, val interface{}) Properties 17 | Value(key string) (interface{}, error) 18 | DecodePath(rawVal interface{}) error 19 | } 20 | 21 | type nodeProperties struct { 22 | keypairs map[string]interface{} 23 | } 24 | 25 | func NewProperties() Properties { 26 | return &nodeProperties{ 27 | keypairs: make(map[string]interface{}), 28 | } 29 | } 30 | 31 | func NewPropertiesWithString(vals string) Properties { 32 | return &nodeProperties{} 33 | } 34 | 35 | func NewPropertiesWithValues(vals map[string]interface{}) Properties { 36 | return &nodeProperties{ 37 | keypairs: vals, 38 | } 39 | } 40 | 41 | func (c *nodeProperties) Keys() []string { 42 | keys := []string{} 43 | for key, _ := range c.keypairs { 44 | keys = append(keys, key) 45 | } 46 | return keys 47 | } 48 | 49 | func (c *nodeProperties) Value(key string) (interface{}, error) { 50 | if val, found := c.keypairs[key]; found { 51 | return val, nil 52 | } 53 | return nil, fmt.Errorf("key '%s' not found", key) 54 | } 55 | 56 | func (c *nodeProperties) With(key string, val interface{}) Properties { 57 | c.keypairs[key] = val 58 | return c 59 | } 60 | 61 | func (c *nodeProperties) DecodePath(rawVal interface{}) error { 62 | //return utils.Map2Struct(c.keypairs, rawVal) 63 | return mapstructure.Decode(c.keypairs, rawVal) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/filter_script_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | const ScriptFilterNodeName = "ScriptFilterNode" 8 | 9 | type scriptFilterNode struct { 10 | bareNode 11 | Script string `json:"script" yaml:"script"` 12 | } 13 | 14 | type scriptFilterNodeFactory struct{} 15 | 16 | func (f scriptFilterNodeFactory) Name() string { return ScriptFilterNodeName } 17 | func (f scriptFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER } 18 | func (f scriptFilterNodeFactory) Labels() []string { return []string{"True", "False", "Failure"} } 19 | func (f scriptFilterNodeFactory) Create(id string, meta Properties) (Node, error) { 20 | node := &scriptFilterNode{ 21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 22 | } 23 | return decodePath(meta, node) 24 | } 25 | 26 | func (n *scriptFilterNode) Handle(msg *message.Message) error { 27 | n.Debug(msg, message.DEBUGIN, "") 28 | 29 | trueLabelNode := n.GetLinkedNode("True") 30 | falseLabelNode := n.GetLinkedNode("False") 31 | failureLabelNode := n.GetLinkedNode("Failure") 32 | 33 | scriptEngine := NewScriptEngine(*msg, "Filter", n.Script) 34 | isTrue, err := scriptEngine.ScriptOnFilter() 35 | if err != nil { 36 | n.Debug(msg, message.DEBUGOUT, err.Error()) 37 | if failureLabelNode != nil { 38 | return failureLabelNode.Handle(msg) 39 | } 40 | } 41 | 42 | if isTrue && trueLabelNode != nil { 43 | n.Debug(msg, message.DEBUGOUT, "") 44 | return trueLabelNode.Handle(msg) 45 | } else { 46 | n.Debug(msg, message.DEBUGOUT, "Script脚本执行失败") 47 | if falseLabelNode != nil { 48 | return falseLabelNode.Handle(msg) 49 | } 50 | } 51 | 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /deploy/manifest/pandax/pandax-deployment-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pandax 5 | labels: 6 | app.kubernetes.io/name: pandax 7 | spec: 8 | type: LoadBalancer 9 | ports: 10 | - port: 7788 11 | targetPort: 7788 12 | protocol: TCP 13 | - port: 9001 14 | targetPort: 9001 15 | protocol: TCP 16 | - port: 9002 17 | targetPort: 9002 18 | protocol: TCP 19 | - port: 9003 20 | targetPort: 9003 21 | protocol: TCP 22 | - port: 8801 23 | targetPort: 8801 24 | protocol: TCP 25 | - port: 5060 26 | targetPort: 5060 27 | protocol: TCP 28 | selector: 29 | app.kubernetes.io/name: pandax 30 | --- 31 | apiVersion: apps/v1 32 | kind: Deployment 33 | metadata: 34 | name: pandax 35 | labels: 36 | app.kubernetes.io/name: pandax 37 | spec: 38 | replicas: 1 39 | selector: 40 | matchLabels: 41 | app.kubernetes.io/name: pandax 42 | template: 43 | metadata: 44 | labels: 45 | app.kubernetes.io/name: pandax 46 | spec: 47 | serviceAccountName: pandax 48 | containers: 49 | - name: pandax 50 | image: ccr.ccs.tencentyun.com/pandax/pandax:v1.9.1 51 | imagePullPolicy: IfNotPresent 52 | ports: 53 | - containerPort: 7788 54 | protocol: TCP 55 | - containerPort: 9001 56 | protocol: TCP 57 | - containerPort: 9002 58 | protocol: TCP 59 | - containerPort: 9003 60 | protocol: TCP 61 | - containerPort: 8801 62 | protocol: TCP 63 | - containerPort: 5060 64 | protocol: TCP 65 | 66 | -------------------------------------------------------------------------------- /apps/system/entity/role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/casbin" 5 | "github.com/PandaXGO/PandaKit/model" 6 | ) 7 | 8 | const ( 9 | SELFDATASCOPE = "0" 10 | ALLDATASCOPE = "1" 11 | DIYDATASCOPE = "2" 12 | ORGDATASCOPE = "3" 13 | ORGALLDATASCOPE = "4" 14 | ) 15 | 16 | type SysRole struct { 17 | model.BaseModel 18 | RoleId int64 `json:"roleId" gorm:"primary_key;AUTO_INCREMENT"` 19 | RoleName string `json:"roleName" gorm:"type:varchar(128);comment:角色名称"` 20 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"` 21 | RoleKey string `json:"roleKey" gorm:"type:varchar(128);comment:角色代码"` 22 | RoleSort int64 `json:"roleSort" gorm:"type:int;comment:角色排序"` 23 | DataScope string `json:"dataScope" gorm:"type:varchar(1);comment:数据范围(0: 本人数据 1:全部数据权限 2:自定数据权限 3:本组织数据权限 4:本组织及以下数据权限)"` 24 | CreateBy string `json:"createBy" gorm:"type:varchar(128);comment:创建人"` 25 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);comment:修改人"` 26 | Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"` 27 | ApiIds []casbin.CasbinRule `json:"apiIds" gorm:"-"` 28 | MenuIds []int64 `json:"menuIds" gorm:"-"` 29 | OrganizationIds []int64 `json:"organizationIds" gorm:"-"` 30 | } 31 | type SysRoleAuth struct { 32 | Org string `json:"org" gorm:"column:org"` 33 | DataScope string `json:"dataScope"` 34 | } 35 | type MenuIdList struct { 36 | MenuId int64 `json:"menuId"` 37 | } 38 | 39 | type OrganizationIdList struct { 40 | OrganizationId int64 `json:"organizationId"` 41 | } 42 | -------------------------------------------------------------------------------- /pkg/config/db.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | type Mysql struct { 6 | Host string `mapstructure:"host" json:"host" yaml:"host"` 7 | Config string `mapstructure:"config" json:"config" yaml:"config"` 8 | Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` 9 | Username string `mapstructure:"username" json:"username" yaml:"username"` 10 | Password string `mapstructure:"password" json:"password" yaml:"password"` 11 | MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` 12 | MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` 13 | LogMode bool `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` 14 | LogZap string `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` 15 | } 16 | 17 | func (m *Mysql) Dsn() string { 18 | return m.Username + ":" + m.Password + "@tcp(" + m.Host + ")/" + m.Dbname + "?" + m.Config 19 | } 20 | 21 | type Postgresql struct { 22 | Host string `mapstructure:"host" json:"host" yaml:"host"` 23 | Port int `mapstructure:"port" json:"port" yaml:"port"` 24 | Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` 25 | Username string `mapstructure:"username" json:"username" yaml:"username"` 26 | Password string `mapstructure:"password" json:"password" yaml:"password"` 27 | MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` 28 | MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` 29 | } 30 | 31 | func (m *Postgresql) PgDsn() string { 32 | return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", m.Host, m.Port, m.Username, m.Password, m.Dbname) 33 | } 34 | -------------------------------------------------------------------------------- /resource/default.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpgIBAAKCAQEA0CsawvEZl42Vf+0BlTuZ3Dp10yW8Oty1tjimxUj3s0WPeKil 3 | 6+TehnQELS8vGJfek+yT99nyrt+bkRmg1kxZ57FtQFEuthG4OQZoaMDUz6Ab+8P1 4 | PQ9VH0XimnnYabxztJiQjl8HdJt6N4WP35kGlcul7qQ+Qc7iwjhSadfAhXVycqVI 5 | cGQyHiPPfbmYRjueAIC4czmMUxwFKCwjepGYkwzWuGkpMD0hg/SIXpFJE2dcqYPR 6 | 2nCah1gxZZG00lHU1X2pehNmmgeHRkB5S7mrsCdyyV/33SAYk6T6PT7dOqY54bfn 7 | h3C0k+T7IzvKTXKG76eG63STmxVa6luVoKMvxwIDAQABAoIBAQCI2Y2CUpYMd9us 8 | edbskH4ZtaT35nrUB3y+Cog4cjvE8xnarKRHa/KOWX7VZYuEk3KTtJeh/Pn51K6k 9 | uUBvIUqJcq7r9XLL5uJBOuEw3HQK+qrq3GxAc+/12y+Zdji7alR2iUWfEwIHup6i 10 | GX/38tXNbE/tjrQO9z9Dh1tGkbvS/66tPn/T/oMxsRvZB6mCjB7yuOlEIwYTomYB 11 | pUFemELt8T5RtfxRa8T1VoITbfuj7zvecqlThW0H8UizsFxvrOCUaga7jtsJOCHo 12 | bcW5WvWwazoOfQ2BGpksKkBDf1N6pj85e4kOoYcVG9UN03ZwDvAGfQPWUlHB4YzW 13 | PybMwIQBAoGBAPfuOQ+ukVmkiEKj6wCBe3Z0pYeNqBGec9aj62bKFh79BCE3ZopS 14 | 7JtGs8VfBKkBAaOy+MDuvJ2fvRNRtHT4BYe1U6ZRsmVFqHScACOaO/7TR0tz0ihL 15 | 0QLCkbSwsXExG6bYbwP4jMHkhHArT7Hy8WXvup8PffjSiEs1A1uGvYSBAoGBANbx 16 | lHo+39nsc1OO8TUAWZChIQUib2hFIwzQYngSzINdfXQaGFT/omOsudAtfdjvp+qO 17 | Tr7WpwgFEFveDFsdJfZ2Kc2x9a3ty7IYIWaAjK2ghkAKz3Tt4gClreB6qG2SBycP 18 | 4C2ImbY6hMaFHz3ENtTEzzTMdD1ByxQVMvoem3BHAoGBAJdaTmtMXl8jGivUdXnx 19 | kbVWsFZ4G8nluUGm/+XYKHjybLr6XxbCWL7SApzSzL1/Z8jPURw2od53za0li8x8 20 | PKQEBfTamtVIGPZW5Z7WYRnHURa2tezzm7zbmqd71lcLa54HMn5yFTuojVEMn7I6 21 | ZTOdjYfcpUJpA9slmc8eCkQBAoGBAJEIbxRRaoBEQMkH8Y++zbB+WKZ7RssHo4/Y 22 | 6Ch3HtIg+i6mEPcBitRQzww+NeV0SExHe7Dfa9NIf3JNkO7F60CzGJ/3zXtvsftY 23 | tujQIpxhbVS3NqaCgPXI1VtbyFwupW7hEnYG7xj7wW2mk578z7afmeTZdDGFPH8v 24 | krccgeuvAoGBAPAwiqbZlXNx+ueI1B3T8VpXnG0ozKxG+l5B71kssZWa7xcv9yRd 25 | c15l2PSXNtnoT/mBID7+dqQOmfYxsDHAkUdd/BrxhXtdi9FR3AfHSEQz+VKsogAD 26 | uLyRd7jWTYqqGa2UToF/CBV+c6QyMB+6pzFNk5DmUEm4Gd6jcHDITYeI 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/action_switch_meta_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/apps/device/services" 5 | "pandax/pkg/rule_engine/message" 6 | ) 7 | 8 | type switchMetaNode struct { 9 | bareNode 10 | DeviceId string `json:"deviceId" yaml:"deviceId"` 11 | } 12 | 13 | type switchMetaNodeFactory struct{} 14 | 15 | func (f switchMetaNodeFactory) Name() string { return "SwitchMetaNode" } 16 | func (f switchMetaNodeFactory) Category() string { return NODE_CATEGORY_ACTION } 17 | func (f switchMetaNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 18 | func (f switchMetaNodeFactory) Create(id string, meta Properties) (Node, error) { 19 | node := &switchMetaNode{ 20 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 21 | } 22 | return decodePath(meta, node) 23 | } 24 | 25 | func (n *switchMetaNode) Handle(msg *message.Message) error { 26 | n.Debug(msg, message.DEBUGIN, "") 27 | 28 | successLabelNode := n.GetLinkedNode("Success") 29 | failureLabelNode := n.GetLinkedNode("Failure") 30 | // 获取设备信息 31 | device, err := services.DeviceModelDao.FindOne(n.DeviceId) 32 | if err != nil { 33 | n.Debug(msg, message.DEBUGOUT, err.Error()) 34 | if failureLabelNode != nil { 35 | return failureLabelNode.Handle(msg) 36 | } else { 37 | return err 38 | } 39 | } 40 | // 更改元数据基本信息 41 | msg.Metadata = map[string]interface{}{ 42 | "deviceId": n.DeviceId, 43 | "deviceName": device.Name, 44 | "deviceType": device.DeviceType, 45 | "deviceProtocol": device.Product.ProtocolName, 46 | "productId": device.Pid, 47 | "orgId": device.OrgId, 48 | "owner": device.Owner, 49 | } 50 | if successLabelNode != nil { 51 | n.Debug(msg, message.DEBUGOUT, "") 52 | return successLabelNode.Handle(msg) 53 | } 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /apps/rule/router/rulechain_log.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/rule/api" 7 | "pandax/apps/rule/services" 8 | 9 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 10 | "github.com/emicklei/go-restful/v3" 11 | ) 12 | 13 | func InitRuleChainMsgLogRouter(container *restful.Container) { 14 | s := &api.RuleChainMsgLogApi{ 15 | RuleChainMsgLogApp: services.RuleChainMsgLogModelDao, 16 | } 17 | 18 | ws := new(restful.WebService) 19 | ws.Path("/rule/chain/log").Produces(restful.MIME_JSON) 20 | tags := []string{"规则链日志"} 21 | 22 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 23 | restfulx.NewReqCtx(request, response).WithLog("获取规则引擎日志分页列表").Handle(s.GetRuleChainMsgLogList) 24 | }). 25 | Doc("获取规则引擎日志分页列表"). 26 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 27 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 28 | Param(ws.QueryParameter("deviceName", "设备名").Required(false).DataType("string")). 29 | Param(ws.QueryParameter("msgType", "消息类型").Required(false).DataType("string")). 30 | Metadata(restfulspec.KeyOpenAPITags, tags). 31 | Writes(model.ResultPage{}). 32 | Returns(200, "OK", model.ResultPage{})) 33 | 34 | ws.Route(ws.GET("/delete").To(func(request *restful.Request, response *restful.Response) { 35 | restfulx.NewReqCtx(request, response).WithLog("删除规则引擎信息").Handle(s.DeleteRuleChainMsgLog) 36 | }). 37 | Doc("删除规则链日志信息"). 38 | Param(ws.QueryParameter("deviceName", "设备名").Required(false).DataType("string")). 39 | Param(ws.QueryParameter("msgType", "消息类型").Required(false).DataType("string")). 40 | Metadata(restfulspec.KeyOpenAPITags, tags)) 41 | 42 | container.Add(ws) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /resource/template/go/entity.template: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // 生成日期:{{.CreatedAt}} 3 | // 生成路径: apps/{{.PackageName}}/entity/{{.TableName}}.go 4 | // 生成人:{{.FunctionAuthor}} 5 | // ========================================================================== 6 | package entity 7 | 8 | {{$hasGTime:=false}} 9 | {{- range $index, $column := .Columns -}} 10 | {{- if eq $column.GoType "Time" -}} 11 | {{$hasGTime = true}} 12 | {{- end -}} 13 | {{- end -}} 14 | {{if $hasGTime -}}import "time"{{- end }} 15 | 16 | type {{.ClassName}} struct { 17 | {{- range $index, $column := .Columns}} 18 | {{- if eq $column.IsPk "1" }} 19 | {{$column.GoField}} {{if eq $column.GoType "Time"}}time.Time{{else}}{{$column.GoType}}{{end}} `gorm:"primary_key;{{if eq $column.IsIncrement "1"}}AUTO_INCREMENT{{end}}" json:"{{$column.JsonField}}"` // {{$column.ColumnComment}} 20 | {{- else if ne $column.LinkTableName ""}} 21 | {{$column.GoField}} {{if eq $column.GoType "Time"}}time.Time{{else}}{{$column.GoType}}{{end}} `gorm:"{{$column.ColumnName}};type:{{$column.ColumnType}};comment:{{$column.ColumnComment}}" json:"{{$column.JsonField}}"` 22 | {{$column.LinkTableClass}} {{$column.LinkTableClass}} `json:"{{$column.LinkTablePackage}}" gorm:"foreignKey:{{$column.LinkLabelName}};references:{{$column.GoField}};"` 23 | {{- else }} 24 | {{$column.GoField}} {{if eq $column.GoType "Time"}}time.Time{{else}}{{$column.GoType}}{{end}} `gorm:"{{$column.ColumnName}};type:{{$column.ColumnType}};comment:{{$column.ColumnComment}}" json:"{{$column.JsonField}}" {{if eq $column.IsRequired "1"}}binding:"required"{{- end }}` // {{$column.ColumnComment}} 25 | {{- end -}} 26 | {{end}} 27 | } 28 | 29 | func ({{.ClassName}}) TableName() string { 30 | return "{{.TableName}}" 31 | } 32 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/external_nats_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "github.com/nats-io/nats.go" 5 | "pandax/pkg/rule_engine/message" 6 | ) 7 | 8 | type externalNatsNode struct { 9 | bareNode 10 | Url string `json:"url"` 11 | Subject string `json:"subject"` 12 | Body string 13 | client *nats.Conn 14 | } 15 | 16 | type externalNatsNodeFactory struct{} 17 | 18 | func (f externalNatsNodeFactory) Name() string { return "NatsNode" } 19 | func (f externalNatsNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL } 20 | func (f externalNatsNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 21 | func (f externalNatsNodeFactory) Create(id string, meta Properties) (Node, error) { 22 | node := &externalNatsNode{ 23 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 24 | } 25 | _, err := decodePath(meta, node) 26 | if err != nil { 27 | return node, err 28 | } 29 | connect, err := nats.Connect(node.Url) 30 | if err != nil { 31 | return node, err 32 | } 33 | node.client = connect 34 | return node, nil 35 | } 36 | 37 | func (n *externalNatsNode) Handle(msg *message.Message) error { 38 | n.Debug(msg, message.DEBUGIN, "") 39 | defer n.client.Close() 40 | successLabelNode := n.GetLinkedNode("Success") 41 | failureLabelNode := n.GetLinkedNode("Failure") 42 | template, err := ParseTemplate(n.Body, msg.GetAllMap()) 43 | if err != nil { 44 | return err 45 | } 46 | err = n.client.Publish(n.Subject, []byte(template)) 47 | if err != nil { 48 | n.Debug(msg, message.DEBUGOUT, err.Error()) 49 | if failureLabelNode != nil { 50 | return failureLabelNode.Handle(msg) 51 | } else { 52 | return err 53 | } 54 | } 55 | if successLabelNode != nil { 56 | n.Debug(msg, message.DEBUGOUT, "") 57 | return successLabelNode.Handle(msg) 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /apps/log/api/log_login.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "github.com/PandaXGO/PandaKit/utils" 8 | "pandax/apps/log/entity" 9 | "pandax/apps/log/services" 10 | ) 11 | 12 | type LogLoginApi struct { 13 | LogLoginApp services.LogLoginModel 14 | } 15 | 16 | func (l *LogLoginApi) GetLoginLogList(rc *restfulx.ReqCtx) { 17 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 18 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 19 | loginLocation := restfulx.QueryParam(rc, "loginLocation") 20 | username := restfulx.QueryParam(rc, "username") 21 | list, total, err := l.LogLoginApp.FindListPage(pageNum, pageSize, entity.LogLogin{LoginLocation: loginLocation, Username: username}) 22 | biz.ErrIsNil(err, "查询任务日志失败") 23 | rc.ResData = model.ResultPage{ 24 | Total: total, 25 | PageNum: int64(pageNum), 26 | PageSize: int64(pageSize), 27 | Data: list, 28 | } 29 | } 30 | 31 | func (l *LogLoginApi) GetLoginLog(rc *restfulx.ReqCtx) { 32 | infoId := restfulx.PathParamInt(rc, "infoId") 33 | data, err := l.LogLoginApp.FindOne(int64(infoId)) 34 | biz.ErrIsNil(err, "查询日志信息失败") 35 | rc.ResData = data 36 | } 37 | 38 | func (l *LogLoginApi) UpdateLoginLog(rc *restfulx.ReqCtx) { 39 | var log entity.LogLogin 40 | restfulx.BindQuery(rc, &log) 41 | _, err := l.LogLoginApp.Update(log) 42 | biz.ErrIsNil(err, "修改日志信息失败") 43 | } 44 | 45 | func (l *LogLoginApi) DeleteLoginLog(rc *restfulx.ReqCtx) { 46 | infoIds := restfulx.PathParam(rc, "infoId") 47 | group := utils.IdsStrToIdsIntGroup(infoIds) 48 | err := l.LogLoginApp.Delete(group) 49 | biz.ErrIsNil(err, "删除日志失败") 50 | } 51 | 52 | func (l *LogLoginApi) DeleteAll(rc *restfulx.ReqCtx) { 53 | err := l.LogLoginApp.DeleteAll() 54 | biz.ErrIsNil(err, "清空日志失败") 55 | } 56 | -------------------------------------------------------------------------------- /pkg/tdengine/tdengine_log.go: -------------------------------------------------------------------------------- 1 | package tdengine 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/kakuilan/kgo" 8 | ) 9 | 10 | const logTableName = "device_logs" 11 | 12 | // 日志 TDengine 13 | type TdLog struct { 14 | Ts string `json:"ts" dc:"时间"` 15 | DeviceId string `json:"deviceId" dc:"设备标识"` 16 | TraceId string `json:"traceId" dc:"追踪"` 17 | Type string `json:"type" dc:"日志类型"` // 命令调用 上行 下行 18 | Content string `json:"content" dc:"日志内容"` 19 | } 20 | 21 | // CreateLogStable 添加LOG超级表 22 | func (s *TdEngine) CreateLogStable() (err error) { 23 | sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS ? (ts TIMESTAMP,deviceId NCHAR(64),traceId NCHAR(64),type NCHAR(20), content VARCHAR(1000))", s.dbName, logTableName) 24 | _, err = s.db.Exec(sql) 25 | return 26 | } 27 | 28 | // InsertLog 写入数据 29 | func (s *TdEngine) InsertLog(log *TdLog) (err error) { 30 | logs, err := kgo.KConv.Struct2Map(*log, "") 31 | if err != nil { 32 | return err 33 | } 34 | err = s.InsertDevice(logTableName, logs) 35 | return 36 | } 37 | 38 | // ClearLog 清理过期数据 39 | func (s *TdEngine) ClearLog() (err error) { 40 | ts := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02") 41 | 42 | sql := fmt.Sprintf("DELETE FROM %s.%s WHERE ts < ?", s.dbName, logTableName) 43 | _, err = s.db.Exec(sql, ts) 44 | 45 | return 46 | } 47 | 48 | // GetAllLog 超级表查询,多条数据 49 | func (s *TdEngine) GetAllLog(sql string, args ...any) (list []TdLog, err error) { 50 | rows, err := s.db.Query(sql, args...) 51 | if err != nil { 52 | return nil, err 53 | } 54 | defer rows.Close() 55 | 56 | for rows.Next() { 57 | var log TdLog 58 | 59 | err = rows.Scan(&log.Ts, &log.DeviceId, &log.TraceId, &log.Type, &log.Content) 60 | if err != nil { 61 | return nil, err 62 | } 63 | log.Ts = s.Time(log.Ts) 64 | 65 | list = append(list, log) 66 | } 67 | 68 | return 69 | } 70 | -------------------------------------------------------------------------------- /apps/rule/entity/rulechain.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "pandax/pkg/global/model" 5 | "time" 6 | ) 7 | 8 | type RuleChainBaseLabel struct { 9 | Id string `json:"id"` 10 | Root string `json:"root"` 11 | RuleName string `json:"ruleName"` 12 | } 13 | 14 | type RuleChainBase struct { 15 | model.BaseAuthModel 16 | Root string `json:"root" gorm:"comment:是否根节点,1 根链 0 普通链"` 17 | RuleName string `gorm:"ruleName;type:varchar(50);comment:名称" json:"ruleName"` 18 | RuleBase64 string `gorm:"ruleBase64;type:longtext;comment:Base64缩略图" json:"ruleBase64"` //缩略图 base64 19 | RuleRemark string `gorm:"ruleRemark;type:varchar(256);comment:说明" json:"ruleRemark"` 20 | } 21 | 22 | type RuleChain struct { 23 | RuleChainBase 24 | RuleDataJson string `gorm:"ruleDataJson;type:longtext;comment:Json数据" json:"ruleDataJson"` 25 | } 26 | 27 | func (RuleChain) TableName() string { 28 | return "rule_chain" 29 | } 30 | 31 | type RuleChainMsgLog struct { 32 | MessageId string `gorm:"message_id;type:varchar(64);comment:消息Id" json:"messageId"` 33 | OrgId int64 `json:"orgId" gorm:"type:int;comment:机构ID"` 34 | Owner string `json:"owner" gorm:"type:varchar(64);comment:创建者,所有者"` 35 | MsgType string `gorm:"msg_type;type:varchar(64);comment:消息类型" json:"msgType"` 36 | DeviceId string `gorm:"device_id;type:varchar(64);comment:设备ID" json:"deviceId"` 37 | DeviceName string `gorm:"device_name;type:varchar(255);comment:设备名称" json:"deviceName"` 38 | Ts time.Time `gorm:"ts;type:varchar(64);comment:时间" json:"ts"` 39 | Content string `gorm:"content;type:varchar(1024);comment:内容" json:"content"` 40 | CreatedAt time.Time `gorm:"column:create_at" json:"create_at"` // 创建时间 41 | 42 | RoleId int64 `gorm:"-"` // 角色数据权限 43 | } 44 | 45 | func (RuleChainMsgLog) TableName() string { 46 | return "rule_chain_msg_log" 47 | } 48 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/factory.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const ( 8 | NODE_CATEGORY_FILTER = "filter" 9 | NODE_CATEGORY_ACTION = "action" 10 | NODE_CATEGORY_ENRICHMENT = "enrichment" 11 | NODE_CATEGORY_TRANSFORM = "transform" 12 | NODE_CATEGORY_EXTERNAL = "external" 13 | NODE_CATEGORY_OTHERS = "others" 14 | NODE_CATEGORY_FLOWS = "flows" 15 | ) 16 | 17 | type Factory interface { 18 | Name() string 19 | Category() string 20 | Labels() []string 21 | Create(id string, meta Properties) (Node, error) 22 | } 23 | 24 | var ( 25 | // 所有节点对应关系 26 | allNodeFactories map[string]Factory = make(map[string]Factory) 27 | 28 | // 所有节点的类型MAP 29 | allNodeCategories map[string][]map[string]interface{} = make(map[string][]map[string]interface{}) 30 | allCategories []map[string]interface{} = make([]map[string]interface{}, 0) 31 | ) 32 | 33 | func RegisterFactory(f Factory) { 34 | allNodeFactories[f.Name()] = f 35 | 36 | if allNodeCategories[f.Category()] == nil { 37 | allNodeCategories[f.Category()] = []map[string]interface{}{} 38 | } 39 | allNodeCategories[f.Category()] = append(allNodeCategories[f.Category()], map[string]interface{}{"name": f.Name(), "labels": f.Labels()}) 40 | allCategories = append(allCategories, map[string]interface{}{"name": f.Name(), "labels": f.Labels()}) 41 | } 42 | 43 | // NewNode is the only way to create a new node 44 | func NewNode(nodeType string, id string, meta Properties) (Node, error) { 45 | if f, found := allNodeFactories[nodeType]; found { 46 | return f.Create(id, meta) 47 | } 48 | return nil, fmt.Errorf("invalid node type '%s'", nodeType) 49 | } 50 | 51 | // GetCategoryNodes 获取所有分类节点 52 | func GetCategoryNodes() map[string][]map[string]interface{} { return allNodeCategories } 53 | 54 | func GetCategory() []map[string]interface{} { return allCategories } 55 | -------------------------------------------------------------------------------- /apps/job/services/log_job.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "pandax/apps/job/entity" 5 | "pandax/pkg/global" 6 | "pandax/pkg/global/model" 7 | ) 8 | 9 | type ( 10 | JobLogModel interface { 11 | Insert(data entity.JobLog) (*entity.JobLog, error) 12 | FindListPage(page, pageSize int, data entity.JobLog) (*[]entity.JobLog, int64, error) 13 | Delete(infoId []string) error 14 | DeleteAll() error 15 | } 16 | 17 | JobLogModelImpl struct { 18 | table string 19 | } 20 | ) 21 | 22 | var JobLogModelDao JobLogModel = &JobLogModelImpl{ 23 | table: `job_logs`, 24 | } 25 | 26 | func (m *JobLogModelImpl) Insert(data entity.JobLog) (*entity.JobLog, error) { 27 | err := global.Db.Table(m.table).Create(&data).Error 28 | return &data, err 29 | } 30 | 31 | func (m *JobLogModelImpl) FindListPage(page, pageSize int, data entity.JobLog) (*[]entity.JobLog, int64, error) { 32 | list := make([]entity.JobLog, 0) 33 | var total int64 = 0 34 | offset := pageSize * (page - 1) 35 | db := global.Db.Table(m.table) 36 | // 此处填写 where参数判断 37 | if data.Status != "" { 38 | db = db.Where("status = ?", data.Status) 39 | } 40 | if data.Name != "" { 41 | db = db.Where("name like ?", "%"+data.Name+"%") 42 | } 43 | 44 | // 组织数据访问权限 45 | err := model.OrgAuthSet(db, data.RoleId, data.Owner) 46 | if err != nil { 47 | return &list, total, err 48 | } 49 | err = db.Count(&total).Error 50 | if err != nil { 51 | return &list, total, err 52 | } 53 | err = db.Order("create_time desc").Limit(pageSize).Offset(offset).Find(&list).Error 54 | return &list, total, err 55 | } 56 | 57 | func (m *JobLogModelImpl) Delete(logIds []string) error { 58 | err := global.Db.Table(m.table).Delete(&entity.JobLog{}, "id in (?)", logIds).Error 59 | return err 60 | } 61 | 62 | func (m *JobLogModelImpl) DeleteAll() error { 63 | return global.Db.Exec("DELETE FROM job_logs").Error 64 | } 65 | -------------------------------------------------------------------------------- /apps/job/router/job_log.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/job/api" 7 | "pandax/apps/job/services" 8 | 9 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 10 | "github.com/emicklei/go-restful/v3" 11 | ) 12 | 13 | func InitJobLogRouter(container *restful.Container) { 14 | // Job日志 15 | s := &api.JobLogApi{ 16 | JobLogApp: services.JobLogModelDao, 17 | } 18 | 19 | ws := new(restful.WebService) 20 | ws.Path("/job/log").Produces(restful.MIME_JSON) 21 | tags := []string{"任务日志"} 22 | 23 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 24 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志列表").Handle(s.GetJobLogList) 25 | }). 26 | Doc("获取操作日志列表"). 27 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 28 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 29 | Param(ws.QueryParameter("status", "status").DataType("string")). 30 | Param(ws.QueryParameter("name", "name").DataType("string")). 31 | Metadata(restfulspec.KeyOpenAPITags, tags). 32 | Writes(model.ResultPage{}). 33 | Returns(200, "OK", model.ResultPage{})) 34 | 35 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) { 36 | restfulx.NewReqCtx(request, response).WithLog("删除操作日志信息").Handle(s.DeleteJobLog) 37 | }). 38 | Doc("删除操作日志信息"). 39 | Metadata(restfulspec.KeyOpenAPITags, tags). 40 | Param(ws.PathParameter("id", "多id 1,2,3").DataType("string"))) 41 | 42 | ws.Route(ws.DELETE("/all").To(func(request *restful.Request, response *restful.Response) { 43 | restfulx.NewReqCtx(request, response).WithLog("清空操作日志信息").Handle(s.DeleteAll) 44 | }). 45 | Doc("清空操作日志信息"). 46 | Metadata(restfulspec.KeyOpenAPITags, tags)) 47 | 48 | container.Add(ws) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/action_log_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/apps/rule/entity" 5 | "pandax/apps/rule/services" 6 | "pandax/pkg/rule_engine/message" 7 | ) 8 | 9 | type logNode struct { 10 | bareNode 11 | Script string `json:"script"` 12 | } 13 | 14 | type logNodeFactory struct{} 15 | 16 | func (f logNodeFactory) Name() string { return "LogNode" } 17 | func (f logNodeFactory) Category() string { return NODE_CATEGORY_ACTION } 18 | func (f logNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 19 | func (f logNodeFactory) Create(id string, meta Properties) (Node, error) { 20 | node := &logNode{ 21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 22 | } 23 | return decodePath(meta, node) 24 | } 25 | 26 | func (n *logNode) Handle(msg *message.Message) error { 27 | successLableNode := n.GetLinkedNode("Success") 28 | failureLableNode := n.GetLinkedNode("Failure") 29 | 30 | scriptEngine := NewScriptEngine(*msg, "ToString", n.Script) 31 | logMessage, err := scriptEngine.ScriptToString() 32 | if err != nil { 33 | if failureLableNode != nil { 34 | return failureLableNode.Handle(msg) 35 | } else { 36 | return err 37 | } 38 | } 39 | services.RuleChainMsgLogModelDao.Insert(entity.RuleChainMsgLog{ 40 | MessageId: msg.Id, 41 | MsgType: msg.MsgType, 42 | DeviceId: msg.Metadata.GetStringValue("deviceId"), 43 | OrgId: int64(msg.Metadata.GetIntValue("orgId")), 44 | Owner: msg.Metadata.GetStringValue("owner"), 45 | DeviceName: msg.Metadata.GetStringValue("deviceName"), 46 | Ts: msg.Ts, 47 | Content: logMessage, 48 | }) 49 | if err != nil { 50 | if failureLableNode != nil { 51 | return failureLableNode.Handle(msg) 52 | } else { 53 | return err 54 | } 55 | } 56 | if successLableNode != nil { 57 | return successLableNode.Handle(msg) 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/transform_delete_key_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | "strings" 6 | ) 7 | 8 | type transformDeleteKeyNode struct { 9 | bareNode 10 | FormType string `json:"formType" yaml:"formType"` //msg metadata 11 | Keys string `json:"keys" yaml:"keys"` 12 | } 13 | type transformDeleteKeyNodeFactory struct{} 14 | 15 | func (f transformDeleteKeyNodeFactory) Name() string { return "DeleteKeyNode" } 16 | func (f transformDeleteKeyNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM } 17 | func (f transformDeleteKeyNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 18 | func (f transformDeleteKeyNodeFactory) Create(id string, meta Properties) (Node, error) { 19 | node := &transformDeleteKeyNode{ 20 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 21 | } 22 | return decodePath(meta, node) 23 | } 24 | 25 | func (n *transformDeleteKeyNode) Handle(msg *message.Message) error { 26 | n.Debug(msg, message.DEBUGIN, "") 27 | 28 | successLabelNode := n.GetLinkedNode("Success") 29 | failureLabelNode := n.GetLinkedNode("Failure") 30 | keys := strings.Split(n.Keys, ",") 31 | if n.FormType == "msg" { 32 | data := msg.Msg 33 | for _, key := range keys { 34 | if _, found := data[key]; found { 35 | delete(data, key) 36 | msg.Msg = data 37 | } 38 | } 39 | } else if n.FormType == "metadata" { 40 | data := msg.Metadata 41 | for _, key := range keys { 42 | if data.GetValue(key) != nil { 43 | delete(data, key) 44 | msg.Metadata = data 45 | } 46 | } 47 | } else { 48 | if failureLabelNode != nil { 49 | n.Debug(msg, message.DEBUGOUT, "未识别FormType") 50 | return failureLabelNode.Handle(msg) 51 | } 52 | } 53 | if successLabelNode != nil { 54 | n.Debug(msg, message.DEBUGOUT, "") 55 | return successLabelNode.Handle(msg) 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /apps/develop/router/gen.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/restfulx" 5 | "pandax/apps/develop/api" 6 | "pandax/apps/develop/services" 7 | 8 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 9 | "github.com/emicklei/go-restful/v3" 10 | ) 11 | 12 | func InitGenRouter(container *restful.Container) { 13 | 14 | // 登录日志 15 | s := &api.GenApi{ 16 | GenTableApp: services.DevGenTableModelDao, 17 | } 18 | 19 | ws := new(restful.WebService) 20 | ws.Path("/develop/code/gen").Produces(restful.MIME_JSON) 21 | tags := []string{"代码生成"} 22 | 23 | ws.Route(ws.GET("/preview/{tableId}").To(func(request *restful.Request, response *restful.Response) { 24 | restfulx.NewReqCtx(request, response).WithLog("获取生成代码视图").Handle(s.Preview) 25 | }). 26 | Doc("获取生成代码视图"). 27 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")). 28 | Metadata(restfulspec.KeyOpenAPITags, tags). 29 | Returns(200, "OK", map[string]any{}). 30 | Returns(404, "Not Found", nil)) 31 | 32 | ws.Route(ws.GET("/code/{tableId}").To(func(request *restful.Request, response *restful.Response) { 33 | restfulx.NewReqCtx(request, response).WithLog("生成代码").Handle(s.GenCode) 34 | }). 35 | Doc("生成代码"). 36 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")). 37 | Metadata(restfulspec.KeyOpenAPITags, tags). 38 | Returns(200, "OK", map[string]any{}). 39 | Returns(404, "Not Found", nil)) 40 | 41 | ws.Route(ws.GET("/configure/{tableId}").To(func(request *restful.Request, response *restful.Response) { 42 | restfulx.NewReqCtx(request, response).WithLog("生成配置").Handle(s.GenConfigure) 43 | }). 44 | Doc("生成配置"). 45 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")). 46 | Metadata(restfulspec.KeyOpenAPITags, tags). 47 | Returns(200, "OK", map[string]any{}). 48 | Returns(404, "Not Found", nil)) 49 | 50 | container.Add(ws) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/PandaXGO/PandaKit/biz" 7 | "github.com/PandaXGO/PandaKit/utils" 8 | "path/filepath" 9 | ) 10 | 11 | func InitConfig(configFilePath string) *Config { 12 | // 获取启动参数中,配置文件的绝对路径 13 | path, _ := filepath.Abs(configFilePath) 14 | startConfigParam = &CmdConfigParam{ConfigFilePath: path} 15 | // 读取配置文件信息 16 | yc := &Config{} 17 | if err := utils.LoadYml(startConfigParam.ConfigFilePath, yc); err != nil { 18 | panic(any(fmt.Sprintf("读取配置文件[%s]失败: %s", startConfigParam.ConfigFilePath, err.Error()))) 19 | } 20 | // 校验配置文件内容信息 21 | yc.Valid() 22 | 23 | return yc 24 | 25 | } 26 | 27 | // 启动配置参数 28 | type CmdConfigParam struct { 29 | ConfigFilePath string // -e 配置文件路径 30 | } 31 | 32 | // 启动可执行文件时的参数 33 | var startConfigParam *CmdConfigParam 34 | 35 | // yaml配置文件映射对象 36 | type Config struct { 37 | App *App `yaml:"app"` 38 | Server *Server `yaml:"server"` 39 | Queue *Queue `yaml:"queue"` 40 | Jwt *Jwt `yaml:"jwt"` 41 | Redis *Redis `yaml:"redis"` 42 | Mysql *Mysql `yaml:"mysql"` 43 | Postgresql *Postgresql `yaml:"postgresql"` 44 | Oss *Oss `yaml:"oss"` 45 | Taos *Taos `yaml:"taos"` 46 | Mqtt *Mqtt `yaml:"mqtt"` 47 | Casbin *Casbin `yaml:"casbin"` 48 | Gen *Gen `yaml:"gen"` 49 | Ys *Ys `yaml:"ys"` 50 | Log *Log `yaml:"log"` 51 | } 52 | 53 | // 配置文件内容校验 54 | func (c *Config) Valid() { 55 | biz.IsTrue(c.Jwt != nil, "配置文件的[jwt]信息不能为空") 56 | c.Jwt.Valid() 57 | } 58 | 59 | // 获取执行可执行文件时,指定的启动参数 60 | func getStartConfig() *CmdConfigParam { 61 | configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录") 62 | flag.Parse() 63 | // 获取配置文件绝对路径 64 | path, _ := filepath.Abs(*configFilePath) 65 | sc := &CmdConfigParam{ConfigFilePath: path} 66 | return sc 67 | } 68 | -------------------------------------------------------------------------------- /apps/system/entity/menu.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type SysMenu struct { 6 | MenuId int64 `json:"menuId" gorm:"primary_key;AUTO_INCREMENT"` 7 | MenuName string `json:"menuName" gorm:"type:varchar(128);"` 8 | Title string `json:"title" gorm:"type:varchar(64);"` 9 | ParentId int64 `json:"parentId" gorm:"type:int;"` 10 | Sort int64 `json:"sort" gorm:"type:int;"` 11 | Icon string `json:"icon" gorm:"type:varchar(128);"` 12 | Path string `json:"path" gorm:"type:varchar(128);"` 13 | Component string `json:"component" gorm:"type:varchar(255);"` // 组件路径 14 | IsIframe string `json:"isIframe" gorm:"type:varchar(1);"` //是否为内嵌 15 | IsLink string `json:"isLink" gorm:"type:varchar(255);"` //是否超链接菜单 16 | MenuType string `json:"menuType" gorm:"type:varchar(1);"` //菜单类型(M目录 C菜单 F按钮) 17 | IsHide string `json:"isHide" gorm:"type:varchar(1);"` //显示状态(0显示 1隐藏) 18 | IsKeepAlive string `json:"isKeepAlive" gorm:"type:varchar(1);"` //是否缓存组件状态(0是 1否) 19 | IsAffix string `json:"isAffix" gorm:"type:varchar(1);"` //是否固定在 tagsView 栏上(0是 1否) 20 | Permission string `json:"permission" gorm:"type:varchar(32);"` //权限标识 21 | Status string `json:"status" gorm:"type:varchar(1);` // 菜单状态(0正常 1停用) 22 | CreateBy string `json:"createBy" gorm:"type:varchar(128);"` 23 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);"` 24 | Remark string `json:"remark" gorm:"type:varchar(256);` // 备注 25 | Children []SysMenu `json:"children" gorm:"-"` 26 | model.BaseModel 27 | } 28 | 29 | type MenuLable struct { 30 | MenuId int64 `json:"menuId" gorm:"-"` 31 | MenuName string `json:"menuName" gorm:"-"` 32 | Children []MenuLable `json:"children" gorm:"-"` 33 | } 34 | 35 | type MenuRole struct { 36 | SysMenu 37 | IsSelect bool `json:"is_select" gorm:"-"` 38 | } 39 | -------------------------------------------------------------------------------- /apps/system/router/upload.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/restfulx" 5 | "pandax/apps/system/api" 6 | 7 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 8 | "github.com/emicklei/go-restful/v3" 9 | ) 10 | 11 | func InitUploadRouter(container *restful.Container) { 12 | s := &api.UploadApi{} 13 | ws := new(restful.WebService) 14 | ws.Path("/upload").Produces(restful.MIME_JSON) 15 | tags := []string{"system", "文件"} 16 | 17 | ws.Route(ws.POST("/up/{path}").To(func(request *restful.Request, response *restful.Response) { 18 | restfulx.NewReqCtx(request, response).WithLog("上传图片").Handle(s.UploadImage) 19 | }). 20 | Doc("上传图片"). 21 | Param(ws.FormParameter("imagefile", "文件")). 22 | Param(ws.PathParameter("path", "文件类型")). 23 | Metadata(restfulspec.KeyOpenAPITags, tags). 24 | Returns(200, "OK", map[string]string{})) 25 | 26 | ws.Route(ws.POST("/up/oss").To(func(request *restful.Request, response *restful.Response) { 27 | restfulx.NewReqCtx(request, response).WithLog("上传图片").Handle(s.UplaodToOss) 28 | }). 29 | Doc("上传图片到oss"). 30 | Param(ws.FormParameter("imagefile", "文件")). 31 | Metadata(restfulspec.KeyOpenAPITags, tags). 32 | Returns(200, "OK", map[string]string{})) 33 | 34 | ws.Route(ws.GET("/down/{path}/{subpath:*}").To(func(request *restful.Request, response *restful.Response) { 35 | restfulx.NewReqCtx(request, response).WithNeedToken(false).WithNeedCasbin(false).WithLog("获取文件").Handle(s.GetImage) 36 | }). 37 | Doc("获取文件"). 38 | Param(ws.PathParameter("subpath", "文件名")). 39 | Metadata(restfulspec.KeyOpenAPITags, tags)) 40 | 41 | ws.Route(ws.DELETE("/delete").To(func(request *restful.Request, response *restful.Response) { 42 | restfulx.NewReqCtx(request, response).WithLog("删除图片").Handle(s.DeleteImage) 43 | }). 44 | Doc("删除图片"). 45 | Metadata(restfulspec.KeyOpenAPITags, tags). 46 | Param(ws.QueryParameter("fileName", "文件名称").DataType("string"))) 47 | 48 | container.Add(ws) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/action_clear_alarm_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "pandax/apps/device/services" 7 | "pandax/pkg/global" 8 | "pandax/pkg/rule_engine/message" 9 | ) 10 | 11 | const ClearAlarmNodeName = "ClearAlarmNode" 12 | 13 | type clearAlarmNodeFactory struct{} 14 | 15 | type clearAlarmNode struct { 16 | bareNode 17 | Script string `json:"script" yaml:"script"` 18 | AlarmType string `json:"alarmType" yaml:"alarmType"` 19 | } 20 | 21 | func (f clearAlarmNodeFactory) Name() string { return ClearAlarmNodeName } 22 | func (f clearAlarmNodeFactory) Category() string { return NODE_CATEGORY_ACTION } 23 | func (f clearAlarmNodeFactory) Labels() []string { return []string{"Cleared", "Failure"} } 24 | func (f clearAlarmNodeFactory) Create(id string, meta Properties) (Node, error) { 25 | node := &clearAlarmNode{ 26 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 27 | } 28 | return decodePath(meta, node) 29 | } 30 | 31 | func (n *clearAlarmNode) Handle(msg *message.Message) error { 32 | n.Debug(msg, message.DEBUGIN, "") 33 | 34 | cleared := n.GetLinkedNode("Cleared") 35 | failure := n.GetLinkedNode("Failure") 36 | var deviceId string 37 | if did, ok := msg.Metadata.GetValue("deviceId").(string); ok { 38 | deviceId = did 39 | } else { 40 | return errors.New("元数据中为获取到设备ID") 41 | } 42 | var err error 43 | alarm, err := services.DeviceAlarmModelDao.FindOneByType(deviceId, n.AlarmType, "0") 44 | if err == nil { 45 | alarm.State = global.CLEARED 46 | marshal, _ := json.Marshal(msg.Msg) 47 | alarm.Details = string(marshal) 48 | err = services.DeviceAlarmModelDao.Update(*alarm) 49 | if err == nil { 50 | if cleared != nil { 51 | n.Debug(msg, message.DEBUGOUT, "") 52 | return cleared.Handle(msg) 53 | } 54 | } 55 | } 56 | if err != nil { 57 | n.Debug(msg, message.DEBUGOUT, err.Error()) 58 | if failure != nil { 59 | return failure.Handle(msg) 60 | } else { 61 | return err 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/middleware/log.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/PandaXGO/PandaKit/biz" 7 | "github.com/PandaXGO/PandaKit/restfulx" 8 | "github.com/PandaXGO/PandaKit/utils" 9 | "pandax/pkg/global" 10 | "reflect" 11 | "runtime/debug" 12 | 13 | "github.com/sirupsen/logrus" 14 | ) 15 | 16 | func LogHandler(rc *restfulx.ReqCtx) error { 17 | li := rc.LogInfo 18 | if li == nil { 19 | return nil 20 | } 21 | 22 | lfs := logrus.Fields{} 23 | if la := rc.LoginAccount; la != nil { 24 | lfs["uid"] = la.UserId 25 | lfs["uname"] = la.UserName 26 | } 27 | 28 | req := rc.Request.Request 29 | lfs[req.Method] = req.URL.Path 30 | 31 | if err := rc.Err; err != nil { 32 | global.Log.WithFields(lfs).Error(getErrMsg(rc, err)) 33 | return nil 34 | } 35 | global.Log.WithFields(lfs).Info(getLogMsg(rc)) 36 | return nil 37 | } 38 | 39 | func getLogMsg(rc *restfulx.ReqCtx) string { 40 | msg := rc.LogInfo.Description + fmt.Sprintf(" ->%dms", rc.Timed) 41 | if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) { 42 | rb, _ := json.Marshal(rc.ReqParam) 43 | msg = msg + fmt.Sprintf("\n--> %s", string(rb)) 44 | } 45 | 46 | // 返回结果不为空,则记录返回结果 47 | if rc.LogInfo.LogResp && !utils.IsBlank(reflect.ValueOf(rc.ResData)) { 48 | respB, _ := json.Marshal(rc.ResData) 49 | msg = msg + fmt.Sprintf("\n<-- %s", string(respB)) 50 | } 51 | return msg 52 | } 53 | 54 | func getErrMsg(rc *restfulx.ReqCtx, err any) string { 55 | msg := rc.LogInfo.Description 56 | if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) { 57 | rb, _ := json.Marshal(rc.ReqParam) 58 | msg = msg + fmt.Sprintf("\n--> %s", string(rb)) 59 | } 60 | 61 | var errMsg string 62 | switch t := err.(type) { 63 | case *biz.BizError: 64 | errMsg = fmt.Sprintf("\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error()) 65 | case error: 66 | errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t.Error(), string(debug.Stack())) 67 | case string: 68 | errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t, string(debug.Stack())) 69 | } 70 | return (msg + errMsg) 71 | } 72 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | app: 2 | name: pandax 3 | version: 1.0.0 4 | 5 | server: 6 | # debug release test 7 | model: release 8 | port: 7788 9 | # iothub服务端口 使用的rpc端口 9000 9001 可能与minio端口冲突 10 | grpc-port: 9001 11 | http-port: 9002 12 | tcp-port: 9003 13 | cors: true 14 | # 接口限流 15 | rate: 16 | enable: true 17 | rate-num: 100 18 | db-type: mysql 19 | # 是否开启数据库表初始化 20 | isInitTable: false 21 | excel-dir: ./resource/excel/ 22 | tls: 23 | enable: false 24 | key-file: ./resource/default.key 25 | cert-file: ./resource/default.pem 26 | jwt: 27 | key: PandaX 28 | # 过期时间单位秒 7天 小时 29 | expire-time: 168 30 | #数据上报并发识别任务数量限制 31 | queue: 32 | queue-pool: 5 #消息队列池 33 | task-num: 100 #任务队列数, 34 | ch-num: 3000 #并发执行数,同时处理多少条数据 35 | 36 | redis: 37 | host: 127.0.0.1 38 | password: 123456 39 | port: 6379 40 | 41 | mysql: 42 | host: 127.0.0.1:3306 43 | username: root 44 | password: 123456 45 | db-name: pandax 46 | config: charset=utf8&loc=Local&parseTime=true 47 | 48 | postgresql: 49 | username: postgres 50 | password: 123456 51 | host: 127.0.0.1 52 | port: 5432 53 | db-name: pandax_iot 54 | max-idle-conns: 10 55 | max-open-conns: 10 56 | 57 | # mini0 58 | oss: 59 | endpoint: 127.0.0.1:9000 60 | accessKey: minioadmin 61 | secretKey: minioadmin 62 | bucketName: pandaxiot 63 | useSSL: false 64 | 65 | taos: 66 | username: "root" 67 | password: "taosdata" 68 | host: "127.0.0.1:6041" 69 | database: "iot" 70 | config: "" 71 | 72 | mqtt: 73 | broker: tcp://127.0.0.1:1883 74 | httpBroker: http://127.0.0.1:18083/api 75 | qos: 1 76 | username: dbda318b31319d49 77 | password: wwqPbYu9BCsIFgW3m0aICmRrvxUHcIi44AQuG24wQARE 78 | 79 | casbin: 80 | model-path: './resource/rbac_model.conf' 81 | 82 | gen: 83 | # 代码生成读取的数据库名称 84 | dbname: pandax_iot 85 | # 代码生成是使用前端代码存放位置,需要指定到src文件夹,相对路径 86 | frontpath: ../PandaUi/src 87 | 88 | log: 89 | # 日志等级, trace, debug, info, warn, error, fatal 90 | level: info 91 | # file: 92 | # path: ./ 93 | # name: panda_log.log 94 | -------------------------------------------------------------------------------- /apps/system/api/vo/systemVo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "pandax/apps/system/entity" 4 | 5 | /** 6 | * @Description 7 | * @Author 熊猫 8 | * @Date 2022/8/4 15:25 9 | **/ 10 | 11 | type OrganizationTreeVo struct { 12 | Organizations []entity.OrganizationLable `json:"organizations"` 13 | CheckedKeys []int64 `json:"checkedKeys"` 14 | } 15 | 16 | type MenuTreeVo struct { 17 | Menus []entity.MenuLable `json:"menus"` 18 | CheckedKeys []int64 `json:"checkedKeys"` 19 | } 20 | 21 | type MenuPermisVo struct { 22 | Menus []RouterVo `json:"menus"` 23 | Permissions []string `json:"permissions"` 24 | } 25 | 26 | type CaptchaVo struct { 27 | Base64Captcha string `json:"base64Captcha"` 28 | CaptchaId string `json:"captchaId"` 29 | } 30 | 31 | type TokenVo struct { 32 | Token string `json:"token"` 33 | Expire int64 `json:"expire"` 34 | } 35 | 36 | type AuthVo struct { 37 | User entity.SysUserView `json:"user"` 38 | Role entity.SysRole `json:"role"` 39 | Permissions []string `json:"permissions"` 40 | Menus []RouterVo `json:"menus"` 41 | } 42 | 43 | type UserProfileVo struct { 44 | Data any `json:"data"` 45 | PostIds []int64 `json:"postIds"` 46 | RoleIds []int64 `json:"roleIds"` 47 | Roles []entity.SysRole `json:"roles"` 48 | Posts []entity.SysPost `json:"posts"` 49 | Organization []entity.SysOrganization `json:"organization"` 50 | } 51 | 52 | type UserVo struct { 53 | Data any `json:"data"` 54 | PostIds string `json:"postIds"` 55 | RoleIds string `json:"roleIds"` 56 | Roles []entity.SysRole `json:"roles"` 57 | Posts []entity.SysPost `json:"posts"` 58 | Organizations []entity.SysOrganization `json:"organizations"` 59 | } 60 | 61 | type UserRolePost struct { 62 | Roles []entity.SysRole `json:"roles"` 63 | Posts []entity.SysPost `json:"posts"` 64 | } 65 | -------------------------------------------------------------------------------- /apps/system/api/tenant.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "github.com/PandaXGO/PandaKit/utils" 7 | "pandax/apps/system/entity" 8 | "pandax/apps/system/services" 9 | ) 10 | 11 | type SysTenantsApi struct { 12 | SysTenantsApp services.SysTenantsModel 13 | } 14 | 15 | func (p *SysTenantsApi) GetSysTenantsList(rc *restfulx.ReqCtx) { 16 | data := entity.SysTenants{} 17 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 18 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 19 | 20 | list, total := p.SysTenantsApp.FindListPage(pageNum, pageSize, data) 21 | 22 | rc.ResData = model.ResultPage{ 23 | Total: total, 24 | PageNum: int64(pageNum), 25 | PageSize: int64(pageSize), 26 | Data: list, 27 | } 28 | } 29 | 30 | func (p *SysTenantsApi) GetSysTenantsAll(rc *restfulx.ReqCtx) { 31 | list := make([]entity.SysTenants, 0) 32 | if rc.LoginAccount.RoleKey == "admin" { 33 | data := entity.SysTenants{} 34 | list = *p.SysTenantsApp.FindList(data) 35 | } else { 36 | list = append(list, *p.SysTenantsApp.FindOne(rc.LoginAccount.TenantId)) 37 | } 38 | rc.ResData = list 39 | } 40 | 41 | func (p *SysTenantsApi) GetSysTenants(rc *restfulx.ReqCtx) { 42 | tenantId := restfulx.PathParamInt(rc, "tenantId") 43 | p.SysTenantsApp.FindOne(int64(tenantId)) 44 | } 45 | 46 | func (p *SysTenantsApi) InsertSysTenants(rc *restfulx.ReqCtx) { 47 | var data entity.SysTenants 48 | restfulx.BindQuery(rc, &data) 49 | 50 | p.SysTenantsApp.Insert(data) 51 | } 52 | 53 | func (p *SysTenantsApi) UpdateSysTenants(rc *restfulx.ReqCtx) { 54 | var data entity.SysTenants 55 | restfulx.BindQuery(rc, &data) 56 | 57 | p.SysTenantsApp.Update(data) 58 | } 59 | 60 | func (p *SysTenantsApi) DeleteSysTenants(rc *restfulx.ReqCtx) { 61 | tenantId := rc.Request.PathParameter("tenantId") 62 | tenantIds := utils.IdsStrToIdsIntGroup(tenantId) 63 | p.SysTenantsApp.Delete(tenantIds) 64 | } 65 | 66 | // IsTenantAdmin 是否为主租户 67 | func IsTenantAdmin(tenantId int64) bool { 68 | return tenantId == 1 69 | } 70 | -------------------------------------------------------------------------------- /apps/device/api/device_alarm.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // ========================================================================== 4 | import ( 5 | "github.com/PandaXGO/PandaKit/biz" 6 | "github.com/PandaXGO/PandaKit/model" 7 | "github.com/PandaXGO/PandaKit/restfulx" 8 | "strings" 9 | 10 | "pandax/apps/device/entity" 11 | "pandax/apps/device/services" 12 | ) 13 | 14 | type DeviceAlarmApi struct { 15 | DeviceAlarmApp services.DeviceAlarmModel 16 | } 17 | 18 | func (p *DeviceAlarmApi) GetDeviceAlarmPanel(rc *restfulx.ReqCtx) { 19 | panel, _ := p.DeviceAlarmApp.FindAlarmPanel() 20 | rc.ResData = panel 21 | } 22 | 23 | // GetDeviceAlarmList 告警列表数据 24 | func (p *DeviceAlarmApi) GetDeviceAlarmList(rc *restfulx.ReqCtx) { 25 | data := entity.DeviceAlarmForm{} 26 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 27 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 28 | data.DeviceId = restfulx.QueryParam(rc, "deviceId") 29 | data.Type = restfulx.QueryParam(rc, "type") 30 | data.Level = restfulx.QueryParam(rc, "level") 31 | data.State = restfulx.QueryParam(rc, "state") 32 | data.StartTime = restfulx.QueryParam(rc, "startTime") 33 | data.EndTime = restfulx.QueryParam(rc, "endTime") 34 | 35 | data.RoleId = rc.LoginAccount.RoleId 36 | data.Owner = rc.LoginAccount.UserName 37 | 38 | list, total, err := p.DeviceAlarmApp.FindListPage(pageNum, pageSize, data) 39 | biz.ErrIsNil(err, "设备查询失败") 40 | rc.ResData = model.ResultPage{ 41 | Total: total, 42 | PageNum: int64(pageNum), 43 | PageSize: int64(pageSize), 44 | Data: list, 45 | } 46 | } 47 | 48 | // UpdateDeviceAlarm 修改告警 49 | func (p *DeviceAlarmApi) UpdateDeviceAlarm(rc *restfulx.ReqCtx) { 50 | var data entity.DeviceAlarm 51 | restfulx.BindJsonAndValid(rc, &data) 52 | err := p.DeviceAlarmApp.Update(data) 53 | biz.ErrIsNil(err, "修改告警失败") 54 | } 55 | 56 | // DeleteDeviceAlarm 删除告警 57 | func (p *DeviceAlarmApi) DeleteDeviceAlarm(rc *restfulx.ReqCtx) { 58 | id := restfulx.PathParam(rc, "id") 59 | ids := strings.Split(id, ",") 60 | err := p.DeviceAlarmApp.Delete(ids) 61 | biz.ErrIsNil(err, "删除告警失败") 62 | } 63 | -------------------------------------------------------------------------------- /pkg/tool/base.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "reflect" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | func ToCamelCase(s string) string { 10 | re := regexp.MustCompile(`[_\W]+`) 11 | words := re.Split(s, -1) 12 | for i := range words { 13 | if i != 0 { 14 | words[i] = strings.Title(words[i]) 15 | } 16 | } 17 | return strings.Join(words, "") 18 | } 19 | 20 | func RegexpKey(str string) []string { 21 | // 定义正则表达式 22 | re := regexp.MustCompile(`\${([^}]+)}`) 23 | matches := re.FindAllStringSubmatch(str, -1) 24 | // 提取匹配项的内容 25 | var results []string 26 | for _, match := range matches { 27 | if len(match) >= 2 { 28 | results = append(results, match[1]) 29 | } 30 | } 31 | return results 32 | } 33 | 34 | func RegexpGetSql(str string) string { 35 | // 定义正则表达式 36 | re := regexp.MustCompile(`\${([^}]+)}`) 37 | return re.ReplaceAllString(str, "?") 38 | } 39 | 40 | func GetStructKeys(obj interface{}) []string { 41 | val := reflect.ValueOf(obj) 42 | typ := val.Type() 43 | 44 | keys := make([]string, 0, typ.NumField()) 45 | 46 | for i := 0; i < typ.NumField(); i++ { 47 | field := typ.Field(i) 48 | keys = append(keys, field.Name) 49 | } 50 | 51 | return keys 52 | } 53 | 54 | func GetMapKeys(obj map[string]interface{}) []string { 55 | keys := make([]string, 0, len(obj)) 56 | 57 | for key := range obj { 58 | keys = append(keys, key) 59 | } 60 | 61 | return keys 62 | } 63 | 64 | func CheckInterfaceIsArray(data interface{}) (bool, []map[string]interface{}) { 65 | if data == nil { 66 | return false, nil 67 | } 68 | valueType := reflect.TypeOf(data) 69 | // 判断类型是否为数组或切片 70 | if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array { 71 | var maps []map[string]interface{} 72 | for _, item := range data.([]interface{}) { 73 | if m, ok := item.(map[string]interface{}); ok { 74 | maps = append(maps, m) 75 | } 76 | } 77 | return true, maps 78 | } 79 | return false, nil 80 | } 81 | 82 | func GetInterfaceType(v interface{}) string { 83 | interfaceType := reflect.TypeOf(v) 84 | return interfaceType.String() 85 | } 86 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/transform_rename_key_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "pandax/pkg/rule_engine/message" 5 | ) 6 | 7 | type transformRenameKeyNode struct { 8 | bareNode 9 | FormType string `json:"formType" yaml:"formType"` //msg metadata 10 | Keys []KeyName `json:"keys" yaml:"keys"` 11 | } 12 | type KeyName struct { 13 | OldName string `json:"oldName" yaml:"oldName"` 14 | NewName string `json:"newName" yaml:"newName"` 15 | } 16 | type transformRenameKeyNodeFactory struct{} 17 | 18 | func (f transformRenameKeyNodeFactory) Name() string { return "RenameKeyNode" } 19 | func (f transformRenameKeyNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM } 20 | func (f transformRenameKeyNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 21 | func (f transformRenameKeyNodeFactory) Create(id string, meta Properties) (Node, error) { 22 | node := &transformRenameKeyNode{ 23 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 24 | } 25 | return decodePath(meta, node) 26 | } 27 | 28 | func (n *transformRenameKeyNode) Handle(msg *message.Message) error { 29 | n.Debug(msg, message.DEBUGIN, "") 30 | 31 | successLabelNode := n.GetLinkedNode("Success") 32 | failureLabelNode := n.GetLinkedNode("Failure") 33 | if n.FormType == "msg" { 34 | data := msg.Msg 35 | for _, key := range n.Keys { 36 | if _, found := data[key.OldName]; found { 37 | data[key.NewName] = data[key.OldName] 38 | delete(data, key.OldName) 39 | msg.Msg = data 40 | } 41 | } 42 | } else if n.FormType == "metadata" { 43 | data := msg.Metadata 44 | for _, key := range n.Keys { 45 | if data.GetValue(key.OldName) != nil { 46 | data[key.NewName] = data[key.OldName] 47 | delete(data, key.OldName) 48 | msg.Metadata = data 49 | } 50 | } 51 | } else { 52 | if failureLabelNode != nil { 53 | n.Debug(msg, message.DEBUGOUT, "未识别FormType") 54 | return failureLabelNode.Handle(msg) 55 | } 56 | } 57 | if successLabelNode != nil { 58 | n.Debug(msg, message.DEBUGOUT, "") 59 | return successLabelNode.Handle(msg) 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /apps/device/api/device_cmd.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // ========================================================================== 4 | import ( 5 | "encoding/json" 6 | "github.com/PandaXGO/PandaKit/biz" 7 | "github.com/PandaXGO/PandaKit/model" 8 | "github.com/PandaXGO/PandaKit/restfulx" 9 | devicerpc "pandax/pkg/device_rpc" 10 | "strings" 11 | 12 | "pandax/apps/device/entity" 13 | "pandax/apps/device/services" 14 | "pandax/apps/device/util" 15 | ) 16 | 17 | type DeviceCmdLogApi struct { 18 | DeviceCmdLogApp services.DeviceCmdLogModel 19 | DeviceApp services.DeviceModel 20 | } 21 | 22 | // GetDeviceCmdLogList 告警列表数据 23 | func (p *DeviceCmdLogApi) GetDeviceCmdLogList(rc *restfulx.ReqCtx) { 24 | data := entity.DeviceCmdLog{} 25 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 26 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 27 | data.DeviceId = restfulx.QueryParam(rc, "deviceId") 28 | data.State = restfulx.QueryParam(rc, "state") 29 | data.Type = restfulx.QueryParam(rc, "type") 30 | 31 | list, total, err := p.DeviceCmdLogApp.FindListPage(pageNum, pageSize, data) 32 | biz.ErrIsNil(err, "查询告警列表数据失败") 33 | rc.ResData = model.ResultPage{ 34 | Total: total, 35 | PageNum: int64(pageNum), 36 | PageSize: int64(pageSize), 37 | Data: list, 38 | } 39 | } 40 | 41 | // InsertDeviceCmdLog 添加DeviceCmdLog 42 | func (p *DeviceCmdLogApi) InsertDeviceCmdLog(rc *restfulx.ReqCtx) { 43 | var data entity.DeviceCmdLog 44 | restfulx.BindJsonAndValid(rc, &data) 45 | biz.NotEmpty(data.CmdContent, "请设置指令内容") 46 | //验证指令格式 47 | ms := make(map[string]interface{}) 48 | err := json.Unmarshal([]byte(data.CmdContent), &ms) 49 | biz.ErrIsNil(err, "指令格式不正确") 50 | biz.IsTrue(len(ms) > 0, "指令格式不正确") 51 | rpc := devicerpc.RpcPayload{ 52 | Method: data.CmdName, 53 | Params: ms, 54 | } 55 | err = util.BuildRunDeviceRpc(data.DeviceId, data.Mode, rpc) 56 | biz.ErrIsNil(err, "添加指令记录失败") 57 | } 58 | 59 | // DeleteDeviceCmdLog 删除告警 60 | func (p *DeviceCmdLogApi) DeleteDeviceCmdLog(rc *restfulx.ReqCtx) { 61 | id := restfulx.PathParam(rc, "id") 62 | ids := strings.Split(id, ",") 63 | biz.ErrIsNil(p.DeviceCmdLogApp.Delete(ids), "删除指令失败") 64 | } 65 | -------------------------------------------------------------------------------- /apps/device/router/device_cmd.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/device/api" 7 | "pandax/apps/device/entity" 8 | "pandax/apps/device/services" 9 | 10 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 11 | "github.com/emicklei/go-restful/v3" 12 | ) 13 | 14 | func InitDeviceCmdLogRouter(container *restful.Container) { 15 | s := &api.DeviceCmdLogApi{ 16 | DeviceCmdLogApp: services.DeviceCmdLogModelDao, 17 | DeviceApp: services.DeviceModelDao, 18 | } 19 | 20 | ws := new(restful.WebService) 21 | ws.Path("/device/cmd").Produces(restful.MIME_JSON) 22 | tags := []string{"设备命令"} 23 | 24 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 25 | restfulx.NewReqCtx(request, response).WithLog("获取命令下发分页列表").Handle(s.GetDeviceCmdLogList) 26 | }). 27 | Doc("获取命令下发分页列表"). 28 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 29 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 30 | Param(ws.QueryParameter("deviceId", "设备Id").Required(false).DataType("string")). 31 | Param(ws.QueryParameter("type", "命令下发分类").Required(false).DataType("string")). 32 | Param(ws.QueryParameter("state", "命令下发状态").Required(false).DataType("string")). 33 | Metadata(restfulspec.KeyOpenAPITags, tags). 34 | Writes(model.ResultPage{}). 35 | Returns(200, "OK", model.ResultPage{})) 36 | 37 | ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) { 38 | restfulx.NewReqCtx(request, response).WithLog("命令下发").Handle(s.InsertDeviceCmdLog) 39 | }). 40 | Doc("命令下发"). 41 | Metadata(restfulspec.KeyOpenAPITags, tags). 42 | Reads(entity.DeviceCmdLog{})) 43 | 44 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) { 45 | restfulx.NewReqCtx(request, response).WithLog("删除命令下发信息").Handle(s.DeleteDeviceCmdLog) 46 | }). 47 | Doc("删除命令下发信息"). 48 | Metadata(restfulspec.KeyOpenAPITags, tags). 49 | Param(ws.PathParameter("id", "多id 1,2,3").DataType("string"))) 50 | 51 | container.Add(ws) 52 | } 53 | -------------------------------------------------------------------------------- /deploy/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | services: 3 | redis: 4 | image: redis:7.0.12 5 | container_name: redis 6 | restart: always 7 | command: redis-server /etc/redis/redis.conf --requirepass pandax --appendonly yes 8 | volumes: 9 | - "./redis/data:/data" 10 | - "./redis/logs:/logs" 11 | ports: 12 | - 6379:6379 13 | tdengine: 14 | image: tdengine/tdengine:3.0.4.2 15 | container_name: tdengine-server 16 | restart: always 17 | environment: 18 | - TZ=Asia/Shanghai 19 | volumes: 20 | - ./taos/data:/var/lib/taos 21 | - ./taos/logs:/var/log/taos 22 | ports: 23 | - 6030:6030 24 | - 6041:6041 25 | - 6030-6040:6030-6040/udp 26 | emqx: 27 | image: emqx/emqx:5.1.0 28 | restart: always 29 | container_name: emqx-server 30 | ports: 31 | - 1883:1883 32 | - 8083:8083 33 | - 8084:8084 34 | - 8883:8883 35 | - 18083:18083 36 | mysql: 37 | image: mysql:8.0.23 38 | container_name: mysql-server 39 | restart: always 40 | environment: 41 | MYSQL_ROOT_PASSWORD: pandax 42 | MYSQL_DATABASE: pandax_iot 43 | volumes: 44 | - ./mysql/data:/var/lib/mysql 45 | ports: 46 | - "3306:3306" 47 | pandax: 48 | image: ccr.ccs.tencentyun.com/pandax/pandax:v0.1 49 | container_name: pandax 50 | restart: always 51 | depends_on: 52 | - mysql-server 53 | - emqx-server 54 | - tdengine-server 55 | - redis 56 | ports: 57 | - "7788:7788" 58 | - "9001:9001" 59 | - "9002:9002" 60 | - "9003:9003" 61 | - "9003:9003/udp" 62 | pandax-web: 63 | image: ccr.ccs.tencentyun.com/pandax/pandaxweb:v0.1 64 | container_name: pandax-web 65 | restart: always 66 | ports: 67 | - "7789:7789" 68 | pandax-screen: 69 | image: ccr.ccs.tencentyun.com/pandax/pandaxscreen:v0.1 70 | container_name: pandax-screen 71 | restart: always 72 | ports: 73 | - "7790:7790" 74 | pandax-rule: 75 | image: ccr.ccs.tencentyun.com/pandax/pandaxrule:v0.1 76 | container_name: pandax-rule 77 | restart: always 78 | ports: 79 | - "7791:7791" -------------------------------------------------------------------------------- /apps/system/services/notice.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "pandax/apps/system/entity" 5 | "pandax/pkg/global" 6 | ) 7 | 8 | type ( 9 | SysNoticeModel interface { 10 | Insert(data entity.SysNotice) (*entity.SysNotice, error) 11 | FindOne(postId int64) (*entity.SysNotice, error) 12 | FindListPage(page, pageSize int, data entity.SysNotice) (*[]entity.SysNotice, int64, error) 13 | Update(data entity.SysNotice) error 14 | Delete(postId []int64) error 15 | } 16 | 17 | sysNoticeModelImpl struct { 18 | table string 19 | } 20 | ) 21 | 22 | var SysNoticeModelDao SysNoticeModel = &sysNoticeModelImpl{ 23 | table: `sys_notices`, 24 | } 25 | 26 | func (m *sysNoticeModelImpl) Insert(data entity.SysNotice) (*entity.SysNotice, error) { 27 | err := global.Db.Table(m.table).Create(&data).Error 28 | return &data, err 29 | } 30 | 31 | func (m *sysNoticeModelImpl) FindOne(postId int64) (*entity.SysNotice, error) { 32 | resData := new(entity.SysNotice) 33 | err := global.Db.Table(m.table).Where("post_id = ?", postId).First(resData).Error 34 | return resData, err 35 | } 36 | 37 | func (m *sysNoticeModelImpl) FindListPage(page, pageSize int, data entity.SysNotice) (*[]entity.SysNotice, int64, error) { 38 | list := make([]entity.SysNotice, 0) 39 | var total int64 = 0 40 | offset := pageSize * (page - 1) 41 | db := global.Db.Table(m.table) 42 | // 此处填写 where参数判断 43 | if data.Title != "" { 44 | db = db.Where("title like ?", "%"+data.Title+"%") 45 | } 46 | if data.NoticeType != "" { 47 | db = db.Where("notice_type = ?", data.NoticeType) 48 | } 49 | if len(data.OrganizationIds) > 0 { 50 | db = db.Where("organization_id in (?)", data.OrganizationIds) 51 | } 52 | db.Where("delete_time IS NULL") 53 | err := db.Count(&total).Error 54 | err = db.Order("create_time").Limit(pageSize).Offset(offset).Find(&list).Error 55 | return &list, total, err 56 | } 57 | 58 | func (m *sysNoticeModelImpl) Update(data entity.SysNotice) error { 59 | return global.Db.Table(m.table).Updates(&data).Error 60 | } 61 | 62 | func (m *sysNoticeModelImpl) Delete(postIds []int64) error { 63 | return global.Db.Table(m.table).Delete(&entity.SysNotice{}, "notice_id in (?)", postIds).Error 64 | } 65 | -------------------------------------------------------------------------------- /pkg/rule_engine/instance_test.go: -------------------------------------------------------------------------------- 1 | package rule_engine 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "pandax/pkg/rule_engine/message" 7 | "pandax/pkg/rule_engine/nodes" 8 | "testing" 9 | ) 10 | 11 | func TestNewRuleChainInstance(t *testing.T) { 12 | buf, err := os.ReadFile("./manifest/manifest_sample.json") 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | instance, err := NewRuleChainInstance("11", buf) 17 | if err != nil { 18 | t.Error(err) 19 | } 20 | 21 | metadata := message.Metadata{ 22 | "deviceName": "ws432", 23 | "deviceId": "d_1928b99619910dae5a001fa7", 24 | "deviceType": "direct", 25 | "productId": "p_3ba460634520cf4590dc90e5", 26 | } 27 | msg := message.NewMessage("1", message.TelemetryMes, message.Msg{"temperature": 60.4, "humidity": 32.5}, metadata) 28 | t.Log("开始执行力流程") 29 | err = instance.StartRuleChain(context.Background(), msg) 30 | if err != nil { 31 | t.Log(err) 32 | } 33 | } 34 | 35 | func TestScriptEngine(t *testing.T) { 36 | msg := message.NewMessage("1", message.UpEventMes, map[string]interface{}{"aa": 5}, map[string]interface{}{"device": "aa"}) 37 | const baseScript = ` 38 | function nextRelation(metadata, msg) { 39 | return ['one','nine']; 40 | } 41 | if(metadata.device === 'aa') { 42 | return ['six']; 43 | } 44 | if(msgType === 'Post telemetry') { 45 | return ['two']; 46 | } 47 | return nextRelation(metadata, msg); 48 | ` 49 | scriptEngine := nodes.NewScriptEngine(*msg, "Switch", baseScript) 50 | SwitchResults, err := scriptEngine.ScriptOnSwitch() 51 | 52 | if err != nil { 53 | t.Error(err) 54 | } 55 | t.Log(SwitchResults) 56 | } 57 | 58 | func TestScriptOnMessage(t *testing.T) { 59 | msg := message.NewMessage("1", message.UpEventMes, map[string]interface{}{"aa": 5}, map[string]interface{}{"device": "aa"}) 60 | 61 | const baseScript = ` 62 | msg.bb = "33" 63 | metadata.event = 55 64 | return {msg: msg, metadata: metadata, msgType: msgType}; 65 | ` 66 | scriptEngine := nodes.NewScriptEngine(*msg, "Transform", baseScript) 67 | ScriptOnMessageResults, err := scriptEngine.ScriptOnMessage() 68 | 69 | if err != nil { 70 | t.Error(err) 71 | } 72 | t.Log(ScriptOnMessageResults.Metadata) 73 | } 74 | -------------------------------------------------------------------------------- /apps/device/util/device_rpc.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "pandax/apps/device/services" 7 | ruleEntity "pandax/apps/rule/entity" 8 | ruleService "pandax/apps/rule/services" 9 | devicerpc "pandax/pkg/device_rpc" 10 | "pandax/pkg/global" 11 | "pandax/pkg/rule_engine" 12 | "pandax/pkg/rule_engine/message" 13 | "pandax/pkg/tool" 14 | ) 15 | 16 | func BuildRunDeviceRpc(deviceId, mode string, rp devicerpc.RpcPayload) error { 17 | device, err := services.DeviceModelDao.FindOne(deviceId) 18 | if err != nil { 19 | return err 20 | } 21 | if device.LinkStatus != global.ONLINE { 22 | return errors.New("设备不在线无法设置属性") 23 | } 24 | 25 | //新建规则链实体 26 | ruleInstance := rule_engine.RuleEngine.GetRuleInstance(device.Product.Id) 27 | if ruleInstance == nil { 28 | findOne, err := ruleService.RuleChainModelDao.FindOne(device.Product.RuleChainId) 29 | if err != nil { 30 | global.Log.Error("查询规则链数据失败", err) 31 | return errors.New("查询规则链数据失败") 32 | } 33 | ruleData := ruleEntity.RuleDataJson{} 34 | err = tool.StringToStruct(findOne.RuleDataJson, &ruleData) 35 | if err != nil { 36 | global.Log.Error("规则链数据转化失败", err) 37 | return errors.New("规则链数据转化失败") 38 | } 39 | dataCode := ruleData.DataCode 40 | code, err := json.Marshal(dataCode) 41 | if err != nil { 42 | global.Log.Error("规则链数据解析失败", err) 43 | return errors.New("规则链数据解析失败") 44 | } 45 | ruleInstance, err = rule_engine.NewRuleChainInstance(findOne.Id, code) 46 | if err != nil { 47 | return err 48 | } 49 | rule_engine.RuleEngine.SaveRuleInstance(device.Product.Id, ruleInstance) 50 | } 51 | metadataVals := map[string]interface{}{ 52 | "deviceId": device.Id, 53 | "mode": mode, 54 | "deviceName": device.Name, 55 | "deviceType": device.DeviceType, 56 | "deviceProtocol": device.Product.ProtocolName, 57 | "productId": device.Pid, 58 | "orgId": device.OrgId, 59 | "owner": device.Owner, 60 | } 61 | msg := message.NewMessage(device.Owner, message.RpcRequestToDevice, rp.ToMap(), metadataVals) 62 | err = rule_engine.RuleEngine.StartRuleInstance(ruleInstance, msg) 63 | if err != nil { 64 | global.Log.Error("规则链执行失败", err) 65 | } 66 | return err 67 | } 68 | -------------------------------------------------------------------------------- /apps/system/api/notice.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "github.com/PandaXGO/PandaKit/utils" 8 | "pandax/apps/system/entity" 9 | "pandax/apps/system/services" 10 | "strings" 11 | ) 12 | 13 | type NoticeApi struct { 14 | OrganizationApp services.SysOrganizationModel 15 | NoticeApp services.SysNoticeModel 16 | } 17 | 18 | // GetNoticeList 通知列表数据 19 | func (p *NoticeApi) GetNoticeList(rc *restfulx.ReqCtx) { 20 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 21 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 22 | noticeType := restfulx.QueryParam(rc, "noticeType") 23 | title := restfulx.QueryParam(rc, "title") 24 | 25 | // 获取组织的子组织id 26 | one, err := p.OrganizationApp.FindOne(rc.LoginAccount.OrganizationId) 27 | biz.ErrIsNil(err, "组织查询失败") 28 | split := strings.Split(strings.Trim(one.OrganizationPath, "/"), "/") 29 | // 获取所有父组织id 30 | ids := utils.OrganizationPCIds(split, rc.LoginAccount.OrganizationId, true) 31 | notice := entity.SysNotice{NoticeType: noticeType, Title: title, OrganizationIds: ids} 32 | list, total, err := p.NoticeApp.FindListPage(pageNum, pageSize, notice) 33 | biz.ErrIsNil(err, "查询通知列表失败") 34 | rc.ResData = model.ResultPage{ 35 | Total: total, 36 | PageNum: int64(pageNum), 37 | PageSize: int64(pageSize), 38 | Data: list, 39 | } 40 | } 41 | 42 | // InsertNotice 添加通知 43 | func (p *NoticeApi) InsertNotice(rc *restfulx.ReqCtx) { 44 | var notice entity.SysNotice 45 | restfulx.BindJsonAndValid(rc, ¬ice) 46 | notice.UserName = rc.LoginAccount.UserName 47 | _, err := p.NoticeApp.Insert(notice) 48 | biz.ErrIsNil(err, "添加通知失败") 49 | } 50 | 51 | // UpdateNotice 修改通知 52 | func (p *NoticeApi) UpdateNotice(rc *restfulx.ReqCtx) { 53 | var notice entity.SysNotice 54 | restfulx.BindJsonAndValid(rc, ¬ice) 55 | 56 | err := p.NoticeApp.Update(notice) 57 | biz.ErrIsNil(err, "修改通知失败") 58 | } 59 | 60 | // DeleteNotice 删除通知 61 | func (p *NoticeApi) DeleteNotice(rc *restfulx.ReqCtx) { 62 | noticeId := restfulx.PathParam(rc, "noticeId") 63 | noticeIds := utils.IdsStrToIdsIntGroup(noticeId) 64 | err := p.NoticeApp.Delete(noticeIds) 65 | biz.ErrIsNil(err, "删除通知失败") 66 | } 67 | -------------------------------------------------------------------------------- /pkg/rule_engine/manifest/manifest.go: -------------------------------------------------------------------------------- 1 | package manifest 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/sirupsen/logrus" 6 | ) 7 | 8 | type Node struct { 9 | Type string `json:"type" yaml:"type"` 10 | Id string `json:"id" yaml:"id"` 11 | Properties map[string]interface{} `json:"properties" yaml:"properties"` //debugMode 12 | } 13 | 14 | type Edge struct { 15 | SourceNodeId string `json:"sourceNodeId" yaml:"sourceNodeId"` 16 | TargetNodeId string `json:"targetNodeId" yaml:"targetNodeId"` 17 | Type string `json:"type" yaml:"type"` //success or fail 18 | Properties map[string]interface{} `json:"properties" yaml:"properties"` //debugMode 19 | } 20 | 21 | type Manifest struct { 22 | FirstRuleNodeId string `json:"firstRuleNodeId" yaml:"firstRuleNodeId"` 23 | Nodes []Node `json:"nodes" yaml:"nodes"` 24 | Edges []Edge `json:"edges" yaml:"edges"` 25 | } 26 | 27 | func New(data []byte) (*Manifest, error) { 28 | firstRuleNodeId := "" 29 | manifest := make(map[string]interface{}) 30 | if err := json.Unmarshal(data, &manifest); err != nil { 31 | logrus.WithError(err).Errorf("invalid node chain manifest file") 32 | return nil, err 33 | } 34 | nodes := make([]Node, 0) 35 | for _, mn := range manifest["nodes"].([]interface{}) { 36 | node := mn.(map[string]interface{}) 37 | if node["type"].(string) == "InputNode" { 38 | firstRuleNodeId = node["id"].(string) 39 | } 40 | nodes = append(nodes, Node{ 41 | Id: node["id"].(string), 42 | Type: node["type"].(string), 43 | Properties: node["properties"].(map[string]interface{}), 44 | }) 45 | } 46 | edges := make([]Edge, 0) 47 | for _, en := range manifest["edges"].([]interface{}) { 48 | edge := en.(map[string]interface{}) 49 | edges = append(edges, Edge{ 50 | Type: edge["type"].(string), 51 | Properties: edge["properties"].(map[string]interface{}), 52 | SourceNodeId: edge["sourceNodeId"].(string), 53 | TargetNodeId: edge["targetNodeId"].(string), 54 | }) 55 | } 56 | m := &Manifest{ 57 | FirstRuleNodeId: firstRuleNodeId, 58 | Nodes: nodes, 59 | Edges: edges, 60 | } 61 | return m, nil 62 | } 63 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/external_wechat_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/PandaXGO/PandaKit/httpclient" 6 | "pandax/pkg/rule_engine/message" 7 | ) 8 | 9 | type externalWechatNode struct { 10 | bareNode 11 | WebHook string `json:"webHook" yaml:"webHook"` 12 | MsgType string `json:"msgType" yaml:"msgType"` 13 | Content string `json:"content" yaml:"content"` 14 | IsAtAll bool `json:"isAtAll" yaml:"isAtAll"` 15 | AtMobiles []string `json:"atMobiles" yaml:"atMobiles"` 16 | } 17 | 18 | type externalWechatNodeFactory struct{} 19 | 20 | func (f externalWechatNodeFactory) Name() string { return "WechatNode" } 21 | func (f externalWechatNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL } 22 | func (f externalWechatNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 23 | func (f externalWechatNodeFactory) Create(id string, meta Properties) (Node, error) { 24 | node := &externalWechatNode{ 25 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 26 | } 27 | return decodePath(meta, node) 28 | } 29 | 30 | func (n *externalWechatNode) Handle(msg *message.Message) error { 31 | n.Debug(msg, message.DEBUGIN, "") 32 | 33 | successLabelNode := n.GetLinkedNode("Success") 34 | failureLabelNode := n.GetLinkedNode("Failure") 35 | template, err := ParseTemplate(n.Content, msg.GetAllMap()) 36 | 37 | sendData := map[string]interface{}{ 38 | "msgtype": "text", 39 | "text": map[string]interface{}{"content": template}, 40 | } 41 | if n.IsAtAll { 42 | sendData["text"].(map[string]interface{})["mentioned_mobile_list"] = []string{"@all"} 43 | } else { 44 | sendData["text"].(map[string]interface{})["mentioned_mobile_list"] = n.AtMobiles 45 | } 46 | marshal, _ := json.Marshal(sendData) 47 | postJson := httpclient.NewRequest(n.WebHook).Header("Content-Type", "application/json").PostJson(string(marshal)) 48 | if postJson.StatusCode != 200 { 49 | n.Debug(msg, message.DEBUGOUT, "请求微信机器人hook接口失败") 50 | if failureLabelNode != nil { 51 | return failureLabelNode.Handle(msg) 52 | } else { 53 | return err 54 | } 55 | } 56 | if successLabelNode != nil { 57 | n.Debug(msg, message.DEBUGOUT, "") 58 | return successLabelNode.Handle(msg) 59 | } 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /pkg/tool/excel.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "fmt" 5 | "github.com/xuri/excelize/v2" 6 | ) 7 | 8 | // 读取数据表 9 | func ReadExcel(filename string) ([]string, []map[string]interface{}) { 10 | ret := make([]map[string]interface{}, 0) 11 | f, err := excelize.OpenFile(filename) 12 | if err != nil { 13 | fmt.Println("读取excel文件出错", err.Error()) 14 | return nil, ret 15 | } 16 | sheets := f.GetSheetMap() 17 | sheet1 := sheets[1] 18 | rows, err := f.GetRows(sheet1) 19 | cols := make([]string, 0) 20 | isHead := true 21 | for _, row := range rows { 22 | if isHead { //取得第一行的所有数据---execel表头 23 | if len(row) == 0 { 24 | continue 25 | } 26 | for _, colCell := range row { 27 | cols = append(cols, colCell) 28 | } 29 | isHead = false 30 | } else { 31 | theRow := map[string]interface{}{} 32 | for j, colCell := range row { 33 | k := cols[j] 34 | theRow[k] = colCell 35 | } 36 | ret = append(ret, theRow) 37 | } 38 | } 39 | return cols, ret 40 | } 41 | 42 | /* 43 | func ReadExcelByFilter(filename string, data entity.DataSetDataReq) ([]string, []map[string]interface{}) { 44 | dataDs := make([]string, 0) 45 | for _, ds := range data.DataDs { 46 | dataDs = append(dataDs, ds.Value) 47 | } 48 | 49 | ret := make([]map[string]interface{}, 0) 50 | f, err := excelize.OpenFile(filename) 51 | if err != nil { 52 | fmt.Println("读取excel文件出错", err.Error()) 53 | return nil, ret 54 | } 55 | sheets := f.GetSheetMap() 56 | sheet1 := sheets[1] 57 | rows, err := f.GetRows(sheet1) 58 | cols := make([]string, 0) 59 | colsIndex := make([]int, 0) 60 | isHead := true 61 | count := 0 62 | for _, row := range rows { 63 | if data.ShowNumType == "2" { 64 | if count == int(data.ShowNum) { 65 | break 66 | } 67 | } 68 | if isHead { //取得第一行的所有数据---execel表头 69 | if len(row) == 0 { 70 | continue 71 | } 72 | for i, colCell := range row { 73 | cols = append(cols, colCell) 74 | colsIndex = append(colsIndex, i) 75 | } 76 | isHead = false 77 | } else { 78 | theRow := map[string]interface{}{} 79 | for j, colCell := range row { 80 | k := cols[j] 81 | theRow[k] = colCell 82 | 83 | } 84 | ret = append(ret, theRow) 85 | } 86 | count++ 87 | } 88 | return cols, ret 89 | } 90 | */ 91 | -------------------------------------------------------------------------------- /apps/log/services/log_oper.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "pandax/apps/log/entity" 5 | "pandax/pkg/global" 6 | ) 7 | 8 | type ( 9 | LogOperModel interface { 10 | Insert(data entity.LogOper) (*entity.LogOper, error) 11 | FindOne(infoId int64) (*entity.LogOper, error) 12 | FindListPage(page, pageSize int, data entity.LogOper) (*[]entity.LogOper, int64, error) 13 | Delete(infoId []int64) error 14 | DeleteAll() error 15 | } 16 | 17 | logLogOperModelImpl struct { 18 | table string 19 | } 20 | ) 21 | 22 | var LogOperModelDao LogOperModel = &logLogOperModelImpl{ 23 | table: `log_opers`, 24 | } 25 | 26 | func (m *logLogOperModelImpl) Insert(data entity.LogOper) (*entity.LogOper, error) { 27 | err := global.Db.Table(m.table).Create(&data).Error 28 | return &data, err 29 | } 30 | 31 | func (m *logLogOperModelImpl) FindOne(operId int64) (*entity.LogOper, error) { 32 | resData := new(entity.LogOper) 33 | err := global.Db.Table(m.table).Where("oper_id = ?", operId).First(resData).Error 34 | return resData, err 35 | } 36 | 37 | func (m *logLogOperModelImpl) FindListPage(page, pageSize int, data entity.LogOper) (*[]entity.LogOper, int64, error) { 38 | list := make([]entity.LogOper, 0) 39 | var total int64 = 0 40 | offset := pageSize * (page - 1) 41 | db := global.Db.Table(m.table) 42 | // 此处填写 where参数判断 43 | if data.BusinessType != "" { 44 | db = db.Where("business_type = ?", data.BusinessType) 45 | } 46 | if data.OperLocation != "" { 47 | db = db.Where("oper_location like ?", "%"+data.OperLocation+"%") 48 | } 49 | if data.Title != "" { 50 | db = db.Where("title like ?", "%"+data.Title+"%") 51 | } 52 | if data.OperName != "" { 53 | db = db.Where("oper_name like ?", "%"+data.OperName+"%") 54 | } 55 | err := db.Where("delete_time IS NULL").Count(&total).Error 56 | if err != nil { 57 | return &list, total, err 58 | } 59 | err = db.Order("create_time desc").Limit(pageSize).Offset(offset).Find(&list).Error 60 | return &list, total, err 61 | } 62 | 63 | func (m *logLogOperModelImpl) Delete(operIds []int64) error { 64 | err := global.Db.Table(m.table).Delete(&entity.LogOper{}, "oper_id in (?)", operIds).Error 65 | return err 66 | } 67 | 68 | func (m *logLogOperModelImpl) DeleteAll() error { 69 | return global.Db.Exec("DELETE FROM log_opers").Error 70 | } 71 | -------------------------------------------------------------------------------- /apps/system/api/upload.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "github.com/PandaXGO/PandaKit/biz" 6 | "github.com/PandaXGO/PandaKit/oss" 7 | "github.com/PandaXGO/PandaKit/restfulx" 8 | "pandax/pkg/config" 9 | "pandax/pkg/global" 10 | "pandax/pkg/tool" 11 | "path" 12 | "time" 13 | ) 14 | 15 | type UploadApi struct{} 16 | 17 | // UploadImage 图片上传 18 | func (up *UploadApi) UploadImage(rc *restfulx.ReqCtx) { 19 | _, fileHeader, err := rc.Request.Request.FormFile("file") 20 | fileType := restfulx.PathParam(rc, "path") 21 | biz.ErrIsNil(err, "请传入文件") 22 | local := &tool.Local{Path: tool.GetFilePath(fileType)} 23 | link, fileName, err := local.UploadFile(fileHeader) 24 | biz.ErrIsNil(err, "文件上传失败") 25 | 26 | rc.ResData = map[string]string{"fileName": fileName, "filePath": link} 27 | } 28 | 29 | // UplaodResOsses 上传文件ResOsses 30 | func (p *UploadApi) UplaodToOss(rc *restfulx.ReqCtx) { 31 | _, handler, _ := rc.Request.Request.FormFile("file") 32 | yunFileTmpPath := "uploads/" + time.Now().Format("2006-01-02") + "/" + handler.Filename 33 | // 读取本地文件。 34 | f, openError := handler.Open() 35 | biz.ErrIsNil(openError, "function file.Open() Failed") 36 | config := global.Conf.Oss 37 | biz.ErrIsNil(NewOss(*config).PutObj(yunFileTmpPath, f), "上传OSS失败") 38 | 39 | rc.ResData = fmt.Sprintf("http://%s/%s/%s", config.Endpoint, config.BucketName, yunFileTmpPath) 40 | } 41 | 42 | // subpath 是fileName 43 | func (up *UploadApi) GetImage(rc *restfulx.ReqCtx) { 44 | fileType := restfulx.PathParam(rc, "path") 45 | actual := path.Join(tool.GetFilePath(fileType), restfulx.PathParam(rc, "subpath")) 46 | 47 | rc.Download(actual) 48 | } 49 | 50 | func (up *UploadApi) DeleteImage(rc *restfulx.ReqCtx) { 51 | fileName := restfulx.QueryParam(rc, "fileName") 52 | fileType := restfulx.QueryParam(rc, "fileType") 53 | biz.NotEmpty(fileName, "请传要删除的图片名") 54 | local := &tool.Local{Path: tool.GetFilePath(fileType)} 55 | err := local.DeleteFile(fileName) 56 | biz.ErrIsNil(err, "文件删除失败") 57 | } 58 | 59 | func NewOss(ens config.Oss) oss.Driver { 60 | return oss.NewMiniOss(oss.MiniOConfig{ 61 | BucketName: ens.BucketName, 62 | Endpoint: ens.Endpoint, 63 | AccessKeyID: ens.AccessKey, 64 | SecretAccessKey: ens.SecretKey, 65 | UseSSL: ens.UseSSL, 66 | Location: "cn-north-1", 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /apps/device/tsl/tsl.go: -------------------------------------------------------------------------------- 1 | package tsl 2 | 3 | const ( 4 | TypeEnum = "enum" // 枚举类型 5 | TypeInt = "int64" 6 | TypeString = "string" 7 | TypeBool = "bool" 8 | TypeFloat = "float64" 9 | TypeDate = "date" 10 | TypeStruct = "struct" 11 | ) 12 | 13 | // DefineAttribute 属性拓展 14 | type DefineAttribute struct { 15 | DefaultValue *string `json:"defaultValue"` // 属性时 16 | Rw *string `json:"rw"` 17 | } 18 | 19 | // DefineBase 基础类型参数 20 | type DefineBase struct { 21 | Max *float64 `json:"max" ` // 最大,数字类型:int64、float64 22 | Min *float64 `json:"min" ` // 最小,数字类型:int64、float64 23 | Step *float64 `json:"step"` // 小数位数,数字类型:int64、float64 24 | Decimals *int `json:"decimals"` // 小数位数,数字类型:float64 25 | Unit *string `json:"unit"` // 单位,数字类型: int64、float64 26 | 27 | MaxLength *int `json:"maxLength"` // 最大长度,字符类型:string 28 | 29 | DefineBool []DefineBool `json:"boolDefine"` 30 | Enums []DefineEnum `json:"enumDefine"` // 枚举类型:enum 31 | Struct any `json:"structDefine" ` // 对象类型:Struct 32 | } 33 | 34 | type DefineBool struct { 35 | Key string `json:"key"` // 键 0、1 36 | Value string `json:"value"` // 枚举值 37 | } 38 | 39 | type DefineEnum struct { 40 | Key string `json:"key"` // 键 0、1 41 | Value string `json:"value"` // 枚举值 42 | } 43 | 44 | // DefineStruct 扩展类型参数:对象型 45 | type DefineStruct struct { 46 | Key string `json:"key"` //参数标识 47 | Name string `json:"name"` // 参数名称 48 | ValueType ValueType `json:"valueType"` // 参数值 49 | Desc string `json:"desc"` // 描述 50 | } 51 | 52 | type ValueType struct { 53 | Type string `json:"type"` // 类型 54 | DefineBase // 参数 55 | } 56 | 57 | // DefineCommands 命令 58 | type DefineCommands struct { 59 | Key string `json:"key" ` 60 | Name string `json:"name"` // 功能名称 61 | Inputs []DefineCommandsInput `json:"inputs"` // 输入参数 62 | Output ValueType `json:"output"` // 输出参数 63 | Desc string `json:"desc"` // 描述 64 | } 65 | 66 | // DefineCommandsInput 命令:输入参数 67 | type DefineCommandsInput struct { 68 | Key string `json:"key"` 69 | Name string `json:"name"` // 输入参数名称 70 | ValueType ValueType `json:"valueType"` // 参数值 71 | Desc string `json:"desc"` // 描述 72 | } 73 | -------------------------------------------------------------------------------- /apps/system/api/config.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/model" 6 | "github.com/PandaXGO/PandaKit/restfulx" 7 | "github.com/PandaXGO/PandaKit/utils" 8 | entity "pandax/apps/system/entity" 9 | services "pandax/apps/system/services" 10 | ) 11 | 12 | type ConfigApi struct { 13 | ConfigApp services.SysConfigModel 14 | } 15 | 16 | func (p *ConfigApi) GetConfigList(rc *restfulx.ReqCtx) { 17 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 18 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 19 | configName := rc.Request.QueryParameter("configName") 20 | configKey := rc.Request.QueryParameter("configKey") 21 | configType := rc.Request.QueryParameter("configType") 22 | config := entity.SysConfig{ConfigName: configName, ConfigKey: configKey, ConfigType: configType} 23 | list, total, err := p.ConfigApp.FindListPage(pageNum, pageSize, config) 24 | biz.ErrIsNil(err, "查询配置分页列表失败") 25 | rc.ResData = model.ResultPage{ 26 | Total: total, 27 | PageNum: int64(pageNum), 28 | PageSize: int64(pageSize), 29 | Data: list, 30 | } 31 | } 32 | 33 | func (p *ConfigApi) GetConfigListByKey(rc *restfulx.ReqCtx) { 34 | configKey := rc.Request.QueryParameter("configKey") 35 | biz.IsTrue(configKey != "", "请传入配置Key") 36 | data, err := p.ConfigApp.FindList(entity.SysConfig{ConfigKey: configKey}) 37 | biz.ErrIsNil(err, "查询配置列表失败") 38 | rc.ResData = data 39 | } 40 | 41 | func (p *ConfigApi) GetConfig(rc *restfulx.ReqCtx) { 42 | id := restfulx.PathParamInt(rc, "configId") 43 | data, err := p.ConfigApp.FindOne(int64(id)) 44 | biz.ErrIsNil(err, "查询配置失败") 45 | rc.ResData = data 46 | } 47 | 48 | func (p *ConfigApi) InsertConfig(rc *restfulx.ReqCtx) { 49 | var config entity.SysConfig 50 | restfulx.BindJsonAndValid(rc, &config) 51 | 52 | _, err := p.ConfigApp.Insert(config) 53 | biz.ErrIsNil(err, "添加配置失败") 54 | } 55 | 56 | func (p *ConfigApi) UpdateConfig(rc *restfulx.ReqCtx) { 57 | var post entity.SysConfig 58 | restfulx.BindJsonAndValid(rc, &post) 59 | err := p.ConfigApp.Update(post) 60 | biz.ErrIsNil(err, "修改配置失败") 61 | } 62 | 63 | func (p *ConfigApi) DeleteConfig(rc *restfulx.ReqCtx) { 64 | configId := rc.Request.PathParameter("configId") 65 | err := p.ConfigApp.Delete(utils.IdsStrToIdsIntGroup(configId)) 66 | biz.ErrIsNil(err, "删除配置失败") 67 | } 68 | -------------------------------------------------------------------------------- /apps/system/router/notice.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/system/api" 7 | "pandax/apps/system/entity" 8 | "pandax/apps/system/services" 9 | 10 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 11 | "github.com/emicklei/go-restful/v3" 12 | ) 13 | 14 | func InitNoticeRouter(container *restful.Container) { 15 | s := &api.NoticeApi{ 16 | OrganizationApp: services.SysOrganizationModelDao, 17 | NoticeApp: services.SysNoticeModelDao, 18 | } 19 | ws := new(restful.WebService) 20 | ws.Path("/system/notice").Produces(restful.MIME_JSON) 21 | tags := []string{"system", "通知"} 22 | 23 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 24 | restfulx.NewReqCtx(request, response).WithLog("获取通知分页列表").Handle(s.GetNoticeList) 25 | }). 26 | Doc("获取通知分页列表"). 27 | Metadata(restfulspec.KeyOpenAPITags, tags). 28 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 29 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 30 | Param(ws.QueryParameter("noticeType", "noticeType").DataType("string")). 31 | Param(ws.QueryParameter("title", "title").DataType("string")). 32 | Writes(model.ResultPage{}). 33 | Returns(200, "OK", model.ResultPage{})) 34 | 35 | ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) { 36 | restfulx.NewReqCtx(request, response).WithLog("添加通知信息").Handle(s.InsertNotice) 37 | }). 38 | Doc("添加通知信息"). 39 | Metadata(restfulspec.KeyOpenAPITags, tags). 40 | Reads(entity.SysNotice{})) 41 | 42 | ws.Route(ws.PUT("").To(func(request *restful.Request, response *restful.Response) { 43 | restfulx.NewReqCtx(request, response).WithLog("修改通知信息").Handle(s.UpdateNotice) 44 | }). 45 | Doc("修改通知信息"). 46 | Metadata(restfulspec.KeyOpenAPITags, tags). 47 | Reads(entity.SysNotice{})) 48 | 49 | ws.Route(ws.DELETE("/{noticeId}").To(func(request *restful.Request, response *restful.Response) { 50 | restfulx.NewReqCtx(request, response).WithLog("删除通知信息").Handle(s.DeleteNotice) 51 | }). 52 | Doc("删除通知信息"). 53 | Metadata(restfulspec.KeyOpenAPITags, tags). 54 | Param(ws.PathParameter("noticeId", "多id 1,2,3").DataType("string"))) 55 | 56 | container.Add(ws) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /pkg/global/model/device_auth_model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "github.com/google/uuid" 9 | "gorm.io/gorm" 10 | "pandax/apps/system/entity" 11 | "pandax/apps/system/services" 12 | "pandax/pkg/cache" 13 | "strings" 14 | ) 15 | 16 | type DeviceAuth struct { 17 | Owner string `json:"owner"` 18 | OrgId int64 `json:"orgId"` 19 | DeviceId string `json:"deviceId"` 20 | DeviceType string `json:"deviceType"` 21 | DeviceGroup string `json:"deviceGroup"` 22 | DeviceExt map[string]any `json:"deviceExt"` 23 | Protocol string `json:"protocol"` 24 | ProductId string `json:"productId"` 25 | Name string `json:"name"` 26 | Status string `json:"status"` 27 | } 28 | 29 | func (entity *DeviceAuth) GetDeviceToken(key string) error { 30 | if err := cache.GetDeviceEtoken(key, entity); err != nil { 31 | return err 32 | } 33 | return nil 34 | } 35 | 36 | func (token *DeviceAuth) MD5ID() string { 37 | buf := bytes.NewBufferString(token.DeviceId) 38 | buf.WriteString(token.DeviceType) 39 | access := base64.URLEncoding.EncodeToString([]byte(uuid.NewMD5(uuid.Must(uuid.NewRandom()), buf.Bytes()).String())) 40 | access = strings.TrimRight(access, "=") 41 | return access 42 | } 43 | 44 | func (token *DeviceAuth) GetMarshal() string { 45 | marshal, _ := json.Marshal(*token) 46 | return string(marshal) 47 | } 48 | 49 | func (token *DeviceAuth) GetUnMarshal(data []byte) error { 50 | return json.Unmarshal(data, token) 51 | } 52 | 53 | // 序列化 54 | func (m *DeviceAuth) MarshalBinary() (data []byte, err error) { 55 | return json.Marshal(m) 56 | } 57 | 58 | // 反序列化 59 | func (m *DeviceAuth) UnmarshalBinary(data []byte) error { 60 | return json.Unmarshal(data, m) 61 | } 62 | 63 | func OrgAuthSet(tx *gorm.DB, roleId int64, owner string) error { 64 | if roleId == 0 { 65 | return errors.New("角色Id不能为空") 66 | } 67 | role, err := services.SysRoleModelDao.FindOrganizationsByRoleId(roleId) 68 | if err != nil { 69 | return err 70 | } 71 | if role.DataScope != entity.SELFDATASCOPE { 72 | if len(role.Org) <= 0 { 73 | return errors.New("该角色下未分配组织权限") 74 | } 75 | tx.Where("org_id in (?)", strings.Split(role.Org, ",")) 76 | } else { 77 | tx.Where("owner = ?", owner) 78 | } 79 | 80 | return nil 81 | } 82 | -------------------------------------------------------------------------------- /apps/system/entity/user.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type LoginM struct { 6 | Username string `gorm:"type:varchar(64)" json:"username"` 7 | Password string `gorm:"type:varchar(128)" json:"password"` 8 | } 9 | 10 | type SysUserId struct { 11 | UserId int64 `gorm:"primary_key;AUTO_INCREMENT" json:"userId"` // 编码 12 | } 13 | 14 | type SysUserB struct { 15 | NickName string `gorm:"type:varchar(128)" json:"nickName"` // 昵称 16 | Phone string `gorm:"type:varchar(11)" json:"phone"` // 手机号 17 | RoleId int64 `gorm:"type:int" json:"roleId"` // 角色编码 18 | Salt string `gorm:"type:varchar(255)" json:"salt"` //盐 19 | Avatar string `gorm:"type:varchar(255)" json:"avatar"` //头像 20 | Sex string `gorm:"type:varchar(255)" json:"sex"` //性别 21 | Email string `gorm:"type:varchar(128)" json:"email"` //邮箱 22 | OrganizationId int64 `gorm:"type:int" json:"organizationId"` //组织编码 23 | PostId int64 `gorm:"type:int" json:"postId"` //职位编码 24 | RoleIds string `gorm:"type:varchar(255)" json:"roleIds"` //多角色 25 | PostIds string `gorm:"type:varchar(255)" json:"postIds"` // 多岗位 26 | CreateBy string `gorm:"type:varchar(128)" json:"createBy"` // 27 | UpdateBy string `gorm:"type:varchar(128)" json:"updateBy"` // 28 | Remark string `gorm:"type:varchar(255)" json:"remark"` //备注 29 | Status string `gorm:"type:varchar(1);" json:"status"` 30 | model.BaseModel 31 | } 32 | 33 | type SysUser struct { 34 | SysUserId 35 | SysUserB 36 | LoginM 37 | } 38 | 39 | type SysUserPwd struct { 40 | OldPassword string `json:"oldPassword" form:"oldPassword"` 41 | NewPassword string `json:"newPassword" form:"newPassword"` 42 | } 43 | 44 | type SysUserPage struct { 45 | SysUserId 46 | SysUserB 47 | LoginM 48 | OrganizationName string `gorm:"-" json:"organizationName"` 49 | } 50 | 51 | type Login struct { 52 | Username string `form:"username" json:"username" binding:"required"` 53 | Password string `form:"password" json:"password" binding:"required"` 54 | Code string `form:"code" json:"code" binding:"required"` 55 | UUID string `form:"UUID" json:"uuid" binding:"required"` 56 | } 57 | 58 | type SysUserView struct { 59 | SysUserId 60 | SysUserB 61 | LoginM 62 | RoleName string `gorm:"column:role_name" json:"role_name"` 63 | } 64 | -------------------------------------------------------------------------------- /iothub/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 原始数据上报格式 3 | ```text 4 | 010304026C00883BF0 5 | ``` 6 | iothub将原始数据序列化成,在规则链中 msg就是以下数据。 7 | ```json 8 | { 9 | "rowdata": "010304026C00883BF0" 10 | } 11 | ``` 12 | 需要在规则链中通过函数键 进行解析脚本 13 | ```js 14 | /*直连设备:tempVal是产品物模型中所定义属性的标识符*/ 15 | var tempVal = msg.rowdata; 16 | /*物模型温度标识符*/ 17 | msg.temperature = (parseInt('0x'+tempVal.substr(10, 4))*0.1).toFixed(2); 18 | /*物模型湿度标识符*/ 19 | msg.humidity = (parseInt('0x'+tempVal.substr(6, 4))*0.1).toFixed(2); 20 | return {msg: msg, metadata: metadata, msgType: msgType}; 21 | ``` 22 | ## 属性上报格式 23 | ```json 24 | { 25 | "attribute1": "value1", 26 | "attribute2": 0 27 | } 28 | ``` 29 | 30 | ## 遥测上报格式 31 | 32 | ```json 33 | { 34 | "ts": 1689837909000, 35 | "values": { 36 | "telemetry1": "value1", 37 | "telemetry2": 0 38 | } 39 | } 40 | ``` 41 | 如果边缘无法获取时间 42 | ```json 43 | { 44 | "telemetry1": "value1", 45 | "telemetry2": 0 46 | } 47 | ``` 48 | 49 | ## 网关子设备属性上报格式 50 | devA 为设备标识 51 | ```json 52 | { 53 | "devA": { 54 | "attribute1": "value1", 55 | "attribute2": 0 56 | }, 57 | "devB": { 58 | "attribute1": "value1", 59 | "attribute2": 0 60 | } 61 | } 62 | ``` 63 | 64 | ## 网关子设备遥测上报格式 65 | devA 为设备标识 66 | ```json 67 | { 68 | "devA": { 69 | "ts": 1689837909000, 70 | "values": { 71 | "telemetry1": "value1", 72 | "telemetry2": 0 73 | } 74 | } 75 | } 76 | ``` 77 | 或者 78 | ```json 79 | { 80 | "devA": { 81 | "telemetry1": "value1", 82 | "telemetry2": 0 83 | } 84 | } 85 | ``` 86 | 87 | ## 网关子设备连接或断开上报格式 88 | ```json 89 | { 90 | "devA": "online", 91 | "devB": "offline" 92 | } 93 | ``` 94 | ## 服务端命令下发,设备请求格式, 95 | ```json 96 | { 97 | "method": "restart", 98 | "params": { 99 | "firmware_address": "http://xxx.yyy.com", 100 | "version": "latest", 101 | "secret": "****", 102 | "http_method": "GET" 103 | } 104 | } 105 | ``` 106 | ## 服务端属性下发 method: 'setAttributes' 107 | ```json 108 | { 109 | "method": "setAttributes", 110 | "params": { 111 | "aa": "2" 112 | } 113 | } 114 | ``` 115 | 116 | ## 设备端 请求的格式 117 | { 118 | "method": "getCurrentTime", 119 | "params": { 120 | "aa": "2" 121 | } 122 | } 123 | 124 | ## 设备端 响应的格式 125 | { 126 | "method": "cmdResp", 127 | "params": { 128 | "aa": "2" 129 | } 130 | } -------------------------------------------------------------------------------- /apps/log/services/log_login.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "pandax/apps/log/entity" 5 | "pandax/pkg/global" 6 | ) 7 | 8 | type ( 9 | LogLoginModel interface { 10 | Insert(data entity.LogLogin) (*entity.LogLogin, error) 11 | FindOne(infoId int64) (*entity.LogLogin, error) 12 | FindListPage(page, pageSize int, data entity.LogLogin) (*[]entity.LogLogin, int64, error) 13 | Update(data entity.LogLogin) (*entity.LogLogin, error) 14 | Delete(infoId []int64) error 15 | DeleteAll() error 16 | } 17 | 18 | logLoginModelImpl struct { 19 | table string 20 | } 21 | ) 22 | 23 | var LogLoginModelDao LogLoginModel = &logLoginModelImpl{ 24 | table: `log_logins`, 25 | } 26 | 27 | func (m *logLoginModelImpl) Insert(data entity.LogLogin) (*entity.LogLogin, error) { 28 | err := global.Db.Table(m.table).Create(&data).Error 29 | return &data, err 30 | } 31 | 32 | func (m *logLoginModelImpl) FindOne(infoId int64) (*entity.LogLogin, error) { 33 | resData := new(entity.LogLogin) 34 | err := global.Db.Table(m.table).Where("info_id = ?", infoId).First(resData).Error 35 | return resData, err 36 | } 37 | 38 | func (m *logLoginModelImpl) FindListPage(page, pageSize int, data entity.LogLogin) (*[]entity.LogLogin, int64, error) { 39 | list := make([]entity.LogLogin, 0) 40 | var total int64 = 0 41 | offset := pageSize * (page - 1) 42 | db := global.Db.Table(m.table) 43 | // 此处填写 where参数判断 44 | if data.Status != "" { 45 | db = db.Where("status = ?", data.Status) 46 | } 47 | if data.LoginLocation != "" { 48 | db = db.Where("login_location like ?", "%"+data.LoginLocation+"%") 49 | } 50 | if data.Username != "" { 51 | db = db.Where("username like ?", "%"+data.Username+"%") 52 | } 53 | err := db.Where("delete_time IS NULL").Count(&total).Error 54 | if err != nil { 55 | return &list, total, err 56 | } 57 | err = db.Order("info_id desc").Limit(pageSize).Offset(offset).Find(&list).Error 58 | return &list, total, err 59 | } 60 | 61 | func (m *logLoginModelImpl) Update(data entity.LogLogin) (*entity.LogLogin, error) { 62 | err := global.Db.Table(m.table).Updates(&data).Error 63 | return &data, err 64 | } 65 | 66 | func (m *logLoginModelImpl) Delete(infoIds []int64) error { 67 | err := global.Db.Table(m.table).Delete(&entity.LogLogin{}, "info_id in (?)", infoIds).Error 68 | return err 69 | } 70 | 71 | func (m *logLoginModelImpl) DeleteAll() error { 72 | return global.Db.Exec("DELETE FROM log_logins").Error 73 | } 74 | -------------------------------------------------------------------------------- /apps/log/router/oper_log.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/log/api" 7 | "pandax/apps/log/entity" 8 | "pandax/apps/log/services" 9 | 10 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 11 | "github.com/emicklei/go-restful/v3" 12 | ) 13 | 14 | func InitOperLogRouter(container *restful.Container) { 15 | // 操作日志 16 | s := &api.LogOperApi{ 17 | LogOperApp: services.LogOperModelDao, 18 | } 19 | 20 | ws := new(restful.WebService) 21 | ws.Path("/log/logOper").Produces(restful.MIME_JSON) 22 | tags := []string{"日志信息"} 23 | 24 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 25 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志列表").Handle(s.GetOperLogList) 26 | }). 27 | Doc("获取操作日志列表"). 28 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 29 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 30 | Param(ws.QueryParameter("businessType", "businessType").DataType("string")). 31 | Param(ws.QueryParameter("operName", "operName").DataType("string")). 32 | Param(ws.QueryParameter("title", "title").DataType("string")). 33 | Metadata(restfulspec.KeyOpenAPITags, tags). 34 | Writes(model.ResultPage{}). 35 | Returns(200, "OK", model.ResultPage{})) 36 | 37 | ws.Route(ws.GET("/{operId}").To(func(request *restful.Request, response *restful.Response) { 38 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志信息").Handle(s.GetOperLog) 39 | }). 40 | Doc("获取操作日志信息"). 41 | Param(ws.PathParameter("operId", "Id").DataType("int")). 42 | Metadata(restfulspec.KeyOpenAPITags, tags). 43 | Writes(entity.LogOper{}). // on the response 44 | Returns(200, "OK", entity.LogOper{}). 45 | Returns(404, "Not Found", nil)) 46 | 47 | ws.Route(ws.DELETE("/{operId}").To(func(request *restful.Request, response *restful.Response) { 48 | restfulx.NewReqCtx(request, response).WithLog("删除操作日志信息").Handle(s.DeleteOperLog) 49 | }). 50 | Doc("删除操作日志信息"). 51 | Metadata(restfulspec.KeyOpenAPITags, tags). 52 | Param(ws.PathParameter("operId", "多id 1,2,3").DataType("string"))) 53 | 54 | ws.Route(ws.DELETE("/all").To(func(request *restful.Request, response *restful.Response) { 55 | restfulx.NewReqCtx(request, response).WithLog("清空操作日志信息").Handle(s.DeleteAll) 56 | }). 57 | Doc("清空操作日志信息"). 58 | Metadata(restfulspec.KeyOpenAPITags, tags)) 59 | 60 | container.Add(ws) 61 | } 62 | -------------------------------------------------------------------------------- /apps/system/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/biz" 5 | "github.com/PandaXGO/PandaKit/casbin" 6 | "github.com/PandaXGO/PandaKit/model" 7 | "github.com/PandaXGO/PandaKit/restfulx" 8 | "github.com/PandaXGO/PandaKit/utils" 9 | entity "pandax/apps/system/entity" 10 | services "pandax/apps/system/services" 11 | "pandax/pkg/global" 12 | ) 13 | 14 | type SystemApiApi struct { 15 | ApiApp services.SysApiModel 16 | } 17 | 18 | func (s *SystemApiApi) CreateApi(rc *restfulx.ReqCtx) { 19 | var api entity.SysApi 20 | restfulx.BindJsonAndValid(rc, &api) 21 | _, err := s.ApiApp.Insert(api) 22 | biz.ErrIsNil(err, "添加APi失败") 23 | } 24 | 25 | func (s *SystemApiApi) DeleteApi(rc *restfulx.ReqCtx) { 26 | ids := rc.Request.PathParameter("id") 27 | err := s.ApiApp.Delete(utils.IdsStrToIdsIntGroup(ids)) 28 | biz.ErrIsNil(err, "删除APi失败") 29 | } 30 | 31 | func (s *SystemApiApi) GetApiList(rc *restfulx.ReqCtx) { 32 | pageNum := restfulx.QueryInt(rc, "pageNum", 1) 33 | pageSize := restfulx.QueryInt(rc, "pageSize", 10) 34 | path := rc.Request.QueryParameter("path") 35 | description := rc.Request.QueryParameter("description") 36 | method := rc.Request.QueryParameter("method") 37 | apiGroup := rc.Request.QueryParameter("apiGroup") 38 | api := entity.SysApi{Path: path, Description: description, Method: method, ApiGroup: apiGroup} 39 | list, total, err := s.ApiApp.FindListPage(pageNum, pageSize, api) 40 | biz.ErrIsNil(err, "查询APi分页列表失败") 41 | rc.ResData = model.ResultPage{ 42 | Total: total, 43 | PageNum: int64(pageNum), 44 | PageSize: int64(pageSize), 45 | Data: list, 46 | } 47 | } 48 | 49 | func (s *SystemApiApi) GetApiById(rc *restfulx.ReqCtx) { 50 | id := restfulx.QueryInt(rc, "id", 0) 51 | data, err := s.ApiApp.FindOne(int64(id)) 52 | biz.ErrIsNil(err, "查询APi失败") 53 | rc.ResData = data 54 | 55 | } 56 | 57 | func (s *SystemApiApi) UpdateApi(rc *restfulx.ReqCtx) { 58 | var api entity.SysApi 59 | restfulx.BindJsonAndValid(rc, &api) 60 | err := s.ApiApp.Update(api) 61 | biz.ErrIsNil(err, "查询APi失败") 62 | } 63 | 64 | func (s *SystemApiApi) GetAllApis(rc *restfulx.ReqCtx) { 65 | data, err := s.ApiApp.FindList(entity.SysApi{}) 66 | biz.ErrIsNil(err, "查询APi列表失败") 67 | rc.ResData = data 68 | } 69 | 70 | func (s *SystemApiApi) GetPolicyPathByRoleId(rc *restfulx.ReqCtx) { 71 | roleKey := rc.Request.QueryParameter("roleKey") 72 | ca := casbin.CasbinService{ModelPath: global.Conf.Casbin.ModelPath} 73 | rc.ResData = ca.GetPolicyPathByRoleId(roleKey) 74 | } 75 | -------------------------------------------------------------------------------- /pkg/rule_engine/nodes/action_delay_node.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "fmt" 5 | "pandax/pkg/rule_engine/message" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const DelayNodeName = "DelayNode" 11 | 12 | type delayNode struct { 13 | bareNode 14 | PeriodTs int `json:"periodTs" yaml:"periodTs" jpath:"periodTs"` //周期时间 15 | MaxPendingMessages int `json:"maxPendingMessages" yaml:"maxPendingMessages" jpath:"maxPendingMessages"` //最大等待消息数 16 | messageQueue []*message.Message `jpath:"-"` 17 | delayTimer *time.Timer `jpath:"-"` 18 | lock sync.Mutex `jpath:"-"` 19 | } 20 | 21 | type delayNodeFactory struct{} 22 | 23 | func (f delayNodeFactory) Name() string { return DelayNodeName } 24 | func (f delayNodeFactory) Category() string { return NODE_CATEGORY_ACTION } 25 | func (f delayNodeFactory) Labels() []string { return []string{"Success", "Failure"} } 26 | func (f delayNodeFactory) Create(id string, meta Properties) (Node, error) { 27 | node := &delayNode{ 28 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()), 29 | lock: sync.Mutex{}, 30 | } 31 | _, err := decodePath(meta, node) 32 | node.messageQueue = make([]*message.Message, node.MaxPendingMessages) 33 | return node, err 34 | } 35 | 36 | func (n *delayNode) Handle(msg *message.Message) error { 37 | n.Debug(msg, message.DEBUGIN, "") 38 | successLabelNode := n.GetLinkedNode("Success") 39 | failureLabelNode := n.GetLinkedNode("Failure") 40 | if successLabelNode == nil || failureLabelNode == nil { 41 | return fmt.Errorf("no valid label linked node in %s", n.Name()) 42 | } 43 | 44 | // check wethere the time had already been started, queue message if started 45 | if n.delayTimer == nil { 46 | n.messageQueue = append(n.messageQueue, msg) 47 | n.delayTimer = time.NewTimer(time.Duration(n.PeriodTs) * time.Second) 48 | 49 | go func(n *delayNode) error { 50 | defer n.delayTimer.Stop() 51 | for { 52 | <-n.delayTimer.C 53 | n.lock.Lock() 54 | defer n.lock.Unlock() 55 | if len(n.messageQueue) > 0 { 56 | msg := n.messageQueue[0] 57 | n.messageQueue = n.messageQueue[0:] 58 | return successLabelNode.Handle(msg) 59 | } 60 | } 61 | }(n) 62 | return nil 63 | } 64 | n.lock.Lock() 65 | defer n.lock.Unlock() 66 | if len(n.messageQueue) == n.MaxPendingMessages { 67 | return failureLabelNode.Handle(msg) 68 | } 69 | n.messageQueue = append(n.messageQueue, msg) 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /apps/rule/services/rulechain_log.go: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | // 生成日期:2023-03-29 20:01:11 +0800 CST 3 | // 生成路径: apps/visual/services/rulechain.go 4 | // 生成人:panda 5 | // ========================================================================== 6 | 7 | package services 8 | 9 | import ( 10 | "pandax/apps/rule/entity" 11 | "pandax/pkg/global" 12 | "pandax/pkg/global/model" 13 | ) 14 | 15 | type ( 16 | RuleChainMsgLogModel interface { 17 | Insert(data entity.RuleChainMsgLog) (*entity.RuleChainMsgLog, error) 18 | FindListPage(page, pageSize int, data entity.RuleChainMsgLog) (*[]entity.RuleChainMsgLog, int64, error) 19 | Delete(data entity.RuleChainMsgLog) error 20 | } 21 | 22 | ruleChainLogModelImpl struct { 23 | table string 24 | } 25 | ) 26 | 27 | var RuleChainMsgLogModelDao RuleChainMsgLogModel = &ruleChainLogModelImpl{ 28 | table: `rule_chain_msg_log`, 29 | } 30 | 31 | func (m *ruleChainLogModelImpl) Insert(data entity.RuleChainMsgLog) (*entity.RuleChainMsgLog, error) { 32 | err := global.Db.Table(m.table).Create(&data).Error 33 | return &data, err 34 | } 35 | 36 | func (m *ruleChainLogModelImpl) FindListPage(page, pageSize int, data entity.RuleChainMsgLog) (*[]entity.RuleChainMsgLog, int64, error) { 37 | list := make([]entity.RuleChainMsgLog, 0) 38 | var total int64 = 0 39 | offset := pageSize * (page - 1) 40 | db := global.Db.Table(m.table) 41 | // 此处填写 where参数判断 42 | if data.DeviceName != "" { 43 | db = db.Where("device_name = ?", data.DeviceName) 44 | } 45 | if data.MessageId != "" { 46 | db = db.Where("message_id = ?", data.MessageId) 47 | } 48 | if data.MsgType != "" { 49 | db = db.Where("msg_type = ?", data.MsgType) 50 | } 51 | // 组织数据访问权限 52 | err := model.OrgAuthSet(db, data.RoleId, data.Owner) 53 | if err != nil { 54 | return nil, 0, err 55 | } 56 | err = db.Count(&total).Error 57 | if err != nil { 58 | return nil, 0, err 59 | } 60 | err = db.Order("create_at").Limit(pageSize).Offset(offset).Find(&list).Error 61 | return &list, total, err 62 | } 63 | 64 | func (m *ruleChainLogModelImpl) Delete(data entity.RuleChainMsgLog) error { 65 | db := global.Db.Table(m.table) 66 | // 此处填写 where参数判断 67 | if data.DeviceName != "" { 68 | db = db.Where("device_name = ?", data.DeviceName) 69 | } 70 | if data.MessageId != "" { 71 | db = db.Where("message_id = ?", data.MessageId) 72 | } 73 | if data.MsgType != "" { 74 | db = db.Where("msg_type = ?", data.MsgType) 75 | } 76 | return db.Delete(&entity.RuleChainMsgLog{}).Error 77 | } 78 | -------------------------------------------------------------------------------- /apps/develop/entity/dev_gen_table.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "github.com/PandaXGO/PandaKit/model" 4 | 5 | type DevGenTable struct { 6 | TableId int64 `gorm:"primaryKey;autoIncrement" json:"tableId"` // 编号 7 | OrgId int64 `json:"orgId" gorm:"type:int;comment:机构ID"` 8 | Owner string `json:"owner" gorm:"type:varchar(64);comment:创建者,所有者"` 9 | TableName string `gorm:"table_name" json:"tableName"` // 表名称 10 | TableComment string `gorm:"table_comment" json:"tableComment"` // 表描述 11 | ClassName string `gorm:"class_name" json:"className"` // 实体类名称 12 | TplCategory string `gorm:"tpl_category" json:"tplCategory"` // 使用的模板(crud单表操作 tree树表操作) 13 | PackageName string `gorm:"package_name" json:"packageName"` // 生成包路径 14 | ModuleName string `gorm:"module_name" json:"moduleName"` // 生成模块名 15 | BusinessName string `gorm:"business_name" json:"businessName"` // 生成业务名 16 | FunctionName string `gorm:"function_name" json:"functionName"` // 生成功能名 17 | FunctionAuthor string `gorm:"function_author" json:"functionAuthor"` // 生成功能作者 18 | Options string `gorm:"options" json:"options"` // 其它生成选项 19 | Remark string `gorm:"remark" json:"remark"` // 备注 20 | PkColumn string `gorm:"pk_column;" json:"pkColumn"` 21 | PkGoField string `gorm:"pk_go_field" json:"pkGoField"` 22 | PkGoType string `gorm:"pk_go_type" json:"pkGoType"` 23 | PkJsonField string `gorm:"pk_json_field" json:"pkJsonField"` 24 | Columns []DevGenTableColumn `gorm:"-" json:"columns"` // 字段信息 25 | model.BaseModel 26 | 27 | RoleId int64 `gorm:"-"` // 角色数据权限 28 | } 29 | 30 | type DBTables struct { 31 | TableName string `gorm:"column:TABLE_NAME" json:"tableName"` 32 | Engine string `gorm:"column:ENGINE" json:"engine"` 33 | TableRows string `gorm:"column:TABLE_ROWS" json:"tableRows"` 34 | TableCollation string `gorm:"column:TABLE_COLLATION" json:"tableCollation"` 35 | CreateTime string `gorm:"column:CREATE_TIME" json:"createTime"` 36 | UpdateTime string `gorm:"column:UPDATE_TIME" json:"updateTime"` 37 | TableComment string `gorm:"column:TABLE_COMMENT" json:"tableComment"` 38 | } 39 | -------------------------------------------------------------------------------- /apps/device/router/device_alarm.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/PandaXGO/PandaKit/model" 5 | "github.com/PandaXGO/PandaKit/restfulx" 6 | "pandax/apps/device/api" 7 | "pandax/apps/device/entity" 8 | "pandax/apps/device/services" 9 | 10 | restfulspec "github.com/emicklei/go-restful-openapi/v2" 11 | "github.com/emicklei/go-restful/v3" 12 | ) 13 | 14 | func InitDeviceAlarmRouter(container *restful.Container) { 15 | s := &api.DeviceAlarmApi{ 16 | DeviceAlarmApp: services.DeviceAlarmModelDao, 17 | } 18 | 19 | ws := new(restful.WebService) 20 | ws.Path("/device/alarm").Produces(restful.MIME_JSON) 21 | tags := []string{"设备告警"} 22 | 23 | ws.Route(ws.GET("/panel").To(func(request *restful.Request, response *restful.Response) { 24 | restfulx.NewReqCtx(request, response).WithLog("获取面板告警分组").Handle(s.GetDeviceAlarmPanel) 25 | }). 26 | Doc("获取面板告警分组"). 27 | Metadata(restfulspec.KeyOpenAPITags, tags). 28 | Writes(entity.DeviceAlarmCount{}). 29 | Returns(200, "OK", entity.DeviceAlarmCount{})) 30 | 31 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) { 32 | restfulx.NewReqCtx(request, response).WithLog("获取告警分页列表").Handle(s.GetDeviceAlarmList) 33 | }). 34 | Doc("获取告警分页列表"). 35 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")). 36 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")). 37 | Param(ws.QueryParameter("deviceId", "设备Id").Required(false).DataType("string")). 38 | Param(ws.QueryParameter("type", "告警类型").Required(false).DataType("string")). 39 | Param(ws.QueryParameter("level", "告警等级").Required(false).DataType("string")). 40 | Param(ws.QueryParameter("state", "告警状态").Required(false).DataType("string")). 41 | Metadata(restfulspec.KeyOpenAPITags, tags). 42 | Writes(model.ResultPage{}). 43 | Returns(200, "OK", model.ResultPage{})) 44 | 45 | ws.Route(ws.PUT("").To(func(request *restful.Request, response *restful.Response) { 46 | restfulx.NewReqCtx(request, response).WithLog("修改告警信息").Handle(s.UpdateDeviceAlarm) 47 | }). 48 | Doc("修改告警信息"). 49 | Metadata(restfulspec.KeyOpenAPITags, tags). 50 | Reads(entity.DeviceAlarm{})) 51 | 52 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) { 53 | restfulx.NewReqCtx(request, response).WithLog("删除告警信息").Handle(s.DeleteDeviceAlarm) 54 | }). 55 | Doc("删除告警信息"). 56 | Metadata(restfulspec.KeyOpenAPITags, tags). 57 | Param(ws.PathParameter("id", "多id 1,2,3").DataType("string"))) 58 | 59 | container.Add(ws) 60 | } 61 | --------------------------------------------------------------------------------