Browse Source

feature: 代理商经销商添加钉钉审批

liuyaqi 2 years ago
parent
commit
3c15013f11

+ 5 - 2
opms_libary/plugin/dingtalk/client_test.go

@@ -13,8 +13,11 @@ func TestQuerySchemaByProcessCode(t *testing.T) {
 	client := NewClient()
 
 	w := client.GetWorkflow()
-	s, _ := w.QuerySchemaByProcessCode("PROC-E5A8B695-A6AF-49CF-9909-9A31C33A1211")
-
+	// s, _ := w.QuerySchemaByProcessCode("PROC-E5A8B695-A6AF-49CF-9909-9A31C33A1211")
+	s, _ := w.QuerySchemaByProcessCode("PROC-33514974-4E38-430F-A852-D5694944F20B")
+	
+	// s, _ := w.QuerySchemaByProcessCode("PROC-9494B87D-DE96-49EE-B676-D3913911BE21")
+	
 	fmt.Println(gconv.String(s))
 }
 

+ 12 - 4
opms_parent/app/dao/base/internal/base_distributor.go

@@ -8,14 +8,13 @@ import (
 	"context"
 	"database/sql"
 	"fmt"
-	"strings"
-	"time"
-
 	"github.com/gogf/gf/container/garray"
 	"github.com/gogf/gf/database/gdb"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/frame/gmvc"
 	"github.com/gogf/gf/util/gconv"
+	"strings"
+	"time"
 
 	model "dashoo.cn/micro/app/model/base"
 )
@@ -57,6 +56,9 @@ type baseDistributorColumns struct {
 	ProxyEndTime     string // 代理签约有效期结束(代理商)
 	ProxyDistrict    string // 授权代理区域(代理商)
 	ContractUrl      string // 代理合同(代理商)
+	ApproItem        string // 审批项名称: 创建代理商 经销商转代理商 代理商续签 代理商转经销商
+	ApproData        string // 审核数据
+	ApproStatus      string // 审核状态 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销
 	Remark           string // 备注
 	CreatedBy        string // 创建者
 	CreatedName      string // 创建人
@@ -100,6 +102,9 @@ var (
 			ProxyEndTime:     "proxy_end_time",
 			ProxyDistrict:    "proxy_district",
 			ContractUrl:      "contract_url",
+			ApproItem:        "appro_item",
+			ApproData:        "appro_data",
+			ApproStatus:      "appro_status",
 			Remark:           "remark",
 			CreatedBy:        "created_by",
 			CreatedName:      "created_name",
@@ -145,6 +150,9 @@ func NewBaseDistributorDao(tenant string) BaseDistributorDao {
 			ProxyEndTime:     "proxy_end_time",
 			ProxyDistrict:    "proxy_district",
 			ContractUrl:      "contract_url",
+			ApproItem:        "appro_item",
+			ApproData:        "appro_data",
+			ApproStatus:      "appro_status",
 			Remark:           "remark",
 			CreatedBy:        "created_by",
 			CreatedName:      "created_name",
@@ -872,7 +880,7 @@ func (d *BaseDistributorDao) checkColumnsName(dataScope map[string]interface{},
 	delete(dataScope, "roles")
 	delete(dataScope, "posts")
 	delete(dataScope, bigColumns)
-	if specialFlag && len(args) == 1 {
+	if len(colsContrast) > 0 {
 		for k, v := range dataScope {
 			if data, ok := colsContrast[k]; ok {
 				dataScope[data.(string)] = v

+ 2 - 2
opms_parent/app/handler/base/distributor.go

@@ -24,7 +24,7 @@ func (p *DistributorHandler) GetList(ctx context.Context, req *model.BaseDistrib
 
 		return err
 	}
-	total, list, err := distributorServer.GetList(req)
+	total, list, err := distributorServer.GetList(ctx, req)
 	if err != nil {
 
 		return err
@@ -42,7 +42,7 @@ func (p *DistributorHandler) Create(ctx context.Context, req *model.AddDistribut
 	if err != nil {
 		return err
 	}
-	_, err = distributorServer.Create(req)
+	_, err = distributorServer.Create(ctx, req)
 	if err != nil {
 		return myerrors.CreateError(err, "经销商")
 	}

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

@@ -3,6 +3,7 @@ package dingtalk
 import (
 	"context"
 	model "dashoo.cn/micro/app/model/workflow"
+	baseSrv "dashoo.cn/micro/app/service/base"
 	contractSrv "dashoo.cn/micro/app/service/contract"
 	custServer "dashoo.cn/micro/app/service/cust"
 	"dashoo.cn/micro/app/service/dingtalk_log"
@@ -183,6 +184,62 @@ func (h *DingHandler) handleBpmsInstanceChange(msg *message.MixMessage, ctx *din
 			return err.Error()
 		}
 		return "success"
+	case model.DistProxyCreate:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			err = baseSrv.ApprovalProxyCreate(ctx.SubsMessage.Ctx, instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+		}
+		err = s.Update(instance, msg)
+		if err != nil {
+			glog.Error(err)
+			return err.Error()
+		}
+		return "success"
+	case model.DistToProxy:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			err = baseSrv.ApprovalDistToProxy(ctx.SubsMessage.Ctx, instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+		}
+		err = s.Update(instance, msg)
+		if err != nil {
+			glog.Error(err)
+			return err.Error()
+		}
+		return "success"
+	case model.DistProxyRenew:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			err = baseSrv.ApprovalDistRenew(ctx.SubsMessage.Ctx, instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+		}
+		err = s.Update(instance, msg)
+		if err != nil {
+			glog.Error(err)
+			return err.Error()
+		}
+		return "success"
+	case model.DistToDist:
+		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
+			err = baseSrv.ApprovalDistToDist(ctx.SubsMessage.Ctx, instance, msg)
+			if err != nil {
+				glog.Error(err)
+				return err.Error()
+			}
+		}
+		err = s.Update(instance, msg)
+		if err != nil {
+			glog.Error(err)
+			return err.Error()
+		}
+		return "success"
 	case model.ProjectCreate:
 		if msg.ProcessType == "finish" || msg.ProcessType == "terminate" {
 			srv, err := projService.NewBusinessService(ctx.SubsMessage.Ctx)

+ 25 - 6
opms_parent/app/model/base/base_distributor.go

@@ -62,18 +62,37 @@ type AddDistributor struct {
 	ProxyEndTime     *gtime.Time `json:"proxyEndTime"`                                        // 代理签约有效期结束(代理商)
 	ProxyDistrict    string      `json:"proxyDistrict"`                                       // 授权代理区域(代理商)
 	ContractUrl      string      `json:"contractUrl"`                                         // 代理合同(代理商)
+	ContractFileName string      `json:"contracFileName"`                                     // 代理合同(代理商)
 }
 
 type DistributorToProxyReq struct {
-	Id             int         `p:"id" json:"id" v:"required# id不能为空"`
-	CustomerType   string      `json:"customerType" v:"required#授权客户类型不能为空"`    // 授权客户类型
-	ProxyStartTime *gtime.Time `json:"proxyStartTime" v:"required#代理签约有效期不能为空"` // 代理签约有效期开始(代理商)
-	ProxyEndTime   *gtime.Time `json:"proxyEndTime" v:"required#代理签约有效期不能为空"`   // 代理签约有效期结束(代理商)
-	ProxyDistrict  string      `json:"proxyDistrict" v:"required#授权代理区域不能为空"`   // 授权代理区域(代理商)
-	ContractUrl    string      `json:"contractUrl"`                             // 代理合同(代理商)
+	Id               int         `p:"id" json:"id" v:"required# id不能为空"`
+	CustomerType     string      `json:"customerType" v:"required#授权客户类型不能为空"`    // 授权客户类型
+	ProxyStartTime   *gtime.Time `json:"proxyStartTime" v:"required#代理签约有效期不能为空"` // 代理签约有效期开始(代理商)
+	ProxyEndTime     *gtime.Time `json:"proxyEndTime" v:"required#代理签约有效期不能为空"`   // 代理签约有效期结束(代理商)
+	ProxyDistrict    string      `json:"proxyDistrict" v:"required#授权代理区域不能为空"`   // 授权代理区域(代理商)
+	ContractUrl      string      `json:"contractUrl"`                             // 代理合同(代理商)
+	ContractFileName string      `json:"contracFileName"`                         // 代理合同(代理商)
+}
+
+type ToProxyApproveData struct {
+	CustomerType   string      `json:"customerType"`
+	ProxyStartTime *gtime.Time `json:"proxyStartTime"`
+	ProxyEndTime   *gtime.Time `json:"proxyEndTime"`
+	ProxyDistrict  string      `json:"proxyDistrict"`
+	ContractUrl    string      `json:"contractUrl"`
+	OperatedId     int         `json:"operatedId"`
+	OperatedName   string      `json:"operatedName"`
+}
+
+type ToDistApproveData struct {
+	ToDistReason string `json:"customerType"` // 转移经销商原因
+	OperatedId   int    `json:"operatedId"`
+	OperatedName string `json:"operatedName"`
 }
 
 type DistributorRenewReq DistributorToProxyReq
+type RenewApproveData ToProxyApproveData
 
 type DistributorToDistReq struct {
 	Id           int    `p:"id" json:"id" v:"required# id不能为空"`

+ 3 - 0
opms_parent/app/model/base/internal/base_distributor.go

@@ -36,6 +36,9 @@ type BaseDistributor struct {
 	ProxyEndTime     *gtime.Time `orm:"proxy_end_time"    json:"proxyEndTime"`     // 代理签约有效期结束(代理商)
 	ProxyDistrict    string      `orm:"proxy_district"    json:"proxyDistrict"`    // 授权代理区域(代理商)
 	ContractUrl      string      `orm:"contract_url"      json:"contractUrl"`      // 代理合同(代理商)
+	ApproItem        string      `orm:"appro_item"        json:"approItem"`        // 审批项名称: 创建代理商 经销商转代理商 代理商续签 代理商转经销商
+	ApproData        string      `orm:"appro_data"        json:"approData"`        // 审核数据
+	ApproStatus      string      `orm:"appro_status"      json:"approStatus"`      // 审核状态 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销
 	Remark           string      `orm:"remark"            json:"remark"`           // 备注
 	CreatedBy        int         `orm:"created_by"        json:"createdBy"`        // 创建者
 	CreatedName      string      `orm:"created_name"      json:"createdName"`      // 创建人

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

@@ -23,6 +23,10 @@ const (
 	ContractCreate   = "30" // 合同创建
 	ContractInvoice  = "31" // 申请发票
 	PlatTaskApproval = "40" // 督办任务审批
+	DistProxyCreate  = "51" // 创建代理商
+	DistToProxy      = "52" // 经销商转代理商
+	DistProxyRenew   = "53" // 代理商续签
+	DistToDist       = "54" // 代理商转经销商
 )
 
 // PlatWorkflow is the golang structure for table plat_workflow.

+ 737 - 111
opms_parent/app/service/base/base_distributor.go

@@ -5,10 +5,17 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"path"
+	"strconv"
+	"strings"
 	"time"
 
+	"dashoo.cn/opms_libary/micro_srv"
 	"dashoo.cn/opms_libary/myerrors"
-	"github.com/gogf/gf/container/garray"
+	"dashoo.cn/opms_libary/plugin/dingtalk"
 	"github.com/gogf/gf/database/gdb"
 	"github.com/gogf/gf/frame/g"
 	"github.com/gogf/gf/os/gtime"
@@ -21,7 +28,12 @@ import (
 	model "dashoo.cn/micro/app/model/base"
 	contractmodel "dashoo.cn/micro/app/model/contract"
 	projmodel "dashoo.cn/micro/app/model/proj"
+	workflowmodel "dashoo.cn/micro/app/model/workflow"
 	"dashoo.cn/micro/app/service"
+	workflowService "dashoo.cn/micro/app/service/workflow"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
+	"dashoo.cn/opms_libary/plugin/dingtalk/workflow"
+	"dashoo.cn/opms_libary/utils"
 )
 
 type distributorService struct {
@@ -50,12 +62,9 @@ func NewDistributorService(ctx context.Context) (svc *distributorService, err er
 }
 
 // GetList 经销商信息列表
-func (s *distributorService) GetList(req *model.BaseDistributorSearchReq) (total int, distributorList []*model.BaseDistributorListRsp, err error) {
+func (s *distributorService) GetList(ctx context.Context, req *model.BaseDistributorSearchReq) (total int, distributorList []*model.BaseDistributorListRsp, err error) {
 	distributorModel := s.Dao.FieldsEx(s.Dao.C.DeletedTime)
-	// 用户仅有销售工程师角色展示自己的数据,其他人可以看到所有数据
-	if garray.NewStrArrayFrom(s.CxtUser.Roles, true).Contains("SalesEngineer") {
-		distributorModel = distributorModel.WhereIn("belong_sale_id", s.DataScope["userIds"])
-	}
+	distributorModel = distributorModel.DataScope(ctx, "belong_sale_id")
 	if req.DistCode != "" {
 		distributorModel = distributorModel.WhereLike(s.Dao.C.DistCode, "%"+req.DistCode+"%")
 	}
@@ -139,25 +148,114 @@ func (s *distributorService) getDistributorCode(distCode string) (string, error)
 }
 
 // Create 经销商创建
-func (s *distributorService) Create(req *model.AddDistributor) (lastId int64, err error) {
+func (s *distributorService) Create(ctx context.Context, req *model.AddDistributor) (int64, error) {
 	DistributorData := new(model.BaseDistributor)
-	if err = gconv.Struct(req, DistributorData); err != nil {
-		return
+	if err := gconv.Struct(req, DistributorData); err != nil {
+		return 0, err
 	}
-	DistributorData.DistCode, err = s.getDistributorCode("JXS")
+	code, err := s.getDistributorCode("JXS")
 	if err != nil {
 		return 0, err
 	}
+	DistributorData.DistCode = code
+	if DistributorData.DistType == "20" {
+		DistributorData.ApproItem = "创建代理商"
+		DistributorData.ApproStatus = "20"
+		if req.ContractUrl != "" && req.ContractFileName == "" {
+			return 0, myerrors.TipsError("合同文件名不能为空")
+		}
+	}
 	service.SetCreatedInfo(DistributorData, s.GetCxtUserId(), s.GetCxtUserName())
-	lastId, err = s.Dao.InsertAndGetId(DistributorData)
-	if err != nil {
-		return 0, err
+	var id int64
+	txerr := s.Dao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
+		id, err = tx.InsertAndGetId("base_distributor", DistributorData)
+		if err != nil {
+			return err
+		}
+		DistributorData.Id = int(id)
+		if DistributorData.DistType == "20" {
+			err = s.createDingtalkProcess(ctx, DistributorData, req.ContractFileName)
+			if err != nil {
+				return err
+			}
+		}
+		err = s.AddDynamicsByCurrentUser(tx, int(id), "创建经销商/代理商", map[string]interface{}{})
+		return err
+	})
+	return id, txerr
+}
+
+var ProcessCodeDistProxyCreate = "PROC-9494B87D-DE96-49EE-B676-D3913911BE21" // 创建代理商
+
+func (s *distributorService) createDingtalkProcess(ctx context.Context, ent *model.BaseDistributor, contractFileName string) error {
+	var fileinfoByte []byte
+	if ent.ContractUrl != "" {
+		var err error
+		fileinfoByte, err = UploadDingtalk(s.CxtUser.DingtalkId, ent.ContractUrl, contractFileName)
+		if err != nil {
+			return err
+		}
 	}
-	err = s.Dao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
-		err = s.AddDynamicsByCurrentUser(tx, int(lastId), "创建经销商/代理商", map[string]interface{}{})
+
+	workflowSrv, err := workflowService.NewFlowService(ctx)
+	if err != nil {
 		return err
+	}
+	bizCode := strconv.Itoa(ent.Id)
+	form := []*workflow.StartProcessInstanceRequestFormComponentValues{
+		{
+			Id:    utils.String("TextField-K2AD4O5B"),
+			Name:  utils.String("代理商名称"),
+			Value: utils.String(ent.DistName),
+		},
+		{
+			Id:    utils.String("TextField_DZ2HNXRKHCG"),
+			Name:  utils.String("所在省"),
+			Value: utils.String(ent.ProvinceDesc),
+		},
+		{
+			Id:    utils.String("TextField_1M07EV7YVOPS0"),
+			Name:  utils.String("业务范围"),
+			Value: utils.String(ent.BusinessScope),
+		},
+		{
+			Id:    utils.String("TextField_UGJ0UKCU5DS0"),
+			Name:  utils.String("注册资金(万元)"),
+			Value: utils.String(strconv.FormatFloat(ent.Capital, 'f', 2, 64)),
+		},
+		{
+			Id:    utils.String("TextField_1RC4FO1WUZ4W0"),
+			Name:  utils.String("注册地"),
+			Value: utils.String(ent.RegisterDistrict),
+		},
+		{
+			Id:    utils.String("TextField_FKM6LUQYLFS0"),
+			Name:  utils.String("现有销售人数"),
+			Value: utils.String(strconv.Itoa(ent.SaleNum)),
+		},
+		{
+			Id:    utils.String("TextField_1L89WCJM3ZMO0"),
+			Name:  utils.String("已有代理名牌和产品"),
+			Value: utils.String(ent.ExistedProduct),
+		},
+		{
+			Id:    utils.String("TextField_P5JA5OZ5XKW0"),
+			Name:  utils.String("历史合作的终端客户名称"),
+			Value: utils.String(ent.HistoryCustomer),
+		},
+	}
+	if len(fileinfoByte) != 0 {
+		form = append(form, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("DDAttachment_SD6QFFBOSAO0"),
+			Name:  utils.String("代理合同"),
+			Value: utils.String(string(fileinfoByte)),
+		})
+	}
+	_, err = workflowSrv.StartProcessInstance(bizCode, workflowmodel.DistProxyCreate, "", &workflow.StartProcessInstanceRequest{
+		ProcessCode:         &ProcessCodeDistProxyCreate,
+		FormComponentValues: form,
 	})
-	return
+	return err
 }
 
 // GetEntityById 详情
@@ -184,15 +282,19 @@ func (s *distributorService) GetEntityById(id int64) (distributorInfo *model.Bas
 
 // UpdateById 修改数据
 func (s *distributorService) UpdateById(req *model.UpdateDistributorReq) (err error) {
-	count, err := s.Dao.Where("id = ", req.Id).Count()
+	ent, err := s.Dao.Where("id = ", req.Id).One()
 	if err != nil {
 		g.Log().Error(err)
 		return
 	}
-	if count == 0 {
+	if ent == nil {
 		err = myerrors.TipsError("无修改数据")
 		return
 	}
+	if ent.ApproStatus == "20" {
+		err = myerrors.TipsError("不能修改待审核数据")
+		return
+	}
 
 	distData := new(model.BaseDistributor)
 	if err = gconv.Struct(req, distData); err != nil {
@@ -217,6 +319,9 @@ func (s *distributorService) ToProxy(ctx context.Context, req *model.Distributor
 	if validErr != nil {
 		return myerrors.TipsError(validErr.Current().Error())
 	}
+	if req.ContractUrl != "" && req.ContractFileName == "" {
+		return myerrors.TipsError("合同文件名不能为空")
+	}
 
 	ent, err := s.Dao.Where("id = ?", req.Id).One()
 	if err != nil {
@@ -225,49 +330,102 @@ func (s *distributorService) ToProxy(ctx context.Context, req *model.Distributor
 	if ent == nil {
 		return myerrors.TipsError(fmt.Sprintf("代理商/经销商不存在: %d", req.Id))
 	}
+	approvalData := model.ToProxyApproveData{
+		CustomerType:   req.CustomerType,
+		ProxyStartTime: req.ProxyStartTime,
+		ProxyEndTime:   req.ProxyEndTime,
+		ProxyDistrict:  req.ProxyDistrict,
+		ContractUrl:    req.ContractUrl,
+		OperatedId:     s.CxtUser.Id,
+		OperatedName:   s.CxtUser.NickName,
+	}
+	approvalDataByte, err := json.Marshal(approvalData)
+	if err != nil {
+		return err
+	}
 
-	txerr := s.Dao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+	txerr := s.Dao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
 		_, err = tx.Update("base_distributor", map[string]interface{}{
-			"dist_type":        "20",
-			"customer_type":    req.CustomerType,
-			"proxy_start_time": req.ProxyStartTime,
-			"proxy_end_time":   req.ProxyEndTime,
-			"proxy_district":   req.ProxyDistrict,
-			"contract_url":     req.ContractUrl,
+			"appro_status": "20",
+			"appro_data":   string(approvalDataByte),
+			"appro_item":   "经销商转代理商",
 		}, "id = ?", req.Id)
 		if err != nil {
 			return err
 		}
-		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
-			DistId:          req.Id,
-			DistType:        "20",
-			BusinessScope:   ent.BusinessScope,
-			CustomerType:    req.CustomerType,
-			ProxyDistrict:   req.ProxyDistrict,
-			ProxyStartTime:  req.ProxyStartTime,
-			ProxyEndTime:    req.ProxyEndTime,
-			ContractUrl:     req.ContractUrl,
-			ExistedProduct:  ent.ExistedProduct,
-			HistoryCustomer: ent.HistoryCustomer,
-			ToDistReason:    "",
-			Remark:          "",
-			CreatedBy:       s.GetCxtUserId(),
-			CreatedName:     s.GetCxtUserName(),
-			CreatedTime:     gtime.Now(),
-			UpdatedBy:       s.GetCxtUserId(),
-			UpdatedName:     s.GetCxtUserName(),
-			UpdatedTime:     gtime.Now(),
-		})
+		return s.toProxyDingtalkProcess(ctx, ent, req)
+	})
+	return txerr
+}
+
+var ProcessCodeDistToProxy = "PROC-39C96165-131C-48EC-B8C0-AA528E0C9F3A" // 经销商转代理商
+func (s *distributorService) toProxyDingtalkProcess(ctx context.Context, ent *model.BaseDistributor, req *model.DistributorToProxyReq) error {
+	var fileinfoByte []byte
+	if req.ContractUrl != "" {
+		var err error
+		fileinfoByte, err = UploadDingtalk(s.CxtUser.DingtalkId, req.ContractUrl, req.ContractFileName)
 		if err != nil {
 			return err
 		}
-		err = s.AddDynamicsByCurrentUser(tx, req.Id, "转为代理商", map[string]interface{}{})
-		if err != nil {
-			return err
+	}
+
+	cusTypeMap, err := service.GetDictDataByType(ctx, "cust_idy")
+	if err != nil {
+		return err
+	}
+	cusType := []string{}
+	for _, t := range strings.Split(req.CustomerType, ",") {
+		if v := cusTypeMap[t]; v != "" {
+			cusType = append(cusType, v)
 		}
-		return nil
+	}
+	custTypeByte, err := json.Marshal(cusType)
+	if err != nil {
+		return err
+	}
+
+	proxyTime := req.ProxyStartTime.Time.Format("2006/01/02") + "-" +
+		req.ProxyEndTime.Time.Format("2006/01/02")
+
+	workflowSrv, err := workflowService.NewFlowService(ctx)
+	if err != nil {
+		return err
+	}
+	form := []*workflow.StartProcessInstanceRequestFormComponentValues{
+		{
+			Id:    utils.String("TextField-K2AD4O5B"),
+			Name:  utils.String("代理商名称"),
+			Value: utils.String(ent.DistName),
+		},
+		{
+			Id:    utils.String("DDSelectField_X3ALRAZA4BK0"),
+			Name:  utils.String("授权客户类型"),
+			Value: utils.String(string(custTypeByte)),
+		},
+		{
+			Id:    utils.String("TextField_1M07EV7YVOPS0"),
+			Name:  utils.String("授权代理区域"),
+			Value: utils.String(req.ProxyDistrict),
+		},
+		{
+			Id:    utils.String("TextField_UGJ0UKCU5DS0"),
+			Name:  utils.String("代理签约有效期"),
+			Value: utils.String(proxyTime),
+		},
+	}
+	if len(fileinfoByte) != 0 {
+		form = append(form, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("DDAttachment_SD6QFFBOSAO0"),
+			Name:  utils.String("代理合同"),
+			Value: utils.String(string(fileinfoByte)),
+		})
+	}
+	bizCode := strconv.Itoa(ent.Id)
+	_, err = workflowSrv.StartProcessInstance(bizCode, workflowmodel.DistToProxy, "", &workflow.StartProcessInstanceRequest{
+		ProcessCode:         &ProcessCodeDistToProxy,
+		FormComponentValues: form,
 	})
-	return txerr
+	return err
 }
 
 func (s *distributorService) Renew(ctx context.Context, req *model.DistributorRenewReq) (err error) {
@@ -275,6 +433,9 @@ func (s *distributorService) Renew(ctx context.Context, req *model.DistributorRe
 	if validErr != nil {
 		return myerrors.TipsError(validErr.Current().Error())
 	}
+	if req.ContractUrl != "" && req.ContractFileName == "" {
+		return myerrors.TipsError("合同文件名不能为空")
+	}
 
 	ent, err := s.Dao.Where("id = ?", req.Id).One()
 	if err != nil {
@@ -284,47 +445,102 @@ func (s *distributorService) Renew(ctx context.Context, req *model.DistributorRe
 		return myerrors.TipsError(fmt.Sprintf("代理商/经销商不存在: %d", req.Id))
 	}
 
-	txerr := s.Dao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+	approvalData := model.RenewApproveData{
+		CustomerType:   req.CustomerType,
+		ProxyStartTime: req.ProxyStartTime,
+		ProxyEndTime:   req.ProxyEndTime,
+		ProxyDistrict:  req.ProxyDistrict,
+		ContractUrl:    req.ContractUrl,
+		OperatedId:     s.CxtUser.Id,
+		OperatedName:   s.CxtUser.NickName,
+	}
+	approvalDataByte, err := json.Marshal(approvalData)
+	if err != nil {
+		return err
+	}
+
+	txerr := s.Dao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
 		_, err = tx.Update("base_distributor", map[string]interface{}{
-			"customer_type":    req.CustomerType,
-			"proxy_start_time": req.ProxyStartTime,
-			"proxy_end_time":   req.ProxyEndTime,
-			"proxy_district":   req.ProxyDistrict,
-			"contract_url":     req.ContractUrl,
+			"appro_status": "20",
+			"appro_data":   string(approvalDataByte),
+			"appro_item":   "代理商续签",
 		}, "id = ?", req.Id)
 		if err != nil {
 			return err
 		}
-		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
-			DistId:          req.Id,
-			DistType:        "20",
-			BusinessScope:   ent.BusinessScope,
-			CustomerType:    req.CustomerType,
-			ProxyDistrict:   req.ProxyDistrict,
-			ProxyStartTime:  req.ProxyStartTime,
-			ProxyEndTime:    req.ProxyEndTime,
-			ContractUrl:     req.ContractUrl,
-			ExistedProduct:  ent.ExistedProduct,
-			HistoryCustomer: ent.HistoryCustomer,
-			ToDistReason:    "",
-			Remark:          "",
-			CreatedBy:       s.GetCxtUserId(),
-			CreatedName:     s.GetCxtUserName(),
-			CreatedTime:     gtime.Now(),
-			UpdatedBy:       s.GetCxtUserId(),
-			UpdatedName:     s.GetCxtUserName(),
-			UpdatedTime:     gtime.Now(),
-		})
+		return s.renewDingtalkProcess(ctx, ent, req)
+	})
+	return txerr
+}
+
+var ProcessCodeDistRenew = "PROC-33514974-4E38-430F-A852-D5694944F20B" // 代理商续签
+func (s *distributorService) renewDingtalkProcess(ctx context.Context, ent *model.BaseDistributor, req *model.DistributorRenewReq) error {
+	var fileinfoByte []byte
+	if req.ContractUrl != "" {
+		var err error
+		fileinfoByte, err = UploadDingtalk(s.CxtUser.DingtalkId, req.ContractUrl, req.ContractFileName)
 		if err != nil {
 			return err
 		}
-		err = s.AddDynamicsByCurrentUser(tx, req.Id, "续签代理商", map[string]interface{}{})
-		if err != nil {
-			return err
+	}
+
+	cusTypeMap, err := service.GetDictDataByType(ctx, "cust_idy")
+	if err != nil {
+		return err
+	}
+	cusType := []string{}
+	for _, t := range strings.Split(req.CustomerType, ",") {
+		if v := cusTypeMap[t]; v != "" {
+			cusType = append(cusType, v)
 		}
-		return nil
+	}
+	custTypeByte, err := json.Marshal(cusType)
+	if err != nil {
+		return err
+	}
+
+	proxyTime := req.ProxyStartTime.Time.Format("2006/01/02") + "-" +
+		req.ProxyEndTime.Time.Format("2006/01/02")
+
+	workflowSrv, err := workflowService.NewFlowService(ctx)
+	if err != nil {
+		return err
+	}
+	form := []*workflow.StartProcessInstanceRequestFormComponentValues{
+		{
+			Id:    utils.String("TextField-K2AD4O5B"),
+			Name:  utils.String("代理商名称"),
+			Value: utils.String(ent.DistName),
+		},
+		{
+			Id:    utils.String("DDSelectField_X3ALRAZA4BK0"),
+			Name:  utils.String("授权客户类型"),
+			Value: utils.String(string(custTypeByte)),
+		},
+		{
+			Id:    utils.String("TextField_1M07EV7YVOPS0"),
+			Name:  utils.String("授权代理区域"),
+			Value: utils.String(req.ProxyDistrict),
+		},
+		{
+			Id:    utils.String("TextField_UGJ0UKCU5DS0"),
+			Name:  utils.String("代理签约有效期"),
+			Value: utils.String(proxyTime),
+		},
+	}
+	if len(fileinfoByte) != 0 {
+		form = append(form, &workflow.StartProcessInstanceRequestFormComponentValues{
+			Id:    utils.String("DDAttachment_SD6QFFBOSAO0"),
+			Name:  utils.String("代理合同"),
+			Value: utils.String(string(fileinfoByte)),
+		})
+	}
+	bizCode := strconv.Itoa(ent.Id)
+	_, err = workflowSrv.StartProcessInstance(bizCode, workflowmodel.DistProxyRenew, "", &workflow.StartProcessInstanceRequest{
+		ProcessCode:         &ProcessCodeDistRenew,
+		FormComponentValues: form,
 	})
-	return txerr
+	return err
 }
 
 func (s *distributorService) ToDist(ctx context.Context, req *model.DistributorToDistReq) (err error) {
@@ -341,45 +557,56 @@ func (s *distributorService) ToDist(ctx context.Context, req *model.DistributorT
 		return myerrors.TipsError(fmt.Sprintf("代理商/经销商不存在: %d", req.Id))
 	}
 
-	txerr := s.Dao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+	approvalData := model.ToDistApproveData{
+		ToDistReason: req.ToDistReason,
+		OperatedId:   s.CxtUser.Id,
+		OperatedName: s.CxtUser.NickName,
+	}
+	approvalDataByte, err := json.Marshal(approvalData)
+	if err != nil {
+		return err
+	}
+
+	txerr := s.Dao.DB.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
 		_, err = tx.Update("base_distributor", map[string]interface{}{
-			"dist_type": "10",
+			"appro_status": "20",
+			"appro_data":   string(approvalDataByte),
+			"appro_item":   "代理商转经销商",
 		}, "id = ?", req.Id)
 		if err != nil {
 			return err
 		}
-		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
-			DistId:          req.Id,
-			DistType:        "10",
-			BusinessScope:   ent.BusinessScope,
-			CustomerType:    ent.CustomerType,
-			ProxyDistrict:   "",
-			ProxyStartTime:  nil,
-			ProxyEndTime:    nil,
-			ContractUrl:     "",
-			ExistedProduct:  ent.ExistedProduct,
-			HistoryCustomer: ent.HistoryCustomer,
-			ToDistReason:    req.ToDistReason,
-			Remark:          "",
-			CreatedBy:       s.GetCxtUserId(),
-			CreatedName:     s.GetCxtUserName(),
-			CreatedTime:     gtime.Now(),
-			UpdatedBy:       s.GetCxtUserId(),
-			UpdatedName:     s.GetCxtUserName(),
-			UpdatedTime:     gtime.Now(),
-		})
-		if err != nil {
-			return err
-		}
-		err = s.AddDynamicsByCurrentUser(tx, req.Id, "转为经销商", map[string]interface{}{})
-		if err != nil {
-			return err
-		}
-		return nil
+		return s.toDistDingtalkProcess(ctx, ent, req)
 	})
 	return txerr
 }
 
+var ProcessCodeProxyToDist = "PROC-59A0E9F5-8233-41D5-B36D-021F3E8B48D2" // 代理商转经销商
+func (s *distributorService) toDistDingtalkProcess(ctx context.Context, ent *model.BaseDistributor, req *model.DistributorToDistReq) error {
+	workflowSrv, err := workflowService.NewFlowService(ctx)
+	if err != nil {
+		return err
+	}
+	form := []*workflow.StartProcessInstanceRequestFormComponentValues{
+		{
+			Id:    utils.String("TextField-K2AD4O5B"),
+			Name:  utils.String("代理商名称"),
+			Value: utils.String(ent.DistName),
+		},
+		{
+			Id:    utils.String("TextareaField_5XMN9CGTDAS0"),
+			Name:  utils.String("转移原因"),
+			Value: utils.String(req.ToDistReason),
+		},
+	}
+	bizCode := strconv.Itoa(ent.Id)
+	_, err = workflowSrv.StartProcessInstance(bizCode, workflowmodel.DistToDist, "", &workflow.StartProcessInstanceRequest{
+		ProcessCode:         &ProcessCodeProxyToDist,
+		FormComponentValues: form,
+	})
+	return err
+}
+
 func (s *distributorService) TransRecord(ctx context.Context, req *model.DistributorTransRecordReq) (int, []*model.BaseDistributorRecord, error) {
 	dao := s.RecordDao.As("a")
 	if req.DistId != 0 {
@@ -620,3 +847,402 @@ func (s distributorService) DynamicsList(ctx context.Context, req *model.Distrib
 	}
 	return total, ret, err
 }
+
+func DownFile(url string) ([]byte, error) {
+	r, err := http.Get(url)
+	if err != nil {
+		return nil, err
+	}
+	if r.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("DownFile from %s StatusCode %d", url, r.StatusCode)
+	}
+	defer r.Body.Close()
+	return ioutil.ReadAll(r.Body)
+}
+
+// '审核状态 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销'
+func ApprovalProxyCreate(ctx context.Context, flow *workflowmodel.PlatWorkflow, msg *message.MixMessage) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return fmt.Errorf("获取租户码异常:%s", err.Error())
+	}
+	distdao := base.NewBaseDistributorDao(tenant)
+
+	entId, err := strconv.Atoi(flow.BizCode)
+	if err != nil {
+		return fmt.Errorf("创建代理商钉钉审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	ent, err := distdao.Where("id = ?", entId).One()
+	if err != nil {
+		return err
+	}
+	if ent == 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" && msg.Result != "" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+
+	if msg.ProcessType == "terminate" {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "50",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	pass := msg.Result == "agree"
+	if !pass {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "40",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+		"appro_status": "30",
+		"appro_data":   "",
+	}).Update()
+	return err
+}
+
+func ApprovalDistToProxy(ctx context.Context, flow *workflowmodel.PlatWorkflow, msg *message.MixMessage) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return fmt.Errorf("获取租户码异常:%s", err.Error())
+	}
+	distdao := base.NewBaseDistributorDao(tenant)
+
+	entId, err := strconv.Atoi(flow.BizCode)
+	if err != nil {
+		return fmt.Errorf("经销商转代理商钉钉审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	ent, err := distdao.Where("id = ?", entId).One()
+	if err != nil {
+		return err
+	}
+	if ent == 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" && msg.Result != "" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+
+	if msg.ProcessType == "terminate" {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "50",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	pass := msg.Result == "agree"
+	if !pass {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "40",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	approData := model.ToProxyApproveData{}
+	err = json.Unmarshal([]byte(ent.ApproData), &approData)
+	if err != nil {
+		return fmt.Errorf("经销商转代理商钉钉审批 ApproData 解析异常:%s Id: %d ApproData:%s", flow.BizCode, flow.Id, ent.ApproData)
+	}
+
+	txerr := distdao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+		_, err = tx.Update("base_distributor", map[string]interface{}{
+			"dist_type":        "20",
+			"appro_status":     "30",
+			"appro_data":       "",
+			"customer_type":    approData.CustomerType,
+			"proxy_start_time": approData.ProxyStartTime,
+			"proxy_end_time":   approData.ProxyEndTime,
+			"proxy_district":   approData.ProxyDistrict,
+			"contract_url":     approData.ContractUrl,
+		}, "id = ?", ent.Id)
+		if err != nil {
+			return err
+		}
+		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
+			DistId:          ent.Id,
+			DistType:        "20",
+			BusinessScope:   ent.BusinessScope,
+			CustomerType:    approData.CustomerType,
+			ProxyDistrict:   approData.ProxyDistrict,
+			ProxyStartTime:  approData.ProxyStartTime,
+			ProxyEndTime:    approData.ProxyEndTime,
+			ContractUrl:     approData.ContractUrl,
+			ExistedProduct:  ent.ExistedProduct,
+			HistoryCustomer: ent.HistoryCustomer,
+			ToDistReason:    "",
+			Remark:          "",
+			CreatedBy:       approData.OperatedId,
+			CreatedName:     approData.OperatedName,
+			CreatedTime:     gtime.Now(),
+			UpdatedBy:       approData.OperatedId,
+			UpdatedName:     approData.OperatedName,
+			UpdatedTime:     gtime.Now(),
+		})
+		if err != nil {
+			return err
+		}
+		err = addDynamicsByCurrentUser(tx, ent.Id, "转为代理商",
+			approData, approData.OperatedId, approData.OperatedName)
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	return txerr
+}
+
+func ApprovalDistRenew(ctx context.Context, flow *workflowmodel.PlatWorkflow, msg *message.MixMessage) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return fmt.Errorf("获取租户码异常:%s", err.Error())
+	}
+	distdao := base.NewBaseDistributorDao(tenant)
+
+	entId, err := strconv.Atoi(flow.BizCode)
+	if err != nil {
+		return fmt.Errorf("代理商续签钉钉审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	ent, err := distdao.Where("id = ?", entId).One()
+	if err != nil {
+		return err
+	}
+	if ent == 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" && msg.Result != "" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+
+	if msg.ProcessType == "terminate" {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "50",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	pass := msg.Result == "agree"
+	if !pass {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "40",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	approData := model.RenewApproveData{}
+	err = json.Unmarshal([]byte(ent.ApproData), &approData)
+	if err != nil {
+		return fmt.Errorf("代理商续签钉钉审批 ApproData 解析异常:%s Id: %d ApproData:%s", flow.BizCode, flow.Id, ent.ApproData)
+	}
+
+	txerr := distdao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+		_, err = tx.Update("base_distributor", map[string]interface{}{
+			"appro_status":     "30",
+			"appro_data":       "",
+			"customer_type":    approData.CustomerType,
+			"proxy_start_time": approData.ProxyStartTime,
+			"proxy_end_time":   approData.ProxyEndTime,
+			"proxy_district":   approData.ProxyDistrict,
+			"contract_url":     approData.ContractUrl,
+		}, "id = ?", ent.Id)
+		if err != nil {
+			return err
+		}
+		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
+			DistId:          ent.Id,
+			DistType:        "20",
+			BusinessScope:   ent.BusinessScope,
+			CustomerType:    approData.CustomerType,
+			ProxyDistrict:   approData.ProxyDistrict,
+			ProxyStartTime:  approData.ProxyStartTime,
+			ProxyEndTime:    approData.ProxyEndTime,
+			ContractUrl:     approData.ContractUrl,
+			ExistedProduct:  ent.ExistedProduct,
+			HistoryCustomer: ent.HistoryCustomer,
+			ToDistReason:    "",
+			Remark:          "",
+			CreatedBy:       approData.OperatedId,
+			CreatedName:     approData.OperatedName,
+			CreatedTime:     gtime.Now(),
+			UpdatedBy:       approData.OperatedId,
+			UpdatedName:     approData.OperatedName,
+			UpdatedTime:     gtime.Now(),
+		})
+		if err != nil {
+			return err
+		}
+		err = addDynamicsByCurrentUser(tx, ent.Id, "续签代理商",
+			approData, approData.OperatedId, approData.OperatedName)
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	return txerr
+}
+
+func ApprovalDistToDist(ctx context.Context, flow *workflowmodel.PlatWorkflow, msg *message.MixMessage) error {
+	tenant, err := micro_srv.GetTenant(ctx)
+	if err != nil {
+		return fmt.Errorf("获取租户码异常:%s", err.Error())
+	}
+	distdao := base.NewBaseDistributorDao(tenant)
+
+	entId, err := strconv.Atoi(flow.BizCode)
+	if err != nil {
+		return fmt.Errorf("代理商转经销商钉钉审批 bizCode 不合法:%s Id: %d", flow.BizCode, flow.Id)
+	}
+	ent, err := distdao.Where("id = ?", entId).One()
+	if err != nil {
+		return err
+	}
+	if ent == 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" && msg.Result != "" {
+		return fmt.Errorf("无法识别的 Result :%s", msg.Result)
+	}
+
+	if msg.ProcessType == "terminate" {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "50",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	pass := msg.Result == "agree"
+	if !pass {
+		_, err = distdao.Where("id = ?", entId).Data(map[string]interface{}{
+			"appro_status": "40",
+			"appro_data":   "",
+		}).Update()
+		return err
+	}
+
+	approData := model.ToDistApproveData{}
+	err = json.Unmarshal([]byte(ent.ApproData), &approData)
+	if err != nil {
+		return fmt.Errorf("代理商转经销商钉钉审批 ApproData 解析异常:%s Id: %d ApproData:%s", flow.BizCode, flow.Id, ent.ApproData)
+	}
+
+	txerr := distdao.DB.Transaction(context.TODO(), func(ctx context.Context, tx *gdb.TX) error {
+		_, err = tx.Update("base_distributor", map[string]interface{}{
+			"dist_type":    "10",
+			"appro_status": "30",
+			"appro_data":   "",
+		}, "id = ?", ent.Id)
+		if err != nil {
+			return err
+		}
+		_, err = tx.Insert("base_distributor_record", model.BaseDistributorRecord{
+			DistId:          ent.Id,
+			DistType:        "10",
+			BusinessScope:   ent.BusinessScope,
+			CustomerType:    ent.CustomerType,
+			ProxyDistrict:   "",
+			ProxyStartTime:  nil,
+			ProxyEndTime:    nil,
+			ContractUrl:     "",
+			ExistedProduct:  ent.ExistedProduct,
+			HistoryCustomer: ent.HistoryCustomer,
+			ToDistReason:    approData.ToDistReason,
+			Remark:          "",
+			CreatedBy:       approData.OperatedId,
+			CreatedName:     approData.OperatedName,
+			CreatedTime:     gtime.Now(),
+			UpdatedBy:       approData.OperatedId,
+			UpdatedName:     approData.OperatedName,
+			UpdatedTime:     gtime.Now(),
+		})
+		if err != nil {
+			return err
+		}
+		err = addDynamicsByCurrentUser(tx, ent.Id, "转为经销商",
+			approData, approData.OperatedId, approData.OperatedName)
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	return txerr
+}
+
+func addDynamicsByCurrentUser(tx *gdb.TX, distId int, opnType string, content interface{}, operatedId int, opreatedName string) error {
+	contentByte, err := json.Marshal(content)
+	if err != nil {
+		return err
+	}
+	_, err = tx.InsertAndGetId("base_distributor_dynamics", model.BaseDistributorDynamics{
+		DistId:      distId,
+		OpnPeopleId: operatedId,
+		OpnPeople:   opreatedName,
+		OpnDate:     gtime.Now(),
+		OpnType:     opnType,
+		OpnContent:  string(contentByte),
+		Remark:      "",
+		CreatedBy:   operatedId,
+		CreatedName: opreatedName,
+		CreatedTime: gtime.Now(),
+		UpdatedBy:   operatedId,
+		UpdatedName: opreatedName,
+		UpdatedTime: gtime.Now(),
+	})
+	return err
+}
+
+func UploadDingtalk(uid, url, name string) ([]byte, error) {
+	if uid == "" {
+		return nil, fmt.Errorf("该用户钉钉 uid 为空")
+	}
+	filedata, err := DownFile(url)
+	if err != nil {
+		return nil, err
+	}
+	filepath := path.Join(os.TempDir(), name)
+	err = ioutil.WriteFile(filepath, filedata, 0644)
+	if err != nil {
+		return nil, fmt.Errorf("WriteFile %s %s", filepath, err)
+	}
+	defer os.Remove(filepath)
+
+	resp, err := dingtalk.Client.GetStorage().UploadFile(service.DingTalkSpaceId, uid, name, filepath)
+	if err != nil {
+		return nil, fmt.Errorf("钉钉上传文件异常 %s", err.Error())
+	}
+	file := []contractmodel.DingFileInfo{
+		{
+			SpaceId:  resp.Dentry.SpaceId,
+			FileId:   resp.Dentry.Id,
+			FileName: resp.Dentry.Name,
+			FileSize: resp.Dentry.Size,
+			FileType: resp.Dentry.Extension,
+		},
+	}
+	return json.Marshal(file)
+}

+ 41 - 0
opms_parent/app/service/base/base_distributor_test.go

@@ -0,0 +1,41 @@
+package base
+
+import (
+	"context"
+	"testing"
+
+	modelWorkflow "dashoo.cn/micro/app/model/workflow"
+	"dashoo.cn/opms_libary/plugin/dingtalk/message"
+	"github.com/gogf/gf/frame/g"
+	"github.com/smallnest/rpcx/share"
+)
+
+
+var testTenant = "default"
+
+func fakeCtx(parent context.Context, tenant string) context.Context {
+	ctx := context.WithValue(parent, share.ReqMetaDataKey, map[string]string{
+		"tenant": tenant,
+	})
+	return ctx
+}
+
+
+func TestApprovalProxyCreate(t *testing.T) {
+	ctx := fakeCtx(context.Background(), testTenant)
+
+	instance := modelWorkflow.PlatWorkflow{}
+	err := g.DB(testTenant).Table("plat_workflow").Where("id = ?", 155).Struct(&instance)
+	if err != nil {
+		panic(err)
+	}
+	err = ApprovalDistToProxy(ctx, &instance, &message.MixMessage{
+		ProcessType: "finish",
+		// ProcessType: "terminate",
+		Result: "agree",
+		// Result: "refuse",
+	})
+	if err != nil {
+		panic(err)
+	}
+}

+ 2 - 0
opms_parent/schema/distributor.sql

@@ -25,6 +25,8 @@ CREATE TABLE `base_distributor` (
   `proxy_end_time` datetime DEFAULT NULL COMMENT '代理签约有效期结束(代理商)',
   `proxy_district` varchar(255) DEFAULT NULL COMMENT '授权代理区域(代理商)',
   `contract_url` text DEFAULT NULL COMMENT '代理合同(代理商)',
+  `appro_item` varchar(255) COMMENT '审批项名称: 创建代理商 经销商转代理商 代理商续签 代理商转经销商',
+  `appro_status` varchar(4) COMMENT '审核状态 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销',
   `remark` text DEFAULT NULL COMMENT '备注',
   `created_by` int(11) NOT NULL COMMENT '创建者',
   `created_name` varchar(90) NOT NULL COMMENT '创建人',

+ 4 - 0
opms_parent/schema/tmp.sql

@@ -125,3 +125,7 @@ CREATE TABLE `base_distributor_target` (
   PRIMARY KEY (`id`) USING BTREE
 ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMMENT = '代理商业务指标';
 -- alter table base_distributor_contact modify `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键';
+
+alter table base_distributor add `appro_item` varchar(255) COMMENT '审批项名称: 创建代理商 经销商转代理商 代理商续签 代理商转经销商' after contract_url
+alter table base_distributor add `appro_data` text COMMENT '审核数据' after appro_item
+alter table base_distributor add `appro_status` varchar(4) COMMENT '审核状态 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销' after appro_data