GO项目基础实操总结

前情:

上一篇讲了关于go基本的安装、环境搭建、项目搭建和运行,这一篇继续深入;
上一篇参考:使用 vsCode创建GO项目

本篇要点:
  1. GO 连接数据库&CRUD
  2. GO 测试方法开发
  3. GO 和java普通属性对比
  4. GO 和java组件对比(多线程、事务、切面、拦截器)
  5. GO 系统权限控制
  6. GO Proto Buffer
  7. GO 实现服务网格

1.GO 连接数据库&CRUD

1.1 go 的传参方式
我们通常使用gin 框架绑定函数创建接口,接口的参数都封装在了gin.Context对象中,通常使用 header用于参数的传递:

const NAME string = "name"
const USER_ID string = "user_id"
func GetParam(c *gin.Context) {
	userId := c.GetHeader(USER_ID)
	fmt.Println(userId)
	name := c.GetHeader(NAME) 
	fmt.Println(name)
	name3, ok := c.GetPostFormArray(NAME)
	fmt.Println(name3)
	fmt.Println(ok)
}

GO项目基础实操总结_第1张图片
GetPostFormArray 也可以接收
GO项目基础实操总结_第2张图片

1.2 go 连接 mysql
连接首先需要配置application.yaml

# database config
database:
  driver: ${DATABASE_DRIVER:mysql} #mysql
  table-prefix: mydb
  mysql-dsn: ${DATABASE_USER:root}:${DATABASE_PASSWORD:password}@tcp(${DATABASE_HOST:localhost}:${DATABASE_PORT:3306})/${DATABASE_NAME:mydb}?charset=utf8mb4&parseTime=True&loc=Local
  max-idle-conns: ${DATABASE_MAX_IDLE_CONNS:10}
  max-open-conns: ${DATABASE_MAX_OPEN_CONNS:100}

driver/mysql-dsn 用于gorm创建数据库的连接,
gorm 是一个 Go 语言编写的 ORM(Object Relational Mapping,即对象关系映射)库,它使得在 Go 中操作数据库变得更加方便和高效。gorm 提供了简洁的 API 来进行数据库操作,包括创建表、插入数据、查询数据、更新数据和删除数据等。
连接代码如下:

import (
	"chapter-1/utils/config"
	"github.com/jinzhu/gorm"
)
var gdb *gorm.DB
func CreateDB() (*gorm.DB, error) {
	if gdb == nil {
		driver := config.GetString("database.driver")
		dsn := dsn = config.GetString("database.mysql-dsn")
		db, err := gorm.Open(driver, dsn)
		if err != nil {
			return nil, err
		}
		db.DB().SetMaxIdleConns(config.GetInt("database.max-idle-conns"))
		db.DB().SetMaxOpenConns(config.GetInt("database.max-open-conns"))
		db.LogMode(config.GetBool("application.debug"))
		gdb = db
	}
	return gdb, nil
}

1.3 gorm
通过gorm 连接到数据库之后,就可以使用gorm 提供的简洁的 API 来进行数据库操作,
实例service代码:

package service

import (
	"chapter-1/moddel"
	"chapter-1/utils/orm"
	"errors"
	"github.com/sirupsen/logrus"
)

func Add(userProject moddel.TestModel) error {
	db, err := orm.CreateDB()
	if err != nil {
		logrus.Errorf("projectList service, orm.CreateDB error on row 17 :%v\n", err.Error())
		return errors.New("数据库连接异常")
	}
	errrr := userProject.Add(db)
	return errrr
}
func Update(userProject moddel.TestModel) error {
	db, err := orm.CreateDB()
	if err != nil {
		logrus.Errorf("projectList service, orm.CreateDB error on row 17 :%v\n", err.Error())
		return errors.New("数据库连接异常")
	}

	errrr := userProject.Update(db)
	return errrr
}

func Detelete(userId string) error {
	db, err := orm.CreateDB()
	if err != nil {
		logrus.Errorf("projectList service, orm.CreateDB error on row 17 :%v\n", err.Error())
		return errors.New("数据库连接异常")
	}

	userProject := moddel.TestModel{ID: userId}
	errrr := userProject.Delete(db)
	return errrr
}
func GetById(userId string) ([]moddel.TestModel, error) {
	db, err := orm.CreateDB()
	if err != nil {
		logrus.Errorf("projectList service, orm.CreateDB error on row 17 :%v\n", err.Error())
		return nil, errors.New("数据库连接异常")
	}

	userProject := moddel.TestModel{ID: userId}
	ups, err := userProject.RawByUserId(db)
	if err != nil {
		logrus.Errorf("projectList service, userProject.FindByUserId error on row 24:%v\n", err.Error())
		return nil, errors.New("数据库查询异常")
	}
	if len(ups) == 0 {
		return nil, nil
	}
	return ups, err
}

实例 gorm 代码:

package moddel

import (
	"strings"
	"github.com/jinzhu/gorm"
)

type TestModel struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}
// 直接使用 gorm 的方法,默认使用gorm创建的表 mydb_test_models
func (this *TestModel) Add(db *gorm.DB) (err error) {
	return db.Create(this).Error
}
func (this *TestModel) Update(db *gorm.DB) (err error) {
	return db.Save(this).Error
}
func (this *TestModel) Delete(db *gorm.DB) (err error) {
	return db.Delete(this).Error
}
func (this *TestModel) FindByUserId(db *gorm.DB) (userProjects []TestModel, err error) {
	db = db.Find(&userProjects, "id = ?", this.ID)
	return userProjects, db.Error
}
// 这种使用方式,不会使用gorm 默认的表,这里使用 test_1
func (this *TestModel) RawByUserId(db *gorm.DB) (userProjects []TestModel, err error) {
	var sql = " select * from test_1 where id=${id} "
	sql = strings.Replace(sql, "${id}", this.ID, -1)
	db = db.Raw(sql)
	db = db.Scan(&userProjects)
	return userProjects, db.Error
}

参考:gorm官网连接

2.GO 测试方法开发

2.1 测试文件名称 _test.go 结尾
2.2 测试方法名称 Test 开始
2.3 测试方法参数 t *testing.T
GO项目基础实操总结_第3张图片

3. Go 和 java 对比

3.1 语法和风格:
  • Go 语言的语法相对简单,简洁明了。它没有复杂的类继承和多态机制,而是采用了组合和接口的方式来实现代码的复用和扩展。
  • Java 的语法相对复杂,有更多的语法结构和关键字。Java 强调面向对象编程的概念,包括类继承、多态和抽象类等。
3.2性能:
  • Go 语言是一种静态类型语言,具有较高的运行效率和并发性能。它的内存管理和垃圾回收机制相对简单,对于高并发和性能要求较高的场景有优势。
  • Java 是一种解释型语言,需要在运行时进行解释和编译。Java 的性能相对较慢,但通过 JIT(即时编译)技术可以在运行时优化代码。
3.2.1 Go 语言高效性能

主要有以下几个方面的原因:
高效的编译:Go 语言的编译器非常高效,能够生成高效的机器码。它采用了一些优化技术,如静态单赋值(SSA)形式、寄存器分配优化等,以提高代码的运行效率。
垃圾回收:Go 语言具有内置的垃圾回收机制,可以自动管理内存分配和释放。垃圾回收器的设计目标是在减少内存占用和提高性能之间取得平衡。
并发支持:Go 语言原生支持并发编程,通过轻量级的线程(goroutine)和通道(channel)来实现。这使得并发编程变得简单而高效,可以充分利用多核 CPU 的优势。
内存管理:Go 语言的内存管理相对简单,不需要手动管理内存。它采用了内存池和垃圾回收的方式来优化内存使用。
网络和 I/O 性能:Go 语言在网络和 I/O 操作方面具有很高的性能。它的网络库和文件 I/O 库经过了精心设计和优化,能够高效地处理大规模的网络通信和文件读写。

3.3并发支持:
  • Go 语言原生支持并发编程,通过 goroutine 和 channel 等机制实现轻量级的线程和通信。Go 的并发模型简单而高效。
  • Java 也提供了并发支持,通过线程和并发工具类来实现并发编程。Java 的并发模型相对复杂,但提供了更多的并发控制和同步机制。
3.3.1 使用 go 关键字实现并发

实例代码:

func TestRoutine(t *testing.T) {

	go fmt.Println("Hello from goroutine")
	fmt.Println("Main thread")

	// 等待一段时间,以便 goroutine 有足够的时间执行
	time.Sleep(2 * time.Second)
}
3.3.2 使用 channel

在 Go 语言中,通过channel可以实现并发控制。channel是 Go 语言中的一种通信机制,用于在不同的协程之间进行数据交换和同步。
实例代码:

func TestChannel(t *testing.T) {
	// 创建一个 int 类型的 channel
	ch := make(chan int)

	// 启动三个 worker
	for i := 1; i <= 3; i++ {
		go worker(i, ch)
	}

	// 发送数据到 channel
	for i := 1; i <= 10; i++ {
		ch <- i
	}

	// 关闭 channel
	close(ch)

	fmt.Println("All workers have finished")
}
func worker(id int, ch chan int) {
	// 使用 range 遍历 channel
	for value := range ch {
		fmt.Printf("Worker %d received value: %d\n", id, value)
		println("sss", "dddddd")
	}
}
3.4内存管理和垃圾回收:
  • Go 语言的内存管理和垃圾回收机制相对简单,采用了三色标记法和并发垃圾回收。
  • Java 的内存管理和垃圾回收机制相对复杂,提供了多种垃圾回收算法和调优选项。
三色标记法 :白->灰->黑

首先将所有对象都标记为白色,
然后从根对象开始遍历内存中的对象,把直接引用的对象标记为灰色,
再判断灰色集合中的对象是否存在 子引用,不存在则放入黑色集合,
如果存在,就把子引用对象放入到灰色集合,

不断重复这个步骤,直到灰色集合中所有的对象变黑后,本轮标记完成,
最后还处于白色标记的对象就是不可达对象,可以直接被回收。

3.5 应用领域:
  • Go 语言常用于网络编程、分布式系统、云计算和容器化等领域,因为它的并发性能和简洁性。
  • Java 广泛应用于企业级应用开发、Web 开发、移动应用开发等领域,因为它的成熟生态和丰富的库和框架。
    需要注意的是,以上对比仅是一些常见的差异性和优缺点,具体的选择应根据项目需求、开发团队的技能和经验以及目标平台等因素来决定。
3.6依赖管理:
  • go不用手动管理依赖,也没有pom文件,每次先 import 再使用;
  • java有pom
3.7继承:
  • go没有类只有包,没有继承,可以通过对象可以作为成员复用的方式实现类似功能;
type TestModel struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	father EmbedModel
	others []EmbedModel
}
type EmbedModel struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

4.GO 组件和java组件对比

4.1 事务

go 没有现成的事务管理组件,需要手动处理,实例:

func TestTransaction(t *testing.T) {
	db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/mydb")
	if err != nil {
		log.Fatal(err)
	}
	// 延迟关闭数据库连接
	defer db.Close()

	// 开始执行事务
	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}

	// 执行一些数据库操作

	_, err = tx.Exec("INSERT INTO test_1 (id, name) VALUES (3, 'John')")
	if err != nil {
		log.Fatal(err)
	}

	_, err = tx.Exec("INSERT INTO test_1 (id, name) VALUES (4, 'Jane')")
	if err != nil {
		// 如果出现错误,回滚事务
		tx.Rollback()
		log.Fatal(err)
	}

	// 提交事务
	err = tx.Commit()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("事务成功执行")
}
4.2 切面

go 没有内置的切面(AOP)功能。只能通过方法复用的方式实现。

4.3 拦截器

go 使用gin框架的中间件实现拦截器的功能,实例如下:
制作中间价:

func ErrorHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 执行处理逻辑
		fmt.Println("Error:THIS IS A INTERCEPTOR")
		r := c.Request
		fmt.Println("Request:", r.URL.Path)
		c.Next()
	}
}

绑定中间件:

func main() {
	// 1. 创建路由
	route := gin.Default()
	// 2. 绑定中间件
    route.Use(middleware.ErrorHandler())
	// 3. 监听端口
	route.Run(port)

5. GO 系统权限控制

系统权限控制这里采用的是 中间件的方式,实例代码:

func Authorize() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("token")
		if token == "" {
			c.Abort()
			c.JSON(http.StatusUnauthorized, response.NewBaseResponse(response.ResponseCodeUnauthorized))
			return
		}
		userID := c.GetHeader("user_id")
		if userID == "" {
			c.Abort()
			c.JSON(http.StatusBadRequest, response.NewBaseResponse(response.ResponseCodeBadRequest))
			return
		}

		localUser, err := redis.Store.GetString("token:" + token)
		if err != nil || localUser != userID {
			c.Abort()
			c.JSON(http.StatusForbidden, response.NewBaseResponse(response.ResponseCodeForbidden))
			return
		}
		c.Next()
	}
}

6. GO Proto Buffer

Protocol Buffers(协议缓冲区,简称 protobuf)是一种用于序列化和反序列化结构化数据的跨平台、跨语言的二进制格式。它由 Google 开发,并广泛应用于各种应用程序和系统中。
以下是 Protocol Buffers 的一些特点和优势:

6.1高效性:

Protocol Buffers 采用二进制格式进行数据序列化和反序列化,相对于文本格式(如 JSON),它具有更小的体积和更高的解析效率,能够有效地减少网络传输带宽和提高数据处理速度。

6.2可扩展性:

Protocol Buffers 支持定义自定义的数据类型和字段,可以根据需求灵活地扩展和修改消息结构,而不影响现有代码。

6.3多语言支持:

Protocol Buffers 提供了多种编程语言的 API 和生成代码工具,支持 C++、Java、Python、Go 等常见编程语言,可以方便地在不同语言之间进行数据交换。

6.4版本兼容性:

Protocol Buffers 通过字段编号和标记来表示字段的存在和类型,使得在不同版本的协议之间进行数据解析时能够保持向后兼容性。

6.5性能优化:

Protocol Buffers 可以进行一些性能优化,如字段排序、字段压缩等,以提高数据的存储和传输效率。
在使用 Protocol Buffers 时,通常需要先定义消息类型(使用 .proto 文件),然后使用相应的编程语言生成代码,用于序列化和反序列化数据。Protocol Buffers 常用于网络通信、数据存储、RPC( Remote Procedure Call )等场景,它提供了一种高效、灵活和标准化的方式来处理结构化数据。
如果你对 Protocol Buffers 感兴趣,可以查阅相关文档和教程,了解更多关于它的使用方法和应用场景。

ProtoBuf(Protocol Buffers)与Service Mesh没有直接的关系,它是一种独立的序列化和反序列化框架,用于高效地处理结构化数据在不同编程语言之间的传输。
而Service Mesh是一种用于管理微服务之间通信和控制的基础设施层,它将服务间通讯以及与此相关的管理控制功能从业务程序中下移到一个基础设施层,从而彻底隔离了业务逻辑和服务通讯两个关注点。

参考:
官网
知乎

7. GO 实现服务网格

Service Mesh(服务网格)是一种用于管理和监控微服务架构中服务之间通信的基础设施。它提供了一个可编程的、统一的方式来处理服务之间的通信、路由、流量控制、安全性和可观测性等方面的问题。
Service Mesh 的主要目标是解决在复杂的微服务环境中,服务之间的通信变得复杂且难以管理的问题。它通过在服务之间插入一个中间层(称为 Sidecar 代理)来实现对服务通信的控制和管理。
以下是 Service Mesh 的一些关键功能:

  • 服务发现和注册:
    Service Mesh 可以自动发现和注册服务,使服务能够相互发现并进行通信。
  • 流量管理:它可以实现负载均衡、路由、熔断等功能,以确保服务之间的流量得到有效的分配和管理。
  • 安全:Service Mesh 可以提供安全功能,如身份验证、授权、加密等,以保护服务之间的通信。
  • 可观测性:它收集和监控服务之间的性能指标和日志,帮助开发人员诊断和解决问题。
  • 灰度发布和 A/B 测试:Service Mesh 可以支持渐进式的发布和 A/B 测试,实现新版本的平滑部署。
    使用 Service Mesh,开发人员可以将服务的通信细节从应用代码中分离出来,从而使应用代码更加关注业务逻辑。它还提供了更好的可扩展性、可靠性和故障恢复能力。
    常见的 Service Mesh 实现包括 Istio、Linkerd、Consul、Envoy 等。这些工具提供了不同的功能和特点,可以根据具体需求选择适合的 Service Mesh 解决方案。
    下面是一个简单的demo:
7.1 客户端定义接口

entity.proto

syntax = "proto3";
option java_package = "cc.iooc.common.rpc.snowflake.proto";
option java_multiple_files = true;
import "string.proto";
package proto;

// imc/entity/client/entity_rpc.go文件中方法对应的定义
service Entity {
    // SaveForm()方法 保存表单定义
    rpc SaveForm (EntitySaveFormRequest) returns (EntitySaveFormResponse) {
    }
}

// SaveForm() Request
// tenant, formID string, major, minor int, form string
message EntitySaveFormRequest {
    string tenant = 1;
    string formID = 2;
    int64 major = 3;
    int64 minor = 4;
    string form = 5;
}
// SaveForm() Response
// method []model.SystemMethod, err error
message EntitySaveFormResponse {
    repeated SystemMethod systemMethod = 1;
}
7.2 客户端接口生成GO代码

entity.pb.go(这个文件是通过命令生成)

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: entity.proto

package proto

import (
	context "context"
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	math "math"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package

// SaveForm() Request
// tenant, formID string, major, minor int, form string
type EntitySaveFormRequest struct {
	Tenant               string   `protobuf:"bytes,1,opt,name=tenant,proto3" json:"tenant,omitempty"`
	FormID               string   `protobuf:"bytes,2,opt,name=formID,proto3" json:"formID,omitempty"`
	Major                int64    `protobuf:"varint,3,opt,name=major,proto3" json:"major,omitempty"`
	Minor                int64    `protobuf:"varint,4,opt,name=minor,proto3" json:"minor,omitempty"`
	Form                 string   `protobuf:"bytes,5,opt,name=form,proto3" json:"form,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *EntitySaveFormRequest) Reset()         { *m = EntitySaveFormRequest{} }
func (m *EntitySaveFormRequest) String() string { return proto.CompactTextString(m) }
func (*EntitySaveFormRequest) ProtoMessage()    {}
func (*EntitySaveFormRequest) Descriptor() ([]byte, []int) {
	return fileDescriptor_cf50d946d740d100, []int{0}
}

func (m *EntitySaveFormRequest) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_EntitySaveFormRequest.Unmarshal(m, b)
}
func (m *EntitySaveFormRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_EntitySaveFormRequest.Marshal(b, m, deterministic)
}
func (m *EntitySaveFormRequest) XXX_Merge(src proto.Message) {
	xxx_messageInfo_EntitySaveFormRequest.Merge(m, src)
}
func (m *EntitySaveFormRequest) XXX_Size() int {
	return xxx_messageInfo_EntitySaveFormRequest.Size(m)
}
func (m *EntitySaveFormRequest) XXX_DiscardUnknown() {
	xxx_messageInfo_EntitySaveFormRequest.DiscardUnknown(m)
}

var xxx_messageInfo_EntitySaveFormRequest proto.InternalMessageInfo

func (m *EntitySaveFormRequest) GetTenant() string {
	if m != nil {
		return m.Tenant
	}
	return ""
}

func (m *EntitySaveFormRequest) GetFormID() string {
	if m != nil {
		return m.FormID
	}
	return ""
}

func (m *EntitySaveFormRequest) GetMajor() int64 {
	if m != nil {
		return m.Major
	}
	return 0
}

func (m *EntitySaveFormRequest) GetMinor() int64 {
	if m != nil {
		return m.Minor
	}
	return 0
}

func (m *EntitySaveFormRequest) GetForm() string {
	if m != nil {
		return m.Form
	}
	return ""
}

// SaveForm() Response
// method []model.SystemMethod, err error
type EntitySaveFormResponse struct {
	SystemMethod         []*SystemMethod `protobuf:"bytes,1,rep,name=systemMethod,proto3" json:"systemMethod,omitempty"`
	XXX_NoUnkeyedLiteral struct{}        `json:"-"`
	XXX_unrecognized     []byte          `json:"-"`
	XXX_sizecache        int32           `json:"-"`
}

func (m *EntitySaveFormResponse) Reset()         { *m = EntitySaveFormResponse{} }
func (m *EntitySaveFormResponse) String() string { return proto.CompactTextString(m) }
func (*EntitySaveFormResponse) ProtoMessage()    {}
func (*EntitySaveFormResponse) Descriptor() ([]byte, []int) {
	return fileDescriptor_cf50d946d740d100, []int{1}
}

func (m *EntitySaveFormResponse) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_EntitySaveFormResponse.Unmarshal(m, b)
}
func (m *EntitySaveFormResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_EntitySaveFormResponse.Marshal(b, m, deterministic)
}
func (m *EntitySaveFormResponse) XXX_Merge(src proto.Message) {
	xxx_messageInfo_EntitySaveFormResponse.Merge(m, src)
}
func (m *EntitySaveFormResponse) XXX_Size() int {
	return xxx_messageInfo_EntitySaveFormResponse.Size(m)
}
func (m *EntitySaveFormResponse) XXX_DiscardUnknown() {
	xxx_messageInfo_EntitySaveFormResponse.DiscardUnknown(m)
}

var xxx_messageInfo_EntitySaveFormResponse proto.InternalMessageInfo

func (m *EntitySaveFormResponse) GetSystemMethod() []*SystemMethod {
	if m != nil {
		return m.SystemMethod
	}
	return nil
}

func init() {
	proto.RegisterType((*EntitySaveFormRequest)(nil), "proto.EntitySaveFormRequest")
	proto.RegisterType((*EntitySaveFormResponse)(nil), "proto.EntitySaveFormResponse")
}

func init() {
	proto.RegisterFile("entity.proto", fileDescriptor_cf50d946d740d100)
}

var fileDescriptor_cf50d946d740d100 = []byte{
	// 457 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0x4d, 0x6f, 0xd4, 0x30,
	0x10, 0x6d, 0x9a, 0x6e, 0xd4, 0x1d, 0xf6, 0x50, 0xb9, 0xa5, 0x44, 0xa1, 0x54, 0x2b, 0x1f, 0xd0,
	0x9e, 0x72, 0x28, 0x07, 0x84, 0xb8, 0x20, 0x68, 0x2b, 0x56, 0x62, 0x25, 0x48, 0x25, 0xee, 0x26,
	0x9d, 0x85, 0xd0, 0xc4, 0x0e, 0xb6, 0x03, 0xda, 0x5f, 0xc0, 0x9f, 0xe1, 0x47, 0xa2, 0xf8, 0x63,
	0x89, 0x43, 0xab, 0x4a, 0x3d, 0xd9, 0xf3, 0x66, 0xfc, 0xe6, 0xbd, 0x19, 0xc3, 0x0c, 0xb9, 0xae,
	0xf4, 0x26, 0x6f, 0xa5, 0xd0, 0x82, 0x4c, 0xcc, 0x91, 0xcd, 0x94, 0x96, 0x15, 0xff, 0x6a, 0x41,
	0xfa, 0x3b, 0x82, 0xc7, 0x17, 0xa6, 0xea, 0x8a, 0xfd, 0xc4, 0x4b, 0x21, 0x9b, 0x02, 0x7f, 0x74,
	0xa8, 0x34, 0x39, 0x86, 0x44, 0x23, 0x67, 0x5c, 0xa7, 0xd1, 0x3c, 0x5a, 0x4c, 0x0b, 0x17, 0xf5,
	0xf8, 0x5a, 0xc8, 0x66, 0x79, 0x9e, 0xee, 0x5a, 0xdc, 0x46, 0xe4, 0x08, 0x26, 0x0d, 0xfb, 0x2e,
	0x64, 0x1a, 0xcf, 0xa3, 0x45, 0x5c, 0xd8, 0xc0, 0xa0, 0x15, 0x17, 0x32, 0xdd, 0x73, 0x68, 0x1f,
	0x10, 0x02, 0x7b, 0xfd, 0xab, 0x74, 0x62, 0x18, 0xcc, 0x9d, 0x7e, 0x82, 0xe3, 0xb1, 0x10, 0xd5,
	0x0a, 0xae, 0x90, 0xbc, 0x84, 0x99, 0xda, 0x28, 0x8d, 0xcd, 0x0a, 0xf5, 0x37, 0x71, 0x9d, 0x46,
	0xf3, 0x78, 0xf1, 0xe8, 0xec, 0xd0, 0x3a, 0xc8, 0xaf, 0x06, 0xa9, 0x22, 0x28, 0xa4, 0x0c, 0x0e,
	0x2d, 0xe5, 0x07, 0xb6, 0x11, 0x9d, 0xbe, 0xcf, 0x59, 0x06, 0xfb, 0x6d, 0xcd, 0xb4, 0x51, 0x66,
	0xbd, 0x6d, 0xe3, 0x81, 0xeb, 0x78, 0xe8, 0x9a, 0x7e, 0x86, 0xa3, 0xb0, 0x85, 0xd3, 0x3c, 0xe4,
	0x8a, 0x46, 0x5c, 0x14, 0x66, 0xb5, 0xa9, 0x3e, 0xc7, 0x75, 0xc5, 0xd1, 0xf5, 0x0a, 0x30, 0x7a,
	0xe1, 0xa5, 0x5f, 0x56, 0x58, 0x5f, 0xab, 0x07, 0x2e, 0x85, 0xbe, 0xf1, 0xf2, 0x3c, 0x8d, 0x93,
	0xb7, 0x80, 0x44, 0xa2, 0xea, 0x6a, 0xed, 0x86, 0x79, 0xe0, 0x86, 0xb9, 0x62, 0x6d, 0x61, 0xf0,
	0xc2, 0xe5, 0xe9, 0x6b, 0x98, 0x6e, 0x41, 0x72, 0x00, 0xf1, 0x0d, 0x6e, 0x5c, 0xef, 0xfe, 0x4a,
	0x4e, 0x60, 0x5a, 0x71, 0x8d, 0x72, 0xcd, 0x4a, 0x6f, 0xe4, 0x1f, 0x40, 0xdf, 0xfb, 0x9d, 0x2e,
	0x79, 0xdb, 0xe9, 0xa2, 0xab, 0xf1, 0xa1, 0x46, 0x5e, 0xc1, 0x93, 0xff, 0x98, 0x9c, 0x97, 0x53,
	0x80, 0xca, 0x83, 0xca, 0xd1, 0x0d, 0x90, 0xb3, 0x3f, 0xbb, 0x90, 0xd8, 0xb7, 0x64, 0x09, 0xfb,
	0xfe, 0x77, 0x91, 0x13, 0x67, 0xf9, 0xd6, 0xdf, 0x9f, 0x3d, 0xbb, 0x23, 0x6b, 0x7b, 0xd2, 0x1d,
	0xf2, 0x0e, 0x12, 0xbb, 0x72, 0x92, 0x05, 0xa5, 0xc1, 0x57, 0xcb, 0x9e, 0xde, 0x9a, 0x1b, 0x92,
	0xd8, 0xc5, 0x8c, 0x48, 0x82, 0xa5, 0x8f, 0x48, 0xc2, 0x4d, 0xd2, 0x1d, 0xb2, 0x02, 0xd8, 0x0e,
	0x45, 0x91, 0x50, 0xf8, 0x78, 0xee, 0xd9, 0xe9, 0x5d, 0x69, 0x4f, 0xf7, 0xf6, 0x39, 0xd0, 0xb2,
	0xcc, 0x2b, 0x21, 0xca, 0xbc, 0x14, 0x4d, 0x23, 0x78, 0x2e, 0xdb, 0x32, 0x57, 0x5c, 0xfc, 0x5a,
	0xd7, 0xec, 0x06, 0xed, 0xfb, 0x8f, 0xd1, 0x97, 0xc4, 0x5c, 0x5e, 0xfc, 0x0d, 0x00, 0x00, 0xff,
	0xff, 0xba, 0x17, 0x45, 0x3a, 0x65, 0x04, 0x00, 0x00,
}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface

// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6

// EntityClient is the client API for Entity service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type EntityClient interface {
	// SaveForm()方法 保存表单定义
	SaveForm(ctx context.Context, in *EntitySaveFormRequest, opts ...grpc.CallOption) (*EntitySaveFormResponse, error)
}

type entityClient struct {
	cc grpc.ClientConnInterface
}

func NewEntityClient(cc grpc.ClientConnInterface) EntityClient {
	return &entityClient{cc}
}

func (c *entityClient) SaveForm(ctx context.Context, in *EntitySaveFormRequest, opts ...grpc.CallOption) (*EntitySaveFormResponse, error) {
	out := new(EntitySaveFormResponse)
	err := c.cc.Invoke(ctx, "/proto.Entity/SaveForm", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// EntityServer is the server API for Entity service.
type EntityServer interface {
	// SaveForm()方法 保存表单定义
	SaveForm(context.Context, *EntitySaveFormRequest) (*EntitySaveFormResponse, error)
}

// UnimplementedEntityServer can be embedded to have forward compatible implementations.
type UnimplementedEntityServer struct {
}

func (*UnimplementedEntityServer) SaveForm(ctx context.Context, req *EntitySaveFormRequest) (*EntitySaveFormResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method SaveForm not implemented")
}

func RegisterEntityServer(s *grpc.Server, srv EntityServer) {
	s.RegisterService(&_Entity_serviceDesc, srv)
}

func _Entity_SaveForm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(EntitySaveFormRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(EntityServer).SaveForm(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/proto.Entity/SaveForm",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(EntityServer).SaveForm(ctx, req.(*EntitySaveFormRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _Entity_serviceDesc = grpc.ServiceDesc{
	ServiceName: "proto.Entity",
	HandlerType: (*EntityServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "SaveForm",
			Handler:    _Entity_SaveForm_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "entity.proto",
}

7.3 客户端实现接口

server

package server

import (
	"context"
	"fmt"
	"imcs/common/errors"
	"imcs/common/log"
	imcsResponse "imcs/common/response"
	"imcs/entity/service"
	"imcs/proto"
)

type EntityGRPCServer struct {
}

// @author []
// 这一系列仍然需要定义entity.proto文件之后编码
func (egs *EntityGRPCServer) SaveForm(ctx context.Context, request *proto.EntitySaveFormRequest) (response *proto.EntitySaveFormResponse, err error) {
	tenantId := request.Tenant
	formId := request.Form
	minor := request.Minor
	major := request.Major
	form := request.Form
	if tenantId == "" || formId == "" || minor <= 0 || major <= 0 {
		return nil, errors.New(20003, "参数不合法")
	}
	method, err := service.SaveForm(tenantId, formId, int(major), int(minor), form)
	response = new(proto.EntitySaveFormResponse)
	response.SystemMethod = method
	return response, err
}

service+DB

package service

import (
	"encoding/json"
	"github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"
	"imcs/common/errors"
	"imcs/common/orm"
	baseModel "imcs/common/orm/model"
	"imcs/entity/model"
	fieldStringClient "imcs/field_string/client"
	formModel "imcs/form/client/model"
	"imcs/proto"
)

// 保存表单定义
func SaveForm(tenantId, formId string, major, minor int, form string) (method []*proto.SystemMethod, err error) {

	var formMap map[string]interface{}
	err = json.Unmarshal([]byte(form), &formMap)
	if err != nil {
		logrus.Errorf("entity.service.SaveForm form转换json出错 :%v\n", err.Error())
		return nil, errors.New(errors.JsonUnMarshallError, "form转换json出错")
	}
	db, err := orm.WriteDB()
	if err != nil {
		logrus.Errorf("entity.service.SaveForm 数据库连接异常 :%v\n", err.Error())
		return nil, errors.New(errors.DataBaseConnError, "数据库连接异常")
	}
	// 其他实现逻辑+数据库操作
}
7.4 服务端调用接口

client 方法

package client

import (
	"context"
	"google.golang.org/grpc"
	"imcs/common/config"
	"imcs/common/errors"
	"imcs/common/log"
	"imcs/common/rpc"
	"imcs/form/client/model"
	"imcs/proto"
)

type EntityGRPCClient struct {
	Address string
}

func NewEntityGRPCClient() *EntityGRPCClient {
	return &EntityGRPCClient{Address: config.GetString("grpc.client.entity.address")}
}

//保存表单定义
//tenant	企业ID
//formID	表单ID
//major		表单主版本号
//minor		表单子版本号
//form		表单JSON字符串
//method	字段ID、版本及system方法切片的切片
func (e *EntityGRPCClient) SaveForm(tenant, formID string, major, minor int, form string) (
	method []model.SystemMethod, err error) {
	//entity应处理应用类型中包含$system{}的方法
	// #1.参数校验
	if tenant == "" || formID == "" || major == 0 || minor == 0 || form == "" {
		return []model.SystemMethod{}, errors.New(errors.BadRequest, "参数校验失败")
	}

	// #2.获取连接
	conn, err := rpc.GetConn(e.Address)
	if err != nil {
		log.Info(err.Error())
		return []model.SystemMethod{}, errors.New(errors.GrpcConnError, "EntityGRPCClient中SaveForm()获取连接失败")
	}
	defer rpc.Close(e.Address, conn)

	// #3...
	response, err := func(conn *grpc.ClientConn) (interface{}, error) {
		// 调用proto buffers 生成的客户端对象;提供了在.proto文件中我们之前定义的方法,也是我们需要的方法。
		client := proto.NewEntityClient(conn)
		// 调用grpc生成的接口及其实现方法
		// 给proto生成的请求对象的属性设置值
		response, err := client.SaveForm(context.Background(), &proto.EntitySaveFormRequest{
			Tenant: tenant,
			FormID: formID,
			Major:  int64(major),
			Minor:  int64(minor),
			Form:   form,
		})
		return response, err
	}(conn)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	// 从生成的相应对象中获取属性值作为返回值
	var addrArr = response.(*proto.EntitySaveFormResponse).SystemMethod
	protoToModel(addrArr, &method)
	return method, nil
}

// description: 批量转化不同包的对象
func protoToModel(addrArr []*proto.SystemMethod, method *[]model.SystemMethod) {
	for _, addr := range addrArr {
		var out = model.SystemMethod{
			FieldID: addr.FieldID,
			Version: int(addr.Version),
		}
		for _, single := range addr.Methods {
			var inner = model.Method{
				Scope:  single.Scope,
				Name:   single.Name,
				Params: single.Params,
			}
			out.Methods = append(out.Methods, inner)
		}
		*method = append(*method, out)
	}
}

测试方法

package client

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"testing"
)

func TestSaveForm(t *testing.T) {
	var tenant = "1212"
	var formId = "1235555"
	var major = 1
	var minor = 2
	var form = `{"id":"xX","major":1,"minor":1,"tenant":"","name":"表单名称","type":"entity","style":"tree","description":"",
"detail":false,"form_status":["禁用","未付款"],"platform":["app","pc","pad"],"create":["app","pc","pad"],"delete":["app","pc","pad"],
"update":["app","pc","pad"],"suspend":["app","pc","pad"],"print":["app","pc","pad"],"load":["app","pc","pad"],"export":["app","pc","pad"],
"trace":["app","pc","pad"],"custom_header":["app","pc","pad"],"version":true,"input_rule":[{"triggers":[{"field_id":"4672208957060812800","operate":"<=",
"value":"s"},{"field_id":"4672208369858252800","operate":"<=","value":"电脑1"},{"status":"状态","operate":"=","value":"启用"}],"empty_field":["创建记录"],
"readonly":["日期"],"value":[{"field_id":"XXXXX","value":"1235a"}]}],"group_unique":[{"group":["4672208369858252800"],"rule":[{"field_id":"4672208369858252800","value":"12"}]}],
"key":["fieldId",""],"sort":[{"field_id":"asc"}],
"header":{"pc":[{"name":"编号","order":true,"id":"4672208789288652800"},{"name":"产品信息","order":false,"children":[{"name":"名称",
"order":false,"id":"4672208369858252800"},{"name":"数量","order":true,"id":"4672208957060812800"}]}],"app":[{"name":"编号","order":true,
"id":"4672208789288652800"},{"name":"产品信息","order":false,"children":[{"name":"名称","order":false,"id":"4672208369858252800"},
{"name":"数量","order":true,"id":"4672208957060812800"}]}]},"sub_form":["form_id","form_id2"],"brief":"$field_id{get}XXX($field_id{get})",
"fields":[{"field_id":"","type":"string","name":"","format":"","default":"0","multiline":false,"max_length":100,"min_length":10,
"formula":"$field_id1{get}XXX$field_id2{get}$system{now,year}"},{"field_id":"","type":"number","name":"","format":"","default":0,
"decimals":2,"thousand":true,"start_with":"¥","end_with":"元","max":"$1001{get}+min+1000","min":"","formula":"$field_id1{get}+$field_id2{get}"},
{"field_id":"","name":"","type":"select","style":"radio/checkbox/select","data":["部门1","部门2"],"default":0},{"field_id":"","name":"",
"type":"increment","force":true,"length":4,"step":2,"start":1,"group":"$field_id{get}$system{time,\"yyyy\"}","format":"BillXXX$self{group}$self{get}$field_id2{get}"},
{"field_id":"","name":"","type":"date","style":"base/year/month/year-month/month-day","max":"","min":"","format":"MM"},{"field_id":"","name":"",
"type":"quote","init":true,"data":false,"quote_source":[{"form_id":"用户表ID","name":"","condition":[{"field_id":"电话","operate":"=",
"value":"$system{user,员工编号}"}],"source_field_id":""}],"source_field_type":"string"},{"field_id":"","name":"","type":"list","max":10,"min":1,"form_id":""}],
"layout":{"PC":[{"name":"基本信息","type":"common","rows":[[{"id":"4672208789288652800","rate":24,"name":"订单编号"}],[{"id":"4672208369858252800","rate":12,"name":"产品"},
{"id":"4672208957060812800","rate":12,"name":"数量"}]]}]}
,"scene":[]}`
	var test = NewEntityGRPCClient()
	data, err := test.SaveForm(tenant, formId, major, minor, form)
	if err != nil {
		logrus.Info(err)
	} else {
		fmt.Printf("=========>%v", data)
	}
}

你可能感兴趣的:(golang,开发语言,后端)