lijian преди 6 години
родител
ревизия
fa9d994dd5
променени са 39 файла, в които са добавени 1393 реда и са изтрити 234 реда
  1. 2 1
      .gitignore
  2. 13 0
      pkg/models/application.go
  3. 7 6
      pkg/models/product.go
  4. 27 0
      pkg/models/protocal.go
  5. 1 0
      pkg/mysql/migrate.go
  6. 12 0
      pkg/rpcs/fileaccess.go
  7. 9 1
      pkg/server/server.go
  8. 38 0
      pkg/utils/util.go
  9. 0 0
      publish.sh
  10. 3 3
      run.sh
  11. 12 12
      services/apiprovider/jwt.go
  12. 0 57
      services/apiprovider/product.go
  13. 24 28
      services/apiprovider/router.go
  14. 0 116
      services/apiprovider/user.go
  15. 68 0
      services/fileaccess/file.go
  16. 23 0
      services/fileaccess/fileaccess.go
  17. 21 0
      services/fileaccess/flags.go
  18. 47 0
      services/fileaccess/main.go
  19. 39 0
      services/fileaccess/readme.markdown
  20. 100 0
      services/knowoapi/controllers/application.go
  21. 2 0
      services/knowoapi/controllers/errorresponse.go
  22. 2 0
      services/knowoapi/controllers/parsebody.go
  23. 31 1
      services/knowoapi/controllers/produdct.go
  24. 103 0
      services/knowoapi/controllers/protocal.go
  25. 1 0
      services/knowoapi/controllers/token.go
  26. 5 0
      services/knowoapi/controllers/user.go
  27. BIN
      services/knowoapi/knowoapi
  28. 7 3
      services/knowoapi/model/all.go
  29. 87 0
      services/knowoapi/model/application.go
  30. 30 0
      services/knowoapi/model/product.go
  31. 82 0
      services/knowoapi/model/protocal.go
  32. 14 6
      services/knowoapi/router.go
  33. 58 0
      services/knowoapi/services/application.go
  34. 7 0
      services/knowoapi/services/product.go
  35. 44 0
      services/knowoapi/services/protocal.go
  36. 20 0
      vendor/github.com/iris-contrib/middleware/cors/LICENSE
  37. 378 0
      vendor/github.com/iris-contrib/middleware/cors/cors.go
  38. 70 0
      vendor/github.com/iris-contrib/middleware/cors/util.go
  39. 6 0
      vendor/vendor.json

+ 2 - 1
.gitignore

@@ -27,4 +27,5 @@ _testmain.go
 *.exe
 *.test
 *.prof
-
+upload
+upload/*

+ 13 - 0
pkg/models/application.go

@@ -2,9 +2,12 @@
 package models
 
 import (
+	"errors"
+
 	"github.com/jinzhu/gorm"
 )
 
+// Application 代指一个应用程序
 type Application struct {
 	gorm.Model
 	// App-Key for api
@@ -19,4 +22,14 @@ type Application struct {
 	AppDescription string `sql:"type:text;"`
 	// app domain which allows wildcard string like "*", "vendor/12", "product/10"
 	AppDomain string `sql:"type:varchar(200);not null;"`
+	// vendor id
+	VendorID uint
+}
+
+// Validate 验证规则
+func (a *Application) Validate() error {
+	if a.AppName == "" {
+		return errors.New("非法参数:[AppName]")
+	}
+	return nil
 }

+ 7 - 6
pkg/models/product.go

@@ -1,4 +1,3 @@
-// product is a abstract define of same devices made by some vendor
 package models
 
 import (
@@ -8,26 +7,28 @@ import (
 )
 
 // Product product
+// product is a abstract define of same devices made by some vendor
 type Product struct {
 	gorm.Model
 	// which vendor
 	VendorID int32
 	// name
-	ProductName string `sql:"type:varchar(200);not null;" binding:"required"`
+	ProductName string `sql:"type:varchar(200);not null;"`
 	// desc
 	ProductDescription string `sql:"type:text;not null;"`
 	// product key to auth a product
 	ProductKey string `sql:"type:varchar(200);not null;unique;key;"`
 	// product config string (JSON)
 	ProductConfig string `sql:"type:text; not null;"`
-
-	Devices []Device
+	// icon of product
+	ProductImage string
+	Devices      []Device
 }
 
 // Validate 验证
 func (a *Product) Validate() error {
-	if a.ProductName == "" || a.VendorID == 0 {
-		return errors.New("非法参数:[ProductName, VendorID]")
+	if a.ProductName == "" {
+		return errors.New("非法参数:[ProductName]")
 	}
 	return nil
 }

+ 27 - 0
pkg/models/protocal.go

@@ -0,0 +1,27 @@
+package models
+
+import (
+	"errors"
+
+	"github.com/jinzhu/gorm"
+)
+
+// Protocal 产品协议(数据点)
+type Protocal struct {
+	gorm.Model
+	Name          string `gorm:"size:30;not null;"` //名称
+	Label         string `gorm:"size:20;not null;"` // 标签
+	Type          int
+	ReadWriteType int
+	UnitSymbol    string
+	Description   string
+	ProductID     uint //所属产品
+}
+
+// Validate 验证
+func (a *Protocal) Validate() error {
+	if a.Name == "" || a.Label == "" {
+		return errors.New("非法参数[Name, Label]")
+	}
+	return nil
+}

+ 1 - 0
pkg/mysql/migrate.go

@@ -41,6 +41,7 @@ func MigrateDatabase(dbhost, dbport, dbname, dbuser, dbpass string) error {
 		&models.User{},
 		&models.Privilege{},
 		&models.Role{},
+		&models.Protocal{},
 	).Error
 	if err != nil {
 		fmt.Printf("%s", err.Error())

+ 12 - 0
pkg/rpcs/fileaccess.go

@@ -0,0 +1,12 @@
+package rpcs
+
+// ArgsMoveFile move file rpc args
+type ArgsMoveFile struct {
+	Source string
+	Target string
+}
+
+// ReplyMoveFile move file rpc reply
+type ReplyMoveFile struct {
+	FilePath string
+}

+ 9 - 1
pkg/server/server.go

@@ -188,7 +188,7 @@ func GetServerHosts(serverName string, hostType string) ([]string, error) {
 	return serverInstance.svrmgr.GetServerHosts(serverName, hostType)
 }
 
-// get this server's rpc host
+// GetRPCHost get this server's rpc host
 func GetRPCHost() string {
 	if serverInstance == nil || serverInstance.rpcsvr == nil {
 		return ""
@@ -197,6 +197,14 @@ func GetRPCHost() string {
 	return serverInstance.rpcsvr.addr
 }
 
+// GetHTTPHost get this server's http host addr
+func GetHTTPHost() string {
+	if serverInstance == nil || serverInstance.httpsvr == nil {
+		return ""
+	}
+	return serverInstance.httpsvr.addr
+}
+
 // start service
 func Run() error {
 	if serverInstance == nil {

+ 38 - 0
pkg/utils/util.go

@@ -4,11 +4,49 @@ import (
 	"crypto/md5"
 	"encoding/hex"
 	"fmt"
+	"os"
+	"path/filepath"
+
+	uuid "github.com/satori/go.uuid"
 )
 
+// Md5 md5加密算法
 func Md5(s string) string {
 	h := md5.New()
 	h.Write([]byte(s))
 	cipherStr := h.Sum(nil)
 	return fmt.Sprintf("%s", hex.EncodeToString(cipherStr))
 }
+
+// CreateIfNotExist 创建目录
+func CreateIfNotExist(filename string) error {
+	dir := filepath.Dir(filename)
+	if dir != "" {
+		exists, _ := Exists(dir)
+		if !exists {
+			err := os.MkdirAll(dir, os.ModePerm)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+// Exists ...
+func Exists(filename string) (bool, error) {
+	exists := true
+	_, err := os.Stat(filename)
+	if err != nil {
+		if os.IsNotExist(err) {
+			exists = false
+		}
+	}
+	return exists, err
+}
+
+// UUID uuid
+func UUID() string {
+	s, _ := uuid.NewV4()
+	return s.String()
+}

+ 0 - 0
publish.sh


+ 3 - 3
run.sh

@@ -1,6 +1,6 @@
 export GOPATH=/Users/terrence/go
 
-sudo killall -9 httpaccess registry apiprovider devicemanager controller mqttaccess knowoapi
+sudo killall -9 httpaccess registry apiprovider devicemanager controller mqttaccess knowoapi fileaccess
 
 # start services
 #$GOPATH/bin/httpaccess -etcd http://localhost:2379 -httphost internal:443 -loglevel debug -usehttps -keyfile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/key.pem -cafile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/cert.pem &
@@ -11,8 +11,8 @@ $GOPATH/bin/devicemanager -etcd http://192.168.175.60:2379 -loglevel debug  -rpc
 $GOPATH/bin/controller -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20032 &
 #$GOPATH/bin/mqttaccess -etcd http://localhost:2379 -loglevel debug  -rpchost localhost:20030 -tcphost internal:1883 -usetls -keyfile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/key.pem -cafile $GOPATH/src/github.com/PandoCloud/pando-cloud/pkg/server/testdata/cert.pem &
 $GOPATH/bin/mqttaccess -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20030 -tcphost 0.0.0.0:1883  &
-$GOPATH/bin/knowoapi -etcd http://192.168.175.60:2379 -loglevel debug  -httphost localhost:8888 -dbhost 192.168.175.60 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP &
-
+$GOPATH/bin/knowoapi -etcd http://192.168.175.60:2379 -loglevel debug  -httphost localhost:8889 -dbhost 192.168.175.60 -dbname SparrowCloud -dbport 3306 -dbuser SparrowCloud -dbpass 123456 -aeskey ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP &
+$GOPATH/bin/fileaccess -etcd http://192.168.175.60:2379 -loglevel debug  -rpchost localhost:20035 -httphost localhost:9000
 exit 0
 
 

+ 12 - 12
services/apiprovider/jwt.go

@@ -197,19 +197,19 @@ func (m *Middleware) CheckJWT(req *http.Request, res http.ResponseWriter, ctx ma
 
 	// If we get here, everything worked and we can set the
 	// user property in context.
-	ctx.Map(parseToken(parsedToken))
+	//ctx.Map(parseToken(parsedToken))
 	return nil
 }
 
 // 把token解析成一个usertoken对象
-func parseToken(token *jwt.Token) *UserToken {
-	claims := token.Claims.(jwt.MapClaims)
-	ut := &UserToken{
-		UserID:   uint(claims["UserID"].(float64)),
-		UserName: claims["UserName"].(string),
-		UserType: claims["UserType"].(string),
-		RoleCode: int(claims["RoleCode"].(float64)),
-		VendorID: uint(claims["VendorID"].(float64)),
-	}
-	return ut
-}
+// func parseToken(token *jwt.Token) *UserToken {
+// 	claims := token.Claims.(jwt.MapClaims)
+// 	ut := &UserToken{
+// 		UserID:   uint(claims["UserID"].(float64)),
+// 		UserName: claims["UserName"].(string),
+// 		UserType: claims["UserType"].(string),
+// 		RoleCode: int(claims["RoleCode"].(float64)),
+// 		VendorID: uint(claims["VendorID"].(float64)),
+// 	}
+// 	return ut
+// }

+ 0 - 57
services/apiprovider/product.go

@@ -1,57 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"sparrow/pkg/models"
-	"sparrow/pkg/rpcs"
-	"sparrow/pkg/server"
-	"strconv"
-
-	"github.com/martini-contrib/render"
-)
-
-// SaveProduct 添加或修改产品
-func SaveProduct(user *UserToken, product models.Product, r render.Render) {
-	product.VendorID = int32(user.VendorID)
-	reply := models.Product{}
-	err := server.RPCCallByName("registry", "Registry.SaveProduct", &product, &reply)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-	r.JSON(http.StatusOK, done(reply))
-	return
-}
-
-// DeleteProduct 删除产品
-func DeleteProduct(user *UserToken, product models.Product, r render.Render) {
-	if int32(user.VendorID) != product.VendorID {
-		r.JSON(http.StatusOK, renderError(ErrIllegalityAction, errIllegalityString))
-		return
-	}
-	r.JSON(http.StatusOK, done("删除成功"))
-	return
-}
-
-// GetProducts 分页
-func GetProducts(user *UserToken, req *http.Request, r render.Render) {
-	pi, _ := strconv.Atoi(req.URL.Query().Get("pi"))
-	ps, _ := strconv.Atoi(req.URL.Query().Get("ps"))
-	name := req.URL.Query().Get("name")
-
-	args := rpcs.ArgsProductList{
-		ProductName: name,
-	}
-	args.Pi = pi
-	args.Ps = ps
-	args.VendorID = user.VendorID
-	var reply map[string]interface{}
-	err := server.RPCCallByName("registry", "Registry.GetProducts", &args, &reply)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-	r.JSON(http.StatusOK, done(reply))
-	return
-
-}

+ 24 - 28
services/apiprovider/router.go

@@ -1,11 +1,7 @@
 package main
 
 import (
-	"sparrow/pkg/models"
-
-	jwt "github.com/dgrijalva/jwt-go"
 	"github.com/go-martini/martini"
-	"github.com/martini-contrib/binding"
 	"github.com/xyproto/permissionsql"
 )
 
@@ -13,12 +9,12 @@ import (
 func route(m *martini.ClassicMartini, perm *permissionsql.Permissions) {
 
 	// jwt handler
-	handler := New(Config{
-		ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
-			return []byte(SignedString), nil
-		},
-		SigningMethod: jwt.SigningMethodHS256,
-	})
+	// handler := New(Config{
+	// 	ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
+	// 		return []byte(SignedString), nil
+	// 	},
+	// 	SigningMethod: jwt.SigningMethodHS256,
+	// })
 
 	// find a device by key
 	m.Get("/application/v1/device/info", GetDeviceInfoByKey)
@@ -50,24 +46,24 @@ func route(m *martini.ClassicMartini, perm *permissionsql.Permissions) {
 	m.Post("/application/v1/devices/:identifier/rules",
 		ApplicationAuthOnDeviceIdentifer, CheckDeviceIdentifier,
 		AddRule)
-	// user login
-	m.Post("/api/v1/login", binding.Bind(models.LoginRequest{}),
-		UserLogin)
-	// user register
-	m.Post("/api/v1/reg", binding.Bind(models.Reqrequest{}),
-		UserRegister)
+	// // user login
+	// m.Post("/api/v1/login", binding.Bind(models.LoginRequest{}),
+	// 	UserLogin)
+	// // user register
+	// m.Post("/api/v1/reg", binding.Bind(models.Reqrequest{}),
+	// 	UserRegister)
 
-	// user api group
-	// jwt check and pass UserToken
-	m.Group("/api/v1/user", func(r martini.Router) {
-		// user modify password api
-		r.Post("/modifypass", ModifyPassword)
-		// user add a product
-		r.Post("/product", binding.Bind(models.Product{}), SaveProduct)
-		// delete a product
-		r.Delete("/product", binding.Bind(models.Product{}), DeleteProduct)
-		// get products
-		r.Get("/product", GetProducts)
-	}, handler.Serve)
+	// // user api group
+	// // jwt check and pass UserToken
+	// m.Group("/api/v1/user", func(r martini.Router) {
+	// 	// user modify password api
+	// 	r.Post("/modifypass", ModifyPassword)
+	// 	// user add a product
+	// 	r.Post("/product", binding.Bind(models.Product{}), SaveProduct)
+	// 	// delete a product
+	// 	r.Delete("/product", binding.Bind(models.Product{}), DeleteProduct)
+	// 	// get products
+	// 	r.Get("/product", GetProducts)
+	// }, handler.Serve)
 
 }

+ 0 - 116
services/apiprovider/user.go

@@ -1,116 +0,0 @@
-package main
-
-import (
-	"net/http"
-	"sparrow/pkg/models"
-	"sparrow/pkg/rpcs"
-	"sparrow/pkg/server"
-	"time"
-
-	jwt "github.com/dgrijalva/jwt-go"
-	"github.com/martini-contrib/render"
-)
-
-// SignedString 签名
-const SignedString = "www.yehaoji.com"
-
-// UserToken 用户token结构体
-type UserToken struct {
-	UserID   uint
-	UserName string
-	UserType string
-	RoleCode int
-	VendorID uint
-	jwt.StandardClaims
-}
-
-// UserLogin 用户登陆
-func UserLogin(loginRequest models.LoginRequest, r render.Render) {
-
-	reply := models.User{}
-	err := server.RPCCallByName("registry", "Registry.Login", &loginRequest, &reply)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-
-	usertype := "member"
-	if reply.UserType == 1 {
-		usertype = "admin"
-	}
-	claims := UserToken{
-		UserID:   reply.ID,
-		UserName: reply.UserName,
-		UserType: usertype,
-		RoleCode: reply.UserRoleID,
-		VendorID: reply.VendorID,
-	}
-	claims.ExpiresAt = time.Now().Add(time.Hour * 24).Unix()
-	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
-	ser, _ := token.SignedString([]byte(SignedString))
-	result := map[string]interface{}{
-		"username":     reply.UserName,
-		"userkey":      reply.UserKey,
-		"company":      reply.Vendor.VendorName,
-		"access_token": ser,
-	}
-	r.JSON(http.StatusOK, result)
-	return
-}
-
-// UserRegister 用户注册
-func UserRegister(req models.Reqrequest, r render.Render) {
-	vargs := models.Vendor{
-		VendorName: req.VendorName,
-	}
-	vreply := &models.Vendor{}
-
-	err := server.RPCCallByName("registry", "Registry.SaveVendor", &vargs, vreply)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-	uargs := &models.User{
-		UserName: req.UserName,
-		UserPass: req.PassWord,
-		VendorID: vreply.ID,
-		Phone:    req.Phone,
-		Email:    req.Email,
-	}
-	ureply := &models.User{}
-	err = server.RPCCallByName("registry", "Registry.Register", &uargs, ureply)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-	res := map[string]interface{}{
-		"username": uargs.UserName,
-		"phone":    uargs.Phone,
-		"email":    uargs.Email,
-	}
-	r.JSON(http.StatusOK, res)
-	return
-}
-
-// ModifyPassword 用户修改密码
-func ModifyPassword(req *http.Request, r render.Render, user *UserToken) {
-	newpass := req.URL.Query().Get("newpass")
-	oldpass := req.URL.Query().Get("oldpass")
-	if len(newpass) == 0 || len(oldpass) == 0 {
-		r.JSON(http.StatusOK, renderError(ErrWrongRequestFormat, errBadRequestString))
-		return
-	}
-	args := rpcs.ArgsUserModifyPass{
-		UserID:  user.UserID,
-		NewPass: newpass,
-		OldPass: oldpass,
-	}
-	err := server.RPCCallByName("registry", "Registry.ModifyPass", &args, nil)
-	if err != nil {
-		r.JSON(http.StatusOK, renderError(ErrSystemFault, err))
-		return
-	}
-
-	r.JSON(http.StatusOK, done("修改成功"))
-	return
-}

+ 68 - 0
services/fileaccess/file.go

@@ -0,0 +1,68 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"path"
+	"regexp"
+	"sparrow/pkg/server"
+	"sparrow/pkg/utils"
+	"strconv"
+
+	"github.com/kataras/iris"
+)
+
+func registerRouter(app *iris.Application) {
+	app.Post("/upload_file", iris.LimitRequestBodySize(int64(*conMaxSize)),
+		func(ctx iris.Context) {
+			file, info, err := ctx.FormFile("file")
+			if err != nil {
+				server.Log.Error(err)
+				ctx.JSON(map[string]interface{}{
+					"code":    -1,
+					"message": "文件大小超过限制(" + strconv.Itoa(*conMaxSize) + "字节)",
+				})
+				return
+			}
+			defer file.Close()
+			fname := info.Filename
+			fileSuffix := path.Ext(fname)
+			re := regexp.MustCompile(*conAllowExt)
+			if !re.MatchString(fileSuffix) {
+				server.Log.Errorf("%s", "非法的文件格式"+fileSuffix)
+				ctx.JSON(map[string]interface{}{
+					"code":    -2,
+					"message": "非法的文件格式",
+				})
+				return
+			}
+			newname := fmt.Sprintf("%s%s", utils.UUID(), fileSuffix)
+			newfile := "./upload/tmp/" + newname
+			utils.CreateIfNotExist(newfile)
+			out, err := os.OpenFile(newfile,
+				os.O_WRONLY|os.O_CREATE, 0666)
+
+			if err != nil {
+				ctx.JSON(map[string]interface{}{
+					"code":    -3,
+					"message": "文件上传失败:" + err.Error(),
+				})
+				return
+			}
+			defer out.Close()
+			io.Copy(out, file)
+			var fileURL string
+			if *conDomain == "" {
+				fileURL = "http://" + server.GetHTTPHost() + "/upload/tmp/" + newname
+			} else {
+				fileURL = *conDomain
+			}
+			ctx.JSON(map[string]interface{}{
+				"code":    0,
+				"message": "success",
+				"file":    fileURL,
+			})
+			return
+		})
+}

+ 23 - 0
services/fileaccess/fileaccess.go

@@ -0,0 +1,23 @@
+package main
+
+import (
+	"sparrow/pkg/rpcs"
+	"sparrow/pkg/server"
+	"sync"
+)
+
+// FileAccessor RPC服务
+type FileAccess struct {
+	mu sync.RWMutex
+}
+
+// NewFileAccessor create a FileAccessor instance
+func NewFileAccess() *FileAccess {
+	return &FileAccess{}
+}
+
+// MoveFile move a file to new path
+func (f *FileAccess) MoveFile(args *rpcs.ArgsMoveFile, reply *rpcs.ReplyMoveFile) error {
+	server.Log.Info("调用rpc")
+	return nil
+}

+ 21 - 0
services/fileaccess/flags.go

@@ -0,0 +1,21 @@
+package main
+
+import "flag"
+
+const (
+	flagStaticPath = "static"   //静态文件参数
+	flagMaxSize    = "maxsize"  //最大允许上传的文件大小
+	flagAllowExt   = "allowext" //允许上传的文件格式
+	flagDomain     = "domain"   // 文件服务域名
+
+	defaultStaticPath = "./upload"
+	defaultMaxSize    = 300 << 10         //默认300K
+	defaultAllowExt   = ".jpeg|.jpg|.png" //注意.号
+)
+
+var (
+	conStaticPath = flag.String(flagStaticPath, defaultStaticPath, "static file path")
+	conMaxSize    = flag.Int(flagMaxSize, defaultMaxSize, "允许上传的最大文件尺寸")
+	conAllowExt   = flag.String(flagAllowExt, defaultAllowExt, "允许上传的文件格式")
+	conDomain     = flag.String(flagDomain, "", "文件服务器域名")
+)

+ 47 - 0
services/fileaccess/main.go

@@ -0,0 +1,47 @@
+package main
+
+import (
+	"sparrow/pkg/server"
+
+	"github.com/iris-contrib/middleware/cors"
+	"github.com/kataras/iris"
+)
+
+func main() {
+	err := server.Init("fileaccess")
+	if err != nil {
+		server.Log.Fatal(err)
+		return
+	}
+	app := iris.New()
+	app.Use(iris.Gzip)
+	iris.WithPostMaxMemory(int64(*conMaxSize))
+	opts := cors.Options{
+		AllowedOrigins: []string{"*"},
+		AllowedHeaders: []string{"Content-Type"},
+		AllowedMethods: []string{"POST"},
+		ExposedHeaders: []string{"X-Header"},
+	}
+	app.Use(cors.New(opts))
+	app.AllowMethods(iris.MethodOptions)
+	app.StaticWeb("/upload", *conStaticPath)
+	registerRouter(app)
+	app.Build()
+	// register a http handler
+	err = server.RegisterHTTPHandler(app)
+	if err != nil {
+		server.Log.Errorf("RegisterHTTPHandler Error: %s", err)
+		return
+	}
+	fileaccess := NewFileAccess()
+	err = server.RegisterRPCHandler(fileaccess)
+	if err != nil {
+		server.Log.Errorf("RegisterRPCHandler Error: %s", err)
+		return
+	}
+	// go
+	err = server.Run()
+	if err != nil {
+		server.Log.Fatal(err)
+	}
+}

+ 39 - 0
services/fileaccess/readme.markdown

@@ -0,0 +1,39 @@
+## 实现一个简单文件服务器
+
+* 上传的文件全部放到upload/tmp临时目录中
+* 执行保存操作后,把文件拷贝到正式的文件目录,并生成文件路径
+* 定时清理tmp目录中的文件(如24小时清理一次)
+
+### 接口说明:
+
+#### 1.上传文件
+
+* url: `/upload_file`
+* Method: `POST`
+* Response:
+
+```json
+{
+    "file":"http://file.xxx.com/upload/tmp/xxx.jpeg"
+}
+```
+
+#### 2.移动文件(把文件转移到正式目录)
+RPC调用
+* 方法名:MoveFile
+* 参数结构体:
+
+```
+type MoveFileArgs struct {
+    Source string
+    Target string
+}
+```
+
+* 返回结构体
+
+```
+type MoveFileReply struct {
+    FileName string
+}
+```

+ 100 - 0
services/knowoapi/controllers/application.go

@@ -0,0 +1,100 @@
+package controllers
+
+import (
+	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/services"
+
+	"github.com/kataras/iris"
+)
+
+// AppController api
+type AppController struct {
+	Ctx     iris.Context
+	Service services.ApplicationService
+	Token   Token
+}
+
+// Post 创建app
+// POST /application
+func (a *AppController) Post() {
+	app := new(models.Application)
+	if err := parseBody(a.Ctx, app); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	app.VendorID = a.Token.getVendorID(a.Ctx)
+	err := a.Service.Create(app)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, app)
+}
+
+// Get 获取我的app
+// GET /application?pi=1&ps=&name=
+func (a *AppController) Get() {
+	pi, err := a.Ctx.URLParamInt("pi")
+	if err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	ps, err := a.Ctx.URLParamInt("ps")
+	if err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	name := a.Ctx.URLParam("name")
+	ds, total, err := a.Service.GetVendorApps(a.Token.getVendorID(a.Ctx), pi, ps, name)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, map[string]interface{}{
+		"list":  ds,
+		"total": total,
+	})
+}
+
+// Delete 删除
+// DELETE /application
+func (a *AppController) Delete() {
+	app := new(models.Application)
+	if err := parseBody(a.Ctx, app); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	err := a.Service.Delete(app)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, "删除成功")
+}
+
+// GetBy 获取app信息
+// GET /application/{key}
+func (a *AppController) GetBy(key string) {
+	app, err := a.Service.GetAppInfo(a.Token.getVendorID(a.Ctx), key)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, app)
+}
+
+// Put 更新
+// PUT /application
+func (a *AppController) Put() {
+	app := new(models.Application)
+	if err := parseBody(a.Ctx, app); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	result, err := a.Service.Update(app)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, result)
+}

+ 2 - 0
services/knowoapi/controllers/errorresponse.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"fmt"
+	"sparrow/pkg/server"
 
 	"github.com/kataras/iris"
 )
@@ -20,6 +21,7 @@ type ErrorResponse struct {
 }
 
 func responseError(ctx iris.Context, code int, message string) {
+	server.Log.Errorf("错误:%s", message)
 	ctx.JSON(ErrorResponse{
 		Code:    code,
 		Message: message,

+ 2 - 0
services/knowoapi/controllers/parsebody.go

@@ -1,6 +1,8 @@
 package controllers
 
 import (
+	"sparrow/services/knowoapi/model"
+
 	"github.com/kataras/iris"
 )
 

+ 31 - 1
services/knowoapi/controllers/produdct.go

@@ -2,6 +2,8 @@ package controllers
 
 import (
 	"sparrow/pkg/models"
+	"sparrow/pkg/rpcs"
+	"sparrow/pkg/server"
 	"sparrow/services/knowoapi/services"
 
 	"github.com/kataras/iris"
@@ -22,6 +24,23 @@ func (a *ProductController) Post() {
 		return
 	}
 	product.VendorID = int32(a.Token.getVendorID(a.Ctx))
+
+	// if upload a image file
+	if product.ProductImage != "" {
+		//move image
+		args := &rpcs.ArgsMoveFile{
+			Source: product.ProductImage,
+			Target: "product",
+		}
+		reply := &rpcs.ReplyMoveFile{}
+		err := server.RPCCallByName("fileaccess", "FileAccess.MoveFile", args, reply)
+		if err != nil {
+			server.Log.Error(err)
+			product.ProductImage = ""
+		} else {
+			product.ProductImage = reply.FilePath
+		}
+	}
 	err := a.Service.Create(product)
 	if err != nil {
 		responseError(a.Ctx, ErrDatabase, err.Error())
@@ -80,7 +99,7 @@ func (a *ProductController) Get() {
 		badRequest(a.Ctx, err)
 		return
 	}
-	name := a.Ctx.URLParam("companyname")
+	name := a.Ctx.URLParam("name")
 	ds, total, err := a.Service.GetVendorProducts(a.Token.getVendorID(a.Ctx),
 		pi,
 		ps,
@@ -94,3 +113,14 @@ func (a *ProductController) Get() {
 		"total": total,
 	})
 }
+
+// GetBy 获取产品信息
+// GET /product/{key}
+func (a *ProductController) GetBy(key string) {
+	pro, err := a.Service.QueryOne(a.Token.getVendorID(a.Ctx), key)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, pro)
+}

+ 103 - 0
services/knowoapi/controllers/protocal.go

@@ -0,0 +1,103 @@
+package controllers
+
+import (
+	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/services"
+
+	"github.com/kataras/iris"
+)
+
+// ProtocalController api
+type ProtocalController struct {
+	Ctx     iris.Context
+	Service services.ProtocalService
+	Token   Token
+}
+
+// Post 创建
+// POST /protocal
+func (a *ProtocalController) Post() {
+	ptl := new(models.Protocal)
+	if err := parseBody(a.Ctx, ptl); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	err := a.Service.Create(ptl)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, ptl)
+}
+
+// Delete 删除
+// DELETE /protocal
+func (a *ProtocalController) Delete() {
+	ptl := new(models.Protocal)
+	if err := parseBody(a.Ctx, ptl); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	err := a.Service.Delete(ptl)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, "删除成功")
+}
+
+// Put 更新
+// PUT /protocal
+func (a *ProtocalController) Put() {
+	ptl := new(models.Protocal)
+	if err := parseBody(a.Ctx, ptl); err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+
+	p, err := a.Service.Update(ptl)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, p)
+}
+
+// GetBy 根据id查询协议的内容
+// GET /protocal/{pid}
+func (a *ProtocalController) GetBy(pid int) {
+	p, err := a.Service.GetProtocalInfo(uint(pid))
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, p)
+}
+
+// Get 获取某个产品的协议列表
+// GET /protocal?proid=1&pi&ps&name&label
+func (a *ProtocalController) Get() {
+	pi, err := a.Ctx.URLParamInt("pi")
+	if err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	ps, err := a.Ctx.URLParamInt("ps")
+	if err != nil {
+		badRequest(a.Ctx, err)
+		return
+	}
+	proid, err := a.Ctx.URLParamInt("proid")
+	name := a.Ctx.URLParam("name")
+	label := a.Ctx.URLParam("label")
+
+	ds, total, err := a.Service.GetProductProtocals(uint(proid), pi, ps, name, label)
+	if err != nil {
+		responseError(a.Ctx, ErrDatabase, err.Error())
+		return
+	}
+	done(a.Ctx, map[string]interface{}{
+		"list":  ds,
+		"total": total,
+	})
+}

+ 1 - 0
services/knowoapi/controllers/token.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/model"
 	"time"
 
 	"github.com/kataras/iris"

+ 5 - 0
services/knowoapi/controllers/user.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/model"
 	"sparrow/services/knowoapi/services"
 
 	"github.com/kataras/iris"
@@ -86,3 +87,7 @@ func (a *UserController) PostRegistry() {
 		"user_id":   user.ID,
 	})
 }
+
+func (a *UserController) PostFile() {
+
+}

BIN
services/knowoapi/knowoapi


+ 7 - 3
services/knowoapi/model/all.go

@@ -4,9 +4,11 @@ import "github.com/jinzhu/gorm"
 
 // All 导出
 type All struct {
-	Product *Product
-	Vendor  *Vendor
-	User    *User
+	Product     *Product
+	Vendor      *Vendor
+	User        *User
+	Application *Application
+	Protocal    *Protocal
 }
 
 // Init 初始化所有model
@@ -14,5 +16,7 @@ func (a *All) Init(db *gorm.DB) *All {
 	a.Product = new(Product).Init(db)
 	a.Vendor = new(Vendor).Init(db)
 	a.User = new(User).Init(db)
+	a.Application = new(Application).Init(db)
+	a.Protocal = new(Protocal).Init(db)
 	return a
 }

+ 87 - 0
services/knowoapi/model/application.go

@@ -0,0 +1,87 @@
+package model
+
+import (
+	"fmt"
+	"sparrow/pkg/models"
+
+	"github.com/jinzhu/gorm"
+)
+
+// Application 应用
+type Application struct {
+	db *gorm.DB
+}
+
+// Init 初始化
+func (a *Application) Init(db *gorm.DB) *Application {
+	a.db = db
+	return a
+}
+
+// Create 创建
+func (a *Application) Create(app *models.Application) error {
+	cache := getCache()
+	key := fmt.Sprintf("Application:%s", app.AppKey)
+	cache.Set(key, app)
+	return a.db.Create(app).Error
+}
+
+//Delete 删除
+func (a *Application) Delete(app *models.Application) error {
+	cache := getCache()
+	key := fmt.Sprintf("Application:%s", app.AppKey)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
+	return a.db.Delete(app).Error
+}
+
+// GetAppCount 查询一个厂商的Application数量
+func (a *Application) GetAppCount(vendorid uint) (int, error) {
+	var count int
+	err := a.db.Model(&models.Application{}).Where("vendor_id = ?", vendorid).Count(&count).Error
+	if err != nil {
+		return 0, err
+	}
+	return count, nil
+}
+
+// GetVendorApps 获取厂商的app列表
+func (a *Application) GetVendorApps(vendorid uint, pi, ps int, name string) (datas []models.Application, count int, err error) {
+	tx := a.db.Where("vendor_id = ? and 1=1", vendorid)
+	if name != "" {
+		tx = tx.Where("app_name = ?", name)
+	}
+	err = tx.Limit(ps).Offset((pi - 1) * ps).Find(&datas).Error
+	tx.Model(&models.Application{}).Count(&count)
+	return
+}
+
+// GetAppInfo 查询app信息
+func (a *Application) GetAppInfo(vendorid uint, appkey string) (data models.Application, err error) {
+
+	cache := getCache()
+	key := fmt.Sprintf("Application:%s", appkey)
+	if v, ok := cache.Get(key); ok {
+		_d := v.(*models.Application)
+		data = *_d
+	} else {
+		err = a.db.Where("vendor_id = ? and app_key = ?", vendorid, appkey).First(&data).Error
+		if err == nil {
+			cache.Set(key, &data)
+		}
+	}
+	return
+}
+
+// Update 更新
+func (a *Application) Update(app *models.Application) (data models.Application, err error) {
+	cache := getCache()
+	key := fmt.Sprintf("Application:%s", app.AppKey)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
+	cache.Set(key, app)
+	err = a.db.Model(&data).Update(app).Error
+	return
+}

+ 30 - 0
services/knowoapi/model/product.go

@@ -1,6 +1,7 @@
 package model
 
 import (
+	"fmt"
 	"sparrow/pkg/models"
 
 	"github.com/jinzhu/gorm"
@@ -19,16 +20,30 @@ func (a *Product) Init(_db *gorm.DB) *Product {
 
 //Create 添加
 func (a *Product) Create(product *models.Product) error {
+	cache := getCache()
+	key := fmt.Sprintf("Product:%s", product.ProductKey)
+	cache.Set(key, product)
 	return a.db.Save(product).Error
 }
 
 // Delete 删除
 func (a *Product) Delete(product *models.Product) error {
+	cache := getCache()
+	key := fmt.Sprintf("Product:%s", product.ProductKey)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
 	return a.db.Delete(product).Error
 }
 
 // Update 更新
 func (a *Product) Update(product *models.Product) (pro models.Product, err error) {
+	cache := getCache()
+	key := fmt.Sprintf("Product:%s", product.ProductKey)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
+	cache.Set(key, product)
 	err = a.db.Model(&pro).Update(product).Error
 	return
 }
@@ -43,3 +58,18 @@ func (a *Product) GetVendorProducts(vendorid uint, pi, ps int, name string) (dat
 	tx.Model(&models.Product{}).Count(&total)
 	return
 }
+
+// QueryOne 获取单个产品内容
+func (a *Product) QueryOne(productkey string, vendorid uint) (data models.Product, err error) {
+	cache := getCache()
+	key := fmt.Sprintf("Product:%s", productkey)
+	if v, ok := cache.Get(key); ok {
+		_d := v.(*models.Product)
+		data = *_d
+	} else {
+		err = a.db.Where("vendor_id = ? and product_key = ?", vendorid, productkey).First(&data).Error
+		cache.Set(key, &data)
+	}
+
+	return
+}

+ 82 - 0
services/knowoapi/model/protocal.go

@@ -0,0 +1,82 @@
+package model
+
+import (
+	"fmt"
+	"sparrow/pkg/models"
+
+	"github.com/jinzhu/gorm"
+)
+
+// Protocal ``
+type Protocal struct {
+	db *gorm.DB
+}
+
+// Init 初始化
+func (a *Protocal) Init(db *gorm.DB) *Protocal {
+	a.db = db
+	return a
+}
+
+// Create 创建
+func (a *Protocal) Create(ptl *models.Protocal) error {
+	cache := getCache()
+	key := fmt.Sprintf("Protocal:%d", ptl.ID)
+	err := a.db.Create(ptl).Error
+	if err != nil {
+		return err
+	}
+	cache.Set(key, ptl)
+	return nil
+}
+
+// Delete 删除
+func (a *Protocal) Delete(ptl *models.Protocal) error {
+	cache := getCache()
+	key := fmt.Sprintf("Protocal:%d", ptl.ID)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
+	return a.db.Delete(ptl).Error
+}
+
+// Update 更新
+func (a *Protocal) Update(ptl *models.Protocal) (data models.Protocal, err error) {
+	cache := getCache()
+	key := fmt.Sprintf("Protocal:%d", ptl.ID)
+	if _, ok := cache.Get(key); ok {
+		cache.Delete(key)
+	}
+	err = a.db.Model(&data).Update(ptl).Error
+	return
+}
+
+// GetProductProtocals 获取产品的数据点列表
+func (a *Protocal) GetProductProtocals(productid uint, pi, ps int, name, label string) (datas []models.Protocal, total int, err error) {
+	tx := a.db.Where("product_id = ? and 1 = 1", productid)
+	if name != "" {
+		tx = tx.Where("name like ? ", "%"+name+"%")
+	}
+	if label != "" {
+		tx = tx.Where("label = ?", label)
+	}
+
+	err = tx.Limit(ps).Offset((pi - 1) * ps).Find(&datas).Error
+	tx.Model(&models.Protocal{}).Count(&total)
+	return
+}
+
+// GetProtocalInfo 获取数据点内容
+func (a *Protocal) GetProtocalInfo(pid uint) (data models.Protocal, err error) {
+	cache := getCache()
+	key := fmt.Sprintf("Protocal:%d", pid)
+	if v, ok := cache.Get(key); ok {
+		data = *(v.(*models.Protocal))
+	} else {
+		err = a.db.Where("id = ?", pid).First(&data).Error
+		if err == nil {
+			cache.Set(key, &data)
+		}
+	}
+	return
+}

+ 14 - 6
services/knowoapi/router.go

@@ -12,8 +12,6 @@ import (
 	"github.com/kataras/iris/mvc"
 )
 
-var router iris.Party
-
 func registerErrors(srv *iris.Application) {
 	srv.OnAnyErrorCode(handleErrors)
 }
@@ -46,15 +44,25 @@ func newJWThandle() func(ctx iris.Context) {
 func registerRouters(srv *iris.Application, models *model.All, gen *generator.KeyGenerator) {
 	pService := services.NewProductService(models, gen)
 	userService := services.NewUserService(models, gen)
-	router = srv.Party("/api/v1")
+	appService := services.NewAppService(models, gen)
+	protocalService := services.NewProtocalService(models)
+
+	v1router := srv.Party("/api/v1")
 
 	// 登陆,注册
-	loginAPI := mvc.New(router.Party("/"))
+	loginAPI := mvc.New(v1router.Party("/"))
 	loginAPI.Register(userService).Handle(new(controllers.UserController))
 
 	// 用户接口组
-	userRouter := router.Party("/user")
-
+	userRouter := v1router.Party("/user")
+	// product api
 	productAPI := mvc.New(userRouter.Party("/product", newJWThandle()))
 	productAPI.Register(pService).Handle(new(controllers.ProductController))
+	//application api
+	appAPI := mvc.New(userRouter.Party("/application", newJWThandle()))
+	appAPI.Register(appService).Handle(new(controllers.AppController))
+	// protocal api
+	protocalAPI := mvc.New(userRouter.Party("/protocal", newJWThandle()))
+	protocalAPI.Register(protocalService).Handle(new(controllers.ProtocalController))
+
 }

+ 58 - 0
services/knowoapi/services/application.go

@@ -0,0 +1,58 @@
+package services
+
+import (
+	"errors"
+	"sparrow/pkg/generator"
+	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/model"
+)
+
+// ApplicationService 业务接口
+type ApplicationService interface {
+	Create(*models.Application) error
+	Delete(*models.Application) error
+	Update(*models.Application) (models.Application, error)
+	// 查询App信息
+	// 参数 :厂商ID,APPKey
+	GetAppInfo(uint, string) (models.Application, error)
+	GetVendorApps(vendorid uint, pi, ps int, name string) ([]models.Application, int, error)
+}
+
+type appService struct {
+	model  *model.All
+	keyGen *generator.KeyGenerator
+}
+
+// NewAppService new app service
+func NewAppService(model *model.All, gen *generator.KeyGenerator) ApplicationService {
+	return appService{
+		model:  model,
+		keyGen: gen,
+	}
+}
+
+func (a appService) Create(app *models.Application) error {
+	count, err := a.model.Application.GetAppCount(app.VendorID)
+	if err != nil {
+		return err
+	}
+	if count > 1 {
+		return errors.New("一个厂商只能创建一个app")
+	}
+	app.AppKey, _ = a.keyGen.GenRandomKey(app.ID)
+	return a.model.Application.Create(app)
+}
+
+func (a appService) Delete(app *models.Application) error {
+	return a.model.Application.Delete(app)
+}
+func (a appService) Update(app *models.Application) (models.Application, error) {
+	return a.model.Application.Update(app)
+}
+func (a appService) GetAppInfo(vendorid uint, key string) (models.Application, error) {
+	return a.model.Application.GetAppInfo(vendorid, key)
+}
+
+func (a appService) GetVendorApps(vendorid uint, pi, ps int, name string) ([]models.Application, int, error) {
+	return a.model.Application.GetVendorApps(vendorid, pi, ps, name)
+}

+ 7 - 0
services/knowoapi/services/product.go

@@ -13,6 +13,9 @@ type ProductService interface {
 	Update(*models.Product) (models.Product, error)
 	// 查询厂商所有产品列表
 	GetVendorProducts(vendorid uint, pi, ps int, name string) ([]models.Product, int, error)
+	// 查询单个产品信息
+	//参数:厂商ID,产品KEY
+	QueryOne(uint, string) (models.Product, error)
 }
 
 type productService struct {
@@ -43,3 +46,7 @@ func (p *productService) Update(pro *models.Product) (models.Product, error) {
 func (p *productService) GetVendorProducts(vendorid uint, pi, ps int, name string) ([]models.Product, int, error) {
 	return p.model.Product.GetVendorProducts(vendorid, pi, ps, name)
 }
+
+func (p *productService) QueryOne(vendorid uint, key string) (models.Product, error) {
+	return p.model.Product.QueryOne(key, vendorid)
+}

+ 44 - 0
services/knowoapi/services/protocal.go

@@ -0,0 +1,44 @@
+package services
+
+import (
+	"sparrow/pkg/models"
+	"sparrow/services/knowoapi/model"
+)
+
+// ProtocalService 业务接口
+type ProtocalService interface {
+	Create(*models.Protocal) error
+	Update(*models.Protocal) (models.Protocal, error)
+	Delete(*models.Protocal) error
+	GetProtocalInfo(pid uint) (models.Protocal, error)
+	// 获取产品的协议列表
+	// 参数 : 产品id, pi, ps, 名称,标签
+	GetProductProtocals(productid uint, pi, ps int, name, label string) ([]models.Protocal, int, error)
+}
+
+type protocalService struct {
+	model *model.All
+}
+
+// NewProtocalService new protocal service
+func NewProtocalService(model *model.All) ProtocalService {
+	return protocalService{
+		model: model,
+	}
+}
+
+func (a protocalService) Create(ptl *models.Protocal) error {
+	return a.model.Protocal.Create(ptl)
+}
+func (a protocalService) Delete(ptl *models.Protocal) error {
+	return a.model.Protocal.Delete(ptl)
+}
+func (a protocalService) Update(ptl *models.Protocal) (models.Protocal, error) {
+	return a.model.Protocal.Update(ptl)
+}
+func (a protocalService) GetProductProtocals(proid uint, pi, ps int, name, label string) ([]models.Protocal, int, error) {
+	return a.model.Protocal.GetProductProtocals(proid, pi, ps, name, label)
+}
+func (a protocalService) GetProtocalInfo(pid uint) (models.Protocal, error) {
+	return a.model.Protocal.GetProtocalInfo(pid)
+}

+ 20 - 0
vendor/github.com/iris-contrib/middleware/cors/LICENSE

@@ -0,0 +1,20 @@
+Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
+Copyright (c) 2018 Gerasimos Maropoulos <kataras2006@hotmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 378 - 0
vendor/github.com/iris-contrib/middleware/cors/cors.go

@@ -0,0 +1,378 @@
+package cors
+
+import (
+	"log"
+	"net/http"
+	"os"
+	"strconv"
+	"strings"
+
+	"github.com/kataras/iris/context"
+)
+
+// Options is a configuration container to setup the CORS middleware.
+type Options struct {
+	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
+	// If the special "*" value is present in the list, all origins will be allowed.
+	// An origin may contain a wildcard (*) to replace 0 or more characters
+	// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty.
+	// Only one wildcard can be used per origin.
+	// Default value is ["*"]
+	AllowedOrigins []string
+	// AllowOriginFunc is a custom function to validate the origin. It take the origin
+	// as argument and returns true if allowed or false otherwise. If this option is
+	// set, the content of AllowedOrigins is ignored.
+	AllowOriginFunc func(origin string) bool
+	// AllowedMethods is a list of methods the client is allowed to use with
+	// cross-domain requests. Default value is simple methods (HEAD, GET and POST).
+	AllowedMethods []string
+	// AllowedHeaders is list of non simple headers the client is allowed to use with
+	// cross-domain requests.
+	// If the special "*" value is present in the list, all headers will be allowed.
+	// Default value is [] but "Origin" is always appended to the list.
+	AllowedHeaders []string
+	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
+	// API specification
+	ExposedHeaders []string
+	// MaxAge indicates how long (in seconds) the results of a preflight request
+	// can be cached
+	MaxAge int
+	// AllowCredentials indicates whether the request can include user credentials like
+	// cookies, HTTP authentication or client side SSL certificates.
+	AllowCredentials bool
+	// OptionsPassthrough instructs preflight to let other potential next handlers to
+	// process the OPTIONS method. Turn this on if your application handles OPTIONS.
+	OptionsPassthrough bool
+	// Debugging flag adds additional output to debug server side CORS issues
+	Debug bool
+}
+
+// Cors http handler
+type Cors struct {
+	// Debug logger
+	Log *log.Logger
+	// Normalized list of plain allowed origins
+	allowedOrigins []string
+	// List of allowed origins containing wildcards
+	allowedWOrigins []wildcard
+	// Optional origin validator function
+	allowOriginFunc func(origin string) bool
+	// Normalized list of allowed headers
+	allowedHeaders []string
+	// Normalized list of allowed methods
+	allowedMethods []string
+	// Normalized list of exposed headers
+	exposedHeaders []string
+	maxAge         int
+	// Set to true when allowed origins contains a "*"
+	allowedOriginsAll bool
+	// Set to true when allowed headers contains a "*"
+	allowedHeadersAll bool
+	allowCredentials  bool
+	optionPassthrough bool
+}
+
+// New creates a new Cors handler with the provided options.
+func New(options Options) context.Handler {
+	c := &Cors{
+		exposedHeaders:    convert(options.ExposedHeaders, http.CanonicalHeaderKey),
+		allowOriginFunc:   options.AllowOriginFunc,
+		allowCredentials:  options.AllowCredentials,
+		maxAge:            options.MaxAge,
+		optionPassthrough: options.OptionsPassthrough,
+	}
+	if options.Debug {
+		c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
+	}
+
+	// Normalize options
+	// Note: for origins and methods matching, the spec requires a case-sensitive matching.
+	// As it may error prone, we chose to ignore the spec here.
+
+	// Allowed Origins
+	if len(options.AllowedOrigins) == 0 {
+		if options.AllowOriginFunc == nil {
+			// Default is all origins
+			c.allowedOriginsAll = true
+		}
+	} else {
+		c.allowedOrigins = []string{}
+		c.allowedWOrigins = []wildcard{}
+		for _, origin := range options.AllowedOrigins {
+			// Normalize
+			origin = strings.ToLower(origin)
+			if origin == "*" {
+				// If "*" is present in the list, turn the whole list into a match all
+				c.allowedOriginsAll = true
+				c.allowedOrigins = nil
+				c.allowedWOrigins = nil
+				break
+			} else if i := strings.IndexByte(origin, '*'); i >= 0 {
+				// Split the origin in two: start and end string without the *
+				w := wildcard{origin[0:i], origin[i+1:]}
+				c.allowedWOrigins = append(c.allowedWOrigins, w)
+			} else {
+				c.allowedOrigins = append(c.allowedOrigins, origin)
+			}
+		}
+	}
+
+	// Allowed Headers
+	if len(options.AllowedHeaders) == 0 {
+		// Use sensible defaults
+		c.allowedHeaders = []string{"Origin", "Accept", "Content-Type", "X-Requested-With"}
+	} else {
+		// Origin is always appended as some browsers will always request for this header at preflight
+		c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
+		for _, h := range options.AllowedHeaders {
+			if h == "*" {
+				c.allowedHeadersAll = true
+				c.allowedHeaders = nil
+				break
+			}
+		}
+	}
+
+	// Allowed Methods
+	if len(options.AllowedMethods) == 0 {
+		// Default is spec's "simple" methods
+		c.allowedMethods = []string{"GET", "POST", "HEAD"}
+	} else {
+		c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
+	}
+
+	return c.Serve
+}
+
+// Default creates a new Cors handler with default options.
+func Default() context.Handler {
+	return New(Options{})
+}
+
+// AllowAll create a new Cors handler with permissive configuration allowing all
+// origins with all standard methods with any header and credentials.
+func AllowAll() context.Handler {
+	return New(Options{
+		AllowedOrigins:   []string{"*"},
+		AllowedMethods:   []string{"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"},
+		AllowedHeaders:   []string{"*"},
+		AllowCredentials: true,
+	})
+}
+
+// Serve apply the CORS specification on the request, and add relevant CORS headers
+// as necessary.
+func (c *Cors) Serve(ctx context.Context) {
+	if ctx.Method() == http.MethodOptions && ctx.GetHeader("Access-Control-Request-Method") != "" {
+		c.logf("Serve: Preflight request")
+		c.handlePreflight(ctx)
+		if c.optionPassthrough { // handle the options by routes.
+			ctx.Next()
+			return
+		}
+
+		if !ctx.IsStopped() {
+			// just 200.
+			ctx.StatusCode(http.StatusOK)
+			ctx.StopExecution()
+		}
+
+		return
+	}
+
+	c.logf("Serve: Actual request")
+	c.handleActualRequest(ctx)
+	ctx.Next()
+}
+
+// handlePreflight handles pre-flight CORS requests
+func (c *Cors) handlePreflight(ctx context.Context) {
+	origin := ctx.GetHeader("Origin")
+
+	if ctx.Method() != http.MethodOptions {
+		c.logf("  Preflight aborted: %s!=OPTIONS", ctx.Method())
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+	// Always set Vary headers.
+	ctx.Header("Vary", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers")
+
+	if origin == "" {
+		c.logf("  Preflight aborted: empty origin")
+		return
+	}
+	if !c.isOriginAllowed(origin) {
+		c.logf("  Preflight aborted: origin '%s' not allowed", origin)
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+
+	reqMethod := ctx.GetHeader("Access-Control-Request-Method")
+	if !c.isMethodAllowed(reqMethod) {
+		c.logf("  Preflight aborted: method '%s' not allowed", reqMethod)
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+	reqHeaders := parseHeaderList(ctx.GetHeader("Access-Control-Request-Headers"))
+	if !c.areHeadersAllowed(reqHeaders) {
+		c.logf("  Preflight aborted: headers '%v' not allowed", reqHeaders)
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+	if c.allowedOriginsAll && !c.allowCredentials {
+		ctx.Header("Access-Control-Allow-Origin", "*")
+	} else {
+		ctx.Header("Access-Control-Allow-Origin", origin)
+	}
+	// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
+	// by Access-Control-Request-Method (if supported) can be enough
+	ctx.Header("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
+	if len(reqHeaders) > 0 {
+		// Spec says: Since the list of headers can be unbounded, simply returning supported headers
+		// from Access-Control-Request-Headers can be enough
+		ctx.Header("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
+	}
+	if c.allowCredentials {
+		ctx.Header("Access-Control-Allow-Credentials", "true")
+	}
+	if c.maxAge > 0 {
+		ctx.Header("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
+	}
+	c.logf("  Preflight response headers: %v", ctx.ResponseWriter().Header())
+}
+
+// handleActualRequest handles simple cross-origin requests, actual request or redirects
+func (c *Cors) handleActualRequest(ctx context.Context) {
+	origin := ctx.GetHeader("Origin")
+
+	if ctx.Method() == http.MethodOptions {
+		c.logf("  Actual request no headers added: method == %s", ctx.Method())
+		//
+		ctx.StatusCode(http.StatusMethodNotAllowed)
+		ctx.StopExecution()
+		//
+		return
+	}
+	// Always set Vary, see https://github.com/rs/cors/issues/10
+	ctx.ResponseWriter().Header().Add("Vary", "Origin")
+	if origin == "" {
+		c.logf("  Actual request no headers added: missing origin")
+		return
+	}
+	if !c.isOriginAllowed(origin) {
+		c.logf("  Actual request no headers added: origin '%s' not allowed", origin)
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+
+	// Note that spec does define a way to specifically disallow a simple method like GET or
+	// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
+	// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
+	// We think it's a nice feature to be able to have control on those methods though.
+	if !c.isMethodAllowed(ctx.Method()) {
+		c.logf("  Actual request no headers added: method '%s' not allowed", ctx.Method())
+		//
+		ctx.StatusCode(http.StatusForbidden)
+		ctx.StopExecution()
+		//
+		return
+	}
+	if c.allowedOriginsAll && !c.allowCredentials {
+		ctx.Header("Access-Control-Allow-Origin", "*")
+	} else {
+		ctx.Header("Access-Control-Allow-Origin", origin)
+	}
+	if len(c.exposedHeaders) > 0 {
+		ctx.Header("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
+	}
+	if c.allowCredentials {
+		ctx.Header("Access-Control-Allow-Credentials", "true")
+	}
+	c.logf("  Actual response added headers: %v", ctx.ResponseWriter().Header())
+}
+
+// convenience method. checks if debugging is turned on before printing
+func (c *Cors) logf(format string, a ...interface{}) {
+	if c.Log != nil {
+		c.Log.Printf(format, a...)
+	}
+}
+
+// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
+// on the endpoint
+func (c *Cors) isOriginAllowed(origin string) bool {
+	if c.allowOriginFunc != nil {
+		return c.allowOriginFunc(origin)
+	}
+	if c.allowedOriginsAll {
+		return true
+	}
+	origin = strings.ToLower(origin)
+	for _, o := range c.allowedOrigins {
+		if o == origin {
+			return true
+		}
+	}
+	for _, w := range c.allowedWOrigins {
+		if w.match(origin) {
+			return true
+		}
+	}
+	return false
+}
+
+// isMethodAllowed checks if a given method can be used as part of a cross-domain request
+// on the endpoing
+func (c *Cors) isMethodAllowed(method string) bool {
+	if len(c.allowedMethods) == 0 {
+		// If no method allowed, always return false, even for preflight request
+		return false
+	}
+	method = strings.ToUpper(method)
+	if method == http.MethodOptions {
+		// Always allow preflight requests
+		return true
+	}
+	for _, m := range c.allowedMethods {
+		if m == method {
+			return true
+		}
+	}
+	return false
+}
+
+// areHeadersAllowed checks if a given list of headers are allowed to used within
+// a cross-domain request.
+func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
+	if c.allowedHeadersAll || len(requestedHeaders) == 0 {
+		return true
+	}
+	for _, header := range requestedHeaders {
+		header = http.CanonicalHeaderKey(header)
+		found := false
+		for _, h := range c.allowedHeaders {
+			if h == header {
+				found = true
+			}
+		}
+		if !found {
+			return false
+		}
+	}
+	return true
+}

+ 70 - 0
vendor/github.com/iris-contrib/middleware/cors/util.go

@@ -0,0 +1,70 @@
+package cors
+
+import "strings"
+
+const toLower = 'a' - 'A'
+
+type converter func(string) string
+
+type wildcard struct {
+	prefix string
+	suffix string
+}
+
+func (w wildcard) match(s string) bool {
+	return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
+}
+
+// convert converts a list of string using the passed converter function
+func convert(s []string, c converter) []string {
+	out := []string{}
+	for _, i := range s {
+		out = append(out, c(i))
+	}
+	return out
+}
+
+// parseHeaderList tokenize + normalize a string containing a list of headers
+func parseHeaderList(headerList string) []string {
+	l := len(headerList)
+	h := make([]byte, 0, l)
+	upper := true
+	// Estimate the number headers in order to allocate the right splice size
+	t := 0
+	for i := 0; i < l; i++ {
+		if headerList[i] == ',' {
+			t++
+		}
+	}
+	headers := make([]string, 0, t)
+	for i := 0; i < l; i++ {
+		b := headerList[i]
+		if b >= 'a' && b <= 'z' {
+			if upper {
+				h = append(h, b-toLower)
+			} else {
+				h = append(h, b)
+			}
+		} else if b >= 'A' && b <= 'Z' {
+			if !upper {
+				h = append(h, b+toLower)
+			} else {
+				h = append(h, b)
+			}
+		} else if b == '-' || b == '_' || (b >= '0' && b <= '9') {
+			h = append(h, b)
+		}
+
+		if b == ' ' || b == ',' || i == l-1 {
+			if len(h) > 0 {
+				// Flush the found header
+				headers = append(headers, string(h))
+				h = h[:0]
+				upper = true
+			}
+		} else {
+			upper = b == '-' || b == '_'
+		}
+	}
+	return headers
+}

+ 6 - 0
vendor/vendor.json

@@ -192,6 +192,12 @@
 			"revision": "b1670413ba6a7532e46c12ff23d0914c270d2768",
 			"revisionTime": "2018-09-01T16:27:21Z"
 		},
+		{
+			"checksumSHA1": "zwn4ZL24FSFg1y/VXP9DPCpKgkc=",
+			"path": "github.com/iris-contrib/middleware/cors",
+			"revision": "d0897469986fd8cbb04265da38d9fce539750ec8",
+			"revisionTime": "2018-08-02T23:36:05Z"
+		},
 		{
 			"checksumSHA1": "P5UW1XI9SKV3x6XeuQqHtN6AFoY=",
 			"path": "github.com/iris-contrib/middleware/jwt",