├── default.log
├── shutdown.bat
├── resource
├── excel
│ └── 字典_20211201154616.xlsx
├── rbac_model.conf
├── template
│ ├── js
│ │ └── api.template
│ └── go
│ │ └── entity.template
├── default.pem
└── default.key
├── pkg
├── config
│ ├── queue.go
│ ├── casbin.go
│ ├── redis.go
│ ├── app.go
│ ├── oss.go
│ ├── taos.go
│ ├── gen.go
│ ├── mqtt.go
│ ├── jwt.go
│ ├── log.go
│ ├── server.go
│ ├── config.go
│ └── db.go
├── tool
│ ├── base_test.go
│ ├── conv.go
│ ├── device.go
│ ├── base.go
│ ├── excel.go
│ └── local.go
├── transport
│ ├── transport.go
│ ├── grpc_server.go
│ └── http_server.go
├── rule_engine
│ ├── nodes
│ │ ├── template_engine.go
│ │ ├── input_node.go
│ │ ├── init.go
│ │ ├── filter_message_type_node.go
│ │ ├── filter_script_node.go
│ │ ├── filter_message_type_switch_node.go
│ │ ├── transform_script_node.go
│ │ ├── filter_device_type_switch_node.go
│ │ ├── action_rpc_request_node.go
│ │ ├── filter_switch_node.go
│ │ ├── action_rpc_respond_node.go
│ │ ├── metadata.go
│ │ ├── external_rule_chain_node.go
│ │ ├── action_save_attributes_node.go
│ │ ├── external_send_sms_node.go
│ │ ├── action_save_timeseries_node.go
│ │ ├── action_log_node.go
│ │ ├── external_nats_node.go
│ │ ├── factory.go
│ │ ├── transform_delete_key_node.go
│ │ ├── action_clear_alarm_node.go
│ │ ├── action_generator_node.go
│ │ ├── transform_rename_key_node.go
│ │ ├── external_wechat_node.go
│ │ ├── action_delay_node.go
│ │ └── action_create_alarm_node.go
│ ├── instance.go
│ ├── instance_test.go
│ ├── manifest
│ │ └── manifest.go
│ └── message
│ │ └── message1.go
├── middleware
│ ├── escape_html.go
│ ├── cors.go
│ ├── rate.go
│ ├── oper.go
│ ├── swagger.go
│ ├── permission.go
│ └── log.go
├── global
│ ├── global.go
│ ├── global_model.go
│ └── const_device.go
├── initialize
│ ├── event.go
│ ├── table.go
│ └── router.go
├── websocket
│ └── socket_server_pool.go
├── tdengine
│ ├── TDengineModel.go
│ ├── tdengine_event.go
│ └── tdengine_log.go
└── mqtt
│ └── mqtt.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_dept.go
│ │ ├── role_menu.go
│ │ ├── api.go
│ │ ├── tenant.go
│ │ ├── notice.go
│ │ ├── post.go
│ │ ├── config.go
│ │ ├── dept.go
│ │ ├── role.go
│ │ ├── dict.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
│ │ ├── notice.go
│ │ ├── config.go
│ │ ├── api.go
│ │ ├── tenant.go
│ │ ├── upload.go
│ │ ├── post.go
│ │ └── system.go
│ └── services
│ │ ├── role_dept.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
├── device
│ ├── entity
│ │ ├── device_vo.go
│ │ └── device_exp.go
│ ├── api
│ │ ├── device_alarm.go
│ │ ├── product_ota.go
│ │ ├── device_cmd.go
│ │ └── device_group.go
│ ├── router
│ │ ├── device_cmd.go
│ │ └── device_alarm.go
│ └── tsl
│ │ └── tsl.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
├── uploads
└── file
│ ├── 626213519d0907c2f2ab12919fbd7779_20230414142538.png
│ ├── 9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png
│ └── dc49aad85da7ae4c37374bf79e29134c_20230414144346.png
├── deploy
├── manifest
│ ├── namespace.yaml
│ ├── kustomization.yaml
│ ├── service.yaml
│ ├── rbac.yaml
│ └── deployment.yaml
├── mysql
│ ├── namespace.yaml
│ ├── kustomization.yaml
│ ├── mysql-pv.yaml
│ └── deployment-service.yaml
├── README.md
├── deploy.yaml
└── mysql.yaml
├── fatal.log
├── .idea
├── vcs.xml
├── .gitignore
├── modules.xml
└── PandaX.iml
├── iothub
├── iothub_session.go
├── reverse_control.go
├── grpc_server.go
├── topic.go
└── README.md
├── .gitignore
├── Dockerfile
└── config.yml
/default.log:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/shutdown.bat:
--------------------------------------------------------------------------------
1 | taskkill /pid 25952 -t -f
--------------------------------------------------------------------------------
/resource/excel/字典_20211201154616.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PandaGoAdmin/PandaX/HEAD/resource/excel/字典_20211201154616.xlsx
--------------------------------------------------------------------------------
/pkg/config/queue.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Queue struct {
4 | Enable bool `yaml:" enable"`
5 | Num int64 `yaml:" num"` //并发数
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 |
--------------------------------------------------------------------------------
/pkg/tool/base_test.go:
--------------------------------------------------------------------------------
1 | package tool
2 |
3 | import "testing"
4 |
5 | func TestToCamelCase(t *testing.T) {
6 | camelCase := ToCamelCase("hello_world")
7 | t.Log(camelCase)
8 | }
9 |
--------------------------------------------------------------------------------
/uploads/file/626213519d0907c2f2ab12919fbd7779_20230414142538.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PandaGoAdmin/PandaX/HEAD/uploads/file/626213519d0907c2f2ab12919fbd7779_20230414142538.png
--------------------------------------------------------------------------------
/uploads/file/9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PandaGoAdmin/PandaX/HEAD/uploads/file/9b37cd4ca37090649adcee8bf17cfdcc_20230414141350.png
--------------------------------------------------------------------------------
/uploads/file/dc49aad85da7ae4c37374bf79e29134c_20230414144346.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PandaGoAdmin/PandaX/HEAD/uploads/file/dc49aad85da7ae4c37374bf79e29134c_20230414144346.png
--------------------------------------------------------------------------------
/deploy/manifest/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
--------------------------------------------------------------------------------
/deploy/mysql/namespace.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: mysql
6 | app.kubernetes.io/version: master
7 | name: pandax-db
--------------------------------------------------------------------------------
/fatal.log:
--------------------------------------------------------------------------------
1 |
2 | 2023-08-22 15:15:49--------------------------------
3 |
4 | 2023-08-22 15:51:54--------------------------------
5 |
6 | 2023-08-23 16:11:33--------------------------------
7 |
--------------------------------------------------------------------------------
/deploy/mysql/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - namespace.yaml
3 | - mysql-pv.yaml
4 | - deployment-service.yaml
5 |
6 | commonLabels:
7 | app.kubernetes.io/version: 1.0.0
8 | namespace: pandax-db
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/deploy/manifest/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - namespace.yaml
3 | - rbac.yaml
4 | - service.yaml
5 | - deployment.yaml
6 |
7 | commonLabels:
8 | app.kubernetes.io/version: 1.0.0
9 | namespace: pandax
10 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 | ./idea
--------------------------------------------------------------------------------
/iothub/iothub_session.go:
--------------------------------------------------------------------------------
1 | package iothub
2 |
3 | type DeviceEventInfo struct {
4 | DeviceId string `json:"deviceId"`
5 | Datas string `json:"datas"`
6 | Type string `json:"type"`
7 | RequestId string `json:"requestId"`
8 | }
9 |
--------------------------------------------------------------------------------
/apps/system/entity/role_dept.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type SysRoleDept struct {
4 | RoleId int64 `gorm:"type:int"`
5 | DeptId int64 `gorm:"type:int"`
6 | Id int64 `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id" form:"id"`
7 | }
8 |
--------------------------------------------------------------------------------
/.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 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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 == '*')
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:latest
2 | LABEL MAINTAINER="PandaX"
3 |
4 | WORKDIR /go/src/panda
5 | COPY ./pandax ./
6 | COPY ./config.yml ./
7 | COPY ./resource ./resource
8 | COPY ./uploads ./uploads
9 |
10 | RUN chmod 755 ./pandax
11 |
12 | EXPOSE 7788
13 | EXPOSE 9001
14 | EXPOSE 8801
15 | EXPOSE 5060/udp
16 |
17 | ENTRYPOINT ./pandax
--------------------------------------------------------------------------------
/deploy/manifest/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: 80
11 | targetPort: http
12 | protocol: TCP
13 | name: http
14 | selector:
15 | app.kubernetes.io/name: pandax
16 |
--------------------------------------------------------------------------------
/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/config/mqtt.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type Mqtt struct {
4 | Broker string `mapstructure:"broker" json:"broker" yaml:"broker"`
5 | Qos int `mapstructure:"qos" json:"qos" yaml:"qos"`
6 | Username string `mapstructure:"username" json:"username" yaml:"username"`
7 | Password string `mapstructure:"password" json:"password" yaml:"password"`
8 | }
9 |
--------------------------------------------------------------------------------
/.idea/PandaX.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/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/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/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/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"
5 | )
6 |
7 | type JobLog struct {
8 | global.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 |
--------------------------------------------------------------------------------
/pkg/global/global.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/rediscli"
5 | "github.com/sirupsen/logrus"
6 | "gorm.io/gorm"
7 | "pandax/pkg/config"
8 | "pandax/pkg/events"
9 | "pandax/pkg/mqtt"
10 | "pandax/pkg/tdengine"
11 | )
12 |
13 | var (
14 | Log *logrus.Logger // 日志
15 | Db *gorm.DB // gorm
16 | RedisDb *rediscli.RedisDB
17 | TdDb *tdengine.TdEngine
18 | Conf *config.Config
19 | MqttClient *mqtt.IothubMqttClient
20 | )
21 | var EventEmitter = events.EventEmitter{}
22 |
--------------------------------------------------------------------------------
/deploy/mysql/mysql-pv.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolume
3 | metadata:
4 | name: mysql-pv-volume
5 | labels:
6 | type: local
7 | spec:
8 | storageClassName: manual
9 | capacity:
10 | storage: 20Gi
11 | accessModes:
12 | - ReadWriteOnce
13 | hostPath:
14 | path: "/mnt/data"
15 | ---
16 | apiVersion: v1
17 | kind: PersistentVolumeClaim
18 | metadata:
19 | name: mysql-pv-claim
20 | spec:
21 | storageClassName: manual
22 | accessModes:
23 | - ReadWriteOnce
24 | resources:
25 | requests:
26 | storage: 20Gi
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | DeptId int64 `json:"deptId" gorm:"type:int;comment:部门Id,部门及子部门"`
11 | UserName string `json:"userName" gorm:"type:varchar(64);comment:发布人"`
12 |
13 | DeptIds []int64 `json:"deptIds" gorm:"-"`
14 | model.BaseModel
15 | }
16 |
--------------------------------------------------------------------------------
/deploy/manifest/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: pandax
5 | labels:
6 | app.kubernetes.io/name: pandax
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app.kubernetes.io/name: pandax
12 | template:
13 | metadata:
14 | labels:
15 | app.kubernetes.io/name: pandax
16 | spec:
17 | serviceAccountName: pandax
18 | containers:
19 | - name: pandax
20 | image: xmadmin/pandax:v1.0
21 | imagePullPolicy: IfNotPresent
22 | ports:
23 | - name: http
24 | containerPort: 8080
25 | protocol: TCP
26 |
--------------------------------------------------------------------------------
/apps/rule/entity/rulechain_data.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | type RuleDataJson struct {
8 | LfData struct {
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 | } `json:"lfData"`
14 | }
15 |
16 | // 序列化
17 | func (m *RuleDataJson) MarshalBinary() (data []byte, err error) {
18 | return json.Marshal(m)
19 | }
20 |
21 | // 反序列化
22 | func (m *RuleDataJson) UnmarshalBinary(data []byte) error {
23 | return json.Unmarshal(data, m)
24 | }
25 |
--------------------------------------------------------------------------------
/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/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/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/initialize/event.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "pandax/apps/device/entity"
5 | "pandax/apps/device/services"
6 | "pandax/pkg/events"
7 | "pandax/pkg/global"
8 | "time"
9 | )
10 |
11 | // 初始化事件监听
12 | func InitEvents() {
13 | // 监听规则链改变 更新所有绑定改规则链的产品
14 | global.EventEmitter.On(events.ProductChainRuleEvent, func(ruleId, codeData string) {
15 | global.Log.Infof("规则链%s变更", ruleId)
16 | list := services.ProductModelDao.FindList(entity.Product{
17 | RuleChainId: ruleId,
18 | })
19 | if list != nil {
20 | for _, product := range *list {
21 | err := global.RedisDb.Set(product.Id, codeData, time.Hour*24*365)
22 | if err != nil {
23 | global.Log.Errorf("事件监听执行错误:%s", err.Error())
24 | }
25 | }
26 | }
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/global/global_model.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import "time"
4 |
5 | type BaseModel struct {
6 | Id string `json:"id" gorm:"primary_key;"`
7 | CreatedAt time.Time `gorm:"column:create_time" json:"createTime" form:"create_time"`
8 | UpdatedAt time.Time `gorm:"column:update_time" json:"updateTime" form:"update_time"`
9 | }
10 |
11 | type BaseAuthModel struct {
12 | Id string `json:"id" gorm:"primary_key;"`
13 | Owner string `json:"owner" gorm:"type:varchar(64);comment:创建者,所有者"`
14 | OrgId string `json:"orgId" gorm:"type:varchar(64);comment:机构ID"`
15 | CreatedAt time.Time `gorm:"column:create_time" json:"createTime" form:"create_time"`
16 | UpdatedAt time.Time `gorm:"column:update_time" json:"updateTime" form:"update_time"`
17 | }
18 |
--------------------------------------------------------------------------------
/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/global/const_device.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | // 告警等级
4 | const (
5 | CRITICAL = "CRITICAL" // 危险
6 | MAJOR = "MAJOR" // 重要
7 | MINOR = "MINOR" // 次要
8 | WARNING = "WARNING" // 警告
9 | INDETERMINATE = "INDETERMINATE" // 不确定
10 | )
11 |
12 | // 告警状态
13 | const (
14 | ALARMING = "0" // 告警中
15 | CONFIRMED = "1" // 已确认
16 | CLEARED = "2" // 已清除
17 | CLOSED = "3" // 已关闭
18 | )
19 |
20 | // 设备状态
21 | const (
22 | INACTIVE = "inactive" //未激活
23 | ONLINE = "online" //在线
24 | OFFLINE = "offline" // 离线
25 | )
26 |
27 | // 设备类型
28 | const (
29 | DIRECT = "direct" //直连设备
30 | GATEWAY = "gateway" //网关设备
31 | GATEWAYS = "gatewayS" // 网关子设备
32 | MONITOR = "monitor" // 监控设备
33 | )
34 |
35 | // 设备命令状态
36 | const (
37 | CMDSUCCESS = "0" //执行成功
38 | CMDFAIL = "1" //执行失败
39 | CMDRUNNING = "2" // 执行中
40 | )
41 |
--------------------------------------------------------------------------------
/deploy/README.md:
--------------------------------------------------------------------------------
1 | ## K3S
2 | 1. 安装
3 | curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION="v1.23.1+k3s1" sh -
4 | 2. 卸载
5 | sudo /usr/local/bin/k3s-uninstall.sh
6 |
7 | ## mysql 安装
8 | 1. kubectl kustomize ./deploy/mysql -o ./deploy/mysql.yaml
9 | 2. kubectl apply -f ./deploy/mysql.yaml
10 |
11 | ## k3s部署流程
12 | 1. 设置打包环境
13 | go env -w GOOS=linux
14 | go env -w GOARCH=amd64
15 | 2. 构建Linux执行命令
16 | go build -o pandax .
17 | 4. 构建docker镜像 (修改版本号 xmadmin/pandax:v1.0)
18 | docker build -t xmadmin/pandax:v1.4 --rm .
19 | 5. 上传daocker镜像
20 | docker push xmadmin/pandax:v1.4
21 | 6. 生成 deploy.yaml
22 | kubectl kustomize deploy/manifest -o deploy/deploy.yaml
23 | 7. k8s安装yaml
24 | kubectl apply -f deploy/deploy.yaml
25 |
26 | ## 查看部署状态
27 | 8. 查看 yaml 的安装状态
28 | kubectl get pods -n pandax
29 | kubectl get services -n pandax
--------------------------------------------------------------------------------
/apps/job/entity/job.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "pandax/pkg/global"
5 | )
6 |
7 | type SysJob struct {
8 | global.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 |
--------------------------------------------------------------------------------
/apps/device/entity/device_vo.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | type DeviceStatusVo struct {
4 | Name string `json:"name"`
5 | Key string `json:"key"`
6 | Type string `json:"type"`
7 | Define any `json:"define"`
8 | Value any `json:"value"`
9 | Time any `json:"time"`
10 | }
11 |
12 | type VisualClass struct {
13 | ClassId string `json:"classId"`
14 | Name string `json:"name"`
15 | Attrs []VisualTwinAttr `json:"attrs"`
16 | }
17 |
18 | type VisualTwinAttr struct {
19 | Key string `json:"key"`
20 | Name string `json:"name"`
21 | Type string `json:"type"`
22 | Rw string `json:"rw"` //属性的操作权限
23 | }
24 |
25 | type VisualTwin struct {
26 | TwinId string `json:"twinId"`
27 | Name string `json:"name"`
28 | }
29 |
30 | // 发送数据
31 | type VisualTwinSendAttrs struct {
32 | TwinId string `json:"twinId"`
33 | Attrs map[string]interface{} `json:"attrs"`
34 | }
35 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/iothub/reverse_control.go:
--------------------------------------------------------------------------------
1 | package iothub
2 |
3 | // 指令下发
4 | /*func Control(assets, thingModel, device_name, parameter string, operation bool) error {
5 | topic := fmt.Sprintf("control/%s/%s", assets, device_name)
6 | log.Println(topic)
7 | payload := fmt.Sprintf(`{"method":"control","data":{"parameter": "%s","operation":%t}}`, parameter, operation)
8 | //Publish(*global.GVA_MQTT, topic, 1, payload)
9 | return nil
10 | }
11 |
12 | func ControlState(assets, thingModel, device_name string) (map[string]interface{}, error) {
13 | topic := fmt.Sprintf("control/%s/%s", assets, device_name)
14 | payload := fmt.Sprintf(`{"method":"state","data":{}}`)
15 | if Publish(*global.GVA_MQTT, topic, 1, payload) != nil {
16 | return nil, errors.New("下发获取状态参数指令失败")
17 | }
18 | select {
19 | case state := <-controlState:
20 | return state, nil
21 | case <-time.After(10 * time.Second):
22 | return nil, errors.New("请求指令状态超时")
23 | }
24 | }
25 | */
26 |
--------------------------------------------------------------------------------
/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 Metadata) (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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/iothub/grpc_server.go:
--------------------------------------------------------------------------------
1 | package iothub
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 |
--------------------------------------------------------------------------------
/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 |
24 | // 日志 TDengine
25 | type TdLog struct {
26 | Ts string `json:"ts" dc:"时间"`
27 | Device string `json:"device" dc:"设备标识"`
28 | Type string `json:"type" dc:"日志类型"`
29 | Content string `json:"content" dc:"日志内容"`
30 | }
31 |
--------------------------------------------------------------------------------
/deploy/mysql/deployment-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: mysql
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 | selector:
20 | matchLabels:
21 | app: mysql
22 | strategy:
23 | type: Recreate
24 | template:
25 | metadata:
26 | labels:
27 | app: mysql
28 | spec:
29 | containers:
30 | - image: mysql:8.0.23
31 | name: mysql
32 | env:
33 | - name: MYSQL_ROOT_PASSWORD
34 | value: pandax
35 | ports:
36 | - containerPort: 3306
37 | name: mysql
38 | protocol: TCP
39 | volumeMounts:
40 | - name: mysql-persistent-storage
41 | mountPath: /var/lib/mysql
42 | volumes:
43 | - name: mysql-persistent-storage
44 | persistentVolumeClaim:
45 | claimName: mysql-pv-claim
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/apps/system/services/role_dept.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "fmt"
5 | "github.com/PandaXGO/PandaKit/biz"
6 | "pandax/apps/system/entity"
7 | "pandax/pkg/global"
8 | )
9 |
10 | type (
11 | SysRoleDeptModel interface {
12 | Insert(roleId int64, deptIds []int64) bool
13 | Delete(rm entity.SysRoleDept)
14 | }
15 |
16 | sysRoleDeptImpl struct {
17 | table string
18 | }
19 | )
20 |
21 | var SysRoleDeptModelDao SysRoleDeptModel = &sysRoleDeptImpl{
22 | table: `sys_role_depts`,
23 | }
24 |
25 | func (m *sysRoleDeptImpl) Insert(roleId int64, deptIds []int64) bool {
26 | sql := "INSERT INTO sys_role_depts (role_id, dept_id) VALUES "
27 |
28 | for i := 0; i < len(deptIds); i++ {
29 | if len(deptIds)-1 == i {
30 | //最后一条数据 以分号结尾
31 | sql += fmt.Sprintf("(%d,%d);", roleId, deptIds[i])
32 | } else {
33 | sql += fmt.Sprintf("(%d,%d),", roleId, deptIds[i])
34 | }
35 | }
36 | global.Db.Exec(sql)
37 | return true
38 | }
39 |
40 | func (m *sysRoleDeptImpl) Delete(rm entity.SysRoleDept) {
41 | biz.ErrIsNil(global.Db.Table(m.table).Where("role_id = ?", rm.RoleId).Delete(&rm).Error, "删除角色失败")
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/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}}:number) {
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}}: string) {
40 | return request({
41 | url: '/{{.PackageName}}/{{.BusinessName}}/' + {{.PkJsonField}},
42 | method: 'delete'
43 | })
44 | }
--------------------------------------------------------------------------------
/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.SysDept{},
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.SysRoleDept{},
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 |
--------------------------------------------------------------------------------
/apps/job/api/log_job.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/job/entity"
8 | "pandax/apps/job/services"
9 | )
10 |
11 | type JobLogApi struct {
12 | JobLogApp services.JobLogModel
13 | }
14 |
15 | // GetJobLogList Job日志列表
16 | func (l *JobLogApi) GetJobLogList(rc *restfulx.ReqCtx) {
17 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
18 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
19 | name := restfulx.QueryParam(rc, "name")
20 | status := restfulx.QueryParam(rc, "status")
21 |
22 | list, total := l.JobLogApp.FindListPage(pageNum, pageSize, entity.JobLog{Name: name, Status: status})
23 | rc.ResData = model.ResultPage{
24 | Total: total,
25 | PageNum: int64(pageNum),
26 | PageSize: int64(pageNum),
27 | Data: list,
28 | }
29 | }
30 |
31 | // DeleteJobLog 批量删除登录日志
32 | func (l *JobLogApi) DeleteJobLog(rc *restfulx.ReqCtx) {
33 | logIds := restfulx.QueryParam(rc, "logId")
34 | group := utils.IdsStrToIdsIntGroup(logIds)
35 | l.JobLogApp.Delete(group)
36 | }
37 |
38 | // DeleteAll 清空登录日志
39 | func (l *JobLogApi) DeleteAll(rc *restfulx.ReqCtx) {
40 | l.JobLogApp.DeleteAll()
41 | }
42 |
--------------------------------------------------------------------------------
/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 | DeptId int64 `json:"deptId"` // 角色ID
34 | }
35 |
--------------------------------------------------------------------------------
/apps/system/entity/dept.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "github.com/PandaXGO/PandaKit/model"
4 |
5 | // 部门组织
6 | type SysDept struct {
7 | DeptId int64 `json:"deptId" gorm:"primary_key;AUTO_INCREMENT"` //部门编码
8 | ParentId int64 `json:"parentId" gorm:"type:int;comment:上级部门"`
9 | DeptPath string `json:"deptPath" gorm:"type:varchar(255);comment:部门路径"`
10 | DeptName string `json:"deptName" 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 []SysDept `json:"children" gorm:"-"`
19 | model.BaseModel
20 | }
21 |
22 | type DeptLable struct {
23 | DeptId int64 `gorm:"-" json:"deptId"`
24 | DeptName string `gorm:"-" json:"deptName"`
25 | Children []DeptLable `gorm:"-" json:"children"`
26 | }
27 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | Model string `yaml:"model"`
9 | Cors bool `yaml:"cors"`
10 | Rate *Rate `yaml:"rate"`
11 | IsInitTable bool `yaml:"isInitTable"`
12 | DbType string `yaml:"db-type"`
13 | ExcelDir string `yaml:"excel-dir"`
14 | Tls *Tls `yaml:"tls"`
15 | Static *[]*Static `yaml:"static"`
16 | StaticFile *[]*StaticFile `yaml:"static-file"`
17 | }
18 |
19 | func (s *Server) GetPort() string {
20 | return fmt.Sprintf(":%d", s.Port)
21 | }
22 |
23 | type Static struct {
24 | RelativePath string `yaml:"relative-path"`
25 | Root string `yaml:"root"`
26 | }
27 |
28 | type StaticFile struct {
29 | RelativePath string `yaml:"relative-path"`
30 | Filepath string `yaml:"filepath"`
31 | }
32 |
33 | type Tls struct {
34 | Enable bool `yaml:"enable"` // 是否启用tls
35 | KeyFile string `yaml:"key-file"` // 私钥文件路径
36 | CertFile string `yaml:"cert-file"` // 证书文件路径
37 | }
38 |
39 | type Rate struct {
40 | Enable bool `yaml:"enable"` // 是否限流
41 | RateNum float64 `yaml:"rate-num"` // 限流数量
42 | }
43 |
--------------------------------------------------------------------------------
/apps/rule/api/rulechain_log.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/model"
5 | "github.com/PandaXGO/PandaKit/restfulx"
6 | "pandax/apps/rule/entity"
7 | "pandax/apps/rule/services"
8 | "pandax/pkg/rule_engine/nodes"
9 | "strings"
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 | list, total := p.RuleChainMsgLogApp.FindListPage(pageNum, pageSize, data)
27 |
28 | rc.ResData = model.ResultPage{
29 | Total: total,
30 | PageNum: int64(pageNum),
31 | PageSize: int64(pageNum),
32 | Data: list,
33 | }
34 | }
35 |
36 | // DeleteRuleChainMsgLog 删除规则链
37 | func (p *RuleChainMsgLogApi) DeleteRuleChainMsgLog(rc *restfulx.ReqCtx) {
38 | id := restfulx.PathParam(rc, "id")
39 | ids := strings.Split(id, ",")
40 | p.RuleChainMsgLogApp.Delete(ids)
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/tdengine/tdengine_event.go:
--------------------------------------------------------------------------------
1 | package tdengine
2 |
3 | import (
4 | "fmt"
5 | "github.com/kakuilan/kgo"
6 | "strings"
7 | )
8 |
9 | type ConnectInfo struct {
10 | Ts string `json:"ts"`
11 | ClientID string `json:"clientId"`
12 | Type string `json:"type"` // 连接类型
13 | PeerHost string `json:"peerHost"`
14 | SocketPort string `json:"sockPort"`
15 | Protocol string `json:"protocol"`
16 | DeviceId string `json:"deviceId"`
17 | }
18 |
19 | // CreateEventTable 创建设备连接事件表
20 | func (s *TdEngine) CreateEventTable() (err error) {
21 | sql := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.device_connect (ts TIMESTAMP,deviceId NCHAR(64),
22 | type NCHAR(64),clientId NCHAR(64),peerHost NCHAR(64),sockPort NCHAR(64),protocol NCHAR(64))`, s.dbName)
23 | _, err = s.db.Exec(sql)
24 | return
25 | }
26 |
27 | func (s *TdEngine) InsertEvent(data map[string]any) (err error) {
28 | if len(data) == 0 {
29 | return
30 | }
31 | var (
32 | field = []string{}
33 | value = []string{}
34 | )
35 | for k, v := range data {
36 | field = append(field, k)
37 | value = append(value, "'"+kgo.KConv.ToStr(v)+"'")
38 | }
39 |
40 | sql := "INSERT INTO ? (?) VALUES (?)"
41 | _, err = s.db.Exec(sql, "device_connect", strings.Join(field, ","), strings.Join(value, ","))
42 | return err
43 | }
44 |
--------------------------------------------------------------------------------
/apps/rule/entity/rulechain.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import (
4 | "pandax/pkg/global"
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 | global.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 `json:"message_id"`
33 | MsgType string `json:"msg_type"`
34 | DeviceName string `json:"device_name"`
35 | Ts time.Time `json:"ts"`
36 | Content string `json:"content"`
37 | CreatedAt time.Time // 创建时间
38 | }
39 |
40 | func (RuleChainMsgLog) TableName() string {
41 | return "rule_chain_msg_log"
42 | }
43 |
--------------------------------------------------------------------------------
/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(rpcRespondFactory{})
32 | RegisterFactory(rpcRequestNodeFactory{})
33 | }
34 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | type SysRole struct {
9 | model.BaseModel
10 | RoleId int64 `json:"roleId" gorm:"primary_key;AUTO_INCREMENT"`
11 | RoleName string `json:"roleName" gorm:"type:varchar(128);comment:角色名称"`
12 | Status string `json:"status" gorm:"type:varchar(1);comment:状态"`
13 | RoleKey string `json:"roleKey" gorm:"type:varchar(128);comment:角色代码"`
14 | RoleSort int64 `json:"roleSort" gorm:"type:int;comment:角色排序"`
15 | DataScope string `json:"dataScope" gorm:"type:varchar(1);comment:数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)"`
16 | CreateBy string `json:"createBy" gorm:"type:varchar(128);comment:创建人"`
17 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);comment:修改人"`
18 | Remark string `json:"remark" gorm:"type:varchar(255);comment:备注"`
19 | ApiIds []casbin.CasbinRule `json:"apiIds" gorm:"-"`
20 | MenuIds []int64 `json:"menuIds" gorm:"-"`
21 | DeptIds []int64 `json:"deptIds" gorm:"-"`
22 | }
23 |
24 | type MenuIdList struct {
25 | MenuId int64 `json:"menuId"`
26 | }
27 |
28 | type DeptIdList struct {
29 | DeptId int64 `json:"deptId"`
30 | }
31 |
--------------------------------------------------------------------------------
/iothub/topic.go:
--------------------------------------------------------------------------------
1 | package iothub
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 | DisconnectGatewayTopic = "v1/gateway/disconnect"
19 |
20 | RpcReqReg = `v1/devices/me/rpc/request/(.*?)$`
21 | )
22 |
23 | var IotHubTopic = NewIotHubTopic()
24 |
25 | type TopicMeg map[string]string
26 |
27 | // 消息的来源类型
28 | func NewIotHubTopic() TopicMeg {
29 | return map[string]string{
30 | AttributesTopic: message.AttributesMes,
31 | RowTopic: message.RowMes,
32 | TelemetryTopic: message.TelemetryMes,
33 | AttributesGatewayTopic: message.GATEWAY,
34 | TelemetryGatewayTopic: message.GATEWAY,
35 | ConnectGatewayTopic: message.GATEWAY,
36 | DisconnectGatewayTopic: 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") {
45 | return message.RpcRequestMes
46 | }
47 | return ""
48 | }
49 |
--------------------------------------------------------------------------------
/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 | "github.com/dgrijalva/jwt-go"
9 | "pandax/pkg/global"
10 | )
11 |
12 | func PermissionHandler(rc *restfulx.ReqCtx) error {
13 | permission := rc.RequiredPermission
14 | // 如果需要的权限信息不为空,并且不需要token,则不返回错误,继续后续逻辑
15 | if permission != nil && !permission.NeedToken {
16 | return nil
17 | }
18 | tokenStr := rc.Request.Request.Header.Get("X-TOKEN")
19 | // header不存在则从查询参数token中获取
20 | if tokenStr == "" {
21 | tokenStr = rc.Request.QueryParameter("token")
22 | }
23 | if tokenStr == "" {
24 | return biz.PermissionErr
25 | }
26 | j := token.NewJWT("", []byte(global.Conf.Jwt.Key), jwt.SigningMethodHS256)
27 | loginAccount, err := j.ParseToken(tokenStr)
28 | if err != nil || loginAccount == nil {
29 | return biz.PermissionErr
30 | }
31 | rc.LoginAccount = loginAccount
32 |
33 | if !permission.NeedCasbin {
34 | return nil
35 | }
36 |
37 | ca := casbin.CasbinS{ModelPath: global.Conf.Casbin.ModelPath}
38 | e := ca.Casbin()
39 | // 判断策略中是否存在
40 | success, err := e.Enforce(loginAccount.RoleKey, rc.Request.Request.URL.Path, rc.Request.Request.Method)
41 | if !success {
42 | return biz.CasbinErr
43 | }
44 |
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/pkg/transport/http_server.go:
--------------------------------------------------------------------------------
1 | package transport
2 |
3 | import (
4 | "context"
5 | "github.com/PandaXGO/PandaKit/logger"
6 | "github.com/emicklei/go-restful/v3"
7 | "net/http"
8 | "pandax/pkg/global"
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 | global.Log.Infof("HTTP Server listen: %s", s.Addr)
39 | go func() {
40 | s.srv.ListenAndServe()
41 | /*if err := s.srv.ListenAndServe(); err != nil {
42 | global.Log.Errorf("error http serve: %s", err)
43 | }*/
44 | }()
45 | return nil
46 | }
47 |
48 | func (s *HttpServer) Stop(ctx context.Context) error {
49 | return s.srv.Shutdown(ctx)
50 | }
51 |
52 | type httpLog struct{}
53 |
54 | func (t *httpLog) Print(v ...any) {
55 | logger.Log.Debug(v...)
56 | }
57 |
58 | func (t *httpLog) Printf(format string, v ...any) {
59 | logger.Log.Debugf(format, v...)
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/rule_engine/instance.go:
--------------------------------------------------------------------------------
1 | package rule_engine
2 |
3 | import (
4 | "context"
5 | "github.com/sirupsen/logrus"
6 | "pandax/pkg/rule_engine/manifest"
7 | "pandax/pkg/rule_engine/message"
8 | "pandax/pkg/rule_engine/nodes"
9 | )
10 |
11 | type ruleChainInstance struct {
12 | firstRuleNodeId string
13 | nodes map[string]nodes.Node
14 | }
15 |
16 | func NewRuleChainInstance(data []byte) (*ruleChainInstance, []error) {
17 | errors := make([]error, 0)
18 |
19 | manifest, err := manifest.New(data)
20 | if err != nil {
21 | errors = append(errors, err)
22 | logrus.WithError(err).Errorf("invalidi manifest file")
23 | return nil, errors
24 | }
25 | return newInstanceWithManifest(manifest)
26 | }
27 |
28 | // newWithManifest create rule chain by user's manifest file
29 | func newInstanceWithManifest(m *manifest.Manifest) (*ruleChainInstance, []error) {
30 | errs := make([]error, 0)
31 | nodes, err := nodes.GetNodes(m)
32 | if err != nil {
33 | errs = append(errs, err)
34 | return nil, errs
35 | }
36 | r := &ruleChainInstance{
37 | firstRuleNodeId: m.FirstRuleNodeId,
38 | nodes: nodes,
39 | }
40 | return r, errs
41 | }
42 |
43 | // StartRuleChain TODO 是否需要添加context
44 | func (c *ruleChainInstance) StartRuleChain(context context.Context, message *message.Message) error {
45 | if node, found := c.nodes[c.firstRuleNodeId]; found {
46 | node.Handle(message)
47 | }
48 | return nil
49 | }
50 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/filter_message_type_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type messageTypeFilterNode struct {
9 | bareNode
10 | MessageTypes []string `json:"messageTypes" yaml:"messageTypes"`
11 | }
12 |
13 | type messageTypeFilterNodeFactory struct{}
14 |
15 | func (f messageTypeFilterNodeFactory) Name() string { return "MessageTypeNode" }
16 | func (f messageTypeFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER }
17 | func (f messageTypeFilterNodeFactory) Labels() []string { return []string{"True", "False"} }
18 |
19 | func (f messageTypeFilterNodeFactory) Create(id string, meta Metadata) (Node, error) {
20 | node := &messageTypeFilterNode{
21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
22 | MessageTypes: []string{},
23 | }
24 | return decodePath(meta, node)
25 | }
26 |
27 | func (n *messageTypeFilterNode) Handle(msg *message.Message) error {
28 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
29 |
30 | trueLabelNode := n.GetLinkedNode("True")
31 | falseLabelNode := n.GetLinkedNode("False")
32 | messageType := msg.MsgType
33 |
34 | for _, filterType := range n.MessageTypes {
35 | if filterType == messageType && trueLabelNode != nil {
36 | return trueLabelNode.Handle(msg)
37 | }
38 | }
39 | if falseLabelNode != nil {
40 | return falseLabelNode.Handle(msg)
41 | }
42 | return nil
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/filter_script_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | const ScriptFilterNodeName = "ScriptFilterNode"
9 |
10 | type scriptFilterNode struct {
11 | bareNode
12 | Script string `json:"script" yaml:"script"`
13 | }
14 |
15 | type scriptFilterNodeFactory struct{}
16 |
17 | func (f scriptFilterNodeFactory) Name() string { return ScriptFilterNodeName }
18 | func (f scriptFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER }
19 | func (f scriptFilterNodeFactory) Labels() []string { return []string{"True", "False"} }
20 | func (f scriptFilterNodeFactory) Create(id string, meta Metadata) (Node, error) {
21 | node := &scriptFilterNode{
22 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
23 | }
24 | return decodePath(meta, node)
25 | }
26 |
27 | func (n *scriptFilterNode) Handle(msg *message.Message) error {
28 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
29 |
30 | trueLabelNode := n.GetLinkedNode("True")
31 | falseLabelNode := n.GetLinkedNode("False")
32 | scriptEngine := NewScriptEngine(*msg, "Filter", n.Script)
33 | isTrue, error := scriptEngine.ScriptOnFilter()
34 | if isTrue == true && error == nil && trueLabelNode != nil {
35 | return trueLabelNode.Handle(msg)
36 | } else {
37 | if falseLabelNode != nil {
38 | return falseLabelNode.Handle(msg)
39 | }
40 | }
41 |
42 | return nil
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/filter_message_type_switch_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type messageTypeSwitchNode struct {
9 | bareNode
10 | }
11 | type messageTypeSwitchNodeFactory struct{}
12 |
13 | func (f messageTypeSwitchNodeFactory) Name() string { return "MessageTypeSwitchNode" }
14 | func (f messageTypeSwitchNodeFactory) Category() string { return NODE_CATEGORY_FILTER }
15 | func (f messageTypeSwitchNodeFactory) Labels() []string {
16 | return []string{
17 | message.RowMes,
18 | message.AttributesMes,
19 | message.TelemetryMes,
20 | message.RpcRequestMes,
21 | message.AlarmMes,
22 | message.UpEventMes,
23 | message.ConnectMes,
24 | message.ConnectMes,
25 | message.DisConnectMes,
26 | }
27 | }
28 | func (f messageTypeSwitchNodeFactory) Create(id string, meta Metadata) (Node, error) {
29 | node := &messageTypeSwitchNode{
30 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
31 | }
32 | return decodePath(meta, node)
33 | }
34 |
35 | func (n *messageTypeSwitchNode) Handle(msg *message.Message) error {
36 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
37 | msg.Metadata = map[string]interface{}{"AA": "BB", "deviceName": "fff"}
38 | nodes := n.GetLinkedNodes()
39 | messageType := msg.MsgType
40 | for label, node := range nodes {
41 | if messageType == label {
42 | return node.Handle(msg)
43 | }
44 | }
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/transform_script_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type transformScriptNode struct {
9 | bareNode
10 | Script string `json:"script" yaml:"script"`
11 | }
12 |
13 | type transformScriptNodeFactory struct{}
14 |
15 | func (f transformScriptNodeFactory) Name() string { return "ScriptKeyNode" }
16 | func (f transformScriptNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM }
17 | func (f transformScriptNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
18 | func (f transformScriptNodeFactory) Create(id string, meta Metadata) (Node, error) {
19 | node := &transformScriptNode{
20 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
21 | }
22 | return decodePath(meta, node)
23 | }
24 |
25 | func (n *transformScriptNode) Handle(msg *message.Message) error {
26 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
27 |
28 | successLabelNode := n.GetLinkedNode("Success")
29 | failureLabelNode := n.GetLinkedNode("Failure")
30 |
31 | scriptEngine := NewScriptEngine(*msg, "Transform", n.Script)
32 | newMessage, err := scriptEngine.ScriptOnMessage()
33 | if err != nil {
34 | if failureLabelNode != nil {
35 | return failureLabelNode.Handle(msg)
36 | } else {
37 | return err
38 | }
39 | }
40 | if successLabelNode != nil {
41 | return successLabelNode.Handle(newMessage)
42 | }
43 | return nil
44 | }
45 |
--------------------------------------------------------------------------------
/apps/log/api/log_oper.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 | "log"
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 := l.LogOperApp.FindListPage(pageNum, pageSize, entity.LogOper{BusinessType: businessType, OperName: operName, Title: title})
23 | rc.ResData = model.ResultPage{
24 | Total: total,
25 | PageNum: int64(pageNum),
26 | PageSize: int64(pageNum),
27 | Data: list,
28 | }
29 | }
30 |
31 | func (l *LogOperApi) GetOperLog(rc *restfulx.ReqCtx) {
32 | operId := restfulx.PathParamInt(rc, "operId")
33 | rc.ResData = l.LogOperApp.FindOne(int64(operId))
34 | }
35 |
36 | func (l *LogOperApi) DeleteOperLog(rc *restfulx.ReqCtx) {
37 | operIds := restfulx.PathParam(rc, "operId")
38 | group := utils.IdsStrToIdsIntGroup(operIds)
39 | log.Println("group", group)
40 | l.LogOperApp.Delete(group)
41 | }
42 |
43 | func (l *LogOperApi) DeleteAll(rc *restfulx.ReqCtx) {
44 | l.LogOperApp.DeleteAll()
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/filter_device_type_switch_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | // 检查关联关系
9 | // 该消息来自与哪个实体或到那个实体
10 | type deviceTypeSwitchNode struct {
11 | bareNode
12 | }
13 |
14 | type deviceTypeSwitchNodeFactory struct{}
15 |
16 | func (f deviceTypeSwitchNodeFactory) Name() string { return "DeviceTypeSwitchNode" }
17 | func (f deviceTypeSwitchNodeFactory) Category() string { return NODE_CATEGORY_FILTER }
18 | func (f deviceTypeSwitchNodeFactory) Labels() []string {
19 | return []string{message.DEVICE, message.GATEWAY}
20 | }
21 | func (f deviceTypeSwitchNodeFactory) Create(id string, meta Metadata) (Node, error) {
22 | node := &deviceTypeSwitchNode{
23 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
24 | }
25 | return decodePath(meta, node)
26 | }
27 |
28 | func (n *deviceTypeSwitchNode) Handle(msg *message.Message) error {
29 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
30 |
31 | deviceLabelNode := n.GetLinkedNode(message.DEVICE)
32 | gatewayLabelNode := n.GetLinkedNode(message.GATEWAY)
33 |
34 | if msg.Metadata.GetValue("deviceType").(string) == message.DEVICE {
35 | if deviceLabelNode != nil {
36 | return deviceLabelNode.Handle(msg)
37 | }
38 | }
39 | if msg.Metadata.GetValue("deviceType").(string) == message.GATEWAY {
40 | if gatewayLabelNode != nil {
41 | return gatewayLabelNode.Handle(msg)
42 | }
43 | }
44 | return nil
45 | }
46 |
--------------------------------------------------------------------------------
/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/action_rpc_request_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "pandax/pkg/global"
5 | "pandax/pkg/mqtt"
6 | "pandax/pkg/rule_engine/message"
7 | )
8 |
9 | type rpcRequestNode struct {
10 | bareNode
11 | Timeout int `json:"timeout"`
12 | Payload mqtt.RpcPayload `json:"payload"`
13 | }
14 |
15 | type rpcRequestNodeFactory struct{}
16 |
17 | func (f rpcRequestNodeFactory) Name() string { return "RpcRequestNode" }
18 | func (f rpcRequestNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
19 | func (f rpcRequestNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
20 | func (f rpcRequestNodeFactory) Create(id string, meta Metadata) (Node, error) {
21 | node := &rpcRequestNode{
22 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
23 | }
24 | return decodePath(meta, node)
25 | }
26 |
27 | func (n *rpcRequestNode) Handle(msg *message.Message) error {
28 | successLableNode := n.GetLinkedNode("Success")
29 | failureLableNode := n.GetLinkedNode("Failure")
30 |
31 | var rpc = &mqtt.RpcRequest{Client: global.MqttClient, Mode: "double", Timeout: n.Timeout}
32 | rpc.GetRequestId()
33 | respPayload, err := rpc.RequestCmd(n.Payload)
34 | if err != nil {
35 | if failureLableNode != nil {
36 | return failureLableNode.Handle(msg)
37 | } else {
38 | return err
39 | }
40 | }
41 | msgM := msg.Msg
42 | msgM["payload"] = respPayload
43 | msg.Msg = msgM
44 | if successLableNode != nil {
45 | return successLableNode.Handle(msg)
46 | }
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/apps/device/api/device_alarm.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | // ==========================================================================
4 | import (
5 | "github.com/PandaXGO/PandaKit/model"
6 | "github.com/PandaXGO/PandaKit/restfulx"
7 | "strings"
8 |
9 | "pandax/apps/device/entity"
10 | "pandax/apps/device/services"
11 | )
12 |
13 | type DeviceAlarmApi struct {
14 | DeviceAlarmApp services.DeviceAlarmModel
15 | }
16 |
17 | // GetDeviceAlarmList 告警列表数据
18 | func (p *DeviceAlarmApi) GetDeviceAlarmList(rc *restfulx.ReqCtx) {
19 | data := entity.DeviceAlarm{}
20 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
21 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
22 | data.DeviceId = restfulx.QueryParam(rc, "deviceId")
23 | data.Type = restfulx.QueryParam(rc, "type")
24 | data.Level = restfulx.QueryParam(rc, "level")
25 | data.State = restfulx.QueryParam(rc, "state")
26 |
27 | list, total := p.DeviceAlarmApp.FindListPage(pageNum, pageSize, data)
28 |
29 | rc.ResData = model.ResultPage{
30 | Total: total,
31 | PageNum: int64(pageNum),
32 | PageSize: int64(pageNum),
33 | Data: list,
34 | }
35 | }
36 |
37 | // UpdateDeviceAlarm 修改告警
38 | func (p *DeviceAlarmApi) UpdateDeviceAlarm(rc *restfulx.ReqCtx) {
39 | var data entity.DeviceAlarm
40 | restfulx.BindJsonAndValid(rc, &data)
41 | p.DeviceAlarmApp.Update(data)
42 | }
43 |
44 | // DeleteDeviceAlarm 删除告警
45 | func (p *DeviceAlarmApi) DeleteDeviceAlarm(rc *restfulx.ReqCtx) {
46 | id := restfulx.PathParam(rc, "id")
47 | ids := strings.Split(id, ",")
48 | p.DeviceAlarmApp.Delete(ids)
49 | }
50 |
--------------------------------------------------------------------------------
/apps/log/api/log_login.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/log/entity"
8 | "pandax/apps/log/services"
9 | )
10 |
11 | type LogLoginApi struct {
12 | LogLoginApp services.LogLoginModel
13 | }
14 |
15 | func (l *LogLoginApi) GetLoginLogList(rc *restfulx.ReqCtx) {
16 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
17 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
18 | loginLocation := restfulx.QueryParam(rc, "loginLocation")
19 | username := restfulx.QueryParam(rc, "username")
20 | list, total := l.LogLoginApp.FindListPage(pageNum, pageSize, entity.LogLogin{LoginLocation: loginLocation, Username: username})
21 | rc.ResData = model.ResultPage{
22 | Total: total,
23 | PageNum: int64(pageNum),
24 | PageSize: int64(pageNum),
25 | Data: list,
26 | }
27 | }
28 |
29 | func (l *LogLoginApi) GetLoginLog(rc *restfulx.ReqCtx) {
30 | infoId := restfulx.PathParamInt(rc, "infoId")
31 | rc.ResData = l.LogLoginApp.FindOne(int64(infoId))
32 | }
33 |
34 | func (l *LogLoginApi) UpdateLoginLog(rc *restfulx.ReqCtx) {
35 | var log entity.LogLogin
36 | restfulx.BindQuery(rc, &log)
37 | l.LogLoginApp.Update(log)
38 | }
39 |
40 | func (l *LogLoginApi) DeleteLoginLog(rc *restfulx.ReqCtx) {
41 | infoIds := restfulx.PathParam(rc, "infoId")
42 | group := utils.IdsStrToIdsIntGroup(infoIds)
43 | l.LogLoginApp.Delete(group)
44 | }
45 |
46 | func (l *LogLoginApi) DeleteAll(rc *restfulx.ReqCtx) {
47 | l.LogLoginApp.DeleteAll()
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/filter_switch_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type switchFilterNode struct {
9 | bareNode
10 | Script string `json:"script" yaml:"script"`
11 | }
12 |
13 | type switchFilterNodeFactory struct{}
14 |
15 | func (f switchFilterNodeFactory) Name() string { return "SwitchNode" }
16 | func (f switchFilterNodeFactory) Category() string { return NODE_CATEGORY_FILTER }
17 | func (f switchFilterNodeFactory) Labels() []string {
18 | return []string{
19 | "True", "False",
20 | message.RowMes,
21 | message.AttributesMes,
22 | message.TelemetryMes,
23 | message.RpcRequestMes,
24 | message.AlarmMes,
25 | message.UpEventMes,
26 | message.ConnectMes,
27 | message.DisConnectMes,
28 | }
29 | }
30 | func (f switchFilterNodeFactory) Create(id string, meta Metadata) (Node, error) {
31 | node := &switchFilterNode{
32 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
33 | }
34 | return decodePath(meta, node)
35 | }
36 |
37 | func (n *switchFilterNode) Handle(msg *message.Message) error {
38 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
39 |
40 | scriptEngine := NewScriptEngine(*msg, "Switch", n.Script)
41 | SwitchResults, err := scriptEngine.ScriptOnSwitch()
42 | if err != nil {
43 | return err
44 | }
45 | nodes := n.GetLinkedNodes()
46 | for label, node := range nodes {
47 | for _, switchresult := range SwitchResults {
48 | if label == switchresult {
49 | go node.Handle(msg)
50 | }
51 | }
52 | }
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/tdengine/tdengine_log.go:
--------------------------------------------------------------------------------
1 | package tdengine
2 |
3 | import "time"
4 |
5 | // CreateLogStable 添加LOG超级表
6 | func (s *TdEngine) CreateLogStable() (err error) {
7 | var name string
8 | err = s.db.QueryRow("SELECT stable_name FROM information_schema.ins_stables WHERE stable_name = 'device_log' LIMIT 1").Scan(&name)
9 | if name != "" {
10 | return
11 | }
12 | sql := "CREATE STABLE device_log (ts TIMESTAMP, type VARCHAR(20), content VARCHAR(1000)) TAGS (device VARCHAR(255))"
13 | _, err = s.db.Exec(sql)
14 | return
15 | }
16 |
17 | // InsertLog 写入数据
18 | func (s *TdEngine) InsertLog(log *TdLog) (err error) {
19 | sql := "INSERT INTO ? USING device_log TAGS ('?') VALUES ('?', '?', '?')"
20 | _, err = s.db.Exec(sql, "log_"+log.Device, log.Device, log.Ts, log.Type, log.Content)
21 |
22 | return
23 | }
24 |
25 | // ClearLog 清理过期数据
26 | func (s *TdEngine) ClearLog() (err error) {
27 | ts := time.Now().Add(-7 * 24 * time.Hour).Format("2006-01-02")
28 |
29 | sql := "DELETE FROM device_log WHERE ts < '" + ts + "'"
30 | _, err = s.db.Exec(sql)
31 |
32 | return
33 | }
34 |
35 | // GetAllLog 超级表查询,多条数据
36 | func (s *TdEngine) GetAllLog(sql string, args ...any) (list []TdLog, err error) {
37 | rows, err := s.db.Query(sql, args...)
38 | if err != nil {
39 | return nil, err
40 | }
41 | defer rows.Close()
42 |
43 | for rows.Next() {
44 | var log TdLog
45 |
46 | err = rows.Scan(&log.Ts, &log.Type, &log.Content, &log.Device)
47 | if err != nil {
48 | return nil, err
49 | }
50 | log.Ts = s.Time(log.Ts)
51 |
52 | list = append(list, log)
53 | }
54 |
55 | return
56 | }
57 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/action_rpc_respond_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "pandax/pkg/global"
5 | "pandax/pkg/mqtt"
6 | "pandax/pkg/rule_engine/message"
7 | )
8 |
9 | type rpcRespondNode struct {
10 | bareNode
11 | RequestId int `json:"requestId"`
12 | }
13 |
14 | type rpcRespondFactory struct{}
15 |
16 | func (f rpcRespondFactory) Name() string { return "RpcRespondNode" }
17 | func (f rpcRespondFactory) Category() string { return NODE_CATEGORY_ACTION }
18 | func (f rpcRespondFactory) Labels() []string { return []string{"Success", "Failure"} }
19 | func (f rpcRespondFactory) Create(id string, meta Metadata) (Node, error) {
20 | node := &rpcRespondNode{
21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
22 | }
23 | return decodePath(meta, node)
24 | }
25 |
26 | func (n *rpcRespondNode) Handle(msg *message.Message) error {
27 | successLableNode := n.GetLinkedNode("Success")
28 | failureLableNode := n.GetLinkedNode("Failure")
29 | RequestId := n.RequestId
30 | if RequestId == 0 {
31 | RequestId = int(msg.Metadata.GetValue("requestId").(float64))
32 | }
33 | var datas = mqtt.RpcPayload{
34 | Method: msg.Msg.GetValue("method").(string),
35 | Params: msg.Msg.GetValue("params"),
36 | }
37 | rpc := &mqtt.RpcRequest{Client: global.MqttClient, RequestId: RequestId}
38 | err := rpc.RespondTpc(datas)
39 | if err != nil {
40 | if failureLableNode != nil {
41 | return failureLableNode.Handle(msg)
42 | } else {
43 | return err
44 | }
45 | }
46 | if successLableNode != nil {
47 | return successLableNode.Handle(msg)
48 | }
49 | return nil
50 | }
51 |
--------------------------------------------------------------------------------
/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 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
7 | "github.com/emicklei/go-restful/v3"
8 | "pandax/apps/rule/api"
9 | "pandax/apps/rule/services"
10 | )
11 |
12 | func InitRuleChainMsgLogRouter(container *restful.Container) {
13 | s := &api.RuleChainMsgLogApi{
14 | RuleChainMsgLogApp: services.RuleChainMsgLogModelDao,
15 | }
16 |
17 | ws := new(restful.WebService)
18 | ws.Path("/rule/chain/log").Produces(restful.MIME_JSON)
19 | tags := []string{"RuleChainMsgLog"}
20 |
21 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
22 | restfulx.NewReqCtx(request, response).WithLog("获取规则引擎分页列表").Handle(s.GetRuleChainMsgLogList)
23 | }).
24 | Doc("获取规则引擎分页列表").
25 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")).
26 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")).
27 | Param(ws.QueryParameter("ruleName", "规则名").Required(false).DataType("string")).
28 | Metadata(restfulspec.KeyOpenAPITags, tags).
29 | Writes(model.ResultPage{}).
30 | Returns(200, "OK", model.ResultPage{}))
31 |
32 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) {
33 | restfulx.NewReqCtx(request, response).WithLog("删除规则引擎信息").Handle(s.DeleteRuleChainMsgLog)
34 | }).
35 | Doc("删除规则链日志信息").
36 | Metadata(restfulspec.KeyOpenAPITags, tags).
37 | Param(ws.PathParameter("id", "多id 1,2,3").DataType("string")))
38 |
39 | container.Add(ws)
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/metadata.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 | // Metadata 前端 参数 Properties
14 | type Metadata interface {
15 | Keys() []string
16 | With(key string, val interface{}) Metadata
17 | Value(key string) (interface{}, error)
18 | DecodePath(rawVal interface{}) error
19 | }
20 |
21 | type nodeMetadata struct {
22 | keypairs map[string]interface{}
23 | }
24 |
25 | func NewMetadata() Metadata {
26 | return &nodeMetadata{
27 | keypairs: make(map[string]interface{}),
28 | }
29 | }
30 |
31 | func NewMetadataWithString(vals string) Metadata {
32 | return &nodeMetadata{}
33 | }
34 |
35 | func NewMetadataWithValues(vals map[string]interface{}) Metadata {
36 | return &nodeMetadata{
37 | keypairs: vals,
38 | }
39 | }
40 |
41 | func (c *nodeMetadata) 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 *nodeMetadata) 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 *nodeMetadata) With(key string, val interface{}) Metadata {
57 | c.keypairs[key] = val
58 | return c
59 | }
60 |
61 | func (c *nodeMetadata) DecodePath(rawVal interface{}) error {
62 | //return utils.Map2Struct(c.keypairs, rawVal)
63 | return mapstructure.Decode(c.keypairs, rawVal)
64 | }
65 |
--------------------------------------------------------------------------------
/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 Metadata) (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 | data := services.RuleChainModelDao.FindOne(n.RuleId)
33 | if data == 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/action_save_attributes_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/global"
6 | "pandax/pkg/rule_engine/message"
7 | )
8 |
9 | type saveAttributesNode struct {
10 | bareNode
11 | }
12 |
13 | type saveAttributesNodeFactory struct{}
14 |
15 | func (f saveAttributesNodeFactory) Name() string { return "SaveAttributesNode" }
16 | func (f saveAttributesNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
17 | func (f saveAttributesNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
18 | func (f saveAttributesNodeFactory) Create(id string, meta Metadata) (Node, error) {
19 | node := &saveAttributesNode{
20 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
21 | }
22 | return decodePath(meta, node)
23 | }
24 |
25 | func (n *saveAttributesNode) Handle(msg *message.Message) error {
26 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
27 | successLabelNode := n.GetLinkedNode("Success")
28 | failureLabelNode := n.GetLinkedNode("Failure")
29 | if msg.MsgType != message.AttributesMes {
30 | if failureLabelNode != nil {
31 | return failureLabelNode.Handle(msg)
32 | } else {
33 | return nil
34 | }
35 | }
36 | //deviceId := msg.GetMetadata().GetValues()["deviceId"].(string)
37 | deviceName := msg.Metadata["deviceName"].(string)
38 | err := global.TdDb.InsertDevice(deviceName+"_attributes", msg.Msg)
39 | if err != nil {
40 | if failureLabelNode != nil {
41 | return failureLabelNode.Handle(msg)
42 | }
43 | }
44 | if successLabelNode != nil {
45 | return successLabelNode.Handle(msg)
46 | }
47 | return nil
48 | }
49 |
--------------------------------------------------------------------------------
/apps/job/services/log_job.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/biz"
5 | "pandax/apps/job/entity"
6 | "pandax/pkg/global"
7 | )
8 |
9 | type (
10 | JobLogModel interface {
11 | Insert(data entity.JobLog) *entity.JobLog
12 | FindListPage(page, pageSize int, data entity.JobLog) (*[]entity.JobLog, int64)
13 | Delete(infoId []int64)
14 | DeleteAll()
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 {
27 | global.Db.Table(m.table).Create(&data)
28 | return &data
29 | }
30 |
31 | func (m *JobLogModelImpl) FindListPage(page, pageSize int, data entity.JobLog) (*[]entity.JobLog, int64) {
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 | err := db.Count(&total).Error
44 | err = db.Order("create_time desc").Limit(pageSize).Offset(offset).Find(&list).Error
45 |
46 | biz.ErrIsNil(err, "查询登录分页日志信息失败")
47 | return &list, total
48 | }
49 |
50 | func (m *JobLogModelImpl) Delete(logIds []int64) {
51 | err := global.Db.Table(m.table).Delete(&entity.JobLog{}, "id in (?)", logIds).Error
52 | biz.ErrIsNil(err, "删除登录日志信息失败")
53 | return
54 | }
55 |
56 | func (m *JobLogModelImpl) DeleteAll() {
57 | global.Db.Exec("DELETE FROM log_jobs")
58 | }
59 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/external_send_sms_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type externalSendSmsNode struct {
9 | bareNode
10 | SecretId string `json:"secretId" yaml:"secretId"`
11 | SecretKey string `json:"secretKey" yaml:"secretKey"`
12 | SdkAppId string `json:"sdkAppId" yaml:"sdkAppId"` //应用Id(腾讯) 或 签名名称(阿里)
13 | PhoneNumber string `json:"phoneNumber" yaml:"phoneNumber"` //发送到手机号
14 | TemplateId string `json:"templateId" yaml:"templateId"` //短信模板Id
15 | TemplateParam map[string]interface{} `json:"templateParam" yaml:"templateParam"` //模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,若无模板参数,则设置为空*/
16 | }
17 |
18 | type externalSendSmsNodeFactory struct{}
19 |
20 | func (f externalSendSmsNodeFactory) Name() string { return "SendSmsNode" }
21 | func (f externalSendSmsNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
22 | func (f externalSendSmsNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
23 | func (f externalSendSmsNodeFactory) Create(id string, meta Metadata) (Node, error) {
24 | node := &externalSendSmsNode{
25 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
26 | }
27 | return decodePath(meta, node)
28 | }
29 |
30 | func (n *externalSendSmsNode) Handle(msg *message.Message) error {
31 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
32 |
33 | successLabelNode := n.GetLinkedNode("Success")
34 | //failureLabelNode := n.GetLinkedNode("Failure")
35 |
36 | return successLabelNode.Handle(msg)
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/action_save_timeseries_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "log"
6 | "pandax/pkg/global"
7 | "pandax/pkg/rule_engine/message"
8 | )
9 |
10 | type saveTimeSeriesNode struct {
11 | bareNode
12 | }
13 |
14 | type saveTimeSeriesNodeFactory struct{}
15 |
16 | func (f saveTimeSeriesNodeFactory) Name() string { return "SaveTimeSeriesNode" }
17 | func (f saveTimeSeriesNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
18 | func (f saveTimeSeriesNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
19 | func (f saveTimeSeriesNodeFactory) Create(id string, meta Metadata) (Node, error) {
20 | node := &saveTimeSeriesNode{
21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
22 | }
23 | return decodePath(meta, node)
24 | }
25 |
26 | func (n *saveTimeSeriesNode) Handle(msg *message.Message) error {
27 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
28 | successLabelNode := n.GetLinkedNode("Success")
29 | failureLabelNode := n.GetLinkedNode("Failure")
30 | if msg.MsgType != message.TelemetryMes {
31 | if failureLabelNode != nil {
32 | return failureLabelNode.Handle(msg)
33 | } else {
34 | return nil
35 | }
36 | }
37 | //deviceId := msg.GetMetadata().GetValues()["deviceId"].(string)
38 | deviceName := msg.Metadata["deviceName"].(string)
39 | log.Println(msg.Msg)
40 | log.Println(msg.Metadata)
41 | err := global.TdDb.InsertDevice(deviceName+"_telemetry", msg.Msg)
42 | log.Println(err)
43 | if err != nil {
44 | if failureLabelNode != nil {
45 | return failureLabelNode.Handle(msg)
46 | }
47 | }
48 | if successLabelNode != nil {
49 | return successLabelNode.Handle(msg)
50 | }
51 | return nil
52 | }
53 |
--------------------------------------------------------------------------------
/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 | Oss *Oss `yaml:"oss"`
44 | Taos *Taos `yaml:"taos"`
45 | Mqtt *Mqtt `yaml:"mqtt"`
46 | Casbin *Casbin `yaml:"casbin"`
47 | Gen *Gen `yaml:"gen"`
48 | Log *Log `yaml:"log"`
49 | }
50 |
51 | // 配置文件内容校验
52 | func (c *Config) Valid() {
53 | biz.IsTrue(c.Jwt != nil, "配置文件的[jwt]信息不能为空")
54 | c.Jwt.Valid()
55 | }
56 |
57 | // 获取执行可执行文件时,指定的启动参数
58 | func getStartConfig() *CmdConfigParam {
59 | configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
60 | flag.Parse()
61 | // 获取配置文件绝对路径
62 | path, _ := filepath.Abs(*configFilePath)
63 | sc := &CmdConfigParam{ConfigFilePath: path}
64 | return sc
65 | }
66 |
--------------------------------------------------------------------------------
/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/global"
7 | "pandax/pkg/rule_engine/message"
8 | )
9 |
10 | type logNode struct {
11 | bareNode
12 | Script string `json:"script"`
13 | }
14 |
15 | type logNodeFactory struct{}
16 |
17 | func (f logNodeFactory) Name() string { return "LogNode" }
18 | func (f logNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
19 | func (f logNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
20 | func (f logNodeFactory) Create(id string, meta Metadata) (Node, error) {
21 | node := &logNode{
22 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
23 | }
24 | return decodePath(meta, node)
25 | }
26 |
27 | func (n *logNode) Handle(msg *message.Message) error {
28 | successLableNode := n.GetLinkedNode("Success")
29 | failureLableNode := n.GetLinkedNode("Failure")
30 |
31 | scriptEngine := NewScriptEngine(*msg, "ToString", n.Script)
32 | logMessage, err := scriptEngine.ScriptToString()
33 | if err != nil {
34 | if failureLableNode != nil {
35 | return failureLableNode.Handle(msg)
36 | } else {
37 | return err
38 | }
39 | }
40 | services.RuleChainMsgLogModelDao.Insert(entity.RuleChainMsgLog{
41 | MessageId: msg.Id,
42 | MsgType: msg.MsgType,
43 | DeviceName: msg.Metadata["deviceName"].(string),
44 | Ts: msg.Ts,
45 | Content: logMessage,
46 | })
47 | global.Log.Info(logMessage)
48 | if err != nil {
49 | if failureLableNode != nil {
50 | return failureLableNode.Handle(msg)
51 | } else {
52 | return err
53 | }
54 | }
55 | if successLableNode != nil {
56 | return successLableNode.Handle(msg)
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/tool/conv.go:
--------------------------------------------------------------------------------
1 | package tool
2 |
3 | import (
4 | "encoding/json"
5 | "strings"
6 | )
7 |
8 | // SnakeString snake string, XxYy to xx_yy , XxYY to xx_y_y
9 | func SnakeString(s string) string {
10 | data := make([]byte, 0, len(s)*2)
11 | j := false
12 | num := len(s)
13 | for i := 0; i < num; i++ {
14 | d := s[i]
15 | if i > 0 && d >= 'A' && d <= 'Z' && j {
16 | data = append(data, '_')
17 | }
18 | if d != '_' {
19 | j = true
20 | }
21 | data = append(data, d)
22 | }
23 | return strings.ToLower(string(data))
24 | }
25 |
26 | // CamelString camel string, xx_yy to XxYy
27 | func CamelString(s string) string {
28 | data := make([]byte, 0, len(s))
29 | flag, num := true, len(s)-1
30 | for i := 0; i <= num; i++ {
31 | d := s[i]
32 | if d == '_' {
33 | flag = true
34 | continue
35 | } else if flag {
36 | if d >= 'a' && d <= 'z' {
37 | d = d - 32
38 | }
39 | flag = false
40 | }
41 | data = append(data, d)
42 | }
43 | return string(data)
44 | }
45 |
46 | func FirstLowCamelString(s string) string {
47 | data := make([]byte, 0, len(s))
48 | flag, num := true, len(s)-1
49 | for i := 0; i <= num; i++ {
50 | d := s[i]
51 | if d == '_' {
52 | flag = true
53 | continue
54 | } else if flag {
55 | if d >= 'a' && d <= 'z' {
56 | d = d - 32
57 | }
58 | flag = false
59 | }
60 | data = append(data, d)
61 | }
62 | if len(data) > 0 && data[0] >= 65 && data[0] <= 90 {
63 | data[0] = data[0] + 32
64 | }
65 | return string(data)
66 | }
67 |
68 | func MapToStruct(m map[string]interface{}, s interface{}) error {
69 | data, err := json.Marshal(m)
70 | if err != nil {
71 | return err
72 | }
73 |
74 | err = json.Unmarshal(data, s)
75 | if err != nil {
76 | return err
77 | }
78 |
79 | return nil
80 | }
81 |
--------------------------------------------------------------------------------
/pkg/tool/device.go:
--------------------------------------------------------------------------------
1 | package tool
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/json"
7 | "github.com/google/uuid"
8 | "pandax/pkg/global"
9 | "strconv"
10 | "strings"
11 | )
12 |
13 | type DeviceAuth struct {
14 | User string `json:"user"`
15 | DeviceId string `json:"device_id"`
16 | DeviceType string `json:"device_type"`
17 | ProductId string `json:"product_id"`
18 | RuleChainId string `json:"rule_chain_id"`
19 | Name string `json:"name"`
20 | Token string `json:"token"`
21 | CreatedAt int64 `json:"created_at"`
22 | ExpiredAt int64 `json:"expired_at"`
23 | }
24 |
25 | func (entity *DeviceAuth) CreateDeviceToken() (err error) {
26 |
27 | return nil
28 | }
29 |
30 | func (entity *DeviceAuth) GetDeviceToken(key string) error {
31 | if err := global.RedisDb.Get(key, entity); err != nil {
32 | return err
33 | }
34 | return nil
35 | }
36 |
37 | func (token *DeviceAuth) MD5ID() string {
38 | buf := bytes.NewBufferString(token.DeviceId)
39 | buf.WriteString(token.DeviceType)
40 | buf.WriteString(strconv.FormatInt(token.CreatedAt, 10))
41 | access := base64.URLEncoding.EncodeToString([]byte(uuid.NewMD5(uuid.Must(uuid.NewRandom()), buf.Bytes()).String()))
42 | access = strings.TrimRight(access, "=")
43 | return access
44 | }
45 | func (token *DeviceAuth) GetMarshal() string {
46 | marshal, _ := json.Marshal(*token)
47 | return string(marshal)
48 | }
49 |
50 | func (token *DeviceAuth) GetUnMarshal(data []byte) error {
51 | return json.Unmarshal(data, token)
52 | }
53 |
54 | // 序列化
55 | func (m *DeviceAuth) MarshalBinary() (data []byte, err error) {
56 | return json.Marshal(m)
57 | }
58 |
59 | // 反序列化
60 | func (m *DeviceAuth) UnmarshalBinary(data []byte) error {
61 | return json.Unmarshal(data, m)
62 | }
63 |
--------------------------------------------------------------------------------
/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/external_nats_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/nats-io/nats.go"
5 | "github.com/sirupsen/logrus"
6 | "pandax/pkg/rule_engine/message"
7 | )
8 |
9 | type externalNatsNode struct {
10 | bareNode
11 | Url string `json:"url"`
12 | Subject string `json:"subject"`
13 | Body string
14 | client *nats.Conn
15 | }
16 |
17 | type externalNatsNodeFactory struct{}
18 |
19 | func (f externalNatsNodeFactory) Name() string { return "NatsNode" }
20 | func (f externalNatsNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
21 | func (f externalNatsNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
22 | func (f externalNatsNodeFactory) Create(id string, meta Metadata) (Node, error) {
23 | node := &externalNatsNode{
24 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
25 | }
26 | _, err := decodePath(meta, node)
27 | if err != nil {
28 | return node, err
29 | }
30 | connect, err := nats.Connect(node.Url)
31 | if err != nil {
32 | return node, err
33 | }
34 | node.client = connect
35 | return node, nil
36 | }
37 |
38 | func (n *externalNatsNode) Handle(msg *message.Message) error {
39 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
40 | defer n.client.Close()
41 | successLabelNode := n.GetLinkedNode("Success")
42 | failureLabelNode := n.GetLinkedNode("Failure")
43 | template, err := ParseTemplate(n.Body, msg.GetAllMap())
44 | if err != nil {
45 | return err
46 | }
47 | err = n.client.Publish(n.Subject, []byte(template))
48 | if err != nil {
49 | if failureLabelNode != nil {
50 | return failureLabelNode.Handle(msg)
51 | } else {
52 | return err
53 | }
54 | }
55 | if successLabelNode != nil {
56 | return successLabelNode.Handle(msg)
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 DeptTreeVo struct {
12 | Depts []entity.DeptLable `json:"depts"`
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 | Dept []entity.SysDept `json:"dept"`
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 | Depts []entity.SysDept `json:"depts"`
59 | }
60 |
61 | type UserRolePost struct {
62 | Roles []entity.SysRole `json:"roles"`
63 | Posts []entity.SysPost `json:"posts"`
64 | }
65 |
--------------------------------------------------------------------------------
/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 Metadata) (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 Metadata) (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 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/transform_delete_key_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | "strings"
7 | )
8 |
9 | type transformDeleteKeyNode struct {
10 | bareNode
11 | FormType string `json:"formType" yaml:"formType"` //msg metadata
12 | Keys string `json:"keys" yaml:"keys"`
13 | }
14 | type transformDeleteKeyNodeFactory struct{}
15 |
16 | func (f transformDeleteKeyNodeFactory) Name() string { return "DeleteKeyNode" }
17 | func (f transformDeleteKeyNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM }
18 | func (f transformDeleteKeyNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
19 | func (f transformDeleteKeyNodeFactory) Create(id string, meta Metadata) (Node, error) {
20 | node := &transformDeleteKeyNode{
21 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
22 | }
23 | return decodePath(meta, node)
24 | }
25 |
26 | func (n *transformDeleteKeyNode) Handle(msg *message.Message) error {
27 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
28 |
29 | successLabelNode := n.GetLinkedNode("Success")
30 | failureLabelNode := n.GetLinkedNode("Failure")
31 | keys := strings.Split(n.Keys, ",")
32 | if n.FormType == "msg" {
33 | data := msg.Msg
34 | for _, key := range keys {
35 | if _, found := data[key]; found {
36 | delete(data, key)
37 | msg.Msg = data
38 | }
39 | }
40 | } else if n.FormType == "metadata" {
41 | data := msg.Metadata
42 | for _, key := range keys {
43 | if data.GetValue(key) != nil {
44 | delete(data, key)
45 | msg.Metadata = data
46 | }
47 | }
48 | } else {
49 | if failureLabelNode != nil {
50 | return failureLabelNode.Handle(msg)
51 | }
52 | }
53 | if successLabelNode != nil {
54 | return successLabelNode.Handle(msg)
55 | }
56 | return nil
57 | }
58 |
--------------------------------------------------------------------------------
/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 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
7 | "github.com/emicklei/go-restful/v3"
8 | "pandax/apps/job/api"
9 | "pandax/apps/job/services"
10 | )
11 |
12 | func InitJobLogRouter(container *restful.Container) {
13 | // Job日志
14 | s := &api.JobLogApi{
15 | JobLogApp: services.JobLogModelDao,
16 | }
17 |
18 | ws := new(restful.WebService)
19 | ws.Path("/job/log").Produces(restful.MIME_JSON)
20 | tags := []string{"JobLog"}
21 |
22 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
23 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志列表").Handle(s.GetJobLogList)
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("status", "status").DataType("string")).
29 | Param(ws.QueryParameter("name", "name").DataType("string")).
30 | Metadata(restfulspec.KeyOpenAPITags, tags).
31 | Writes(model.ResultPage{}).
32 | Returns(200, "OK", model.ResultPage{}))
33 |
34 | ws.Route(ws.DELETE("/{logId}").To(func(request *restful.Request, response *restful.Response) {
35 | restfulx.NewReqCtx(request, response).WithLog("删除操作日志信息").Handle(s.DeleteJobLog)
36 | }).
37 | Doc("删除操作日志信息").
38 | Metadata(restfulspec.KeyOpenAPITags, tags).
39 | Param(ws.PathParameter("logId", "多id 1,2,3").DataType("string")))
40 |
41 | ws.Route(ws.DELETE("/all").To(func(request *restful.Request, response *restful.Response) {
42 | restfulx.NewReqCtx(request, response).WithLog("清空操作日志信息").Handle(s.DeleteAll)
43 | }).
44 | Doc("清空操作日志信息").
45 | Metadata(restfulspec.KeyOpenAPITags, tags))
46 |
47 | container.Add(ws)
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/action_clear_alarm_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/sirupsen/logrus"
6 | "log"
7 | "pandax/apps/device/services"
8 | "pandax/pkg/global"
9 | "pandax/pkg/rule_engine/message"
10 | )
11 |
12 | const ClearAlarmNodeName = "ClearAlarmNode"
13 |
14 | type clearAlarmNodeFactory struct{}
15 |
16 | type clearAlarmNode struct {
17 | bareNode
18 | Script string `json:"script" yaml:"script"`
19 | AlarmType string `json:"alarmType" yaml:"alarmType"`
20 | }
21 |
22 | func (f clearAlarmNodeFactory) Name() string { return ClearAlarmNodeName }
23 | func (f clearAlarmNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
24 | func (f clearAlarmNodeFactory) Labels() []string { return []string{"Cleared", "Failure"} }
25 | func (f clearAlarmNodeFactory) Create(id string, meta Metadata) (Node, error) {
26 | node := &clearAlarmNode{
27 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
28 | }
29 | return decodePath(meta, node)
30 | }
31 |
32 | func (n *clearAlarmNode) Handle(msg *message.Message) error {
33 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
34 | cleared := n.GetLinkedNode("Cleared")
35 | failure := n.GetLinkedNode("Failure")
36 |
37 | alarm := services.DeviceAlarmModelDao.FindOneByType(msg.Metadata.GetValue("deviceId").(string), n.AlarmType, "0")
38 | if alarm.DeviceId != "" {
39 | log.Println("清除告警")
40 | alarm.State = global.CLEARED
41 | marshal, _ := json.Marshal(msg.Msg)
42 | alarm.Details = string(marshal)
43 | err := services.DeviceAlarmModelDao.Update(*alarm)
44 | if err != nil {
45 | if failure != nil {
46 | return failure.Handle(msg)
47 | }
48 | } else {
49 | if cleared != nil {
50 | return cleared.Handle(msg)
51 | }
52 | }
53 | } else {
54 | if failure != nil {
55 | return failure.Handle(msg)
56 | }
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/apps/system/router/upload.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/restfulx"
5 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
6 | "github.com/emicklei/go-restful/v3"
7 | "pandax/apps/system/api"
8 | )
9 |
10 | func InitUploadRouter(container *restful.Container) {
11 | s := &api.UploadApi{}
12 | ws := new(restful.WebService)
13 | ws.Path("/upload").Produces(restful.MIME_JSON)
14 | tags := []string{"upload"}
15 |
16 | ws.Route(ws.POST("/up").To(func(request *restful.Request, response *restful.Response) {
17 | restfulx.NewReqCtx(request, response).WithLog("上传图片").Handle(s.UploadImage)
18 | }).
19 | Doc("上传图片").
20 | Param(ws.FormParameter("imagefile", "文件")).
21 | Metadata(restfulspec.KeyOpenAPITags, tags).
22 | Returns(200, "OK", map[string]string{}))
23 |
24 | ws.Route(ws.POST("/up/oss").To(func(request *restful.Request, response *restful.Response) {
25 | restfulx.NewReqCtx(request, response).WithLog("上传图片").Handle(s.UplaodToOss)
26 | }).
27 | Doc("上传图片到oss").
28 | Param(ws.FormParameter("imagefile", "文件")).
29 | Metadata(restfulspec.KeyOpenAPITags, tags).
30 | Returns(200, "OK", map[string]string{}))
31 |
32 | ws.Route(ws.GET("/get/{subpath}").To(func(request *restful.Request, response *restful.Response) {
33 | restfulx.NewReqCtx(request, response).WithNeedToken(false).WithNeedCasbin(false).WithLog("获取图片").Handle(s.GetImage)
34 | }).
35 | Doc("获取图片").
36 | Param(ws.PathParameter("subpath", "文件名")).
37 | Metadata(restfulspec.KeyOpenAPITags, tags))
38 |
39 | ws.Route(ws.DELETE("/delete").To(func(request *restful.Request, response *restful.Response) {
40 | restfulx.NewReqCtx(request, response).WithLog("删除图片").Handle(s.DeleteImage)
41 | }).
42 | Doc("删除图片").
43 | Metadata(restfulspec.KeyOpenAPITags, tags).
44 | Param(ws.QueryParameter("fileName", "文件名称").DataType("string")))
45 |
46 | container.Add(ws)
47 | }
48 |
--------------------------------------------------------------------------------
/apps/develop/router/gen.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/restfulx"
5 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
6 | "github.com/emicklei/go-restful/v3"
7 | "pandax/apps/develop/api"
8 | "pandax/apps/develop/services"
9 | )
10 |
11 | func InitGenRouter(container *restful.Container) {
12 |
13 | // 登录日志
14 | s := &api.GenApi{
15 | GenTableApp: services.DevGenTableModelDao,
16 | }
17 |
18 | ws := new(restful.WebService)
19 | ws.Path("/develop/code/gen").Produces(restful.MIME_JSON)
20 | tags := []string{"codegen"}
21 |
22 | ws.Route(ws.GET("/preview/{tableId}").To(func(request *restful.Request, response *restful.Response) {
23 | restfulx.NewReqCtx(request, response).WithLog("获取生成代码视图").Handle(s.Preview)
24 | }).
25 | Doc("获取生成代码视图").
26 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")).
27 | Metadata(restfulspec.KeyOpenAPITags, tags).
28 | Returns(200, "OK", map[string]any{}).
29 | Returns(404, "Not Found", nil))
30 |
31 | ws.Route(ws.GET("/code/{tableId}").To(func(request *restful.Request, response *restful.Response) {
32 | restfulx.NewReqCtx(request, response).WithLog("生成代码").Handle(s.GenCode)
33 | }).
34 | Doc("生成代码").
35 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")).
36 | Metadata(restfulspec.KeyOpenAPITags, tags).
37 | Returns(200, "OK", map[string]any{}).
38 | Returns(404, "Not Found", nil))
39 |
40 | ws.Route(ws.GET("/configure/{tableId}").To(func(request *restful.Request, response *restful.Response) {
41 | restfulx.NewReqCtx(request, response).WithLog("生成配置").Handle(s.GenConfigure)
42 | }).
43 | Doc("生成配置").
44 | Param(ws.PathParameter("tableId", "Id").DataType("int").DefaultValue("1")).
45 | Metadata(restfulspec.KeyOpenAPITags, tags).
46 | Returns(200, "OK", map[string]any{}).
47 | Returns(404, "Not Found", nil))
48 |
49 | container.Add(ws)
50 | }
51 |
--------------------------------------------------------------------------------
/apps/device/api/product_ota.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | // ==========================================================================
4 | import (
5 | "github.com/PandaXGO/PandaKit/model"
6 | "github.com/PandaXGO/PandaKit/restfulx"
7 | "github.com/kakuilan/kgo"
8 | "strings"
9 |
10 | "pandax/apps/device/entity"
11 | "pandax/apps/device/services"
12 | )
13 |
14 | type ProductOtaApi struct {
15 | ProductOtaApp services.ProductOtaModel
16 | }
17 |
18 | // GetProductOtaList Ota列表数据
19 | func (p *ProductOtaApi) GetProductOtaList(rc *restfulx.ReqCtx) {
20 | data := entity.ProductOta{}
21 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
22 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
23 | data.Name = restfulx.QueryParam(rc, "name")
24 | data.Pid = restfulx.QueryParam(rc, "pid")
25 |
26 | list, total := p.ProductOtaApp.FindListPage(pageNum, pageSize, data)
27 |
28 | rc.ResData = model.ResultPage{
29 | Total: total,
30 | PageNum: int64(pageNum),
31 | PageSize: int64(pageNum),
32 | Data: list,
33 | }
34 | }
35 |
36 | // GetProductOta 获取Ota
37 | func (p *ProductOtaApi) GetProductOta(rc *restfulx.ReqCtx) {
38 | id := restfulx.PathParam(rc, "id")
39 | rc.ResData = p.ProductOtaApp.FindOne(id)
40 | }
41 |
42 | // InsertProductOta 添加Ota
43 | func (p *ProductOtaApi) InsertProductOta(rc *restfulx.ReqCtx) {
44 | var data entity.ProductOta
45 | restfulx.BindJsonAndValid(rc, &data)
46 | data.Id = kgo.KStr.Uniqid("ota_")
47 | p.ProductOtaApp.Insert(data)
48 | }
49 |
50 | // UpdateProductOta 修改Ota
51 | func (p *ProductOtaApi) UpdateProductOta(rc *restfulx.ReqCtx) {
52 | var data entity.ProductOta
53 | restfulx.BindJsonAndValid(rc, &data)
54 |
55 | p.ProductOtaApp.Update(data)
56 | }
57 |
58 | // DeleteProductOta 删除Ota
59 | func (p *ProductOtaApi) DeleteProductOta(rc *restfulx.ReqCtx) {
60 | id := restfulx.PathParam(rc, "id")
61 | ids := strings.Split(id, ",")
62 | p.ProductOtaApp.Delete(ids)
63 | }
64 |
--------------------------------------------------------------------------------
/apps/device/entity/device_exp.go:
--------------------------------------------------------------------------------
1 | package entity
2 |
3 | import "time"
4 |
5 | // 说明
6 | // 设备上报属性,遥测,连接事件存储到时序数据库中
7 | // 其他存在MySQL中
8 |
9 | // DeviceAlarm 设备告警表 需要更改告警状态不能存在时序数据库中
10 | type DeviceAlarm struct {
11 | Id string `json:"id" gorm:"primary_key;"`
12 | Time time.Time `gorm:"comment:告警时间" json:"time"`
13 | Name string `gorm:"type:varchar(64);comment:告警名称" json:"name"`
14 | DeviceId string `gorm:"type:varchar(64);comment:所属设备" json:"deviceId"`
15 | ProductId string `gorm:"type:varchar(64);comment:所属产品" json:"productId"`
16 | Type string `gorm:"type:varchar(64);comment:告警类型" json:"type"`
17 | Level string `gorm:"type:varchar(64);comment:告警级别" json:"level"` // 危险 重要 次要 警告 不确定
18 | State string `gorm:"type:varchar(1);comment:告警状态" json:"state"` // 告警中 0 已确认 1 已清除 2 已关闭 3
19 | Details string `gorm:"type:varchar(255);comment:告警详情" json:"details"`
20 | }
21 |
22 | type DeviceCmdLog struct {
23 | Id string `json:"id" gorm:"primary_key;"`
24 | DeviceId string `gorm:"type:varchar(64);comment:所属设备" json:"deviceId"`
25 | CmdName string `gorm:"type:varchar(64);comment:命令名称" json:"cmdName"`
26 | CmdContent string `gorm:"type:longtext;comment:命令内容" json:"cmdContent"`
27 | State string `gorm:"type:varchar(1);comment:命令状态" json:"state"`
28 | Type string `gorm:"type:varchar(1);comment:命令类型" json:"type"` // 0 自定义 1 命令
29 | ResponseContent string `gorm:"type:longtext;comment:响应内容" json:"responseContent"`
30 | RequestTime string `gorm:"comment:命令下发时间" json:"requestTime"`
31 | ResponseTime string `gorm:"comment:命令响应时间" json:"responseTime"`
32 | }
33 |
34 | func (DeviceCmdLog) TableName() string {
35 | return "device_cmd_log"
36 | }
37 |
38 | // DeviceStatistics 设备统计表
39 | // 每小时、每天、每周、每月的平均值、最大值、最小值等。可以使用定时任务或实时计算框架来更新数据统计表。
40 | type DeviceStatistics struct {
41 | Time time.Time `gorm:"comment:时间" json:"time"`
42 | }
43 |
--------------------------------------------------------------------------------
/apps/system/api/notice.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 | "strings"
10 | )
11 |
12 | type NoticeApi struct {
13 | DeptApp services.SysDeptModel
14 | NoticeApp services.SysNoticeModel
15 | }
16 |
17 | // GetNoticeList 通知列表数据
18 | func (p *NoticeApi) GetNoticeList(rc *restfulx.ReqCtx) {
19 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
20 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
21 | noticeType := restfulx.QueryParam(rc, "noticeType")
22 | title := restfulx.QueryParam(rc, "title")
23 |
24 | // 获取部门的子部门id
25 | one := p.DeptApp.FindOne(rc.LoginAccount.DeptId)
26 | split := strings.Split(strings.Trim(one.DeptPath, "/"), "/")
27 | // 获取所有父部门id
28 | ids := utils.DeptPCIds(split, rc.LoginAccount.DeptId, true)
29 | notice := entity.SysNotice{NoticeType: noticeType, Title: title, DeptIds: ids}
30 | list, total := p.NoticeApp.FindListPage(pageNum, pageSize, notice)
31 |
32 | rc.ResData = model.ResultPage{
33 | Total: total,
34 | PageNum: int64(pageNum),
35 | PageSize: int64(pageNum),
36 | Data: list,
37 | }
38 | }
39 |
40 | // InsertNotice 添加通知
41 | func (p *NoticeApi) InsertNotice(rc *restfulx.ReqCtx) {
42 | var notice entity.SysNotice
43 | restfulx.BindJsonAndValid(rc, ¬ice)
44 | notice.UserName = rc.LoginAccount.UserName
45 | p.NoticeApp.Insert(notice)
46 | }
47 |
48 | // UpdateNotice 修改通知
49 | func (p *NoticeApi) UpdateNotice(rc *restfulx.ReqCtx) {
50 | var notice entity.SysNotice
51 | restfulx.BindJsonAndValid(rc, ¬ice)
52 |
53 | p.NoticeApp.Update(notice)
54 | }
55 |
56 | // DeleteNotice 删除通知
57 | func (p *NoticeApi) DeleteNotice(rc *restfulx.ReqCtx) {
58 | noticeId := restfulx.PathParam(rc, "noticeId")
59 | noticeIds := utils.IdsStrToIdsIntGroup(noticeId)
60 | p.NoticeApp.Delete(noticeIds)
61 | }
62 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pkg/tool/base.go:
--------------------------------------------------------------------------------
1 | package tool
2 |
3 | import (
4 | "log"
5 | "reflect"
6 | "regexp"
7 | "strings"
8 | )
9 |
10 | func ToCamelCase(s string) string {
11 | re := regexp.MustCompile(`[_\W]+`)
12 | words := re.Split(s, -1)
13 | for i := range words {
14 | if i != 0 {
15 | words[i] = strings.Title(words[i])
16 | }
17 | }
18 | return strings.Join(words, "")
19 | }
20 |
21 | func RegexpKey(str string) []string {
22 | // 定义正则表达式
23 | re := regexp.MustCompile(`\${([^}]+)}`)
24 | matches := re.FindAllStringSubmatch(str, -1)
25 | // 提取匹配项的内容
26 | var results []string
27 | for _, match := range matches {
28 | if len(match) >= 2 {
29 | results = append(results, match[1])
30 | }
31 | }
32 | return results
33 | }
34 |
35 | func RegexpGetSql(str string) string {
36 | // 定义正则表达式
37 | re := regexp.MustCompile(`\${([^}]+)}`)
38 | return re.ReplaceAllString(str, "?")
39 | }
40 |
41 | func GetStructKeys(obj interface{}) []string {
42 | val := reflect.ValueOf(obj)
43 | typ := val.Type()
44 |
45 | keys := make([]string, 0, typ.NumField())
46 |
47 | for i := 0; i < typ.NumField(); i++ {
48 | field := typ.Field(i)
49 | keys = append(keys, field.Name)
50 | }
51 |
52 | return keys
53 | }
54 |
55 | func GetMapKeys(obj map[string]interface{}) []string {
56 | keys := make([]string, 0, len(obj))
57 |
58 | for key := range obj {
59 | keys = append(keys, key)
60 | }
61 |
62 | return keys
63 | }
64 |
65 | func CheckInterfaceIsArray(data interface{}) (bool, []map[string]interface{}) {
66 | if data == nil {
67 | return false, nil
68 | }
69 | valueType := reflect.TypeOf(data)
70 | // 判断类型是否为数组或切片
71 | if valueType.Kind() == reflect.Slice || valueType.Kind() == reflect.Array {
72 | var maps []map[string]interface{}
73 | for _, item := range data.([]interface{}) {
74 | log.Println("item", item)
75 | if m, ok := item.(map[string]interface{}); ok {
76 | maps = append(maps, m)
77 | }
78 | }
79 | return true, maps
80 | }
81 | return false, nil
82 | }
83 |
--------------------------------------------------------------------------------
/deploy/deploy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: pandax
6 | app.kubernetes.io/version: 1.0.0
7 | name: pandax
8 | ---
9 | apiVersion: v1
10 | kind: ServiceAccount
11 | metadata:
12 | labels:
13 | app.kubernetes.io/name: pandax
14 | app.kubernetes.io/version: 1.0.0
15 | name: pandax
16 | namespace: pandax
17 | ---
18 | apiVersion: rbac.authorization.k8s.io/v1
19 | kind: ClusterRoleBinding
20 | metadata:
21 | labels:
22 | app.kubernetes.io/name: pandax
23 | app.kubernetes.io/version: 1.0.0
24 | name: pandax-rolebinding
25 | roleRef:
26 | apiGroup: rbac.authorization.k8s.io
27 | kind: ClusterRole
28 | name: cluster-admin
29 | subjects:
30 | - kind: ServiceAccount
31 | name: pandax
32 | namespace: pandax
33 | ---
34 | apiVersion: v1
35 | kind: Service
36 | metadata:
37 | labels:
38 | app.kubernetes.io/name: pandax
39 | app.kubernetes.io/version: 1.0.0
40 | name: pandax
41 | namespace: pandax
42 | spec:
43 | ports:
44 | - name: http
45 | port: 8080
46 | protocol: TCP
47 | targetPort: http
48 | selector:
49 | app.kubernetes.io/name: pandax
50 | app.kubernetes.io/version: 1.0.0
51 | type: LoadBalancer
52 | ---
53 | apiVersion: apps/v1
54 | kind: Deployment
55 | metadata:
56 | labels:
57 | app.kubernetes.io/name: pandax
58 | app.kubernetes.io/version: 1.0.0
59 | name: pandax
60 | namespace: pandax
61 | spec:
62 | replicas: 1
63 | selector:
64 | matchLabels:
65 | app.kubernetes.io/name: pandax
66 | app.kubernetes.io/version: 1.0.0
67 | template:
68 | metadata:
69 | labels:
70 | app.kubernetes.io/name: pandax
71 | app.kubernetes.io/version: 1.0.0
72 | spec:
73 | containers:
74 | - image: xmadmin/pandax:v1.4
75 | imagePullPolicy: Always
76 | name: pandax
77 | ports:
78 | - containerPort: 7788
79 | name: http
80 | protocol: TCP
81 | serviceAccountName: pandax
82 |
--------------------------------------------------------------------------------
/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 := p.ConfigApp.FindListPage(pageNum, pageSize, config)
24 |
25 | rc.ResData = model.ResultPage{
26 | Total: total,
27 | PageNum: int64(pageNum),
28 | PageSize: int64(pageNum),
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 | rc.ResData = p.ConfigApp.FindList(entity.SysConfig{ConfigKey: configKey})
37 | }
38 |
39 | func (p *ConfigApi) GetConfig(rc *restfulx.ReqCtx) {
40 | id := restfulx.PathParamInt(rc, "configId")
41 | p.ConfigApp.FindOne(int64(id))
42 | }
43 |
44 | func (p *ConfigApi) InsertConfig(rc *restfulx.ReqCtx) {
45 | var config entity.SysConfig
46 | restfulx.BindJsonAndValid(rc, &config)
47 |
48 | p.ConfigApp.Insert(config)
49 | }
50 |
51 | func (p *ConfigApi) UpdateConfig(rc *restfulx.ReqCtx) {
52 | var post entity.SysConfig
53 | restfulx.BindJsonAndValid(rc, &post)
54 | p.ConfigApp.Update(post)
55 | }
56 |
57 | func (p *ConfigApi) DeleteConfig(rc *restfulx.ReqCtx) {
58 | configId := rc.Request.PathParameter("configId")
59 | p.ConfigApp.Delete(utils.IdsStrToIdsIntGroup(configId))
60 | }
61 |
--------------------------------------------------------------------------------
/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/logger"
8 | "github.com/PandaXGO/PandaKit/restfulx"
9 | "github.com/PandaXGO/PandaKit/utils"
10 | "github.com/sirupsen/logrus"
11 | "reflect"
12 | "runtime/debug"
13 | )
14 |
15 | func LogHandler(rc *restfulx.ReqCtx) error {
16 | li := rc.LogInfo
17 | if li == nil {
18 | return nil
19 | }
20 |
21 | lfs := logrus.Fields{}
22 | if la := rc.LoginAccount; la != nil {
23 | lfs["uid"] = la.UserId
24 | lfs["uname"] = la.UserName
25 | }
26 |
27 | req := rc.Request.Request
28 | lfs[req.Method] = req.URL.Path
29 |
30 | if err := rc.Err; err != nil {
31 | logger.Log.WithFields(lfs).Error(getErrMsg(rc, err))
32 | return nil
33 | }
34 | logger.Log.WithFields(lfs).Info(getLogMsg(rc))
35 | return nil
36 | }
37 |
38 | func getLogMsg(rc *restfulx.ReqCtx) string {
39 | msg := rc.LogInfo.Description + fmt.Sprintf(" ->%dms", rc.Timed)
40 | if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
41 | rb, _ := json.Marshal(rc.ReqParam)
42 | msg = msg + fmt.Sprintf("\n--> %s", string(rb))
43 | }
44 |
45 | // 返回结果不为空,则记录返回结果
46 | if rc.LogInfo.LogResp && !utils.IsBlank(reflect.ValueOf(rc.ResData)) {
47 | respB, _ := json.Marshal(rc.ResData)
48 | msg = msg + fmt.Sprintf("\n<-- %s", string(respB))
49 | }
50 | return msg
51 | }
52 |
53 | func getErrMsg(rc *restfulx.ReqCtx, err any) string {
54 | msg := rc.LogInfo.Description
55 | if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
56 | rb, _ := json.Marshal(rc.ReqParam)
57 | msg = msg + fmt.Sprintf("\n--> %s", string(rb))
58 | }
59 |
60 | var errMsg string
61 | switch t := err.(type) {
62 | case *biz.BizError:
63 | errMsg = fmt.Sprintf("\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())
64 | case error:
65 | errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t.Error(), string(debug.Stack()))
66 | case string:
67 | errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t, string(debug.Stack()))
68 | }
69 | return (msg + errMsg)
70 | }
71 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/action_generator_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | "time"
7 | )
8 |
9 | type messageGeneratorNode struct {
10 | bareNode
11 | Script string `json:"script" yaml:"script"`
12 | PeriodSecond int64 `json:"periodSecond" yaml:"periodSecond"` //周期
13 | MessageCount int64 `json:"messageCount" yaml:"messageCount"`
14 | }
15 |
16 | type messageGeneratorNodeFactory struct{}
17 |
18 | func (f messageGeneratorNodeFactory) Name() string { return "MessageGeneratorNode" }
19 | func (f messageGeneratorNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
20 | func (f messageGeneratorNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
21 | func (f messageGeneratorNodeFactory) Create(id string, meta Metadata) (Node, error) {
22 | node := &messageGeneratorNode{
23 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
24 | }
25 | return decodePath(meta, node)
26 | }
27 |
28 | func (n *messageGeneratorNode) Handle(msg *message.Message) error {
29 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
30 |
31 | successLabelNode := n.GetLinkedNode("Success")
32 | failureLabelNode := n.GetLinkedNode("Failure")
33 |
34 | ticker := time.NewTicker(time.Duration(n.PeriodSecond) * time.Second)
35 | count := 0
36 |
37 | go func() {
38 | for {
39 | <-ticker.C
40 | count++
41 | if int64(count) == n.MessageCount {
42 | ticker.Stop()
43 | return
44 | }
45 | scriptEngine := NewScriptEngine(*msg, "Generate", n.Script)
46 | generate, err := scriptEngine.ScriptGenerate()
47 | if err != nil {
48 | if failureLabelNode != nil {
49 | go failureLabelNode.Handle(msg)
50 | }
51 | return
52 | }
53 | msg.Msg = generate["msg"].(message.Msg)
54 | msg.Metadata = generate["metadata"].(message.Metadata)
55 | msg.MsgType = generate["msgType"].(string)
56 | if successLabelNode != nil {
57 | go successLabelNode.Handle(msg)
58 | }
59 | }
60 | }()
61 |
62 | return nil
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/transform_rename_key_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "pandax/pkg/rule_engine/message"
6 | )
7 |
8 | type transformRenameKeyNode struct {
9 | bareNode
10 | FormType string `json:"formType" yaml:"formType"` //msg metadata
11 | Keys []KeyName `json:"keys" yaml:"keys"`
12 | }
13 | type KeyName struct {
14 | OldName string `json:"oldName" yaml:"oldName"`
15 | NewName string `json:"newName" yaml:"newName"`
16 | }
17 | type transformRenameKeyNodeFactory struct{}
18 |
19 | func (f transformRenameKeyNodeFactory) Name() string { return "RenameKeyNode" }
20 | func (f transformRenameKeyNodeFactory) Category() string { return NODE_CATEGORY_TRANSFORM }
21 | func (f transformRenameKeyNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
22 | func (f transformRenameKeyNodeFactory) Create(id string, meta Metadata) (Node, error) {
23 | node := &transformRenameKeyNode{
24 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
25 | }
26 | return decodePath(meta, node)
27 | }
28 |
29 | func (n *transformRenameKeyNode) Handle(msg *message.Message) error {
30 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
31 |
32 | successLabelNode := n.GetLinkedNode("Success")
33 | failureLabelNode := n.GetLinkedNode("Failure")
34 | if n.FormType == "msg" {
35 | data := msg.Msg
36 | for _, key := range n.Keys {
37 | if _, found := data[key.OldName]; found {
38 | data[key.NewName] = data[key.OldName]
39 | delete(data, key.OldName)
40 | msg.Msg = data
41 | }
42 | }
43 | } else if n.FormType == "metadata" {
44 | data := msg.Metadata
45 | for _, key := range n.Keys {
46 | if data.GetValue(key.OldName) != nil {
47 | data[key.NewName] = data[key.OldName]
48 | delete(data, key.OldName)
49 | msg.Metadata = data
50 | }
51 | }
52 | } else {
53 | if failureLabelNode != nil {
54 | return failureLabelNode.Handle(msg)
55 | }
56 | }
57 | if successLabelNode != nil {
58 | return successLabelNode.Handle(msg)
59 | }
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/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 | cors: true
12 | # 数据上报 队列池
13 | queue-num: 1000
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: 604800
30 | #数据上报并发识别任务数量限制
31 | queue:
32 | enable: false
33 | num: 3000
34 | redis:
35 | host: 127.0.0.1
36 | password: root
37 | port: 6379
38 |
39 | mysql:
40 | host: 127.0.0.1:3306
41 | username: root
42 | password: '!MyEMS1'
43 | db-name: pandax_iot
44 | config: charset=utf8&loc=Local&parseTime=true
45 | # mini0
46 | oss:
47 | endpoint: 127.0.0.1:9000
48 | accessKey: minioadmin
49 | secretKey: minioadmin
50 | bucketName: pandaxiot
51 | useSSL: false
52 |
53 | taos:
54 | username: "root"
55 | password: "taosdata"
56 | host: "127.0.0.1:6041"
57 | database: "iot"
58 | config: ""
59 |
60 | mqtt:
61 | broker: 127.0.0.1:1883
62 | qos: 1
63 | username: pandax
64 | password: pandax
65 |
66 | casbin:
67 | model-path: './resource/rbac_model.conf'
68 |
69 | gen:
70 | # 代码生成读取的数据库名称
71 | dbname: pandax_iot
72 | # 代码生成是使用前端代码存放位置,需要指定到src文件夹,相对路径
73 | frontpath: ../PandaUi/src
74 |
75 | log:
76 | # 日志等级, trace, debug, info, warn, error, fatal
77 | level: info
78 | file:
79 | path: ./
80 | # name: panda_log.log
81 |
82 | # 视频服务器使用的全局配置
83 | global:
84 | http:
85 | listenaddr: :8801 # 网关地址,用于访问API
86 | listenaddrtls: :8443 # 用于HTTPS方式访问API的端口配置
87 |
88 | gb28181:
89 | #sip服务器地址 默认 自动适配设备网段
90 | sipip: ""
91 | serial: "34020000002000000001"
92 | realm: "3402000000"
93 | password: "pandax"
94 | #sip服务器端口
95 | port:
96 | sip: udp:5060
--------------------------------------------------------------------------------
/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 | "github.com/PandaXGO/PandaKit/biz"
11 | "pandax/apps/rule/entity"
12 | "pandax/pkg/global"
13 | )
14 |
15 | type (
16 | RuleChainMsgLogModel interface {
17 | Insert(data entity.RuleChainMsgLog) *entity.RuleChainMsgLog
18 | FindListPage(page, pageSize int, data entity.RuleChainMsgLog) (*[]entity.RuleChainMsgLog, int64)
19 | Delete(ids []string)
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 {
32 | err := global.Db.Table(m.table).Create(&data).Error
33 | biz.ErrIsNil(err, "添加规则链失败")
34 | return &data
35 | }
36 |
37 | func (m *ruleChainLogModelImpl) FindListPage(page, pageSize int, data entity.RuleChainMsgLog) (*[]entity.RuleChainMsgLog, int64) {
38 | list := make([]entity.RuleChainMsgLog, 0)
39 | var total int64 = 0
40 | offset := pageSize * (page - 1)
41 | db := global.Db.Table(m.table)
42 | // 此处填写 where参数判断
43 | if data.DeviceName != "" {
44 | db = db.Where("device_name = ?", data.DeviceName)
45 | }
46 | if data.MessageId != "" {
47 | db = db.Where("message_id = ?", data.MessageId)
48 | }
49 | if data.MsgType != "" {
50 | db = db.Where("msg_type = ?", data.MsgType)
51 | }
52 | err := db.Count(&total).Error
53 | err = db.Order("create_at").Limit(pageSize).Offset(offset).Find(&list).Error
54 | biz.ErrIsNil(err, "查询规则链分页列表失败")
55 | return &list, total
56 | }
57 |
58 | func (m *ruleChainLogModelImpl) Delete(ids []string) {
59 | biz.ErrIsNil(global.Db.Table(m.table).Delete(&entity.RuleChainMsgLog{}, "id in (?)", ids).Error, "删除规则链失败")
60 | }
61 |
--------------------------------------------------------------------------------
/apps/device/api/device_cmd.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 | "github.com/kakuilan/kgo"
9 | "pandax/pkg/global"
10 | "pandax/pkg/mqtt"
11 | "strings"
12 | "time"
13 |
14 | "pandax/apps/device/entity"
15 | "pandax/apps/device/services"
16 | )
17 |
18 | type DeviceCmdLogApi struct {
19 | DeviceCmdLogApp services.DeviceCmdLogModel
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 := p.DeviceCmdLogApp.FindListPage(pageNum, pageSize, data)
32 |
33 | rc.ResData = model.ResultPage{
34 | Total: total,
35 | PageNum: int64(pageNum),
36 | PageSize: int64(pageNum),
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 | data.Id = kgo.KStr.Uniqid("cmd_")
46 | data.State = "2"
47 | data.RequestTime = time.Now().Format("2006-01-02 15:04:05")
48 | err := p.DeviceCmdLogApp.Insert(data)
49 | biz.ErrIsNil(err, "添加指令记录失败")
50 | // 下发指令
51 | var rpc = &mqtt.RpcRequest{Client: global.MqttClient, Mode: "single"}
52 | rpc.GetRequestId()
53 | _, err = rpc.RequestCmd(mqtt.RpcPayload{Method: data.CmdName, Params: data.CmdContent})
54 | if err != nil {
55 | global.Log.Error("指令下发失败")
56 | }
57 | }
58 |
59 | // DeleteDeviceCmdLog 删除告警
60 | func (p *DeviceCmdLogApi) DeleteDeviceCmdLog(rc *restfulx.ReqCtx) {
61 | id := restfulx.PathParam(rc, "id")
62 | ids := strings.Split(id, ",")
63 | 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 | }
18 |
19 | ws := new(restful.WebService)
20 | ws.Path("/device/cmd").Produces(restful.MIME_JSON)
21 | tags := []string{"cmd"}
22 |
23 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
24 | restfulx.NewReqCtx(request, response).WithLog("获取命令下发分页列表").Handle(s.GetDeviceCmdLogList)
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("deviceId", "设备Id").Required(false).DataType("string")).
30 | Param(ws.QueryParameter("type", "命令下发分类").Required(false).DataType("string")).
31 | Param(ws.QueryParameter("state", "命令下发状态").Required(false).DataType("string")).
32 | Metadata(restfulspec.KeyOpenAPITags, tags).
33 | Writes(model.ResultPage{}).
34 | Returns(200, "OK", model.ResultPage{}))
35 |
36 | ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) {
37 | restfulx.NewReqCtx(request, response).WithLog("命令下发").Handle(s.InsertDeviceCmdLog)
38 | }).
39 | Doc("命令下发").
40 | Metadata(restfulspec.KeyOpenAPITags, tags).
41 | Reads(entity.DeviceCmdLog{}))
42 |
43 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) {
44 | restfulx.NewReqCtx(request, response).WithLog("删除命令下发信息").Handle(s.DeleteDeviceCmdLog)
45 | }).
46 | Doc("删除命令下发信息").
47 | Metadata(restfulspec.KeyOpenAPITags, tags).
48 | Param(ws.PathParameter("id", "多id 1,2,3").DataType("string")))
49 |
50 | container.Add(ws)
51 | }
52 |
--------------------------------------------------------------------------------
/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 为设备ID
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 为设备ID
66 | ```json
67 | {
68 | "devA": [
69 | {
70 | "ts": 1689837909000,
71 | "values": {
72 | "telemetry1": "value1",
73 | "telemetry2": 0
74 | }
75 | }
76 | ]
77 | }
78 | ```
79 |
80 | ## 网关子设备连接或断开上报格式
81 | ```json
82 | {
83 | "devA": "online",
84 | "devB": "offline"
85 | }
86 | ```
87 | ## 命令下发格式
88 | 下发的ID和相应的ID相同
89 | ```json
90 | {
91 | "id": "2343",
92 | "cmd": "restart",
93 | "content": {
94 | "firmware_address": "http://xxx.yyy.com",
95 | "version": "latest",
96 | "secret": "****",
97 | "http_method": "GET"
98 | }
99 | }
100 | ```
101 |
102 | ## 命令响应的格式
103 | success 返回结果必传 content代表输出参数可选
104 | ```json
105 | {
106 | "id": "2343",
107 | "success": true,
108 | "content": {
109 | "aa": "2"
110 | }
111 | }
112 | ```
113 |
--------------------------------------------------------------------------------
/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{"alarm"}
22 |
23 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
24 | restfulx.NewReqCtx(request, response).WithLog("获取告警分页列表").Handle(s.GetDeviceAlarmList)
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("deviceId", "设备Id").Required(false).DataType("string")).
30 | Param(ws.QueryParameter("type", "告警类型").Required(false).DataType("string")).
31 | Param(ws.QueryParameter("level", "告警等级").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.PUT("").To(func(request *restful.Request, response *restful.Response) {
38 | restfulx.NewReqCtx(request, response).WithLog("修改告警信息").Handle(s.UpdateDeviceAlarm)
39 | }).
40 | Doc("修改告警信息").
41 | Metadata(restfulspec.KeyOpenAPITags, tags).
42 | Reads(entity.DeviceAlarm{}))
43 |
44 | ws.Route(ws.DELETE("/{id}").To(func(request *restful.Request, response *restful.Response) {
45 | restfulx.NewReqCtx(request, response).WithLog("删除告警信息").Handle(s.DeleteDeviceAlarm)
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 |
--------------------------------------------------------------------------------
/apps/system/api/api.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/casbin"
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 | "pandax/pkg/global"
11 | )
12 |
13 | type SystemApiApi struct {
14 | ApiApp services.SysApiModel
15 | }
16 |
17 | func (s *SystemApiApi) CreateApi(rc *restfulx.ReqCtx) {
18 | var api entity.SysApi
19 | restfulx.BindJsonAndValid(rc, &api)
20 | s.ApiApp.Insert(api)
21 | }
22 |
23 | func (s *SystemApiApi) DeleteApi(rc *restfulx.ReqCtx) {
24 | ids := rc.Request.PathParameter("id")
25 | s.ApiApp.Delete(utils.IdsStrToIdsIntGroup(ids))
26 | }
27 |
28 | func (s *SystemApiApi) GetApiList(rc *restfulx.ReqCtx) {
29 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
30 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
31 | path := rc.Request.QueryParameter("path")
32 | description := rc.Request.QueryParameter("description")
33 | method := rc.Request.QueryParameter("method")
34 | apiGroup := rc.Request.QueryParameter("apiGroup")
35 | api := entity.SysApi{Path: path, Description: description, Method: method, ApiGroup: apiGroup}
36 | list, total := s.ApiApp.FindListPage(pageNum, pageSize, api)
37 | rc.ResData = model.ResultPage{
38 | Total: total,
39 | PageNum: int64(pageNum),
40 | PageSize: int64(pageNum),
41 | Data: list,
42 | }
43 | }
44 |
45 | func (s *SystemApiApi) GetApiById(rc *restfulx.ReqCtx) {
46 | id := restfulx.QueryInt(rc, "id", 0)
47 | rc.ResData = s.ApiApp.FindOne(int64(id))
48 |
49 | }
50 |
51 | func (s *SystemApiApi) UpdateApi(rc *restfulx.ReqCtx) {
52 | var api entity.SysApi
53 | restfulx.BindJsonAndValid(rc, &api)
54 | s.ApiApp.Update(api)
55 | }
56 |
57 | func (s *SystemApiApi) GetAllApis(rc *restfulx.ReqCtx) {
58 | rc.ResData = s.ApiApp.FindList(entity.SysApi{})
59 | }
60 |
61 | func (s *SystemApiApi) GetPolicyPathByRoleId(rc *restfulx.ReqCtx) {
62 | roleKey := rc.Request.QueryParameter("roleKey")
63 | ca := casbin.CasbinS{ModelPath: global.Conf.Casbin.ModelPath}
64 | rc.ResData = ca.GetPolicyPathByRoleId(roleKey)
65 | }
66 |
--------------------------------------------------------------------------------
/deploy/mysql.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: mysql
6 | app.kubernetes.io/version: 1.0.0
7 | name: pandax-db
8 | ---
9 | apiVersion: v1
10 | kind: Service
11 | metadata:
12 | labels:
13 | app.kubernetes.io/version: 1.0.0
14 | name: mysql
15 | namespace: pandax-db
16 | spec:
17 | ports:
18 | - port: 3306
19 | protocol: TCP
20 | targetPort: mysql
21 | selector:
22 | app: mysql
23 | app.kubernetes.io/version: 1.0.0
24 | type: LoadBalancer
25 | ---
26 | apiVersion: v1
27 | kind: PersistentVolume
28 | metadata:
29 | labels:
30 | app.kubernetes.io/version: 1.0.0
31 | type: local
32 | name: mysql-pv-volume
33 | spec:
34 | accessModes:
35 | - ReadWriteOnce
36 | capacity:
37 | storage: 20Gi
38 | hostPath:
39 | path: /mnt/data
40 | storageClassName: manual
41 | ---
42 | apiVersion: v1
43 | kind: PersistentVolumeClaim
44 | metadata:
45 | labels:
46 | app.kubernetes.io/version: 1.0.0
47 | name: mysql-pv-claim
48 | namespace: pandax-db
49 | spec:
50 | accessModes:
51 | - ReadWriteOnce
52 | resources:
53 | requests:
54 | storage: 20Gi
55 | storageClassName: manual
56 | ---
57 | apiVersion: apps/v1
58 | kind: Deployment
59 | metadata:
60 | labels:
61 | app.kubernetes.io/version: 1.0.0
62 | name: mysql
63 | namespace: pandax-db
64 | spec:
65 | selector:
66 | matchLabels:
67 | app: mysql
68 | app.kubernetes.io/version: 1.0.0
69 | strategy:
70 | type: Recreate
71 | template:
72 | metadata:
73 | labels:
74 | app: mysql
75 | app.kubernetes.io/version: 1.0.0
76 | spec:
77 | containers:
78 | - env:
79 | - name: MYSQL_ROOT_PASSWORD
80 | value: pandax
81 | image: mysql:8.0.23
82 | name: mysql
83 | ports:
84 | - containerPort: 3306
85 | name: mysql
86 | protocol: TCP
87 | volumeMounts:
88 | - mountPath: /var/lib/mysql
89 | name: mysql-persistent-storage
90 | volumes:
91 | - name: mysql-persistent-storage
92 | persistentVolumeClaim:
93 | claimName: mysql-pv-claim
94 |
--------------------------------------------------------------------------------
/apps/system/api/tenant.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | /**
4 | * @Description
5 | * @Author 熊猫
6 | * @Date 2022/7/14 17:55
7 | **/
8 | import (
9 | "github.com/PandaXGO/PandaKit/model"
10 | "github.com/PandaXGO/PandaKit/restfulx"
11 | "github.com/PandaXGO/PandaKit/utils"
12 | "pandax/apps/system/entity"
13 | "pandax/apps/system/services"
14 | )
15 |
16 | type SysTenantsApi struct {
17 | SysTenantsApp services.SysTenantsModel
18 | }
19 |
20 | func (p *SysTenantsApi) GetSysTenantsList(rc *restfulx.ReqCtx) {
21 | data := entity.SysTenants{}
22 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
23 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
24 |
25 | list, total := p.SysTenantsApp.FindListPage(pageNum, pageSize, data)
26 |
27 | rc.ResData = model.ResultPage{
28 | Total: total,
29 | PageNum: int64(pageNum),
30 | PageSize: int64(pageNum),
31 | Data: list,
32 | }
33 | }
34 |
35 | func (p *SysTenantsApi) GetSysTenantsAll(rc *restfulx.ReqCtx) {
36 | list := make([]entity.SysTenants, 0)
37 | if rc.LoginAccount.RoleKey == "admin" {
38 | data := entity.SysTenants{}
39 | list = *p.SysTenantsApp.FindList(data)
40 | } else {
41 | list = append(list, *p.SysTenantsApp.FindOne(rc.LoginAccount.TenantId))
42 | }
43 | rc.ResData = list
44 | }
45 |
46 | func (p *SysTenantsApi) GetSysTenants(rc *restfulx.ReqCtx) {
47 | tenantId := restfulx.PathParamInt(rc, "tenantId")
48 | p.SysTenantsApp.FindOne(int64(tenantId))
49 | }
50 |
51 | func (p *SysTenantsApi) InsertSysTenants(rc *restfulx.ReqCtx) {
52 | var data entity.SysTenants
53 | restfulx.BindQuery(rc, &data)
54 |
55 | p.SysTenantsApp.Insert(data)
56 | }
57 |
58 | func (p *SysTenantsApi) UpdateSysTenants(rc *restfulx.ReqCtx) {
59 | var data entity.SysTenants
60 | restfulx.BindQuery(rc, &data)
61 |
62 | p.SysTenantsApp.Update(data)
63 | }
64 |
65 | func (p *SysTenantsApi) DeleteSysTenants(rc *restfulx.ReqCtx) {
66 | tenantId := rc.Request.PathParameter("tenantId")
67 | tenantIds := utils.IdsStrToIdsIntGroup(tenantId)
68 | p.SysTenantsApp.Delete(tenantIds)
69 | }
70 |
71 | // IsTenantAdmin 是否为主租户
72 | func IsTenantAdmin(tenantId int64) bool {
73 | if tenantId == 1 {
74 | return true
75 | }
76 | return false
77 | }
78 |
--------------------------------------------------------------------------------
/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 | "net/http"
9 | "os"
10 | "pandax/pkg/config"
11 | "pandax/pkg/global"
12 | "pandax/pkg/tool"
13 | "path"
14 | "time"
15 | )
16 |
17 | type UploadApi struct{}
18 |
19 | const filePath = "uploads/file"
20 |
21 | // UploadImage 图片上传
22 | func (up *UploadApi) UploadImage(rc *restfulx.ReqCtx) {
23 | _, fileHeader, err := rc.Request.Request.FormFile("file")
24 | biz.ErrIsNil(err, "请传入文件")
25 | local := &tool.Local{Path: filePath}
26 | link, fileName, err := local.UploadFile(fileHeader)
27 | biz.ErrIsNil(err, "文件上传失败")
28 | rc.ResData = map[string]string{"fileName": fileName, "filePath": link}
29 | }
30 |
31 | // UplaodResOsses 上传文件ResOsses
32 | func (p *UploadApi) UplaodToOss(rc *restfulx.ReqCtx) {
33 | _, handler, _ := rc.Request.Request.FormFile("file")
34 | yunFileTmpPath := "uploads/" + time.Now().Format("2006-01-02") + "/" + handler.Filename
35 | // 读取本地文件。
36 | f, openError := handler.Open()
37 | biz.ErrIsNil(openError, "function file.Open() Failed")
38 | config := global.Conf.Oss
39 | biz.ErrIsNil(NewOss(*config).PutObj(yunFileTmpPath, f), "上传OSS失败")
40 |
41 | rc.ResData = fmt.Sprintf("http://%s/%s/%s", config.Endpoint, config.BucketName, yunFileTmpPath)
42 | }
43 |
44 | func (up *UploadApi) GetImage(rc *restfulx.ReqCtx) {
45 | actual := path.Join(filePath, restfulx.PathParam(rc, "subpath"))
46 | http.ServeFile(
47 | rc.Response.ResponseWriter,
48 | rc.Request.Request,
49 | actual)
50 | }
51 |
52 | func (up *UploadApi) DeleteImage(rc *restfulx.ReqCtx) {
53 | fileName := restfulx.QueryParam(rc, "fileName")
54 | biz.NotEmpty(fileName, "请传要删除的图片名")
55 | err := os.Remove(fmt.Sprintf("%s/%s", filePath, 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 |
--------------------------------------------------------------------------------
/pkg/rule_engine/instance_test.go:
--------------------------------------------------------------------------------
1 | package rule_engine
2 |
3 | import (
4 | "context"
5 | "io/ioutil"
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 := ioutil.ReadFile("./manifest/manifest_sample.json")
13 | if err != nil {
14 | t.Error(err)
15 | }
16 | instance, errs := NewRuleChainInstance(buf)
17 | if len(errs) > 0 {
18 | t.Error(errs[0])
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 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/external_wechat_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/PandaXGO/PandaKit/httpclient"
6 | "github.com/sirupsen/logrus"
7 | "pandax/pkg/rule_engine/message"
8 | )
9 |
10 | type externalWechatNode struct {
11 | bareNode
12 | WebHook string `json:"webHook" yaml:"webHook"`
13 | MsgType string `json:"msgType" yaml:"msgType"`
14 | Content string `json:"content" yaml:"content"`
15 | IsAtAll bool `json:"isAtAll" yaml:"isAtAll"`
16 | AtMobiles []string `json:"atMobiles" yaml:"atMobiles"`
17 | }
18 |
19 | type externalWechatNodeFactory struct{}
20 |
21 | func (f externalWechatNodeFactory) Name() string { return "WechatNode" }
22 | func (f externalWechatNodeFactory) Category() string { return NODE_CATEGORY_EXTERNAL }
23 | func (f externalWechatNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
24 | func (f externalWechatNodeFactory) Create(id string, meta Metadata) (Node, error) {
25 | node := &externalWechatNode{
26 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
27 | }
28 | return decodePath(meta, node)
29 | }
30 |
31 | func (n *externalWechatNode) Handle(msg *message.Message) error {
32 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
33 |
34 | successLabelNode := n.GetLinkedNode("Success")
35 | failureLabelNode := n.GetLinkedNode("Failure")
36 | template, err := ParseTemplate(n.Content, msg.GetAllMap())
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 | if failureLabelNode != nil {
50 | return failureLabelNode.Handle(msg)
51 | } else {
52 | return err
53 | }
54 | }
55 | if successLabelNode != nil {
56 | return successLabelNode.Handle(msg)
57 | }
58 | return nil
59 | }
60 |
--------------------------------------------------------------------------------
/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 | TableName string `gorm:"table_name" json:"tableName"` // 表名称
8 | TableComment string `gorm:"table_comment" json:"tableComment"` // 表描述
9 | ClassName string `gorm:"class_name" json:"className"` // 实体类名称
10 | TplCategory string `gorm:"tpl_category" json:"tplCategory"` // 使用的模板(crud单表操作 tree树表操作)
11 | PackageName string `gorm:"package_name" json:"packageName"` // 生成包路径
12 | ModuleName string `gorm:"module_name" json:"moduleName"` // 生成模块名
13 | BusinessName string `gorm:"business_name" json:"businessName"` // 生成业务名
14 | FunctionName string `gorm:"function_name" json:"functionName"` // 生成功能名
15 | FunctionAuthor string `gorm:"function_author" json:"functionAuthor"` // 生成功能作者
16 | Options string `gorm:"options" json:"options"` // 其它生成选项
17 | Remark string `gorm:"remark" json:"remark"` // 备注
18 | PkColumn string `gorm:"pk_column;" json:"pkColumn"`
19 | PkGoField string `gorm:"pk_go_field" json:"pkGoField"`
20 | PkJsonField string `gorm:"pk_json_field" json:"pkJsonField"`
21 | Columns []DevGenTableColumn `gorm:"-" json:"columns"` // 字段信息
22 | model.BaseModel
23 | }
24 |
25 | type DBTables struct {
26 | TableName string `gorm:"column:TABLE_NAME" json:"tableName"`
27 | Engine string `gorm:"column:ENGINE" json:"engine"`
28 | TableRows string `gorm:"column:TABLE_ROWS" json:"tableRows"`
29 | TableCollation string `gorm:"column:TABLE_COLLATION" json:"tableCollation"`
30 | CreateTime string `gorm:"column:CREATE_TIME" json:"createTime"`
31 | UpdateTime string `gorm:"column:UPDATE_TIME" json:"updateTime"`
32 | TableComment string `gorm:"column:TABLE_COMMENT" json:"tableComment"`
33 | }
34 |
--------------------------------------------------------------------------------
/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/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 | "github.com/PandaXGO/PandaKit/biz"
5 | "pandax/apps/log/entity"
6 | "pandax/pkg/global"
7 | )
8 |
9 | type (
10 | LogOperModel interface {
11 | Insert(data entity.LogOper) *entity.LogOper
12 | FindOne(infoId int64) *entity.LogOper
13 | FindListPage(page, pageSize int, data entity.LogOper) (*[]entity.LogOper, int64)
14 | Delete(infoId []int64)
15 | DeleteAll()
16 | }
17 |
18 | logLogOperModelImpl struct {
19 | table string
20 | }
21 | )
22 |
23 | var LogOperModelDao LogOperModel = &logLogOperModelImpl{
24 | table: `log_opers`,
25 | }
26 |
27 | func (m *logLogOperModelImpl) Insert(data entity.LogOper) *entity.LogOper {
28 | global.Db.Table(m.table).Create(&data)
29 | return &data
30 | }
31 |
32 | func (m *logLogOperModelImpl) FindOne(operId int64) *entity.LogOper {
33 | resData := new(entity.LogOper)
34 | err := global.Db.Table(m.table).Where("oper_id = ?", operId).First(resData).Error
35 | biz.ErrIsNil(err, "查询操作日志信息失败")
36 | return resData
37 | }
38 |
39 | func (m *logLogOperModelImpl) FindListPage(page, pageSize int, data entity.LogOper) (*[]entity.LogOper, int64) {
40 | list := make([]entity.LogOper, 0)
41 | var total int64 = 0
42 | offset := pageSize * (page - 1)
43 | db := global.Db.Table(m.table)
44 | // 此处填写 where参数判断
45 | if data.BusinessType != "" {
46 | db = db.Where("business_type = ?", data.BusinessType)
47 | }
48 | if data.OperLocation != "" {
49 | db = db.Where("oper_location like ?", "%"+data.OperLocation+"%")
50 | }
51 | if data.Title != "" {
52 | db = db.Where("title like ?", "%"+data.Title+"%")
53 | }
54 | if data.OperName != "" {
55 | db = db.Where("oper_name like ?", "%"+data.OperName+"%")
56 | }
57 | err := db.Where("delete_time IS NULL").Count(&total).Error
58 | err = db.Order("create_time desc").Limit(pageSize).Offset(offset).Find(&list).Error
59 |
60 | biz.ErrIsNil(err, "查询操作分页日志信息失败")
61 | return &list, total
62 | }
63 |
64 | func (m *logLogOperModelImpl) Delete(operIds []int64) {
65 | err := global.Db.Table(m.table).Delete(&entity.LogOper{}, "oper_id in (?)", operIds).Error
66 | biz.ErrIsNil(err, "删除操作日志信息失败")
67 | return
68 | }
69 |
70 | func (m *logLogOperModelImpl) DeleteAll() {
71 | global.Db.Exec("DELETE FROM log_opers")
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/mqtt/mqtt.go:
--------------------------------------------------------------------------------
1 | package mqtt
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | mqtt "github.com/eclipse/paho.mqtt.golang"
7 | "github.com/sirupsen/logrus"
8 | "time"
9 | )
10 |
11 | const DefaultDownStreamClientId = `@panda.iothub.internal.clientId`
12 |
13 | type IothubMqttClient struct {
14 | Client mqtt.Client
15 | }
16 |
17 | var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
18 | logrus.Infof("Connected")
19 | }
20 |
21 | var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
22 | logrus.Infof("Connect lost: %v", err)
23 | }
24 |
25 | func InitMqtt(broker, username, password string) *IothubMqttClient {
26 | server := fmt.Sprintf("tcp://%s", broker)
27 | client := GetMqttClinent(server, username, password)
28 | return &IothubMqttClient{
29 | Client: client,
30 | }
31 | }
32 |
33 | func GetMqttClinent(server, username, password string) mqtt.Client {
34 | opts := mqtt.NewClientOptions().AddBroker(server)
35 | time.Now().Unix()
36 | // 默认下行ID iothub会过滤掉改Id
37 | opts.SetClientID(DefaultDownStreamClientId)
38 | if username != "" {
39 | opts.SetUsername(username)
40 | }
41 | if password != "" {
42 | opts.SetPassword(password)
43 | }
44 | opts.OnConnect = connectHandler
45 | opts.OnConnectionLost = connectLostHandler
46 | client := mqtt.NewClient(opts)
47 | if token := client.Connect(); token.Wait() && token.Error() != nil {
48 | panic(token.Error())
49 | }
50 | return client
51 | }
52 |
53 | // 订阅
54 | func (imc *IothubMqttClient) Sub(topic string, qos byte, handler mqtt.MessageHandler) {
55 | if token := imc.Client.Subscribe(topic, qos, handler); token.Wait() && token.Error() != nil {
56 | logrus.Infof(token.Error().Error())
57 | }
58 | }
59 |
60 | // 取消订阅
61 | func (imc *IothubMqttClient) UnSub(topic string) {
62 | if token := imc.Client.Unsubscribe(topic); token.Wait() && token.Error() != nil {
63 | logrus.Infof(token.Error().Error())
64 | }
65 | }
66 |
67 | // 发布
68 | func (imc *IothubMqttClient) Pub(topic string, qos byte, payload string) error {
69 | token := imc.Client.Publish(topic, qos, false, payload)
70 | if token.WaitTimeout(2*time.Second) == false {
71 | return errors.New("推送消息超时")
72 | }
73 | if token.Error() != nil {
74 | return token.Error()
75 | }
76 | return nil
77 | }
78 |
--------------------------------------------------------------------------------
/apps/system/router/notice.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/model"
5 | "github.com/PandaXGO/PandaKit/restfulx"
6 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
7 | "github.com/emicklei/go-restful/v3"
8 | "pandax/apps/system/api"
9 | "pandax/apps/system/entity"
10 | "pandax/apps/system/services"
11 | )
12 |
13 | func InitNoticeRouter(container *restful.Container) {
14 | s := &api.NoticeApi{
15 | DeptApp: services.SysDeptModelDao,
16 | NoticeApp: services.SysNoticeModelDao,
17 | }
18 | ws := new(restful.WebService)
19 | ws.Path("/system/notice").Produces(restful.MIME_JSON)
20 | tags := []string{"notice"}
21 |
22 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
23 | restfulx.NewReqCtx(request, response).WithLog("获取通知分页列表").Handle(s.GetNoticeList)
24 | }).
25 | Doc("获取通知分页列表").
26 | Metadata(restfulspec.KeyOpenAPITags, tags).
27 | Param(ws.QueryParameter("pageNum", "页数").Required(true).DataType("int")).
28 | Param(ws.QueryParameter("pageSize", "每页条数").Required(true).DataType("int")).
29 | Param(ws.QueryParameter("noticeType", "noticeType").DataType("string")).
30 | Param(ws.QueryParameter("title", "title").DataType("string")).
31 | Writes(model.ResultPage{}).
32 | Returns(200, "OK", model.ResultPage{}))
33 |
34 | ws.Route(ws.POST("").To(func(request *restful.Request, response *restful.Response) {
35 | restfulx.NewReqCtx(request, response).WithLog("添加通知信息").Handle(s.InsertNotice)
36 | }).
37 | Doc("添加通知信息").
38 | Metadata(restfulspec.KeyOpenAPITags, tags).
39 | Reads(entity.SysNotice{}))
40 |
41 | ws.Route(ws.PUT("").To(func(request *restful.Request, response *restful.Response) {
42 | restfulx.NewReqCtx(request, response).WithLog("修改通知信息").Handle(s.UpdateNotice)
43 | }).
44 | Doc("修改通知信息").
45 | Metadata(restfulspec.KeyOpenAPITags, tags).
46 | Reads(entity.SysNotice{}))
47 |
48 | ws.Route(ws.DELETE("/{noticeId}").To(func(request *restful.Request, response *restful.Response) {
49 | restfulx.NewReqCtx(request, response).WithLog("删除通知信息").Handle(s.DeleteNotice)
50 | }).
51 | Doc("删除通知信息").
52 | Metadata(restfulspec.KeyOpenAPITags, tags).
53 | Param(ws.PathParameter("noticeId", "多id 1,2,3").DataType("string")))
54 |
55 | container.Add(ws)
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/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 | DeptId int64 `gorm:"type:int" json:"deptId"` //部门编码
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 | DeptName string `gorm:"-" json:"deptName"`
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 |
--------------------------------------------------------------------------------
/apps/system/services/notice.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/biz"
5 | "pandax/apps/system/entity"
6 | "pandax/pkg/global"
7 | )
8 |
9 | type (
10 | SysNoticeModel interface {
11 | Insert(data entity.SysNotice) *entity.SysNotice
12 | FindOne(postId int64) *entity.SysNotice
13 | FindListPage(page, pageSize int, data entity.SysNotice) (*[]entity.SysNotice, int64)
14 | Update(data entity.SysNotice) *entity.SysNotice
15 | Delete(postId []int64)
16 | }
17 |
18 | sysNoticeModelImpl struct {
19 | table string
20 | }
21 | )
22 |
23 | var SysNoticeModelDao SysNoticeModel = &sysNoticeModelImpl{
24 | table: `sys_notices`,
25 | }
26 |
27 | func (m *sysNoticeModelImpl) Insert(data entity.SysNotice) *entity.SysNotice {
28 | err := global.Db.Table(m.table).Create(&data).Error
29 | biz.ErrIsNil(err, "添加通知失败")
30 | return &data
31 | }
32 |
33 | func (m *sysNoticeModelImpl) FindOne(postId int64) *entity.SysNotice {
34 | resData := new(entity.SysNotice)
35 | err := global.Db.Table(m.table).Where("post_id = ?", postId).First(resData).Error
36 | biz.ErrIsNil(err, "查询通知失败")
37 | return resData
38 | }
39 |
40 | func (m *sysNoticeModelImpl) FindListPage(page, pageSize int, data entity.SysNotice) (*[]entity.SysNotice, int64) {
41 | list := make([]entity.SysNotice, 0)
42 | var total int64 = 0
43 | offset := pageSize * (page - 1)
44 | db := global.Db.Table(m.table)
45 | // 此处填写 where参数判断
46 | if data.Title != "" {
47 | db = db.Where("title like ?", "%"+data.Title+"%")
48 | }
49 | if data.NoticeType != "" {
50 | db = db.Where("notice_type = ?", data.NoticeType)
51 | }
52 | if len(data.DeptIds) > 0 {
53 | db = db.Where("dept_id in (?)", data.DeptIds)
54 | }
55 | db.Where("delete_time IS NULL")
56 | err := db.Count(&total).Error
57 | err = db.Order("create_time").Limit(pageSize).Offset(offset).Find(&list).Error
58 | biz.ErrIsNil(err, "查询通知分页列表失败")
59 | return &list, total
60 | }
61 |
62 | func (m *sysNoticeModelImpl) Update(data entity.SysNotice) *entity.SysNotice {
63 | biz.ErrIsNil(global.Db.Table(m.table).Updates(&data).Error, "修改通知失败")
64 | return &data
65 | }
66 |
67 | func (m *sysNoticeModelImpl) Delete(postIds []int64) {
68 | biz.ErrIsNil(global.Db.Table(m.table).Delete(&entity.SysNotice{}, "notice_id in (?)", postIds).Error, "删除通知失败")
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/tool/local.go:
--------------------------------------------------------------------------------
1 | package tool
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "errors"
7 | "io"
8 | "mime/multipart"
9 | "os"
10 | "pandax/pkg/global"
11 | "path"
12 | "strings"
13 | "time"
14 | )
15 |
16 | type Local struct {
17 | Path string
18 | }
19 |
20 | //@object: *Local
21 | //@function: UploadFile
22 | //@description: 上传文件
23 | //@param: file *multipart.FileHeader
24 | //@return: string, string, error
25 |
26 | func (local *Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
27 | // 读取文件后缀
28 | ext := path.Ext(file.Filename)
29 | // 读取文件名并加密
30 | name := strings.TrimSuffix(file.Filename, ext)
31 | name = MD5V([]byte(name))
32 | // 拼接新文件名
33 | filename := name + "_" + time.Now().Format("20060102150405") + ext
34 | // 尝试创建此路径
35 | mkdirErr := os.MkdirAll(local.Path, os.ModePerm)
36 | if mkdirErr != nil {
37 | global.Log.Error("function os.MkdirAll() Filed", mkdirErr.Error())
38 | return "", "", errors.New("function os.MkdirAll() Filed, err:" + mkdirErr.Error())
39 | }
40 | // 拼接路径和文件名
41 | p := local.Path + "/" + filename
42 |
43 | f, openError := file.Open() // 读取文件
44 | if openError != nil {
45 | global.Log.Error("function file.Open() Filed", openError.Error())
46 | return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())
47 | }
48 | defer f.Close() // 创建文件 defer 关闭
49 |
50 | out, createErr := os.Create(p)
51 | if createErr != nil {
52 | global.Log.Error("function os.Create() Filed", createErr.Error())
53 | return "", "", errors.New("function os.Create() Filed, err:" + createErr.Error())
54 | }
55 | defer out.Close() // 创建文件 defer 关闭
56 |
57 | _, copyErr := io.Copy(out, f) // 传输(拷贝)文件
58 | if copyErr != nil {
59 | global.Log.Error("function io.Copy() Filed", copyErr.Error())
60 | return "", "", errors.New("function io.Copy() Filed, err:" + copyErr.Error())
61 | }
62 | return p, filename, nil
63 | }
64 |
65 | //@object: *Local
66 | //@function: DeleteFile
67 | //@description: 删除文件
68 | //@param: key string
69 | //@return: error
70 |
71 | func (local *Local) DeleteFile(key string) error {
72 | p := local.Path + "/" + key
73 | if strings.Contains(p, local.Path) {
74 | if err := os.Remove(p); err != nil {
75 | return errors.New("本地文件删除失败, err:" + err.Error())
76 | }
77 | }
78 | return nil
79 | }
80 |
81 | func MD5V(str []byte) string {
82 | h := md5.New()
83 | h.Write(str)
84 | return hex.EncodeToString(h.Sum(nil))
85 | }
86 |
--------------------------------------------------------------------------------
/apps/log/services/log_login.go:
--------------------------------------------------------------------------------
1 | package services
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/biz"
5 | "pandax/apps/log/entity"
6 | "pandax/pkg/global"
7 | )
8 |
9 | type (
10 | LogLoginModel interface {
11 | Insert(data entity.LogLogin) *entity.LogLogin
12 | FindOne(infoId int64) *entity.LogLogin
13 | FindListPage(page, pageSize int, data entity.LogLogin) (*[]entity.LogLogin, int64)
14 | Update(data entity.LogLogin) *entity.LogLogin
15 | Delete(infoId []int64)
16 | DeleteAll()
17 | }
18 |
19 | logLoginModelImpl struct {
20 | table string
21 | }
22 | )
23 |
24 | var LogLoginModelDao LogLoginModel = &logLoginModelImpl{
25 | table: `log_logins`,
26 | }
27 |
28 | func (m *logLoginModelImpl) Insert(data entity.LogLogin) *entity.LogLogin {
29 | data.CreateBy = "0"
30 | data.UpdateBy = "0"
31 | global.Db.Table(m.table).Create(&data)
32 | return &data
33 | }
34 |
35 | func (m *logLoginModelImpl) FindOne(infoId int64) *entity.LogLogin {
36 | resData := new(entity.LogLogin)
37 | err := global.Db.Table(m.table).Where("info_id = ?", infoId).First(resData).Error
38 | biz.ErrIsNil(err, "查询登录日志信息失败")
39 | return resData
40 | }
41 |
42 | func (m *logLoginModelImpl) FindListPage(page, pageSize int, data entity.LogLogin) (*[]entity.LogLogin, int64) {
43 | list := make([]entity.LogLogin, 0)
44 | var total int64 = 0
45 | offset := pageSize * (page - 1)
46 | db := global.Db.Table(m.table)
47 | // 此处填写 where参数判断
48 | if data.Status != "" {
49 | db = db.Where("status = ?", data.Status)
50 | }
51 | if data.LoginLocation != "" {
52 | db = db.Where("login_location like ?", "%"+data.LoginLocation+"%")
53 | }
54 | if data.Username != "" {
55 | db = db.Where("username like ?", "%"+data.Username+"%")
56 | }
57 | err := db.Where("delete_time IS NULL").Count(&total).Error
58 | err = db.Order("info_id desc").Limit(pageSize).Offset(offset).Find(&list).Error
59 |
60 | biz.ErrIsNil(err, "查询登录分页日志信息失败")
61 | return &list, total
62 | }
63 |
64 | func (m *logLoginModelImpl) Update(data entity.LogLogin) *entity.LogLogin {
65 | err := global.Db.Table(m.table).Updates(&data).Error
66 | biz.ErrIsNil(err, "修改登录日志信息失败")
67 | return &data
68 | }
69 |
70 | func (m *logLoginModelImpl) Delete(infoIds []int64) {
71 | err := global.Db.Table(m.table).Delete(&entity.LogLogin{}, "info_id in (?)", infoIds).Error
72 | biz.ErrIsNil(err, "删除登录日志信息失败")
73 | return
74 | }
75 |
76 | func (m *logLoginModelImpl) DeleteAll() {
77 | global.Db.Exec("DELETE FROM log_logins")
78 | }
79 |
--------------------------------------------------------------------------------
/apps/system/api/post.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/PandaXGO/PandaKit/biz"
7 | "github.com/PandaXGO/PandaKit/model"
8 | "github.com/PandaXGO/PandaKit/restfulx"
9 | "github.com/PandaXGO/PandaKit/utils"
10 | "pandax/apps/system/entity"
11 | "pandax/apps/system/services"
12 | "pandax/pkg/global"
13 | )
14 |
15 | type PostApi struct {
16 | PostApp services.SysPostModel
17 | UserApp services.SysUserModel
18 | RoleApp services.SysRoleModel
19 | }
20 |
21 | // GetPostList 职位列表数据
22 | func (p *PostApi) GetPostList(rc *restfulx.ReqCtx) {
23 |
24 | pageNum := restfulx.QueryInt(rc, "pageNum", 1)
25 | pageSize := restfulx.QueryInt(rc, "pageSize", 10)
26 | status := restfulx.QueryParam(rc, "status")
27 | postName := restfulx.QueryParam(rc, "postName")
28 | postCode := restfulx.QueryParam(rc, "postCode")
29 | post := entity.SysPost{Status: status, PostName: postName, PostCode: postCode}
30 |
31 | list, total := p.PostApp.FindListPage(pageNum, pageSize, post)
32 |
33 | rc.ResData = model.ResultPage{
34 | Total: total,
35 | PageNum: int64(pageNum),
36 | PageSize: int64(pageNum),
37 | Data: list,
38 | }
39 | }
40 |
41 | // GetPost 获取职位
42 | func (p *PostApi) GetPost(rc *restfulx.ReqCtx) {
43 | postId := restfulx.PathParamInt(rc, "postId")
44 | p.PostApp.FindOne(int64(postId))
45 | }
46 |
47 | // InsertPost 添加职位
48 | func (p *PostApi) InsertPost(rc *restfulx.ReqCtx) {
49 | var post entity.SysPost
50 | restfulx.BindJsonAndValid(rc, &post)
51 | post.CreateBy = rc.LoginAccount.UserName
52 | p.PostApp.Insert(post)
53 | }
54 |
55 | // UpdatePost 修改职位
56 | func (p *PostApi) UpdatePost(rc *restfulx.ReqCtx) {
57 | var post entity.SysPost
58 | restfulx.BindJsonAndValid(rc, &post)
59 |
60 | post.CreateBy = rc.LoginAccount.UserName
61 | p.PostApp.Update(post)
62 | }
63 |
64 | // DeletePost 删除职位
65 | func (p *PostApi) DeletePost(rc *restfulx.ReqCtx) {
66 | postId := restfulx.PathParam(rc, "postId")
67 | postIds := utils.IdsStrToIdsIntGroup(postId)
68 |
69 | deList := make([]int64, 0)
70 | for _, id := range postIds {
71 | user := entity.SysUser{}
72 | user.PostId = id
73 | list := p.UserApp.FindList(user)
74 | if len(*list) == 0 {
75 | deList = append(deList, id)
76 | } else {
77 | global.Log.Info(fmt.Sprintf("dictId: %d 存在岗位绑定用户无法删除", id))
78 | }
79 | }
80 | if len(deList) == 0 {
81 | biz.ErrIsNil(errors.New("所有岗位都已绑定用户,无法删除"), "所有岗位都已绑定用户,无法删除")
82 | }
83 | p.PostApp.Delete(deList)
84 | }
85 |
--------------------------------------------------------------------------------
/pkg/initialize/router.go:
--------------------------------------------------------------------------------
1 | package initialize
2 |
3 | import (
4 | "pandax/apps/job/jobs"
5 | ruleRouter "pandax/apps/rule/router"
6 | "pandax/pkg/global"
7 | "pandax/pkg/transport"
8 |
9 | devRouter "pandax/apps/develop/router"
10 | deviceRouter "pandax/apps/device/router"
11 | jobRouter "pandax/apps/job/router"
12 | logRouter "pandax/apps/log/router"
13 | sysRouter "pandax/apps/system/router"
14 | "pandax/pkg/middleware"
15 | )
16 |
17 | func InitRouter() *transport.HttpServer {
18 | // server配置
19 | serverConfig := global.Conf.Server
20 | server := transport.NewHttpServer(serverConfig.GetPort())
21 |
22 | container := server.Container
23 | // 防止XSS
24 | container.Filter(middleware.EscapeHTML)
25 | // 是否允许跨域
26 | if serverConfig.Cors {
27 | container.Filter(middleware.Cors(container).Filter)
28 | }
29 | // 流量限制
30 | if serverConfig.Rate.Enable {
31 | container.Filter(middleware.Rate)
32 | }
33 | // 设置路由组
34 | {
35 | sysRouter.InitSystemRouter(container)
36 | sysRouter.InitDeptRouter(container)
37 | sysRouter.InitConfigRouter(container)
38 | sysRouter.InitApiRouter(container)
39 | sysRouter.InitDictRouter(container)
40 | sysRouter.InitMenuRouter(container)
41 | sysRouter.InitRoleRouter(container)
42 | sysRouter.InitPostRouter(container)
43 | sysRouter.InitUserRouter(container)
44 | sysRouter.InitNoticeRouter(container)
45 | //本地图片上传接口
46 | sysRouter.InitUploadRouter(container)
47 | }
48 | //日志系统
49 | {
50 | logRouter.InitOperLogRouter(container)
51 | logRouter.InitLoginLogRouter(container)
52 | }
53 | // 代码生成
54 | {
55 | devRouter.InitGenTableRouter(container)
56 | devRouter.InitGenRouter(container)
57 | }
58 | //设备
59 | {
60 | deviceRouter.InitProductCategoryRouter(container)
61 | deviceRouter.InitDeviceGroupRouter(container)
62 | deviceRouter.InitProductRouter(container)
63 | deviceRouter.InitProductTemplateRouter(container)
64 | deviceRouter.InitProductOtaRouter(container)
65 | deviceRouter.InitDeviceRouter(container)
66 | deviceRouter.InitDeviceAlarmRouter(container)
67 | deviceRouter.InitDeviceCmdLogRouter(container)
68 | }
69 | {
70 | jobRouter.InitJobRouter(container)
71 | jobRouter.InitJobLogRouter(container)
72 | }
73 | {
74 | ruleRouter.InitRuleChainRouter(container)
75 | ruleRouter.InitRuleChainMsgLogRouter(container)
76 | }
77 | // api接口
78 | middleware.SwaggerConfig(container)
79 | // 开启调度任务
80 | go func() {
81 | jobs.InitJob()
82 | }()
83 |
84 | return server
85 | }
86 |
--------------------------------------------------------------------------------
/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 | restfulspec "github.com/emicklei/go-restful-openapi/v2"
7 | "github.com/emicklei/go-restful/v3"
8 | "pandax/apps/log/api"
9 | "pandax/apps/log/entity"
10 | "pandax/apps/log/services"
11 | )
12 |
13 | func InitOperLogRouter(container *restful.Container) {
14 | // 操作日志
15 | s := &api.LogOperApi{
16 | LogOperApp: services.LogOperModelDao,
17 | }
18 |
19 | ws := new(restful.WebService)
20 | ws.Path("/log/logOper").Produces(restful.MIME_JSON)
21 | tags := []string{"logOper"}
22 |
23 | ws.Route(ws.GET("/list").To(func(request *restful.Request, response *restful.Response) {
24 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志列表").Handle(s.GetOperLogList)
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("businessType", "businessType").DataType("string")).
30 | Param(ws.QueryParameter("operName", "operName").DataType("string")).
31 | Param(ws.QueryParameter("title", "title").DataType("string")).
32 | Metadata(restfulspec.KeyOpenAPITags, tags).
33 | Writes(model.ResultPage{}).
34 | Returns(200, "OK", model.ResultPage{}))
35 |
36 | ws.Route(ws.GET("/{operId}").To(func(request *restful.Request, response *restful.Response) {
37 | restfulx.NewReqCtx(request, response).WithLog("获取操作日志信息").Handle(s.GetOperLog)
38 | }).
39 | Doc("获取操作日志信息").
40 | Param(ws.PathParameter("operId", "Id").DataType("int")).
41 | Metadata(restfulspec.KeyOpenAPITags, tags).
42 | Writes(entity.LogOper{}). // on the response
43 | Returns(200, "OK", entity.LogOper{}).
44 | Returns(404, "Not Found", nil))
45 |
46 | ws.Route(ws.DELETE("/{operId}").To(func(request *restful.Request, response *restful.Response) {
47 | restfulx.NewReqCtx(request, response).WithLog("删除操作日志信息").Handle(s.DeleteOperLog)
48 | }).
49 | Doc("删除操作日志信息").
50 | Metadata(restfulspec.KeyOpenAPITags, tags).
51 | Param(ws.PathParameter("operId", "多id 1,2,3").DataType("string")))
52 |
53 | ws.Route(ws.DELETE("/all").To(func(request *restful.Request, response *restful.Response) {
54 | restfulx.NewReqCtx(request, response).WithLog("清空操作日志信息").Handle(s.DeleteAll)
55 | }).
56 | Doc("清空操作日志信息").
57 | Metadata(restfulspec.KeyOpenAPITags, tags))
58 |
59 | container.Add(ws)
60 | }
61 |
--------------------------------------------------------------------------------
/apps/system/api/system.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "github.com/PandaXGO/PandaKit/biz"
6 | "github.com/PandaXGO/PandaKit/restfulx"
7 | "github.com/PandaXGO/PandaKit/ws"
8 | "github.com/emicklei/go-restful/v3"
9 | "github.com/gorilla/websocket"
10 | "github.com/kakuilan/kgo"
11 | "pandax/pkg/middleware"
12 | "runtime"
13 | )
14 |
15 | type System struct{}
16 |
17 | const (
18 | B = 1
19 | KB = 1024 * B
20 | MB = 1024 * KB
21 | GB = 1024 * MB
22 | )
23 |
24 | func (s *System) ServerInfo(request *restful.Request, response *restful.Response) {
25 | osDic := make(map[string]any, 0)
26 | osDic["goOs"] = runtime.GOOS
27 | osDic["arch"] = runtime.GOARCH
28 | osDic["mem"] = runtime.MemProfileRate
29 | osDic["compiler"] = runtime.Compiler
30 | osDic["version"] = runtime.Version()
31 | osDic["numGoroutine"] = runtime.NumGoroutine()
32 |
33 | info := kgo.KOS.GetSystemInfo()
34 | diskDic := make(map[string]any, 0)
35 | diskDic["total"] = info.DiskTotal / GB
36 | diskDic["free"] = info.DiskFree / GB
37 | diskDic["used"] = info.DiskUsed / GB
38 | diskDic["progress"] = int64((float64(info.DiskUsed) / float64(info.DiskTotal)) * 100)
39 |
40 | memDic := make(map[string]any, 0)
41 | memDic["total"] = info.MemTotal / GB
42 | memDic["used"] = info.MemUsed / GB
43 | memDic["free"] = info.MemFree / GB
44 | memDic["progress"] = int64((float64(info.MemUsed) / float64(info.MemTotal)) * 100)
45 |
46 | cpuDic := make(map[string]any, 0)
47 | cpuDic["num"] = info.CpuNum
48 | cpuDic["used"] = fmt.Sprintf("%.2f", info.CpuUser*100)
49 | cpuDic["free"] = fmt.Sprintf("%.2f", info.CpuFree*100)
50 |
51 | response.WriteEntity(map[string]any{
52 | "code": 200,
53 | "os": osDic,
54 | "mem": memDic,
55 | "cpu": cpuDic,
56 | "disk": diskDic,
57 | })
58 | }
59 |
60 | // ConnectWs 连接websocket
61 | func (s *System) ConnectWs(request *restful.Request, response *restful.Response) {
62 | wsConn, err := ws.Upgrader.Upgrade(response, request.Request, nil)
63 | defer func() {
64 | if err := recover(); &err != nil {
65 | wsConn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("websocket 失败: %v", err)))
66 | wsConn.Close()
67 | }
68 | }()
69 |
70 | if err != nil {
71 | panic(any(biz.NewBizErr("升级websocket失败")))
72 | }
73 | // 权限校验
74 | rc := restfulx.NewReqCtx(request, response)
75 | if err = middleware.PermissionHandler(rc); err != nil {
76 | panic(any(biz.NewBizErr("没有权限")))
77 | }
78 |
79 | // 登录账号信息
80 | la := rc.LoginAccount
81 | ws.Put(uint64(la.UserId), wsConn)
82 | }
83 |
--------------------------------------------------------------------------------
/apps/device/api/device_group.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "github.com/PandaXGO/PandaKit/restfulx"
5 | "github.com/kakuilan/kgo"
6 | "pandax/apps/device/entity"
7 | "pandax/apps/device/services"
8 | "strings"
9 | )
10 |
11 | type DeviceGroupApi struct {
12 | DeviceGroupApp services.DeviceGroupModel
13 | }
14 |
15 | // GetDeviceGroupTree DeviceGroup 树
16 | func (p *DeviceGroupApi) GetDeviceGroupTree(rc *restfulx.ReqCtx) {
17 | name := restfulx.QueryParam(rc, "name")
18 | status := restfulx.QueryParam(rc, "status")
19 | id := restfulx.QueryParam(rc, "id")
20 |
21 | sg := entity.DeviceGroup{Name: name, Status: status}
22 | sg.Id = id
23 | rc.ResData = p.DeviceGroupApp.SelectDeviceGroup(sg)
24 | }
25 |
26 | func (p *DeviceGroupApi) GetDeviceGroupTreeLabel(rc *restfulx.ReqCtx) {
27 | sg := entity.DeviceGroup{}
28 | rc.ResData = p.DeviceGroupApp.SelectDeviceGroupLabel(sg)
29 | }
30 |
31 | func (p *DeviceGroupApi) GetDeviceGroupList(rc *restfulx.ReqCtx) {
32 | name := restfulx.QueryParam(rc, "name")
33 | status := restfulx.QueryParam(rc, "status")
34 | id := restfulx.QueryParam(rc, "id")
35 |
36 | sg := entity.DeviceGroup{Name: name, Status: status}
37 | sg.Id = id
38 | if sg.Name == "" {
39 | rc.ResData = p.DeviceGroupApp.SelectDeviceGroup(sg)
40 | } else {
41 | rc.ResData = p.DeviceGroupApp.FindList(sg)
42 | }
43 | }
44 |
45 | // GetDeviceGroupAllList 查询所有
46 | func (p *DeviceGroupApi) GetDeviceGroupAllList(rc *restfulx.ReqCtx) {
47 | var vsg entity.DeviceGroup
48 | rc.ResData = p.DeviceGroupApp.FindList(vsg)
49 | }
50 |
51 | // GetDeviceGroup 获取DeviceGroup
52 | func (p *DeviceGroupApi) GetDeviceGroup(rc *restfulx.ReqCtx) {
53 | id := restfulx.PathParam(rc, "id")
54 | rc.ResData = p.DeviceGroupApp.FindOne(id)
55 | }
56 |
57 | // InsertDeviceGroup 添加DeviceGroup
58 | func (p *DeviceGroupApi) InsertDeviceGroup(rc *restfulx.ReqCtx) {
59 | var data entity.DeviceGroup
60 | restfulx.BindJsonAndValid(rc, &data)
61 | data.Id = kgo.KStr.Uniqid("dg_")
62 | data.Owner = rc.LoginAccount.UserName
63 | p.DeviceGroupApp.Insert(data)
64 | }
65 |
66 | // UpdateDeviceGroup 修改DeviceGroup
67 | func (p *DeviceGroupApi) UpdateDeviceGroup(rc *restfulx.ReqCtx) {
68 | var data entity.DeviceGroup
69 | restfulx.BindJsonAndValid(rc, &data)
70 |
71 | p.DeviceGroupApp.Update(data)
72 | }
73 |
74 | // DeleteDeviceGroup 删除DeviceGroup
75 | func (p *DeviceGroupApi) DeleteDeviceGroup(rc *restfulx.ReqCtx) {
76 | id := restfulx.PathParam(rc, "id")
77 | ids := strings.Split(id, ",")
78 | p.DeviceGroupApp.Delete(ids)
79 | }
80 |
--------------------------------------------------------------------------------
/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 []DefineStruct `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" dc:"数据类型" v:"required#请选择数据类型"` // 类型
54 | DefineBase // 参数
55 | }
56 |
57 | // DefineCommands 命令
58 | type DefineCommands struct {
59 | Key string `json:"key" dc:"功能标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入功能标识|标识由字母、数字和下划线组成,且不能以数字开头"`
60 | Name string `json:"name" dc:"功能名称" v:"required#请输入功能名称"` // 功能名称
61 | Inputs []DefineCommandsInput `json:"inputs" dc:"输入参数"` // 输入参数
62 | Output ValueType `json:"output" dc:"输出参数"` // 输出参数
63 | Desc string `json:"desc" dc:"描述"` // 描述
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 |
--------------------------------------------------------------------------------
/pkg/rule_engine/message/message1.go:
--------------------------------------------------------------------------------
1 | package message
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/google/uuid"
6 | "time"
7 | )
8 |
9 | // 消息类型
10 | const (
11 | ConnectMes = "Connect"
12 | DisConnectMes = "Disconnect"
13 | RpcRequestMes = "RpcRequest"
14 | UpEventMes = "Event"
15 | AlarmMes = "Alarm"
16 | RowMes = "Row"
17 | TelemetryMes = "Telemetry"
18 | AttributesMes = "Attributes"
19 | )
20 |
21 | // 数据类型Originator
22 | const (
23 | DEVICE = "DEVICE"
24 | GATEWAY = "GATEWAY"
25 | MONITOR = "MONITOR" //监控
26 | )
27 |
28 | type Msg map[string]interface{}
29 | type Metadata map[string]interface{}
30 |
31 | type Message struct {
32 | Id string //uuid 消息Id
33 | Ts time.Time //时间戳
34 | MsgType string //消息类型, attributes(参数),telemetry(遥测),Connect连接事件
35 | UserId string //客户Id UUID 设备发布人
36 | Msg Msg //数据 数据结构JSON 设备原始数据 msg
37 | Metadata Metadata //消息的元数据 包括设备Id,设备类型,产品ID等
38 | }
39 |
40 | // NewMessage ...
41 | func NewMessage(userId, messageType string, msg Msg, metadata Metadata) *Message {
42 | return &Message{
43 | Id: uuid.New().String(),
44 | Ts: time.Now(),
45 | UserId: userId,
46 | MsgType: messageType,
47 | Msg: msg,
48 | Metadata: metadata,
49 | }
50 | }
51 |
52 | func (t *Message) GetAllMap() map[string]interface{} {
53 | data := make(map[string]interface{})
54 | for msgKey, msgValue := range t.Msg {
55 | for metaKey, metaValue := range t.Metadata {
56 | if msgKey == metaKey {
57 | data[msgKey] = metaValue
58 | } else {
59 | if _, ok := data[msgKey]; !ok {
60 | data[msgKey] = msgValue
61 | }
62 | if _, ok := data[metaKey]; !ok {
63 | data[metaKey] = metaValue
64 | }
65 | }
66 | }
67 | }
68 | return data
69 | }
70 |
71 | func (t *Message) MarshalBinary() ([]byte, error) {
72 | return json.Marshal(t)
73 | }
74 |
75 | func (meta *Metadata) Keys() []string {
76 | keys := make([]string, 0)
77 | for key := range *meta {
78 | keys = append(keys, key)
79 | }
80 | return keys
81 | }
82 |
83 | func (meta *Metadata) GetValue(key string) any {
84 | if _, found := (*meta)[key]; !found {
85 | return nil
86 | }
87 | return (*meta)[key]
88 | }
89 |
90 | func (meta *Metadata) SetValue(key string, val interface{}) {
91 | (*meta)[key] = val
92 | }
93 |
94 | func (msg *Msg) GetValue(key string) any {
95 | if _, found := (*msg)[key]; !found {
96 | return nil
97 | }
98 | return (*msg)[key]
99 | }
100 |
--------------------------------------------------------------------------------
/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 | "github.com/sirupsen/logrus"
10 | )
11 |
12 | const DelayNodeName = "DelayNode"
13 |
14 | type delayNode struct {
15 | bareNode
16 | PeriodTs int `json:"periodTs" yaml:"periodTs" jpath:"periodTs"` //周期时间
17 | MaxPendingMessages int `json:"maxPendingMessages" yaml:"maxPendingMessages" jpath:"maxPendingMessages"` //最大等待消息数
18 | messageQueue []*message.Message `jpath:"-"`
19 | delayTimer *time.Timer `jpath:"-"`
20 | lock sync.Mutex `jpath:"-"`
21 | }
22 |
23 | type delayNodeFactory struct{}
24 |
25 | func (f delayNodeFactory) Name() string { return DelayNodeName }
26 | func (f delayNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
27 | func (f delayNodeFactory) Labels() []string { return []string{"Success", "Failure"} }
28 | func (f delayNodeFactory) Create(id string, meta Metadata) (Node, error) {
29 | node := &delayNode{
30 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
31 | lock: sync.Mutex{},
32 | }
33 | _, err := decodePath(meta, node)
34 | node.messageQueue = make([]*message.Message, node.MaxPendingMessages)
35 | return node, err
36 | }
37 |
38 | func (n *delayNode) Handle(msg *message.Message) error {
39 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
40 |
41 | successLabelNode := n.GetLinkedNode("Success")
42 | failureLabelNode := n.GetLinkedNode("Failure")
43 | if successLabelNode == nil || failureLabelNode == nil {
44 | return fmt.Errorf("no valid label linked node in %s", n.Name())
45 | }
46 |
47 | // check wethere the time had already been started, queue message if started
48 | if n.delayTimer == nil {
49 | n.messageQueue = append(n.messageQueue, msg)
50 | n.delayTimer = time.NewTimer(time.Duration(n.PeriodTs) * time.Second)
51 |
52 | go func(n *delayNode) error {
53 | defer n.delayTimer.Stop()
54 | for {
55 | <-n.delayTimer.C
56 | n.lock.Lock()
57 | defer n.lock.Unlock()
58 | if len(n.messageQueue) > 0 {
59 | msg := n.messageQueue[0]
60 | n.messageQueue = n.messageQueue[0:]
61 | return successLabelNode.Handle(msg)
62 | }
63 | }
64 | }(n)
65 | return nil
66 | }
67 | n.lock.Lock()
68 | defer n.lock.Unlock()
69 | if len(n.messageQueue) == n.MaxPendingMessages {
70 | return failureLabelNode.Handle(msg)
71 | }
72 | n.messageQueue = append(n.messageQueue, msg)
73 | return nil
74 | }
75 |
--------------------------------------------------------------------------------
/pkg/rule_engine/nodes/action_create_alarm_node.go:
--------------------------------------------------------------------------------
1 | package nodes
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/kakuilan/kgo"
6 | "github.com/sirupsen/logrus"
7 | "pandax/apps/device/entity"
8 | "pandax/apps/device/services"
9 | "pandax/pkg/global"
10 | "pandax/pkg/rule_engine/message"
11 | "time"
12 | )
13 |
14 | type createAlarmNode struct {
15 | bareNode
16 | AlarmType string `json:"alarmType" yaml:"alarmType"`
17 | AlarmSeverity string `json:"alarmSeverity" yaml:"alarmSeverity"`
18 | }
19 |
20 | type createAlarmNodeFactory struct{}
21 |
22 | func (f createAlarmNodeFactory) Name() string { return "CreateAlarmNode" }
23 | func (f createAlarmNodeFactory) Category() string { return NODE_CATEGORY_ACTION }
24 | func (f createAlarmNodeFactory) Labels() []string { return []string{"Created", "Updated", "Failure"} }
25 | func (f createAlarmNodeFactory) Create(id string, meta Metadata) (Node, error) {
26 | node := &createAlarmNode{
27 | bareNode: newBareNode(f.Name(), id, meta, f.Labels()),
28 | }
29 | return decodePath(meta, node)
30 | }
31 |
32 | func (n *createAlarmNode) Handle(msg *message.Message) error {
33 | logrus.Infof("%s handle message '%s'", n.Name(), msg.MsgType)
34 |
35 | created := n.GetLinkedNode("Created")
36 | updated := n.GetLinkedNode("Updated")
37 | failure := n.GetLinkedNode("Failure")
38 |
39 | alarm := services.DeviceAlarmModelDao.FindOneByType(msg.Metadata.GetValue("deviceId").(string), n.AlarmType, "0")
40 | if alarm.DeviceId != "" {
41 | marshal, _ := json.Marshal(msg.Msg)
42 | alarm.Details = string(marshal)
43 | err := services.DeviceAlarmModelDao.Update(*alarm)
44 | if err != nil {
45 | if failure != nil {
46 | return failure.Handle(msg)
47 | }
48 | } else {
49 | if updated != nil {
50 | return updated.Handle(msg)
51 | }
52 | }
53 | } else {
54 | alarm = &entity.DeviceAlarm{}
55 | alarm.Id = kgo.KStr.Uniqid("a")
56 | alarm.DeviceId = msg.Metadata.GetValue("deviceId").(string)
57 | alarm.ProductId = msg.Metadata.GetValue("productId").(string)
58 | alarm.Name = msg.Metadata.GetValue("deviceName").(string)
59 | alarm.Level = n.AlarmSeverity
60 | alarm.State = global.ALARMING
61 | alarm.Type = n.AlarmType
62 | alarm.Time = time.Now()
63 | marshal, _ := json.Marshal(msg.Msg)
64 | alarm.Details = string(marshal)
65 | err := services.DeviceAlarmModelDao.Insert(*alarm)
66 | if err != nil {
67 | if failure != nil {
68 | return failure.Handle(msg)
69 | }
70 | } else {
71 | if created != nil {
72 | return created.Handle(msg)
73 | }
74 | }
75 | }
76 | return nil
77 | }
78 |
--------------------------------------------------------------------------------