浏览代码

feature: 添加钉钉文件下载

liuyaqi 2 年之前
父节点
当前提交
8c0920a2da

+ 1 - 0
opms_libary/plugin/dingtalk/base/base.go

@@ -120,6 +120,7 @@ func PostJSON(url string, obj interface{}, token string) ([]byte, error) {
 	}
 	defer response.Body.Close()
 
+	fmt.Println(response.StatusCode, response.Status)
 	if response.StatusCode != http.StatusOK {
 		return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", url, response.StatusCode)
 	}

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

@@ -126,6 +126,16 @@ type QueryFileUploadInfoResponse struct {
 	} `json:"headerSignatureInfo"`
 }
 
+type QueryFileDownloadInfoResponse struct {
+	Protocol            string `json:"protocol"`
+	HeaderSignatureInfo struct {
+		ResourceUrls      []string          `json:"resourceUrls"`
+		Headers           map[string]string `json:"headers"`
+		ExpirationSeconds int               `json:"expirationSeconds"`
+		Region            string            `json:"region"`
+	} `json:"headerSignatureInfo"`
+}
+
 type CommitFileRequestOptionAppProperties struct {
 	// 属性名称 该属性名称在当前app下需要保证唯一,不同app间同名属性互不影响
 	Name *string `json:"name,omitempty" xml:"name,omitempty"`

+ 19 - 11
opms_libary/plugin/dingtalk/storage/storage.go

@@ -15,9 +15,10 @@ import (
 )
 
 const (
-	AddPermissionUrl        = "/v1.0/storage/spaces/%s/dentries/0/permissions"  //添加权限
-	QueryFileUploadInfoUrl  = "/v1.0/storage/spaces/%s/files/uploadInfos/query" //获取文件上传信息
-	CommitFileUploadInfoUrl = "/v1.0/storage/spaces/%s/files/commit"            //提交文件
+	AddPermissionUrl         = "/v1.0/storage/spaces/%s/dentries/0/permissions"          //添加权限
+	QueryFileUploadInfoUrl   = "/v1.0/storage/spaces/%s/files/uploadInfos/query"         //获取文件上传信息
+	QueryFileDownloadInfoUrl = "/v1.0/storage/spaces/%s/dentries/%s/downloadInfos/query" //获取文件下载信息
+	CommitFileUploadInfoUrl  = "/v1.0/storage/spaces/%s/files/commit"                    //提交文件
 )
 
 //Storage OA审批
@@ -93,6 +94,13 @@ func (w *Storage) QueryFileUploadInfo(spaceId, unionId string) (response QueryFi
 	return response, err
 }
 
+func (w *Storage) QueryFileDownloadInfo(spaceId, fileId, unionId string) (response QueryFileDownloadInfoResponse, err error) {
+	resp, err := w.HTTPPostJSONWithAccessToken(fmt.Sprintf(QueryFileDownloadInfoUrl, spaceId, fileId) + "?unionId=" + unionId, map[string]interface{}{})
+	err = json.Unmarshal(resp, &response)
+	return response, err
+
+}
+
 //UploadFile 使用OSS的header加签方式上传文件息
 func (w *Storage) uploadFile(url string, headers map[string]string, filePath string) (code int, err error) {
 	count := 100
@@ -149,14 +157,14 @@ func (w *Storage) CommitFile(uploadKey, filename, spaceId, parentId, unionId str
 }
 
 func (w *Storage) UploadFile(spaceId, unionId, fileName, filePath string) (response CommitFileResponse, err error) {
-	permResp, err := w.AddPermission(w.CorpId, spaceId, unionId, "EDITOR", "USER")
-	if err != nil {
-		return response, myerrors.New(500, err, err.Error())
-	}
-
-	if !permResp.Success {
-		return response, myerrors.New(500, nil, "获取钉钉上传文件权限失败!")
-	}
+	// permResp, err := w.AddPermission(w.CorpId, spaceId, unionId, "EDITOR", "USER")
+	// if err != nil {
+	// 	return response, myerrors.New(500, err, err.Error())
+	// }
+
+	// if !permResp.Success {
+	// 	return response, myerrors.New(500, nil, "获取钉钉上传文件权限失败!")
+	// }
 
 	infoResp, err := w.QueryFileUploadInfo(spaceId, unionId)
 	if err != nil {

+ 1 - 1
opms_libary/plugin/dingtalk/workflow/entity.go

@@ -246,7 +246,7 @@ type QueryProcessInstanceResponse struct {
 		} `json:"formComponentValues"`
 		CreateTime string `json:"createTime"`
 	} `json:"result"`
-	Success string `json:"success"`
+	Success bool `json:"success"`
 }
 
 // RevokeProcessInstanceResponse 撤销审批实例

+ 1 - 1
opms_libary/plugin/dingtalk/workflow/workflow.go

@@ -47,7 +47,7 @@ func (w *Workflow) StartProcessInstance(request *StartProcessInstanceRequest) (r
 
 //QueryProcessInstanceDetail 获取单个审批实例详情
 func (w *Workflow) QueryProcessInstanceDetail(instId string) (response QueryProcessInstanceResponse, err error) {
-	resp, err := w.HTTPGetWithAccessToken(RevokeProcessInstanceUrl, g.Map{"processInstanceId": instId})
+	resp, err := w.HTTPGetWithAccessToken(StartProcessInstanceUrl, g.Map{"processInstanceId": instId})
 	if err != nil {
 		return
 	}

+ 14 - 48
opms_parent/app/handler/contract/ctr_contract.go

@@ -2,14 +2,10 @@ package contract
 
 import (
 	"context"
-	"fmt"
-	"strconv"
 
 	model "dashoo.cn/micro/app/model/contract"
 	service "dashoo.cn/micro/app/service/contract"
-	"dashoo.cn/opms_libary/micro_srv"
 	"dashoo.cn/opms_libary/multipart"
-	"dashoo.cn/opms_libary/plugin/dingtalk"
 
 	"dashoo.cn/common_definition/comm_def"
 	"github.com/gogf/gf/frame/g"
@@ -112,7 +108,7 @@ func (c *CtrContract) Commit(ctx context.Context, req *model.CtrContractCommitRe
 	if err != nil {
 		return err
 	}
-	err = s.Commit(ctx, req)
+	_, err = s.Commit(ctx, req)
 	if err != nil {
 		return err
 	}
@@ -121,63 +117,33 @@ func (c *CtrContract) Commit(ctx context.Context, req *model.CtrContractCommitRe
 
 type CtrContractHandler struct{}
 
-var spaceId = "21042518430"
-
 func (h *CtrContractHandler) CommitWithFile(ctx context.Context, args *multipart.MultipartFile, rsp *comm_def.CommonMsg) error {
 	g.Log().Infof("CtrContractHandler.CommitWithFile request %#v ", *args)
-	// 获取用户信息
-	userInfo, err := micro_srv.GetUserInfo(ctx)
-	if err != nil {
-		return fmt.Errorf("获取用户信息异常:%s", err.Error())
-	}
-	if userInfo.DingtalkUid == "" {
-		return fmt.Errorf("该用户钉钉 uid 为空")
-	}
 	s, err := service.NewCtrContractService(ctx)
 	if err != nil {
 		return err
 	}
-	if args.FileName == "" {
-		return fmt.Errorf("文件名称不能为空")
-	}
-	if args.File == nil {
-		return fmt.Errorf("文件不能为空")
-	}
-	if args.File.Name() == "" {
-		return fmt.Errorf("文件路径不能为空")
-	}
-	contractId, err := strconv.Atoi(args.Meta["contractId"])
+	err = s.CommitWithFile(ctx, args)
 	if err != nil {
-		return fmt.Errorf("合同 Id 不合法 %s", args.Meta["contractId"])
+		rsp.Code = 500
+		rsp.Msg = "系统异常"
+		g.Log().Error(err)
+		return err
 	}
+	return nil
+}
 
-	fmt.Println(args.File.Name(), args.FileName, args.FileSize, args.Meta)
-	fmt.Println(spaceId, userInfo.DingtalkId, args.FileName, args.File.Name())
-	// resp, err := s.UploadFile("21042518430", "8xljy04PZiS9iPxp5PhDnUzQiEiE", "引物导入模板-000000.xlsx", "/Users/chengjian/Downloads/引物导入模板.xlsx")
-
-	resp, err := dingtalk.Client.GetStorage().UploadFile(spaceId, userInfo.DingtalkId, args.FileName, args.File.Name())
+func (h *CtrContract) DownloadDingtalkFile(ctx context.Context, req *model.IdRequiredReq, rsp *comm_def.CommonMsg) error {
+	g.Log().Infof("CtrContract.DownloadDingtalkFile request %#v ", *req)
+	s, err := service.NewCtrContractService(ctx)
 	if err != nil {
-		return fmt.Errorf("钉钉上传文件异常 %s", err.Error())
+		return err
 	}
-
-	err = s.Commit(ctx, &model.CtrContractCommitReq{
-		Id:            contractId,
-		ContractModel: args.Meta["contractModel"],
-		Terms:         args.Meta["terms"],
-		PayTerms:      args.Meta["payTerms"],
-		File: []model.DingFileInfo{
-			{
-				SpaceId:  resp.Dentry.SpaceId,
-				FileId:   resp.Dentry.Id,
-				FileName: resp.Dentry.Name,
-				FileSize: resp.Dentry.Size,
-				FileType: resp.Dentry.Extension,
-			},
-		},
-	})
+	data, err := s.DownloadDingtalkFile(ctx, req.Id)
 	if err != nil {
 		return err
 	}
+	rsp.Data = data
 	return nil
 }
 

+ 162 - 13
opms_parent/app/service/contract/ctr_contract.go

@@ -3,15 +3,20 @@ package service
 import (
 	"context"
 	"database/sql"
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"io"
+	"net/http"
 	"strconv"
+	"strings"
 	"time"
 
 	basedao "dashoo.cn/micro/app/dao/base"
 	dao "dashoo.cn/micro/app/dao/contract"
 	custdao "dashoo.cn/micro/app/dao/cust"
 	projdao "dashoo.cn/micro/app/dao/proj"
+	workflowdao "dashoo.cn/micro/app/dao/workflow"
 	model "dashoo.cn/micro/app/model/contract"
 	proj "dashoo.cn/micro/app/model/proj"
 	workflowModel "dashoo.cn/micro/app/model/workflow"
@@ -20,7 +25,9 @@ import (
 	workflowService "dashoo.cn/micro/app/service/workflow"
 
 	"dashoo.cn/opms_libary/micro_srv"
+	"dashoo.cn/opms_libary/multipart"
 	"dashoo.cn/opms_libary/myerrors"
+	"dashoo.cn/opms_libary/plugin/dingtalk"
 	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 	"dashoo.cn/opms_libary/plugin/dingtalk/workflow"
 	"dashoo.cn/opms_libary/request"
@@ -38,6 +45,8 @@ type CtrContractService struct {
 	CtrProductDao   *dao.CtrContractProductDao
 	ProductDao      *basedao.BaseProductDao
 	DynamicsDao     *dao.CtrContractDynamicsDao
+	WorkflowDao     *workflowdao.PlatWorkflowDao
+	AppendDao       *dao.CtrContractAppendDao
 
 	Tenant    string
 	userInfo  request.UserInfo
@@ -62,6 +71,8 @@ func NewCtrContractService(ctx context.Context) (*CtrContractService, error) {
 		CtrProductDao:   dao.NewCtrContractProductDao(tenant),
 		ProductDao:      basedao.NewBaseProductDao(tenant),
 		DynamicsDao:     dao.NewCtrContractDynamicsDao(tenant),
+		WorkflowDao:     workflowdao.NewPlatWorkflowDao(tenant),
+		AppendDao:       dao.NewCtrContractAppendDao(tenant),
 		Tenant:          tenant,
 		userInfo:        userInfo,
 		DataScope:       userInfo.DataScope,
@@ -422,43 +433,43 @@ func (s CtrContractService) Add(ctx context.Context, req *model.CtrContractAddRe
 
 var ContractApplyProcessCode = "PROC-7057E20A-2066-4644-9B35-9331E4DA912C" // 创建合同
 
-func (s CtrContractService) Commit(ctx context.Context, req *model.CtrContractCommitReq) error {
+func (s CtrContractService) Commit(ctx context.Context, req *model.CtrContractCommitReq) (int64, error) {
 	validErr := gvalid.CheckStruct(ctx, req, nil)
 	if validErr != nil {
-		return myerrors.TipsError(validErr.Current().Error())
+		return 0, myerrors.TipsError(validErr.Current().Error())
 	}
 	if !(req.ContractModel == "大数模板" || req.ContractModel == "客户模板") {
-		return myerrors.TipsError("合同模板不合法")
+		return 0, myerrors.TipsError("合同模板不合法")
 	}
 	if !(req.Terms == "接纳全部条款" || req.Terms == "不接纳全部条款") {
-		return myerrors.TipsError("条款情况不合法")
+		return 0, myerrors.TipsError("条款情况不合法")
 	}
 	if req.PayTerms == "" {
-		return myerrors.TipsError("付款条件不能为空")
+		return 0, myerrors.TipsError("付款条件不能为空")
 	}
 	if len(req.File) == 0 {
-		return myerrors.TipsError("附件不能为空")
+		return 0, myerrors.TipsError("附件不能为空")
 	}
 
 	ent, err := s.Dao.Where("id = ?", req.Id).One()
 	if err != nil {
-		return err
+		return 0, err
 	}
 	if ent == nil {
-		return myerrors.TipsError(fmt.Sprintf("合同不存在: %d", req.Id))
+		return 0, myerrors.TipsError(fmt.Sprintf("合同不存在: %d", req.Id))
 	}
 
 	fileinfoByte, err := json.Marshal(req.File)
 	if err != nil {
-		return err
+		return 0, err
 	}
 
 	workflowSrv, err := workflowService.NewFlowService(ctx)
 	if err != nil {
-		return err
+		return 0, err
 	}
 	bizCode := strconv.Itoa(ent.Id)
-	_, err = workflowSrv.StartProcessInstance(bizCode, "30", "", &workflow.StartProcessInstanceRequest{
+	workflowId, err := workflowSrv.StartProcessInstance(bizCode, "30", "", &workflow.StartProcessInstanceRequest{
 		ProcessCode: &ContractApplyProcessCode,
 		FormComponentValues: []*workflow.StartProcessInstanceRequestFormComponentValues{
 			{
@@ -500,13 +511,151 @@ func (s CtrContractService) Commit(ctx context.Context, req *model.CtrContractCo
 		},
 	})
 	if err != nil {
-		return err
+		return 0, err
 	}
 
 	_, err = s.Dao.Where("id = ?", ent.Id).Data(map[string]interface{}{
 		"appro_status": 20,
 	}).Update()
-	return err
+	return workflowId, err
+}
+
+// var spaceId = "21042518430"
+
+var spaceId = "21077726250"
+
+func (s CtrContractService) CommitWithFile(ctx context.Context, args *multipart.MultipartFile) error {
+	if s.userInfo.DingtalkUid == "" {
+		return fmt.Errorf("该用户钉钉 uid 为空")
+	}
+	if args.FileName == "" {
+		return fmt.Errorf("文件名称不能为空")
+	}
+	if args.File == nil {
+		return fmt.Errorf("文件不能为空")
+	}
+	if args.File.Name() == "" {
+		return fmt.Errorf("文件路径不能为空")
+	}
+	contractId, err := strconv.Atoi(args.Meta["contractId"])
+	if err != nil {
+		return fmt.Errorf("合同 Id 不合法 %s", args.Meta["contractId"])
+	}
+
+	fmt.Println(args.File.Name(), args.FileName, args.FileSize, args.Meta)
+	fmt.Println(spaceId, s.userInfo.DingtalkId, args.FileName, args.File.Name())
+	// resp, err := s.UploadFile("21042518430", "8xljy04PZiS9iPxp5PhDnUzQiEiE", "引物导入模板-000000.xlsx", "/Users/chengjian/Downloads/引物导入模板.xlsx")
+
+	resp, err := dingtalk.Client.GetStorage().UploadFile(spaceId, s.userInfo.DingtalkId, args.FileName, args.File.Name())
+	if err != nil {
+		return fmt.Errorf("钉钉上传文件异常 %s", err.Error())
+	}
+
+	_, err = s.Commit(ctx, &model.CtrContractCommitReq{
+		Id:            contractId,
+		ContractModel: args.Meta["contractModel"],
+		Terms:         args.Meta["terms"],
+		PayTerms:      args.Meta["payTerms"],
+		File: []model.DingFileInfo{
+			{
+				SpaceId:  resp.Dentry.SpaceId,
+				FileId:   resp.Dentry.Id,
+				FileName: resp.Dentry.Name,
+				FileSize: resp.Dentry.Size,
+				FileType: resp.Dentry.Extension,
+			},
+		},
+	})
+	if err != nil {
+		return err
+	}
+	// workflow, err := s.WorkflowDao.Where("id = ?", workflowId).One()
+	// if err != nil {
+	// 	return err
+	// }
+	// instance, err := dingtalk.Client.GetWorkflow().QueryProcessInstanceDetail(workflow.ProcessInstId)
+	// if err != nil {
+	// 	return fmt.Errorf("查询审批实例详情错误: %s", err.Error())
+	// }
+	// fmt.Println(workflow.ProcessInstId, instance.Result.ApproverUserIds)
+
+	// approverUserIds := g.Config().GetStrings("dingtalk.approver-user-ids")
+	// fmt.Println(approverUserIds)
+	// for _, uid := range approverUserIds {
+	// 	resp, err := dingtalk.Client.GetStorage().AddPermission(
+	// 		dingtalk.Client.Context.CorpId, spaceId, uid, "DOWNLOADER", "USER")
+	// 	if err != nil {
+	// 		return fmt.Errorf("添加审核附件权限异常: %s", err.Error())
+	// 	}
+	// 	fmt.Println(uid, resp)
+
+	// }
+
+	appendSrv, err := NewCtrContractAppendService(ctx)
+	if err != nil {
+		return err
+	}
+	_, err = appendSrv.Add(ctx, &model.CtrContractAppendAddReq{
+		ContractId: contractId,
+		FileName:   resp.Dentry.Name,
+		FileType:   resp.Dentry.Extension,
+		FileUrl:    strings.Join([]string{"dingtalk", resp.Dentry.SpaceId, resp.Dentry.Id}, ":"),
+		Remark:     "",
+	})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (s CtrContractService) DownloadDingtalkFile(ctx context.Context, id int) (string, error) {
+	ent, err := s.AppendDao.Where("id= ?", id).One()
+	if err != nil {
+		return "", err
+	}
+	if ent == nil {
+		return "", myerrors.TipsError("附件不存在")
+	}
+	if !strings.HasPrefix(ent.FileUrl, "dingtalk") {
+		return "", myerrors.TipsError("此附件不是钉钉附件")
+	}
+	fileInfo := strings.Split(ent.FileUrl, ":")
+	if len(fileInfo) != 3 {
+		return "", myerrors.TipsError("钉钉附件地址不合法")
+	}
+	spaceId := fileInfo[1]
+	fileId := fileInfo[2]
+
+	// res, err := dingtalk.Client.GetStorage().AddPermission(dingtalk.Client.Context.CorpId, spaceId, s.userInfo.DingtalkId, "EDITOR", "USER")
+	// fmt.Println(res, err)
+	// if err != nil {
+	// 	return "", fmt.Errorf("设置权限异常 %s", err.Error())
+	// }
+
+	resp, err := dingtalk.Client.GetStorage().QueryFileDownloadInfo(spaceId, fileId, s.userInfo.DingtalkId)
+	if err != nil {
+		return "", myerrors.TipsError("获取文件下载信息异常")
+	}
+	fmt.Println(resp, err)
+	req, err := http.NewRequest("GET", resp.HeaderSignatureInfo.ResourceUrls[0], nil)
+	if err != nil {
+		return "", fmt.Errorf("构建文件下载请求异常 %s", err.Error())
+	}
+
+	for k, v := range resp.HeaderSignatureInfo.Headers {
+		req.Header.Add(k, v)
+	}
+	fileresp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return "", fmt.Errorf("文件下载异常 %s", err.Error())
+	}
+
+	data, err := io.ReadAll(fileresp.Body)
+	if err != nil {
+		return "", fmt.Errorf("读取下载内容异常 %s", err.Error())
+	}
+
+	return base64.StdEncoding.EncodeToString(data), nil
 }
 
 func ContractApplyApproval(ctx context.Context, flow *workflowModel.PlatWorkflow, msg *message.MixMessage) error {

+ 39 - 0
opms_parent/app/service/contract/ctr_contract_test.go

@@ -2,9 +2,14 @@ package service
 
 import (
 	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
 	"testing"
 
 	modelWorkflow "dashoo.cn/micro/app/model/workflow"
+	"dashoo.cn/opms_libary/plugin/dingtalk"
 	"dashoo.cn/opms_libary/plugin/dingtalk/message"
 	"github.com/gogf/gf/frame/g"
 	"github.com/smallnest/rpcx/share"
@@ -56,3 +61,37 @@ func TestContractApplyApproval(t *testing.T) {
 		panic(err)
 	}
 }
+
+func TestDownloadDingtalk(t *testing.T) {
+	// dingtalk:21042518430:97103341461
+	// dingtalk:21042518430:97112761361
+
+	res, err := dingtalk.Client.GetStorage().AddPermission(dingtalk.Client.Context.CorpId, "21042518430", "8xljy04PZiS9iPxp5PhDnUzQiEiE", "EDITOR", "USER")
+	fmt.Println(res, err)
+
+	resp, err := dingtalk.Client.GetStorage().QueryFileDownloadInfo("21042518430", "97112761361", "8xljy04PZiS9iPxp5PhDnUzQiEiE")
+	// resp, err := dingtalk.Client.GetStorage().QueryFileDownloadInfo("21042518430", "97103341461", "fHJtftdpHZXwvsdOfg8skgiEiE")
+
+	fmt.Println(resp, err)
+	req, err := http.NewRequest("GET", resp.HeaderSignatureInfo.ResourceUrls[0], nil)
+	if err != nil {
+		panic(err)
+	}
+	for k, v := range resp.HeaderSignatureInfo.Headers {
+		req.Header.Add(k, v)
+	}
+	fileresp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		panic(err)
+	}
+	f, err := os.Create("test.jpeg")
+	if err != nil {
+		panic(err)
+	}
+	defer f.Close()
+	_, err = io.Copy(f, fileresp.Body)
+	if err != nil {
+		panic(err)
+	}
+
+}