zhangzengfei
2023-09-05 63645d248c765244488cd34dbc1bb6528ca6b7c7
system-service/service/devInfoSync.go
@@ -1,302 +1,302 @@
package service
import (
   "encoding/json"
   "os/exec"
   "reflect"
   "strconv"
   "strings"
   "time"
   "vamicro/config"
   "vamicro/extend/util"
   "vamicro/system-service/models"
   "vamicro/system-service/sys"
   "vamicro/system-service/vo"
   "basic.com/valib/bhomedbapi.git"
   "basic.com/valib/licence.git"
   "basic.com/valib/logger.git"
   uuid "github.com/satori/go.uuid"
   "github.com/shirou/gopsutil/cpu"
   "github.com/shirou/gopsutil/host"
   "github.com/shirou/gopsutil/mem"
)
func StartSyncDev() {
   tk := time.NewTicker(30 * time.Second)
   for {
      select {
      case <-tk.C:
         syncDevToCloud()
      default:
         time.Sleep(2 * time.Second)
      }
   }
}
const (
   token        = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3NDUwMjU5MjMsInVzZXIiOiJ7XCJpZFwiOlwiZTZjY2QzNmQtNGYxNi00NmZjLTg4ZDUtMDczNjU4NjZkMjA1XCIsXCJwZXJtaXNzaW9uc1wiOltcInByb2R1Y3RNYW5nZTpwdWJsaXNoXCIsXCJjb2RlTWFuZ2U6dmlld1wiLFwiZGV2aWNlTWFuYWdlOmFkZFwiLFwiYWRtaW5NYW5hZ2VcIixcIm9yZGVyTWFuZ2VcIixcImRldmljZU1hbmFnZTp2aWV3XCIsXCJwcm9kdWN0TWFuZ2U6YWRkXCIsXCJhZG1pbk1hbmFnZTp2aWV3XCIsXCJjb2RlTWFuZ2U6YWRkXCIsXCJwcm9kdWN0TWFuZ2U6b2ZmU2FsZVwiLFwib3JkZXJNYW5nZTpjYW5jZWxcIixcInByb2R1Y3RDZW50ZXI6ZG93bmxvYWRcIixcInByb2R1Y3RDZW50ZXI6YnV5XCIsXCJwcm9kdWN0TWFuZ2U6dmlld1wiLFwiYXBpXCIsXCJob21lXCIsXCJvcmRlck1hbmdlOnBheVwiLFwiYWRtaW5NYW5hZ2U6YWRkXCIsXCJvcmRlck1hbmdlOmRvd25sb2FkXCIsXCJwcm9kdWN0Q2VudGVyXCIsXCJkZXZpY2VNYW5hZ2U6dW5iaW5kXCIsXCJvcmRlck1hbmdlOnZpZXdcIixcImFkbWluTWFuYWdlOmVkaXRcIixcImRldmljZU1hbmFnZVwiLFwidmlwTWFuYWdlOmFkZFwiLFwidmlwTWFuYWdlOnZpZXdcIixcInByb2R1Y3RDZW50ZXI6dmlld1wiLFwidmlwTWFuYWdlOmVkaXRcIixcInZpcE1hbmFnZVwiLFwicHJvZHVjdE1hbmdlOmVkaXRcIixcImNvZGVNYW5nZVwiLFwicHJvZHVjdE1hbmdlXCJdLFwidXNlcm5hbWVcIjpcImJhc2ljXCJ9In0.vwjAFkWuEyadRLvIOGK8LFE3MjpY3SQ7j6AlTXnQDG8"
   tokenForSaas = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3NDUwMjU5MjMsInVzZXIiOiJ7XCJpZFwiOlwiZTZjY2QzNmQtNGYxNi00NmZjLTg4ZDUtMDczNjU4NjZkMjA1XCIsXCJwZXJtaXNzaW9uc1wiOltcInByb2R1Y3RNYW5nZTpwdWJsaXNoXCIsXCJjb2RlTWFuZ2U6dmlld1wiLFwiZGV2aWNlTWFuYWdlOmFkZFwiLFwiYWRtaW5NYW5hZ2VcIixcIm9yZGVyTWFuZ2VcIixcImRldmljZU1hbmFnZTp2aWV3XCIsXCJwcm9kdWN0TWFuZ2U6YWRkXCIsXCJhZG1pbk1hbmFnZTp2aWV3XCIsXCJjb2RlTWFuZ2U6YWRkXCIsXCJwcm9kdWN0TWFuZ2U6b2ZmU2FsZVwiLFwib3JkZXJNYW5nZTpjYW5jZWxcIixcInByb2R1Y3RDZW50ZXI6ZG93bmxvYWRcIixcInByb2R1Y3RDZW50ZXI6YnV5XCIsXCJwcm9kdWN0TWFuZ2U6dmlld1wiLFwiYXBpXCIsXCJob21lXCIsXCJvcmRlck1hbmdlOnBheVwiLFwiYWRtaW5NYW5hZ2U6YWRkXCIsXCJvcmRlck1hbmdlOmRvd25sb2FkXCIsXCJwcm9kdWN0Q2VudGVyXCIsXCJkZXZpY2VNYW5hZ2U6dW5iaW5kXCIsXCJvcmRlck1hbmdlOnZpZXdcIixcImFkbWluTWFuYWdlOmVkaXRcIixcImRldmljZU1hbmFnZVwiLFwidmlwTWFuYWdlOmFkZFwiLFwidmlwTWFuYWdlOnZpZXdcIixcInByb2R1Y3RDZW50ZXI6dmlld1wiLFwidmlwTWFuYWdlOmVkaXRcIixcInZpcE1hbmFnZVwiLFwicHJvZHVjdE1hbmdlOmVkaXRcIixcImNvZGVNYW5nZVwiLFwicHJvZHVjdE1hbmdlXCJdLFwidXNlcm5hbWVcIjpcImJhc2ljXCJ9In0.vwjAFkWuEyadRLvIOGK8LFE3MjpY3SQ7j6AlTXnQDG8"
)
var lstSync *vo.SyncDevInfo
/*
设备信息同步到商城云端
*/
func syncDevToCloud() {
   var sysconf models.LocalConfig
   err := sysconf.Select()
   if err != nil { // 查询是否存在
      return
   }
   i := vo.SyncDevInfo{}
   ipv4, mask, _ := sys.GetLocalIP(config.Server.NetworkAdapter)
   gateway, _ := sys.GetDefaultRoute(config.Server.NetworkAdapter)
   dns, _ := sys.GetDnsServer()
   machineCode := licence.GetMachineCode()
   //1.收集设备基本信息
   i.Detail = vo.DevDetail{
      ServerId:        config.Server.AnalyServerId,
      MachineCode:     machineCode,
      ServerName:      sysconf.ServerName,
      ServerPort:      sys.GetNginxListenPort(),
      Ip:              ipv4,
      SubMask:         mask,
      Gateway:         gateway,
      Dns:             dns,
      DeviceNum:       config.Server.DeviceNum,
      DeviceType:      config.Server.DeviceType,
      DeviceModel:     config.Server.DeviceModel,
      DeviceSerialNum: config.Server.DeviceSerialNum,
      MasterVersion:   config.Server.MasterVersion,
      WebVersion:      "1.0.0",
      DeviceDesc:      config.Server.DeviceDesc,
      ChannelCount:    int(sysconf.RealMax),
      NeedAuthPwd:     sysconf.NeedAuthPwd,
      AuthPwd:         sysconf.AuthPwd,
   }
   if strings.HasPrefix(util.GetPlatform(), "X86") {
      vg, _ := util.NvidiaVGpu()
      i.Detail.VGpu = vg
   }
   cmd := exec.Command("/bin/sh", "-c", "lsblk -d | grep -v part | grep -v SWAP | grep -v M | grep disk | awk '{printf $4\" \"}'")
   disks, _ := cmd.Output()
   cpu, _ := cpu.Info()
   mem, _ := mem.VirtualMemory()
   hi, _ := host.Info()
   i.Detail.Disk = string(disks)
   i.Detail.Runtime = util.TimeSpan(time.Unix(int64(hi.BootTime), 0))
   if cpu != nil && len(cpu) > 0 {
      i.Detail.Cpu = cpu[0].ModelName
   }
   if mem != nil {
      i.Detail.Mem = mem.Total
   }
   var initE models.SysInit
   if i2, _ := initE.Select(); i2 > 0 {
      i.Detail.FirstUseTime = initE.InitTime
      i.Detail.ProvinceId = initE.ProvinceId
      i.Detail.CityId = initE.CityId
      i.Detail.CountyId = initE.CountyId
   }
   bSn, e := HttpRCT("POST", "http://127.0.0.1:8888/data/api-v/version/snBus", nil, 3*time.Second)
   if e == nil {
      var dRet bhomedbapi.Result
      if e := json.Unmarshal(bSn, &dRet); e == nil && dRet.Success {
         type tis struct {
            InstallTime string `json:"installTime"`
         }
         var is tis
         if bts, e := json.Marshal(dRet.Data); e == nil {
            if e = json.Unmarshal(bts, &is); e == nil {
               i.Detail.InstallTime = is.InstallTime
            }
         }
      }
   }
   //2.收集已安装算法
   var sdkApi bhomedbapi.SdkApi
   sdks := sdkApi.FindAll("")
   //3.收集已安装应用
   var appApi bhomedbapi.AppApi
   aps := appApi.FindAll("")
   i.Sdks = sdks
   i.Apps = aps
   diff := true
   if lstSync != nil {
      if reflect.DeepEqual(i, *lstSync) {
         diff = false
      }
   }
   // 同步设备数据到saas 已迁移至saas-service
   //if config.SaasConf.Url != "" {
   //   body := util.Struct2Map(i)
   //   url := "http://" + config.SaasConf.Url + "/data/api-d/device/syncDevToCloud"
   //
   //   header := map[string]string{
   //      "Authorization": tokenForSaas,
   //   }
   //   data, err := util.DoPostRequest(url, util.CONTENT_TYPE_JSON, body, nil, header, 10*time.Second)
   //   if err != nil {
   //      logger.Error("send devInfo to saas cloud err:", err, string(data))
   //   } else {
   //      logger.Debug("syncDevToCloud to saas result:", string(data))
   //      var result bhomedbapi.Result
   //      err = json.Unmarshal(data, &result)
   //      if err != nil {
   //         logger.Error("unmarshal err:", err)
   //      }
   //   }
   //}
   if diff {
      // 同步设备信息到商城
      if util.GetShopUrl() != "" {
         body := util.Struct2Map(i)
         url := "http://" + util.GetShopUrl() + "/data/api-d/device/syncDevToCloud"
         header := map[string]string{
            "Authorization": token,
         }
         data, err := util.DoPostRequest(url, util.CONTENT_TYPE_JSON, body, nil, header, 10*time.Second)
         if err != nil {
            logger.Error("send devInfo to cloud err:", err)
            return
         }
         logger.Debug("syncDevToCloud result:", string(data))
         var result bhomedbapi.Result
         err = json.Unmarshal(data, &result)
         if err != nil {
            logger.Error("unmarshal err:", err)
            return
         }
         if result.Success {
            lstSync = &i
         } else {
            logger.Error("syncDevToCloud result:", result)
         }
      }
      //查看是否有管理节点管理着此台设备
      syncToDevManager(i)
      //需要主动通知serf1更新数据,设备管理app的数据从serf1中获取的
   }
}
func syncToDevManager(info vo.SyncDevInfo) error {
   var ad models.AuthDevice
   managers := ad.FindByStatus(models.AuthStatus_Agreed) //获取已授权的设备列表
   if len(managers) > 0 {
      //写入device,deviceApp及deviceSdk表中通过serf同步到其它节点
      tx := models.GetDB().Begin()
      var err error
      defer func() {
         if err != nil && tx != nil {
            logger.Debug("syncToDevManager err:", err)
            tx.Rollback()
         }
      }()
      var dE models.Device
      dc, e := dE.FindByDevId(info.Detail.ServerId)
      if e == nil && dc > 0 { //已存在则更新
         cd := vo.Copy2DeviceModel(info.Detail)
         cd.Id = dE.Id
         cd.ActivateCode = dE.ActivateCode
         cd.ProductId = dE.ProductId
         cd.UserId = dE.UserId
         cd.Address = dE.Address
         cd.DevGpu = dE.DevGpu
         cd.InstallTime = dE.InstallTime
         cd.FirstUseTime = dE.FirstUseTime
         cd.ClusterId = dE.ClusterId
         cd.ClusterName = dE.ClusterName
         cd.Status = dE.Status
         cd.CreateTime = dE.CreateTime
         cd.UpdateTime = dE.UpdateTime
         if err = tx.Table(cd.TableName()).Save(&cd).Error; err != nil {
            return err
         }
      } else { //创建
         cd := vo.Copy2DeviceModel(info.Detail)
         cd.Id = uuid.NewV4().String()
         if err = tx.Table(cd.TableName()).Create(&cd).Error; err != nil {
            return err
         }
      }
      if len(info.Apps) > 0 {
         for _, a := range info.Apps {
            var da models.DeviceApp
            if row, _ := da.Exist(config.Server.AnalyServerId, a.Id); row > 0 {
               da.MachineCode = info.Detail.MachineCode
               da.ExpireTime = ""
               da.ActivateCode = ""
               da.InstallTime = ""
               if err = tx.Table(da.TableName()).Where("id=?", da.Id).Update(&da).Error; err != nil {
                  return err
               }
            } else {
               tmp := models.DeviceApp{
                  Id:           uuid.NewV4().String(),
                  DevId:        config.Server.AnalyServerId,
                  MachineCode:  info.Detail.MachineCode,
                  ActivateCode: "",
                  AppId:        a.Id,
                  ExpireTime:   "",
                  InstallTime:  "",
               }
               if err = tx.Table(tmp.TableName()).Create(&tmp).Error; err != nil {
                  return err
               }
            }
         }
      }
      if len(info.Sdks) > 0 {
         for _, s := range info.Sdks {
            var ds models.DeviceSdk
            if row, _ := ds.Exist(config.Server.AnalyServerId, s.Id); row > 0 {
               ds.MachineCode = info.Detail.MachineCode
               ds.ExpireTime = ""
               ds.ActivateCode = ""
               ds.InstallTime = ""
               if err = tx.Table(ds.TableName()).Where("id=?", ds.Id).Update(&ds).Error; err != nil {
                  return err
               }
            } else {
               tmp := models.DeviceSdk{
                  Id:           uuid.NewV4().String(),
                  DevId:        config.Server.AnalyServerId,
                  MachineCode:  info.Detail.MachineCode,
                  ActivateCode: "",
                  SdkId:        s.Id,
                  ExpireTime:   "",
                  InstallTime:  "",
               }
               if err = tx.Table(tmp.TableName()).Create(&tmp).Error; err != nil {
                  return err
               }
            }
         }
      }
      tx.Commit()
      return nil
   }
   return nil
}
//计算持续时长
func timeSpan(startTime time.Time) string {
   sp := time.Since(startTime)
   day := strconv.Itoa(int(sp.Hours() / 24))      //天数
   hour := strconv.Itoa(int(sp.Hours()) % 24)     //小时数
   minute := strconv.Itoa(int(sp.Minutes()) % 60) //分钟数
   sec := strconv.Itoa(int(sp.Seconds()) % 60)    //秒数
   return day + "天" + hour + "时" + minute + "分" + sec + "秒"
}
package service
import (
   "encoding/json"
   "os/exec"
   "reflect"
   "strconv"
   "strings"
   "time"
   "vamicro/config"
   "vamicro/extend/util"
   "vamicro/system-service/models"
   "vamicro/system-service/sys"
   "vamicro/system-service/vo"
   "basic.com/valib/bhomedbapi.git"
   "basic.com/valib/licence.git"
   "basic.com/valib/logger.git"
   uuid "github.com/satori/go.uuid"
   "github.com/shirou/gopsutil/cpu"
   "github.com/shirou/gopsutil/host"
   "github.com/shirou/gopsutil/mem"
)
func StartSyncDev() {
   tk := time.NewTicker(30 * time.Second)
   for {
      select {
      case <-tk.C:
         syncDevToCloud()
      default:
         time.Sleep(2 * time.Second)
      }
   }
}
const (
   token        = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3NDUwMjU5MjMsInVzZXIiOiJ7XCJpZFwiOlwiZTZjY2QzNmQtNGYxNi00NmZjLTg4ZDUtMDczNjU4NjZkMjA1XCIsXCJwZXJtaXNzaW9uc1wiOltcInByb2R1Y3RNYW5nZTpwdWJsaXNoXCIsXCJjb2RlTWFuZ2U6dmlld1wiLFwiZGV2aWNlTWFuYWdlOmFkZFwiLFwiYWRtaW5NYW5hZ2VcIixcIm9yZGVyTWFuZ2VcIixcImRldmljZU1hbmFnZTp2aWV3XCIsXCJwcm9kdWN0TWFuZ2U6YWRkXCIsXCJhZG1pbk1hbmFnZTp2aWV3XCIsXCJjb2RlTWFuZ2U6YWRkXCIsXCJwcm9kdWN0TWFuZ2U6b2ZmU2FsZVwiLFwib3JkZXJNYW5nZTpjYW5jZWxcIixcInByb2R1Y3RDZW50ZXI6ZG93bmxvYWRcIixcInByb2R1Y3RDZW50ZXI6YnV5XCIsXCJwcm9kdWN0TWFuZ2U6dmlld1wiLFwiYXBpXCIsXCJob21lXCIsXCJvcmRlck1hbmdlOnBheVwiLFwiYWRtaW5NYW5hZ2U6YWRkXCIsXCJvcmRlck1hbmdlOmRvd25sb2FkXCIsXCJwcm9kdWN0Q2VudGVyXCIsXCJkZXZpY2VNYW5hZ2U6dW5iaW5kXCIsXCJvcmRlck1hbmdlOnZpZXdcIixcImFkbWluTWFuYWdlOmVkaXRcIixcImRldmljZU1hbmFnZVwiLFwidmlwTWFuYWdlOmFkZFwiLFwidmlwTWFuYWdlOnZpZXdcIixcInByb2R1Y3RDZW50ZXI6dmlld1wiLFwidmlwTWFuYWdlOmVkaXRcIixcInZpcE1hbmFnZVwiLFwicHJvZHVjdE1hbmdlOmVkaXRcIixcImNvZGVNYW5nZVwiLFwicHJvZHVjdE1hbmdlXCJdLFwidXNlcm5hbWVcIjpcImJhc2ljXCJ9In0.vwjAFkWuEyadRLvIOGK8LFE3MjpY3SQ7j6AlTXnQDG8"
   tokenForSaas = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3NDUwMjU5MjMsInVzZXIiOiJ7XCJpZFwiOlwiZTZjY2QzNmQtNGYxNi00NmZjLTg4ZDUtMDczNjU4NjZkMjA1XCIsXCJwZXJtaXNzaW9uc1wiOltcInByb2R1Y3RNYW5nZTpwdWJsaXNoXCIsXCJjb2RlTWFuZ2U6dmlld1wiLFwiZGV2aWNlTWFuYWdlOmFkZFwiLFwiYWRtaW5NYW5hZ2VcIixcIm9yZGVyTWFuZ2VcIixcImRldmljZU1hbmFnZTp2aWV3XCIsXCJwcm9kdWN0TWFuZ2U6YWRkXCIsXCJhZG1pbk1hbmFnZTp2aWV3XCIsXCJjb2RlTWFuZ2U6YWRkXCIsXCJwcm9kdWN0TWFuZ2U6b2ZmU2FsZVwiLFwib3JkZXJNYW5nZTpjYW5jZWxcIixcInByb2R1Y3RDZW50ZXI6ZG93bmxvYWRcIixcInByb2R1Y3RDZW50ZXI6YnV5XCIsXCJwcm9kdWN0TWFuZ2U6dmlld1wiLFwiYXBpXCIsXCJob21lXCIsXCJvcmRlck1hbmdlOnBheVwiLFwiYWRtaW5NYW5hZ2U6YWRkXCIsXCJvcmRlck1hbmdlOmRvd25sb2FkXCIsXCJwcm9kdWN0Q2VudGVyXCIsXCJkZXZpY2VNYW5hZ2U6dW5iaW5kXCIsXCJvcmRlck1hbmdlOnZpZXdcIixcImFkbWluTWFuYWdlOmVkaXRcIixcImRldmljZU1hbmFnZVwiLFwidmlwTWFuYWdlOmFkZFwiLFwidmlwTWFuYWdlOnZpZXdcIixcInByb2R1Y3RDZW50ZXI6dmlld1wiLFwidmlwTWFuYWdlOmVkaXRcIixcInZpcE1hbmFnZVwiLFwicHJvZHVjdE1hbmdlOmVkaXRcIixcImNvZGVNYW5nZVwiLFwicHJvZHVjdE1hbmdlXCJdLFwidXNlcm5hbWVcIjpcImJhc2ljXCJ9In0.vwjAFkWuEyadRLvIOGK8LFE3MjpY3SQ7j6AlTXnQDG8"
)
var lstSync *vo.SyncDevInfo
/*
设备信息同步到商城云端
*/
func syncDevToCloud() {
   var sysconf models.LocalConfig
   err := sysconf.Select()
   if err != nil { // 查询是否存在
      return
   }
   i := vo.SyncDevInfo{}
   ipv4, mask, _ := sys.GetLocalIP(config.Server.NetworkAdapter)
   gateway, _ := sys.GetDefaultRoute(config.Server.NetworkAdapter)
   dns, _ := sys.GetDnsServer()
   machineCode := licence.GetMachineCode()
   //1.收集设备基本信息
   i.Detail = vo.DevDetail{
      ServerId:        config.Server.AnalyServerId,
      MachineCode:     machineCode,
      ServerName:      sysconf.ServerName,
      ServerPort:      sys.GetNginxListenPort(),
      Ip:              ipv4,
      SubMask:         mask,
      Gateway:         gateway,
      Dns:             dns,
      DeviceNum:       config.Server.DeviceNum,
      DeviceType:      config.Server.DeviceType,
      DeviceModel:     config.Server.DeviceModel,
      DeviceSerialNum: config.Server.DeviceSerialNum,
      MasterVersion:   config.Server.MasterVersion,
      WebVersion:      "1.0.0",
      DeviceDesc:      config.Server.DeviceDesc,
      ChannelCount:    int(sysconf.RealMax),
      NeedAuthPwd:     sysconf.NeedAuthPwd,
      AuthPwd:         sysconf.AuthPwd,
   }
   if strings.HasPrefix(util.GetPlatform(), "X86") {
      vg, _ := util.NvidiaVGpu()
      i.Detail.VGpu = vg
   }
   cmd := exec.Command("/bin/sh", "-c", "lsblk -d | grep -v part | grep -v SWAP | grep -v M | grep disk | awk '{printf $4\" \"}'")
   disks, _ := cmd.Output()
   cpu, _ := cpu.Info()
   mem, _ := mem.VirtualMemory()
   hi, _ := host.Info()
   i.Detail.Disk = string(disks)
   i.Detail.Runtime = util.TimeSpan(time.Unix(int64(hi.BootTime), 0))
   if cpu != nil && len(cpu) > 0 {
      i.Detail.Cpu = cpu[0].ModelName
   }
   if mem != nil {
      i.Detail.Mem = mem.Total
   }
   var initE models.SysInit
   if i2, _ := initE.Select(); i2 > 0 {
      i.Detail.FirstUseTime = initE.InitTime
      i.Detail.ProvinceId = initE.ProvinceId
      i.Detail.CityId = initE.CityId
      i.Detail.CountyId = initE.CountyId
   }
   bSn, e := HttpRCT("POST", "http://127.0.0.1:8888/data/api-v/version/snBus", nil, 3*time.Second)
   if e == nil {
      var dRet bhomedbapi.Result
      if e := json.Unmarshal(bSn, &dRet); e == nil && dRet.Success {
         type tis struct {
            InstallTime string `json:"installTime"`
         }
         var is tis
         if bts, e := json.Marshal(dRet.Data); e == nil {
            if e = json.Unmarshal(bts, &is); e == nil {
               i.Detail.InstallTime = is.InstallTime
            }
         }
      }
   }
   //2.收集已安装算法
   var sdkApi bhomedbapi.SdkApi
   sdks := sdkApi.FindAll("")
   //3.收集已安装应用
   var appApi bhomedbapi.AppApi
   aps := appApi.FindAll("")
   i.Sdks = sdks
   i.Apps = aps
   diff := true
   if lstSync != nil {
      if reflect.DeepEqual(i, *lstSync) {
         diff = false
      }
   }
   // 同步设备数据到saas 已迁移至saas-service
   //if config.SaasConf.Url != "" {
   //   body := util.Struct2Map(i)
   //   url := "http://" + config.SaasConf.Url + "/data/api-d/device/syncDevToCloud"
   //
   //   header := map[string]string{
   //      "Authorization": tokenForSaas,
   //   }
   //   data, err := util.DoPostRequest(url, util.CONTENT_TYPE_JSON, body, nil, header, 10*time.Second)
   //   if err != nil {
   //      logger.Error("send devInfo to saas cloud err:", err, string(data))
   //   } else {
   //      logger.Debug("syncDevToCloud to saas result:", string(data))
   //      var result bhomedbapi.Result
   //      err = json.Unmarshal(data, &result)
   //      if err != nil {
   //         logger.Error("unmarshal err:", err)
   //      }
   //   }
   //}
   if diff {
      // 同步设备信息到商城
      if util.GetShopUrl() != "" {
         body := util.Struct2Map(i)
         url := "http://" + util.GetShopUrl() + "/data/api-d/device/syncDevToCloud"
         header := map[string]string{
            "Authorization": token,
         }
         data, err := util.DoPostRequest(url, util.CONTENT_TYPE_JSON, body, nil, header, 10*time.Second)
         if err != nil {
            logger.Error("send devInfo to cloud err:", err)
            return
         }
         logger.Debug("syncDevToCloud result:", string(data))
         var result bhomedbapi.Result
         err = json.Unmarshal(data, &result)
         if err != nil {
            logger.Error("unmarshal err:", err)
            return
         }
         if result.Success {
            lstSync = &i
         } else {
            logger.Error("syncDevToCloud result:", result)
         }
      }
      //查看是否有管理节点管理着此台设备
      syncToDevManager(i)
      //需要主动通知serf1更新数据,设备管理app的数据从serf1中获取的
   }
}
func syncToDevManager(info vo.SyncDevInfo) error {
   var ad models.AuthDevice
   managers := ad.FindByStatus(models.AuthStatus_Agreed) //获取已授权的设备列表
   if len(managers) > 0 {
      //写入device,deviceApp及deviceSdk表中通过serf同步到其它节点
      tx := models.GetDB().Begin()
      var err error
      defer func() {
         if err != nil && tx != nil {
            logger.Debug("syncToDevManager err:", err)
            tx.Rollback()
         }
      }()
      var dE models.Device
      dc, e := dE.FindByDevId(info.Detail.ServerId)
      if e == nil && dc > 0 { //已存在则更新
         cd := vo.Copy2DeviceModel(info.Detail)
         cd.Id = dE.Id
         cd.ActivateCode = dE.ActivateCode
         cd.ProductId = dE.ProductId
         cd.UserId = dE.UserId
         cd.Address = dE.Address
         cd.DevGpu = dE.DevGpu
         cd.InstallTime = dE.InstallTime
         cd.FirstUseTime = dE.FirstUseTime
         cd.ClusterId = dE.ClusterId
         cd.ClusterName = dE.ClusterName
         cd.Status = dE.Status
         cd.CreateTime = dE.CreateTime
         cd.UpdateTime = dE.UpdateTime
         if err = tx.Table(cd.TableName()).Save(&cd).Error; err != nil {
            return err
         }
      } else { //创建
         cd := vo.Copy2DeviceModel(info.Detail)
         cd.Id = uuid.NewV4().String()
         if err = tx.Table(cd.TableName()).Create(&cd).Error; err != nil {
            return err
         }
      }
      if len(info.Apps) > 0 {
         for _, a := range info.Apps {
            var da models.DeviceApp
            if row, _ := da.Exist(config.Server.AnalyServerId, a.Id); row > 0 {
               da.MachineCode = info.Detail.MachineCode
               da.ExpireTime = ""
               da.ActivateCode = ""
               da.InstallTime = ""
               if err = tx.Table(da.TableName()).Where("id=?", da.Id).Update(&da).Error; err != nil {
                  return err
               }
            } else {
               tmp := models.DeviceApp{
                  Id:           uuid.NewV4().String(),
                  DevId:        config.Server.AnalyServerId,
                  MachineCode:  info.Detail.MachineCode,
                  ActivateCode: "",
                  AppId:        a.Id,
                  ExpireTime:   "",
                  InstallTime:  "",
               }
               if err = tx.Table(tmp.TableName()).Create(&tmp).Error; err != nil {
                  return err
               }
            }
         }
      }
      if len(info.Sdks) > 0 {
         for _, s := range info.Sdks {
            var ds models.DeviceSdk
            if row, _ := ds.Exist(config.Server.AnalyServerId, s.Id); row > 0 {
               ds.MachineCode = info.Detail.MachineCode
               ds.ExpireTime = ""
               ds.ActivateCode = ""
               ds.InstallTime = ""
               if err = tx.Table(ds.TableName()).Where("id=?", ds.Id).Update(&ds).Error; err != nil {
                  return err
               }
            } else {
               tmp := models.DeviceSdk{
                  Id:           uuid.NewV4().String(),
                  DevId:        config.Server.AnalyServerId,
                  MachineCode:  info.Detail.MachineCode,
                  ActivateCode: "",
                  SdkId:        s.Id,
                  ExpireTime:   "",
                  InstallTime:  "",
               }
               if err = tx.Table(tmp.TableName()).Create(&tmp).Error; err != nil {
                  return err
               }
            }
         }
      }
      tx.Commit()
      return nil
   }
   return nil
}
//计算持续时长
func timeSpan(startTime time.Time) string {
   sp := time.Since(startTime)
   day := strconv.Itoa(int(sp.Hours() / 24))      //天数
   hour := strconv.Itoa(int(sp.Hours()) % 24)     //小时数
   minute := strconv.Itoa(int(sp.Minutes()) % 60) //分钟数
   sec := strconv.Itoa(int(sp.Seconds()) % 60)    //秒数
   return day + "天" + hour + "时" + minute + "分" + sec + "秒"
}