Browse Source

Merge branch 'develop' into feature/权限管理

# Conflicts:
#	opms_parent/app/model/cust/cust_customer.go
ZZH-wl 3 years ago
parent
commit
bd0375fd38
62 changed files with 4462 additions and 745 deletions
  1. 1 0
      opms_admin/app/model/internal/sys_user.go
  2. 7 0
      opms_admin/app/service/context.go
  3. 0 1
      opms_admin/config/config.toml
  4. 0 44
      opms_libary/db/db.go
  5. 0 115
      opms_libary/db/db_test.go
  6. 0 95
      opms_libary/db/dict.go
  7. 2 0
      opms_libary/go.mod
  8. 4 2
      opms_libary/go.sum
  9. 2 2
      opms_libary/micro_srv/micro_srv.go
  10. 1 1
      opms_libary/multipart/file.go
  11. 14 7
      opms_libary/plugin/dingtalk/base/base.go
  12. 48 4
      opms_libary/plugin/dingtalk/bridge/msg_handler.go
  13. 95 0
      opms_libary/plugin/dingtalk/calendar/calendar.go
  14. 170 0
      opms_libary/plugin/dingtalk/calendar/entity.go
  15. 22 0
      opms_libary/plugin/dingtalk/client.go
  16. 57 3
      opms_libary/plugin/dingtalk/client_test.go
  17. 37 0
      opms_libary/plugin/dingtalk/contact/contact.go
  18. 48 0
      opms_libary/plugin/dingtalk/contact/entity.go
  19. 1 0
      opms_libary/plugin/dingtalk/context/config.go
  20. 3 0
      opms_libary/plugin/dingtalk/context/context.go
  21. 173 0
      opms_libary/plugin/dingtalk/crypto/crypto.go
  22. 14 0
      opms_libary/plugin/dingtalk/crypto/crypto_test.go
  23. 52 0
      opms_libary/plugin/dingtalk/message/message.go
  24. 48 0
      opms_libary/plugin/dingtalk/storage/entity.go
  25. 42 0
      opms_libary/plugin/dingtalk/storage/storage.go
  26. 171 196
      opms_libary/plugin/dingtalk/workflow/entity.go
  27. 45 27
      opms_libary/plugin/dingtalk/workflow/workflow.go
  28. 9 8
      opms_libary/request/request.go
  29. 491 0
      opms_libary/utils/tea.go
  30. 12 0
      opms_libary/utils/utils.go
  31. 96 73
      opms_parent/app/dao/cust/internal/cust_customer.go
  32. 57 48
      opms_parent/app/dao/plat/internal/plat_schedule.go
  33. 434 0
      opms_parent/app/dao/plat/internal/plat_schedule_attendee.go
  34. 36 0
      opms_parent/app/dao/plat/plat_schedule_attendee.go
  35. 440 0
      opms_parent/app/dao/workflow/internal/plat_workflow.go
  36. 36 0
      opms_parent/app/dao/workflow/plat_workflow.go
  37. 2 1
      opms_parent/app/handler/cust/customer.go
  38. 146 0
      opms_parent/app/handler/dingtalk/ding_event.go
  39. 21 1
      opms_parent/app/handler/plat/schedule.go
  40. 27 0
      opms_parent/app/handler/workflow/plat_workflow.go
  41. 45 31
      opms_parent/app/model/cust/cust_customer.go
  42. 32 25
      opms_parent/app/model/cust/internal/cust_customer.go
  43. 11 0
      opms_parent/app/model/dingtalk/ding_event.go
  44. 32 0
      opms_parent/app/model/plat/dingtalk/dingtalk_sckedule.go
  45. 19 16
      opms_parent/app/model/plat/internal/plat_schedule.go
  46. 27 0
      opms_parent/app/model/plat/internal/plat_schedule_attendee.go
  47. 25 12
      opms_parent/app/model/plat/plat_schedule.go
  48. 14 0
      opms_parent/app/model/plat/plat_schedule_attendee.go
  49. 29 0
      opms_parent/app/model/workflow/internal/plat_workflow.go
  50. 30 0
      opms_parent/app/model/workflow/plat_workflow.go
  51. 12 1
      opms_parent/app/service/base.go
  52. 7 0
      opms_parent/app/service/context.go
  53. 170 16
      opms_parent/app/service/cust/cust_customer.go
  54. 5 2
      opms_parent/app/service/plat/plat_followup.go
  55. 266 12
      opms_parent/app/service/plat/plat_schedule.go
  56. 127 0
      opms_parent/app/service/workflow/work_flow.go
  57. 1 0
      opms_parent/go.mod
  58. 666 0
      opms_parent/go.sum
  59. 9 2
      opms_parent/main.go
  60. 35 0
      opms_parent/schema/customer.sql
  61. 28 0
      opms_parent/schema/function.sql
  62. 8 0
      opms_parent/schema/tmp.sql

+ 1 - 0
opms_admin/app/model/internal/sys_user.go

@@ -14,6 +14,7 @@ type SysUser struct {
 	DeptId      int         `orm:"dept_id"      json:"deptId"`      // 部门ID
 	UserName    string      `orm:"user_name"    json:"userName"`    // 用户账号
 	NickName    string      `orm:"nick_name"    json:"nickName"`    // 用户昵称
+	DingtalkId  string      `orm:"dingtalk_id"  json:"dingtalkId"`  // 关联钉钉用户唯一码(钉钉对外开放的Id)
 	UserType    string      `orm:"user_type"    json:"userType"`    // 用户类型(00系统用户)
 	Email       string      `orm:"email"        json:"email"`       // 用户邮箱
 	Phone       string      `orm:"phone"        json:"phone"`       // 手机号码

+ 7 - 0
opms_admin/app/service/context.go

@@ -158,6 +158,13 @@ func (c *contextService) GetCxtUserName() string {
 	return c.CxtUser.NickName
 }
 
+func (c *contextService) GetCxtUserDingtalkId() string {
+	if c.CxtUser == nil {
+		return "-1"
+	}
+	return c.CxtUser.DingtalkId
+}
+
 func (c *contextService) GetCxtUserDeptId() int {
 	if c.CxtUser == nil {
 		return -1

+ 0 - 1
opms_admin/config/config.toml

@@ -2,7 +2,6 @@
 [setting]
     logpath = "/tmp/log/admin"
     bind-addr = "127.0.0.1:8888"
-    bind-mutipart-addr = "127.0.0.1:9999"
     need-advertise-addr = false
     srv-name = "dashoo.opms.admin-0.0.1"
     env = "dev"

+ 0 - 44
opms_libary/db/db.go

@@ -1,44 +0,0 @@
-package db
-
-import (
-	"github.com/gogf/gf/database/gdb"
-	"github.com/gogf/gf/frame/g"
-
-	myerrors "dashoo.cn/opms_libary/myerrors"
-)
-
-type ServiceBase struct {
-	Tenant    string
-	SafeModel *gdb.Model
-	DB        gdb.DB
-	TableName string
-}
-
-func (s *ServiceBase) Init(tenant string, tableName string) error {
-	//db, err := gdb.Instance(tenant)
-	s.Tenant = tenant
-	db := g.DB(tenant)
-	if db == nil {
-		return myerrors.NewSysError(nil, "数据库初始化异常")
-	}
-	//if err != nil {
-	//	return gerror.New("获取数据库连接失败")
-	//}
-	s.DB = db
-	s.TableName = tableName
-	//s.Model = db.Table(tableName)
-	s.SafeModel = s.DB.Table(tableName).Safe()
-	return nil
-}
-
-// Exists 判断数据是否在数据表里已存在
-func (s *ServiceBase) Exists(tableName string, where ...interface{}) (bool, error) {
-	exist, err := s.DB.Table(tableName).Fields("1 as id").FindOne(where...)
-	if err != nil {
-		return false, myerrors.NewDbError(err)
-	}
-	if len(exist) > 0 {
-		return true, nil
-	}
-	return false, nil
-}

+ 0 - 115
opms_libary/db/db_test.go

@@ -1,115 +0,0 @@
-package db
-
-import (
-	"reflect"
-	"testing"
-
-	"github.com/gogf/gf/database/gdb"
-)
-
-func NewDB() (gdb.DB, error) {
-	configNode := gdb.ConfigNode{
-		Host:             "192.168.0.252",
-		Port:             "3306",
-		User:             "root",
-		Pass:             "Dashoo#190801@ali",
-		Name:             "lims_dev",
-		Type:             "mysql",
-		Role:             "master",
-		Charset:          "utf8",
-		Weight:           1,
-		MaxIdleConnCount: 10,
-		MaxOpenConnCount: 10,
-		MaxConnLifeTime:  600,
-	}
-	gdb.AddConfigNode("test", configNode)
-	return gdb.New()
-}
-
-func NewModel(table string) ServiceBase {
-	db, _ := NewDB()
-	sv := ServiceBase{
-		Tenant:    "test",
-		SafeModel: db.Model(table).Safe(),
-		DB:        db,
-		TableName: table,
-	}
-	return sv
-}
-
-func TestServiceBase_Exists(t *testing.T) {
-	type args struct {
-		tableName string
-		where     []interface{}
-	}
-	wheres := make([]interface{}, 0)
-	wheres = append(wheres, "name = ?", "dongdong")
-	tests := []struct {
-		name    string
-		fields  ServiceBase
-		args    args
-		want    bool
-		wantErr bool
-	}{
-		{"db exists", NewModel("test"), args{
-			tableName: "",
-			where:     wheres,
-		}, false, false},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			s := &ServiceBase{
-				Tenant:    tt.fields.Tenant,
-				SafeModel: tt.fields.SafeModel,
-				DB:        tt.fields.DB,
-				TableName: tt.fields.TableName,
-			}
-			got, err := s.Exists(tt.args.tableName, tt.args.where...)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("Exists() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if got != tt.want {
-				t.Errorf("Exists() got = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-//
-func TestServiceBase_GetDictListForWhere(t *testing.T) {
-	type fields struct {
-		Tenant    string
-		SafeModel *gdb.Model
-		DB        gdb.DB
-		TableName string
-	}
-	type args struct {
-		req DictReq
-	}
-	tests := []struct {
-		name    string
-		fields  ServiceBase
-		args    args
-		want    []Dict
-		wantErr bool
-	}{}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			s := &ServiceBase{
-				Tenant:    tt.fields.Tenant,
-				SafeModel: tt.fields.SafeModel,
-				DB:        tt.fields.DB,
-				TableName: tt.fields.TableName,
-			}
-			got, err := s.GetDictListForWhere(tt.args.req)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("GetDictListForWhere() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("GetDictListForWhere() got = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}

+ 0 - 95
opms_libary/db/dict.go

@@ -1,95 +0,0 @@
-package db
-
-import (
-	"dashoo.cn/opms_libary/myerrors"
-	"errors"
-)
-
-// 新增页面请求参数
-type DictEntity struct {
-	Id   string `json:"id"`
-	Name string `json:"name"`
-}
-
-// DictReq 字典返回项的请求参数
-// v:"name     @required|length:6,30#请输入用户名称|用户名称长度非法"`
-type DictReq struct {
-	TableName    string        `json:"table_name" v:"table_name@required#表名称为必填项"`      // 表名称
-	DisplayField []string      `json:"display_field" v:"display_field@required#显示字段必填"` // 显示字段
-	SortFields   string        `json:"sort_fields"`                                     // 排序字段
-	Where        interface{}   `json:"where"`                                           // 条件
-	Args         []interface{} `json:"args"`                                            // 参数
-}
-
-// Dict 字典项
-type Dict struct {
-	Label string `json:"label"`
-	Val   string `json:"val"`
-}
-
-// GetDictList 获取字典项列表(有效的) FileldParams可选,第一个为IdField,第二个为SortField,第三个为EnabledField
-func (s *ServiceBase) GetDictList(tableName string, NameField string, FileldParams ...string) ([]DictEntity, error) {
-	idField := "Id"
-	sortField := "SortCode"
-	enabledField := "Enabled"
-
-	if len(FileldParams) > 0 {
-		for index, param := range FileldParams {
-			if param == "" {
-				continue
-			}
-
-			if index == 0 {
-				idField = param
-			} else if index == 1 {
-				sortField = param
-			} else if index == 2 {
-				enabledField = param
-			}
-
-		}
-	}
-
-	var entity []DictEntity
-	err := s.DB.Model(tableName).
-		Fields(idField+" as Id", NameField+" as Name").
-		Where(enabledField, 1).
-		Order(sortField + " ASC").
-		Structs(&entity)
-	if err != nil {
-		return nil, myerrors.NewDbError(err)
-	}
-	return entity, nil
-}
-
-// GetDictListForWhere 根据条件返回字典项 (tableName:表名称, displayField 显示字段, where, args 条件参数)
-// 如果 displayField 字段只有一个,默认加 Id是key
-func (s *ServiceBase) GetDictListForWhere(req DictReq) ([]Dict, error) {
-	if len(req.DisplayField) == 0 {
-		return nil, errors.New("显示字段参数不能为空")
-	}
-	defaultField := "Id"
-	model := s.DB.Model(req.TableName)
-	if len(req.DisplayField) == 1 {
-		model = model.Fields(defaultField+" AS Label", req.DisplayField[0]+" AS Val")
-	} else {
-		model = model.Fields(req.DisplayField[0]+" AS Label", req.DisplayField[1]+" AS Val")
-	}
-	// 排序
-	if req.SortFields != "" {
-		model = model.OrderAsc(req.SortFields)
-	}
-	dictList := make([]Dict, 0)
-	if req.Where != nil {
-		if len(req.Args) > 0 {
-			model = model.Where(req.Where, req.Args)
-		} else {
-			model = model.Where(req.Where)
-		}
-	}
-	err := model.Scan(&dictList)
-	if err != nil {
-		return nil, err
-	}
-	return dictList, nil
-}

+ 2 - 0
opms_libary/go.mod

@@ -6,9 +6,11 @@ require (
 	dashoo.cn/common_definition v0.0.0
 	github.com/gogf/gf v1.16.9
 	github.com/mojocn/base64Captcha v1.3.5
+	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
 	github.com/rpcxio/rpcx-consul v0.0.0-20220730062257-1ff0472e730f
 	github.com/smallnest/rpcx v1.8.0
+	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
 )
 
 replace dashoo.cn/common_definition => code.dashoo.cn/dashoo/micro_common_definition.git v0.0.0-20220507071646-3d858a040d5e

+ 4 - 2
opms_libary/go.sum

@@ -247,7 +247,6 @@ github.com/klauspost/reedsolomon v1.10.0/go.mod h1:qHMIzMkuZUWqIh8mS/GruPdo3u0qw
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -312,6 +311,8 @@ github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
 github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -662,8 +663,9 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

+ 2 - 2
opms_libary/micro_srv/micro_srv.go

@@ -5,7 +5,7 @@ import (
 	"dashoo.cn/common_definition/comm_def"
 	"dashoo.cn/opms_libary/dynamic"
 	"dashoo.cn/opms_libary/gtoken"
-	"dashoo.cn/opms_libary/mutipart"
+	"dashoo.cn/opms_libary/multipart"
 	"dashoo.cn/opms_libary/myerrors"
 	"dashoo.cn/opms_libary/request"
 	"encoding/json"
@@ -161,7 +161,7 @@ func streamHandler(conn net.Conn, args *share.StreamServiceArgs) {
 			message := new(dynamic.Message)
 			message.ClassName = className
 			message.MethodName = methodName
-			message.Payload = &mutipart.MultipartFile{FileName: fileName, FileSize: gconv.Int64(fileSize), File: tmpFile}
+			message.Payload = &multipart.MultipartFile{FileName: fileName, FileSize: gconv.Int64(fileSize), File: tmpFile}
 			rsp, err := dynamic.Invoker.HandleInvoker(ctx, message)
 			if err != nil {
 				resp := make(map[string]interface{})

+ 1 - 1
opms_libary/mutipart/file.go → opms_libary/multipart/file.go

@@ -1,4 +1,4 @@
-package mutipart
+package multipart
 
 import "os"
 

+ 14 - 7
opms_libary/plugin/dingtalk/base/base.go

@@ -28,10 +28,14 @@ Do:
 		return
 	}
 	var target = ""
-	if strings.Contains(url, "?") {
-		target = fmt.Sprintf("%s%s&x-acs-dingtalk-access-token=%s", BaseApiUrl, url, accessToken)
+	if strings.Contains(url, "http") {
+		target = fmt.Sprintf("%s?access_token=%s", url, accessToken)
 	} else {
-		target = fmt.Sprintf("%s%s?x-acs-dingtalk-access-token=%s", BaseApiUrl, url, accessToken)
+		if strings.Contains(url, "?") {
+			target = fmt.Sprintf("%s%s&x-acs-dingtalk-access-token=%s", BaseApiUrl, url, accessToken)
+		} else {
+			target = fmt.Sprintf("%s%s?x-acs-dingtalk-access-token=%s", BaseApiUrl, url, accessToken)
+		}
 	}
 	//双重for-range
 	for k, v := range param {
@@ -69,12 +73,15 @@ Do:
 	}
 
 	var target = ""
-	if strings.Contains(url, "?") {
-		target = fmt.Sprintf("%s%s", BaseApiUrl, url)
+	if strings.Contains(url, "http") {
+		target = fmt.Sprintf("%s?access_token=%s", url, accessToken)
 	} else {
-		target = fmt.Sprintf("%s%s", BaseApiUrl, url)
+		if strings.Contains(url, "?") {
+			target = fmt.Sprintf("%s%s", BaseApiUrl, url)
+		} else {
+			target = fmt.Sprintf("%s%s", BaseApiUrl, url)
+		}
 	}
-	fmt.Println(accessToken)
 	resp, err = PostJSON(target, obj, accessToken)
 
 	if err != nil {

+ 48 - 4
opms_libary/plugin/dingtalk/bridge/msg_handler.go

@@ -1,18 +1,62 @@
 package bridge
 
 import (
-	"dashoo.cn/common_definition/comm_def"
 	"dashoo.cn/opms_libary/plugin/dingtalk/context"
-	"github.com/smallnest/rpcx/protocol"
+	"dashoo.cn/opms_libary/plugin/dingtalk/crypto"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
+	"fmt"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/frame/g"
+	"github.com/smallnest/rpcx/share"
 )
 
 //DingTalkHandler struct
 type DingTalkHandler struct {
 	*context.Context
+	handleMessageFunc func(*message.MixMessage) string
+}
+
+//NewDingTalkHandler init
+func NewDingTalkHandler(context *context.Context) *DingTalkHandler {
+	srv := new(DingTalkHandler)
+	fmt.Println("NewMsgHandler:", srv)
+	srv.Context = context
+	return srv
 }
 
 //Handle 处理微信的请求消息
-func (h *DingTalkHandler) Handle(ctx context.Context, msg protocol.Message, commonMsg comm_def.CommonMsg) error {
+func (h *DingTalkHandler) Handle() (reply message.Reply, err error) {
+	ctx := h.SubsMessage.Ctx
+	meta := ctx.Value(share.ReqMetaDataKey).(map[string]string)
+	encrypt := h.Context.SubsMessage.Encrypt
+	var ding = crypto.NewDingTalkCrypto(h.Token, h.AESKey, h.AppKey)
+	decMsg, err := ding.GetDecryptMsg(meta["msg_signature"], meta["timestamp"], meta["nonce"], encrypt)
+	if err != nil {
+		g.Log().Error(err)
+		return reply, err
+	}
+
+	data := new(message.MixMessage)
+	if j, err := gjson.DecodeToJson(decMsg); err != nil {
+		g.Log().Error(err)
+		return reply, err
+	} else {
+		if err := j.Scan(data); err != nil {
+			g.Log().Error(err)
+			return reply, err
+		}
+	}
+
+	msgData := h.handleMessageFunc(data)
+	enMsg, _ := ding.GetEncryptMsg(msgData)
+	reply.MsgSignature = enMsg["msg_signature"]
+	reply.TimeStamp = enMsg["timeStamp"]
+	reply.Nonce = enMsg["nonce"]
+	reply.Encrypt = enMsg["encrypt"]
+	return reply, nil
+}
 
-	return nil
+//SetHandleMessageFunc 设置用户自定义的回调方法
+func (h *DingTalkHandler) SetHandleMessageFunc(handler func(*message.MixMessage) string) {
+	h.handleMessageFunc = handler
 }

+ 95 - 0
opms_libary/plugin/dingtalk/calendar/calendar.go

@@ -0,0 +1,95 @@
+package calendar
+
+import (
+	"dashoo.cn/opms_libary/plugin/dingtalk/base"
+	"dashoo.cn/opms_libary/plugin/dingtalk/context"
+	"dashoo.cn/opms_libary/utils"
+	"fmt"
+)
+
+// 初始化创建日程的接口
+func initCreateEventUrl(userId string) string {
+	return fmt.Sprintf("/v1.0/calendar/users/%v/calendars/primary/events", userId)
+}
+
+// 初始化查询单个日程接口
+func initGetEventByIdUrl(userId, calendarId string) string {
+	return fmt.Sprintf("/v1.0/calendar/users/%v/calendars/primary/events/%v?maxAttendees=100", userId, calendarId)
+}
+
+// 初始化查询用户忙闲的接口
+func initUserBusyStatusUrl(userId string) string {
+	return fmt.Sprintf("/v1.0/calendar/users/%v/querySchedule", userId)
+}
+
+//Calendar 日程
+type Calendar struct {
+	base.Base
+}
+
+//NewCalendar init
+func NewCalendar(context *context.Context) *Calendar {
+	c := new(Calendar)
+	c.Context = context
+	return c
+}
+
+//CreateSchedule 创建日程
+func (c *Calendar) CreateSchedule(params *DingAddScheduleParams) (returnData *CreateEventResponse, err error) {
+	var attendees []*EventAttendees
+	for index := range params.UserIds {
+		att := EventAttendees{
+			Id: &params.UserIds[index],
+		}
+		attendees = append(attendees, &att)
+	}
+	start := &EventStart{
+		DateTime: &params.BeginTime,
+		TimeZone: &params.TimeZone,
+	}
+	end := &EventEnd{
+		DateTime: &params.EndTime,
+		TimeZone: &params.TimeZone,
+	}
+	req := &EventRequest{
+		Summary:     &params.Summary,
+		Description: &params.Description,
+		Start:       start,
+		End:         end,
+		Attendees:   attendees,
+	}
+
+	resp, _ := c.HTTPPostJSONWithAccessToken(initCreateEventUrl(params.UserId), req)
+
+	var data CreateEventResponse
+	err = utils.Bytes2object(resp, &data)
+	return &data, err
+}
+
+//GetScheduleByEventId 根据日程Id获取日程数据
+func (c *Calendar) GetScheduleByEventId(params *DingGetScheduleParams) (returnData *GetEventResponse, err error) {
+	resp, _ := c.HTTPGetWithAccessToken(initGetEventByIdUrl(params.UserId, params.EventId), nil)
+
+	var data GetEventResponse
+	err = utils.Bytes2object(resp, &data)
+	return &data, err
+}
+
+//GetUserBusyStatus 获取用户闲忙信息
+func (c *Calendar) GetUserBusyStatus(params *DingIsUserBusyParams) (returnData *GetScheduleResponse, err error) {
+	var userIds []*string
+	for index := range params.UserIds {
+		userIds = append(userIds, &params.UserIds[index])
+	}
+	req := &GetScheduleRequest{
+		UserIds:   userIds,
+		StartTime: &params.BeginTime,
+		EndTime:   &params.EndTime,
+	}
+
+	resp, _ := c.HTTPPostJSONWithAccessToken(initUserBusyStatusUrl(params.UserId), req)
+
+	var data GetScheduleResponse
+	err = utils.Bytes2object(resp, &data)
+	return &data, err
+}

+ 170 - 0
opms_libary/plugin/dingtalk/calendar/entity.go

@@ -0,0 +1,170 @@
+package calendar
+
+// 基本结构体
+
+type DingParams struct {
+	UserId string
+}
+
+type DingAddScheduleParams struct {
+	DingParams
+	TimeZone    string
+	Summary     string
+	Description string
+	BeginTime   string
+	EndTime     string
+	UserIds     []string
+}
+
+type DingGetScheduleParams struct {
+	DingParams
+	EventId string
+}
+
+type DingIsUserBusyParams struct {
+	DingParams
+	UserIds   []string
+	BeginTime string
+	EndTime   string
+}
+
+type EventStart struct {
+	// 日程开始日期,如果是全天日程必须有值,非全天日程必须留空,格式:yyyy-MM-dd
+	Date *string `json:"date,omitempty" xml:"date,omitempty"`
+	// 日程开始时间,非全天日程必须有值,全天日程必须留空,格式为ISO-8601的date-time格式
+	DateTime *string `json:"dateTime,omitempty" xml:"dateTime,omitempty"`
+	// 日程开始时间所属时区,非全天日程必须有值,全天日程必须留空,tz database name格式,参考:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+	TimeZone *string `json:"timeZone,omitempty" xml:"timeZone,omitempty"`
+}
+
+type EventEnd struct {
+	// 日程结束日期,如果是全天日程必须有值,非全天日程必须留空,格式:yyyy-MM-dd
+	Date *string `json:"date,omitempty" xml:"date,omitempty"`
+	// 日程结束时间,非全天日程必须有值,全天日程必须留空,格式为ISO-8601的date-time格式
+	DateTime *string `json:"dateTime,omitempty" xml:"dateTime,omitempty"`
+	// 日程结束时间所属时区,非全天日程必须有值,全天日程必须留空,tz database name格式,参考:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+	TimeZone *string `json:"timeZone,omitempty" xml:"timeZone,omitempty"`
+}
+
+type EventAttendees struct {
+	Id *string `json:"id,omitempty" xml:"id,omitempty"`
+	// 用户名
+	DisplayName *string `json:"displayName,omitempty" xml:"displayName,omitempty"`
+	// 是否是当前登陆用户
+	Self *bool `json:"self,omitempty" xml:"self,omitempty"`
+}
+
+type EventOrganizer struct {
+	// 用户名
+	DisplayName *string `json:"displayName,omitempty" xml:"displayName,omitempty"`
+	Id          *string `json:"id,omitempty" xml:"id,omitempty"`
+	// 回复状态
+	ResponseStatus *string `json:"responseStatus,omitempty" xml:"responseStatus,omitempty"`
+	// 是否是当前登陆用户
+	Self *bool `json:"self,omitempty" xml:"self,omitempty"`
+}
+
+type ScheduleInformation struct {
+	// 异常描述
+	Error         *string                             `json:"error,omitempty" xml:"error,omitempty"`
+	ScheduleItems []*ScheduleInformationScheduleItems `json:"scheduleItems,omitempty" xml:"scheduleItems,omitempty" type:"Repeated"`
+	// 用户userId
+	UserId *string `json:"userId,omitempty" xml:"userId,omitempty"`
+}
+
+type ScheduleInformationScheduleItems struct {
+	// 结束时间,表示一个日期,或者一个带时区的时间戳
+	End *ScheduleInformationScheduleItemsEnd `json:"end,omitempty" xml:"end,omitempty" type:"Struct"`
+	// 开始时间,表示一个日期,或者一个带时区的时间戳
+	Start *ScheduleInformationScheduleItemsStart `json:"start,omitempty" xml:"start,omitempty" type:"Struct"`
+	// 状态: - BUSY:繁忙, - TENTATIVE:暂定繁忙
+	Status *string `json:"status,omitempty" xml:"status,omitempty"`
+}
+
+type ScheduleInformationScheduleItemsStart struct {
+	// 开始日期
+	Date *string `json:"date,omitempty" xml:"date,omitempty"`
+	// 开始时间戳,按照ISO 8601格式
+	DateTime *string `json:"dateTime,omitempty" xml:"dateTime,omitempty"`
+	// 所属时区
+	TimeZone *string `json:"timeZone,omitempty" xml:"timeZone,omitempty"`
+}
+
+type ScheduleInformationScheduleItemsEnd struct {
+	// 结束日期
+	Date *string `json:"date,omitempty" xml:"date,omitempty"`
+	// 结束时间戳,按照ISO 8601格式
+	DateTime *string `json:"dateTime,omitempty" xml:"dateTime,omitempty"`
+	// 时间戳所属时区
+	TimeZone *string `json:"timeZone,omitempty" xml:"timeZone,omitempty"`
+}
+
+// 请求结构体
+
+type EventRequest struct {
+	Attendees []*EventAttendees `json:"attendees,omitempty" xml:"attendees,omitempty" type:"Repeated"`
+	// 日程描述
+	Description *string `json:"description,omitempty" xml:"description,omitempty"`
+	// 日程结束时间
+	End *EventEnd `json:"end,omitempty" xml:"end,omitempty" type:"Struct"`
+	// 扩展信息
+	Extra map[string]*string `json:"extra,omitempty" xml:"extra,omitempty"`
+	// 是否为全天日程
+	IsAllDay *bool `json:"isAllDay,omitempty" xml:"isAllDay,omitempty"`
+	// 日程开始时间
+	Start *EventStart `json:"start,omitempty" xml:"start,omitempty" type:"Struct"`
+	// 日程标题
+	Summary *string `json:"summary,omitempty" xml:"summary,omitempty"`
+}
+
+type GetScheduleRequest struct {
+	// 查询结束时间
+	EndTime *string `json:"endTime,omitempty" xml:"endTime,omitempty"`
+	// 查询开始时间
+	StartTime *string `json:"startTime,omitempty" xml:"startTime,omitempty"`
+	// 待查询的用户列表
+	UserIds []*string `json:"userIds,omitempty" xml:"userIds,omitempty" type:"Repeated"`
+}
+
+// 响应结构体
+
+type CreateEventResponse struct {
+	Attendees []*EventAttendees `json:"attendees,omitempty" xml:"attendees,omitempty" type:"Repeated"`
+	// 创建时间
+	CreateTime  *string   `json:"createTime,omitempty" xml:"createTime,omitempty"`
+	Description *string   `json:"description,omitempty" xml:"description,omitempty"`
+	End         *EventEnd `json:"end,omitempty" xml:"end,omitempty" type:"Struct"`
+	Id          *string   `json:"id,omitempty" xml:"id,omitempty"`
+	// 日程开始时间
+	Start   *EventStart `json:"start,omitempty" xml:"start,omitempty" type:"Struct"`
+	Summary *string     `json:"summary,omitempty" xml:"summary,omitempty"`
+	// 更新时间
+	UpdateTime *string `json:"updateTime,omitempty" xml:"updateTime,omitempty"`
+}
+
+type GetEventResponse struct {
+	Attendees []*EventAttendees `json:"attendees,omitempty" xml:"attendees,omitempty" type:"Repeated"`
+	// 创建时间
+	CreateTime *string `json:"createTime,omitempty" xml:"createTime,omitempty"`
+	// 日程描述
+	Description *string `json:"description,omitempty" xml:"description,omitempty"`
+	// 日程结束时间
+	End       *EventEnd       `json:"end,omitempty" xml:"end,omitempty" type:"Struct"`
+	Id        *string         `json:"id,omitempty" xml:"id,omitempty"`
+	Organizer *EventOrganizer `json:"organizer,omitempty" xml:"organizer,omitempty" type:"Struct"`
+	// 重复日程的主日程id,非重复日程为空
+	SeriesMasterId *string `json:"seriesMasterId,omitempty" xml:"seriesMasterId,omitempty"`
+	// 日程开始时间
+	Start *EventStart `json:"start,omitempty" xml:"start,omitempty" type:"Struct"`
+	// 日程状态
+	Status *string `json:"status,omitempty" xml:"status,omitempty"`
+	// 日程标题
+	Summary *string `json:"summary,omitempty" xml:"summary,omitempty"`
+	// 更新时间
+	UpdateTime *string `json:"updateTime,omitempty" xml:"updateTime,omitempty"`
+}
+
+type GetScheduleResponse struct {
+	// 闲忙信息
+	ScheduleInformation []*ScheduleInformation `json:"scheduleInformation,omitempty" xml:"scheduleInformation,omitempty" type:"Repeated"`
+}

+ 22 - 0
opms_libary/plugin/dingtalk/client.go

@@ -1,7 +1,11 @@
 package dingtalk
 
 import (
+	"dashoo.cn/opms_libary/plugin/dingtalk/bridge"
+	"dashoo.cn/opms_libary/plugin/dingtalk/calendar"
+	"dashoo.cn/opms_libary/plugin/dingtalk/contact"
 	"dashoo.cn/opms_libary/plugin/dingtalk/context"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 	"dashoo.cn/opms_libary/plugin/dingtalk/workflow"
 	"github.com/gogf/gf/os/gcache"
 	"sync"
@@ -25,6 +29,8 @@ func NewClient() *ClientImpl {
 		//微信公众平台,需要填写的信息
 		AppKey:    "dinguytykawticadfoht",                                             //g.Config().GetString("dingtalk.app-key"),     //"your app id",
 		AppSecret: "zPlj4ZpITsUbeq2C0GrwJ78-e8knH_kIeyvznaNQacqtrSb9zbeZcOajgBKdolky", //g.Config().GetString("dingtalk.app-secret"), //"your app secret",
+		AESKey:    "oUjmeWea8Ow1jsdK4UHoDthy6EMQKq3RGbM2rEeTgnm",
+		Token:     "WaasHsYk8V3wqwN5xRGsCmiiRDB",
 	}
 	return newClient(config)
 }
@@ -52,7 +58,23 @@ func (c *ClientImpl) GetAccessToken() (string, error) {
 	return c.Context.GetAccessToken()
 }
 
+//GetContact 通讯录
+func (c *ClientImpl) GetContact() *contact.Contact {
+	return contact.NewContact(c.Context)
+}
+
 //GetWorkflow OA审批
 func (c *ClientImpl) GetWorkflow() *workflow.Workflow {
 	return workflow.NewWorkflow(c.Context)
 }
+
+//GetCalendar 日程
+func (c *ClientImpl) GetCalendar() *calendar.Calendar {
+	return calendar.NewCalendar(c.Context)
+}
+
+// GetDingTalkHandler 消息管理
+func (c *ClientImpl) GetDingTalkHandler(msg *message.SubsMessage) *bridge.DingTalkHandler {
+	c.Context.SubsMessage = msg
+	return bridge.NewDingTalkHandler(c.Context)
+}

+ 57 - 3
opms_libary/plugin/dingtalk/client_test.go

@@ -1,12 +1,66 @@
 package dingtalk
 
 import (
+	"fmt"
 	"testing"
 )
 
-func TestSendMsg(t *testing.T) {
+func TestQuerySchemaByProcessCode(t *testing.T) {
 	client := NewClient()
 	w := client.GetWorkflow()
-	//w.GetFormSchema("PROC-7A5F6215-A8CF-4DD1-AB2C-5B1AB84C4E19")
-	w.CreateProcessInstance("47073111989114", "PROC-7A5F6215-A8CF-4DD1-AB2C-5B1AB84C4E19", 435711466)
+	s, _ := w.QuerySchemaByProcessCode("PROC-7A5F6215-A8CF-4DD1-AB2C-5B1AB84C4E19")
+
+	fmt.Println(s)
+}
+
+func TestStartProcessInstance(t *testing.T) {
+	//client := NewClient()
+	//w := client.GetWorkflow()
+	////formComponentValues0Details0Details0 := &workflow.StartProcessInstanceRequestFormComponentValuesDetailsDetails{
+	////	Id:            tea.String("PhoneField_IZI2LP8QF6O0"),
+	////	BizAlias:      tea.String("Phone"),
+	////	Name:          tea.String("PhoneField"),
+	////	Value:         tea.String("123xxxxxxxx"),
+	////	ExtValue:      tea.String("总个数:1"),
+	////	ComponentType: tea.String("PhoneField"),
+	////}
+	////formComponentValues0Details0 := &workflow.StartProcessInstanceRequestFormComponentValuesDetails{
+	////	Id:       tea.String("PhoneField_IZI2LP8QF6O0"),
+	////	BizAlias: tea.String("Phone"),
+	////	Name:     tea.String("PhoneField"),
+	////	Value:    tea.String("123xxxxxxxx"),
+	////	ExtValue: tea.String("总个数:1"),
+	////	Details:  []*workflow.StartProcessInstanceRequestFormComponentValuesDetailsDetails{formComponentValues0Details0Details0},
+	////}
+	////表单信息
+	//TextField_1RC8GZWYGO4G0 := &workflow.StartProcessInstanceRequestFormComponentValues{
+	//	Id:    tea.String("TextField_1RC8GZWYGO4G0"),
+	//	Name:  tea.String("单行输入框"),
+	//	Value: tea.String("123xxxxxxxx"),
+	//}
+	//
+	//DDAttachment_17PC5KQBVEM80 := &workflow.StartProcessInstanceRequestFormComponentValues{
+	//	Id:    tea.String("DDAttachment_17PC5KQBVEM80"),
+	//	Name:  tea.String("附件"),
+	//	Value: tea.String("123xxxxxxxx"),
+	//}
+	//
+	//startProcessInstanceRequest := &workflow.StartProcessInstanceRequest{
+	//	OriginatorUserId:    tea.String("47073111989114"),
+	//	ProcessCode:         tea.String("PROC-7A5F6215-A8CF-4DD1-AB2C-5B1AB84C4E19"),
+	//	DeptId:              tea.Int64(435711466),
+	//	FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{TextField_1RC8GZWYGO4G0, DDAttachment_17PC5KQBVEM80},
+	//}
+	////w.StartProcessInstance("47073111989114", "PROC-7A5F6215-A8CF-4DD1-AB2C-5B1AB84C4E19", 435711466) 8xljy04PZiS9iPxp5PhDnUzQiEiE
+	//resp, _ := w.StartProcessInstance(startProcessInstanceRequest)
+	//fmt.Println(resp)
+}
+
+func TestQueryUserInfoByPhone(t *testing.T) {
+	client := NewClient()
+	c := client.GetContact()
+	resp1 := c.QueryUserIdByPhone("18653295560")
+	resp2 := c.QueryUserInfoByUserId(resp1.Result.Userid)
+
+	fmt.Println(resp2)
 }

+ 37 - 0
opms_libary/plugin/dingtalk/contact/contact.go

@@ -0,0 +1,37 @@
+package contact
+
+import (
+	"dashoo.cn/opms_libary/plugin/dingtalk/base"
+	"dashoo.cn/opms_libary/plugin/dingtalk/context"
+	"encoding/json"
+	"github.com/gogf/gf/frame/g"
+)
+
+const (
+	QueryUserIdByPhoneUrl    = "https://oapi.dingtalk.com/topapi/v2/user/getbymobile"
+	QueryUserInfoByUserIdUrl = "https://oapi.dingtalk.com/topapi/v2/user/get"
+)
+
+//Contact 通讯录
+type Contact struct {
+	base.Base
+}
+
+//NewContact init
+func NewContact(context *context.Context) *Contact {
+	material := new(Contact)
+	material.Context = context
+	return material
+}
+
+func (c *Contact) QueryUserIdByPhone(phone string) (response QueryUserIdResponse) {
+	body, _ := c.HTTPPostJSONWithAccessToken(QueryUserIdByPhoneUrl, g.Map{"mobile": phone})
+	json.Unmarshal(body, &response)
+	return response
+}
+
+func (c *Contact) QueryUserInfoByUserId(userid string) (response QueryUserInfoResponse) {
+	body, _ := c.HTTPPostJSONWithAccessToken(QueryUserInfoByUserIdUrl, g.Map{"userid": userid})
+	json.Unmarshal(body, &response)
+	return response
+}

+ 48 - 0
opms_libary/plugin/dingtalk/contact/entity.go

@@ -0,0 +1,48 @@
+package contact
+
+type QueryUserIdResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+	Result  struct {
+		Userid string `json:"userid"`
+	} `json:"result"`
+	RequestId string `json:"request_id"`
+}
+
+type QueryUserInfoResponse struct {
+	Errcode int    `json:"errcode"`
+	Errmsg  string `json:"errmsg"`
+	Result  struct {
+		Boss     bool   `json:"boss"`
+		Unionid  string `json:"unionid"`
+		RoleList []struct {
+			GroupName string `json:"group_name"`
+			Name      string `json:"name"`
+			Id        int64  `json:"id"`
+		} `json:"role_list"`
+		ExclusiveAccount bool   `json:"exclusive_account"`
+		Active           bool   `json:"active"`
+		Admin            bool   `json:"admin"`
+		Avatar           string `json:"avatar"`
+		HideMobile       bool   `json:"hide_mobile"`
+		HiredDate        int64  `json:"hired_date"`
+		Title            string `json:"title"`
+		Userid           string `json:"userid"`
+		Senior           bool   `json:"senior"`
+		DeptOrderList    []struct {
+			DeptId int   `json:"dept_id"`
+			Order  int64 `json:"order"`
+		} `json:"dept_order_list"`
+		RealAuthed  bool   `json:"real_authed"`
+		Name        string `json:"name"`
+		DeptIdList  []int  `json:"dept_id_list"`
+		UnionEmpExt struct {
+		} `json:"union_emp_ext"`
+		JobNumber    string `json:"job_number"`
+		LeaderInDept []struct {
+			Leader bool `json:"leader"`
+			DeptId int  `json:"dept_id"`
+		} `json:"leader_in_dept"`
+	} `json:"result"`
+	RequestId string `json:"request_id"`
+}

+ 1 - 0
opms_libary/plugin/dingtalk/context/config.go

@@ -6,6 +6,7 @@ import "github.com/gogf/gf/os/gcache"
 type Config struct {
 	AppKey    string
 	AppSecret string
+	AESKey    string
 	Token     string
 	Cache     *gcache.Cache
 }

+ 3 - 0
opms_libary/plugin/dingtalk/context/context.go

@@ -1,6 +1,7 @@
 package context
 
 import (
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 	"encoding/json"
 	"fmt"
 	"github.com/gogf/gf/frame/g"
@@ -26,6 +27,8 @@ type Context struct {
 
 	//accessTokenLock 读写锁 同一个AppKey一个
 	accessTokenLock *sync.RWMutex
+
+	SubsMessage *message.SubsMessage
 }
 
 //SetAccessTokenLock 设置读写锁(一个appID一个读写锁)

+ 173 - 0
opms_libary/plugin/dingtalk/crypto/crypto.go

@@ -0,0 +1,173 @@
+package crypto
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"crypto/sha1"
+	"encoding/base64"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	r "math/rand"
+	"sort"
+	"strings"
+	"time"
+)
+
+type DingTalkCrypto struct {
+	Token          string
+	EncodingAESKey string
+	SuiteKey       string
+	BKey           []byte
+	Block          cipher.Block
+}
+
+func NewDingTalkCrypto(token, encodingAESKey, suiteKey string) *DingTalkCrypto {
+	fmt.Println(len(encodingAESKey))
+	if len(encodingAESKey) != int(43) {
+		panic("不合法的EncodingAESKey")
+	}
+	bkey, err := base64.StdEncoding.DecodeString(encodingAESKey + "=")
+	if err != nil {
+		panic(err.Error())
+	}
+	block, err := aes.NewCipher(bkey)
+	if err != nil {
+		panic(err.Error())
+	}
+	c := &DingTalkCrypto{
+		Token:          token,
+		EncodingAESKey: encodingAESKey,
+		SuiteKey:       suiteKey,
+		BKey:           bkey,
+		Block:          block,
+	}
+	return c
+}
+
+func (c *DingTalkCrypto) GetDecryptMsg(signature, timestamp, nonce, secretMsg string) (string, error) {
+	if !c.VerificationSignature(c.Token, timestamp, nonce, secretMsg, signature) {
+		return "", errors.New("ERROR: 签名不匹配")
+	}
+	decode, err := base64.StdEncoding.DecodeString(secretMsg)
+	if err != nil {
+		return "", err
+	}
+	if len(decode) < aes.BlockSize {
+		return "", errors.New("ERROR: 密文太短")
+	}
+	blockMode := cipher.NewCBCDecrypter(c.Block, c.BKey[:c.Block.BlockSize()])
+	plantText := make([]byte, len(decode))
+	blockMode.CryptBlocks(plantText, decode)
+	plantText = pkCS7UnPadding(plantText)
+	size := binary.BigEndian.Uint32(plantText[16:20])
+	plantText = plantText[20:]
+	corpID := plantText[size:]
+	if string(corpID) != c.SuiteKey {
+		return "", errors.New("ERROR: CorpID匹配不正确")
+	}
+	return string(plantText[:size]), nil
+}
+
+func (c *DingTalkCrypto) GetEncryptMsg(msg string) (map[string]string, error) {
+	var timestamp = time.Now().UnixMilli()
+	var nonce = randomString(12)
+	str, sign, err := c.GetEncryptMsgDetail(msg, fmt.Sprint(timestamp), nonce)
+
+	return map[string]string{"nonce": nonce, "timeStamp": fmt.Sprint(timestamp), "encrypt": str, "msg_signature": sign}, err
+}
+func (c *DingTalkCrypto) GetEncryptMsgDetail(msg, timestamp, nonce string) (string, string, error) {
+	size := make([]byte, 4)
+	binary.BigEndian.PutUint32(size, uint32(len(msg)))
+	msg = randomString(16) + string(size) + msg + c.SuiteKey
+	plantText := pkCS7Padding([]byte(msg), c.Block.BlockSize())
+	if len(plantText)%aes.BlockSize != 0 {
+		return "", "", errors.New("ERROR: 消息体size不为16的倍数")
+	}
+	blockMode := cipher.NewCBCEncrypter(c.Block, c.BKey[:c.Block.BlockSize()])
+	chipherText := make([]byte, len(plantText))
+	blockMode.CryptBlocks(chipherText, plantText)
+	outMsg := base64.StdEncoding.EncodeToString(chipherText)
+	signature := c.CreateSignature(c.Token, timestamp, nonce, string(outMsg))
+	return string(outMsg), signature, nil
+}
+
+func sha1Sign(s string) string {
+	// The pattern for generating a hash is `sha1.New()`,
+	// `sha1.Write(bytes)`, then `sha1.Sum([]byte{})`.
+	// Here we start with a new hash.
+	h := sha1.New()
+
+	// `Write` expects bytes. If you have a string `s`,
+	// use `[]byte(s)` to coerce it to bytes.
+	h.Write([]byte(s))
+
+	// This gets the finalized hash result as a byte
+	// slice. The argument to `Sum` can be used to append
+	// to an existing byte slice: it usually isn't needed.
+	bs := h.Sum(nil)
+
+	// SHA1 values are often printed in hex, for example
+	// in git commits. Use the `%x` format verb to convert
+	// a hash results to a hex string.
+	return fmt.Sprintf("%x", bs)
+}
+
+// CreateSignature 数据签名
+func (c *DingTalkCrypto) CreateSignature(token, timestamp, nonce, msg string) string {
+	params := make([]string, 0)
+	params = append(params, token)
+	params = append(params, timestamp)
+	params = append(params, nonce)
+	params = append(params, msg)
+	sort.Strings(params)
+	return sha1Sign(strings.Join(params, ""))
+}
+
+// VerificationSignature 验证数据签名
+func (c *DingTalkCrypto) VerificationSignature(token, timestamp, nonce, msg, sigture string) bool {
+	return c.CreateSignature(token, timestamp, nonce, msg) == sigture
+}
+
+// 解密补位
+func pkCS7UnPadding(plantText []byte) []byte {
+	length := len(plantText)
+	unpadding := int(plantText[length-1])
+	return plantText[:(length - unpadding)]
+}
+
+// 加密补位
+func pkCS7Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+// 随机字符串
+func randomString(n int, alphabets ...byte) string {
+	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+	var bytes = make([]byte, n)
+	var randby bool
+	if num, err := rand.Read(bytes); num != n || err != nil {
+		r.Seed(time.Now().UnixNano())
+		randby = true
+	}
+	for i, b := range bytes {
+		if len(alphabets) == 0 {
+			if randby {
+				bytes[i] = alphanum[r.Intn(len(alphanum))]
+			} else {
+				bytes[i] = alphanum[b%byte(len(alphanum))]
+			}
+		} else {
+			if randby {
+				bytes[i] = alphabets[r.Intn(len(alphabets))]
+			} else {
+				bytes[i] = alphabets[b%byte(len(alphabets))]
+			}
+		}
+	}
+	return string(bytes)
+}

+ 14 - 0
opms_libary/plugin/dingtalk/crypto/crypto_test.go

@@ -0,0 +1,14 @@
+package crypto
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestCrypto(t *testing.T) {
+	var ding = NewDingTalkCrypto("123456", "4g5j64qlyl3zvetqxz5jiocdr586fn2zvjpa8zls3ij", "suite4xxxxxxxxxxxxxxx")
+	msg, _ := ding.GetEncryptMsg("success")
+	fmt.Println(msg)
+	success, _ := ding.GetDecryptMsg("5a65ceeef9aab2d149439f82dc191dd6c5cbe2c0", "1445827045067", "nEXhMP4r", "1a3NBxmCFwkCJvfoQ7WhJHB+iX3qHPsc9JbaDznE1i03peOk1LaOQoRz3+nlyGNhwmwJ3vDMG+OzrHMeiZI7gTRWVdUBmfxjZ8Ej23JVYa9VrYeJ5as7XM/ZpulX8NEQis44w53h1qAgnC3PRzM7Zc/D6Ibr0rgUathB6zRHP8PYrfgnNOS9PhSBdHlegK+AGGanfwjXuQ9+0pZcy0w9lQ==")
+	fmt.Println(success)
+}

+ 52 - 0
opms_libary/plugin/dingtalk/message/message.go

@@ -0,0 +1,52 @@
+package message
+
+import "context"
+
+// EventType 事件类型
+type EventType string
+
+const (
+	//EventCheckUrl 验证URL
+	EventCheckUrl EventType = "check_url"
+	//EventCalendarChange 日程变更
+	EventCalendarChange = "calendar_event_change"
+	//BpmsInstanceChange 审批实例变更
+	BpmsInstanceChange = "bpms_instance_change"
+	//BpmsInstanceChange 审批任务变更
+	BpmsTaskChange = "bpms_task_change"
+)
+
+// SubsMessage 订阅消息
+type SubsMessage struct {
+	Ctx     context.Context
+	Encrypt string `json:"encrypt"`
+}
+
+//MixMessage 存放所有钉钉发送过来的消息和事件
+type MixMessage struct {
+	EventType EventType `json:"EventType"`
+	// 审批相关
+	ProcessInstanceId string `json:"processInstanceId"`
+	CorpId            string `json:"corpId"`
+	CreateTime        string `json:"createTime"`
+	FinishTime        string `json:"finishTime"`
+	Title             string `json:"title"`
+	ProcessType       string `json:"type"`
+	StaffId           string `json:"staffId"`
+	Url               string `json:"url"`
+	Result            string `json:"result"`
+	ProcessCode       string `json:"processCode"`
+	Remark            string `json:"remark"`
+	// 日程变更
+	ChangeType        string   `json:"changeType"`
+	CalendarEventId   string   `json:"CalendarEventId"`
+	UnionIdList       []string `json:"processCode"`
+}
+
+//Reply 消息回复
+type Reply struct {
+	MsgSignature string `json:"msg_signature"`
+	TimeStamp    string `json:"timeStamp"`
+	Nonce        string `json:"nonce"`
+	Encrypt      string `json:"encrypt"`
+}

+ 48 - 0
opms_libary/plugin/dingtalk/storage/entity.go

@@ -0,0 +1,48 @@
+package storage
+
+import "time"
+
+type QueryFileUploadInfoResponse struct {
+	UploadKey           string `json:"uploadKey"`
+	StorageDriver       string `json:"storageDriver"`
+	Protocol            string `json:"protocol"`
+	HeaderSignatureInfo struct {
+		ResourceUrls []string `json:"resourceUrls"`
+		Headers      struct {
+			Key string `json:"key"`
+		} `json:"headers"`
+		ExpirationSeconds    int      `json:"expirationSeconds"`
+		Region               string   `json:"region"`
+		InternalResourceUrls []string `json:"internalResourceUrls"`
+	} `json:"headerSignatureInfo"`
+}
+
+type CommitFileResponse struct {
+	Dentry struct {
+		Id           string    `json:"id"`
+		SpaceId      string    `json:"spaceId"`
+		ParentId     string    `json:"parentId"`
+		Type         string    `json:"type"`
+		Name         string    `json:"name"`
+		Size         int       `json:"size"`
+		Path         string    `json:"path"`
+		Version      int       `json:"version"`
+		Status       string    `json:"status"`
+		Extension    string    `json:"extension"`
+		CreatorId    string    `json:"creatorId"`
+		ModifierId   string    `json:"modifierId"`
+		CreateTime   time.Time `json:"createTime"`
+		ModifiedTime time.Time `json:"modifiedTime"`
+		Properties   struct {
+			ReadOnly bool `json:"readOnly"`
+		} `json:"properties"`
+		AppProperties []struct {
+			Name       string `json:"name"`
+			Value      string `json:"value"`
+			Visibility string `json:"visibility"`
+		} `json:"appProperties"`
+		Uuid          string `json:"uuid"`
+		PartitionType string `json:"partitionType"`
+		StorageDriver string `json:"storageDriver"`
+	} `json:"dentry"`
+}

+ 42 - 0
opms_libary/plugin/dingtalk/storage/storage.go

@@ -0,0 +1,42 @@
+package storage
+
+import (
+	"dashoo.cn/opms_libary/multipart"
+	"dashoo.cn/opms_libary/plugin/dingtalk/base"
+	"dashoo.cn/opms_libary/plugin/dingtalk/context"
+)
+
+const (
+	FormSchemasUrl     = "/v1.0/workflow/forms/schemas/processCodes"
+	ProcessInstanceUrl = "/v1.0/workflow/processInstances"
+)
+
+//Storage OA审批
+type Storage struct {
+	base.Base
+}
+
+//NewStorage init
+func NewStorage(context *context.Context) *Storage {
+	material := new(Storage)
+	material.Context = context
+	return material
+}
+
+//QueryFileUploadInfo 获取文件上传信息
+func (w *Storage) QueryFileUploadInfo(spaceId, unionId string) (response QueryFileUploadInfoResponse, err error) {
+
+	return response, err
+}
+
+//UploadFile 使用OSS的header加签方式上传文件息
+func (w *Storage) UploadFile(url string, headers map[string]string, file *multipart.MultipartFile) (code int64, err error) {
+
+	return 0, err
+}
+
+//CommitFile 提交文件
+func (w *Storage) CommitFile(uploadKey, filename, parentId string) (response CommitFileResponse, err error) {
+
+	return response, err
+}

+ 171 - 196
opms_libary/plugin/dingtalk/workflow/entity.go

@@ -1,201 +1,111 @@
 package workflow
 
-// Schema 审批流表单信息
-type Schema struct {
-	// 表单类型。
-	AppType *int32 `json:"appType,omitempty" xml:"appType,omitempty"`
-	// 表单应用 uuid 或者 corpId。
-	AppUuid *string `json:"appUuid,omitempty" xml:"appUuid,omitempty"`
-	// 代表表单业务含义的类型。
-	BizType *string `json:"bizType,omitempty" xml:"bizType,omitempty"`
-	// 创建人 userId。
-	CreatorUserId *string `json:"creatorUserId,omitempty" xml:"creatorUserId,omitempty"`
-	// 业务自定义设置数据。
-	CustomSetting *string `json:"customSetting,omitempty" xml:"customSetting,omitempty"`
-	// 引擎类型,表单:0,页面:1
-	EngineType *int32 `json:"engineType,omitempty" xml:"engineType,omitempty"`
-	// 表单的唯一码。
-	FormCode *string `json:"formCode,omitempty" xml:"formCode,omitempty"`
-	// 表单 uuid。
-	FormUuid *string `json:"formUuid,omitempty" xml:"formUuid,omitempty"`
-	// 创建时间的时间戳。
-	GmtCreate *string `json:"gmtCreate,omitempty" xml:"gmtCreate,omitempty"`
-	// 修改时间的时间戳。
-	GmtModified *string `json:"gmtModified,omitempty" xml:"gmtModified,omitempty"`
-	// 图标。
-	Icon *string `json:"icon,omitempty" xml:"icon,omitempty"`
-	// 排序 id。
-	ListOrder *int32 `json:"listOrder,omitempty" xml:"listOrder,omitempty"`
-	// 说明文案。
-	Memo *string `json:"memo,omitempty" xml:"memo,omitempty"`
-	// 表单名称。
-	Name *string `json:"name,omitempty" xml:"name,omitempty"`
-	// 数据归属者的 id 类型。企业(orgId), 群(cid), 人(uid)。
-	OwnerIdType *string `json:"ownerIdType,omitempty" xml:"ownerIdType,omitempty"`
-	// 目标类型: inner, outer, customer。
-	ProcType *string `json:"procType,omitempty" xml:"procType,omitempty"`
-	// 表单 schema 详情。
-	SchemaContent *SchemaContent `json:"schemaContent,omitempty" xml:"schemaContent,omitempty" type:"Struct"`
-	// 状态, PUBLISHED(启用), INVALID(停用), SAVED(草稿)
-	Status *string `json:"status,omitempty" xml:"status,omitempty"`
-	// 可见范围类型。
-	VisibleRange *string `json:"visibleRange,omitempty" xml:"visibleRange,omitempty"`
-}
-
-type SchemaContent struct {
-	// 图标
-	Icon *string `json:"icon,omitempty" xml:"icon,omitempty"`
-	// 控件列表
-	Items []*SchemaContentItems `json:"items,omitempty" xml:"items,omitempty" type:"Repeated"`
-	// 表单名称。
-	Title *string `json:"title,omitempty" xml:"title,omitempty"`
-}
-
-type SchemaContentItems struct {
-	// 子控件列表
-	Children []*SchemaContentItemsChildren `json:"children,omitempty" xml:"children,omitempty" type:"Repeated"`
-	// 控件类型,取值:
-	ComponentName *string `json:"componentName,omitempty" xml:"componentName,omitempty"`
-	// 控件属性。
-	Props *SchemaContentItemsProps `json:"props,omitempty" xml:"props,omitempty" type:"Struct"`
-}
-
-type SchemaContentItemsChildren struct {
-	// 控件类型
-	ComponentName *string `json:"componentName,omitempty" xml:"componentName,omitempty"`
-	// 子控件属性
-	Props *SchemaContentItemsProps `json:"props,omitempty" xml:"props,omitempty" type:"Struct"`
-}
-
-type SchemaContentItemsProps struct {
-	// 加班套件4.0新增 加班明细名称。
-	ActionName *string `json:"actionName,omitempty" xml:"actionName,omitempty"`
-	// textnote的样式,top|middle|bottom。
-	Align *string `json:"align,omitempty" xml:"align,omitempty"`
-	// ISV 微应用 appId,用于ISV身份权限识别,ISV可获得相应数据。
-	AppId *int64 `json:"appId,omitempty" xml:"appId,omitempty"`
-	// 套件是否开启异步获取分条件规则,true:开启;false:不开启。
-	AsyncCondition *bool `json:"asyncCondition,omitempty" xml:"asyncCondition,omitempty"`
-	// 请假、出差、外出、加班类型标签。
-	AttendTypeLabel *string `json:"attendTypeLabel,omitempty" xml:"attendTypeLabel,omitempty"`
-	// 表单关联控件列表。
-	BehaviorLinkage []*SchemaContentItemsPropsBehaviorLinkage `json:"behaviorLinkage,omitempty" xml:"behaviorLinkage,omitempty" type:"Repeated"`
-	// 控件业务自定义别名。
-	BizAlias *string `json:"bizAlias,omitempty" xml:"bizAlias,omitempty"`
-	// 业务套件类型。
-	BizType *string `json:"bizType,omitempty" xml:"bizType,omitempty"`
-	// 套件内子组件可见性
-	ChildFieldVisible map[string]*bool `json:"childFieldVisible,omitempty" xml:"childFieldVisible,omitempty"`
-	// 内部联系人choice,1表示多选,0表示单选。
-	Choice *int32 `json:"choice,omitempty" xml:"choice,omitempty"`
-	// common field的commonBizType。
-	CommonBizType *string `json:"commonBizType,omitempty" xml:"commonBizType,omitempty"`
-	// 是否可编辑。
-	Disabled *bool `json:"disabled,omitempty" xml:"disabled,omitempty"`
-	// 是否自动计算时长。
-	Duration *bool `json:"duration,omitempty" xml:"duration,omitempty"`
-	// 兼容字段。
-	DurationLabel *string `json:"durationLabel,omitempty" xml:"durationLabel,omitempty"`
-	// e签宝专用标识。
-	ESign *bool `json:"eSign,omitempty" xml:"eSign,omitempty"`
-	// 套件值是否打平
-	Extract *bool `json:"extract,omitempty" xml:"extract,omitempty"`
-	// 关联表单中的fields存储
-	FieldsInfo *string `json:"fieldsInfo,omitempty" xml:"fieldsInfo,omitempty"`
-	// 时间格式(DDDateField和DDDateRangeField)。
-	Format *string `json:"format,omitempty" xml:"format,omitempty"`
-	// 公式。
-	Formula *string `json:"formula,omitempty" xml:"formula,omitempty"`
-	// 加班套件4.0新增 加班明细是否隐藏。
-	Hidden *bool `json:"hidden,omitempty" xml:"hidden,omitempty"`
-	// textnote在详情页是否隐藏,true隐藏, false不隐藏
-	HiddenInApprovalDetail *bool `json:"hiddenInApprovalDetail,omitempty" xml:"hiddenInApprovalDetail,omitempty"`
-	// 加班套件4.0新增 加班明细是否隐藏标签。
-	HideLabel *bool `json:"hideLabel,omitempty" xml:"hideLabel,omitempty"`
-	// 兼容出勤套件类型。
-	HolidayOptions []map[string]*string `json:"holidayOptions,omitempty" xml:"holidayOptions,omitempty" type:"Repeated"`
-	// 控件 id。
-	Id *string `json:"id,omitempty" xml:"id,omitempty"`
-	// 控件名称。
-	Label *string `json:"label,omitempty" xml:"label,omitempty"`
-	// label是否可修改 true:不可修改。
-	LabelEditableFreeze *bool `json:"labelEditableFreeze,omitempty" xml:"labelEditableFreeze,omitempty"`
-	// 说明文案的链接地址。
-	Link *string `json:"link,omitempty" xml:"link,omitempty"`
-	// 加班套件4.0新增 加班明细描述。
-	MainTitle *string `json:"mainTitle,omitempty" xml:"mainTitle,omitempty"`
-	// 是否参与打印(1表示不打印, 0表示打印)。
-	NotPrint *string `json:"notPrint,omitempty" xml:"notPrint,omitempty"`
-	// 是否需要大写 默认是需要; 1:不需要大写, 空或者0:需要大写。
-	NotUpper *string `json:"notUpper,omitempty" xml:"notUpper,omitempty"`
-	// 选项内容列表,提供给业务方更多的选择器操作。
-	ObjOptions []*SchemaContentItemsPropsObjOptions `json:"objOptions,omitempty" xml:"objOptions,omitempty" type:"Repeated"`
-	// 单选框选项列表。
-	Options []*string `json:"options,omitempty" xml:"options,omitempty" type:"Repeated"`
-	// 是否有支付属性。
-	PayEnable *bool `json:"payEnable,omitempty" xml:"payEnable,omitempty"`
-	// 占位符。
-	Placeholder *string `json:"placeholder,omitempty" xml:"placeholder,omitempty"`
-	// 同步到考勤, 表示是否设置为员工状态。
-	Push *SchemaContentItemsPropsPush `json:"push,omitempty" xml:"push,omitempty" type:"Struct"`
-	// 推送到考勤, 子类型(DDSelectField)。
-	PushToAttendance *bool `json:"pushToAttendance,omitempty" xml:"pushToAttendance,omitempty"`
-	// 是否推送管理日历(DDDateRangeField, 1表示推送, 0表示不推送, 该属性为兼容保留)。
-	PushToCalendar *int32 `json:"pushToCalendar,omitempty" xml:"pushToCalendar,omitempty"`
-	// 是否必填。
-	Required *bool `json:"required,omitempty" xml:"required,omitempty"`
-	// 必填是否可修改 true:不可修改。
-	RequiredEditableFreeze *bool `json:"requiredEditableFreeze,omitempty" xml:"requiredEditableFreeze,omitempty"`
-	// 兼容出勤套件类型。
-	ShowAttendOptions *bool `json:"showAttendOptions,omitempty" xml:"showAttendOptions,omitempty"`
-	// 是否开启员工状态。
-	StaffStatusEnabled *bool `json:"staffStatusEnabled,omitempty" xml:"staffStatusEnabled,omitempty"`
-	// 需要计算总和的明细组件
-	StatField []*SchemaContentItemsPropsStatField `json:"statField,omitempty" xml:"statField,omitempty" type:"Repeated"`
-	// 数字组件/日期区间组件单位属性。
-	Unit *string `json:"unit,omitempty" xml:"unit,omitempty"`
-	// 是否使用考勤日历。
-	UseCalendar *bool `json:"useCalendar,omitempty" xml:"useCalendar,omitempty"`
-	// 明细打印排版方式 false:横向 true:纵向。
-	VerticalPrint *bool `json:"verticalPrint,omitempty" xml:"verticalPrint,omitempty"`
-}
-
-type SchemaContentItemsPropsBehaviorLinkage struct {
-	// 关联控件列表。
-	Targets []*SchemaContentItemsPropsBehaviorLinkageTargets `json:"targets,omitempty" xml:"targets,omitempty" type:"Repeated"`
-	// 控件值。
-	Value *string `json:"value,omitempty" xml:"value,omitempty"`
-}
-
-type SchemaContentItemsPropsBehaviorLinkageTargets struct {
-	// 行为。
-	Behavior *string `json:"behavior,omitempty" xml:"behavior,omitempty"`
-	// 字段 id。
-	FieldId *string `json:"fieldId,omitempty" xml:"fieldId,omitempty"`
-}
-
-type SchemaContentItemsPropsObjOptions struct {
-	Value *string `json:"value,omitempty" xml:"value,omitempty"`
-}
-
-type SchemaContentItemsPropsPush struct {
-	// 考勤类型(1表示请假, 2表示出差, 3表示加班, 4表示外出)
-	AttendanceRule *int32 `json:"attendanceRule,omitempty" xml:"attendanceRule,omitempty"`
-	// 开启状态(1表示开启, 0表示关闭)
-	PushSwitch *int32 `json:"pushSwitch,omitempty" xml:"pushSwitch,omitempty"`
-	// 状态显示名称
-	PushTag *string `json:"pushTag,omitempty" xml:"pushTag,omitempty"`
-}
-
-type SchemaContentItemsPropsStatField struct {
-	// id 值。
-	Id *string `json:"id,omitempty" xml:"id,omitempty"`
-	// 名称。
-	Label *string `json:"label,omitempty" xml:"label,omitempty"`
-	// 单位。
-	Unit *string `json:"unit,omitempty" xml:"unit,omitempty"`
-	// 大写。
-	Upper *bool `json:"upper,omitempty" xml:"upper,omitempty"`
+// QuerySchemaByProcessCodeResponse 审批流表单信息
+type QuerySchemaByProcessCodeResponse struct {
+	Result struct {
+		CreatorUserId string `json:"creatorUserId"`
+		AppUuid       string `json:"appUuid"`
+		FormCode      string `json:"formCode"`
+		FormUuid      string `json:"formUuid"`
+		Name          string `json:"name"`
+		Memo          string `json:"memo"`
+		OwnerIdType   string `json:"ownerIdType"`
+		SchemaContent struct {
+			Title string `json:"title"`
+			Icon  string `json:"icon"`
+			Items []struct {
+				ComponentName string `json:"componentName"`
+				Props         struct {
+					Id             string   `json:"id"`
+					Label          string   `json:"label"`
+					BizAlias       string   `json:"bizAlias"`
+					Required       bool     `json:"required"`
+					Placeholder    string   `json:"placeholder"`
+					Options        []string `json:"options"`
+					AppId          int      `json:"appId"`
+					DurationLabel  string   `json:"durationLabel"`
+					PushToCalendar int      `json:"pushToCalendar"`
+					Align          string   `json:"align"`
+					StatField      []struct {
+						Id    string `json:"id"`
+						Label string `json:"label"`
+						Upper bool   `json:"upper"`
+						Unit  string `json:"unit"`
+					} `json:"statField"`
+					HideLabel  bool `json:"hideLabel"`
+					ObjOptions []struct {
+						Value string `json:"value"`
+					} `json:"objOptions"`
+					Format              string `json:"format"`
+					PushToAttendance    bool   `json:"pushToAttendance"`
+					LabelEditableFreeze bool   `json:"labelEditableFreeze"`
+					Push                struct {
+						PushSwitch     int    `json:"pushSwitch"`
+						PushTag        string `json:"pushTag"`
+						AttendanceRule int    `json:"attendanceRule"`
+					} `json:"push"`
+					CommonBizType          string `json:"commonBizType"`
+					RequiredEditableFreeze bool   `json:"requiredEditableFreeze"`
+					Unit                   string `json:"unit"`
+					Extract                bool   `json:"extract"`
+					Link                   string `json:"link"`
+					PayEnable              bool   `json:"payEnable"`
+					Hidden                 bool   `json:"hidden"`
+					BizType                string `json:"bizType"`
+					StaffStatusEnabled     bool   `json:"staffStatusEnabled"`
+					ActionName             string `json:"actionName"`
+					AttendTypeLabel        string `json:"attendTypeLabel"`
+					ChildFieldVisible      struct {
+						Key bool `json:"key"`
+					} `json:"childFieldVisible"`
+					NotPrint       string `json:"notPrint"`
+					VerticalPrint  bool   `json:"verticalPrint"`
+					Duration       bool   `json:"duration"`
+					HolidayOptions []struct {
+						Key string `json:"key"`
+					} `json:"holidayOptions"`
+					UseCalendar            bool `json:"useCalendar"`
+					HiddenInApprovalDetail bool `json:"hiddenInApprovalDetail"`
+					Disabled               bool `json:"disabled"`
+					AsyncCondition         bool `json:"asyncCondition"`
+					BehaviorLinkage        []struct {
+						Value   string `json:"value"`
+						Targets []struct {
+							FieldId  string `json:"fieldId"`
+							Behavior string `json:"behavior"`
+						} `json:"targets"`
+					} `json:"behaviorLinkage"`
+					ShowAttendOptions bool   `json:"showAttendOptions"`
+					NotUpper          string `json:"notUpper"`
+					FieldsInfo        string `json:"fieldsInfo"`
+					ESign             bool   `json:"eSign"`
+					MainTitle         string `json:"mainTitle"`
+					Formula           string `json:"formula"`
+					Choice            int    `json:"choice"`
+				} `json:"props"`
+				Children []struct {
+					ComponentName string `json:"componentName"`
+					Props         struct {
+						Id       string `json:"id"`
+						Label    string `json:"label"`
+						BizAlias string `json:"bizAlias"`
+						Required bool   `json:"required"`
+					} `json:"props"`
+				} `json:"children"`
+			} `json:"items"`
+		} `json:"schemaContent"`
+		Icon          string `json:"icon"`
+		AppType       int    `json:"appType"`
+		BizType       string `json:"bizType"`
+		EngineType    int    `json:"engineType"`
+		Status        string `json:"status"`
+		ListOrder     int    `json:"listOrder"`
+		CustomSetting string `json:"customSetting"`
+		ProcType      string `json:"procType"`
+		VisibleRange  string `json:"visibleRange"`
+		GmtCreate     string `json:"gmtCreate"`
+		GmtModified   string `json:"gmtModified"`
+	} `json:"result"`
 }
 
 // StartProcessInstanceRequest 启动审批流
@@ -278,3 +188,68 @@ type StartProcessInstanceRequestFormComponentValuesDetailsDetails struct {
 	// 控件值
 	Value *string `json:"value,omitempty" xml:"value,omitempty"`
 }
+
+// StartProcessInstanceResponse 启动审批流
+type StartProcessInstanceResponse struct {
+	InstanceId string `json:"instanceId"`
+}
+
+//QueryProcessInstanceResponse 获取单个审批实例详情
+type QueryProcessInstanceResponse struct {
+	Result struct {
+		Title              string   `json:"title"`
+		FinishTime         string   `json:"finishTime"`
+		OriginatorUserId   string   `json:"originatorUserId"`
+		OriginatorDeptId   string   `json:"originatorDeptId"`
+		OriginatorDeptName string   `json:"originatorDeptName"`
+		Status             string   `json:"status"`
+		ApproverUserIds    []string `json:"approverUserIds"`
+		CcUserIds          []string `json:"ccUserIds"`
+		Result             string   `json:"result"`
+		BusinessId         string   `json:"businessId"`
+		OperationRecords   []struct {
+			UserId      string `json:"userId"`
+			Date        string `json:"date"`
+			Type        string `json:"type"`
+			Result      string `json:"result"`
+			Remark      string `json:"remark"`
+			Attachments []struct {
+				FileName string `json:"fileName"`
+				FileSize string `json:"fileSize"`
+				FileId   string `json:"fileId"`
+				FileType string `json:"fileType"`
+			} `json:"attachments"`
+			CcUserIds []string `json:"ccUserIds"`
+		} `json:"operationRecords"`
+		Tasks []struct {
+			TaskId            int    `json:"taskId"`
+			UserId            string `json:"userId"`
+			Status            string `json:"status"`
+			Result            string `json:"result"`
+			CreateTime        string `json:"createTime"`
+			FinishTime        string `json:"finishTime"`
+			MobileUrl         string `json:"mobileUrl"`
+			PcUrl             string `json:"pcUrl"`
+			ProcessInstanceId string `json:"processInstanceId"`
+			ActivityId        string `json:"activityId"`
+		} `json:"tasks"`
+		BizAction                  string   `json:"bizAction"`
+		AttachedProcessInstanceIds []string `json:"attachedProcessInstanceIds"`
+		MainProcessInstanceId      string   `json:"mainProcessInstanceId"`
+		FormComponentValues        []struct {
+			Id            string `json:"id"`
+			Name          string `json:"name"`
+			Value         string `json:"value"`
+			ExtValue      string `json:"extValue"`
+			ComponentType string `json:"componentType"`
+			BizAlias      string `json:"bizAlias"`
+		} `json:"formComponentValues"`
+		CreateTime string `json:"createTime"`
+	} `json:"result"`
+	Success string `json:"success"`
+}
+
+// RevokeProcessInstanceResponse 撤销审批实例
+type RevokeProcessInstanceResponse struct {
+	InstanceId string `json:"instanceId"`
+}

+ 45 - 27
opms_libary/plugin/dingtalk/workflow/workflow.go

@@ -3,13 +3,14 @@ package workflow
 import (
 	"dashoo.cn/opms_libary/plugin/dingtalk/base"
 	"dashoo.cn/opms_libary/plugin/dingtalk/context"
+	"encoding/json"
 	"github.com/gogf/gf/frame/g"
-	"github.com/gogf/gf/util/gconv"
 )
 
 const (
-	FormSchemasUrl    = "/v1.0/workflow/forms/schemas/processCodes"
-	CreateInstanceUrl = "/v1.0/workflow/processInstances"
+	QuerySchemasUrl          = "/v1.0/workflow/forms/schemas/processCodes"
+	StartProcessInstanceUrl  = "/v1.0/workflow/processInstances"
+	RevokeProcessInstanceUrl = "/v1.0/workflow/processInstances/terminate"
 )
 
 //Workflow OA审批
@@ -24,30 +25,47 @@ func NewWorkflow(context *context.Context) *Workflow {
 	return material
 }
 
-//GetFormSchema 获取表单 schema
-func (w *Workflow) GetFormSchema(processCode string) (mediaID string, err error) {
-	resp, _ := w.HTTPGetWithAccessToken(FormSchemasUrl, g.Map{"processCode": processCode})
-	return gconv.String(resp), nil
+//QuerySchemaByProcessCode 获取表单 schema
+func (w *Workflow) QuerySchemaByProcessCode(processCode string) (response QuerySchemaByProcessCodeResponse, err error) {
+	resp, _ := w.HTTPGetWithAccessToken(QuerySchemasUrl, g.Map{"processCode": processCode})
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(resp, &response)
+	return response, err
 }
 
-//CreateProcessInstance 发起审批实例
-func (w *Workflow) CreateProcessInstance(originUserId, processCode string, deptId int64) (mediaID string, err error) {
-	inst1 := new(StartProcessInstanceRequest)
-	inst1.OriginatorUserId = &originUserId
-	inst1.ProcessCode = &processCode
-	inst1.DeptId = &deptId
-
-	//formComponentValues0 := &StartProcessInstanceRequestFormComponentValues{
-	//	Name:  tea.String("单行输入框"),
-	//	Value: tea.String("22222"),
-	//	Id:    tea.String("TextField-K2AD4O5B"),
-	//}
-	//approvers0 := &StartProcessInstanceRequestApprovers{
-	//	ActionType: tea.String("NONE"),
-	//	UserIds:    []*string{tea.String("47073111989114")},
-	//}
-	//inst1.FormComponentValues = []*StartProcessInstanceRequestFormComponentValues{formComponentValues0}
-	//inst1.Approvers = []*StartProcessInstanceRequestApprovers{approvers0}
-	resp, _ := w.HTTPPostJSONWithAccessToken(CreateInstanceUrl, inst1)
-	return gconv.String(resp), nil
+//StartProcessInstance 发起审批实例
+func (w *Workflow) StartProcessInstance(request *StartProcessInstanceRequest) (response StartProcessInstanceResponse, err error) {
+	resp, _ := w.HTTPPostJSONWithAccessToken(StartProcessInstanceUrl, request)
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(resp, &response)
+	return response, err
+}
+
+//QueryProcessInstanceDetail 获取单个审批实例详情
+func (w *Workflow) QueryProcessInstanceDetail(instId string) (response QueryProcessInstanceResponse, err error) {
+	resp, err := w.HTTPGetWithAccessToken(RevokeProcessInstanceUrl, g.Map{"processInstanceId": instId})
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(resp, &response)
+	return
+}
+
+//RevokeProcessInstance 撤销审批实例
+func (w *Workflow) RevokeProcessInstance(instId, remark, userId string) (response RevokeProcessInstanceResponse, err error) {
+	resp, err := w.HTTPGetWithAccessToken(RevokeProcessInstanceUrl,
+		g.Map{"processInstanceId": instId,
+			"isSystem":        true,
+			"remark":          remark,
+			"operatingUserId": userId,
+		})
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(resp, &response)
+	return
 }

+ 9 - 8
opms_libary/request/request.go

@@ -29,14 +29,15 @@ func (p *PageReq) GetPage() (int, int) {
 
 // UserInfo 登录用户信息
 type UserInfo struct {
-	Id       int      `json:"id"`
-	Uuid     string   `json:"uuid"`
-	UserName string   `json:"userName"`
-	NickName string   `json:"nickName"`
-	DeptId   int      `json:"deptId"` // 所属部门
-	Roles    []string `json:"roles"`  // 所属角色
-	Posts    []string `json:"posts"`  // 所属岗位
-	Groups   []string `json:"groups"` // 所属用户组
+	Id         int      `json:"id"`
+	Uuid       string   `json:"uuid"`
+	UserName   string   `json:"userName"`
+	NickName   string   `json:"nickName"`
+	DingtalkId string   `json:"dingtalk_id"`
+	DeptId     int      `json:"deptId"` // 所属部门
+	Roles      []string `json:"roles"`  // 所属角色
+	Posts      []string `json:"posts"`  // 所属岗位
+	Groups     []string `json:"groups"` // 所属用户组
 }
 
 // 设置当前登录用户信息

+ 491 - 0
opms_libary/utils/tea.go

@@ -0,0 +1,491 @@
+package utils
+
+func String(a string) *string {
+	return &a
+}
+
+func StringValue(a *string) string {
+	if a == nil {
+		return ""
+	}
+	return *a
+}
+
+func Int(a int) *int {
+	return &a
+}
+
+func IntValue(a *int) int {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Int8(a int8) *int8 {
+	return &a
+}
+
+func Int8Value(a *int8) int8 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Int16(a int16) *int16 {
+	return &a
+}
+
+func Int16Value(a *int16) int16 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Int32(a int32) *int32 {
+	return &a
+}
+
+func Int32Value(a *int32) int32 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Int64(a int64) *int64 {
+	return &a
+}
+
+func Int64Value(a *int64) int64 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Bool(a bool) *bool {
+	return &a
+}
+
+func BoolValue(a *bool) bool {
+	if a == nil {
+		return false
+	}
+	return *a
+}
+
+func Uint(a uint) *uint {
+	return &a
+}
+
+func UintValue(a *uint) uint {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Uint8(a uint8) *uint8 {
+	return &a
+}
+
+func Uint8Value(a *uint8) uint8 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Uint16(a uint16) *uint16 {
+	return &a
+}
+
+func Uint16Value(a *uint16) uint16 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Uint32(a uint32) *uint32 {
+	return &a
+}
+
+func Uint32Value(a *uint32) uint32 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Uint64(a uint64) *uint64 {
+	return &a
+}
+
+func Uint64Value(a *uint64) uint64 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Float32(a float32) *float32 {
+	return &a
+}
+
+func Float32Value(a *float32) float32 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func Float64(a float64) *float64 {
+	return &a
+}
+
+func Float64Value(a *float64) float64 {
+	if a == nil {
+		return 0
+	}
+	return *a
+}
+
+func IntSlice(a []int) []*int {
+	if a == nil {
+		return nil
+	}
+	res := make([]*int, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func IntValueSlice(a []*int) []int {
+	if a == nil {
+		return nil
+	}
+	res := make([]int, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Int8Slice(a []int8) []*int8 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*int8, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Int8ValueSlice(a []*int8) []int8 {
+	if a == nil {
+		return nil
+	}
+	res := make([]int8, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Int16Slice(a []int16) []*int16 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*int16, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Int16ValueSlice(a []*int16) []int16 {
+	if a == nil {
+		return nil
+	}
+	res := make([]int16, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Int32Slice(a []int32) []*int32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*int32, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Int32ValueSlice(a []*int32) []int32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]int32, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Int64Slice(a []int64) []*int64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*int64, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Int64ValueSlice(a []*int64) []int64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]int64, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func UintSlice(a []uint) []*uint {
+	if a == nil {
+		return nil
+	}
+	res := make([]*uint, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func UintValueSlice(a []*uint) []uint {
+	if a == nil {
+		return nil
+	}
+	res := make([]uint, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Uint8Slice(a []uint8) []*uint8 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*uint8, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Uint8ValueSlice(a []*uint8) []uint8 {
+	if a == nil {
+		return nil
+	}
+	res := make([]uint8, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Uint16Slice(a []uint16) []*uint16 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*uint16, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Uint16ValueSlice(a []*uint16) []uint16 {
+	if a == nil {
+		return nil
+	}
+	res := make([]uint16, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Uint32Slice(a []uint32) []*uint32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*uint32, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Uint32ValueSlice(a []*uint32) []uint32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]uint32, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Uint64Slice(a []uint64) []*uint64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*uint64, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Uint64ValueSlice(a []*uint64) []uint64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]uint64, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Float32Slice(a []float32) []*float32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*float32, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Float32ValueSlice(a []*float32) []float32 {
+	if a == nil {
+		return nil
+	}
+	res := make([]float32, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func Float64Slice(a []float64) []*float64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]*float64, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func Float64ValueSlice(a []*float64) []float64 {
+	if a == nil {
+		return nil
+	}
+	res := make([]float64, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func StringSlice(a []string) []*string {
+	if a == nil {
+		return nil
+	}
+	res := make([]*string, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func StringSliceValue(a []*string) []string {
+	if a == nil {
+		return nil
+	}
+	res := make([]string, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}
+
+func BoolSlice(a []bool) []*bool {
+	if a == nil {
+		return nil
+	}
+	res := make([]*bool, len(a))
+	for i := 0; i < len(a); i++ {
+		res[i] = &a[i]
+	}
+	return res
+}
+
+func BoolSliceValue(a []*bool) []bool {
+	if a == nil {
+		return nil
+	}
+	res := make([]bool, len(a))
+	for i := 0; i < len(a); i++ {
+		if a[i] != nil {
+			res[i] = *a[i]
+		}
+	}
+	return res
+}

+ 12 - 0
opms_libary/utils/utils.go

@@ -8,6 +8,8 @@ import (
 	"crypto/rand"
 	"encoding/base64"
 	"encoding/hex"
+	"encoding/json"
+	"fmt"
 	"github.com/gogf/gf/crypto/gmd5"
 	"github.com/gogf/gf/encoding/gcharset"
 	"github.com/gogf/gf/encoding/gjson"
@@ -213,3 +215,13 @@ func FirstLower(s string) string {
 	}
 	return strings.ToLower(s[:1]) + s[1:]
 }
+
+func Bytes2object(data []byte, obj interface{}) error {
+	err := json.Unmarshal(data, obj)
+	if err != nil {
+		fmt.Println(err)
+		return err
+	}
+
+	return nil
+}

+ 96 - 73
opms_parent/app/dao/cust/internal/cust_customer.go

@@ -27,31 +27,38 @@ type CustCustomerDao struct {
 
 // CustCustomerColumns defines and stores column names for table cust_customer.
 type custCustomerColumns struct {
-	Id           string // 主键
-	CustCode     string // 客户编号
-	CustName     string // 客户名称
-	AbbrName     string // 助记名
-	CustIndustry string //客户行业
-	CustLevel    string // 客户级别(10 重点客户 20 普通客户 30非优客户)
-	CustDistCode int    // 所在省份ID
-	CustLocation string // 所在地区
-	CustAddress  string // 详细地址
-	CustStatus   string // 客户状态(10正常20)
-	CustSource   string // 客户来源
-	IsPublic     string // 公海客户(10是20否)
-	DeptId       string // 所属部门ID
-	DeptName     string // 所属部门
-	SalesId      string // 所属销售ID
-	SalesName    string // 所属销售
-	FollowUpDate string // 最后跟进时间
-	Remark       string // 备注
-	CreatedBy    string // 创建者
-	CreatedName  string // 创建人
-	CreatedTime  string // 创建时间
-	UpdatedBy    string // 更新者
-	UpdatedName  string // 更新人
-	UpdatedTime  string // 更新时间
-	DeletedTime  string // 删除时间
+	Id             string // 主键
+	CustCode       string // 客户编号
+	CustName       string // 客户名称
+	AbbrName       string // 助记名
+	CustDistCode   string // 客户所在省级ID
+	CustLocation   string // 所在地区
+	CustAddress    string // 详细地址
+	CustProvinceId string // 所在省ID
+	CustProvince   string // 所在省
+	CustCityId     string // 所在市ID
+	CustCity       string // 所在市
+	CustRegionId   string // 所在区县ID
+	CustRegion     string // 所在区县
+	CustIndustry   string // 客户行业
+	CustLevel      string // 客户级别(10 重点客户 20 普通客户 30非优客户)
+	CustStatus     string // 客户状态(10 待领取 20 领取审批 30 已领取)
+	CustSource     string // 客户来源
+	IsPublic       string // 公海客户(10是20否)
+	DeptId         string // 所属部门ID
+	DeptName       string // 所属部门
+	SalesId        string // 所属销售ID
+	SalesName      string // 所属销售
+	FollowUpDate   string // 最后跟进时间
+	FollowUpMan    string // 最后跟进人
+	Remark         string // 备注
+	CreatedBy      string // 创建者
+	CreatedName    string // 创建人
+	CreatedTime    string // 创建时间
+	UpdatedBy      string // 更新者
+	UpdatedName    string // 更新人
+	UpdatedTime    string // 更新时间
+	DeletedTime    string // 删除时间
 }
 
 var (
@@ -61,30 +68,38 @@ var (
 		DB:    g.DB("default"),
 		Table: "cust_customer",
 		Columns: custCustomerColumns{
-			Id:           "id",
-			CustCode:     "cust_code",
-			CustName:     "cust_name",
-			CustIndustry: "cust_industry",
-			CustLevel:    "cust_level",
-			AbbrName:     "abbr_name",
-			CustLocation: "cust_location",
-			CustAddress:  "cust_address",
-			CustSource:   "cust_source",
-			CustStatus:   "cust_status",
-			IsPublic:     "is_public",
-			DeptId:       "dept_id",
-			DeptName:     "dept_name",
-			SalesId:      "sales_id",
-			SalesName:    "sales_name",
-			FollowUpDate: "follow_up_date",
-			Remark:       "remark",
-			CreatedBy:    "created_by",
-			CreatedName:  "created_name",
-			CreatedTime:  "created_time",
-			UpdatedBy:    "updated_by",
-			UpdatedName:  "updated_name",
-			UpdatedTime:  "updated_time",
-			DeletedTime:  "deleted_time",
+			Id:             "id",
+			CustCode:       "cust_code",
+			CustName:       "cust_name",
+			AbbrName:       "abbr_name",
+			CustDistCode:   "cust_dist_code",
+			CustLocation:   "cust_location",
+			CustAddress:    "cust_address",
+			CustProvinceId: "cust_province_id",
+			CustProvince:   "cust_province",
+			CustCityId:     "cust_city_id",
+			CustCity:       "cust_city",
+			CustRegionId:   "cust_region_id",
+			CustRegion:     "cust_region",
+			CustIndustry:   "cust_industry",
+			CustLevel:      "cust_level",
+			CustStatus:     "cust_status",
+			CustSource:     "cust_source",
+			IsPublic:       "is_public",
+			DeptId:         "dept_id",
+			DeptName:       "dept_name",
+			SalesId:        "sales_id",
+			SalesName:      "sales_name",
+			FollowUpDate:   "follow_up_date",
+			FollowUpMan:    "follow_up_man",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
 		},
 	}
 )
@@ -96,30 +111,38 @@ func NewCustCustomerDao(tenant string) CustCustomerDao {
 		DB:    g.DB(tenant),
 		Table: "cust_customer",
 		Columns: custCustomerColumns{
-			Id:           "id",
-			CustCode:     "cust_code",
-			CustName:     "cust_name",
-			AbbrName:     "abbr_name",
-			CustLocation: "cust_location",
-			CustIndustry: "cust_industry",
-			CustLevel:    "cust_level",
-			CustAddress:  "cust_address",
-			CustStatus:   "cust_status",
-			CustSource:   "cust_source",
-			IsPublic:     "is_public",
-			DeptId:       "dept_id",
-			DeptName:     "dept_name",
-			SalesId:      "sales_id",
-			SalesName:    "sales_name",
-			FollowUpDate: "follow_up_date",
-			Remark:       "remark",
-			CreatedBy:    "created_by",
-			CreatedName:  "created_name",
-			CreatedTime:  "created_time",
-			UpdatedBy:    "updated_by",
-			UpdatedName:  "updated_name",
-			UpdatedTime:  "updated_time",
-			DeletedTime:  "deleted_time",
+			Id:             "id",
+			CustCode:       "cust_code",
+			CustName:       "cust_name",
+			AbbrName:       "abbr_name",
+			CustDistCode:   "cust_dist_code",
+			CustLocation:   "cust_location",
+			CustAddress:    "cust_address",
+			CustProvinceId: "cust_province_id",
+			CustProvince:   "cust_province",
+			CustCityId:     "cust_city_id",
+			CustCity:       "cust_city",
+			CustRegionId:   "cust_region_id",
+			CustRegion:     "cust_region",
+			CustIndustry:   "cust_industry",
+			CustLevel:      "cust_level",
+			CustStatus:     "cust_status",
+			CustSource:     "cust_source",
+			IsPublic:       "is_public",
+			DeptId:         "dept_id",
+			DeptName:       "dept_name",
+			SalesId:        "sales_id",
+			SalesName:      "sales_name",
+			FollowUpDate:   "follow_up_date",
+			FollowUpMan:    "follow_up_man",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
 		},
 	}
 	return dao

+ 57 - 48
opms_parent/app/dao/plat/internal/plat_schedule.go

@@ -27,22 +27,25 @@ type PlatScheduleDao struct {
 
 // PlatScheduleColumns defines and stores column names for table plat_schedule.
 type platScheduleColumns struct {
-	Id          string // 主键
-	SchTitle    string // 标题
-	SchContent  string // 内容
-	SchDate     string // 日期
-	UserId      string // 关联用户
-	UserName    string // 用户姓名
-	BizType     string // 关联业务类型
-	BizBillId   string // 关联业务单据
-	Remark      string // 备注
-	CreatedBy   string // 创建者
-	CreatedName string // 创建人
-	CreatedTime string // 创建时间
-	UpdatedBy   string // 更新者
-	UpdatedName string // 更新人
-	UpdatedTime string // 更新时间
-	DeletedTime string // 删除时间
+	Id             string // 主键
+	SchTitle       string // 标题
+	SchContent     string // 内容
+	SchDate        string // 日程开始时间
+	SchDateEnd     string // 日程结束时间
+	UserId         string // 日程组织人Id
+	UserUnionId    string // 日程组织人unionId
+	UserName       string // 日程组织人姓名
+	DingScheduleId string // 日程关联的钉钉日程Id
+	BizType        string // 关联业务类型
+	BizBillId      string // 关联业务单据
+	Remark         string // 备注
+	CreatedBy      string // 创建者
+	CreatedName    string // 创建人
+	CreatedTime    string // 创建时间
+	UpdatedBy      string // 更新者
+	UpdatedName    string // 更新人
+	UpdatedTime    string // 更新时间
+	DeletedTime    string // 删除时间
 }
 
 var (
@@ -52,22 +55,25 @@ var (
 		DB:    g.DB("default"),
 		Table: "plat_schedule",
 		Columns: platScheduleColumns{
-			Id:          "id",
-			SchTitle:    "sch_title",
-			SchContent:  "sch_ content",
-			SchDate:     "sch_ date",
-			UserId:      "user_id",
-			UserName:    "user_name",
-			BizType:     "biz_type",
-			BizBillId:   "biz_bill_id",
-			Remark:      "remark",
-			CreatedBy:   "created_by",
-			CreatedName: "created_name",
-			CreatedTime: "created_time",
-			UpdatedBy:   "updated_by",
-			UpdatedName: "updated_name",
-			UpdatedTime: "updated_time",
-			DeletedTime: "deleted_time",
+			Id:             "id",
+			SchTitle:       "sch_title",
+			SchContent:     "sch_ content",
+			SchDate:        "sch_ date",
+			SchDateEnd:     "sch_ date_end",
+			UserId:         "user_id",
+			UserUnionId:    "user_unionId",
+			UserName:       "user_name",
+			DingScheduleId: "ding_schedule_id",
+			BizType:        "biz_type",
+			BizBillId:      "biz_bill_id",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
 		},
 	}
 )
@@ -79,22 +85,25 @@ func NewPlatScheduleDao(tenant string) PlatScheduleDao {
 		DB:    g.DB(tenant),
 		Table: "plat_schedule",
 		Columns: platScheduleColumns{
-			Id:          "id",
-			SchTitle:    "sch_title",
-			SchContent:  "sch_ content",
-			SchDate:     "sch_ date",
-			UserId:      "user_id",
-			UserName:    "user_name",
-			BizType:     "biz_type",
-			BizBillId:   "biz_bill_id",
-			Remark:      "remark",
-			CreatedBy:   "created_by",
-			CreatedName: "created_name",
-			CreatedTime: "created_time",
-			UpdatedBy:   "updated_by",
-			UpdatedName: "updated_name",
-			UpdatedTime: "updated_time",
-			DeletedTime: "deleted_time",
+			Id:             "id",
+			SchTitle:       "sch_title",
+			SchContent:     "sch_ content",
+			SchDate:        "sch_ date",
+			SchDateEnd:     "sch_ date_end",
+			UserId:         "user_id",
+			UserUnionId:    "user_unionId",
+			UserName:       "user_name",
+			DingScheduleId: "ding_schedule_id",
+			BizType:        "biz_type",
+			BizBillId:      "biz_bill_id",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
 		},
 	}
 	return dao

+ 434 - 0
opms_parent/app/dao/plat/internal/plat_schedule_attendee.go

@@ -0,0 +1,434 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+import (
+	"context"
+	"dashoo.cn/micro/app/model/plat"
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/frame/gmvc"
+	"time"
+)
+
+// PlatScheduleAttendeeDao is the manager for logic model data accessing
+// and custom defined data operations functions management.
+type PlatScheduleAttendeeDao struct {
+	gmvc.M
+	DB      gdb.DB
+	Table   string
+	Columns platScheduleAttendeeColumns
+}
+
+// PlatScheduleAttendeeColumns defines and stores column names for table plat_schedule_attendee.
+type platScheduleAttendeeColumns struct {
+	Id             string // 主键
+	SchId          string // 日程ID
+	DingScheduleId string // 日程关联到钉钉的日程Id
+	UserId         string // 日程参与人Id
+	UserUnionId    string // 日程参与人unionId
+	UserName       string // 日程参与人姓名
+	Remark         string // 备注
+	CreatedBy      string // 创建者
+	CreatedName    string // 创建人
+	CreatedTime    string // 创建时间
+	UpdatedBy      string // 更新者
+	UpdatedName    string // 更新人
+	UpdatedTime    string // 更新时间
+	DeletedTime    string // 删除时间
+}
+
+var (
+	// PlatScheduleAttendee is globally public accessible object for table plat_schedule_attendee operations.
+	PlatScheduleAttendee = PlatScheduleAttendeeDao{
+		M:     g.DB("default").Model("plat_schedule_attendee").Safe(),
+		DB:    g.DB("default"),
+		Table: "plat_schedule_attendee",
+		Columns: platScheduleAttendeeColumns{
+			Id:             "id",
+			SchId:          "sch_id",
+			DingScheduleId: "ding_schedule_id",
+			UserId:         "user_id",
+			UserUnionId:    "user_unionId",
+			UserName:       "user_name",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
+		},
+	}
+)
+
+func NewPlatScheduleAttendeeDao(tenant string) PlatScheduleAttendeeDao {
+	var dao PlatScheduleAttendeeDao
+	dao = PlatScheduleAttendeeDao{
+		M:     g.DB(tenant).Model("plat_schedule_attendee").Safe(),
+		DB:    g.DB(tenant),
+		Table: "plat_schedule_attendee",
+		Columns: platScheduleAttendeeColumns{
+			Id:             "id",
+			SchId:          "sch_id",
+			DingScheduleId: "ding_schedule_id",
+			UserId:         "user_id",
+			UserUnionId:    "user_unionId",
+			UserName:       "user_name",
+			Remark:         "remark",
+			CreatedBy:      "created_by",
+			CreatedName:    "created_name",
+			CreatedTime:    "created_time",
+			UpdatedBy:      "updated_by",
+			UpdatedName:    "updated_name",
+			UpdatedTime:    "updated_time",
+			DeletedTime:    "deleted_time",
+		},
+	}
+	return dao
+}
+
+// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
+// of current DB object and with given context in it.
+// Note that this returned DB object can be used only once, so do not assign it to
+// a global or package variable for long using.
+func (d *PlatScheduleAttendeeDao) Ctx(ctx context.Context) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Ctx(ctx)}
+}
+
+// As sets an alias name for current table.
+func (d *PlatScheduleAttendeeDao) As(as string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.As(as)}
+}
+
+// TX sets the transaction for current operation.
+func (d *PlatScheduleAttendeeDao) TX(tx *gdb.TX) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.TX(tx)}
+}
+
+// Master marks the following operation on master node.
+func (d *PlatScheduleAttendeeDao) Master() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Master()}
+}
+
+// Slave marks the following operation on slave node.
+// Note that it makes sense only if there's any slave node configured.
+func (d *PlatScheduleAttendeeDao) Slave() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Slave()}
+}
+
+// Args sets custom arguments for model operation.
+func (d *PlatScheduleAttendeeDao) Args(args ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Args(args...)}
+}
+
+// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatScheduleAttendeeDao) LeftJoin(table ...string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatScheduleAttendeeDao) RightJoin(table ...string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatScheduleAttendeeDao) InnerJoin(table ...string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *PlatScheduleAttendeeDao) Fields(fieldNamesOrMapStruct ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Fields(fieldNamesOrMapStruct...)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *PlatScheduleAttendeeDao) FieldsEx(fieldNamesOrMapStruct ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.FieldsEx(fieldNamesOrMapStruct...)}
+}
+
+// Option sets the extra operation option for the model.
+func (d *PlatScheduleAttendeeDao) Option(option int) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (d *PlatScheduleAttendeeDao) OmitEmpty() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (d *PlatScheduleAttendeeDao) Filter() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter <where> can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (d *PlatScheduleAttendeeDao) Where(where interface{}, args ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Where(where, args...)}
+}
+
+// WherePri does the same logic as M.Where except that if the parameter <where>
+// is a single condition like int/string/float/slice, it treats the condition as the primary
+// key value. That is, if primary key is "id" and given <where> parameter as "123", the
+// WherePri function treats the condition as "id=123", but M.Where treats the condition
+// as string "123".
+func (d *PlatScheduleAttendeeDao) WherePri(where interface{}, args ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.WherePri(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (d *PlatScheduleAttendeeDao) And(where interface{}, args ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (d *PlatScheduleAttendeeDao) Or(where interface{}, args ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (d *PlatScheduleAttendeeDao) Group(groupBy string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (d *PlatScheduleAttendeeDao) Order(orderBy ...string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter <limit> can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (d *PlatScheduleAttendeeDao) Limit(limit ...int) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (d *PlatScheduleAttendeeDao) Offset(offset int) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter <page> is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (d *PlatScheduleAttendeeDao) Page(page, limit int) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (d *PlatScheduleAttendeeDao) Batch(batch int) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (d *PlatScheduleAttendeeDao) Cache(duration time.Duration, name ...string) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (d *PlatScheduleAttendeeDao) Data(data ...interface{}) *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*model.PlatScheduleAttendee.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *PlatScheduleAttendeeDao) All(where ...interface{}) ([]*plat.PlatScheduleAttendee, error) {
+	all, err := d.M.All(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*plat.PlatScheduleAttendee
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *model.PlatScheduleAttendee.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *PlatScheduleAttendeeDao) One(where ...interface{}) (*plat.PlatScheduleAttendee, error) {
+	one, err := d.M.One(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *plat.PlatScheduleAttendee
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by M.WherePri and M.One.
+// Also see M.WherePri and M.One.
+func (d *PlatScheduleAttendeeDao) FindOne(where ...interface{}) (*plat.PlatScheduleAttendee, error) {
+	one, err := d.M.FindOne(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *plat.PlatScheduleAttendee
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindAll retrieves and returns Result by by M.WherePri and M.All.
+// Also see M.WherePri and M.All.
+func (d *PlatScheduleAttendeeDao) FindAll(where ...interface{}) ([]*plat.PlatScheduleAttendee, error) {
+	all, err := d.M.FindAll(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*plat.PlatScheduleAttendee
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// Struct retrieves one record from table and converts it into given struct.
+// The parameter <pointer> should be type of *struct/**struct. If type **struct is given,
+// it can create the struct internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not nil.
+//
+// Eg:
+// user := new(User)
+// err  := dao.User.Where("id", 1).Struct(user)
+//
+// user := (*User)(nil)
+// err  := dao.User.Where("id", 1).Struct(&user)
+func (d *PlatScheduleAttendeeDao) Struct(pointer interface{}, where ...interface{}) error {
+	return d.M.Struct(pointer, where...)
+}
+
+// Structs retrieves records from table and converts them into given struct slice.
+// The parameter <pointer> should be type of *[]struct/*[]*struct. It can create and fill the struct
+// slice internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not empty.
+//
+// Eg:
+// users := ([]User)(nil)
+// err   := dao.User.Structs(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Structs(&users)
+func (d *PlatScheduleAttendeeDao) Structs(pointer interface{}, where ...interface{}) error {
+	return d.M.Structs(pointer, where...)
+}
+
+// Scan automatically calls Struct or Structs function according to the type of parameter <pointer>.
+// It calls function Struct if <pointer> is type of *struct/**struct.
+// It calls function Structs if <pointer> is type of *[]struct/*[]*struct.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved and given pointer is not empty or nil.
+//
+// Eg:
+// user  := new(User)
+// err   := dao.User.Where("id", 1).Scan(user)
+//
+// user  := (*User)(nil)
+// err   := dao.User.Where("id", 1).Scan(&user)
+//
+// users := ([]User)(nil)
+// err   := dao.User.Scan(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Scan(&users)
+func (d *PlatScheduleAttendeeDao) Scan(pointer interface{}, where ...interface{}) error {
+	return d.M.Scan(pointer, where...)
+}
+
+// Chunk iterates the table with given size and callback function.
+func (d *PlatScheduleAttendeeDao) Chunk(limit int, callback func(entities []*plat.PlatScheduleAttendee, err error) bool) {
+	d.M.Chunk(limit, func(result gdb.Result, err error) bool {
+		var entities []*plat.PlatScheduleAttendee
+		err = result.Structs(&entities)
+		if err == sql.ErrNoRows {
+			return false
+		}
+		return callback(entities, err)
+	})
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (d *PlatScheduleAttendeeDao) LockUpdate() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (d *PlatScheduleAttendeeDao) LockShared() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (d *PlatScheduleAttendeeDao) Unscoped() *PlatScheduleAttendeeDao {
+	return &PlatScheduleAttendeeDao{M: d.M.Unscoped()}
+}

+ 36 - 0
opms_parent/app/dao/plat/plat_schedule_attendee.go

@@ -0,0 +1,36 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package plat
+
+import (
+	"dashoo.cn/micro/app/dao/plat/internal"
+)
+
+// platScheduleAttendeeDao is the manager for logic model data accessing
+// and custom defined data operations functions management. You can define
+// methods on it to extend its functionality as you wish.
+type platScheduleAttendeeDao struct {
+	internal.PlatScheduleAttendeeDao
+}
+
+var (
+	// PlatScheduleAttendee is globally public accessible object for table plat_schedule_attendee operations.
+	PlatScheduleAttendee = platScheduleAttendeeDao{
+		internal.PlatScheduleAttendee,
+	}
+)
+
+type PlatScheduleAttendeeDao struct {
+	internal.PlatScheduleAttendeeDao
+}
+
+func NewPlatScheduleAttendeeDao(tenant string) *PlatScheduleAttendeeDao {
+	dao := internal.NewPlatScheduleAttendeeDao(tenant)
+	return &PlatScheduleAttendeeDao{
+		dao,
+	}
+}
+
+// Fill with you ideas below.

+ 440 - 0
opms_parent/app/dao/workflow/internal/plat_workflow.go

@@ -0,0 +1,440 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+import (
+	"context"
+	"dashoo.cn/micro/app/model/workflow"
+	"database/sql"
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/frame/gmvc"
+	"time"
+)
+
+// PlatWorkflowDao is the manager for logic model data accessing
+// and custom defined data operations functions management.
+type PlatWorkflowDao struct {
+	gmvc.M
+	DB      gdb.DB
+	Table   string
+	Columns platWorkflowColumns
+}
+
+// PlatWorkflowColumns defines and stores column names for table plat_workflow.
+type platWorkflowColumns struct {
+	Id              string // 主键
+	BizCode         string // 业务单号
+	BizType         string // 业务类型(10领用20项目创建30合同创建)
+	CurrentNode     string // 当前节点
+	CurrentNodeTime string // 当前节点时间
+	ApprovalResult  string // 审批结果
+	ProcessCode     string // 审批流编码
+	ProcessInstId   string // 审批实例ID
+	Remark          string // 备注
+	CreatedBy       string // 创建者
+	CreatedName     string // 创建人
+	CreatedTime     string // 创建时间
+	UpdatedBy       string // 更新者
+	UpdatedName     string // 更新人
+	UpdatedTime     string // 更新时间
+	DeletedTime     string // 删除时间
+}
+
+var (
+	// PlatWorkflow is globally public accessible object for table plat_workflow operations.
+	PlatWorkflow = PlatWorkflowDao{
+		M:     g.DB("default").Model("plat_workflow").Safe(),
+		DB:    g.DB("default"),
+		Table: "plat_workflow",
+		Columns: platWorkflowColumns{
+			Id:              "id",
+			BizCode:         "biz_code",
+			BizType:         "biz_type",
+			CurrentNode:     "current_node",
+			CurrentNodeTime: "current_node_time",
+			ApprovalResult:  "approval_result",
+			ProcessCode:     "process_code",
+			ProcessInstId:   "process_inst_id",
+			Remark:          "remark",
+			CreatedBy:       "created_by",
+			CreatedName:     "created_name",
+			CreatedTime:     "created_time",
+			UpdatedBy:       "updated_by",
+			UpdatedName:     "updated_name",
+			UpdatedTime:     "updated_time",
+			DeletedTime:     "deleted_time",
+		},
+	}
+)
+
+func NewPlatWorkflowDao(tenant string) PlatWorkflowDao {
+	var dao PlatWorkflowDao
+	dao = PlatWorkflowDao{
+		M:     g.DB(tenant).Model("plat_workflow").Safe(),
+		DB:    g.DB(tenant),
+		Table: "plat_workflow",
+		Columns: platWorkflowColumns{
+			Id:              "id",
+			BizCode:         "biz_code",
+			BizType:         "biz_type",
+			CurrentNode:     "current_node",
+			CurrentNodeTime: "current_node_time",
+			ApprovalResult:  "approval_result",
+			ProcessCode:     "process_code",
+			ProcessInstId:   "process_inst_id",
+			Remark:          "remark",
+			CreatedBy:       "created_by",
+			CreatedName:     "created_name",
+			CreatedTime:     "created_time",
+			UpdatedBy:       "updated_by",
+			UpdatedName:     "updated_name",
+			UpdatedTime:     "updated_time",
+			DeletedTime:     "deleted_time",
+		},
+	}
+	return dao
+}
+
+// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
+// of current DB object and with given context in it.
+// Note that this returned DB object can be used only once, so do not assign it to
+// a global or package variable for long using.
+func (d *PlatWorkflowDao) Ctx(ctx context.Context) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Ctx(ctx)}
+}
+
+// As sets an alias name for current table.
+func (d *PlatWorkflowDao) As(as string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.As(as)}
+}
+
+// TX sets the transaction for current operation.
+func (d *PlatWorkflowDao) TX(tx *gdb.TX) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.TX(tx)}
+}
+
+// Master marks the following operation on master node.
+func (d *PlatWorkflowDao) Master() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Master()}
+}
+
+// Slave marks the following operation on slave node.
+// Note that it makes sense only if there's any slave node configured.
+func (d *PlatWorkflowDao) Slave() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Slave()}
+}
+
+// Args sets custom arguments for model operation.
+func (d *PlatWorkflowDao) Args(args ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Args(args...)}
+}
+
+// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatWorkflowDao) LeftJoin(table ...string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.LeftJoin(table...)}
+}
+
+// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatWorkflowDao) RightJoin(table ...string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.RightJoin(table...)}
+}
+
+// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
+// The parameter <table> can be joined table and its joined condition,
+// and also with its alias name, like:
+// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
+// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
+func (d *PlatWorkflowDao) InnerJoin(table ...string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.InnerJoin(table...)}
+}
+
+// Fields sets the operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *PlatWorkflowDao) Fields(fieldNamesOrMapStruct ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Fields(fieldNamesOrMapStruct...)}
+}
+
+// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
+// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
+func (d *PlatWorkflowDao) FieldsEx(fieldNamesOrMapStruct ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.FieldsEx(fieldNamesOrMapStruct...)}
+}
+
+// Option sets the extra operation option for the model.
+func (d *PlatWorkflowDao) Option(option int) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Option(option)}
+}
+
+// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
+// the data and where attributes for empty values.
+func (d *PlatWorkflowDao) OmitEmpty() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.OmitEmpty()}
+}
+
+// Filter marks filtering the fields which does not exist in the fields of the operated table.
+func (d *PlatWorkflowDao) Filter() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Filter()}
+}
+
+// Where sets the condition statement for the model. The parameter <where> can be type of
+// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
+// multiple conditions will be joined into where statement using "AND".
+// Eg:
+// Where("uid=10000")
+// Where("uid", 10000)
+// Where("money>? AND name like ?", 99999, "vip_%")
+// Where("uid", 1).Where("name", "john")
+// Where("status IN (?)", g.Slice{1,2,3})
+// Where("age IN(?,?)", 18, 50)
+// Where(User{ Id : 1, UserName : "john"})
+func (d *PlatWorkflowDao) Where(where interface{}, args ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Where(where, args...)}
+}
+
+// WherePri does the same logic as M.Where except that if the parameter <where>
+// is a single condition like int/string/float/slice, it treats the condition as the primary
+// key value. That is, if primary key is "id" and given <where> parameter as "123", the
+// WherePri function treats the condition as "id=123", but M.Where treats the condition
+// as string "123".
+func (d *PlatWorkflowDao) WherePri(where interface{}, args ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.WherePri(where, args...)}
+}
+
+// And adds "AND" condition to the where statement.
+func (d *PlatWorkflowDao) And(where interface{}, args ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.And(where, args...)}
+}
+
+// Or adds "OR" condition to the where statement.
+func (d *PlatWorkflowDao) Or(where interface{}, args ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Or(where, args...)}
+}
+
+// Group sets the "GROUP BY" statement for the model.
+func (d *PlatWorkflowDao) Group(groupBy string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Group(groupBy)}
+}
+
+// Order sets the "ORDER BY" statement for the model.
+func (d *PlatWorkflowDao) Order(orderBy ...string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Order(orderBy...)}
+}
+
+// Limit sets the "LIMIT" statement for the model.
+// The parameter <limit> can be either one or two number, if passed two number is passed,
+// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
+// statement.
+func (d *PlatWorkflowDao) Limit(limit ...int) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Limit(limit...)}
+}
+
+// Offset sets the "OFFSET" statement for the model.
+// It only makes sense for some databases like SQLServer, PostgreSQL, etc.
+func (d *PlatWorkflowDao) Offset(offset int) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Offset(offset)}
+}
+
+// Page sets the paging number for the model.
+// The parameter <page> is started from 1 for paging.
+// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
+func (d *PlatWorkflowDao) Page(page, limit int) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Page(page, limit)}
+}
+
+// Batch sets the batch operation number for the model.
+func (d *PlatWorkflowDao) Batch(batch int) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Batch(batch)}
+}
+
+// Cache sets the cache feature for the model. It caches the result of the sql, which means
+// if there's another same sql request, it just reads and returns the result from cache, it
+// but not committed and executed into the database.
+//
+// If the parameter <duration> < 0, which means it clear the cache with given <name>.
+// If the parameter <duration> = 0, which means it never expires.
+// If the parameter <duration> > 0, which means it expires after <duration>.
+//
+// The optional parameter <name> is used to bind a name to the cache, which means you can later
+// control the cache like changing the <duration> or clearing the cache with specified <name>.
+//
+// Note that, the cache feature is disabled if the model is operating on a transaction.
+func (d *PlatWorkflowDao) Cache(duration time.Duration, name ...string) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Cache(duration, name...)}
+}
+
+// Data sets the operation data for the model.
+// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
+// Eg:
+// Data("uid=10000")
+// Data("uid", 10000)
+// Data(g.Map{"uid": 10000, "name":"john"})
+// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
+func (d *PlatWorkflowDao) Data(data ...interface{}) *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Data(data...)}
+}
+
+// All does "SELECT FROM ..." statement for the model.
+// It retrieves the records from table and returns the result as []*model.PlatWorkflow.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *PlatWorkflowDao) All(where ...interface{}) ([]*workflow.PlatWorkflow, error) {
+	all, err := d.M.All(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*workflow.PlatWorkflow
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// One retrieves one record from table and returns the result as *model.PlatWorkflow.
+// It returns nil if there's no record retrieved with the given conditions from table.
+//
+// The optional parameter <where> is the same as the parameter of M.Where function,
+// see M.Where.
+func (d *PlatWorkflowDao) One(where ...interface{}) (*workflow.PlatWorkflow, error) {
+	one, err := d.M.One(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *workflow.PlatWorkflow
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindOne retrieves and returns a single Record by M.WherePri and M.One.
+// Also see M.WherePri and M.One.
+func (d *PlatWorkflowDao) FindOne(where ...interface{}) (*workflow.PlatWorkflow, error) {
+	one, err := d.M.FindOne(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entity *workflow.PlatWorkflow
+	if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entity, nil
+}
+
+// FindAll retrieves and returns Result by by M.WherePri and M.All.
+// Also see M.WherePri and M.All.
+func (d *PlatWorkflowDao) FindAll(where ...interface{}) ([]*workflow.PlatWorkflow, error) {
+	all, err := d.M.FindAll(where...)
+	if err != nil {
+		return nil, err
+	}
+	var entities []*workflow.PlatWorkflow
+	if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows {
+		return nil, err
+	}
+	return entities, nil
+}
+
+// Struct retrieves one record from table and converts it into given struct.
+// The parameter <pointer> should be type of *struct/**struct. If type **struct is given,
+// it can create the struct internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not nil.
+//
+// Eg:
+// user := new(User)
+// err  := dao.User.Where("id", 1).Struct(user)
+//
+// user := (*User)(nil)
+// err  := dao.User.Where("id", 1).Struct(&user)
+func (d *PlatWorkflowDao) Struct(pointer interface{}, where ...interface{}) error {
+	return d.M.Struct(pointer, where...)
+}
+
+// Structs retrieves records from table and converts them into given struct slice.
+// The parameter <pointer> should be type of *[]struct/*[]*struct. It can create and fill the struct
+// slice internally during converting.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
+// from table and <pointer> is not empty.
+//
+// Eg:
+// users := ([]User)(nil)
+// err   := dao.User.Structs(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Structs(&users)
+func (d *PlatWorkflowDao) Structs(pointer interface{}, where ...interface{}) error {
+	return d.M.Structs(pointer, where...)
+}
+
+// Scan automatically calls Struct or Structs function according to the type of parameter <pointer>.
+// It calls function Struct if <pointer> is type of *struct/**struct.
+// It calls function Structs if <pointer> is type of *[]struct/*[]*struct.
+//
+// The optional parameter <where> is the same as the parameter of Model.Where function,
+// see Model.Where.
+//
+// Note that it returns sql.ErrNoRows if there's no record retrieved and given pointer is not empty or nil.
+//
+// Eg:
+// user  := new(User)
+// err   := dao.User.Where("id", 1).Scan(user)
+//
+// user  := (*User)(nil)
+// err   := dao.User.Where("id", 1).Scan(&user)
+//
+// users := ([]User)(nil)
+// err   := dao.User.Scan(&users)
+//
+// users := ([]*User)(nil)
+// err   := dao.User.Scan(&users)
+func (d *PlatWorkflowDao) Scan(pointer interface{}, where ...interface{}) error {
+	return d.M.Scan(pointer, where...)
+}
+
+// Chunk iterates the table with given size and callback function.
+func (d *PlatWorkflowDao) Chunk(limit int, callback func(entities []*workflow.PlatWorkflow, err error) bool) {
+	d.M.Chunk(limit, func(result gdb.Result, err error) bool {
+		var entities []*workflow.PlatWorkflow
+		err = result.Structs(&entities)
+		if err == sql.ErrNoRows {
+			return false
+		}
+		return callback(entities, err)
+	})
+}
+
+// LockUpdate sets the lock for update for current operation.
+func (d *PlatWorkflowDao) LockUpdate() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.LockUpdate()}
+}
+
+// LockShared sets the lock in share mode for current operation.
+func (d *PlatWorkflowDao) LockShared() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.LockShared()}
+}
+
+// Unscoped enables/disables the soft deleting feature.
+func (d *PlatWorkflowDao) Unscoped() *PlatWorkflowDao {
+	return &PlatWorkflowDao{M: d.M.Unscoped()}
+}

+ 36 - 0
opms_parent/app/dao/workflow/plat_workflow.go

@@ -0,0 +1,36 @@
+// ============================================================================
+// This is auto-generated by gf cli tool only once. Fill this file as you wish.
+// ============================================================================
+
+package workflow
+
+import (
+	"dashoo.cn/micro/app/dao/workflow/internal"
+)
+
+// platWorkflowDao is the manager for logic model data accessing
+// and custom defined data operations functions management. You can define
+// methods on it to extend its functionality as you wish.
+type platWorkflowDao struct {
+	internal.PlatWorkflowDao
+}
+
+var (
+	// PlatWorkflow is globally public accessible object for table plat_workflow operations.
+	PlatWorkflow = platWorkflowDao{
+		internal.PlatWorkflow,
+	}
+)
+
+type PlatWorkflowDao struct {
+	internal.PlatWorkflowDao
+}
+
+func NewPlatWorkflowDao(tenant string) *PlatWorkflowDao {
+	dao := internal.NewPlatWorkflowDao(tenant)
+	return &PlatWorkflowDao{
+		dao,
+	}
+}
+
+// Fill with you ideas below.

+ 2 - 1
opms_parent/app/handler/cust/customer.go

@@ -51,6 +51,7 @@ func (c *CustomerHeader) PublicGetList(ctx context.Context, req *model.CustCusto
 	return nil
 
 }
+
 //Swagger:Customer 客户,测试tag 创建客户
 func (c *CustomerHeader) Create(ctx context.Context, req *model.CustomerAddSeq, rsp *comm_def.CommonMsg) error {
 	if err := gvalid.CheckStruct(ctx, req, nil); err != nil {
@@ -175,7 +176,7 @@ func (c *CustomerHeader) AssignCustomer(ctx context.Context, req *model.AssignCu
 	} else {
 		behavior = "领取"
 	}
-	err = s.AssignCustomer(req)
+	err = s.AssignCustomer(ctx, req)
 	if err != nil {
 		return err
 	}

+ 146 - 0
opms_parent/app/handler/dingtalk/ding_event.go

@@ -0,0 +1,146 @@
+package dingtalk
+
+import (
+	"context"
+	model "dashoo.cn/micro/app/model/workflow"
+	custServer "dashoo.cn/micro/app/service/cust"
+	platServer "dashoo.cn/micro/app/service/plat"
+	workflowServer "dashoo.cn/micro/app/service/workflow"
+	"dashoo.cn/opms_libary/plugin/dingtalk"
+	dingContext "dashoo.cn/opms_libary/plugin/dingtalk/context"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
+	"fmt"
+	"github.com/gogf/gf/os/glog"
+)
+
+type DingHandler struct{}
+
+// CallBack 钉钉事件回调
+// 日程相关详情参照文档:https://open.dingtalk.com/document/orgapp/schedule-event
+// 审批相关详情参照文档:https://open.dingtalk.com/document/orgapp/approval-events
+func (h *DingHandler) CallBack(ctx context.Context, req *message.SubsMessage, rsp *message.Reply) error {
+	req.Ctx = ctx
+	handler := dingtalk.Client.GetDingTalkHandler(req)
+	//设置接收消息的处理方法
+	handler.SetHandleMessageFunc(func(msg *message.MixMessage) string {
+		switch msg.EventType {
+		case message.EventCheckUrl:
+			return h.handleCheckUrl(msg)
+		case message.EventCalendarChange:
+			return h.handleCalendarChange(msg, handler.Context)
+		case message.BpmsInstanceChange:
+			return h.handleBpmsInstanceChange(msg, handler.Context)
+		case message.BpmsTaskChange:
+			return h.handleBpmsTaskChange(msg, handler.Context)
+		}
+		return ""
+	})
+	result, _ := handler.Handle()
+	*rsp = result
+	return nil
+}
+
+// 验证回调参数
+func (h *DingHandler) handleCheckUrl(msg *message.MixMessage) string {
+	fmt.Println(msg)
+	return "success"
+}
+
+// 处理日程回调
+func (h *DingHandler) handleCalendarChange(msg *message.MixMessage, ctx *dingContext.Context) string {
+	fmt.Println(msg)
+
+	// 本系统数据库操作
+	s, err := platServer.NewScheduleService(ctx.SubsMessage.Ctx)
+	if err != nil {
+		glog.Error(err)
+		return err.Error()
+	}
+	// 回填数据
+	err = s.SaveByDingEvent(msg)
+	if err != nil {
+		glog.Error(err)
+		return err.Error()
+	}
+	return "success"
+}
+
+// 处理审批实例回调
+func (h *DingHandler) handleBpmsInstanceChange(msg *message.MixMessage, ctx *dingContext.Context) string {
+	fmt.Println(msg)
+
+	// 构造访问服务
+	s, err := workflowServer.NewFlowService(ctx.SubsMessage.Ctx)
+	if err != nil {
+		glog.Error(err)
+		return err.Error()
+	}
+	// 获取审批实例数据
+	instance, err := s.GetWorkflowInstance(msg.ProcessInstanceId)
+	// 处理实例数据(不需要钉钉审批实例参数)
+	switch instance.BizType {
+	case model.CustomerReceive:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			srv, err := custServer.NewCustomerService(ctx.SubsMessage.Ctx)
+			if err != nil {
+				glog.Error(err)
+				return "fail"
+			}
+			err = srv.AssignCustomerRequestApproval(instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return "fail"
+			}
+		}
+		return "success"
+	case model.ProjectCreate:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			//srv.Handle(instance, msg)
+		}
+		return "success"
+	case model.ContractCreate:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			//srv.Handle(instance, msg)
+		}
+		return "success"
+	}
+
+	// 以下的代码需要调用钉钉接口查询数据,并且以下接口需要审批实例的数据
+	//// 获取钉钉数据
+	//resp, err := s.GetProcessInstanceDetail(msg.ProcessInstanceId)
+	//if err != nil {
+	//	glog.Error(err)
+	//	return err.Error()
+	//}
+	//if resp.Success == "false" {
+	//	return "fail"
+	//}
+	////按照类型处理数据(不需要钉钉审批实例参数)
+	//switch instance.BizType {
+	//case model.CustomerReceive:
+	//	if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+	//		//srv.Handle(instance, msg, resp)
+	//	}
+
+	return "success"
+}
+
+// 处理审批任务回调
+func (h *DingHandler) handleBpmsTaskChange(msg *message.MixMessage, ctx *dingContext.Context) string {
+	fmt.Println(msg)
+
+	// TODO 目前审批任务无处理逻辑
+	//// 本系统数据库操作
+	//s, err := platServer.NewScheduleService(ctx.SubsMessage.Ctx)
+	//if err != nil {
+	//	glog.Error(err)
+	//	return err.Error()
+	//}
+	//// 回填数据
+	//err = s.SaveByDingEvent(msg)
+	//if err != nil {
+	//	glog.Error(err)
+	//	return err.Error()
+	//}
+	return "success"
+}

+ 21 - 1
opms_parent/app/handler/plat/schedule.go

@@ -14,6 +14,7 @@ import (
 type ScheduleHeader struct{}
 
 //GetList 日程列表
+// Swagger:Schedule 日程 日程列表
 func (h *ScheduleHeader) GetList(ctx context.Context, req *model.ScheduleSearchReq, rsp *comm_def.CommonMsg) error {
 	s, err := server.NewScheduleService(ctx)
 	if err != nil {
@@ -30,6 +31,7 @@ func (h *ScheduleHeader) GetList(ctx context.Context, req *model.ScheduleSearchR
 }
 
 //Create 创建日程
+// Swagger:Schedule 日程 创建日程
 func (h *ScheduleHeader) Create(ctx context.Context, req *model.AddScheduleSeq, rsp *comm_def.CommonMsg) error {
 	if err := gvalid.CheckStruct(ctx, req, nil); err != nil {
 		return err
@@ -41,10 +43,28 @@ func (h *ScheduleHeader) Create(ctx context.Context, req *model.AddScheduleSeq,
 	id, err := s.Create(req)
 
 	if err != nil {
-
 		return err
 	}
 	rsp.Data = g.Map{"lastId": id}
 	return nil
 
 }
+
+//GetUserBusyStatus 获取用户繁忙信息
+// Swagger:Schedule 日程 获取用户繁忙信息
+func (h *ScheduleHeader) GetUserBusyStatus(ctx context.Context, req *model.UserBusyStatusReq, rsp *comm_def.CommonMsg) error {
+	if err := gvalid.CheckStruct(ctx, req, nil); err != nil {
+		return err
+	}
+	s, err := server.NewScheduleService(ctx)
+	if err != nil {
+		return err
+	}
+	data, err := s.GetUserBusyStatus(req)
+
+	if err != nil {
+		return err
+	}
+	rsp.Data = g.Map{"userBusyInfos": data}
+	return nil
+}

+ 27 - 0
opms_parent/app/handler/workflow/plat_workflow.go

@@ -0,0 +1,27 @@
+package workflow
+
+import (
+	"context"
+
+	"dashoo.cn/common_definition/comm_def"
+	model "dashoo.cn/micro/app/model/workflow"
+	server "dashoo.cn/micro/app/service/workflow"
+	"github.com/gogf/gf/frame/g"
+)
+
+type PlatWorkflowHandler struct{}
+
+// GetList 获取列表
+func (h *PlatWorkflowHandler) GetList(ctx context.Context, req *model.SearchWorkflowReq, rsp *comm_def.CommonMsg) error {
+	flowService, err := server.NewFlowService(ctx)
+	if err != nil {
+		return err
+	}
+	g.Log().Info("搜索值", req)
+	total, list, err := flowService.GetList(req)
+	if err != nil {
+		return err
+	}
+	rsp.Data = g.Map{"list": list, "total": total}
+	return nil
+}

+ 45 - 31
opms_parent/app/model/cust/cust_customer.go

@@ -31,7 +31,7 @@ type CustCustomerSearchReq struct {
 	request.PageReq
 }
 
-// CustCustomerExport 导出参数
+//CustCustomerExport 导出参数
 type CustCustomerExport struct {
 	CustCustomerSearchReq
 	Columns []string `json:"columns"` // 导出列
@@ -44,20 +44,27 @@ type CustExport struct {
 	Content []byte `json:"content"` // 导出数据流
 }
 
-// CustomerAddSeq 单表添加客户信息表
+//CustomerAddSeq 单表添加客户信息表
 type CustomerAddSeq struct {
-	CustName     string      `p:"custName"        json:"custName"      v:"required#客户名称不能为空"` // 客户名称
-	AbbrName     string      `p:"abbrName"        json:"abbrName"   `                         // 助计名
-	CustLocation string      `p:"custLocation"    json:"custLocation"    `                    // 所在地区
-	CustAddress  string      `p:"custAddress"     json:"custAddress"   `                      // 详细地址
-	FollowUpDate *gtime.Time `p:"followUpDate"    json:"followUpDate"   `                     //跟进时间
-	CustIndustry string      `p:"custIndustry"    json:"custIndustry"  v:"required#客户行业不能为空"` //客户行业
-	CustLevel    string      `p:"custLevel"       json:"custLevel"     v:"required#客户级别不能为空"` //客户级别
-	CustSource   string      `p:"custSource"      json:"source"        v:"required#客户来源不能为空"` //客户来源
-	CustDistCode int         `p:"custDistCode"    json:"custDistCode"  v:"required#省份不能为空" `  // 省份Id
-	Remark       string      `p:"remark"          json:"remark"`                              //备注
-	SalesName    string      `p:"salesName"       json:"salesName"   `                        // 销售名称
-	SalesId      int         `p:"salesId"         json:"salesId"   `                          // 销售id
+	CustName         string      `p:"custName"        json:"custName"      v:"required#客户名称不能为空"` // 客户名称
+	AbbrName         string      `p:"abbrName"        json:"abbrName"   `                         // 助计名
+	CustLocation     string      `p:"custLocation"    json:"custLocation"    `                    // 所在地区
+	CustAddress      string      `p:"custAddress"     json:"custAddress"   `                      // 详细地址
+	CustProvinceId   int         `json:"custProvinceId"`                                          // 所在省ID
+	CustProvince     string      `json:"custProvince"`                                            // 所在省
+	CustCityId       int         `json:"custCityId"`                                              // 所在市ID
+	CustCity         string      `json:"custCity"`                                                // 所在市
+	CustRegionId     int         `json:"custRegionId"`                                            // 所在区县ID
+	CustRegion       string      `json:"custRegion"`                                              // 所在区县
+	FollowUpDate     *gtime.Time `p:"followUpDate"    json:"followUpDate"   `                     //跟进时间
+	CustIndustry     string      `p:"custIndustry"    json:"custIndustry"  v:"required#客户行业不能为空"` //客户行业
+	CustIndustryCode string      `json:"custIndustryCode"`                                        //客户行业编码
+	CustLevel        string      `p:"custLevel"       json:"custLevel"` //客户级别
+	CustSource       string      `p:"custSource"      json:"source"        v:"required#客户来源不能为空"` //客户来源
+	CustDistCode     int         `p:"custDistCode"    json:"custDistCode"  v:"required#省份不能为空" `  // 省份Id
+	Remark           string      `p:"remark"          json:"remark"`                              //备注
+	SalesName        string      `p:"salesName"       json:"salesName"   `                        // 销售名称
+	SalesId          int         `p:"salesId"         json:"salesId"   `                          // 销售id
 }
 
 // 客户联系人信息
@@ -73,22 +80,29 @@ type Information struct {
 //返回信息
 
 type CustList struct {
-	Id           int    `orm:"id,primary"     json:"id"`           // 主键
-	CustCode     string `orm:"cust_code"      json:"custCode"`     // 客户编号
-	CustName     string `orm:"cust_name"      json:"custName"`     // 客户名称
-	AbbrName     string `orm:"abbr_name"      json:"abbrName"`     // 助记名
-	CustLocation string `orm:"cust_location"  json:"custLocation"` // 所在地区
-	CustAddress  string `orm:"cust_address"   json:"custAddress"`  // 详细地址
-	CustStatus   string `orm:"cust_status"    json:"custStatus"`   // 客户状态(10正常20)
-	FollowUpDate string `orm:"follow_up_date" json:"followUpDate"` // 最后跟进时间
-	CustIndustry string `orm:"cust_industry"  json:"custIndustry"` // 客户行业
-	CustLevel    string `orm:"cust_level"     json:"custLevel"`    // 客户级别(10 重点客户 20 普通客户 30非优客户)
-	CustSource   string `orm:"cust_soucrce"    json:"source"`      // 客户来源
-	CreatedName  string `orm:"created_name"   json:"createdName"`  // 创建人
-	CreatedTime  string `orm:"created_time"   json:"createdTime"`  // 创建时间
-	SalesId      int    `orm:"sales_id"   json:"salesId"`          //
-	SalesName    string `orm:"sales_name"   json:"salesName"`      //
-	Remark       string `orm:"remark"   json:"remark"`
+	Id             int    `orm:"id,primary"     json:"id"`           // 主键
+	CustCode       string `orm:"cust_code"      json:"custCode"`     // 客户编号
+	CustName       string `orm:"cust_name"      json:"custName"`     // 客户名称
+	AbbrName       string `orm:"abbr_name"      json:"abbrName"`     // 助记名
+	CustLocation   string `orm:"cust_location"  json:"custLocation"` // 所在地区
+	CustAddress    string `orm:"cust_address"   json:"custAddress"`  // 详细地址
+	CustProvinceId int    `json:"custProvinceId"`                    // 所在省ID
+	CustProvince   string `json:"custProvince"`                      // 所在省
+	CustCityId     int    `json:"custCityId"`                        // 所在市ID
+	CustCity       string `json:"custCity"`                          // 所在市
+	CustRegionId   int    `json:"custRegionId"`                      // 所在区县ID
+	CustRegion     string `json:"custRegion"`                        // 所在区县
+	CustStatus     string `orm:"cust_status"    json:"custStatus"`   // 客户状态(10正常20)
+	FollowUpDate   string `orm:"follow_up_date" json:"followUpDate"` // 最后跟进时间
+	FollowUpMan    string `json:"followUpMan"`                       // 最后跟进人
+	CustIndustry   string `orm:"cust_industry"  json:"custIndustry"` // 客户行业
+	CustLevel      string `orm:"cust_level"     json:"custLevel"`    // 客户级别(10 重点客户 20 普通客户 30非优客户)
+	CustSource     string `orm:"cust_soucrce"    json:"source"`      // 客户来源
+	CreatedName    string `orm:"created_name"   json:"createdName"`  // 创建人
+	CreatedTime    string `orm:"created_time"   json:"createdTime"`  // 创建时间
+	SalesId        int    `orm:"sales_id"   json:"salesId"`          //
+	SalesName      string `orm:"sales_name"   json:"salesName"`      //
+	Remark         string `orm:"remark"   json:"remark"`
 	//Follow       *Follow     `json:"follow"`
 }
 
@@ -132,7 +146,7 @@ type MergeCustomerRep struct {
 	ChooseId []int64 `json:"chooseId,omitempty"` //被合并的客户id
 }
 
-// UpdateCustomer 修改客户
+//UpdateCustomer 修改客户
 type UpdateCustomer struct {
 	Id int64 `json:"id,omitempty"` //客户id
 	*CustomerAddSeq

+ 32 - 25
opms_parent/app/model/cust/internal/cust_customer.go

@@ -10,29 +10,36 @@ import (
 
 // CustCustomer is the golang structure for table cust_customer.
 type CustCustomer struct {
-	Id           int         `orm:"id,primary"     json:"id"`              // 主键
-	CustCode     string      `orm:"cust_code"      json:"custCode"`        // 客户编号
-	CustName     string      `orm:"cust_name"      json:"custName"`        // 客户名称
-	CustDistCode int         `orm:"cust_dist_code"  json:"cust_dist_code"` //客户所在省级ID
-	AbbrName     string      `orm:"abbr_name"      json:"abbrName"`        // 助记名
-	CustLocation string      `orm:"cust_location"  json:"custLocation"`    // 所在地区
-	CustAddress  string      `orm:"cust_address"   json:"custAddress"`     // 详细地址
-	CustIndustry string      `orm:"cust_industry"   json:"custIndustry"`   //客户行业
-	CustLevel    string      `orm:"cust_level"   json:"custLevel"`         //客户级别(10 重点客户 20 普通客户 30非优客户)
-	CustSource   string      `orm:"cust_source"   json:"source"`           //客户来源
-	CustStatus   string      `orm:"cust_status"    json:"custStatus"`      // 客户状态(10正常20)
-	IsPublic     string      `orm:"is_public"      json:"isPublic"`        // 公海客户(10是20否)
-	DeptId       int         `orm:"dept_id"        json:"deptId"`          // 所属部门ID
-	DeptName     string      `orm:"dept_name"      json:"deptName"`        // 所属部门
-	SalesId      int         `orm:"sales_id"       json:"salesId"`         // 所属销售ID
-	SalesName    string      `orm:"sales_name"     json:"salesName"`       // 所属销售
-	FollowUpDate *gtime.Time `orm:"follow_up_date" json:"followUpDate"`    // 最后跟进时间
-	Remark       string      `orm:"remark"         json:"remark"`          // 备注
-	CreatedBy    int         `orm:"created_by"     json:"createdBy"`       // 创建者
-	CreatedName  string      `orm:"created_name"   json:"createdName"`     // 创建人
-	CreatedTime  *gtime.Time `orm:"created_time"   json:"createdTime"`     // 创建时间
-	UpdatedBy    int         `orm:"updated_by"     json:"updatedBy"`       // 更新者
-	UpdatedName  string      `orm:"updated_name"   json:"updatedName"`     // 更新人
-	UpdatedTime  *gtime.Time `orm:"updated_time"   json:"updatedTime"`     // 更新时间
-	DeletedTime  *gtime.Time `orm:"deleted_time"   json:"deletedTime"`     // 删除时间
+	Id             int         `orm:"id,primary"       json:"id"`             // 主键
+	CustCode       string      `orm:"cust_code"        json:"custCode"`       // 客户编号
+	CustName       string      `orm:"cust_name"        json:"custName"`       // 客户名称
+	AbbrName       string      `orm:"abbr_name"        json:"abbrName"`       // 助记名
+	CustDistCode   int         `orm:"cust_dist_code"   json:"custDistCode"`   // 客户所在省级ID
+	CustLocation   string      `orm:"cust_location"    json:"custLocation"`   // 所在地区
+	CustAddress    string      `orm:"cust_address"     json:"custAddress"`    // 详细地址
+	CustProvinceId int         `orm:"cust_province_id" json:"custProvinceId"` // 所在省ID
+	CustProvince   string      `orm:"cust_province"    json:"custProvince"`   // 所在省
+	CustCityId     int         `orm:"cust_city_id"     json:"custCityId"`     // 所在市ID
+	CustCity       string      `orm:"cust_city"        json:"custCity"`       // 所在市
+	CustRegionId   int         `orm:"cust_region_id"   json:"custRegionId"`   // 所在区县ID
+	CustRegion     string      `orm:"cust_region"      json:"custRegion"`     // 所在区县
+	CustIndustry   string      `orm:"cust_industry"    json:"custIndustry"`   // 客户行业
+	CustLevel      string      `orm:"cust_level"       json:"custLevel"`      // 客户级别(10 重点客户 20 普通客户 30非优客户)
+	CustStatus     string      `orm:"cust_status"      json:"custStatus"`     // 客户状态(10 待领取 20 领取审批 30 已领取)
+	CustSource     string      `orm:"cust_source"      json:"custSource"`     // 客户来源
+	IsPublic       string      `orm:"is_public"        json:"isPublic"`       // 公海客户(10是20否)
+	DeptId         int         `orm:"dept_id"          json:"deptId"`         // 所属部门ID
+	DeptName       string      `orm:"dept_name"        json:"deptName"`       // 所属部门
+	SalesId        int         `orm:"sales_id"         json:"salesId"`        // 所属销售ID
+	SalesName      string      `orm:"sales_name"       json:"salesName"`      // 所属销售
+	FollowUpDate   *gtime.Time `orm:"follow_up_date"   json:"followUpDate"`   // 最后跟进时间
+	FollowUpMan    string      `orm:"follow_up_man"    json:"followUpMan"`    // 最后跟进人
+	Remark         string      `orm:"remark"           json:"remark"`         // 备注
+	CreatedBy      int         `orm:"created_by"       json:"createdBy"`      // 创建者
+	CreatedName    string      `orm:"created_name"     json:"createdName"`    // 创建人
+	CreatedTime    *gtime.Time `orm:"created_time"     json:"createdTime"`    // 创建时间
+	UpdatedBy      int         `orm:"updated_by"       json:"updatedBy"`      // 更新者
+	UpdatedName    string      `orm:"updated_name"     json:"updatedName"`    // 更新人
+	UpdatedTime    *gtime.Time `orm:"updated_time"     json:"updatedTime"`    // 更新时间
+	DeletedTime    *gtime.Time `orm:"deleted_time"     json:"deletedTime"`    // 删除时间
 }

+ 11 - 0
opms_parent/app/model/dingtalk/ding_event.go

@@ -0,0 +1,11 @@
+package dingtalk
+
+// DingReq 日程事件回调请求参数
+type DingReq struct {
+	Encrypt string `json:"encrypt"`
+}
+
+// DingEvent 钉钉事件回调,接收参数
+type DingEvent struct {
+	EventType string
+}

+ 32 - 0
opms_parent/app/model/plat/dingtalk/dingtalk_sckedule.go

@@ -0,0 +1,32 @@
+package dingtalk
+
+type DingParams struct {
+	UserId      string
+	AccessToken string
+}
+
+type DingAddScheduleParams struct {
+	DingParams
+	Summary     string
+	Description string
+	BeginTime   string
+	EndTime     string
+	UserIds     []string
+}
+
+type DingGetScheduleListParams struct {
+	DingParams
+	BeginTime string
+}
+
+type DingGetScheduleParams struct {
+	DingParams
+	EventId string
+}
+
+type DingIsUserBusyParams struct {
+	DingParams
+	UserIds   []string
+	BeginTime string
+	EndTime   string
+}

+ 19 - 16
opms_parent/app/model/plat/internal/plat_schedule.go

@@ -10,20 +10,23 @@ import (
 
 // PlatSchedule is the golang structure for table plat_schedule.
 type PlatSchedule struct {
-	Id          int         `orm:"id,primary"   json:"id"`          // 主键
-	SchTitle    string      `orm:"sch_title"    json:"schTitle"`    // 标题
-	SchContent  string      `orm:"sch_content" json:"schContent"`   // 内容
-	SchDate     *gtime.Time `orm:"sch_date"    json:"schDate"`      // 日期
-	UserId      int         `orm:"user_id"      json:"userId"`      // 关联用户
-	UserName    string      `orm:"user_name"    json:"userName"`    // 用户姓名
-	BizType     string      `orm:"biz_type"     json:"bizType"`     // 关联业务类型
-	BizBillId   int         `orm:"biz_bill_id"  json:"bizBillId"`   // 关联业务单据
-	Remark      string      `orm:"remark"       json:"remark"`      // 备注
-	CreatedBy   int         `orm:"created_by"   json:"createdBy"`   // 创建者
-	CreatedName string      `orm:"created_name" json:"createdName"` // 创建人
-	CreatedTime *gtime.Time `orm:"created_time" json:"createdTime"` // 创建时间
-	UpdatedBy   int         `orm:"updated_by"   json:"updatedBy"`   // 更新者
-	UpdatedName string      `orm:"updated_name" json:"updatedName"` // 更新人
-	UpdatedTime *gtime.Time `orm:"updated_time" json:"updatedTime"` // 更新时间
-	DeletedTime *gtime.Time `orm:"deleted_time" json:"deletedTime"` // 删除时间
+	Id             int         `orm:"id,primary"   json:"id"`                    // 主键
+	SchTitle       string      `orm:"sch_title"    json:"schTitle"`              // 标题
+	SchContent     string      `orm:"sch_content"  json:"schContent"`            // 内容
+	SchDate        *gtime.Time `orm:"sch_date"     json:"schDate"`               // 日程开始时间
+	SchDateEnd     *gtime.Time `orm:"sch_date_end" json:"schDateEnd"`            // 日程结束时间
+	UserId         int         `orm:"user_id"      json:"userId"`                // 日程组织人Id
+	UserUnionId    string      `orm:"user_unionId" json:"userUnionId"`           // 日程组织人unionId
+	UserName       string      `orm:"user_name"    json:"userName"`              // 日程组织人姓名
+	DingScheduleId string      `orm:"ding_schedule_id"    json:"dingScheduleId"` // 日程关联到钉钉的日程Id
+	BizType        string      `orm:"biz_type"     json:"bizType"`               // 关联业务类型
+	BizBillId      int         `orm:"biz_bill_id"  json:"bizBillId"`             // 关联业务单据
+	Remark         string      `orm:"remark"       json:"remark"`                // 备注
+	CreatedBy      int         `orm:"created_by"   json:"createdBy"`             // 创建者
+	CreatedName    string      `orm:"created_name" json:"createdName"`           // 创建人
+	CreatedTime    *gtime.Time `orm:"created_time" json:"createdTime"`           // 创建时间
+	UpdatedBy      int         `orm:"updated_by"   json:"updatedBy"`             // 更新者
+	UpdatedName    string      `orm:"updated_name" json:"updatedName"`           // 更新人
+	UpdatedTime    *gtime.Time `orm:"updated_time" json:"updatedTime"`           // 更新时间
+	DeletedTime    *gtime.Time `orm:"deleted_time" json:"deletedTime"`           // 删除时间
 }

+ 27 - 0
opms_parent/app/model/plat/internal/plat_schedule_attendee.go

@@ -0,0 +1,27 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+import (
+	"github.com/gogf/gf/os/gtime"
+)
+
+// PlatScheduleAttendee is the golang structure for table plat_schedule_attendee.
+type PlatScheduleAttendee struct {
+	Id             int         `orm:"id,primary"   json:"id"`                    // 主键
+	SchId          int         `orm:"sch_id"       json:"schId"`                 // 日程ID
+	DingScheduleId string      `orm:"ding_schedule_id"    json:"dingScheduleId"` // 日程关联到钉钉的日程Id
+	UserId         int         `orm:"user_id"      json:"userId"`                // 日程参与人Id
+	UserUnionId    string      `orm:"user_unionId" json:"userUnionId"`           // 日程参与人unionId
+	UserName       string      `orm:"user_name"    json:"userName"`              // 日程参与人姓名
+	Remark         string      `orm:"remark"       json:"remark"`                // 备注
+	CreatedBy      int         `orm:"created_by"   json:"createdBy"`             // 创建者
+	CreatedName    string      `orm:"created_name" json:"createdName"`           // 创建人
+	CreatedTime    *gtime.Time `orm:"created_time" json:"createdTime"`           // 创建时间
+	UpdatedBy      int         `orm:"updated_by"   json:"updatedBy"`             // 更新者
+	UpdatedName    string      `orm:"updated_name" json:"updatedName"`           // 更新人
+	UpdatedTime    *gtime.Time `orm:"updated_time" json:"updatedTime"`           // 更新时间
+	DeletedTime    *gtime.Time `orm:"deleted_time" json:"deletedTime"`           // 删除时间
+}

+ 25 - 12
opms_parent/app/model/plat/plat_schedule.go

@@ -17,26 +17,39 @@ type PlatSchedule internal.PlatSchedule
 // Fill with you ideas below.
 //AddScheduleSeq 添加日程表
 type AddScheduleSeq struct {
-	SchTitle   string      `json:"schTitle" v:"required#日程标题不能为空"`   //标题
+	SchTitle   string      `json:"schTitle"   v:"required#日程标题不能为空"` //标题
 	SchContent string      `json:"schContent" v:"required#日程内容不能为空"` //内容
-	SchDate    *gtime.Time `json:"schDate"  `                        //日期
-	BizType    string      `json:"bizType" omitempty `               //关联业务类型
-	BizBillId  int         `json:"bizBillId" omitempty`              //关联业务单据
+	SchDate    *gtime.Time `json:"schDate"    v:"required#开始时间不能为空"` //日程开始时间
+	SchDateEnd *gtime.Time `json:"schDateEnd" v:"required#结束时间不能为空"` //日程结束时间
+	BizType    string      `json:"bizType,omitempty"`                //关联业务类型
+	BizBillId  int         `json:"bizBillId,omitempty"`              //关联业务单据
+	UserIds    []string    `json:"userIds"    v:"required#用户不能为空"`   // 用户unionId
 }
 
 // ScheduleSearchReq 日程表列表搜索参数
 type ScheduleSearchReq struct {
-	SchDate *gtime.Time `json:schDate` //日期
+	SchDate  *gtime.Time `json:"schDate"`  // 日期
+	DataType string      `json:"dataType"` // 数据类型:(前后端约定)10 查询参与者,其他 查询组织者
 	request.PageReq
 }
 
 // GetScheduleRes  日程管理列表返回
 type GetScheduleRes struct {
-	SchTitle   string      `orm:"sch_title"    json:"schTitle"`   // 标题
-	SchContent string      `orm:"sch_ content" json:"schContent"` // 内容
-	SchDate    *gtime.Time `orm:"sch_ date"    json:"schDate"`    // 日期
-	UserName   string      `orm:"user_name"    json:"userName"`   // 用户姓名
-	BizType    string      `orm:"biz_type"     json:"bizType"`    // 关联业务类型
-	BizBillId  int         `orm:"biz_bill_id"  json:"bizBillId"`  // 关联业务单据
-	Remark     string      `orm:"remark"  json:"remark"`          // 备注
+	PlatSchedule
+	Attendees []*PlatScheduleAttendee `json:"attendees"` // 参与者
+}
+
+// UserBusyStatusReq 用户闲忙信息请求
+type UserBusyStatusReq struct {
+	BeginTime *gtime.Time `json:"beginTime"  v:"required#开始时间不能为空"` // 开始时间
+	EndTime   *gtime.Time `json:"endTime"    v:"required#结束时间不能为空"` // 结束时间
+	UserIds   []string    `json:"userIds"    v:"required#用户不能为空"`   // 用户unionId
+}
+
+// DingScheduleEvent 钉钉事件回调,接收参数
+type DingScheduleEvent struct {
+	ChangeType      string
+	EventType       string
+	CalendarEventId string
+	UnionIdList     []string
 }

+ 14 - 0
opms_parent/app/model/plat/plat_schedule_attendee.go

@@ -0,0 +1,14 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. Fill this file as you wish.
+// ==========================================================================
+
+package plat
+
+import (
+	"dashoo.cn/micro/app/model/plat/internal"
+)
+
+// PlatScheduleAttendee is the golang structure for table plat_schedule_attendee.
+type PlatScheduleAttendee internal.PlatScheduleAttendee
+
+// Fill with you ideas below.

+ 29 - 0
opms_parent/app/model/workflow/internal/plat_workflow.go

@@ -0,0 +1,29 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. DO NOT EDIT THIS FILE MANUALLY.
+// ==========================================================================
+
+package internal
+
+import (
+	"github.com/gogf/gf/os/gtime"
+)
+
+// PlatWorkflow is the golang structure for table plat_workflow.
+type PlatWorkflow struct {
+	Id              int         `orm:"id,primary"        json:"id"`              // 主键
+	BizCode         string      `orm:"biz_code"          json:"bizCode"`         // 业务单号
+	BizType         string      `orm:"biz_type"          json:"bizType"`         // 业务类型(10领用20项目创建30合同创建)
+	CurrentNode     string      `orm:"current_node"      json:"currentNode"`     // 当前节点
+	CurrentNodeTime string      `orm:"current_node_time" json:"currentNodeTime"` // 当前节点时间
+	ApprovalResult  string      `orm:"approval_result"   json:"approvalResult"`  // 审批结果
+	ProcessCode     string      `orm:"process_code"      json:"processCode"`     // 审批流编码
+	ProcessInstId   string      `orm:"process_inst_id"   json:"processInstId"`   // 审批实例ID
+	Remark          string      `orm:"remark"            json:"remark"`          // 备注
+	CreatedBy       int         `orm:"created_by"        json:"createdBy"`       // 创建者
+	CreatedName     string      `orm:"created_name"      json:"createdName"`     // 创建人
+	CreatedTime     *gtime.Time `orm:"created_time"      json:"createdTime"`     // 创建时间
+	UpdatedBy       int         `orm:"updated_by"        json:"updatedBy"`       // 更新者
+	UpdatedName     string      `orm:"updated_name"      json:"updatedName"`     // 更新人
+	UpdatedTime     *gtime.Time `orm:"updated_time"      json:"updatedTime"`     // 更新时间
+	DeletedTime     *gtime.Time `orm:"deleted_time"      json:"deletedTime"`     // 删除时间
+}

+ 30 - 0
opms_parent/app/model/workflow/plat_workflow.go

@@ -0,0 +1,30 @@
+// ==========================================================================
+// This is auto-generated by gf cli tool. Fill this file as you wish.
+// ==========================================================================
+
+package workflow
+
+import (
+	"dashoo.cn/micro/app/model/workflow/internal"
+	"dashoo.cn/opms_libary/request"
+)
+
+// BizType 业务类型(10领用20项目创建30合同创建)
+const (
+	CustomerReceive = "11" // 领取公海客户
+	ProjectCreate   = "20" // 项目创建
+	ContractCreate  = "30" // 合同创建
+)
+
+// PlatWorkflow is the golang structure for table plat_workflow.
+type PlatWorkflow internal.PlatWorkflow
+
+// Fill with you ideas below.
+
+// SearchWorkflowReq 查询
+type SearchWorkflowReq struct {
+	BizCode string `json:"bizCode"`
+	BizType string `son:"bizType"`
+	MySelf  string `json:"mySelf"` // 不为0时,查询自己的数据;为0时,查询全部数据
+	request.PageReq
+}

+ 12 - 1
opms_parent/app/service/base.go

@@ -1,9 +1,11 @@
 package service
 
 import (
-	"github.com/gogf/gf/os/gtime"
 	"log"
 	"reflect"
+
+	"github.com/gogf/gf/database/gdb"
+	"github.com/gogf/gf/os/gtime"
 )
 
 var (
@@ -12,6 +14,15 @@ var (
 	UpdateFieldEx       = []interface{}{"id", "created_by", "created_name", "created_time"}
 )
 
+
+func Sequence(db gdb.DB, name string) (string, error) {
+	v, err := db.GetValue("select `nextval`( ? );", name)
+	if err != nil {
+		return "", err
+	}
+	return v.String(), nil
+}
+
 // SetCreatedInfo 插入数据库时设置创建信息
 func SetCreatedInfo(entry interface{}, id int, name string) {
 	v := reflect.ValueOf(entry)

+ 7 - 0
opms_parent/app/service/context.go

@@ -63,6 +63,13 @@ func (c *ContextService) GetCxtUserName() string {
 	return c.CxtUser.NickName
 }
 
+func (c *ContextService) GetCxtUserDingtalkId() string {
+	if c.CxtUser == nil {
+		return "-1"
+	}
+	return c.CxtUser.DingtalkId
+}
+
 func (c *ContextService) GetCxtUserDeptId() int {
 	if c.CxtUser == nil {
 		return -1

+ 170 - 16
opms_parent/app/service/cust/cust_customer.go

@@ -6,19 +6,26 @@ import (
 	"fmt"
 	"math"
 	"strconv"
+	"strings"
 
 	"dashoo.cn/opms_libary/myerrors"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
+	"dashoo.cn/opms_libary/plugin/dingtalk/workflow"
 	"github.com/360EntSecGroup-Skylar/excelize"
 	"github.com/gogf/gf/database/gdb"
 	"github.com/gogf/gf/encoding/gjson"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/os/gtime"
 	"github.com/gogf/gf/util/gconv"
+	"github.com/mozillazg/go-pinyin"
 
 	"dashoo.cn/micro/app/dao/cust"
 	platdao "dashoo.cn/micro/app/dao/plat"
+	sysDao "dashoo.cn/micro/app/dao/sys"
 	model "dashoo.cn/micro/app/model/cust"
+	workflowModel "dashoo.cn/micro/app/model/workflow"
 	"dashoo.cn/micro/app/service"
+	workflowService "dashoo.cn/micro/app/service/workflow"
 )
 
 type CustomerService struct {
@@ -28,6 +35,7 @@ type CustomerService struct {
 	DynamicsDao    *cust.CustCustomerDynamicsDao
 	ContactDao     *cust.CustCustomerContactDao
 	FollowDao      *platdao.PlatFollowupDao
+	UserDao        *sysDao.SysUserDao
 	BelongServer   *CustomerbelongService
 	ContanctServer *CustomercontactService
 }
@@ -51,6 +59,7 @@ func NewCustomerService(ctx context.Context) (svc *CustomerService, err error) {
 	svc.FollowDao = platdao.NewPlatFollowupDao(svc.Tenant)
 	svc.BelongServer, _ = NewCustomerBelongService(ctx)
 	svc.ContanctServer, _ = NewCustomerContactService(ctx)
+	svc.UserDao = sysDao.NewSysUserDao(svc.Tenant)
 	return svc, nil
 }
 
@@ -107,6 +116,35 @@ func (s *CustomerService) GetList(req *model.CustCustomerSearchReq) (total int,
 	return
 }
 
+func (s *CustomerService) customerCode(province, industry string) (string, error) {
+	sequence, err := service.Sequence(s.Dao.DB, "customer_code")
+	if err != nil {
+		return "", err
+	}
+	return s.customerProvinceCode(province) + industry + sequence, nil
+}
+
+func (s *CustomerService) customerProvinceCode(province string) string {
+	province = strings.Trim(province, "市")
+	province = strings.Trim(province, "省")
+	province = strings.Trim(province, "特别行政区")
+	province = strings.Trim(province, "自治区")
+	province = strings.Trim(province, "壮族")
+	province = strings.Trim(province, "回族")
+	province = strings.Trim(province, "维吾尔")
+	pinyinArg := pinyin.NewArgs()
+	pinyinArg.Style = pinyin.FIRST_LETTER
+	provincePinyin := pinyin.Pinyin(province, pinyinArg)
+	provinceFirstletter := []string{}
+	for _, i := range provincePinyin {
+		provinceFirstletter = append(provinceFirstletter, i[0])
+	}
+	provinceCode := strings.Join(provinceFirstletter, "")
+	provinceCode = strings.ToUpper(provinceCode)
+	fmt.Println(provincePinyin, provinceFirstletter, provinceCode)
+	return provinceCode
+}
+
 // Create 创建客户
 func (s *CustomerService) Create(req *model.CustomerAddSeq) (insertId int64, err error) {
 	cusTomer := new(model.CustCustomer)
@@ -123,7 +161,11 @@ func (s *CustomerService) Create(req *model.CustomerAddSeq) (insertId int64, err
 		return
 	}
 	service.SetCreatedInfo(cusTomer, s.GetCxtUserId(), s.CxtUser.NickName)
-	cusTomer.CustCode = "CT" + strconv.Itoa(int(gtime.Timestamp()))
+	custCode, err := s.customerCode(req.CustProvince, req.CustIndustryCode)
+	if err != nil {
+		return 0, err
+	}
+	cusTomer.CustCode = custCode
 	cusTomer.CustStatus = "10"
 
 	roles := s.GetCxtUserRoles()
@@ -292,8 +334,130 @@ func (s *CustomerService) MoveToPubic(Ids []int64) (err error) {
 	return
 }
 
+var AssignCustomerRequestProcessCode = ""
+
+func ptrString(s string) *string {
+	return &s
+}
+
+// 领取客户申请
+func (s *CustomerService) AssignCustomerRequest(ctx context.Context, req *model.AssignCustomerReq) error {
+	data, err := s.Dao.Where("id in (?)", req.Ids).LockShared().All()
+	if err != nil {
+		return err
+	}
+	if len(data) == 0 {
+		return myerrors.TipsError("领取用户不能为空")
+	}
+
+	for _, v := range data {
+		if v.CustStatus != "10" {
+			return myerrors.TipsError(fmt.Sprintf("客户: %s 已被领取", v.CustName))
+		}
+	}
+
+	workflowSrv, err := workflowService.NewFlowService(ctx)
+	if err != nil {
+		return err
+	}
+	for _, u := range data {
+		bizCode := strconv.Itoa(u.Id) + ":" + strconv.Itoa(s.GetCxtUserId())
+		_, err = workflowSrv.StartProcessInstance(bizCode, "11", &workflow.StartProcessInstanceRequest{
+			ProcessCode: &AssignCustomerRequestProcessCode,
+			FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{
+				{
+					Name:  ptrString(""),
+					Value: ptrString(""),
+				},
+			},
+		})
+		if err != nil {
+			return err
+		}
+
+		_, err = s.Dao.Where("id = ?", u.Id).Data(map[string]interface{}{
+			"is_public":   noPublic,
+			"cust_status": "20",
+		}).Update()
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *CustomerService) AssignCustomerRequestApproval(flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error {
+	bizCode := strings.Split(flow.BizCode, ":")
+	if len(bizCode) != 2 {
+		return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	custId, err := strconv.Atoi(bizCode[0])
+	if err != nil {
+		return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	userId, err := strconv.Atoi(bizCode[1])
+	if err != nil {
+		return fmt.Errorf("客户领取审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	user, err := s.UserDao.Where("id = ?", userId).One()
+	if err != nil {
+		return err
+	}
+	if user == nil {
+		return fmt.Errorf("用户不存在:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	cust, err := s.Dao.Where("id = ?", custId).One()
+	if err != nil {
+		return err
+	}
+	if cust == nil {
+		return fmt.Errorf("客户不存在:%s Id: %d", flow.BizCode, flow.Id)
+	}
+
+	if msg.ProcessType != "finish" && msg.ProcessType != "terminate" {
+		return fmt.Errorf("无法识别的 ProcessType :%s", msg.ProcessType)
+	}
+	if msg.Result != "agree" && msg.Result != "refuse" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+
+	if msg.ProcessType == "terminate" {
+		_, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{
+			"is_public":   isPublic,
+			"cust_status": "10",
+		}).Update()
+		return err
+	}
+
+	pass := msg.Result == "agree"
+	if !pass {
+		_, err = s.Dao.Where("id = ?", custId).Data(map[string]interface{}{
+			"is_public":   isPublic,
+			"cust_status": "10",
+		}).Update()
+		return err
+	}
+
+	err = s.ChangeCustBelong([]int64{int64(custId)}, int64(userId), user.NickName)
+	if err != nil {
+		return err
+	}
+	s.BatchCreatebelong([]*model.CustCustomer{cust}, &model.AssignCustomerReq{
+		Ids:       []int64{int64(custId)},
+		SalesId:   int64(user.Id),
+		SalesName: user.NickName,
+		Remark:    "",
+		Receive:   Receive,
+	})
+	return nil
+}
+
 // AssignCustomer 分配客户
-func (s *CustomerService) AssignCustomer(req *model.AssignCustomerReq) (err error) {
+func (s *CustomerService) AssignCustomer(ctx context.Context, req *model.AssignCustomerReq) (err error) {
+	if req.Receive != "" {
+		return s.AssignCustomerRequest(ctx, req)
+	}
+
 	data, err := s.Dao.Where("id in (?)", req.Ids).LockShared().All()
 	if err != nil {
 		g.Log().Error(err)
@@ -412,6 +576,7 @@ func (s *CustomerService) TransCustomer(req *model.AssignCustomerReq) (err error
 // ChangeCustBelong 变更客户所属关系
 func (s *CustomerService) ChangeCustBelong(Ids []int64, salesId int64, salesName string) (err error) {
 	_, err = s.Dao.Data(g.Map{
+		"cust_status":  "30",
 		"sales_id":     salesId,
 		"is_public":    noPublic,
 		"sales_name":   salesName,
@@ -419,12 +584,7 @@ func (s *CustomerService) ChangeCustBelong(Ids []int64, salesId int64, salesName
 		"updated_name": s.GetCxtUserName(),
 		"updated_time": gtime.Now(),
 	}).Where("id in (?)", Ids).Update()
-
-	if err != nil {
-		g.Log().Error(err)
-		return
-	}
-	return
+	return err
 }
 
 // CreateDynamics 创建客户动态信息
@@ -568,8 +728,7 @@ func (s *CustomerService) BatchCreatebelong(rep []*model.CustCustomer, req *mode
 			"end_date":     gtime.Now(),
 		}).WhereIn(s.BelongDao.Columns.CustId, req.Ids).Update()
 	if err != nil {
-		g.Log().Error(err)
-		return
+		return err
 	}
 	var belongData []*model.CustCustomerBelong
 	userName := s.GetCxtUserName()
@@ -589,12 +748,7 @@ func (s *CustomerService) BatchCreatebelong(rep []*model.CustCustomer, req *mode
 		belongData = append(belongData, belong)
 	}
 	_, err = s.BelongDao.Insert(belongData)
-	if err != nil {
-		g.Log().Error(err)
-		return
-	}
-
-	return
+	return err
 }
 
 // 导出数据

+ 5 - 2
opms_parent/app/service/plat/plat_followup.go

@@ -3,7 +3,6 @@ package plat
 import (
 	"context"
 	"database/sql"
-	"fmt"
 	"strconv"
 	"strings"
 
@@ -99,7 +98,11 @@ func (s *followupService) Create(req *model.AddPlatFollowupReq) (err error) {
 		}
 	}
 	// 更新客户 最后跟进时间 字段
-	_, err = s.Dao.DB.Update("cust_customer", fmt.Sprintf("follow_up_date='%v'", req.FollowDate.Format("Y-m-d H:i:s")), fmt.Sprintf("id='%v' AND follow_up_date<'%v'", req.CustId, req.FollowDate.Format("Y-m-d H:i:s")))
+	toUpdate := map[string]interface{}{
+		"follow_up_date": req.FollowDate,
+		"follow_up_man":  s.GetCxtUserName(),
+	}
+	_, err = s.Dao.DB.Update("cust_customer", toUpdate, "id = ?", req.CustId)
 
 	return
 }

+ 266 - 12
opms_parent/app/service/plat/plat_schedule.go

@@ -2,15 +2,20 @@ package plat
 
 import (
 	"context"
-	"strings"
-
 	"dashoo.cn/opms_libary/myerrors"
-	"github.com/gogf/gf/frame/g"
+	"database/sql"
+	"fmt"
+	"github.com/gogf/gf/errors/gerror"
+	"github.com/gogf/gf/os/gtime"
 	"github.com/gogf/gf/util/gconv"
+	"strings"
 
 	"dashoo.cn/micro/app/dao/plat"
 	model "dashoo.cn/micro/app/model/plat"
 	"dashoo.cn/micro/app/service"
+	libary_dingtalk "dashoo.cn/opms_libary/plugin/dingtalk"
+	"dashoo.cn/opms_libary/plugin/dingtalk/calendar"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 )
 
 type scheduleService struct {
@@ -28,27 +33,73 @@ func NewScheduleService(ctx context.Context) (svc *scheduleService, err error) {
 	return svc, nil
 }
 
-//GetList 日程列表
+// GetList 日程列表  // 数据类型:(前后端约定)10 查询参与者,其他 查询组织者
 func (s *scheduleService) GetList(req *model.ScheduleSearchReq) (total int, scheduleList []*model.GetScheduleRes, err error) {
-	Model := s.Dao.M
-	Model = Model.Where("user_id = ", s.GetCxtUserId())
+	Model := s.Dao.M.InnerJoin("plat_schedule_attendee", "plat_schedule.id=plat_schedule_attendee.sch_id")
+	where := ""
+	if req.DataType == "10" {
+		where = fmt.Sprintf("plat_schedule_attendee.user_id=%v", s.GetCxtUserId())
+		Model = Model.Where("plat_schedule_attendee.user_id = ", s.GetCxtUserId())
+	} else {
+		where = fmt.Sprintf("plat_schedule.user_id=%v", s.GetCxtUserId())
+		Model = Model.Where("plat_schedule.user_id = ", s.GetCxtUserId())
+	}
 	if req.SchDate != nil {
 		schDate := gconv.String(req.SchDate)
 		begin := strings.Split(schDate, " ")[0] + " 00:00:00"
 		end := strings.Split(schDate, " ")[0] + " 23:59:59"
-		Model = Model.Where("sch_date >= ? AND sch_date <= ? ", begin, end)
+		Model = Model.Where("plat_schedule.sch_date >= ? AND plat_schedule.sch_date <= ? ", begin, end)
+		where += fmt.Sprintf(" AND plat_schedule.sch_date >= %v AND plat_schedule.sch_date <= %v", begin, end)
 	}
 	total, err = Model.Count()
 	if err != nil {
-		g.Log().Error(err)
-		err = myerrors.DbError("获取总行数失败。")
+		err = myerrors.DbError("获取总行数失败")
 		return
 	}
-	err = Model.Page(req.GetPage()).Order("id desc").Scan(&scheduleList)
+	err = Model.Page(req.GetPage()).Order("plat_schedule.id desc").Group("plat_schedule.id").Fields("plat_schedule.*").Scan(&scheduleList)
 	if err != nil {
-		g.Log().Error(err)
 		return
 	}
+
+	// 获取参与者信息
+	var attendees []*model.PlatScheduleAttendee
+	err = s.Dao.M.InnerJoin("plat_schedule_attendee", "plat_schedule.id=plat_schedule_attendee.sch_id").Order("plat_schedule_attendee.id ASC").Where(where).Fields("plat_schedule_attendee.*").Scan(&attendees)
+	if err == sql.ErrNoRows {
+		return total, scheduleList, nil
+	} else if err != nil {
+		return
+	}
+	// 组合参与者信息
+	for index, att := range attendees {
+		for schIndex, sch := range scheduleList {
+			if att.SchId == sch.Id {
+				scheduleList[schIndex].Attendees = append(scheduleList[schIndex].Attendees, attendees[index])
+				break
+			}
+		}
+	}
+	return
+}
+
+// GetUserBusyStatus 获取用户忙闲信息
+func (s *scheduleService) GetUserBusyStatus(req *model.UserBusyStatusReq) (UserBusyInfo interface{}, err error) {
+	// 构造请求参数
+	var param calendar.DingIsUserBusyParams
+	param.BeginTime = fmt.Sprintf("%vT%v+08:00", req.BeginTime.Format("Y-m-d"), req.BeginTime.Format("H:i:s"))
+	param.EndTime = fmt.Sprintf("%vT%v+08:00", req.EndTime.Format("Y-m-d"), req.EndTime.Format("H:i:s"))
+	param.UserIds = req.UserIds
+
+	param.UserId = s.GetCxtUserDingtalkId()
+
+	// 调用钉钉接口
+	client := libary_dingtalk.NewClient()
+	cal := client.GetCalendar()
+	returnData, err := cal.GetUserBusyStatus(&param)
+	if err != nil {
+		return 0, err
+	}
+
+	UserBusyInfo = returnData
 	return
 }
 
@@ -61,11 +112,214 @@ func (s *scheduleService) Create(req *model.AddScheduleSeq) (insertId int64, err
 	service.SetCreatedInfo(schedule, s.GetCxtUserId(), s.GetCxtUserName())
 	schedule.UserId = s.GetCxtUserId()
 	schedule.UserName = s.GetCxtUserName()
+
+	// 构造请求参数
+	var param calendar.DingAddScheduleParams
+	param.Summary = schedule.SchTitle
+	param.Description = schedule.SchContent
+	param.BeginTime = fmt.Sprintf("%vT%v+08:00", req.SchDate.Format("Y-m-d"), req.SchDate.Format("H:i:s"))
+	param.EndTime = fmt.Sprintf("%vT%v+08:00", req.SchDateEnd.Format("Y-m-d"), req.SchDateEnd.Format("H:i:s"))
+	param.UserIds = req.UserIds
+
+	param.UserId = s.GetCxtUserDingtalkId()
+
+	// 调用钉钉接口
+	client := libary_dingtalk.NewClient()
+	cal := client.GetCalendar()
+	returnData, err := cal.CreateSchedule(&param)
+	if err != nil {
+		return 0, err
+	}
+
+	// 将钉钉日程回填到系统中
+	schedule.DingScheduleId = *returnData.Id
 	insertId, err = s.Dao.InsertAndGetId(schedule)
 	if err != nil {
-		g.Log().Error(err)
 		return 0, err
 	}
+	// 回填 日程参与人的信息 到系统中
+	var attendees []*model.PlatScheduleAttendee
+	for _, att := range returnData.Attendees {
+		attendee := &model.PlatScheduleAttendee{
+			SchId:          int(insertId),
+			DingScheduleId: schedule.DingScheduleId,
+			UserId:         0, // TODO 用户id需要关联到此处
+			UserUnionId:    *att.Id,
+			UserName:       *att.DisplayName,
+		}
+		service.SetCreatedInfo(attendee, s.GetCxtUserId(), s.GetCxtUserName())
+		attendees = append(attendees, attendee)
+	}
+	_, err = s.Dao.DB.Model("plat_schedule_attendee").Save(attendees)
+
 	return
+}
+
+// SaveByDingEvent 保存日程信息
+func (s *scheduleService) SaveByDingEvent(msg *message.MixMessage) (err error) {
+	if msg.ChangeType == "cancelled" || msg.ChangeType == "deleteView" {
+		// 钉钉日程删除或者取消,本地日程需要删除
+		_, err = s.Dao.Delete("ding_schedule_id", msg.CalendarEventId)
+		return err
+	} else {
+		// 构造请求参数
+		var param calendar.DingGetScheduleParams
+		param.EventId = msg.CalendarEventId
+		param.UserId = msg.UnionIdList[0]
+
+		// 获取日程详情
+		// 调用钉钉接口
+		client := libary_dingtalk.NewClient()
+		cal := client.GetCalendar()
+		returnData, err := cal.GetScheduleByEventId(&param)
+		if err != nil {
+			return err
+		}
+		// 将日程数据回填
+		// 新建日程
+		if msg.ChangeType == "created" {
+			err = s.addScheduleByDing(returnData)
+			return err
+		} else if msg.ChangeType == "updated" {
+			// 获取系统中日程数据
+			schedule, err := s.Dao.Where("ding_schedule_id", msg.CalendarEventId).FindOne()
+			if err != nil {
+				// 查询不到数据时
+				if err == sql.ErrNoRows {
+					err = s.addScheduleByDing(returnData)
+					return err
+				} else {
+					return err
+				}
+			}
+			// 查询不到数据时
+			if schedule == nil {
+				err = s.addScheduleByDing(returnData)
+				return err
+			} else {
+				// 能查询到数据,则更新数据
+				updateScheduleByDing(returnData, schedule)
 
+				// 获取日程参与者
+				var attendees []*model.PlatScheduleAttendee
+				var saveAttendees []*model.PlatScheduleAttendee
+				err = s.Dao.DB.Model("plat_schedule_attendee").Where("sch_id", schedule.Id).Scan(&attendees)
+				if err != nil && err != sql.ErrNoRows {
+					return err
+				}
+				now := gtime.Now()
+				// 将attendees所有数据标记为删除
+				for index := range attendees {
+					attendees[index].DeletedTime = now
+				}
+				// 更新数据,并且将未删除的参与者的删除标记去掉
+				for _, dingSchedule := range returnData.Attendees {
+					isNew := true
+					for index, schedule := range attendees {
+						if schedule.UserUnionId == *dingSchedule.Id {
+							attendees[index].DeletedTime = nil
+							service.SetUpdatedInfo(attendees[index], 0, "钉钉回调参数修改")
+							saveAttendees = append(saveAttendees, attendees[index])
+							isNew = false
+							break
+						}
+					}
+					if isNew {
+						attendee := &model.PlatScheduleAttendee{
+							SchId:          schedule.Id,
+							DingScheduleId: schedule.DingScheduleId,
+							UserId:         0, // TODO 用户id需要关联到此处
+							UserUnionId:    *dingSchedule.Id,
+							UserName:       *dingSchedule.DisplayName,
+						}
+						service.SetCreatedInfo(attendee, 0, "钉钉回调参数创建")
+						service.SetUpdatedInfo(attendee, 0, "钉钉回调参数修改")
+						saveAttendees = append(saveAttendees, attendee)
+					}
+				}
+				// 保存删除数据
+				for index, att := range attendees {
+					if att.DeletedTime != nil {
+						saveAttendees = append(saveAttendees, attendees[index])
+					}
+				}
+				// 保存数据
+				_, err = s.Dao.Save(schedule)
+				if err != nil {
+					return err
+				}
+				// 保存参与人
+				_, err = s.Dao.DB.Model("plat_schedule_attendee").Save(saveAttendees)
+
+				return err
+			}
+		} else {
+			return gerror.New("未知的修改类型:" + msg.ChangeType)
+		}
+	}
+}
+
+// 根据钉钉数据创建日程
+func (s *scheduleService) addScheduleByDing(rsp *calendar.GetEventResponse) error {
+	var schedule model.PlatSchedule
+	service.SetCreatedInfo(&schedule, 0, "钉钉回调参数创建")
+
+	if rsp.Summary != nil {
+		schedule.SchTitle = *rsp.Summary
+	}
+	if rsp.Description != nil {
+		schedule.SchContent = *rsp.Description
+	}
+	if rsp.Start != nil && rsp.Start.DateTime != nil {
+		schedule.SchDate = gtime.NewFromStr(*rsp.Start.DateTime)
+	}
+	if rsp.End != nil && rsp.End.DateTime != nil {
+		schedule.SchDateEnd = gtime.NewFromStr(*rsp.End.DateTime)
+	}
+	schedule.UserId = 0 // TODO 获取系统的用户Id
+	schedule.UserUnionId = *rsp.Organizer.Id
+	schedule.UserName = *rsp.Organizer.DisplayName
+	schedule.DingScheduleId = *rsp.Id
+
+	insertId, err := s.Dao.InsertAndGetId(schedule)
+	if err != nil {
+		return err
+	}
+	// 回填 日程参与人的信息 到系统中
+	var attendees []*model.PlatScheduleAttendee
+	for _, att := range rsp.Attendees {
+		attendee := &model.PlatScheduleAttendee{
+			SchId:          int(insertId),
+			DingScheduleId: schedule.DingScheduleId,
+			UserId:         0, // TODO 用户id需要关联到此处
+			UserUnionId:    *att.Id,
+			UserName:       *att.DisplayName,
+		}
+		service.SetCreatedInfo(attendee, 0, "钉钉回调参数创建")
+		attendees = append(attendees, attendee)
+	}
+	_, err = s.Dao.DB.Model("plat_schedule_attendee").Save(attendees)
+
+	return err
+}
+
+// 根据钉钉数据,更新日程主表数据
+func updateScheduleByDing(rsp *calendar.GetEventResponse, schedule *model.PlatSchedule) {
+	service.SetUpdatedInfo(schedule, 0, "钉钉回调参数修改")
+
+	if rsp.Summary != nil {
+		schedule.SchTitle = *rsp.Summary
+	}
+	if rsp.Description != nil {
+		schedule.SchContent = *rsp.Description
+	}
+	if rsp.Start != nil && rsp.Start.DateTime != nil {
+		schedule.SchDate = gtime.NewFromStr(*rsp.Start.DateTime)
+	}
+	if rsp.End != nil && rsp.End.DateTime != nil {
+		schedule.SchDateEnd = gtime.NewFromStr(*rsp.End.DateTime)
+	}
+	schedule.UserId = 0 // TODO 获取系统的用户Id
+	schedule.UserUnionId = *rsp.Organizer.Id
+	schedule.UserName = *rsp.Organizer.DisplayName
 }

+ 127 - 0
opms_parent/app/service/workflow/work_flow.go

@@ -0,0 +1,127 @@
+package workflow
+
+import (
+	"context"
+	dao "dashoo.cn/micro/app/dao/workflow"
+	model "dashoo.cn/micro/app/model/workflow"
+	"dashoo.cn/micro/app/service"
+	"dashoo.cn/opms_libary/myerrors"
+	"dashoo.cn/opms_libary/plugin/dingtalk"
+	"dashoo.cn/opms_libary/plugin/dingtalk/workflow"
+	"dashoo.cn/opms_libary/utils"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gtime"
+)
+
+type workflowService struct {
+	*service.ContextService
+
+	Dao *dao.PlatWorkflowDao
+}
+
+func NewFlowService(ctx context.Context) (svc *workflowService, err error) {
+	svc = new(workflowService)
+	if svc.ContextService, err = svc.Init(ctx); err != nil {
+		return nil, err
+	}
+	svc.Dao = dao.NewPlatWorkflowDao(svc.Tenant)
+	return svc, nil
+}
+
+// GetProcessInstanceDetail 获取审批实例详情(钉钉接口)
+func (s *workflowService) GetProcessInstanceDetail(instId string) (*workflow.QueryProcessInstanceResponse, error) {
+	g.Log().Info("搜索值", instId)
+	// 调用钉钉接口
+	client := dingtalk.NewClient()
+	w := client.GetWorkflow()
+	resp, err := w.QueryProcessInstanceDetail(instId)
+	if err != nil {
+		return nil, err
+	}
+	return &resp, nil
+}
+
+// StartProcessInstance 发起一个新的审批流(钉钉接口,并回填到本地系统)
+// OriginatorUserId = utils.String("当前用户的钉钉中的Id")
+// ProcessCode = utils.String("每种审批对应固定的code")
+// bizType:业务类型(10领用20项目创建30合同创建
+// 详情参照文档:https://open.dingtalk.com/document/orgapp/create-an-approval-instance
+func (s *workflowService) StartProcessInstance(bizCode, bizType string, flow *workflow.StartProcessInstanceRequest) (insertId int64, err error) {
+	g.Log().Info("搜索值", flow)
+	// 参数调整
+	if flow.OriginatorUserId == nil {
+		// TODO s.GetCxtDingId() 目前写死
+		flow.OriginatorUserId = utils.String("2710120955840801")
+	}
+	// 调用钉钉接口
+	client := dingtalk.NewClient()
+	w := client.GetWorkflow()
+	resp, err := w.StartProcessInstance(flow)
+	if err != nil {
+		return 0, err
+	}
+
+	return s.saveWorkflowInstance(bizCode, bizType, resp.InstanceId)
+}
+
+// RevokeProcessInstance 撤销审批实例(钉钉接口)
+func (s *workflowService) RevokeProcessInstance(instId, remark string) (string, error) {
+	g.Log().Info("搜索值instId, remark:", instId, remark)
+	// 调用钉钉接口
+	client := dingtalk.NewClient()
+	w := client.GetWorkflow()
+	// TODO 获取当前用户的钉钉中的Id  s.GetCxtDingId()
+	resp, err := w.RevokeProcessInstance(instId, remark, "s.GetCxtDingId()")
+	if err != nil {
+		return "", err
+	}
+	return resp.InstanceId, nil
+}
+
+// 将审批实例同步到数据库中
+func (s *workflowService) saveWorkflowInstance(bizCode, bizType, instanceId string) (insertId int64, err error) {
+	now := gtime.Now()
+
+	// 构造基础数据
+	var instance model.PlatWorkflow
+	instance.BizCode = bizCode
+	instance.BizType = bizType
+	instance.CurrentNode = "新建"
+	instance.CurrentNodeTime = now.Format("Y-m-d H:i:s")
+	instance.ProcessInstId = instanceId
+	// 填充创建信息
+	service.SetCreatedInfo(&instance, s.GetCxtUserId(), s.GetCxtUserName())
+
+	// 保存数据并返回
+	return s.Dao.InsertAndGetId(instance)
+}
+
+// GetWorkflowInstance 获取实例(本地数据)
+func (s *workflowService) GetWorkflowInstance(instanceId string) (*model.PlatWorkflow, error) {
+	// 查询日程实例
+	return s.Dao.Where("process_inst_id", instanceId).FindOne()
+}
+
+// GetList 任务信息列表(本地数据)
+func (s *workflowService) GetList(req *model.SearchWorkflowReq) (total int, flowList []*model.PlatWorkflow, err error) {
+	flowModel := s.Dao.M
+	if req.BizCode != "" {
+		flowModel = flowModel.Where("biz_code", req.BizCode)
+	}
+	if req.BizType != "" {
+		flowModel = flowModel.Where("biz_type", req.BizType)
+	}
+	if req.MySelf != "0" {
+		flowModel = flowModel.Where("created_by", s.GetCxtUserId())
+	}
+
+	total, err = flowModel.Count()
+	if err != nil {
+		g.Log().Error(err)
+		err = myerrors.DbError("获取总行数失败。")
+		return
+	}
+
+	err = flowModel.Page(req.GetPage()).Order("plat_task.created_time DESC").Scan(&flowList)
+	return
+}

+ 1 - 0
opms_parent/go.mod

@@ -7,6 +7,7 @@ require (
 	dashoo.cn/opms_libary v0.0.0
 	github.com/360EntSecGroup-Skylar/excelize v1.4.1
 	github.com/gogf/gf v1.16.9
+	github.com/mozillazg/go-pinyin v0.19.0
 	github.com/shopspring/decimal v1.3.1
 	github.com/smallnest/rpcx v1.8.0
 	github.com/stretchr/testify v1.8.0 // indirect

+ 666 - 0
opms_parent/go.sum

@@ -0,0 +1,666 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+code.dashoo.cn/dashoo/micro_common_definition.git v0.0.0-20220507071646-3d858a040d5e h1:4q1IkhL0iuDqCqB0+BaPF6SAiCiA9OoITBzkb17rwlY=
+code.dashoo.cn/dashoo/micro_common_definition.git v0.0.0-20220507071646-3d858a040d5e/go.mod h1:f1tOgUBgwwoOGGtMLtpdt6i7ZfbEVB5/WOUp4bFj0NE=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
+github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/ChimeraCoder/gojson v1.1.0/go.mod h1:nYbTQlu6hv8PETM15J927yM0zGj3njIldp72UT1MqSw=
+github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
+github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alitto/pond v1.8.0 h1:/4wnAU0vOjhsUxOxjtXuNb59oh0J+Jjukf6gtkWpGJk=
+github.com/alitto/pond v1.8.0/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI=
+github.com/alphadose/itogami v0.0.0-20220705100819-134f04183c42/go.mod h1:QDsatlDSUJB4sXxZsJEpawGnTDwSdvX27ZXqtuZY3WA=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY=
+github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
+github.com/armon/go-metrics v0.4.0 h1:yCQqn7dwca4ITXb+CbubHmedzaQYHhNhrEXLYUeEe8Q=
+github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/cenk/backoff v2.2.1+incompatible h1:djdFT7f4gF2ttuzRKPbMOWgZajgesItGLwG5FTQKmmE=
+github.com/cenk/backoff v2.2.1+incompatible/go.mod h1:7FtoeaSnHoZnmZzz47cM35Y9nSW7tNyaidugnHTaFDE=
+github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
+github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
+github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-jump v0.0.0-20170409065014-e1f439676b57/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA=
+github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8sIQGw6ieKdz+BXjpccSOo9vXNl4U=
+github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/edwingeng/doublejump v1.0.0 h1:XW6QAFumXtbfNKXMsgkGYQXB4h14yxpbf/9zbqq72Lw=
+github.com/edwingeng/doublejump v1.0.0/go.mod h1:RzR28DQCDk+hOfZ9W+saaqrkEgOFKZagJSPnKLTQSkE=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
+github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
+github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
+github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
+github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
+github.com/go-redis/redis/v8 v8.8.2/go.mod h1:F7resOH5Kdug49Otu24RjHWwgK7u9AmtqWMnCV1iP5Y=
+github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
+github.com/go-redis/redis_rate/v9 v9.1.2/go.mod h1:oam2de2apSgRG8aJzwJddXbNu91Iyz1m8IKJE2vpvlQ=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/gogf/gf v1.16.4/go.mod h1:EjnxZXddTwfFoLPofDE3NokFWx+immofINtSyFCj280=
+github.com/gogf/gf v1.16.9 h1:Q803UmmRo59+Ws08sMVFOcd8oNpkSWL9vS33hlo/Cyk=
+github.com/gogf/gf v1.16.9/go.mod h1:8Q/kw05nlVRp+4vv7XASBsMe9L1tsVKiGoeP2AHnlkk=
+github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579/go.mod h1:52e6mXyNnHAsFrXrSnj5JPRSKsZKpHylVtA3j4AtMz8=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
+github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk=
+github.com/hashicorp/consul/api v1.13.0 h1:2hnLQ0GjQvw7f3O61jMO8gbasZviZTrt9R8WzgiirHc=
+github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
+github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
+github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-hclog v0.16.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw=
+github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
+github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
+github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
+github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
+github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
+github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
+github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
+github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
+github.com/hashicorp/serf v0.9.8 h1:JGklO/2Drf1QGa312EieQN3zhxQ+aJg6pG+aC3MFaVo=
+github.com/hashicorp/serf v0.9.8/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY=
+github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
+github.com/klauspost/reedsolomon v1.10.0/go.mod h1:qHMIzMkuZUWqIh8mS/GruPdo3u0qwX2jk/LH440ON7Y=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lucas-clemente/quic-go v0.28.0/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
+github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
+github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
+github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
+github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
+github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
+github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
+github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
+github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
+github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
+github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
+github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
+github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
+github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
+github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
+github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg=
+github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
+github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
+github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rpcxio/libkv v0.5.1 h1:M0/QqwTcdXz7us0NB+2i8Kq5+wikTm7zZ4Hyb/jNgME=
+github.com/rpcxio/libkv v0.5.1/go.mod h1:zHGgtLr3cFhGtbalum0BrMPOjhFZFJXCKiws/25ewls=
+github.com/rpcxio/rpcx-consul v0.0.0-20220730062257-1ff0472e730f h1:2lAWNSEM9KGGnHlkNjLGjsWPCxHOCQJmRRD1iCFraE4=
+github.com/rpcxio/rpcx-consul v0.0.0-20220730062257-1ff0472e730f/go.mod h1:N4SjBS0M9HpVdq3CIIXm/MwWv6euXQ39ybhfH2iTh1c=
+github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
+github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
+github.com/rubyist/circuitbreaker v2.2.1+incompatible h1:KUKd/pV8Geg77+8LNDwdow6rVCAYOp8+kHUyFvL6Mhk=
+github.com/rubyist/circuitbreaker v2.2.1+incompatible/go.mod h1:Ycs3JgJADPuzJDwffe12k6BZT8hxVi6lFK+gWYJLN4A=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smallnest/quick v0.0.0-20220703133648-f13409fa6c67/go.mod h1:0UHAX9lwPR4gsKF94tv4fQIDIF+POCyixbrBp/zK6To=
+github.com/smallnest/rpcx v1.7.5/go.mod h1:xAsj679fx0xUWWy2RfiC6YSZLgWwBRZIu3WEbnsK/hA=
+github.com/smallnest/rpcx v1.8.0 h1:pZVSdEFXK8I+HNUg9GDLeJJVGXaMjFEFuDEUwbSYC8Y=
+github.com/smallnest/rpcx v1.8.0/go.mod h1:7TvEqNQKPHhVuY9EExeP8q8QpJXDiKwnDrKwxSqvdhE=
+github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
+github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
+github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
+github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
+github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
+github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
+github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
+github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
+github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
+github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
+go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
+go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
+go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
+go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
+go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
+go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
+go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs=
+go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
+go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
+golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
+golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
+golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
+golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
+google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

+ 9 - 2
opms_parent/main.go

@@ -2,9 +2,10 @@ package main
 
 import (
 	"context"
-	"net/http"
-
+	"dashoo.cn/micro/app/handler/dingtalk"
+	"dashoo.cn/micro/app/handler/workflow"
 	"dashoo.cn/opms_libary/myerrors"
+	"net/http"
 
 	"dashoo.cn/opms_libary/micro_srv"
 	"github.com/gogf/gf/frame/g"
@@ -52,6 +53,10 @@ func main() {
 	s.RegisterName("CtrContractInvoice", new(contract.CtrContractInvoice), "")
 	s.RegisterName("WorkOrder", new(work.WorkOrderHandler), "")
 	s.RegisterName("Schedule", new(plat.ScheduleHeader), "")
+	s.RegisterName("PlatWorkflow", new(workflow.PlatWorkflowHandler), "")
+
+	// 钉钉回调接口
+	s.RegisterName("DingEvent", new(dingtalk.DingHandler), "")
 
 	// 注册服务对象
 	//s.RegisterName("Auth", new(handler.Auth), "")
@@ -77,12 +82,14 @@ var AuthExcludePaths = []string{
 	"/Organize/GetOrgTreeList",
 	"/User/SendResetPasswordEmail",
 	"/User/ResetPasswordFromEmail",
+	"/DingEvent/CallBack",
 }
 
 // 处理Auth
 func handleAuth(ctx context.Context, req *protocol.Message, token string) error {
 	if g.Config().GetBool("setting.fake-auth-for-swagger") {
 		req.Metadata["userInfo"] = `{"createTime":796,"data":{"id":1,"uuid":"","userName":"admin","nickName":"系统管理员","deptId":1,"roles":""},"refreshTime":43200796,"tenant":"default","userKey":"admin","uuid":"9b7ac56a5b2b4d12e52277e9601dbaf1"}`
+		req.Metadata["authExclude"] = "false"
 		return nil
 	}
 

+ 35 - 0
opms_parent/schema/customer.sql

@@ -0,0 +1,35 @@
+CREATE TABLE `cust_customer` (
+    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `cust_code` varchar(32) NOT NULL COMMENT '客户编号',
+    `cust_name` varchar(90) NOT NULL COMMENT '客户名称',
+    `abbr_name` varchar(90) NOT NULL COMMENT '助记名',
+    `cust_dist_code` int(11) NOT NULL COMMENT ' 客户所在省级ID',
+    `cust_location` varchar(32) DEFAULT NULL COMMENT '所在地区',
+    `cust_address` varchar(255) DEFAULT NULL COMMENT '详细地址',
+    `cust_province_id` INT(11) COMMENT '所在省ID',
+    `cust_province` VARCHAR(90) COMMENT '所在省',
+    `cust_city_id` INT(11) COMMENT '所在市ID',
+    `cust_city` VARCHAR(90) COMMENT '所在市',
+    `cust_region_id` INT(11) COMMENT '所在区县ID',
+    `cust_region` VARCHAR(90) COMMENT '所在区县',
+    `cust_industry` varchar(4) DEFAULT NULL COMMENT '客户行业',
+    `cust_level` varchar(4) DEFAULT NULL COMMENT '客户级别(10 重点客户 20 普通客户 30非优客户)',
+    `cust_status` VARCHAR(4) DEFAULT 10 COMMENT '客户状态(10 待领取 20 领取审批 30 已领取)',
+    `cust_source` varchar(20) DEFAULT NULL COMMENT '客户来源',
+    `is_public` varchar(4) DEFAULT NULL COMMENT '公海客户(10是20否)',
+    `dept_id` int(11) DEFAULT NULL COMMENT '所属部门ID',
+    `dept_name` varchar(90) DEFAULT NULL COMMENT '所属部门',
+    `sales_id` int(11) DEFAULT NULL COMMENT '所属销售ID',
+    `sales_name` varchar(90) DEFAULT NULL COMMENT '所属销售',
+    `follow_up_date` datetime DEFAULT NULL COMMENT '最后跟进时间',
+    `follow_up_man` VARCHAR(90) COMMENT '最后跟进人',
+    `remark` text DEFAULT NULL COMMENT '备注',
+    `created_by` int(11) NOT NULL COMMENT '创建者',
+    `created_name` varchar(90) NOT NULL COMMENT '创建人',
+    `created_time` datetime NOT NULL COMMENT '创建时间',
+    `updated_by` int(11) DEFAULT NULL COMMENT '更新者',
+    `updated_name` varchar(90) DEFAULT NULL COMMENT '更新人',
+    `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
+    `deleted_time` datetime DEFAULT NULL COMMENT '删除时间',
+    PRIMARY KEY (`id`)
+) ENGINE = InnoDB AUTO_INCREMENT = 118 DEFAULT CHARSET = utf8mb4 COMMENT = '客户信息'

+ 28 - 0
opms_parent/schema/function.sql

@@ -0,0 +1,28 @@
+CREATE TABLE `plat_sequence` (
+    `name` varchar(32) NOT NULL COMMENT '序列编码',
+    `current_value` int(11) DEFAULT 1000 COMMENT '当前值/初始值',
+    `increment` int(11) DEFAULT NULL COMMENT '步增值',
+    `remark` text DEFAULT NULL COMMENT '备注',
+    `created_by` int(11) NOT NULL COMMENT '创建者',
+    `created_name` varchar(90) NOT NULL COMMENT '创建人',
+    `created_time` datetime NOT NULL COMMENT '创建时间',
+    `updated_by` int(11) DEFAULT NULL COMMENT '更新者',
+    `updated_name` varchar(90) DEFAULT NULL COMMENT '更新人',
+    `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
+    `deleted_time` datetime DEFAULT NULL COMMENT '删除时间',
+    PRIMARY KEY (`name`) USING BTREE
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '序列信息表' ;
+
+
+DROP FUNCTION IF EXISTS nextval;
+DELIMITER $ 
+CREATE FUNCTION nextval (seq_name VARCHAR(50)) RETURNS INTEGER LANGUAGE SQL DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER COMMENT '' BEGIN
+UPDATE plat_sequence
+SET current_value = current_value + increment
+WHERE name = seq_name;
+RETURN currval(seq_name);
+END $ 
+DELIMITER ;
+
+-- INSERT INTO plat_sequence VALUES ('customer_code', 0, 1, '', 1000, '系统管理员', '2023-02-09 10:27:42', null, null, null, null);
+-- select `nextval`('customer_code');

+ 8 - 0
opms_parent/schema/tmp.sql

@@ -0,0 +1,8 @@
+alter table cust_customer add `cust_province_id` INT(11) COMMENT '所在省ID' after cust_address;
+alter table cust_customer add `cust_province` VARCHAR(90) COMMENT '所在省' after cust_province_id;
+alter table cust_customer add `cust_city_id` INT(11) COMMENT '所在市ID' after cust_province;
+alter table cust_customer add `cust_city` VARCHAR(90) COMMENT '所在市' after cust_city_id;
+alter table cust_customer add `cust_region_id` INT(11) COMMENT '所在区县ID' after cust_city;
+alter table cust_customer add `cust_region` VARCHAR(90) COMMENT '所在区县' after cust_region_id;
+alter table cust_customer modify `cust_status` VARCHAR(4) DEFAULT 10 COMMENT '客户状态(10 待领取 20 领取审批 30 已领取)';
+alter table cust_customer add `follow_up_man` VARCHAR(90) COMMENT '最后跟进人' after follow_up_date;