zhangzengfei
2023-10-08 2cd1af13bc4e7aec4c85b9fe88db2d294af6468f
修复集群同步功能
13个文件已修改
305 ■■■■■ 已修改文件
sysinfo-service/main.go 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/controllers/cluster.go 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/main.go 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/models/cluster.go 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/models/db.go 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/models/defHeadPic.go 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/serf/dbLogger.go 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/serf/handler.go 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/serf/serf.go 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/serf/sync.go 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/service/SysService.go 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/service/clusterService.go 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
system-service/service/proc.go 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sysinfo-service/main.go
@@ -76,6 +76,7 @@
}
const urlPrefix = "/data/api-v"
func initFuncMap() (map[string]bhomeclient.MicroFunc, []string) {
    funcMap := make(map[string]bhomeclient.MicroFunc)
@@ -87,5 +88,6 @@
    for key,_ := range funcMap {
        pubTopics = append(pubTopics, key)
    }
    return funcMap, pubTopics
}
system-service/controllers/cluster.go
@@ -1,6 +1,7 @@
package controllers
import (
    "vamicro/config"
    "vamicro/system-service/models"
    "vamicro/system-service/service"
    "vamicro/system-service/vo"
@@ -13,6 +14,48 @@
type ClusterController struct {
}
// @Summary 查询当前集群状态
// @Description 查询本地集群
// @Produce json
// @Tags cluster
// @Success 200 {string} json "{"code":200, success:true, msg:"", data:""}"
// @Failure 500 {string} json "{"code":500, success:false, msg:"",data:""}"
// @Router /data/api-v/cluster/status [get]
func (cc ClusterController) GetClusterStat(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply {
    var clusterE models.Cluster
    var reply = bhomeclient.Reply{
        Success: false,
        Msg:     "leave",
        Data:    nil,
    }
    arr, err := clusterE.FindAll()
    if err == nil {
        if arr != nil && len(arr) > 0 {
            // 表示已加入集群
            reply.Success = true
            var nodeE models.Node
            nodes, _ := nodeE.FindNodesByClusterId(arr[0].ClusterId)
            logger.Debug("查询集群节点:", nodes)
            for _, node := range nodes {
                logger.Debug("节点:", node.NodeId, " servceId:", config.Server.AnalyServerId, " stat:", node.DriftState)
                if node.NodeId == config.Server.AnalyServerId {
                    if node.DriftState == "master" {
                        reply.Msg = "master"
                    } else {
                        reply.Msg = "slave"
                    }
                    break
                }
            }
        }
    }
    return &reply
}
// @Summary 查询本地集群
// @Description 查询本地集群
// @Produce json
system-service/main.go
@@ -3,6 +3,7 @@
import (
    "context"
    "flag"
    "fmt"
    _ "net/http/pprof"
    "os"
    "os/signal"
@@ -60,7 +61,7 @@
        Proc:        *proc,
        Channel:     nil,
        PubTopic:    pubTopics,
        SubTopic:    []string{versionControlS.AuthorizationUpdateTopic},
        SubTopic:    []string{versionControlS.AuthorizationUpdateTopic, "sync-proc-message-to-serf"},
        SubNetTopic: []string{},
    }
@@ -74,7 +75,7 @@
    bhomedbapi.InitGetNetNode(ms.GetLocalNetNodeByTopic)
    bhomedbapi.InitDoReq(ms.RequestOnly)
    //bhomedbapi.InitLog(logger.Debug)
    bhomedbapi.InitLog(logger.Debug)
    //util.AuthCheck(ctx) //授权检查
@@ -178,6 +179,7 @@
    funcMap[urlPrefix+"/cluster/updateClusterName"] = clusterController.UpdateClusterName
    funcMap[urlPrefix+"/cluster/leave"] = clusterController.Leave
    funcMap[urlPrefix+"/cluster/findIpByNode"] = clusterController.FindIpByNode
    funcMap[urlPrefix+"/cluster/status"] = clusterController.GetClusterStat
    sysMenuC := new(controllers.SysMenuController)
    funcMap["/data/api-u/sysmenus/tree"] = sysMenuC.MenuTree
@@ -271,11 +273,13 @@
    for key, _ := range funcMap {
        pubTopics = append(pubTopics, key)
    }
    return funcMap, pubTopics
}
// 测试接收全网消息
func dealSubMsg(ctx context.Context, ms *bhomeclient.MicroNode) {
    fmt.Println("dealSubMsg")
    for {
        select {
        case <-ctx.Done():
system-service/models/cluster.go
@@ -1,6 +1,7 @@
package models
import (
    "errors"
    "fmt"
    "strconv"
    "time"
@@ -48,40 +49,45 @@
    return result, nil
}
func (c *Cluster) Create() bool {
func (c *Cluster) Create() error {
    var localConfig LocalConfig
    e := localConfig.Select()
    if e != nil {
        return false
    err := localConfig.Select()
    if err != nil {
        return err
    }
    serverId := config.Server.AnalyServerId
    if serverId == "" {
        return false
        return errors.New("serverId 为空")
    }
    serverIp, _, e := util.GetLocalIP(config.Server.NetworkAdapter)
    if e != nil {
        return false
        return e
    }
    var err error
    tx := db.Begin()
    defer func() {
        if err != nil && tx != nil {
            tx.Rollback()
        }
    }()
    sql := "insert into cluster (cluster_id,cluster_name,password,virtual_ip) values ('" + c.ClusterId + "','" + c.ClusterName + "','" + c.Password + "','" + c.VirtualIp + "')"
    if err = tx.Exec(sql).Error; err != nil {
        return false
        return err
    }
    timeUnix := time.Now().Unix()
    fmtTimeStr := time.Unix(timeUnix, 0).Format("2006-01-02 15:04:05")
    //添加本身节点
    sql = "insert into cluster_node (id,cluster_id,node_name,node_id,node_ip,create_time,isDelete,device_type) values ('" + serverId + "','" + c.ClusterId + "','" + localConfig.ServerName + "','" + serverId + "','" + (serverIp + ":" + strconv.Itoa(syncdb.DefaultBindPort)) + "','" + fmtTimeStr + "',0,'" + config.Server.DeviceType + "')"
    sql = "insert into cluster_node (id,cluster_id,node_name,node_id,node_ip,create_time,isDelete,device_type,drift_state) values ('" + serverId + "','" + c.ClusterId + "','" + localConfig.ServerName + "','" + serverId + "','" + (serverIp + ":" + strconv.Itoa(syncdb.DefaultBindPort)) + "','" + fmtTimeStr + "',0,'" + config.Server.DeviceType + "','master')"
    if err = tx.Exec(sql).Error; err != nil {
        return false
        return err
    }
    tx.Commit()
    return true
    return nil
}
func (c *Cluster) UpdateClusterName(clusterName string, virtualIp string) bool {
system-service/models/db.go
@@ -4,7 +4,6 @@
    "basic.com/valib/logger.git"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "strings"
    "vamicro/config"
)
@@ -42,17 +41,9 @@
        &AuthConfig{},
        &AuthDevice{},
    )
    InitSysSettingData()
    InitDefHeadPicData()
    initDic()
    //添加传感器字典配置
    addSensorDic()
    //初始化事件推送字典
    setDataPushDic()
    //初始化人脸跟踪的字典
    setFaceTrackDic()
}
// GetDB ...
@@ -62,61 +53,4 @@
func CloseDB() {
    db.Close()
}
func initDic() {
    db.Exec("INSERT INTO dictionary SELECT 'dcf9a925-dd1d-4dc4-a30d-002f976366a4', '1', '是', 'endRecord', '是', 1, '0' where not exists (select 1 from dictionary where id='dcf9a925-dd1d-4dc4-a30d-002f976366a4')")
    db.Exec("INSERT INTO dictionary SELECT 'ec3e1a20-c5fd-4d3c-a57b-2d10f114bb8f', '0', '否', 'endRecord', '否', 2, '0' where not exists (select 1 from dictionary where id='ec3e1a20-c5fd-4d3c-a57b-2d10f114bb8f')")
    db.Exec("INSERT INTO dictionary SELECT '4a4a1f9c-1431-4c56-ac95-12792ff51fd1', '1', '是', 'change', '是', 1, '0' where not exists (select 1 from dictionary where id='4a4a1f9c-1431-4c56-ac95-12792ff51fd1')")
    db.Exec("INSERT INTO dictionary SELECT '8c330403-c36d-440f-92a1-b4101abcc168', '0', '否', 'change', '否', 2, '0' where not exists (select 1 from dictionary where id='8c330403-c36d-440f-92a1-b4101abcc168')")
    //添加默认的通道设置
}
func addSensorDic() {
    db.Exec("INSERT INTO dictionary SELECT 'edded040-f9a4-4bbb-9eb1-23a01eaf595b', 'FaceTemperature', '温度传感器', 'sensorType', '人脸测温', 1, '0' where not exists (select 1 from dictionary where id='edded040-f9a4-4bbb-9eb1-23a01eaf595b')")
}
func setDataPushDic() {
    var sqls []string
    //sqls = append(sqls, "delete from dictionary where type='EVENTRULETOPIC';")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'B2C94522-9077-768C-595D-85502C6A315E', 'camera', '摄像机', 'EVENTRULETOPIC', '主题-摄像机', 1, '0' where not exists (select 1 from dictionary where id='B2C94522-9077-768C-595D-85502C6A315E');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E', 'dbtable', '底库', 'EVENTRULETOPIC', '主题-底库', 2, '0' where not exists (select 1 from dictionary where id='E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '213E9B4A-4E5F-8E56-8279-6C9045621068', 'task', '场景', 'EVENTRULETOPIC', '主题-场景', 3, '0' where not exists (select 1 from dictionary where id='213E9B4A-4E5F-8E56-8279-6C9045621068');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '83536D8B-83D2-59B1-655B-1636DBFD8201', 'alarmLevel', '事件等级', 'EVENTRULETOPIC', '主题-报警等级', 5, '0' where not exists (select 1 from dictionary where id='83536D8B-83D2-59B1-655B-1636DBFD8201');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '0F082502-CEA8-AAA5-FB43-DD555F0BE7D7', 'cameraName', '设备名称', 'EVENTRULETOPIC', 'custom,option', 1, 'B2C94522-9077-768C-595D-85502C6A315E' where not exists (select 1 from dictionary where id='0F082502-CEA8-AAA5-FB43-DD555F0BE7D7');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '1DEC0839-850A-C600-9070-69C871654CE6', 'cameraAddr', '设备地址', 'EVENTRULETOPIC', 'custom,option', 2, 'B2C94522-9077-768C-595D-85502C6A315E' where not exists (select 1 from dictionary where id='1DEC0839-850A-C600-9070-69C871654CE6');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '806E7B4A-9A42-4E79-8708-481BA748EEC8', 'alarmRules.#.alarmLevel', '级别', 'EVENTRULETOPIC', 'option', 0, '83536D8B-83D2-59B1-655B-1636DBFD8201' where not exists (select 1 from dictionary where id='806E7B4A-9A42-4E79-8708-481BA748EEC8');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '7BBDDF13-AF28-49B9-AFAF-78E535C7CDDB', 'baseInfo.#.tableName', '名称', 'EVENTRULETOPIC', '底库的子选项', 0, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='7BBDDF13-AF28-49B9-AFAF-78E535C7CDDB');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '1C9392CD-9FC4-4A23-8DCD-AE2B8E53AF49', 'baseInfo.#.targetName', '人员姓名', 'EVENTRULETOPIC', '底库的子选项', 1, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='1C9392CD-9FC4-4A23-8DCD-AE2B8E53AF49');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '7455827A-43A6-4A9D-9D6F-F2D030E3C7D9', 'baseInfo.#.monitorLevel', '人员等级', 'EVENTRULETOPIC', '底库的子选项', 2, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='7455827A-43A6-4A9D-9D6F-F2D030E3C7D9');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '950DA518-8D94-4293-A686-D85E8F94BD0B', 'baseInfo.#.labels.idCard', '身份证号', 'EVENTRULETOPIC', '底库的子选项', 3, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='950DA518-8D94-4293-A686-D85E8F94BD0B');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '7B72769F-CAFE-458B-8589-B792B4BF123C', 'baseInfo.#.labels.phone', '手机号', 'EVENTRULETOPIC', '底库的子选项', 4, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='7B72769F-CAFE-458B-8589-B792B4BF123C');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '58B67911-CF9F-4468-9B46-3DB9F7765816', 'baseInfo.#.labels.plate', '车牌号', 'EVENTRULETOPIC', '底库的子选项', 5, 'E945DFFC-CD7D-AEED-3F0B-7C88FCD9134E' where not exists (select 1 from dictionary where id='58B67911-CF9F-4468-9B46-3DB9F7765816');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '1EC955EA-E6A5-4CA6-AD9A-4769D3CE96D6', 'taskName', '名称', 'EVENTRULETOPIC', '场景的子选项', 5, '213E9B4A-4E5F-8E56-8279-6C9045621068' where not exists (select 1 from dictionary where id='1EC955EA-E6A5-4CA6-AD9A-4769D3CE96D6');")
    //sqls = append(sqls, "update dictionary set value='alarmRules.#.alarmLevel' where id='806E7B4A-9A42-4E79-8708-481BA748EEC8';")
    db.Exec(strings.Join(sqls, ""))
}
// 添加一些是否的字典值
func setFaceTrackDic() {
    var sqls []string
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '51739a24-20bd-4440-8ef5-47fe93200536', '1', '是', 'bForceSend', '是', 1, '0' where not exists (select 1 from dictionary where id='51739a24-20bd-4440-8ef5-47fe93200536');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'fd8f9b4b-679b-4504-a295-7bb64fcd9ec5', '0', '否', 'bForceSend', '否', 2, '0' where not exists (select 1 from dictionary where id='fd8f9b4b-679b-4504-a295-7bb64fcd9ec5');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'a25055c1-50be-4ed8-ab29-da670958fbd4', '1', '是', 'bForcePush', '是', 1, '0' where not exists (select 1 from dictionary where id='a25055c1-50be-4ed8-ab29-da670958fbd4');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '3c68a1fa-ae57-4e36-b418-719ef28db71e', '0', '否', 'bForcePush', '否', 2, '0' where not exists (select 1 from dictionary where id='3c68a1fa-ae57-4e36-b418-719ef28db71e');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '93d0d77a-ba85-47c7-867c-34f7fe1be553', '1', '是', 'bForceSave', '是', 1, '0' where not exists (select 1 from dictionary where id='93d0d77a-ba85-47c7-867c-34f7fe1be553');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '23dc5d8b-7894-4692-9042-2fa0aaf13375', '0', '否', 'bForceSave', '否', 2, '0' where not exists (select 1 from dictionary where id='23dc5d8b-7894-4692-9042-2fa0aaf13375');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'e1677c52-844a-4f03-afcd-391b477de87b', '1', '是', 'bReid', '是', 1, '0' where not exists (select 1 from dictionary where id='e1677c52-844a-4f03-afcd-391b477de87b');")
    // 进出入统计, 方向
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'f6e4104a-cb6b-41bc-b368-69c8a104466b', 'up', '上', 'inside', '上', 1, '0' where not exists (select 1 from dictionary where id='f6e4104a-cb6b-41bc-b368-69c8a104466b');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '7f588975-ab59-4ab5-9c9e-af92f64d5353', 'down', '下', 'inside', '下', 2, '0' where not exists (select 1 from dictionary where id='7f588975-ab59-4ab5-9c9e-af92f64d5353');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'b24196de-c891-42b2-bf4a-dabfb2870397', 'right', '左', 'inside', '左', 3, '0' where not exists (select 1 from dictionary where id='b24196de-c891-42b2-bf4a-dabfb2870397');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select 'b4535930-e1a9-41b1-a4e5-2b3831f37748', 'left', '右', 'inside', '右', 4, '0' where not exists (select 1 from dictionary where id='b4535930-e1a9-41b1-a4e5-2b3831f37748');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '1a691da7-989f-4492-958a-7e1f4a49b374', 'up', '上', 'outside', '上', 1, '0' where not exists (select 1 from dictionary where id='1a691da7-989f-4492-958a-7e1f4a49b374');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '34aadb9b-3132-4e60-a73f-dc4b6f67a619', 'down', '下', 'outside', '下', 2, '0' where not exists (select 1 from dictionary where id='34aadb9b-3132-4e60-a73f-dc4b6f67a619');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '04d56fa3-2285-470c-be42-a15d7ac4273f', 'right', '左', 'outside', '左', 3, '0' where not exists (select 1 from dictionary where id='04d56fa3-2285-470c-be42-a15d7ac4273f');")
    sqls = append(sqls, "INSERT INTO dictionary(`id`,`value`,`name`,`type`,`description`,`sort`,`parent_id`) select '5c7a68f2-dd0f-4c28-b703-f9626e0d19a2', 'left', '右', 'outside', '右', 4, '0' where not exists (select 1 from dictionary where id='5c7a68f2-dd0f-4c28-b703-f9626e0d19a2');")
    db.Exec(strings.Join(sqls, ""))
}
system-service/models/defHeadPic.go
@@ -1,6 +1,7 @@
package models
import "errors"
func InitDefHeadPicData() {
    var defHeadPic DefHeadPic
    defHeadPic.Id = "1.jpg"
@@ -34,6 +35,7 @@
    defHeadPic.Path = "/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMaaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzE0OCA3OS4xNjQwMzYsIDIwMTkvMDgvMTMtMDE6MDY6NTcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJEMTJEQ0REREU1MjExRUJCQUIzQ0RGOTc1M0ZBRkVGIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJEMTJEQ0RDREU1MjExRUJCQUIzQ0RGOTc1M0ZBRkVGIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCAyMDIwIFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0iREY3RDc0MUY4MTA4MkI2MjQ3OEEyQjBCODFCQzE2RTEiIHN0UmVmOmRvY3VtZW50SUQ9IkRGN0Q3NDFGODEwODJCNjI0NzhBMkIwQjgxQkMxNkUxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQ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"
    defHeadPic.Insert()
}
//报警声音表
type DefHeadPic struct {
    Id        string       `gorm:"column:id;primary_key;type:varchar(100);unique;" json:"id"`
@@ -45,7 +47,7 @@
}
func (v *DefHeadPic) Insert() (bool,error) {
    var tmp Voice
    var tmp DefHeadPic
    if tmp.Exist(v.Id) {
        return false, errors.New("文件不允许重名")
    }
@@ -59,8 +61,8 @@
    return false, errors.New("新增失败")
}
func (v *DefHeadPic) Exist(name string) bool {
    dbSelect := db.Table(v.TableName()).Where("name=?", name).First(&v)
func (v *DefHeadPic) Exist(id string) bool {
    dbSelect := db.Table(v.TableName()).Where("id=?", id).First(&v)
    if dbSelect.Error != nil || dbSelect.RowsAffected ==0 {
        return false
    }
@@ -74,4 +76,3 @@
    }
    return list,nil
}
system-service/serf/dbLogger.go
@@ -13,28 +13,15 @@
}
var SyncTables = []string{
    //"area",
    //"camera_area",
    //"cameras",
    //"gb28181_config",
    //"dbtablepersons",
    //"dbtables",
    "cluster",
    "cluster_node",
    "dictionary",
    "auth_config", //设备管理授权配置
    "t_device",     //设备信息表
    "t_device_app", //设备安装的app
    "t_device_sdk", //设备安装的sdk
}
func (dbLogger *DbLogger) Print(values ...interface{}) {
    var (
        level = values[0]
    )
    if level == "sql" {
        msgArr := gorm.LogFormatter(values...)
        sql := msgArr[3].(string)
system-service/serf/handler.go
@@ -77,6 +77,23 @@
    logger.Info("LTime:", ev.LTime, " Recevie virtualIp change")
    SyncVirtualIpChan <- ev.Payload
}
func HandleUserEventSyncMessage(ev serf.UserEvent) {
    logger.Info("receive a UserEventSyncMessage event")
    var procMsg ProcMessageEvent
    err := json.Unmarshal(ev.Payload, &procMsg)
    if err != nil {
        logger.Error("sqlUe unmarshal err:", err)
        return
    }
    // 自己发送的消息不处理
    if procMsg.Owner != config.Server.AnalyServerId {
        // 判断是否有指定的接收目标
        if procMsg.Target == "" || procMsg.Target == config.Server.AnalyServerId {
            SyncProcMessageChan <- ev.Payload
        }
    }
}
//收到其它节点主动将注册中心的所有topic通知到集群中
func HandleSyncRegisterInfo(ev serf.UserEvent) {
system-service/serf/serf.go
@@ -23,11 +23,15 @@
    UserEventSyncVirtualIp          = "SyncVirtualIp"              //漂移ip修改
    UserEventSyncRegisterInfo       = "SyncRegisterInfo"           //同步注册信息
    DataSystemSerfSubscribe         = "data-system-serf-subscribe" //各app从serf订阅消息
    UserEventSyncMessage            = "SyncMessageForProc"         // 为其他进程同步消息
    TcpTransportPort                = 30194                        //tcp传输大数据量接口
    SUserEventSyncMessage
)
var SyncDbTablePersonCacheChan = make(chan []byte, 512)
var SyncVirtualIpChan = make(chan []byte, 512)
var SyncProcMessageChan = make(chan []byte, 512)
func HandleSerfEvent(event serf.Event) {
    switch ev := event.(type) {
@@ -42,6 +46,9 @@
            HandleSyncRegisterInfo(ev)
        } else if ev.Name == DataSystemSerfSubscribe {
            HandleDataSystemSerfSub(ev)
        } else if ev.Name == UserEventSyncMessage {
            logger.Debug("接收到SyncMessageForProc")
            HandleUserEventSyncMessage(ev)
        }
    case *serf.Query:
        if ev.Name == QueryEventUpdateDBData {
@@ -100,6 +107,14 @@
type SqlUserEvent struct {
    Owner string   `json:"owner"`
    Sql   []string `json:"sql"`
}
type ProcMessageEvent struct {
    Owner   string `json:"owner"`    // 发送者
    Target  string `json:"target"`   // 指定接收者
    Proc    string `json:"procName"` // 进程名
    Topic   string `json:"topic"`    // 主题
    Payload []byte `json:"payload"`  // 消息体,自行解析
}
type TableDesc struct {
@@ -193,7 +208,7 @@
    mbs := a.GroupMembers(clusterId)
    var specmembername string
    for _, m := range mbs {
        logger.Info("m", m)
        logger.Info("member", m)
        if m.Name != config.Server.AnalyServerId { //前缀:DSVAD:分析服务器 DSPAD:进出入pad
            if strings.HasPrefix(config.Server.AnalyServerId, "DSVAD") {
                if strings.HasPrefix(m.Name, "DSVAD") {
@@ -206,7 +221,7 @@
            }
        }
    }
    logger.Info("mbs:", mbs, "specmembername:", specmembername)
    logger.Info("members:", mbs, "specmembername:", specmembername)
    if specmembername == "" { //如果未找到目标节点,说明当前集群内除了本节点,没有其他可用节点
        return nil, errors.New("specmembername not found")
    }
@@ -245,8 +260,8 @@
                logger.Info("Query response's len:", len(msg))
                err := json.Unmarshal(msg, &dumpSqls)
                if err == nil {
                    logger.Error("dumpSql:", dumpSqls)
                    logger.Error("data dump success")
                    //logger.Error("dumpSql:", dumpSqls)
                    logger.Debug("data dump success")
                }
                return
            }
system-service/serf/sync.go
@@ -6,6 +6,7 @@
    "basic.com/valib/bhomeclient.git"
    "basic.com/valib/logger.git"
    "context"
    "encoding/json"
    "github.com/gogo/protobuf/proto"
    "nanomsg.org/go-mangos"
    "nanomsg.org/go-mangos/protocol/req"
@@ -112,6 +113,30 @@
            select {
            case <-ctx.Done():
                return
            case b := <-SyncProcMessageChan:
                {
                    var procMsg ProcMessageEvent
                    err := json.Unmarshal(b, &procMsg)
                    if err != nil {
                        logger.Error("Unmarshal ProcMessageEvent ", err.Error())
                    } else {
                        err = hms.Publish(procMsg.Topic, procMsg.Payload)
                        if err != nil {
                            logger.Error("hms.Publish error ", err.Error())
                        }
                    }
                }
            default:
                time.Sleep(50 * time.Millisecond)
            }
        }
    }()
    go func() {
        for {
            select {
            case <-ctx.Done():
                return
            case b := <-syncSdkCompareCacheChan:
                {
                    logger.Debug("SyncSdkCompareCache in,len(b):", len(b))
system-service/service/SysService.go
@@ -412,6 +412,11 @@
        if err := json.Unmarshal(payloads, &AuthInfo); nil != err {
            logger.Error("handleSubMsg failed to persistent:", topic, string(payloads))
        }
    }
    if "sync-proc-message-to-serf" == topic {
        logger.Debug("handleSubMsg sync-proc-message-to-serf")
        ClusterSyncProcMessage(payloads)
    }
}
system-service/service/clusterService.go
@@ -112,19 +112,21 @@
    arr, err := clusterE.FindAll()
    if err == nil && (arr == nil || len(arr) == 0) {
        b := clusterE.Create()
        if b {
        err = clusterE.Create()
        if err == nil {
            serf.InitAgent(context.Background())
            chMsg := protomsg.DbChangeMessage{
                Id:     clusterE.ClusterId,
                Table:  protomsg.TableChanged_T_Cluster,
                Action: protomsg.DbAction_Insert,
                Info:   "",
                Info:   "create",
            }
            s.AddDbMessage(&chMsg)
            return true, clusterId
        } else {
            logger.Error("初始化集群数据库信息失败. ", err.Error())
        }
    } else {
        if s.UpdateClusterName(clusterName, virtualIp) {
@@ -201,22 +203,21 @@
    agent, err := dbSync.Init(joinArg.ClusterId, joinArg.Password, config.Server.AnalyServerId, joinIps, config.ClusterSet.SerfSnapShotPath, conf)
    if err == nil && agent != nil { //加入成功
        logger.Debug("AddCluster dbSync.Init success")
        agent.RegisterHandleEventFunc(serf.HandleSerfEvent)
        serf.Agent = agent
        t := time.Now()
        syncTableDataFromCluster(joinArg)
        logger.Debugf("AddCluster  time=%v", time.Since(t))
        //if syncB {
        //    //需要重新初始化本地比对进程
        //    go serf.ReInitDbPersonCompareData()
        //    chMsg := protomsg.DbChangeMessage{
        //        Id:     joinArg.ClusterId,
        //        Table:  protomsg.TableChanged_T_Cluster,
        //        Action: protomsg.DbAction_Insert,
        //        Info:   "",
        //    }
        //
        //    s.AddDbMessage(&chMsg)
        chMsg := protomsg.DbChangeMessage{
            Id:     joinArg.ClusterId,
            Table:  protomsg.TableChanged_T_Cluster,
            Action: protomsg.DbAction_Insert,
            Info:   "join",
        }
        s.AddDbMessage(&chMsg)
        logger.Debugf("AddCluster 加入集群成功 time=%v", time.Since(start))
        return true, nil
        //} else {
@@ -332,7 +333,7 @@
            Id:     "",
            Table:  protomsg.TableChanged_T_Cluster,
            Action: protomsg.DbAction_Delete,
            Info:   "",
            Info:   "leave",
        }
        logger.Debugf("Leave delete db time=%v", time.Since(t))
        tm := time.Now()
@@ -341,6 +342,7 @@
    }
    logger.Debugf("Leave success time=%v", time.Since(start))
    return true, nil
}
@@ -374,6 +376,7 @@
    db := models.GetDB()
    db.LogMode(false)
    defer db.LogMode(true)
    tx := db.Begin()
    defer func() {
        if err != nil && tx != nil {
@@ -385,16 +388,12 @@
    tx.Exec("PRAGMA foreign_keys=OFF")
    //1.删除本地的同步库数据
    for _, t := range serf.SyncTables {
        delSql := ""
        if t == "dbtables" {
            delSql = "delete from " + t + " where (analyServerId='' or analyServerId=NULL)"
        } else if t == "dbtablepersons" {
            delSql = "delete from " + t + " where tableId in (select id from dbtables where (analyServerId='' or analyServerId=NULL))"
        } else {
            delSql = "delete from " + t + ""
        }
        delSql := "delete from " + t + ""
        err = tx.Exec(delSql).Error
        if err != nil {
            logger.Error("删除本地的同步库数据失败,", err.Error())
            logger.Error("sql:", delSql)
            return false
        }
    }
@@ -404,8 +403,9 @@
    dumpSqls, err = serf.GetTableDataFromCluster(serf.Agent, joinArg.ClusterId, serf.SyncTables, 20*time.Second)
    if dumpSqls != nil && len(*dumpSqls) > 0 {
        for _, sqlStr := range *dumpSqls {
            logger.Debug("gorm exec dumpSql:", sqlStr)
            //logger.Debug("gorm exec dumpSql:", sqlStr)
            if err = tx.Exec(sqlStr).Error; err != nil {
                logger.Error("gorm exec dumpSql:", sqlStr, " error:", err.Error())
                return false
            }
        }
@@ -457,3 +457,14 @@
    var lc models.Node
    return lc.FindIpByNode(nodeId)
}
func ClusterSyncProcMessage(payload []byte) {
    if serf.Agent == nil {
        logger.Error("未加入集群")
    }
    err := serf.Agent.UserEvent(serf.UserEventSyncMessage, payload, false)
    if err != nil {
        logger.Error("UserEventSyncMessage err:", err)
    }
}
system-service/service/proc.go
@@ -1,4 +1,5 @@
package service
const ProcName = "system-service"
const (
    ProcName = "system-service"
)