From 1b34d7bacad94933ad63fc0e199bd32ac49d9fa5 Mon Sep 17 00:00:00 2001
From: zhangzengfei <zhangzengfei@smartai.com>
Date: 星期二, 05 九月 2023 10:00:00 +0800
Subject: [PATCH] 修复编译

---
 devicemanage-service/models/device.go      |  137 +++++
 devicemanage-service/controllers/device.go |  428 +++++++++++++++
 system-service/service/busRequest.go       |  141 +++++
 devicemanage-service/service/device.go     |  189 +++++++
 devicemanage-service/models/deviceSdk.go   |   85 +++
 devicemanage-service/models/db.go          |   32 +
 devicemanage-service/main.go               |  134 ++++
 devicemanage-service/models/deviceApp.go   |   69 ++
 devicemanage-service/models/deviceApply.go |   69 ++
 devicemanage-service/service/ctrl.go       |   61 ++
 devicemanage-service/vo/ctrl.go            |   47 +
 devicemanage-service/vo/device.go          |   40 +
 system-service/service/toNode.go           |   80 ++
 devicemanage-service/Makefile              |    7 
 devicemanage-service/broadcast/client.go   |   79 ++
 system-service/controllers/syssetconf.go   |    3 
 16 files changed, 1,599 insertions(+), 2 deletions(-)

diff --git a/devicemanage-service/Makefile b/devicemanage-service/Makefile
new file mode 100644
index 0000000..9d3abe8
--- /dev/null
+++ b/devicemanage-service/Makefile
@@ -0,0 +1,7 @@
+BUILD_VERSION := 1.1.1
+#APP_NAME      := myversion
+
+include ../module.dep
+
+
+
diff --git a/devicemanage-service/broadcast/client.go b/devicemanage-service/broadcast/client.go
new file mode 100644
index 0000000..66e255b
--- /dev/null
+++ b/devicemanage-service/broadcast/client.go
@@ -0,0 +1,79 @@
+package broadcast
+
+import (
+	"fmt"
+	"github.com/gogf/greuse"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	port = 31995
+	lport = 31996
+
+	lock sync.Mutex    //鍚屼竴鏃跺埢鍙兘鏈変竴涓嚎绋嬪湪鎵ц鎼滅储
+)
+
+
+//骞挎挱鏀堕泦鍏朵粬鑺傜偣淇℃伅
+func BroadCast() ([]string, error) {
+	lock.Lock()
+	defer lock.Unlock()
+
+	retCh := make(chan []string)
+	go startRecv(retCh)
+
+	conn, err := greuse.Dial("udp", "0.0.0.0:"+strconv.Itoa(lport), "255.255.255.255:"+strconv.Itoa(port))
+
+	if err != nil {
+		fmt.Println("err:", err)
+	} else {
+		defer conn.Close()
+
+		n,err := conn.Write([]byte("who are you?"))
+		if err != nil || n == 0 {
+			fmt.Println("conn.Write err:", err, " n:", n)
+		}
+	}
+
+	nodes := <-retCh
+
+	return nodes, nil
+}
+
+func startRecv(rCh chan []string) {
+	conn, err := greuse.ListenPacket("udp", "0.0.0.0:"+strconv.Itoa(lport))
+
+	if err != nil {
+		fmt.Println("startRecv ListenPacket err:", err)
+		rCh <- []string{}
+		return
+	}
+	//10绉掗挓涔嬪唴鏀跺埌鐨勮繑鍥烇紝鍗宠涓烘槸鍦ㄧ嚎鐨�
+	conn.SetReadDeadline(time.Now().Add(time.Second * 1))
+	ch := time.After(time.Second * 4)
+	var nodes []string
+Loop:
+	for {
+		select {
+		case <-ch:
+			fmt.Println("<-ch")
+			break Loop
+		default:
+			ret := make([]byte, 1024)
+			n, from, e := conn.ReadFrom(ret)
+			if e == nil && n >0 {
+				arr := strings.Split(from.String(), ":")
+				if len(arr) == 2 {
+					nodes = append(nodes, arr[0])
+				}
+				fmt.Println("read message from udp:", string(ret), " from:", from)
+			} else {
+				time.Sleep(time.Millisecond * 100)
+			}
+		}
+	}
+	rCh <- nodes
+}
diff --git a/devicemanage-service/controllers/device.go b/devicemanage-service/controllers/device.go
new file mode 100644
index 0000000..60045bf
--- /dev/null
+++ b/devicemanage-service/controllers/device.go
@@ -0,0 +1,428 @@
+package controllers
+
+import (
+	"basic.com/valib/bhomeclient.git"
+	"basic.com/valib/bhomedbapi.git"
+	"basic.com/valib/logger.git"
+	"context"
+	"encoding/json"
+	"github.com/satori/go.uuid"
+	"nanomsg.org/go-mangos"
+	"nanomsg.org/go-mangos/protocol/rep"
+	"nanomsg.org/go-mangos/transport/ipc"
+	"nanomsg.org/go-mangos/transport/tcp"
+	"strconv"
+	"time"
+	"vamicro/devicemanage-service/broadcast"
+	"vamicro/devicemanage-service/models"
+	"vamicro/devicemanage-service/service"
+	"vamicro/devicemanage-service/vo"
+)
+
+type DeviceController struct {
+
+}
+
+//鎼滅储璁惧
+func (dc *DeviceController) Search(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	nodes, err := broadcast.BroadCast()
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: err.Error()}
+	}
+	if nodes == nil {
+		nodes = make([]string, 0)
+	}
+	return &bhomeclient.Reply{ Success: true, Data: nodes}
+}
+
+//鐢宠娣诲姞璁惧
+func (dc *DeviceController) AddApply(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	type addApplyArg struct {
+		Key 	string 	`json:"key"`
+		DevId 	string 	`json:"devId"`
+		Ip 		string 	`json:"ip"`
+	}
+	var reqBody addApplyArg
+	err := c.BindJSON(&reqBody)
+	if err != nil || reqBody.Key == "" || reqBody.DevId == "" || reqBody.Ip == "" {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	var da models.DeviceApply
+	i,e := da.FindByDevId(reqBody.DevId)
+	if e != nil || i == 0 { //鏈敵璇锋坊鍔犺繃姝よ澶�
+		da.Id = uuid.NewV4().String()
+		da.DevId = reqBody.DevId
+		da.CreateTime = time.Now().Format("2006-01-02 15:04:05")
+		da.ApplyKey = reqBody.Key
+		da.Ip = reqBody.Ip
+		da.Status = models.ApplyStatus_Sending
+		if da.Insert() {
+			return &bhomeclient.Reply{ Success: true, Msg: "娣诲姞鎴愬姛"}
+		} else {
+			return &bhomeclient.Reply{ Success: true, Msg: "娣诲姞澶辫触"}
+		}
+	} else if da.Status == models.ApplyStatus_Reject { //宸茶鎷掔粷鐨勮姹傦紝鍙互閲嶆柊鍙戣捣鐢宠
+		if da.UpdateStatus(models.ApplyStatus_Sending, da.Id) {
+			return &bhomeclient.Reply{ Success: true, Msg: "鎿嶄綔鎴愬姛"}
+		} else {
+			return &bhomeclient.Reply{ Msg: "閲嶆柊鍙戦�佽姹傚け璐�"}
+		}
+	}
+
+	return &bhomeclient.Reply{ Msg: "宸叉坊鍔犲姝よ澶囩殑鐢宠"}
+}
+
+func RecvApprove(ctx context.Context) {
+	url := "0.0.0.0:"+strconv.Itoa(4012)
+	var sock mangos.Socket
+	var err error
+	var msg []byte
+	if sock, err = rep.NewSocket(); err != nil {
+		logger.Debug("new applyApprove socket err:", err)
+	}
+	sock.AddTransport(ipc.NewTransport())
+	sock.AddTransport(tcp.NewTransport())
+	if err = sock.Listen(url); err != nil {
+		logger.Debug("listen on applyApprove socket err:", err)
+	}
+
+	for {
+		select {
+		case <-ctx.Done():
+			logger.Debug("ctx done")
+			return
+		default:
+			msg, err = sock.Recv()
+			if err != nil {
+				continue
+			}
+
+			if len(msg) > 0 {
+				apply := dealApprove(msg)
+				ret, _ := json.Marshal(*apply)
+				retErr := sock.Send(ret)
+				if retErr != nil {
+					logger.Debug("retErr:", retErr)
+				}
+			} else {
+				time.Sleep(time.Millisecond*100)
+			}
+		}
+	}
+}
+
+func dealApprove(msg []byte) *bhomeclient.Reply {
+	type approveArg struct {
+		DevId 	string 	`json:"devId"`  //璁惧id
+		Status 	int 	`json:"status"` //-1琛ㄧず鎷掔粷锛�1锛氳〃绀洪�氳繃
+	}
+
+	var reqBody approveArg
+	err := json.Unmarshal(msg, &reqBody)
+	if err != nil || reqBody.Status < models.ApplyStatus_Reject || reqBody.Status > models.ApplyStatus_Agreed {
+		return &bhomeclient.Reply{ Msg: "璇锋眰鍙傛暟鏈夎"}
+	}
+	var da models.DeviceApply
+	i, _ := da.FindByDevId(reqBody.DevId)
+	if i == 0 {
+		return &bhomeclient.Reply{ Msg: reqBody.DevId+"鐨勭敵璇疯姹傚凡涓嶅瓨鍦紝璇锋鏌�" }
+	}
+	if da.Status != models.ApplyStatus_Waiting {
+		return &bhomeclient.Reply{ Msg: "褰撳墠姝よ澶噄d涓嶆槸鐢宠鐘舵�侊紝璇锋鏌�" }
+	}
+	if da.UpdateStatus(reqBody.Status, da.Id) {
+		return &bhomeclient.Reply{ Success: true, Msg: "鎿嶄綔鎴愬姛"}
+	}
+
+	return &bhomeclient.Reply{ Msg: "鎿嶄綔澶辫触"}
+}
+
+//鍏朵粬鑺傜偣閫氳繃鐢宠鎴栬�呮嫆缁濈敵璇�
+func (dc *DeviceController) ApplyApprove(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	return &bhomeclient.Reply{ Msg: "building..."}
+}
+
+//func (dc *DeviceController) SyncDevToManager(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply {
+//	var reqBody sysVo.SyncDevInfo
+//	err := c.BindJSON(&reqBody)
+//	if err != nil {
+//		return &bhomeclient.Reply{ Msg: err.Error() }
+//	}
+//
+//	return &bhomeclient.Reply{ Data: reqBody }
+//}
+
+//璁惧褰掔被缁熻锛屽垎鍏ㄩ儴璁惧銆侀泦缇x璁惧銆佹湭鍔犲叆闆嗙兢
+func (dc *DeviceController) Types(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	type devStatistic struct {
+		Name   		string 		`json:"name"`
+		DevCount 	int 		`json:"devCount"`
+		SdkCount 	int 		`json:"sdkCount"`
+		Percent 	string 		`json:"percent"`
+		ClusterId 	string 		`json:"clusterId"`
+		Type 		int 		`json:"type"`
+	}
+	list := make([]devStatistic, 0)
+	//1.缁熻鍏ㄩ儴璁惧
+	total := devStatistic{
+		Name: "鍏ㄩ儴璁惧",
+		DevCount: 100,
+		SdkCount: 45,
+		Percent: "80%",
+		Type: 0,
+	}
+	list = append(list, total)
+	//2.鎸夐泦缇ょ粺璁�
+	var d models.Device
+	devCts := d.GroupByCluster()
+	if devCts != nil {
+		for _,g := range devCts {
+			ds := devStatistic{
+				Name: g.ClusterName,
+				ClusterId: g.ClusterId,
+				DevCount: g.Count,
+				SdkCount: 0,
+				Percent: "70%",
+				Type: 1,
+			}
+			list = append(list, ds)
+		}
+	}
+	//3.鏈姞鍏ラ泦缇よ澶�
+	ns := devStatistic{
+		Name: "鏈姞鍏ラ泦缇�",
+		DevCount: 0,
+		SdkCount: 0,
+		Percent: "10%",
+		Type: 2,
+	}
+	list = append(list, ns)
+
+	return &bhomeclient.Reply{ Success: true, Data: list}
+}
+
+//閫氳繃涓嶅悓绫诲埆鑾峰彇璁惧鍒楄〃,鍒嗛〉
+func (dc *DeviceController) PageByType(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	type PageDevArg struct {
+		Type 		int 	`json:"type"`  //绫诲瀷
+		ClusterId 	string 	`json:"clusterId"`   //闆嗙兢id
+		Status 		int 	`json:"status"`   //鐘舵�� 0锛氬叏閮紝1锛氬湪绾匡紝-1锛氱绾�
+		Page 		int 	`json:"page"`
+		Size 		int 	`json:"size"`
+		InputTxt 	string 	`json:"inputTxt"`
+	}
+	var reqBody PageDevArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg:err.Error()}
+	}
+	if reqBody.Page <= 0 {
+		reqBody.Page = 1
+	}
+	if reqBody.Size <= 0 {
+		reqBody.Size = 8
+	}
+	var d models.Device
+	if reqBody.Type == 0 { //鍏ㄩ儴璁惧
+		list, err := d.PageAllDev(reqBody.Page, reqBody.Size, reqBody.Status, reqBody.InputTxt)
+		if err != nil {
+			return &bhomeclient.Reply{ Msg: err.Error()}
+		}
+		return &bhomeclient.Reply{ Success: true, Data: list}
+	} else if reqBody.Type == 1 || reqBody.Type == 2 {  //鎸夐泦缇ょ粺璁�
+		list, err := d.PageDevByCluster(reqBody.Page, reqBody.Size, reqBody.ClusterId, reqBody.Status, reqBody.InputTxt)
+		if err != nil {
+			return &bhomeclient.Reply{ Msg: err.Error()}
+		}
+		return &bhomeclient.Reply{ Success: true, Data: list}
+	}
+
+	return nil
+}
+
+//鑾峰彇璁惧璇︽儏
+func (dc *DeviceController) Detail(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	devId := c.Query("devId")
+	logger.Debug("Detail devId:", devId)
+	var d models.Device
+	i, e := d.FindByDevId(devId)
+	if e == nil && i > 0 {
+		detail := vo.DevDetail{}
+		pd,_ := vo.CopyDeviceFromModel(d)
+		detail.Device = pd
+		detail.Sdks = getSdkDetailsByDevId(d.DevId)
+		detail.Apps = getAppDetailsByDevId(d.DevId)
+
+
+		return &bhomeclient.Reply{ Success:true, Data: detail }
+	} else {
+		return &bhomeclient.Reply{ Msg:"鏈壘鍒版璁惧鐨勭浉鍏充俊鎭�"}
+	}
+}
+
+func getSdkDetailsByDevId(devId string) []vo.SdkDetail {
+	arr := make([]vo.SdkDetail, 0)
+
+	var sdkApi bhomedbapi.SdkApi
+	sdkMap := sdkApi.FindAllMap()
+
+	var ds models.DeviceSdk
+	list, _ := ds.FindByMachineCode("", devId)
+	if list != nil {
+		for _,s :=range list {
+			if v,ok := sdkMap[s.SdkId]; ok {
+				sd := vo.SdkDetail{}
+				sd.Sdk = v
+				sd.InstallTime = s.InstallTime
+				sd.ExpireTime = s.ExpireTime
+				sd.ActivateCode = s.ActivateCode
+				arr = append(arr, sd)
+			}
+		}
+	}
+	return arr
+}
+
+func getAppDetailsByDevId(devId string) []vo.AppDetail {
+	arr := make([]vo.AppDetail, 0)
+
+	var appApi bhomedbapi.AppApi
+	appMap := appApi.FindAppMap()
+
+	var da models.DeviceApp
+	list, _ := da.FindByMachineCode("", devId)
+	if list != nil {
+		for _,s :=range list {
+			if v,ok := appMap[s.AppId]; ok {
+				ad := vo.AppDetail{}
+				ad.App = v
+				ad.InstallTime = s.InstallTime
+				ad.ExpireTime = s.ExpireTime
+				ad.ActivateCode = s.ActivateCode
+				arr = append(arr, ad)
+			}
+		}
+	}
+	return arr
+}
+
+
+//鎺у埗杩滅▼璁惧鍒涘缓闆嗙兢
+func (dc *DeviceController) RemoteCreateCluster(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply {
+	var reqBody vo.CreateClusterArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+
+	r,err := service.RemoteCreateCluster(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error() }
+}
+
+//鎺у埗杩滅▼璁惧鎼滅储闆嗙兢
+func (dc *DeviceController) RemoteSearchCluster(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply {
+	var reqBody vo.SearchClusterArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r, err := service.RemoteSearchCluster(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error() }
+}
+
+//鑾峰彇杩滅▼璁惧鎼滅储鍒扮殑璁惧鍒楄〃
+func (dc *DeviceController) RemoteGetSearchNodes(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply {
+	var reqBody vo.GetSearchNodesArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r,err := service.RemoteGetSearchNodes(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{Msg: err.Error()}
+}
+
+//鍔犲叆闆嗙兢
+func (dc *DeviceController) RemoteJoinCluster(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	var reqBody vo.JoinClusterArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r,err := service.RemoteJoinCluster(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error()}
+}
+
+//璁惧閲嶅惎
+func (dc *DeviceController) RemoteReboot(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	var reqBody vo.RebootArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r,err := service.RemoteReboot(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error()}
+}
+
+//绉婚櫎璁惧
+func (dc *DeviceController) RemoteRemove(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+
+	return nil
+}
+
+//鍗歌浇绠楁硶鎴栬�呭簲鐢�
+func (dc *DeviceController) RemoteUninstall(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	var reqBody vo.UninstallArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r,err := service.RemoteUninstall(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error()}
+}
+
+//鍗囩骇绠楁硶鎴栬�呭簲鐢�
+func (dc *DeviceController) RemoteUpgrade(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	var reqBody vo.UpgradeArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+	r,err := service.RemoteUpgrade(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error()}
+}
+
+//绯荤粺鏇存柊
+func (dc *DeviceController) RemoteSysUpdate(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply{
+	var reqBody vo.SysUpdateArg
+	err := c.BindJSON(&reqBody)
+	if err != nil {
+		return &bhomeclient.Reply{ Msg: "鍙傛暟鏈夎"}
+	}
+
+	r,err := service.RemoteSysUpdate(reqBody)
+	if err == nil {
+		return r
+	}
+	return &bhomeclient.Reply{ Msg: err.Error()}
+}
\ No newline at end of file
diff --git a/devicemanage-service/main.go b/devicemanage-service/main.go
new file mode 100644
index 0000000..598baeb
--- /dev/null
+++ b/devicemanage-service/main.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+	"basic.com/valib/bhomeclient.git"
+	"basic.com/valib/bhomedbapi.git"
+	"basic.com/valib/logger.git"
+	"basic.com/valib/version.git"
+	"context"
+	"flag"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+	"vamicro/config"
+	"vamicro/devicemanage-service/controllers"
+	"vamicro/devicemanage-service/models"
+	"vamicro/devicemanage-service/service"
+)
+
+var (
+	procName = "devicemanage-service"
+	proc = &bhomeclient.ProcInfo{
+		Name: procName, //杩涚▼鍚嶇О
+		ID: procName, //杩涚▼id
+		Info: "", //杩涚▼鐨勬弿杩颁俊鎭紝鐢ㄤ簬鍖哄垎鍚屼竴杩涚▼鍚嶇О涓嬪涓繘绋�
+	}
+	env = flag.String("e", "pro", "")
+)
+
+func init() {
+	flag.Parse()
+	vaversion.Usage()
+
+	config.Init(*env)
+	// 鏃ュ織鍒濆鍖�
+	var logFile = config.LogConf.Path + "vamicro-"+procName+".log"
+	logger.InitLogger(logFile, config.LogConf.Level, config.LogConf.MaxSize, config.LogConf.MaxBackups, config.LogConf.MaxAge)
+	logger.Info("log init success !")
+}
+
+func main(){
+	flag.Parse()
+
+	models.Init()
+	defer models.CloseDB()
+	ctx, cancel := context.WithCancel(context.Background())
+	fm,pubTopics := initFuncMap()
+	var reg = &bhomeclient.RegisterInfo {
+		Proc: *proc,
+		Channel: nil,
+		PubTopic: pubTopics,
+		SubTopic: []string{ service.CollectDeviceTopic },
+	}
+
+	q := make(chan os.Signal, 1)
+	signal.Notify(q, os.Interrupt, os.Kill, syscall.SIGTERM)
+
+	ms, err := bhomeclient.NewMicroNode(ctx, q, config.Server.AnalyServerId, reg, logger.Debug)
+	if err !=nil {
+		return
+	}
+	bhomedbapi.InitLog(logger.Debug)
+	bhomedbapi.InitGetNetNode(ms.GetLocalNetNodeByTopic)
+	bhomedbapi.InitDoReq(ms.RequestOnly)
+
+	go dealSubMsg(ctx, ms)
+	go testPubNetMsg(ms)
+
+	go ms.StartServer(fm)
+	service.DealApply()
+	go controllers.RecvApprove(ctx)
+
+	<-q
+	ms.DeRegister()
+	cancel()
+	ms.Free()
+}
+
+//娴嬭瘯鍙戝竷鍏ㄧ綉娑堟伅
+func testPubNetMsg(ms *bhomeclient.MicroNode) {
+	netTopic := "globalPublishTopic"
+	tk := time.NewTicker(3 * time.Second)
+	for {
+		select {
+		case <-tk.C:
+			pmsg := []byte("hello world"+time.Now().Format("2006-01-02 15:04:05"))
+			n := ms.PublishNetTimeout(nil, netTopic, pmsg, 1000)
+			logger.Debug("PublishNetTimeOut n:", n)
+		default:
+			time.Sleep(1* time.Second)
+		}
+	}
+}
+
+//澶勭悊璁㈤槄娑堟伅
+func dealSubMsg(ctx context.Context, ms *bhomeclient.MicroNode) {
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case msg := <-ms.SubCh:
+			logger.Debug("recv sub msg topic:", string(msg.Topic), " data:", string(msg.Data))
+			if string(msg.Topic) == service.CollectDeviceTopic {
+				service.CollectManageDeviceInfo(msg.Data)
+			}
+		}
+	}
+}
+
+const urlPrefix= "/data/api-v"
+func initFuncMap() (map[string]bhomeclient.MicroFunc,[]string) {
+	m := make(map[string]bhomeclient.MicroFunc)
+	dc := new(controllers.DeviceController)
+	m[urlPrefix+"/device/search"] = dc.Search
+	m[urlPrefix+"/device/addApply"] = dc.AddApply
+	m[urlPrefix+"/device/applyApprove"] = dc.ApplyApprove
+	m[urlPrefix+"/device/types"] = dc.Types
+	m[urlPrefix+"/device/pageByType"] = dc.PageByType
+	m[urlPrefix+"/device/detail"] = dc.Detail
+	m[urlPrefix+"/device/remoteCreateCluster"] = dc.RemoteCreateCluster
+	m[urlPrefix+"/device/remoteSearchCluster"] = dc.RemoteSearchCluster
+	m[urlPrefix+"/device/remoteJoinCluster"] = dc.RemoteJoinCluster
+	m[urlPrefix+"/device/remoteReboot"] = dc.RemoteReboot
+	m[urlPrefix+"/device/remoteRemove"] = dc.RemoteRemove
+	m[urlPrefix+"/device/remoteUninstall"] = dc.RemoteUninstall
+	m[urlPrefix+"/device/remoteUpgrade"] = dc.RemoteUpgrade
+	m[urlPrefix+"/device/remoteSysUpdate"] = dc.RemoteSysUpdate
+
+	var pubTopics []string
+	for key,_ := range m {
+		pubTopics = append(pubTopics, key)
+	}
+	return m, pubTopics
+}
diff --git a/devicemanage-service/models/db.go b/devicemanage-service/models/db.go
new file mode 100644
index 0000000..5f56d2b
--- /dev/null
+++ b/devicemanage-service/models/db.go
@@ -0,0 +1,32 @@
+package models
+
+import (
+	"basic.com/valib/logger.git"
+	"github.com/jinzhu/gorm"
+	_ "github.com/jinzhu/gorm/dialects/sqlite"
+	"vamicro/config"
+)
+
+var db *gorm.DB
+var err error
+
+// Init creates a connection to mysql database and
+// migrates any new models
+func Init() {
+	db, err = gorm.Open(config.DBconf.Name, "../config/devicemanage-service.db")
+	if err != nil {
+		logger.Debug("db open error ", err)
+	}
+	db.LogMode(true)
+	//db.SetLogger(&DbLogger{})
+	db.AutoMigrate(&DeviceApply{})
+}
+
+//GetDB ...
+func GetDB() *gorm.DB {
+	return db
+}
+
+func CloseDB() {
+	db.Close()
+}
diff --git a/devicemanage-service/models/device.go b/devicemanage-service/models/device.go
new file mode 100644
index 0000000..29f7dd5
--- /dev/null
+++ b/devicemanage-service/models/device.go
@@ -0,0 +1,137 @@
+package models
+
+import "strconv"
+
+type Device struct {
+	Id 				string 			`gorm:"primary_key;column:id" json:"id"`
+	DevId 			string 			`gorm:"column:devId;unique;not null;" json:"devId"`         //璁惧id
+	DevType 		string 			`gorm:"column:devType" json:"devType"` 						//璁惧绫诲瀷锛屽锛氬瓨鍌ㄣ�佸垎鏋�(鐩掑瓙鎴朼md鏈嶅姟鍣�)銆佸垎鏋愬瓨鍌ㄤ竴浣�
+	DevMode 		string 			`gorm:"column:devMode" json:"devMode"` 						//璁惧鍨嬪彿,濡傦細Bsk-JS1000X
+	DevName 		string 			`gorm:"column:devName" json:"devName"`						//璁惧鍚嶇О
+	MachineCode 	string 			`gorm:"column:machineCode" json:"machineCode"`				//鏈哄櫒鐮�
+	ActivateCode 	string 			`gorm:"column:activateCode" json:"activateCode"`			//婵�娲荤爜
+	ProductId 		string 			`gorm:"column:productId" json:"productId"`					//浜у搧id
+	UserId 			string 			`gorm:"column:userId" json:"userId"`						//鐢ㄦ埛id
+	Address 		string 			`gorm:"column:address" json:"address"`						//鍦板潃
+	DevIp 		 	string 			`gorm:"column:devIp" json:"devIp"`							//ip
+	DevCpu			string 			`gorm:"column:devCpu" json:"devCpu"` 						//cpu
+	DevGpu			string 			`gorm:"column:devGpu" json:"devGpu"`						//gpu
+	Mem 			int 			`gorm:"column:mem" json:"mem"`  							//鍐呭瓨
+	Disk 			string 			`gorm:"column:disk" json:"disk"`  							//纭洏
+	ChannelCount 	int 			`gorm:"column:channelCount;default:16" json:"channelCount"` //绠楀姏鏁伴噺
+	MasterVersion 	string 			`gorm:"column:masterVersion" json:"masterVersion"`   		//涓绘帶鐗堟湰
+	WebVersion 		string 			`gorm:"column:webVersion" json:"webVersion"`   				//web鐗堟湰
+	ServerPort 		string 			`gorm:"column:serverPort" json:"serverPort"`   				//绔彛锛氶粯璁�7003
+	SubMask 		string 			`gorm:"column:subMask" json:"subMask"`		   				//瀛愮綉鎺╃爜
+	Gateway 		string 			`gorm:"column:gateway" json:"gateway"` 		   				//缃戝叧
+	Dns 			string 			`gorm:"column:dns" json:"dns"` 				   				//dns
+	Runtime 		string 			`gorm:"column:runtime" json:"runtime"`         				//杩愯鏃堕暱
+
+	InstallTime 	string 			`gorm:"column:installTime" json:"installTime"`   			//瀹夎鏃堕棿
+	FirstUseTime 	string 			`gorm:"column:firstUseTime" json:"firstUseTime"` 			//棣栨浣跨敤鏃堕棿
+
+	//闆嗙兢鐩稿叧淇℃伅
+	ClusterId 		string 			`gorm:"column:clusterId" json:"clusterId"`       			//闆嗙兢id
+	ClusterName 	string 			`gorm:"column:clusterName" json:"clusterName"`				//闆嗙兢鍚嶇О
+	Status 			int 			`gorm:"column:status;default:0;" json:"status"`             //鐘舵�� -1:绂荤嚎, 1:鍦ㄧ嚎
+
+	CreateTime 		string 			`gorm:"column:createTime" json:"createTime"`                //璁板綍鏃堕棿
+	UpdateTime 		string 			`gorm:"column:updateTime" json:"updateTime"`				//鏇存柊鏃堕棿
+}
+
+const (
+	DevStatus_OnLine = 1   //鍦ㄧ嚎
+	DevStatus_OffLine = -1   //绂荤嚎
+)
+
+func (Device) TableName() string {
+	return "t_device"
+}
+
+func (d *Device) Insert() bool {
+	result := db.Table(d.TableName()).Create(&d)
+	if result.Error !=nil {
+		return false
+	}
+	return result.RowsAffected>0
+}
+
+func (d *Device) Update() bool {
+	result := db.Table(d.TableName()).Update(&d)
+	if result.Error !=nil {
+		return false
+	}
+	return result.RowsAffected>0
+}
+
+func (d *Device) SelectById(id string) (int64, error){
+	result := db.Table(d.TableName()).Where("id=?",id).First(&d)
+	if result.Error != nil || result.RowsAffected == 0 {
+		return 0, err
+	}
+	return result.RowsAffected, nil
+}
+
+func (d *Device) FindByDevId(devId string) (int64,error) {
+	result := db.Table(d.TableName()).Where("devId=?", devId).First(&d)
+	if result.Error != nil || result.RowsAffected == 0 {
+		return 0, result.Error
+	}
+	return result.RowsAffected, nil
+}
+
+func (d *Device) FindAll() (list []Device){
+	if err:=db.Table(d.TableName()).Scan(&list).Error;err !=nil{
+		return nil
+	}
+	return list
+}
+
+type DevClusterGroup struct {
+	ClusterId 		string 		`json:"clusterId"`   //闆嗙兢id
+	ClusterName 	string 		`json:"clusterName"` //闆嗙兢鍚嶇О
+	Count 			int 		`json:"count"`       //鏁伴噺
+}
+func (d *Device) GroupByCluster() (list []DevClusterGroup) {
+	err := db.Raw("select clusterId,clusterName,count(1) as count from "+d.TableName()+" where clusterId!='' group by clusterId,clusterName order by clusterId asc").Scan(&list).Error
+	if err != nil {
+		return nil
+	}
+	return list
+}
+
+func (d *Device) PageAllDev(page int, size int, status int, inputTxt string) (list []Device, err error) {
+	sql := "select * from "+d.TableName()+" where 1=1"
+	from := (page-1)*size
+	if inputTxt != "" {
+		sql += " and devName like '%"+inputTxt+"%'"
+	}
+	if status == DevStatus_OnLine || status == DevStatus_OffLine {
+		sql += " and status="+strconv.Itoa(status)
+	}
+	sql += " order by createTime limit "+strconv.Itoa(from)+","+strconv.Itoa(size)+""
+	err = db.Raw(sql).Scan(&list).Error
+	if err != nil {
+		return nil, err
+	}
+	return
+}
+
+func (d *Device) PageDevByCluster(page int, size int, clusterId string, status int, inputTxt string) (list []Device, err error) {
+	sql := "select * from "+d.TableName()+" where 1=1 and clusterId='"+clusterId+"'"
+	from := (page-1)*size
+	if inputTxt != "" {
+		sql += " and devName like '%"+inputTxt+"%'"
+	}
+	if status == DevStatus_OnLine || status == DevStatus_OffLine {
+		sql += " and status="+strconv.Itoa(status)
+	}
+
+	sql += " order by createTime desc limit "+strconv.Itoa(from)+","+strconv.Itoa(size)+""
+	err = db.Raw(sql).Scan(&list).Error
+	if err != nil {
+		return nil, err
+	}
+	return
+
+}
diff --git a/devicemanage-service/models/deviceApp.go b/devicemanage-service/models/deviceApp.go
new file mode 100644
index 0000000..e65d84c
--- /dev/null
+++ b/devicemanage-service/models/deviceApp.go
@@ -0,0 +1,69 @@
+package models
+
+type DeviceApp struct {
+	Id 				string 		`gorm:"column:id;primary_key;type:varchar(50);unique;not null;" json:"id"`
+	DevId 			string 		`gorm:"column:devId" json:"devId"`
+	MachineCode 	string 		`gorm:"column:machineCode" json:"machineCode"`
+	ActivateCode 	string 		`gorm:"column:activateCode" json:"activateCode"`
+	AppId 			string 		`gorm:"column:appId" json:"appId"`
+	ExpireTime 		string 		`gorm:"column:expireTime" json:"expireTime"` 	 //婵�娲绘椂闂�
+	InstallTime 	string 		`gorm:"column:installTime" json:"installTime"`   //瀹夎鏃堕棿
+
+	AppName 		string 		`gorm:"column:appName" json:"appName"`   //姹囨�诲睍鐜板埌椤甸潰涓婏紝姝よ繘绋嬫棤娉曡幏鍙朼pp鍜宻dks琛ㄤ俊鎭�
+	IconBlob 		string 		`gorm:"column:iconBlob" json:"iconBlob"`
+	Version 		string 		`gorm:"column:version" json:"version"`
+	IsDefault 		bool 		`gorm:"column:isDefault" json:"isDefault"`
+	Upgrade 		bool 		`gorm:"column:upgrade" json:"upgrade"`
+}
+
+func (DeviceApp) TableName() string {
+	return "t_device_app"
+}
+
+func (da *DeviceApp) Insert() bool {
+	result := db.Table(da.TableName()).Create(&da)
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApp) SelectById(id string) (int64, error){
+	result := db.Table(da.TableName()).Where("id=?",id).First(&da)
+	if result.Error != nil || result.RowsAffected == 0 {
+		return 0, err
+	}
+	return result.RowsAffected, nil
+}
+
+func (da *DeviceApp) Update() bool {
+	result := db.Table(da.TableName()).Where("id=?", da.Id).Update(&da)
+	if result.Error ==nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApp) DeleteById(id string) bool {
+	result := db.Exec("delete from "+da.TableName()+" where id='"+id+"'")
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApp) FindByActivateCode(activateCode string) (list []DeviceApp, err error) {
+	result := db.Table(da.TableName()).Where("activateCode=?", activateCode).Find(&list)
+	if result.Error != nil {
+		return []DeviceApp{},result.Error
+	}
+	return list, nil
+}
+
+func (da *DeviceApp) FindByMachineCode(machineCode string, serverId string) (list []DeviceApp, err error) {
+	result := db.Table(da.TableName()).Where("machineCode=? or devId=?", machineCode, serverId).Order("installTime desc").Find(&list)
+	if result.Error != nil {
+		return []DeviceApp{},result.Error
+	}
+	return list, nil
+}
\ No newline at end of file
diff --git a/devicemanage-service/models/deviceApply.go b/devicemanage-service/models/deviceApply.go
new file mode 100644
index 0000000..71cadec
--- /dev/null
+++ b/devicemanage-service/models/deviceApply.go
@@ -0,0 +1,69 @@
+package models
+
+//璁惧娣诲姞鐢宠璁板綍
+type DeviceApply struct {
+	Id 				string 		`gorm:"column:id;primary_key;type:varchar(50);unique;not null;" json:"id"`
+	DevId 			string 		`gorm:"column:devId;unique;not null;" json:"devId"`
+	DevName 		string 		`gorm:"column:devName" json:"devName"`
+	ApplyKey 		string 		`gorm:"column:applyKey" json:"applyKey"`
+	Ip 				string 		`gorm:"column:ip" json:"ip"`
+	CreateTime 		string 		`gorm:"column:createTime" json:"createTime"`
+	Status 			int 		`gorm:"column:status;default:0;" json:"status"`  //鐘舵�� 0锛氬凡娣诲姞锛屽緟鍙戦�佺粰瀵规柟 1锛氬凡鎴愬姛鍙戦�佽姹傚埌瀵规柟锛岀瓑寰呭弽棣堛�� 2锛氬鏂瑰凡鍚屾剰 -2:瀵规柟宸叉嫆缁�
+}
+
+const (
+	ApplyStatus_KeyErr = -2   //瀵嗛挜閿欒
+	ApplyStatus_Reject = -1  //宸叉嫆缁�
+	ApplyStatus_Sending = 0  //绛夊緟鍙戦�佺粰瀵规柟
+	ApplyStatus_Waiting = 1  //绛夊緟鍝嶅簲
+	ApplyStatus_Agreed = 2   //宸插悓鎰�
+	ApplyStatus_Managed = 3  //宸插浜庣鐞嗙姸鎬�
+)
+
+func (DeviceApply) TableName() string {
+	return "t_device_apply"
+}
+
+func (da *DeviceApply) Insert() bool {
+	result := db.Table(da.TableName()).Create(&da)
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApply) FindByDevId(devId string) (int64,error) {
+	result := db.Table(da.TableName()).Where("devId=?", devId).First(&da)
+	if result.Error != nil || result.RowsAffected == 0 {
+		return 0, result.Error
+	}
+	return result.RowsAffected, nil
+}
+
+func (da *DeviceApply) Update() bool {
+	result := db.Table(da.TableName()).Where("id=?", da.Id).Update(&da)
+	if result.Error ==nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApply) DeleteById(id string) bool {
+	result := db.Exec("delete from "+da.TableName()+" where id='"+id+"'")
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (da *DeviceApply) FindByStatus(status int) (list []DeviceApply) {
+	err := db.Table(da.TableName()).Where("status=?", status).Find(&list).Error
+	if err != nil {
+		return nil
+	}
+	return list
+}
+
+func (da *DeviceApply) UpdateStatus(status int, id string) bool {
+	return db.Exec("update "+da.TableName()+" set status=? where id=?", status, id).RowsAffected >0
+}
\ No newline at end of file
diff --git a/devicemanage-service/models/deviceSdk.go b/devicemanage-service/models/deviceSdk.go
new file mode 100644
index 0000000..9506ef5
--- /dev/null
+++ b/devicemanage-service/models/deviceSdk.go
@@ -0,0 +1,85 @@
+package models
+
+type DeviceSdk struct {
+	Id 				string 		`gorm:"column:id;primary_key;type:varchar(50);unique;not null;" json:"id"`
+	DevId 			string 		`gorm:"column:devId" json:"devId"`
+	MachineCode 	string 		`gorm:"column:machineCode" json:"machineCode"`
+	ActivateCode 	string 		`gorm:"column:activateCode" json:"activateCode"`
+	SdkId 			string 		`gorm:"column:sdkId" json:"sdkId"`
+	ExpireTime 		string 		`gorm:"column:expireTime" json:"expireTime"`
+	InstallTime 	string 		`gorm:"column:installTime" json:"installTime"`   //瀹夎鏃堕棿
+
+	SdkName 		string 		`gorm:"column:sdkName" json:"sdkName"`
+	IconBlob 		string 		`gorm:"column:iconBlob" json:"iconBlob"`
+	Version 		string 		`gorm:"column:version" json:"version"`
+	Upgrade 		bool 		`gorm:"column:upgrade" json:"upgrade"`
+}
+
+func (DeviceSdk) TableName() string {
+	return "t_device_sdk"
+}
+
+func (ds *DeviceSdk) Insert() bool {
+	result := db.Table(ds.TableName()).Create(&ds)
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (ds *DeviceSdk) SelectById(id string) (int64, error){
+	result := db.Table(ds.TableName()).Where("id=?",id).First(&ds)
+	if result.Error != nil || result.RowsAffected == 0 {
+		return 0, err
+	}
+	return result.RowsAffected, nil
+}
+
+func (ds *DeviceSdk) Update() bool {
+	result := db.Table(ds.TableName()).Where("id=?", ds.Id).Update(&ds)
+	if result.Error ==nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (ds *DeviceSdk) DeleteById(id string) bool {
+	result := db.Exec("delete from "+ds.TableName()+" where id='"+id+"'")
+	if result.Error == nil && result.RowsAffected > 0 {
+		return true
+	}
+	return false
+}
+
+func (ds *DeviceSdk) FindByActivateCode(activateCode string) (list []DeviceSdk, err error) {
+	result := db.Table(ds.TableName()).Where("activateCode=?", activateCode).Find(&list)
+	if result.Error != nil {
+		return []DeviceSdk{},result.Error
+	}
+	return list, nil
+}
+
+func (ds *DeviceSdk) FindByMachineCode(machineCode string, serverId string) (list []DeviceSdk, err error) {
+	result := db.Table(ds.TableName()).Where("machineCode=? or devId=?", machineCode, serverId).Order("installTime desc").Find(&list)
+	if result.Error != nil {
+		return []DeviceSdk{},result.Error
+	}
+	return list, nil
+}
+
+func (ds *DeviceSdk) FindByMacAndSdk(macCode, serverId, sdkId string) (list []DeviceSdk, err error) {
+	result := db.Table(ds.TableName()).Where("(machineCode=? or devId=?) and sdkId=?", macCode, serverId, sdkId).Find(&list)
+	if result.Error != nil {
+		return []DeviceSdk{},result.Error
+	}
+	return list, nil
+}
+
+//鎸夎澶噄d缁熻绠楁硶鏁伴噺
+func (ds *DeviceSdk) CountByDevId(devId string) (int,error) {
+	var count int
+	if err := db.Raw("select count(1) as count from "+ds.TableName()+" where devId=?",devId).Count(&count).Error; err != nil {
+		return 0, err
+	}
+	return count, nil
+}
\ No newline at end of file
diff --git a/devicemanage-service/service/ctrl.go b/devicemanage-service/service/ctrl.go
new file mode 100644
index 0000000..ae0fcb9
--- /dev/null
+++ b/devicemanage-service/service/ctrl.go
@@ -0,0 +1,61 @@
+package service
+
+import (
+	"basic.com/valib/bhomeclient.git"
+	"encoding/json"
+	"errors"
+	"vamicro/devicemanage-service/vo"
+)
+
+func remoteCall(arg interface{}, rTopic string, targetDevId string, ip string) (*bhomeclient.Reply, error) {
+	body, err := json.Marshal(arg)
+	if err != nil {
+		return nil, err
+	}
+	r, err := WrapQueryRpc(rTopic, body, targetDevId, ip)
+	if err != nil {
+		return nil, err
+	}
+	if len(r) != 1 {
+		return nil, errors.New("no response")
+	}
+	var ret bhomeclient.Reply
+	err = json.Unmarshal(r[0].Payload, &ret)
+	if err != nil {
+		return nil, err
+	}
+	return &ret, nil
+}
+
+//鎺у埗鍏跺畠鑺傜偣鍒涘缓闆嗙兢
+func RemoteCreateCluster(arg vo.CreateClusterArg) (*bhomeclient.Reply, error){
+	return remoteCall(arg, "RemoteCreateCluster", "", arg.Ip)
+}
+
+func RemoteSearchCluster(arg vo.SearchClusterArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteSearchCluster", arg.DevId, arg.Ip)
+}
+
+func RemoteGetSearchNodes(arg vo.GetSearchNodesArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteGetSearchNodes", arg.DevId, arg.Ip)
+}
+
+func RemoteJoinCluster(arg vo.JoinClusterArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteJoinCluster", arg.DevId, arg.Ip)
+}
+
+func RemoteReboot(arg vo.RebootArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteReboot", arg.DevId, arg.Ip)
+}
+
+func RemoteUninstall(arg vo.UninstallArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteUninstall", "", "")
+}
+
+func RemoteUpgrade(arg vo.UpgradeArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteUpgrade", "", "")
+}
+
+func RemoteSysUpdate(arg vo.SysUpdateArg) (*bhomeclient.Reply, error) {
+	return remoteCall(arg, "RemoteSysUpdate", "", "")
+}
\ No newline at end of file
diff --git a/devicemanage-service/service/device.go b/devicemanage-service/service/device.go
new file mode 100644
index 0000000..e73c0da
--- /dev/null
+++ b/devicemanage-service/service/device.go
@@ -0,0 +1,189 @@
+package service
+
+import (
+	"basic.com/valib/logger.git"
+	"basic.com/valib/serf.git/client"
+	"encoding/json"
+	"errors"
+	"time"
+	"vamicro/config"
+	"vamicro/devicemanage-service/models"
+	"vamicro/system-service/serf"
+)
+
+//澶勭悊鐢宠淇℃伅锛岀淮鎶ょ姸鎬�
+func DealApply() {
+	//1.澶勭悊寰呭彂閫佽姹傜殑鏁版嵁锛屽皢璇锋眰鍙戦�佸埌鐩爣璁惧锛屽苟灏唖tatus璁剧疆涓哄凡鐢宠
+	go applying()
+	//2.澶勭悊宸查�氳繃鐨勬暟鎹紝灏嗘璁惧鍐欏叆鍒癲evice琛ㄤ腑锛岃〃绀哄彲浠ユ帶鍒舵璁惧浜�
+	go applied()
+}
+
+func applying() {
+	var da models.DeviceApply
+	for {
+		list := da.FindByStatus(models.ApplyStatus_Sending)
+		if list != nil && len(list) > 0 {
+			for _,d := range list {
+				if sendApply(d.ApplyKey, config.Server.AnalyServerId, d.Ip, d.DevId ) {
+					da.UpdateStatus(models.ApplyStatus_Waiting, d.Id)
+				}
+			}
+		}
+		time.Sleep(time.Second * 3)
+	}
+}
+
+func applied() {
+	var da models.DeviceApply
+	for {
+		list := da.FindByStatus(models.ApplyStatus_Agreed)
+		if list != nil && len(list) > 0 {
+			for _,d :=range list {
+				//1.灏嗕俊鎭啓鍏ュ埌device琛ㄤ腑
+				//2.灏嗘鏉$敵璇蜂俊鎭疆涓篗anaged鐘舵��
+				doAgreedByTx(d)
+			}
+		}
+		time.Sleep(time.Second * 3)
+	}
+}
+
+func doAgreedByTx(da models.DeviceApply)  {
+	tx := models.GetDB().Begin()
+	var err error
+	defer func() {
+		if err != nil || tx != nil {
+			tx.Rollback()
+		}
+	}()
+	var tmp models.DeviceApply
+	i, _ := tmp.FindByDevId(da.DevId)
+	if i >0 {
+		err = errors.New("device琛ㄤ腑宸插瓨鍦ㄦ璁惧")
+		return
+	}
+	//1.鑾峰彇璁惧淇℃伅
+	//d := getRemoteDevInfo(da.DevId, da.Ip)
+	//d.Id = uuid.NewV4().String()
+	//if b := d.Insert(); !b {
+	//	err = errors.New("鏂板device澶辫触")
+	//	return
+	//}
+	if !da.UpdateStatus(models.ApplyStatus_Managed, da.Id) {
+		err = errors.New("淇敼managed鐘舵�佸け璐�")
+		return
+	}
+	tx.Commit()
+}
+
+type devCollectNew struct {
+	DeviceList 	[]models.Device 	`json:"deviceList"`
+	Sdks 		[]models.DeviceSdk 	`json:"sdks"`
+	Apps 		[]models.DeviceApp 	`json:"apps"`
+}
+const CollectDeviceTopic = "collect-manage-device-info"
+func CollectManageDeviceInfo(data []byte) error {
+	applyM := make(map[string]struct{})
+	var dApply models.DeviceApply
+	list := dApply.FindByStatus(models.ApplyStatus_Managed)
+	for _, d := range list {
+		applyM[d.DevId] = struct{}{}
+	}
+	var dc devCollectNew
+	e := json.Unmarshal(data, &dc)
+	if e == nil {
+		tx := models.GetDB().Begin()
+		var err error
+		var devModel models.Device
+		var dsModel models.DeviceSdk
+		var daModel models.DeviceApp
+		defer func() {
+			if err != nil && tx != nil {
+				tx.Rollback()
+			}
+		}()
+		for _, d := range dc.DeviceList {
+			if _,ok := applyM[d.DevId]; ok {
+				if rows,_ := devModel.SelectById(d.Id); rows >0 {
+					if err = tx.Table(d.TableName()).Save(&d).Error;err != nil {
+						return err
+					}
+				} else {
+					if err = tx.Table(d.TableName()).Create(&d).Error;err != nil {
+						return err
+					}
+				}
+			}
+
+		}
+		for _,ds := range dc.Sdks {
+			if rows,_ := dsModel.SelectById(ds.Id); rows >0 {
+				if err = tx.Table(ds.TableName()).Save(&ds).Error;err != nil {
+					return err
+				}
+			} else {
+				if err = tx.Table(ds.TableName()).Create(&ds).Error;err != nil {
+					return err
+				}
+			}
+		}
+
+		for _,da := range dc.Apps {
+			if rows,_ := daModel.SelectById(da.Id); rows >0 {
+				if err = tx.Table(da.TableName()).Save(&da).Error;err != nil {
+					return err
+				}
+			} else {
+				if err = tx.Table(da.TableName()).Create(&da).Error;err != nil {
+					return err
+				}
+			}
+		}
+
+
+		tx.Commit()
+		return nil
+	} else {
+		logger.Error("unmarshal err:", e)
+		return e
+	}
+}
+
+func getRemoteDevInfo(devId, ip string)  {
+
+
+}
+
+//灏嗚姹傚彂閫佸埌鎸囧畾鑺傜偣涓�
+func sendApply(key, devId, ip, targetDevId string) bool {
+	//ipv4, _, _ := util.GetLocalIP(config.Server.NetworkAdapter)
+
+	body := map[string]interface{} {
+		"key":       key,
+		"fromDevId": devId,
+		"fromIp":    ip,
+	}
+	bts, _ := json.Marshal(body)
+
+	resp, err := WrapQueryRpc("DevAuthApply", bts, targetDevId, ip)
+
+	return err ==nil && len(resp) >0
+}
+
+func WrapQueryRpc(topic string, data []byte, targetDevId string, targetIp string) ([]client.NodeResponse, error) {
+	arg := serf.RpcParamTopic{
+		Topic: topic,
+		Data: data,
+	}
+	d, _ := json.Marshal(arg)
+
+	param := serf.RpcParam{
+		Name: serf.QueryRpc,
+		Timeout: time.Second * 5,
+		FilterNodes: []string { targetDevId },
+		Data: d,
+	}
+
+	return serf.RpcQuery(targetIp, &param)
+}
\ No newline at end of file
diff --git a/devicemanage-service/vo/ctrl.go b/devicemanage-service/vo/ctrl.go
new file mode 100644
index 0000000..46289d6
--- /dev/null
+++ b/devicemanage-service/vo/ctrl.go
@@ -0,0 +1,47 @@
+package vo
+
+type CreateClusterArg struct {
+	DevId 		string 	`json:"devId"`
+	Ip 			string 	`json:"ip"`
+	Password 	string 	`json:"password"`
+	ClusterName string 	`json:"clusterName"`
+	ClusterId 	string 	`json:"clusterId"`
+	VirtualIp 	string 	`json:"virtualIp"`
+}
+
+type SearchClusterArg struct {
+	DevId 		string 	`json:"devId"`
+	Ip 			string 	`json:"ip"`
+	Password 	string 	`json:"password"`
+}
+
+type GetSearchNodesArg struct {
+	DevId string `json:"devId"`
+	Ip    string `json:"ip"`
+}
+
+type JoinClusterArg struct {
+	DevId 		string 		`json:"devId"`
+	Ip    		string 		`json:"ip"`
+	ClusterId 	string 		`json:"clusterId"`
+	Password 	string 		`json:"password"`
+	NodeIps 	[]string 	`json:"nodeIps"`
+}
+
+type RebootArg struct {
+	DevId 		string 		`json:"devId"`
+	Ip    		string 		`json:"ip"`
+}
+
+type UninstallArg struct {
+	Id 		string 		`json:"id"`	   //鍗歌浇鐨勭畻娉曟垨搴旂敤id
+	Type 	int 		`json:"type"`  //1.绠楁硶锛�2.搴旂敤
+}
+
+type UpgradeArg struct {
+	Id 		string 		`json:"id"` 	//鍗囩骇绠楁硶鎴栧簲鐢╥d
+}
+
+type SysUpdateArg struct {
+
+}
\ No newline at end of file
diff --git a/devicemanage-service/vo/device.go b/devicemanage-service/vo/device.go
new file mode 100644
index 0000000..8039540
--- /dev/null
+++ b/devicemanage-service/vo/device.go
@@ -0,0 +1,40 @@
+package vo
+
+import (
+	"basic.com/pubsub/protomsg.git"
+	"encoding/json"
+	"vamicro/devicemanage-service/models"
+	"vamicro/proto"
+)
+
+type DevDetail struct {
+	proto.Device
+	Sdks 		[]SdkDetail		`json:"sdks"`
+	Apps 		[]AppDetail		`json:"apps"`
+}
+
+func CopyDeviceFromModel(d models.Device) (proto.Device, error) {
+	var pd proto.Device
+	b, err := json.Marshal(d)
+	if err != nil {
+		return pd, err
+	}
+	err = json.Unmarshal(b, &pd)
+	return pd, err
+}
+
+type SdkDetail struct {
+	protomsg.Sdk
+	InstallInfo
+}
+
+type AppDetail struct {
+	protomsg.App
+	InstallInfo
+}
+
+type InstallInfo struct {
+	InstallTime 		string 	`json:"installTime"`
+	ExpireTime 			string 	`json:"expireTime"`
+	ActivateCode 		string 	`json:"activateCode"`
+}
\ No newline at end of file
diff --git a/system-service/controllers/syssetconf.go b/system-service/controllers/syssetconf.go
index 117a5ce..c5b2efa 100644
--- a/system-service/controllers/syssetconf.go
+++ b/system-service/controllers/syssetconf.go
@@ -10,7 +10,6 @@
 	"time"
 	"vamicro/config"
 	"vamicro/extend/util"
-	service2 "vamicro/saas-service/service"
 	"vamicro/system-service/models"
 	"vamicro/system-service/service"
 	"vamicro/system-service/sys"
@@ -1180,7 +1179,7 @@
 	machineCode := licence.GetMachineCode()
 
 	// 鑾峰彇璁惧婵�娲绘椂闂�
-	res, err := service2.DoBusReq("/data/api-v/version/snBus", config.Server.AnalyServerId, aiot.RequestMethod_Post, aiot.RequestContentType_ApplicationJson, map[string]interface{}{})
+	res, err := service.DoBusReq("/data/api-v/version/snBus", config.Server.AnalyServerId, aiot.RequestMethod_Post, aiot.RequestContentType_ApplicationJson, map[string]interface{}{})
 	logger.Warn("snBus", string(res))
 	installTime := ""
 	if err == nil {
diff --git a/system-service/service/busRequest.go b/system-service/service/busRequest.go
new file mode 100644
index 0000000..dfbd9d8
--- /dev/null
+++ b/system-service/service/busRequest.go
@@ -0,0 +1,141 @@
+package service
+
+import (
+	"encoding/json"
+	"strings"
+	"vamicro/config"
+
+	"basic.com/valib/bhomeclient.git"
+	"basic.com/valib/bhomedbapi.git"
+	"basic.com/valib/go-aiot.git/aiotProto/aiot"
+	"basic.com/valib/logger.git"
+	"gitee.com/LearingIt/goTypeTrans/trans"
+)
+
+const (
+	Token = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3NDUwMjU5MjMsInVzZXIiOiJ7XCJpZFwiOlwiZTZjY2QzNmQtNGYxNi00NmZjLTg4ZDUtMDczNjU4NjZkMjA1XCIsXCJwZXJtaXNzaW9uc1wiOltcInByb2R1Y3RNYW5nZTpwdWJsaXNoXCIsXCJjb2RlTWFuZ2U6dmlld1wiLFwiZGV2aWNlTWFuYWdlOmFkZFwiLFwiYWRtaW5NYW5hZ2VcIixcIm9yZGVyTWFuZ2VcIixcImRldmljZU1hbmFnZTp2aWV3XCIsXCJwcm9kdWN0TWFuZ2U6YWRkXCIsXCJhZG1pbk1hbmFnZTp2aWV3XCIsXCJjb2RlTWFuZ2U6YWRkXCIsXCJwcm9kdWN0TWFuZ2U6b2ZmU2FsZVwiLFwib3JkZXJNYW5nZTpjYW5jZWxcIixcInByb2R1Y3RDZW50ZXI6ZG93bmxvYWRcIixcInByb2R1Y3RDZW50ZXI6YnV5XCIsXCJwcm9kdWN0TWFuZ2U6dmlld1wiLFwiYXBpXCIsXCJob21lXCIsXCJvcmRlck1hbmdlOnBheVwiLFwiYWRtaW5NYW5hZ2U6YWRkXCIsXCJvcmRlck1hbmdlOmRvd25sb2FkXCIsXCJwcm9kdWN0Q2VudGVyXCIsXCJkZXZpY2VNYW5hZ2U6dW5iaW5kXCIsXCJvcmRlck1hbmdlOnZpZXdcIixcImFkbWluTWFuYWdlOmVkaXRcIixcImRldmljZU1hbmFnZVwiLFwidmlwTWFuYWdlOmFkZFwiLFwidmlwTWFuYWdlOnZpZXdcIixcInByb2R1Y3RDZW50ZXI6dmlld1wiLFwidmlwTWFuYWdlOmVkaXRcIixcInZpcE1hbmFnZVwiLFwicHJvZHVjdE1hbmdlOmVkaXRcIixcImNvZGVNYW5nZVwiLFwicHJvZHVjdE1hbmdlXCJdLFwidXNlcm5hbWVcIjpcImJhc2ljXCJ9In0.vwjAFkWuEyadRLvIOGK8LFE3MjpY3SQ7j6AlTXnQDG8"
+)
+
+// string杞琲nterface
+func stringParams2paramsBody(params map[string]string) map[string]interface{} {
+	paramsBody := make(map[string]interface{})
+	for k, v := range params {
+		paramsBody[k] = v
+	}
+	return paramsBody
+}
+
+// interface杞瑂tring
+func interfaceParams2paramsBody(params map[string]interface{}) map[string]string {
+	paramsBody := make(map[string]string)
+	for k, v := range params {
+		paramsBody[k] = trans.Any2String(v)
+	}
+	return paramsBody
+}
+
+// 灏佽缁熶竴璇锋眰鏂规硶
+func DoBusReq(topic string, nodeId string, method aiot.RequestMethod, contentType aiot.RequestContentType, params map[string]interface{}) ([]byte, error) {
+	var err error
+	var data []byte
+	logger.Debug("DoBusReq...", topic, nodeId, method, contentType, GetContentTypeString(contentType), params)
+	switch method {
+	case aiot.RequestMethod_Get:
+		// 鍙戦�乬et璇锋眰
+		return DoGetToBus(nodeId, topic, interfaceParams2paramsBody(params))
+	case aiot.RequestMethod_Post:
+		// post璇锋眰
+		return DoPostToBus(nodeId, topic, GetContentTypeString(contentType), params)
+	case aiot.RequestMethod_Put:
+		// put
+		return DoPutToBus(nodeId, topic, GetContentTypeString(contentType), params)
+	case aiot.RequestMethod_Delete:
+		// delete
+		return DoDeleteToBus(nodeId, topic, GetContentTypeString(contentType), params)
+	}
+	return data, err
+}
+
+// 鑾峰彇鑺傜偣IP
+func GetNodeIp(nodeId string) (string, error) {
+	client := bhomedbapi.NewClient(bhomedbapi.WithNodes(nil))
+	params := map[string]interface{}{
+		"node_id": nodeId,
+	}
+	node, err := client.DoPostRequest("/data/api-v/cluster/findIpByNode", bhomedbapi.CONTENT_TYPE_JSON, params, nil, nil)
+	if err != nil {
+		logger.Error("Fail to execute GetNodeIp", err)
+		return "", err
+	}
+
+	var ret bhomeclient.Reply
+	err = json.Unmarshal(node, &ret)
+	if err != nil {
+		return "", err
+	}
+	ip := trans.Any2String(ret.Data)
+	if ip != "" && strings.Index(ip, ":") > 0 {
+		return ip[0:strings.Index(ip, ":")], nil
+	}
+	return ip, nil
+}
+
+// bus-get璇锋眰
+func DoGetToBus(nodeId string, url string, params map[string]string) ([]byte, error) {
+	ip, _ := GetNodeIp(nodeId)
+	header := map[string]string{
+		"Authorization": Token,
+	}
+	client := bhomedbapi.NewClient(bhomedbapi.WithIp(ip), bhomedbapi.WithDevId(nodeId), bhomedbapi.WithTopic(url))
+	if client == nil || nodeId != config.Server.AnalyServerId {
+		logger.Warn("client is nil. return from bus to http")
+		client = bhomedbapi.HttpClient{}
+		url = "http://" + ip + ":8888/" + strings.Trim(url, "/")
+	}
+	return client.DoGetRequest(url, params, header)
+}
+
+// bus-post璇锋眰
+func DoPostToBus(nodeId string, url string, contentType string, params map[string]interface{}) ([]byte, error) {
+	ip, _ := GetNodeIp(nodeId)
+	header := map[string]string{
+		"Authorization": Token,
+	}
+	client := bhomedbapi.NewClient(bhomedbapi.WithIp(ip), bhomedbapi.WithDevId(nodeId), bhomedbapi.WithTopic(url))
+	if client == nil || nodeId != config.Server.AnalyServerId {
+		logger.Warn("client is nil. return from bus to http")
+		client = bhomedbapi.HttpClient{}
+		url = "http://" + ip + ":8888/" + strings.Trim(url, "/")
+	}
+	return client.DoPostRequest(url, contentType, params, nil, header)
+}
+
+// bus-put璇锋眰
+func DoPutToBus(nodeId string, url string, contentType string, params map[string]interface{}) ([]byte, error) {
+	ip, _ := GetNodeIp(nodeId)
+	header := map[string]string{
+		"Authorization": Token,
+	}
+	client := bhomedbapi.NewClient(bhomedbapi.WithIp(ip), bhomedbapi.WithDevId(nodeId), bhomedbapi.WithTopic(url))
+	if client == nil || nodeId != config.Server.AnalyServerId {
+		logger.Warn("client is nil. return from bus to http")
+		client = bhomedbapi.HttpClient{}
+		url = "http://" + ip + ":8888/" + strings.Trim(url, "/")
+	}
+	return client.DoPutRequest(url, contentType, params, header)
+}
+
+// bus-delete璇锋眰
+func DoDeleteToBus(nodeId string, url string, contentType string, params map[string]interface{}) ([]byte, error) {
+	ip, _ := GetNodeIp(nodeId)
+	header := map[string]string{
+		"Authorization": Token,
+	}
+	client := bhomedbapi.NewClient(bhomedbapi.WithIp(ip), bhomedbapi.WithDevId(nodeId), bhomedbapi.WithTopic(url))
+	if client == nil || nodeId != config.Server.AnalyServerId {
+		logger.Warn("client is nil. return from bus to http")
+		client = bhomedbapi.HttpClient{}
+		url = "http://" + ip + ":8888/" + strings.Trim(url, "/")
+	}
+	return client.DoDeleteRequest(url, contentType, params, header)
+}
diff --git a/system-service/service/toNode.go b/system-service/service/toNode.go
new file mode 100644
index 0000000..04c8aee
--- /dev/null
+++ b/system-service/service/toNode.go
@@ -0,0 +1,80 @@
+package service
+
+import (
+	"encoding/json"
+	"errors"
+
+	"basic.com/valib/bhomedbapi.git"
+	"basic.com/valib/go-aiot.git/aiotProto/aiot"
+	"basic.com/valib/logger.git"
+)
+
+// 杩斿洖缁撴灉
+type Reply struct {
+	Success bool        `json:"success"`
+	Msg     string      `json:"msg"`
+	Data    interface{} `json:"data"`
+}
+
+// 鑾峰彇content-type瀛楃涓�
+func GetContentTypeString(contentType aiot.RequestContentType) string {
+	switch contentType {
+	case aiot.RequestContentType_ApplicationJson:
+		// application/json 鏍煎紡
+		return bhomedbapi.CONTENT_TYPE_JSON
+	case aiot.RequestContentType_ApplicationXWwwFormUrlencoded:
+		// application/x-www-form-urlencoded 鏍煎紡
+		return bhomedbapi.CONTENT_TYPE_FORM
+	case aiot.RequestContentType_MultipartFormData:
+		// multipart/form-data 鏍煎紡
+		return bhomedbapi.CONTENT_TYPE_MULFORM
+	case aiot.RequestContentType_ApplicationXml:
+		// application/xml
+		return "application/xml"
+	default:
+		// application/json 鏍煎紡
+		return bhomedbapi.CONTENT_TYPE_JSON
+	}
+}
+
+// 鍗曡妭鐐硅姹�
+func NodeReq(msg *aiot.Protocol, req *aiot.NodeReq, reply *aiot.BusinessReply) error {
+	// 鏍规嵁鍙傛暟璋冪敤鏈嶅姟
+	var data []byte
+	param := make(map[string]interface{})
+	err := json.Unmarshal(req.Req, &param)
+	if err != nil {
+		logger.Error("Fail to Unmarshal req.Req", req.Req, err)
+		return err
+	}
+
+	var nodeId string
+	if len(msg.DeviceProto.DeviceIds) == 1 {
+		nodeId = msg.DeviceProto.DeviceIds[0]
+	} else if msg.DeviceProto.MasterDeviceId != "" {
+		// debug_test
+		nodeId = msg.DeviceProto.MasterDeviceId
+	} else {
+		err = errors.New("the node can not be null for NodeReq")
+		logger.Error("The node is null", err, msg)
+		return err
+	}
+
+	logger.Debugf("bus return result nodeId=%v, topic=%v, Method=%v, DeviceProto=%v", nodeId, req.Topic, req.Method, msg.DeviceProto)
+	data, err = DoBusReq(req.Topic, nodeId, req.Method, req.ContentType, param)
+	dataReply := Reply{}
+	_ = json.Unmarshal(data, &dataReply)
+
+	reply.Code = 200
+	if !dataReply.Success {
+		reply.Code = 500
+	}
+	reply.Success = dataReply.Success
+	reply.Msg = dataReply.Msg
+	reply.Data, _ = json.Marshal(dataReply.Data)
+
+	if err != nil {
+		return err
+	}
+	return nil
+}

--
Gitblit v1.8.0