gozero实现对接在线签名签署文件流程管理的设计与实践

290 阅读4分钟

在使用 go-zero 框架来实现对接 签宝 的签名与验签、待签署文件管理、电子印章、配置签署场景与签署方式、签署流程管理的相关接口时,可以按照 go-zero 的最佳实践进行代码设计与分层。go-zero 提供了强大的工具支持,能够让你高效地开发和管理服务。

image.png 下面是基于 go-zero 架构进行分层设计的建议,并包含如何实现相关接口。

1. 项目目录结构

/esign
    /api                     # 表示层,处理 HTTP 请求与响应
    /logic                   # 业务逻辑层
    /model                   # 数据层,管理数据库模型和 API 请求响应模型
    /service                 # 服务层,启动服务与接口注册
    /config                  # 配置文件
    /client                  # 对接 e签宝 API 的客户端
    /utils                   # 工具类层,封装常用的功能
    main.go                  # 启动文件

image.png

2. 各层的功能职责

  • 表示层(API):负责接收前端的请求,调用业务逻辑层,处理请求和响应。
  • 业务逻辑层(Logic):处理核心业务逻辑,如签名、验签、获取待签署文件等,封装业务流程。
  • 数据层(Model):定义与数据库交互的模型,保存签署流程等信息。
  • 服务层(Service):启动服务、注册路由等。
  • 工具类层(Utils):封装一些常用功能,如 HTTP 请求、日志等。

3. 具体代码实现

3.1 工具类层:utils/http.go

工具类负责发送 HTTP 请求到 签宝 API,处理请求响应。

package utils

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

// DoRequest 通用 HTTP 请求函数
func DoRequest(url, method string, headers map[string]string, body interface{}) ([]byte, error) {
	var reqBody []byte
	var err error

	// 如果请求体存在,则进行 JSON 序列化
	if body != nil {
		reqBody, err = json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal body: %v", err)
		}
	}

	// 创建 HTTP 请求
	req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %v", err)
	}

	// 设置请求头
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute request: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应体
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response body: %v", err)
	}

	return respBody, nil
}

3.2 数据层(Model):model/signature_model.go

定义与 e签宝相关的数据结构,便于与 API 交互。

package model

// 签署文件结构
type File struct {
	FileID   string `json:"file_id"`
	FileName string `json:"file_name"`
	FileURL  string `json:"file_url"`
}

// 签署流程结构
type SignProcess struct {
	ProcessID string `json:"process_id"`
	FileID    string `json:"file_id"`
	SignerID  string `json:"signer_id"`
	Status    string `json:"status"`
}

// API 响应结构
type APIResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    interface{} `json:"data"`
}

3.3 业务逻辑层(Logic):logic/signature_logic.go

业务逻辑层负责封装业务流程,调用 e签宝 API 完成签署和验签等操作。

package logic

import (
	"encoding/json"
	"fmt"
	"github.com/your_project/utils"
	"github.com/your_project/model"
	"github.com/your_project/client"
	"github.com/zeromicro/go-zero/core/logx"
)

type SignatureLogic struct {
	client *client.ESignClient
}

func NewSignatureLogic(client *client.ESignClient) *SignatureLogic {
	return &SignatureLogic{client: client}
}

// 创建签署流程
func (logic *SignatureLogic) CreateSignProcess(fileID, signerID string) (*model.SignProcess, error) {
	url := fmt.Sprintf("%s/v1/sign/processes", logic.client.BaseURL)
	headers := map[string]string{
		"Authorization": logic.client.APIKey,
	}
	body := map[string]interface{}{
		"file_id":   fileID,
		"signer_id": signerID,
	}

	respData, err := utils.DoRequest(url, "POST", headers, body)
	if err != nil {
		logx.Errorf("Error creating sign process: %v", err)
		return nil, err
	}

	var response model.APIResponse
	if err := json.Unmarshal(respData, &response); err != nil {
		logx.Errorf("Failed to parse response: %v", err)
		return nil, err
	}

	if response.Code != 0 {
		return nil, fmt.Errorf("error response from eSign API: %s", response.Message)
	}

	// 返回签署流程信息
	signProcess := &model.SignProcess{
		ProcessID: response.Data["process_id"].(string),
		FileID:    fileID,
		SignerID:  signerID,
		Status:    "created",
	}
	return signProcess, nil
}

3.4 客户端层(Client):client/esign_client.go

封装与 e签宝的 API 通信,确保与外部服务的交互简单、清晰。

package client

import (
	"fmt"
	"github.com/your_project/utils"
)

// ESignClient 用于与 e签宝 API 的交互
type ESignClient struct {
	BaseURL string
	APIKey  string
}

// NewESignClient 创建一个新的 eSign 客户端实例
func NewESignClient(baseURL, apiKey string) *ESignClient {
	return &ESignClient{
		BaseURL: baseURL,
		APIKey:  apiKey,
	}
}

// 上传文件
func (client *ESignClient) UploadFile(filePath string) (string, error) {
	url := fmt.Sprintf("%s/v1/files", client.BaseURL)
	headers := map[string]string{
		"Authorization": client.APIKey,
	}

	// 假设有一个工具函数处理文件上传(可以自行实现文件上传函数)
	fileData, err := utils.UploadFile(filePath)
	if err != nil {
		return "", err
	}

	respData, err := utils.DoRequest(url, "POST", headers, fileData)
	if err != nil {
		return "", err
	}

	// 假设 API 返回的文件 ID 在 response 中
	var result map[string]interface{}
	if err := json.Unmarshal(respData, &result); err != nil {
		return "", err
	}

	return result["file_id"].(string), nil
}

3.5 表示层(API):api/signature_api.go

处理 HTTP 请求并调用相应的业务逻辑,返回结果给客户端。

package api

import (
	"github.com/your_project/logic"
	"github.com/your_project/model"
	"github.com/zeromicro/go-zero/rest/httpx"
	"net/http"
)

type SignatureAPI struct {
	SignatureLogic *logic.SignatureLogic
}

func NewSignatureAPI(signatureLogic *logic.SignatureLogic) *SignatureAPI {
	return &SignatureAPI{
		SignatureLogic: signatureLogic,
	}
}

func (api *SignatureAPI) CreateSignProcessHandler(w http.ResponseWriter, r *http.Request) {
	var fileID, signerID string
	// 解析请求 URL 参数等

	signProcess, err := api.SignatureLogic.CreateSignProcess(fileID, signerID)
	if err != nil {
		httpx.Error(w, err)
		return
	}

	httpx.OkJson(w, model.APIResponse{
		Code:    0,
		Message: "Success",
		Data:    signProcess,
	})
}

3.6 启动服务:main.go

在启动文件中,初始化各个模块,启动 HTTP 服务。

package main

import (
	"github.com/your_project/client"
	"github.com/your_project/logic"
	"github.com/your_project/api"
	"github.com/zeromicro/go-zero/rest"
	"github.com/zeromicro/go-zero/zrpc"
)

func main() {
	// 创建 eSign 客户端
	client := client.NewESignClient("https://api.esign.com", "your_api_key")

	// 创建逻辑层
	signatureLogic := logic.NewSignatureLogic(client)

	// 创建 API 层
	signatureAPI := api.NewSignatureAPI(signatureLogic)

	// 创建并启动 HTTP 服务
	server := rest.MustNewServer(rest.RestConf{
		Host: "localhost",
		Port: 8080,
	})
	server.AddRoute(httpx.MethodPost, "/sign/{file_id}/{signer_id}", signatureAPI.CreateSignProcessHandler)

	// 启动服务
	defer server.Stop()
	server.Start()
}

4. 总结

  • 通过 go-zero 的分层架构,可以使得代码的模块化程度更高,职责分明。
  • 工具类层(如 HTTP 请求封装)可以复用,避免了重复的代码。
  • 业务逻辑层封装了具体的操作,如签署流程的创建、文件上传等。
  • API 层负责与外部交互,提供用户接口,保证业务逻辑的清晰性。
  • 最终通过 服务层 启动 Web 服务,使得整个流程更加清晰和可维护。

这种架构和设计能够有效地支持可扩展性,方便后期的维护和功能扩展。