zhangzengfei
2023-09-05 63645d248c765244488cd34dbc1bb6528ca6b7c7
version-control/service/upgrade.go
@@ -1,941 +1,941 @@
package service
import (
   "basic.com/valib/bhomeclient.git"
   "basic.com/valib/bhomedbapi.git"
   "basic.com/valib/c_bhomebus.git/proto/source/bhome_msg"
   "basic.com/valib/licence.git"
   "basic.com/valib/logger.git"
   "basic.com/valib/version.git"
   "context"
   "crypto/md5"
   "encoding/base64"
   "encoding/json"
   "errors"
   "fmt"
   "github.com/skip2/go-qrcode"
   "io"
   "io/ioutil"
   "net/http"
   "net/url"
   "os"
   "path"
   "runtime"
   "strings"
   "sync"
   "time"
   "vamicro/config"
   "vamicro/extend/util"
   "vamicro/version-control/models"
   "vamicro/version-control/response"
   "vamicro/version-control/utils"
)
const (
   uriVersion  string = "/data/api-u/upgrade/findUpgradeVersion"
   uriDownload string = "/data/api-p/download"
   uriAuth     string = "/data/api-s/authorization"
   uriMobile   string = "/data/api-s/fromQrcode"
)
type (
   Payload struct {
      Arch     string            `json:"arch"`
      Programs []*models.Program `json:"programs"`
      Version  string            `json:"version"`
      Intro    string            `json:"intro"`
      PatchUrl string            `json:"patchUrl"`
   }
   UpdateInfo struct {
      response.ResponseHead
      Data []Payload `json:"data"`
   }
   VersionInfo struct {
      Build   string `json:"build"`
      Commit  string `json:"commit"`
      Name    string `json:"name"`
      Version string `json:"version"`
   }
   UpdateNotice struct {
      NoticeUser        map[string]int
      HaveNewVersion    int64 //是否有新版本,1有,0没有
      PkgDownloaded     int64 //是否下载完成,1有,0没有
      LastNoticeTime    int64
      NewVersionProgram []*models.Program
      NoticeStatus      bool
   }
)
var (
   updateNotice     UpdateNotice
   updateNoticeLock sync.Mutex
   LastDownFile     []string
   LastDownLock     sync.RWMutex
)
func Init() {
   backUpPath := GetBackupPath()
   if _, err := os.Stat(backUpPath); os.IsNotExist(err) {
      _ = os.MkdirAll(backUpPath, 0777)
   }
   preDownPath := GetPreDownPath()
   if _, err := os.Stat(preDownPath); os.IsNotExist(err) {
      _ = os.MkdirAll(preDownPath, 0777)
   }
   updateNotice.NoticeUser = make(map[string]int)
   LastDownFile = make([]string, 0)
}
func OnlineUpgrade() ([]*models.Program, error) {
   programMap := make(map[string]*models.Program)
   LastDownLock.Lock()
   defer LastDownLock.Unlock()
   //查找预下载升级包
   preDowns := GetLastDownFile()
   logger.Info("系统开始升级!")
   for _, preDown := range preDowns {
      logger.Info(preDowns)
      ps, err := UpgradeViaZip(preDown)
      if err != nil {
         return []*models.Program{}, err
      }
      for _, p := range ps {
         programMap[p.Name] = p
      }
   }
   logger.Info("系统升级完成!!")
   if len(programMap) > 0 { //使用预下载升级完成
      programs := make([]*models.Program, 0, len(programMap))
      for _, v := range programMap {
         programs = append(programs, v)
      }
      return programs, nil
   }
   info, err := getUpdateInfo()
   if nil != err {
      return []*models.Program{}, errors.New("系统没有更新可用")
   }
   logger.Info("系统升级开始!")
   //循环下载,解压,覆盖升级包
   for _, payload := range info.Data {
      ps, err := HandlerPatchPkg(payload)
      if err != nil {
         return []*models.Program{}, err
      }
      for _, p := range ps {
         programMap[p.Name] = p
      }
   }
   logger.Info("系统升级完成!!")
   if len(programMap) > 0 { //
      programs := make([]*models.Program, 0, len(programMap))
      for _, v := range programMap {
         programs = append(programs, v)
      }
      return programs, nil
   }
   return []*models.Program{}, err
}
//循环下载,解压,覆盖升级包
func HandlerPatchPkg(payload Payload) ([]*models.Program, error) {
   if payload.PatchUrl == "" {
      return []*models.Program{}, errors.New("No dist file get")
   }
   //u, err := url.Parse("http://" + util.GetShopUrl() + uriDownload)
   u, err := url.Parse(payload.PatchUrl)
   if err != nil {
      logger.Error("parse url failed, url:", payload.PatchUrl, ", err:", err.Error())
      return []*models.Program{}, err
   }
   upgradePath := GetPreDownPath()
   upfile := upgradePath + "/" + GetMd5(payload.Version) + ".tgz"
   //tmpFile, err := ioutil.TempFile("", "dist-*.zip")
   tmpFile, err := os.OpenFile(upfile, os.O_CREATE|os.O_WRONLY, 0666)
   if nil != err {
      logger.Error("OnlineUpgrade create temp file failed, err:", err.Error())
      return []*models.Program{}, err
   }
   fmt.Println("OnlineUpgrade tmpFile.Name: ", tmpFile.Name())
   defer func() {
      tmpFile.Close()
      backupPath := GetBackupPath()
      //文件从upgrade目录移动到backup目录
      os.Rename(tmpFile.Name(), backupPath+"/"+tmpFile.Name())
      //os.Remove(tmpFile.Name())
   }()
   //query := u.Query()
   //query.Set("filename", info.Data.Archive)
   //u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("OnlineUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      return []*models.Program{}, err
   }
   defer resp.Body.Close()
   if resp.StatusCode != 200 {
      logger.Error("OnlineUpgrade incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return []*models.Program{}, errors.New("Status code not 200")
   }
   _, err = io.Copy(tmpFile, resp.Body)
   if err != nil {
      logger.Error("OnlineUpgrade save upgrade file failed, url:", u.String(), ", err:", err.Error())
      return []*models.Program{}, err
   }
   return UpgradeViaZip(tmpFile.Name())
}
//循环下载升级包
func DownloadPatchPkg(payload Payload) (string, error) {
   if payload.PatchUrl == "" {
      return "", errors.New("no dist file get")
   }
   //u, err := url.Parse("http://" + util.GetShopUrl() + uriDownload)
   u, err := url.Parse(payload.PatchUrl)
   if err != nil {
      logger.Error("parse url failed, url:", payload.PatchUrl, ", err:", err.Error())
      return "", err
   }
   upgradePath := GetPreDownPath()
   upfile := upgradePath + "/" + GetMd5(payload.Version) + ".tgz"
   _, err = os.Stat(upfile)
   if nil == err {
      //已下载过
      logger.Info("PreDownUpgrade predown upgrade file have down:", upfile)
      return upfile, nil
   }
   tmpFile, err := os.OpenFile(upfile, os.O_CREATE|os.O_WRONLY, 0666)
   if nil != err {
      logger.Error("OnlineUpgrade create temp file failed, err:", err.Error())
      return "", err
   }
   fmt.Println("OnlineUpgrade tmpFile.Name: ", tmpFile.Name())
   defer func() {
      tmpFile.Close()
   }()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("OnlineUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      return "", err
   }
   defer resp.Body.Close()
   if resp.StatusCode != 200 {
      logger.Error("OnlineUpgrade incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return "", errors.New("status code not 200")
   }
   _, err = io.Copy(tmpFile, resp.Body)
   if err != nil {
      logger.Error("PreDownUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      tmpFile.Close()
      os.Remove(upfile)
      return "", err
   }
   return upfile, nil
}
//检查是否需要更新版本
func CheckVersion() (string, string, string) {
   //获取服务端所有程序最新版本信息。
   info, err := getUpdateInfo()
   if nil != err {
      return "", "", ""
   }
   if len(info.Data) <= 0 {
      logger.Error("checkVersion no programs get")
      return "", "", ""
   }
   //获取需要更新版本的程序列表
   //programs := needUpgrade(info.Data.Programs)
   last := len(info.Data) - 1
   return info.Data[last].PatchUrl, info.Data[last].Version, info.Data[last].Intro
}
func GetCurVersion() string {
   curEnv, err := GetRunVersionEnv()
   if err != nil {
      return ""
   }
   return curEnv
}
//获取服务端最新程序及其版本
func getUpdateInfo() (*UpdateInfo, error) {
   //const PrtSize = 32 << uintptr(^uintptr(0)>>63)
   //wordSize := strconv.Itoa(PrtSize)
   u, err := url.Parse("http://" + util.GetShopUrl() + uriVersion)
   if err != nil {
      logger.Error("parse url failed, url:", "http://"+util.GetShopUrl()+uriVersion, ", err:", err.Error())
      return nil, err
   }
   query := u.Query()
   //query.Set("os", runtime.GOOS)
   query.Set("arch", runtime.GOARCH)
   query.Set("versionNum", GetCurVersion())
   //query.Set("wordSize", wordSize)
   u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("checkVersion parse url failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   if resp.StatusCode != 200 {
      logger.Error("checkVersion incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return nil, errors.New("Status code not 200")
   }
   defer resp.Body.Close()
   body, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      logger.Error("checkVersion read body failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   logger.Info("checkVersion dump body:", string(body))
   var info UpdateInfo
   err = json.Unmarshal(body, &info)
   if err != nil {
      logger.Error("checkVersion json.Unmarshal failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   //b, err := json.Marshal(&info)
   //if err != nil {
   //   logger.Error("checkVersion json.Marshal failed, url:", u.String(), ", err:", err.Error())
   //} else {
   //   logger.Info("checkVersion get response, url:", u.String(), ", response:", string(b))
   //}
   return &info, nil
}
//需要升级的程序
func needUpgrade(programs []*models.Program) []*models.Program {
   //获取当前运行版本目录
   //dir := utils.GetExePath()
   dir := util.GetVamicroPath()
   newPrograms := []*models.Program{}
   for _, p := range programs {
      exe := path.Join(dir, p.Name)
      if !utils.PathExists(exe) {
         newPrograms = append(newPrograms, p)
         continue
      }
      lVersion, err := dynamicGetVersion(dir, exe)
      if err != nil {
         logger.Error("exe:", exe, "get local version failed, err:", err.Error())
         continue
      }
      //版本名称转换为版本号
      rVersion, err := vaversion.VersionName2VaVersion(p.Version)
      if err != nil {
         logger.Error("exe:", exe, "get remote version failed:", p.Version, ", err:", err.Error())
         continue
      }
      c, err := lVersion.Compare(rVersion)
      if err != nil {
         logger.Error("exe:", exe, "version compare failed, remote:", p.Version, ", err:", err.Error())
         continue
      }
      if c > 0 {
         logger.Error("exe:", exe, "local version greater, local:", lVersion, "remote:", p.Version)
         continue
      }
      if c < 0 {
         newPrograms = append(newPrograms, p)
      }
   }
   return newPrograms
}
func NoticeTick(c context.Context) {
   // tick := time.Tick(1 * time.Second)
   tick := time.Tick(24 * time.Hour)
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         //logger.Info("NoticeTick !!!")
         updateNoticeLock.Lock()
         for key, _ := range updateNotice.NoticeUser {
            if 0 < updateNotice.NoticeUser[key] {
               updateNotice.NoticeUser[key]--
            }
         }
         updateNoticeLock.Unlock()
      }
   }
}
//升级配置处理
func SelfUpdateStart(c context.Context, ms *bhomeclient.MicroNode) {
   //三十秒检查一次配置并更新
   // tick := time.Tick(30 * time.Second)
   tick := time.Tick(24 * time.Hour)
   var setting models.SysSetting
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         settings, err := setting.GetAllSetting()
         if nil != err {
            logger.Error("fetch setting failed")
            continue
         }
         for _, set := range settings {
            data, _ := json.Marshal(set)
            var nodes []bhome_msg.BHAddress
            nodes = append(nodes, bhome_msg.BHAddress{})
            go ms.PublishNetTimeout(nodes, SysUpdateConfigTopic, data, 10)
            switch set.Name {
            case "sys_auto_clean":
               if "1" == set.Value {
                  err := os.RemoveAll(GetBackupPath())
                  if nil != err {
                     logger.Error("clean update package failed:", err.Error())
                  }
                  _ = os.MkdirAll(GetBackupPath(), 0777)
               }
            case "sys_update_notice":
               {
                  // 判断有新版本, 不再重复检测
                  if updateNotice.HaveNewVersion == 1 {
                     continue
                  }
                  // 设备更新提醒
                  updateNoticeLock.Lock()
                  //获取需要更新版本的程序列表
                  _, version, _ := CheckVersion()
                  curVersion := GetCurVersion()
                  IsLastUpdate := true
                  if version != curVersion {
                     // 设置更新提醒, 重新下载升级包
                     updateNotice.HaveNewVersion = 1
                     updateNotice.PkgDownloaded = 0
                     IsLastUpdate = true
                  } else {
                     IsLastUpdate = false
                  }
                  if !IsLastUpdate { //如果没有更新,设置用户延迟时间为0
                     for key, _ := range updateNotice.NoticeUser {
                        if 0 > updateNotice.NoticeUser[key] {
                           updateNotice.NoticeUser[key] = 0
                        }
                     }
                  }
                  if "1" == set.Value {
                     updateNotice.NoticeStatus = true
                  } else {
                     updateNotice.NoticeStatus = false
                  }
                  updateNoticeLock.Unlock()
               }
            case "sys_auto_update":
               {
                  if "1" == set.Value && updateNotice.HaveNewVersion > 0 && updateNotice.PkgDownloaded == 0 {
                     err := PreDownUpdateFile()
                     if nil != err {
                        logger.Error("pre download update file failed:", err.Error())
                     } else {
                        updateNotice.PkgDownloaded = 1
                     }
                  }
               }
            }
         }
      }
   }
}
//检查是否距上次比较有更新
func IsLastUpdate(programs []*models.Program, NewVersionProgram []*models.Program) bool {
   for _, program := range programs {
      for _, program2 := range NewVersionProgram {
         if program.Name == program2.Name {
            if program.Version != program2.Version {
               return true
            }
         }
      }
   }
   return true
}
//获取更新提醒
func GetUpdateNotice() UpdateNotice {
   return updateNotice
}
//延迟提醒
func DelayNotice(uid string, second int) UpdateNotice {
   updateNoticeLock.Lock()
   updateNotice.NoticeUser[uid] = second
   updateNoticeLock.Unlock()
   return updateNotice
}
//获取代码备份位置
func GetBackupPath() string {
   dir := util.GetVamicroPath() + "/backup"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./backup"
      }
   }
   return dir
}
//获取解压后的补丁包位置
func GetPatchPath() string {
   dir := util.GetVamicroPath() + "/patch"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./patch"
      }
   }
   return dir
}
//获取预下载升级包目录
func GetPreDownPath() string {
   dir := util.GetVamicroPath() + "/upgrade"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./upgrade"
      }
   }
   return dir
}
//获取上传升级包目录
func GetPreUploadPath() string {
   dir := util.GetVamicroPath() + "/upgrade_manual"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./upgrade_manual"
      }
   }
   return dir
}
func GetMd5(in string) string {
   data := md5.Sum([]byte(in))
   return fmt.Sprintf("%x", data)
}
//预下载升级文件
func PreDownUpdateFile() error {
   info, err := getUpdateInfo()
   if nil != err {
      return err
   }
   LastDownLock.Lock()
   defer LastDownLock.Unlock()
   LastDownFile = make([]string, 0)
   //循环下载升级包
   for _, payload := range info.Data {
      logger.Info("正在下载版本补丁【" + payload.Version + "】")
      upfile, err := DownloadPatchPkg(payload)
      logger.Info("下载版本补丁完成【" + payload.Version + "】")
      if err != nil {
         return err
      }
      LastDownFile = append(LastDownFile, upfile)
   }
   return nil
}
//获取预下载升级包
func GetLastDownFile() []string {
   var res = make([]string, 0)
   if len(LastDownFile) > 0 {
      for _, file := range LastDownFile {
         _, err := os.Stat(file)
         if nil == err {
            res = append(res, file)
         }
      }
   }
   return res
}
//回滚版本
func Rollback(version string) error {
   dir := util.GetVamicroPath() + "/" + version
   _, err := os.Stat(dir)
   if nil != err {
      return err
   }
   SetRunVersionEnv(version)
   versionEnv = version
   return nil
}
type RegUserInfo struct {
   UserType   string `json:"userType"`   //个人:personal  公司: company
   PhoneNum   string `json:"phoneNum"`   //手机号码
   Name       string `json:"name"`       //姓名或公司名称
   ProvinceId string `json:"provinceId"` //省
   CityId     string `json:"cityId"`     //市
   CountyId   string `json:"countyId"`   //县
   Email      string `json:"email"`      //邮箱
}
//获取授权
func Authorization(code string, isManual bool) (authinfo util.AuthorizationInfo, err error) {
   sn := util.GetVamicroPath() + "/sn.txt"
   authorization := util.GetVamicroPath() + "/auth.txt"
   activateCode := ""
   if len(code) == 29 && code[5:6] == "-" { //25位激活码激活
      activateCode = code
   }
   if "" != code && len(code) != 29 && code[5:6] != "-" {
      authinfo, err := util.GetAuthorizationInfo(code)
      if nil == err {
         activateCode = authinfo.ActivateCode
         logger.Debug("code found:" + code)
         if isManual { //手动操作更新授权文件,则立即返回,不用再次访问商城
            ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
            ioutil.WriteFile(authorization, []byte(code), os.ModePerm)
            return authinfo, nil
         } else {
            //否则以商城为准
            defer func() {
               if nil != err {
                  //ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
                  //ioutil.WriteFile(authorization, []byte(code), os.ModePerm)
               }
            }()
         }
      } else {
         return authinfo, errors.New("非法的授权!!")
      }
   }
   devId := config.Server.AnalyServerId
   machineCode := licence.GetMachineCode()
   authinfo, authcode, err := postAuthReq(util.GetSn(), activateCode, devId, machineCode, "")
   if nil != err {
      logger.Error("Authorization err:", err.Error(), authcode)
      //ioutil.WriteFile(authorization, []byte(authcode), os.ModePerm)  //bug:局域网会把授权清除
      return authinfo, err
   }
   if authinfo.MachineCode != machineCode {
      logger.Error("GetAuthorization machineCode not match, local:", machineCode, " remote:", authinfo.MachineCode)
      return authinfo, errors.New("授权不匹配")
   }
   ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
   ioutil.WriteFile(authorization, []byte(authcode), os.ModePerm)
   return authinfo, nil
}
func postAuthReq(sn string, activateCode string, deviceId string, machineCode string, oldDeviceId string) (authinfo util.AuthorizationInfo, authcode string, err error) {
   u, err := url.Parse("http://" + util.GetShopUrl() + uriAuth)
   if err != nil {
      logger.Error("parse url failed, url:", "http://"+util.GetShopUrl()+uriAuth, ", err:", err.Error())
      return authinfo, "", err
   }
   query := u.Query()
   query.Set("sn", sn)
   query.Set("activateCode", activateCode)
   query.Set("deviceId", deviceId)
   query.Set("machineCode", machineCode)
   query.Set("oldDeviceId", oldDeviceId)
   query.Set("deviceType", config.Server.DeviceType)
   query.Set("deviceMode", config.Server.DeviceModel)
   query.Set("vGpu", util.GetVGpu())
   var sysInitApi bhomedbapi.SysInitApi
   b, rInfo := sysInitApi.GetRegInfo()
   if b {
      rbd, e := json.Marshal(rInfo)
      if e == nil {
         var sysRI RegUserInfo
         if e = json.Unmarshal(rbd, &sysRI); e == nil {
            query.Set("userType", sysRI.UserType)
            query.Set("name", sysRI.Name)
            query.Set("phoneNum", sysRI.PhoneNum)
            query.Set("provinceId", sysRI.ProvinceId)
            query.Set("cityId", sysRI.CityId)
            query.Set("countyId", sysRI.CountyId)
            query.Set("email", sysRI.Email)
         } else {
            logger.Error("json.Unmarshal sysRI e:", e)
         }
      } else {
         logger.Error("json.Marshal rInfo e:", e)
      }
   }
   u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("GetAuthorization parse url failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   if resp.StatusCode != 200 {
      logger.Error("GetAuthorization incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return authinfo, "", errors.New("Status code not 200")
   }
   defer resp.Body.Close()
   body, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      logger.Error("GetAuthorization read body failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   logger.Info("postAuthReq body:", string(body))
   var info map[string]interface{}
   err = json.Unmarshal(body, &info)
   if err != nil {
      logger.Error("GetAuthorization json.Unmarshal failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   authinfo, err = util.GetAuthorizationInfo(info["data"].(string))
   if nil == err {
      logger.Error("err:", err)
      return authinfo, info["data"].(string), nil
   }
   if 500 == int(info["code"].(float64)) {
      logger.Error("ret 500")
      return authinfo, "", errors.New(info["msg"].(string))
   }
   if true != info["success"].(bool) {
      logger.Error("not success")
      return authinfo, "", errors.New(info["msg"].(string))
   }
   return authinfo, info["data"].(string), nil
}
func GenQRCode() ([]byte, string, error) {
   sn := util.GetSn()
   var authInfo util.AuthorizationInfo
   authInfo.Sn = sn
   authInfo.DevId = config.Server.AnalyServerId
   authInfo.MachineCode = licence.GetMachineCode()
   authInfo.DeviceType = config.Server.DeviceType
   authInfo.DeviceMode = config.Server.DeviceModel
   authInfo.VGpu = util.GetVGpu()
   var sysInitApi bhomedbapi.SysInitApi
   if bsi, rInfo := sysInitApi.GetRegInfo(); bsi {
      rbd, e := json.Marshal(rInfo)
      if e == nil {
         var sysRI RegUserInfo
         if e = json.Unmarshal(rbd, &sysRI); e == nil {
            authInfo.UserType = sysRI.UserType
            authInfo.Name = sysRI.Name
            authInfo.PhoneNum = sysRI.PhoneNum
            authInfo.ProvinceId = sysRI.ProvinceId
            authInfo.CityId = sysRI.CityId
            authInfo.CountyId = sysRI.CountyId
            authInfo.Email = sysRI.Email
         } else {
            logger.Error("json.Unmarshal sysRI e:", e)
         }
      } else {
         logger.Error("json.Marshal rInfo e:", e)
      }
   }
   hackQ, _ := HackAuthorizationInfo(authInfo)
   url := "http://" + util.GetShopUrl() + uriMobile + "?q=" + hackQ
   logger.Info("qrcode len:", len(url), " content:", url)
   // 生成二维码
   q, err := qrcode.New(url, qrcode.Highest)
   if err != nil {
      return nil, url, err
   }
   png, err1 := q.PNG(350)
   return png, url, err1
}
//刷新授权到其他进程
func AuthorizationUpdate(c context.Context, ms *bhomeclient.MicroNode) {
   //三十秒检查一次配置并更新
   // tick := time.Tick(30 * time.Second)
   tick := time.Tick(24 * time.Hour)
   var authInfo util.AuthorizationInfo
   var err error
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         author := util.GetAuthorization()
         sn := util.GetSn()
         authInfo, err = Authorization(sn, false)
         if nil != err && !strings.Contains(err.Error(), "成功") {
            logger.Error("GetAuthorization error:", err.Error())
            if "" != author {
               authInfo, err = util.GetAuthorizationInfo(author)
            }
         }
         logger.Debug("authInfo:", authInfo)
         data, _ := json.Marshal(authInfo)
         var nodes []bhome_msg.BHAddress
         nodes = append(nodes, bhome_msg.BHAddress{})
         go ms.PublishNetTimeout(nodes, AuthorizationUpdateTopic, data, 10)
      }
   }
}
func HackAuthorizationInfo(authorizationInfo util.AuthorizationInfo) (string, error) {
   b, err := json.Marshal(authorizationInfo)
   if nil != err {
      return "", err
   }
   logger.Debug("authorInfo", authorizationInfo)
   info, err := util.RsaEncrypt(b)
   if nil != err {
      logger.Error("HackAuthorizationInfo utils.RsaEncrypt failed, err:", err.Error())
      return "", err
   }
   return base64.StdEncoding.EncodeToString(info), nil
}
func GetQ() string {
   sn := util.GetSn()
   var authinfo util.AuthorizationInfo
   authinfo.Sn = sn
   authinfo.DevId = config.Server.AnalyServerId
   authinfo.MachineCode = licence.GetMachineCode()
   authinfo.VGpu = util.GetVGpu()
   q, err := HackAuthorizationInfo(authinfo)
   if nil == err {
      return q
   }
   return err.Error()
}
func CancelAuthorization(passwd string, q string) (error, string) {
   uApi := bhomedbapi.UserApi{}
   ok, _ := uApi.Login("basic", passwd)
   if !ok {
      return errors.New("密码不正确!"), ""
   }
   //获取请求码里的devId
   qInfo, err := util.GetAuthorizationInfo(q)
   if nil != err {
      return errors.New("请求码格式不正确"), ""
   }
   curAuthInfo := util.GetAuthorization()
   //获取当前授权码
   authInfo, err := util.GetAuthorizationInfo(curAuthInfo)
   if nil != err {
      return errors.New("当前服务器授权信息不完整"), ""
   }
   authInfo.OldDeviceId = authInfo.DevId
   authInfo.DevId = qInfo.DevId
   authInfo.VGpu = util.GetVGpu()
   sn := util.GetVamicroPath() + "/sn.txt"
   authorization := util.GetVamicroPath() + "/auth.txt"
   authCode, err := HackAuthorizationInfo(authInfo)
   if nil != err {
      return errors.New("产品密钥导出失败"), ""
   }
   ioutil.WriteFile(sn, []byte(""), os.ModePerm)
   time.Sleep(100 * time.Microsecond)
   ioutil.WriteFile(authorization, []byte(""), os.ModePerm)
   _, _, err = postAuthReq(authInfo.Sn, authInfo.ActivateCode, qInfo.DevId, licence.GetMachineCode(), authInfo.OldDeviceId)
   if nil != err {
      return err, ""
   }
   return nil, authCode
}
package service
import (
   "basic.com/valib/bhomeclient.git"
   "basic.com/valib/bhomedbapi.git"
   "basic.com/valib/c_bhomebus.git/proto/source/bhome_msg"
   "basic.com/valib/licence.git"
   "basic.com/valib/logger.git"
   "basic.com/valib/version.git"
   "context"
   "crypto/md5"
   "encoding/base64"
   "encoding/json"
   "errors"
   "fmt"
   "github.com/skip2/go-qrcode"
   "io"
   "io/ioutil"
   "net/http"
   "net/url"
   "os"
   "path"
   "runtime"
   "strings"
   "sync"
   "time"
   "vamicro/config"
   "vamicro/extend/util"
   "vamicro/version-control/models"
   "vamicro/version-control/response"
   "vamicro/version-control/utils"
)
const (
   uriVersion  string = "/data/api-u/upgrade/findUpgradeVersion"
   uriDownload string = "/data/api-p/download"
   uriAuth     string = "/data/api-s/authorization"
   uriMobile   string = "/data/api-s/fromQrcode"
)
type (
   Payload struct {
      Arch     string            `json:"arch"`
      Programs []*models.Program `json:"programs"`
      Version  string            `json:"version"`
      Intro    string            `json:"intro"`
      PatchUrl string            `json:"patchUrl"`
   }
   UpdateInfo struct {
      response.ResponseHead
      Data []Payload `json:"data"`
   }
   VersionInfo struct {
      Build   string `json:"build"`
      Commit  string `json:"commit"`
      Name    string `json:"name"`
      Version string `json:"version"`
   }
   UpdateNotice struct {
      NoticeUser        map[string]int
      HaveNewVersion    int64 //是否有新版本,1有,0没有
      PkgDownloaded     int64 //是否下载完成,1有,0没有
      LastNoticeTime    int64
      NewVersionProgram []*models.Program
      NoticeStatus      bool
   }
)
var (
   updateNotice     UpdateNotice
   updateNoticeLock sync.Mutex
   LastDownFile     []string
   LastDownLock     sync.RWMutex
)
func Init() {
   backUpPath := GetBackupPath()
   if _, err := os.Stat(backUpPath); os.IsNotExist(err) {
      _ = os.MkdirAll(backUpPath, 0777)
   }
   preDownPath := GetPreDownPath()
   if _, err := os.Stat(preDownPath); os.IsNotExist(err) {
      _ = os.MkdirAll(preDownPath, 0777)
   }
   updateNotice.NoticeUser = make(map[string]int)
   LastDownFile = make([]string, 0)
}
func OnlineUpgrade() ([]*models.Program, error) {
   programMap := make(map[string]*models.Program)
   LastDownLock.Lock()
   defer LastDownLock.Unlock()
   //查找预下载升级包
   preDowns := GetLastDownFile()
   logger.Info("系统开始升级!")
   for _, preDown := range preDowns {
      logger.Info(preDowns)
      ps, err := UpgradeViaZip(preDown)
      if err != nil {
         return []*models.Program{}, err
      }
      for _, p := range ps {
         programMap[p.Name] = p
      }
   }
   logger.Info("系统升级完成!!")
   if len(programMap) > 0 { //使用预下载升级完成
      programs := make([]*models.Program, 0, len(programMap))
      for _, v := range programMap {
         programs = append(programs, v)
      }
      return programs, nil
   }
   info, err := getUpdateInfo()
   if nil != err {
      return []*models.Program{}, errors.New("系统没有更新可用")
   }
   logger.Info("系统升级开始!")
   //循环下载,解压,覆盖升级包
   for _, payload := range info.Data {
      ps, err := HandlerPatchPkg(payload)
      if err != nil {
         return []*models.Program{}, err
      }
      for _, p := range ps {
         programMap[p.Name] = p
      }
   }
   logger.Info("系统升级完成!!")
   if len(programMap) > 0 { //
      programs := make([]*models.Program, 0, len(programMap))
      for _, v := range programMap {
         programs = append(programs, v)
      }
      return programs, nil
   }
   return []*models.Program{}, err
}
//循环下载,解压,覆盖升级包
func HandlerPatchPkg(payload Payload) ([]*models.Program, error) {
   if payload.PatchUrl == "" {
      return []*models.Program{}, errors.New("No dist file get")
   }
   //u, err := url.Parse("http://" + util.GetShopUrl() + uriDownload)
   u, err := url.Parse(payload.PatchUrl)
   if err != nil {
      logger.Error("parse url failed, url:", payload.PatchUrl, ", err:", err.Error())
      return []*models.Program{}, err
   }
   upgradePath := GetPreDownPath()
   upfile := upgradePath + "/" + GetMd5(payload.Version) + ".tgz"
   //tmpFile, err := ioutil.TempFile("", "dist-*.zip")
   tmpFile, err := os.OpenFile(upfile, os.O_CREATE|os.O_WRONLY, 0666)
   if nil != err {
      logger.Error("OnlineUpgrade create temp file failed, err:", err.Error())
      return []*models.Program{}, err
   }
   fmt.Println("OnlineUpgrade tmpFile.Name: ", tmpFile.Name())
   defer func() {
      tmpFile.Close()
      backupPath := GetBackupPath()
      //文件从upgrade目录移动到backup目录
      os.Rename(tmpFile.Name(), backupPath+"/"+tmpFile.Name())
      //os.Remove(tmpFile.Name())
   }()
   //query := u.Query()
   //query.Set("filename", info.Data.Archive)
   //u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("OnlineUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      return []*models.Program{}, err
   }
   defer resp.Body.Close()
   if resp.StatusCode != 200 {
      logger.Error("OnlineUpgrade incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return []*models.Program{}, errors.New("Status code not 200")
   }
   _, err = io.Copy(tmpFile, resp.Body)
   if err != nil {
      logger.Error("OnlineUpgrade save upgrade file failed, url:", u.String(), ", err:", err.Error())
      return []*models.Program{}, err
   }
   return UpgradeViaZip(tmpFile.Name())
}
//循环下载升级包
func DownloadPatchPkg(payload Payload) (string, error) {
   if payload.PatchUrl == "" {
      return "", errors.New("no dist file get")
   }
   //u, err := url.Parse("http://" + util.GetShopUrl() + uriDownload)
   u, err := url.Parse(payload.PatchUrl)
   if err != nil {
      logger.Error("parse url failed, url:", payload.PatchUrl, ", err:", err.Error())
      return "", err
   }
   upgradePath := GetPreDownPath()
   upfile := upgradePath + "/" + GetMd5(payload.Version) + ".tgz"
   _, err = os.Stat(upfile)
   if nil == err {
      //已下载过
      logger.Info("PreDownUpgrade predown upgrade file have down:", upfile)
      return upfile, nil
   }
   tmpFile, err := os.OpenFile(upfile, os.O_CREATE|os.O_WRONLY, 0666)
   if nil != err {
      logger.Error("OnlineUpgrade create temp file failed, err:", err.Error())
      return "", err
   }
   fmt.Println("OnlineUpgrade tmpFile.Name: ", tmpFile.Name())
   defer func() {
      tmpFile.Close()
   }()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("OnlineUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      return "", err
   }
   defer resp.Body.Close()
   if resp.StatusCode != 200 {
      logger.Error("OnlineUpgrade incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return "", errors.New("status code not 200")
   }
   _, err = io.Copy(tmpFile, resp.Body)
   if err != nil {
      logger.Error("PreDownUpgrade parse url failed, url:", u.String(), ", err:", err.Error())
      tmpFile.Close()
      os.Remove(upfile)
      return "", err
   }
   return upfile, nil
}
//检查是否需要更新版本
func CheckVersion() (string, string, string) {
   //获取服务端所有程序最新版本信息。
   info, err := getUpdateInfo()
   if nil != err {
      return "", "", ""
   }
   if len(info.Data) <= 0 {
      logger.Error("checkVersion no programs get")
      return "", "", ""
   }
   //获取需要更新版本的程序列表
   //programs := needUpgrade(info.Data.Programs)
   last := len(info.Data) - 1
   return info.Data[last].PatchUrl, info.Data[last].Version, info.Data[last].Intro
}
func GetCurVersion() string {
   curEnv, err := GetRunVersionEnv()
   if err != nil {
      return ""
   }
   return curEnv
}
//获取服务端最新程序及其版本
func getUpdateInfo() (*UpdateInfo, error) {
   //const PrtSize = 32 << uintptr(^uintptr(0)>>63)
   //wordSize := strconv.Itoa(PrtSize)
   u, err := url.Parse("http://" + util.GetShopUrl() + uriVersion)
   if err != nil {
      logger.Error("parse url failed, url:", "http://"+util.GetShopUrl()+uriVersion, ", err:", err.Error())
      return nil, err
   }
   query := u.Query()
   //query.Set("os", runtime.GOOS)
   query.Set("arch", runtime.GOARCH)
   query.Set("versionNum", GetCurVersion())
   //query.Set("wordSize", wordSize)
   u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("checkVersion parse url failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   if resp.StatusCode != 200 {
      logger.Error("checkVersion incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return nil, errors.New("Status code not 200")
   }
   defer resp.Body.Close()
   body, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      logger.Error("checkVersion read body failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   logger.Info("checkVersion dump body:", string(body))
   var info UpdateInfo
   err = json.Unmarshal(body, &info)
   if err != nil {
      logger.Error("checkVersion json.Unmarshal failed, url:", u.String(), ", err:", err.Error())
      return nil, err
   }
   //b, err := json.Marshal(&info)
   //if err != nil {
   //   logger.Error("checkVersion json.Marshal failed, url:", u.String(), ", err:", err.Error())
   //} else {
   //   logger.Info("checkVersion get response, url:", u.String(), ", response:", string(b))
   //}
   return &info, nil
}
//需要升级的程序
func needUpgrade(programs []*models.Program) []*models.Program {
   //获取当前运行版本目录
   //dir := utils.GetExePath()
   dir := util.GetVamicroPath()
   newPrograms := []*models.Program{}
   for _, p := range programs {
      exe := path.Join(dir, p.Name)
      if !utils.PathExists(exe) {
         newPrograms = append(newPrograms, p)
         continue
      }
      lVersion, err := dynamicGetVersion(dir, exe)
      if err != nil {
         logger.Error("exe:", exe, "get local version failed, err:", err.Error())
         continue
      }
      //版本名称转换为版本号
      rVersion, err := vaversion.VersionName2VaVersion(p.Version)
      if err != nil {
         logger.Error("exe:", exe, "get remote version failed:", p.Version, ", err:", err.Error())
         continue
      }
      c, err := lVersion.Compare(rVersion)
      if err != nil {
         logger.Error("exe:", exe, "version compare failed, remote:", p.Version, ", err:", err.Error())
         continue
      }
      if c > 0 {
         logger.Error("exe:", exe, "local version greater, local:", lVersion, "remote:", p.Version)
         continue
      }
      if c < 0 {
         newPrograms = append(newPrograms, p)
      }
   }
   return newPrograms
}
func NoticeTick(c context.Context) {
   // tick := time.Tick(1 * time.Second)
   tick := time.Tick(24 * time.Hour)
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         //logger.Info("NoticeTick !!!")
         updateNoticeLock.Lock()
         for key, _ := range updateNotice.NoticeUser {
            if 0 < updateNotice.NoticeUser[key] {
               updateNotice.NoticeUser[key]--
            }
         }
         updateNoticeLock.Unlock()
      }
   }
}
//升级配置处理
func SelfUpdateStart(c context.Context, ms *bhomeclient.MicroNode) {
   //三十秒检查一次配置并更新
   // tick := time.Tick(30 * time.Second)
   tick := time.Tick(24 * time.Hour)
   var setting models.SysSetting
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         settings, err := setting.GetAllSetting()
         if nil != err {
            logger.Error("fetch setting failed")
            continue
         }
         for _, set := range settings {
            data, _ := json.Marshal(set)
            var nodes []bhome_msg.BHAddress
            nodes = append(nodes, bhome_msg.BHAddress{})
            go ms.PublishNetTimeout(nodes, SysUpdateConfigTopic, data, 10)
            switch set.Name {
            case "sys_auto_clean":
               if "1" == set.Value {
                  err := os.RemoveAll(GetBackupPath())
                  if nil != err {
                     logger.Error("clean update package failed:", err.Error())
                  }
                  _ = os.MkdirAll(GetBackupPath(), 0777)
               }
            case "sys_update_notice":
               {
                  // 判断有新版本, 不再重复检测
                  if updateNotice.HaveNewVersion == 1 {
                     continue
                  }
                  // 设备更新提醒
                  updateNoticeLock.Lock()
                  //获取需要更新版本的程序列表
                  _, version, _ := CheckVersion()
                  curVersion := GetCurVersion()
                  IsLastUpdate := true
                  if version != curVersion {
                     // 设置更新提醒, 重新下载升级包
                     updateNotice.HaveNewVersion = 1
                     updateNotice.PkgDownloaded = 0
                     IsLastUpdate = true
                  } else {
                     IsLastUpdate = false
                  }
                  if !IsLastUpdate { //如果没有更新,设置用户延迟时间为0
                     for key, _ := range updateNotice.NoticeUser {
                        if 0 > updateNotice.NoticeUser[key] {
                           updateNotice.NoticeUser[key] = 0
                        }
                     }
                  }
                  if "1" == set.Value {
                     updateNotice.NoticeStatus = true
                  } else {
                     updateNotice.NoticeStatus = false
                  }
                  updateNoticeLock.Unlock()
               }
            case "sys_auto_update":
               {
                  if "1" == set.Value && updateNotice.HaveNewVersion > 0 && updateNotice.PkgDownloaded == 0 {
                     err := PreDownUpdateFile()
                     if nil != err {
                        logger.Error("pre download update file failed:", err.Error())
                     } else {
                        updateNotice.PkgDownloaded = 1
                     }
                  }
               }
            }
         }
      }
   }
}
//检查是否距上次比较有更新
func IsLastUpdate(programs []*models.Program, NewVersionProgram []*models.Program) bool {
   for _, program := range programs {
      for _, program2 := range NewVersionProgram {
         if program.Name == program2.Name {
            if program.Version != program2.Version {
               return true
            }
         }
      }
   }
   return true
}
//获取更新提醒
func GetUpdateNotice() UpdateNotice {
   return updateNotice
}
//延迟提醒
func DelayNotice(uid string, second int) UpdateNotice {
   updateNoticeLock.Lock()
   updateNotice.NoticeUser[uid] = second
   updateNoticeLock.Unlock()
   return updateNotice
}
//获取代码备份位置
func GetBackupPath() string {
   dir := util.GetVamicroPath() + "/backup"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./backup"
      }
   }
   return dir
}
//获取解压后的补丁包位置
func GetPatchPath() string {
   dir := util.GetVamicroPath() + "/patch"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./patch"
      }
   }
   return dir
}
//获取预下载升级包目录
func GetPreDownPath() string {
   dir := util.GetVamicroPath() + "/upgrade"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./upgrade"
      }
   }
   return dir
}
//获取上传升级包目录
func GetPreUploadPath() string {
   dir := util.GetVamicroPath() + "/upgrade_manual"
   _, err := os.Stat(dir)
   if nil != err {
      if os.IsNotExist(err) {
         os.Mkdir(dir, 0744)
      } else {
         return "./upgrade_manual"
      }
   }
   return dir
}
func GetMd5(in string) string {
   data := md5.Sum([]byte(in))
   return fmt.Sprintf("%x", data)
}
//预下载升级文件
func PreDownUpdateFile() error {
   info, err := getUpdateInfo()
   if nil != err {
      return err
   }
   LastDownLock.Lock()
   defer LastDownLock.Unlock()
   LastDownFile = make([]string, 0)
   //循环下载升级包
   for _, payload := range info.Data {
      logger.Info("正在下载版本补丁【" + payload.Version + "】")
      upfile, err := DownloadPatchPkg(payload)
      logger.Info("下载版本补丁完成【" + payload.Version + "】")
      if err != nil {
         return err
      }
      LastDownFile = append(LastDownFile, upfile)
   }
   return nil
}
//获取预下载升级包
func GetLastDownFile() []string {
   var res = make([]string, 0)
   if len(LastDownFile) > 0 {
      for _, file := range LastDownFile {
         _, err := os.Stat(file)
         if nil == err {
            res = append(res, file)
         }
      }
   }
   return res
}
//回滚版本
func Rollback(version string) error {
   dir := util.GetVamicroPath() + "/" + version
   _, err := os.Stat(dir)
   if nil != err {
      return err
   }
   SetRunVersionEnv(version)
   versionEnv = version
   return nil
}
type RegUserInfo struct {
   UserType   string `json:"userType"`   //个人:personal  公司: company
   PhoneNum   string `json:"phoneNum"`   //手机号码
   Name       string `json:"name"`       //姓名或公司名称
   ProvinceId string `json:"provinceId"` //省
   CityId     string `json:"cityId"`     //市
   CountyId   string `json:"countyId"`   //县
   Email      string `json:"email"`      //邮箱
}
//获取授权
func Authorization(code string, isManual bool) (authinfo util.AuthorizationInfo, err error) {
   sn := util.GetVamicroPath() + "/sn.txt"
   authorization := util.GetVamicroPath() + "/auth.txt"
   activateCode := ""
   if len(code) == 29 && code[5:6] == "-" { //25位激活码激活
      activateCode = code
   }
   if "" != code && len(code) != 29 && code[5:6] != "-" {
      authinfo, err := util.GetAuthorizationInfo(code)
      if nil == err {
         activateCode = authinfo.ActivateCode
         logger.Debug("code found:" + code)
         if isManual { //手动操作更新授权文件,则立即返回,不用再次访问商城
            ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
            ioutil.WriteFile(authorization, []byte(code), os.ModePerm)
            return authinfo, nil
         } else {
            //否则以商城为准
            defer func() {
               if nil != err {
                  //ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
                  //ioutil.WriteFile(authorization, []byte(code), os.ModePerm)
               }
            }()
         }
      } else {
         return authinfo, errors.New("非法的授权!!")
      }
   }
   devId := config.Server.AnalyServerId
   machineCode := licence.GetMachineCode()
   authinfo, authcode, err := postAuthReq(util.GetSn(), activateCode, devId, machineCode, "")
   if nil != err {
      logger.Error("Authorization err:", err.Error(), authcode)
      //ioutil.WriteFile(authorization, []byte(authcode), os.ModePerm)  //bug:局域网会把授权清除
      return authinfo, err
   }
   if authinfo.MachineCode != machineCode {
      logger.Error("GetAuthorization machineCode not match, local:", machineCode, " remote:", authinfo.MachineCode)
      return authinfo, errors.New("授权不匹配")
   }
   ioutil.WriteFile(sn, []byte(authinfo.ActivateCode), os.ModePerm)
   ioutil.WriteFile(authorization, []byte(authcode), os.ModePerm)
   return authinfo, nil
}
func postAuthReq(sn string, activateCode string, deviceId string, machineCode string, oldDeviceId string) (authinfo util.AuthorizationInfo, authcode string, err error) {
   u, err := url.Parse("http://" + util.GetShopUrl() + uriAuth)
   if err != nil {
      logger.Error("parse url failed, url:", "http://"+util.GetShopUrl()+uriAuth, ", err:", err.Error())
      return authinfo, "", err
   }
   query := u.Query()
   query.Set("sn", sn)
   query.Set("activateCode", activateCode)
   query.Set("deviceId", deviceId)
   query.Set("machineCode", machineCode)
   query.Set("oldDeviceId", oldDeviceId)
   query.Set("deviceType", config.Server.DeviceType)
   query.Set("deviceMode", config.Server.DeviceModel)
   query.Set("vGpu", util.GetVGpu())
   var sysInitApi bhomedbapi.SysInitApi
   b, rInfo := sysInitApi.GetRegInfo()
   if b {
      rbd, e := json.Marshal(rInfo)
      if e == nil {
         var sysRI RegUserInfo
         if e = json.Unmarshal(rbd, &sysRI); e == nil {
            query.Set("userType", sysRI.UserType)
            query.Set("name", sysRI.Name)
            query.Set("phoneNum", sysRI.PhoneNum)
            query.Set("provinceId", sysRI.ProvinceId)
            query.Set("cityId", sysRI.CityId)
            query.Set("countyId", sysRI.CountyId)
            query.Set("email", sysRI.Email)
         } else {
            logger.Error("json.Unmarshal sysRI e:", e)
         }
      } else {
         logger.Error("json.Marshal rInfo e:", e)
      }
   }
   u.RawQuery = query.Encode()
   resp, err := http.Get(u.String())
   if err != nil {
      logger.Error("GetAuthorization parse url failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   if resp.StatusCode != 200 {
      logger.Error("GetAuthorization incorrect status, url:", u.String(), ", status:", resp.StatusCode)
      return authinfo, "", errors.New("Status code not 200")
   }
   defer resp.Body.Close()
   body, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      logger.Error("GetAuthorization read body failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   logger.Info("postAuthReq body:", string(body))
   var info map[string]interface{}
   err = json.Unmarshal(body, &info)
   if err != nil {
      logger.Error("GetAuthorization json.Unmarshal failed, url:", u.String(), ", err:", err.Error())
      return authinfo, "", err
   }
   authinfo, err = util.GetAuthorizationInfo(info["data"].(string))
   if nil == err {
      logger.Error("err:", err)
      return authinfo, info["data"].(string), nil
   }
   if 500 == int(info["code"].(float64)) {
      logger.Error("ret 500")
      return authinfo, "", errors.New(info["msg"].(string))
   }
   if true != info["success"].(bool) {
      logger.Error("not success")
      return authinfo, "", errors.New(info["msg"].(string))
   }
   return authinfo, info["data"].(string), nil
}
func GenQRCode() ([]byte, string, error) {
   sn := util.GetSn()
   var authInfo util.AuthorizationInfo
   authInfo.Sn = sn
   authInfo.DevId = config.Server.AnalyServerId
   authInfo.MachineCode = licence.GetMachineCode()
   authInfo.DeviceType = config.Server.DeviceType
   authInfo.DeviceMode = config.Server.DeviceModel
   authInfo.VGpu = util.GetVGpu()
   var sysInitApi bhomedbapi.SysInitApi
   if bsi, rInfo := sysInitApi.GetRegInfo(); bsi {
      rbd, e := json.Marshal(rInfo)
      if e == nil {
         var sysRI RegUserInfo
         if e = json.Unmarshal(rbd, &sysRI); e == nil {
            authInfo.UserType = sysRI.UserType
            authInfo.Name = sysRI.Name
            authInfo.PhoneNum = sysRI.PhoneNum
            authInfo.ProvinceId = sysRI.ProvinceId
            authInfo.CityId = sysRI.CityId
            authInfo.CountyId = sysRI.CountyId
            authInfo.Email = sysRI.Email
         } else {
            logger.Error("json.Unmarshal sysRI e:", e)
         }
      } else {
         logger.Error("json.Marshal rInfo e:", e)
      }
   }
   hackQ, _ := HackAuthorizationInfo(authInfo)
   url := "http://" + util.GetShopUrl() + uriMobile + "?q=" + hackQ
   logger.Info("qrcode len:", len(url), " content:", url)
   // 生成二维码
   q, err := qrcode.New(url, qrcode.Highest)
   if err != nil {
      return nil, url, err
   }
   png, err1 := q.PNG(350)
   return png, url, err1
}
//刷新授权到其他进程
func AuthorizationUpdate(c context.Context, ms *bhomeclient.MicroNode) {
   //三十秒检查一次配置并更新
   // tick := time.Tick(30 * time.Second)
   tick := time.Tick(24 * time.Hour)
   var authInfo util.AuthorizationInfo
   var err error
   for {
      select {
      case <-c.Done():
         logger.Info("proc close, self update exit")
         return
      case <-tick:
         author := util.GetAuthorization()
         sn := util.GetSn()
         authInfo, err = Authorization(sn, false)
         if nil != err && !strings.Contains(err.Error(), "成功") {
            logger.Error("GetAuthorization error:", err.Error())
            if "" != author {
               authInfo, err = util.GetAuthorizationInfo(author)
            }
         }
         logger.Debug("authInfo:", authInfo)
         data, _ := json.Marshal(authInfo)
         var nodes []bhome_msg.BHAddress
         nodes = append(nodes, bhome_msg.BHAddress{})
         go ms.PublishNetTimeout(nodes, AuthorizationUpdateTopic, data, 10)
      }
   }
}
func HackAuthorizationInfo(authorizationInfo util.AuthorizationInfo) (string, error) {
   b, err := json.Marshal(authorizationInfo)
   if nil != err {
      return "", err
   }
   logger.Debug("authorInfo", authorizationInfo)
   info, err := util.RsaEncrypt(b)
   if nil != err {
      logger.Error("HackAuthorizationInfo utils.RsaEncrypt failed, err:", err.Error())
      return "", err
   }
   return base64.StdEncoding.EncodeToString(info), nil
}
func GetQ() string {
   sn := util.GetSn()
   var authinfo util.AuthorizationInfo
   authinfo.Sn = sn
   authinfo.DevId = config.Server.AnalyServerId
   authinfo.MachineCode = licence.GetMachineCode()
   authinfo.VGpu = util.GetVGpu()
   q, err := HackAuthorizationInfo(authinfo)
   if nil == err {
      return q
   }
   return err.Error()
}
func CancelAuthorization(passwd string, q string) (error, string) {
   uApi := bhomedbapi.UserApi{}
   ok, _ := uApi.Login("basic", passwd)
   if !ok {
      return errors.New("密码不正确!"), ""
   }
   //获取请求码里的devId
   qInfo, err := util.GetAuthorizationInfo(q)
   if nil != err {
      return errors.New("请求码格式不正确"), ""
   }
   curAuthInfo := util.GetAuthorization()
   //获取当前授权码
   authInfo, err := util.GetAuthorizationInfo(curAuthInfo)
   if nil != err {
      return errors.New("当前服务器授权信息不完整"), ""
   }
   authInfo.OldDeviceId = authInfo.DevId
   authInfo.DevId = qInfo.DevId
   authInfo.VGpu = util.GetVGpu()
   sn := util.GetVamicroPath() + "/sn.txt"
   authorization := util.GetVamicroPath() + "/auth.txt"
   authCode, err := HackAuthorizationInfo(authInfo)
   if nil != err {
      return errors.New("产品密钥导出失败"), ""
   }
   ioutil.WriteFile(sn, []byte(""), os.ModePerm)
   time.Sleep(100 * time.Microsecond)
   ioutil.WriteFile(authorization, []byte(""), os.ModePerm)
   _, _, err = postAuthReq(authInfo.Sn, authInfo.ActivateCode, qInfo.DevId, licence.GetMachineCode(), authInfo.OldDeviceId)
   if nil != err {
      return err, ""
   }
   return nil, authCode
}