319 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package logic
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"fonchain-fiee/pkg/model"
 | |
| 	"fonchain-fiee/pkg/service/bundle/common"
 | |
| 	"fonchain-fiee/pkg/service/upload"
 | |
| 	"github.com/signintech/gopdf"
 | |
| 	"go.uber.org/zap"
 | |
| 	"image"
 | |
| 	"io"
 | |
| 	"log"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| func SignContract(customerNum, contract, signImgPath, companyImgPath, contractNo, buyerName, idNo, telNum, address string, price float32, contractDuration int64) (outputUrl string, err error) {
 | |
| 	filePath := model.MediaPath + customerNum + time.Now().Format("20060102150405") + ".pdf"
 | |
| 	downloadFileErr := DownloadFile(filePath, contract)
 | |
| 	if downloadFileErr != nil {
 | |
| 		zap.L().Error("download file error: ", zap.Error(downloadFileErr))
 | |
| 		//service.Error(c, errors.New(common.ErrorDownloadFile))
 | |
| 		return outputUrl, errors.New(common.ErrorDownloadFile)
 | |
| 	}
 | |
| 
 | |
| 	signFile := model.MediaPath + customerNum + "signed" + time.Now().Format("20060102150405") + ".pdf"
 | |
| 
 | |
| 	/*signErr := InsertSignature(filePath, signFile, signImgPath, contractNo, idNo, telNum, address, price)
 | |
| 	if signErr != nil {
 | |
| 		zap.L().Error("insert signature error: ", zap.Error(signErr))
 | |
| 		return outputUrl, errors.New(common.ErrorInsertSignature)
 | |
| 	}*/
 | |
| 
 | |
| 	signErr := InsertSignatureV1(filePath, signFile, signImgPath, companyImgPath, contractNo, buyerName, idNo, telNum, address, price, contractDuration)
 | |
| 	if signErr != nil {
 | |
| 		zap.L().Error("insert signature error: ", zap.Error(signErr))
 | |
| 		return outputUrl, errors.New(common.ErrorInsertSignature)
 | |
| 	}
 | |
| 
 | |
| 	os.Remove(filePath)
 | |
| 
 | |
| 	outputUrl, ossErr := upload.PutBos(signFile, upload.PdfType, true)
 | |
| 	if ossErr != nil {
 | |
| 		return "", errors.New(common.ErrorUploadFile)
 | |
| 	}
 | |
| 	return outputUrl, nil
 | |
| }
 | |
| 
 | |
| // InsertSignature 把用户签字内容 填充到 合同模版里面
 | |
| func InsertSignature(templatePath string, outputPath string, signImgPath string) error {
 | |
| 	pdf := gopdf.GoPdf{}
 | |
| 	pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
 | |
| 
 | |
| 	resp, imageErr := http.Get(signImgPath)
 | |
| 	if imageErr != nil {
 | |
| 		return errors.New("error downloading signature image")
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	b, _err := io.ReadAll(resp.Body)
 | |
| 	if _err != nil {
 | |
| 		return errors.New("error reading signature image")
 | |
| 	}
 | |
| 
 | |
| 	// Create a new reader from the bytes
 | |
| 	imgReader := bytes.NewReader(b)
 | |
| 	img, _, err := image.DecodeConfig(imgReader)
 | |
| 	if err != nil {
 | |
| 		return errors.New("error decoding signature image")
 | |
| 	}
 | |
| 	w := img.Width
 | |
| 	h := img.Height
 | |
| 
 | |
| 	// 导入模板文件中的页面
 | |
| 	err = pdf.ImportPagesFromSource(templatePath, "/MediaBox")
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("无法导入页面: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// 获取模板文件的总页数
 | |
| 	totalPages := pdf.GetNumberOfPages()
 | |
| 	fmt.Printf("模板文件的总页数: %d\n", totalPages)
 | |
| 
 | |
| 	targetPage := totalPages
 | |
| 
 | |
| 	// 遍历所有页
 | |
| 	for i := 1; i <= totalPages; i++ {
 | |
| 		tpl := pdf.ImportPage(templatePath, i, "/MediaBox")
 | |
| 		pdf.AddPage()
 | |
| 		pdf.UseImportedTemplate(tpl, 0, 0, gopdf.PageSizeA4.W, gopdf.PageSizeA4.H)
 | |
| 		// 只在目标页插入签名
 | |
| 		if i == targetPage {
 | |
| 
 | |
| 			imgH1, _err := gopdf.ImageHolderByBytes(b)
 | |
| 			if _err != nil {
 | |
| 				//zap.L().Error("SignContract err", zap.Error(err))
 | |
| 				return errors.New("error processing signature image")
 | |
| 			}
 | |
| 			newWidth := 80.0
 | |
| 			newHeight := (newWidth / float64(w)) * float64(h)
 | |
| 			err = pdf.ImageByHolder(imgH1, 380, 540, &gopdf.Rect{W: newWidth, H: newHeight})
 | |
| 			if err != nil {
 | |
| 				//zap.L().Error("SignContract err", zap.Error(err))
 | |
| 				return errors.New("error inserting signature")
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// 生成新的 PDF
 | |
| 	if err = pdf.WritePdf(outputPath); err != nil {
 | |
| 		//zap.L().Error("WritePdf err", zap.Error(err))
 | |
| 		return errors.New("error writing final PDF")
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func InsertSignatureV1(templatePath, outputPath, signImgPath, companyImgPath, contractNo, buyerName, idNo, telNum, address string, price float32, contractDuration int64) error {
 | |
| 	pdf := gopdf.GoPdf{}
 | |
| 	pdf.Start(gopdf.Config{PageSize: *gopdf.PageSizeA4})
 | |
| 
 | |
| 	// 导入模板文件中的页面
 | |
| 	err := pdf.ImportPagesFromSource(templatePath, "/MediaBox")
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("无法导入页面: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// 获取模板文件的总页数
 | |
| 	totalPages := pdf.GetNumberOfPages()
 | |
| 	fmt.Printf("模板文件的总页数: %d\n", totalPages)
 | |
| 
 | |
| 	buyerPage := 1
 | |
| 
 | |
| 	pricePage := 2
 | |
| 
 | |
| 	limitTimePage := 3
 | |
| 
 | |
| 	signPage := totalPages
 | |
| 
 | |
| 	tffErr := pdf.AddTTFFont("simfang", "./data/simfang.ttf")
 | |
| 	if tffErr != nil {
 | |
| 		fmt.Printf("加载中文字体失败: %v\n", tffErr)
 | |
| 		log.Fatalf("加载中文字体失败: %v", tffErr)
 | |
| 	}
 | |
| 
 | |
| 	// 设置字体和字号
 | |
| 	err = pdf.SetFont("simfang", "", 14)
 | |
| 	if err != nil {
 | |
| 		fmt.Printf("设置字体失败: %v\n", err)
 | |
| 		log.Fatalf("设置字体失败: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	// 写 buyer info
 | |
| 	pdf.SetPage(buyerPage)
 | |
| 	pdf.SetX(145)
 | |
| 	pdf.SetY(134)
 | |
| 	pdf.Cell(nil, contractNo)
 | |
| 
 | |
| 	pdf.SetX(145)
 | |
| 	pdf.SetY(159)
 | |
| 	pdf.Cell(nil, time.Now().Format("2006-01-02"))
 | |
| 
 | |
| 	pdf.SetX(180)
 | |
| 	pdf.SetY(208)
 | |
| 	pdf.Cell(nil, buyerName)
 | |
| 
 | |
| 	pdf.SetX(210)
 | |
| 	pdf.SetY(233)
 | |
| 	pdf.Cell(nil, idNo)
 | |
| 
 | |
| 	pdf.SetX(200)
 | |
| 	pdf.SetY(259)
 | |
| 	pdf.Cell(nil, telNum)
 | |
| 
 | |
| 	pdf.SetX(200)
 | |
| 	pdf.SetY(284)
 | |
| 	pdf.Cell(nil, address)
 | |
| 
 | |
| 	// 填 金额
 | |
| 	pdf.SetPage(pricePage)
 | |
| 	pdf.SetX(379)
 | |
| 	pdf.SetY(639)
 | |
| 	pdf.Cell(nil, strconv.FormatFloat(float64(price), 'f', 2, 32))
 | |
| 
 | |
| 	// 写 有效期
 | |
| 	t := time.Now().AddDate(int(contractDuration), 0, 0)
 | |
| 	pdf.SetPage(limitTimePage)
 | |
| 	pdf.SetX(327)
 | |
| 	pdf.SetY(358)
 | |
| 	pdf.Cell(nil, t.Format("2006"))
 | |
| 
 | |
| 	pdf.SetX(420)
 | |
| 	pdf.SetY(358)
 | |
| 	pdf.Cell(nil, t.Format("01"))
 | |
| 
 | |
| 	pdf.SetX(486)
 | |
| 	pdf.SetY(358)
 | |
| 	pdf.Cell(nil, t.Format("02"))
 | |
| 
 | |
| 	// 只在目标页插入签名
 | |
| 	pdf.SetPage(signPage)
 | |
| 
 | |
| 	signImgResp, imageErr := http.Get(signImgPath)
 | |
| 	if imageErr != nil {
 | |
| 		fmt.Printf("下载签名图片失败: %v\n", imageErr)
 | |
| 		return errors.New("error downloading signature image")
 | |
| 	}
 | |
| 	defer signImgResp.Body.Close()
 | |
| 
 | |
| 	signImgB, _err := io.ReadAll(signImgResp.Body)
 | |
| 	if _err != nil {
 | |
| 		fmt.Printf("读取签名图片失败: %v\n", _err)
 | |
| 		return errors.New("error reading signature image")
 | |
| 	}
 | |
| 
 | |
| 	// Create a new reader from the bytes
 | |
| 	signImgReader := bytes.NewReader(signImgB)
 | |
| 	signImg, _, err := image.DecodeConfig(signImgReader)
 | |
| 	if err != nil {
 | |
| 		fmt.Printf("解码签名图片失败: %v\n", err)
 | |
| 		return errors.New("error decoding signature image")
 | |
| 	}
 | |
| 
 | |
| 	signImgw := signImg.Width
 | |
| 	signImgh := signImg.Height
 | |
| 
 | |
| 	imgH1, _err := gopdf.ImageHolderByBytes(signImgB)
 | |
| 	if _err != nil {
 | |
| 		fmt.Printf("处理签名图片失败: %v\n", _err)
 | |
| 		return errors.New("error processing signature image")
 | |
| 	}
 | |
| 	newSignWidth := 80.0
 | |
| 	newSignHeight := (newSignWidth / float64(signImgw)) * float64(signImgh)
 | |
| 
 | |
| 	// buyer sign
 | |
| 	err = pdf.ImageByHolder(imgH1, 180, 560, &gopdf.Rect{W: newSignWidth, H: newSignHeight})
 | |
| 	if err != nil {
 | |
| 		fmt.Printf("插入签名失败: %v\n", err)
 | |
| 		return errors.New("error inserting signature")
 | |
| 	}
 | |
| 
 | |
| 	pdf.SetX(320)
 | |
| 	pdf.SetY(585)
 | |
| 	pdf.Cell(nil, time.Now().Format("2006-01-02"))
 | |
| 
 | |
| 	companyImgResp, companyImageErr := http.Get(companyImgPath)
 | |
| 	if companyImageErr != nil {
 | |
| 		fmt.Printf("下载公司章图片: %v\n", companyImgPath)
 | |
| 		fmt.Printf("下载公司章图片失败: %v\n", companyImageErr)
 | |
| 		return errors.New("error downloading signature image")
 | |
| 	}
 | |
| 	defer companyImgResp.Body.Close()
 | |
| 
 | |
| 	companyImgB, _err := io.ReadAll(companyImgResp.Body)
 | |
| 	if _err != nil {
 | |
| 		fmt.Printf("读取公司章图片失败: %v\n", _err)
 | |
| 		return errors.New("error reading signature image")
 | |
| 	}
 | |
| 
 | |
| 	// Create a new reader from the bytes
 | |
| 	companyImgReader := bytes.NewReader(companyImgB)
 | |
| 	companyImg, _, err := image.DecodeConfig(companyImgReader)
 | |
| 	if err != nil {
 | |
| 		fmt.Printf("解码公司章图片失败: %v\n", err)
 | |
| 		return errors.New("error decoding signature image")
 | |
| 	}
 | |
| 
 | |
| 	companyImgw := companyImg.Width
 | |
| 	companyImgh := companyImg.Height
 | |
| 
 | |
| 	companyImgH1, _err := gopdf.ImageHolderByBytes(companyImgB)
 | |
| 	if _err != nil {
 | |
| 		fmt.Printf("处理公司章图片失败: %v\n", _err)
 | |
| 		return errors.New("error processing signature image")
 | |
| 	}
 | |
| 	newCompanyWidth := 80.0
 | |
| 	newCompanyHeight := (newCompanyWidth / float64(companyImgw)) * float64(companyImgh)
 | |
| 
 | |
| 	// seller sign
 | |
| 	err = pdf.ImageByHolder(companyImgH1, 180, 610, &gopdf.Rect{W: newCompanyWidth, H: newCompanyHeight})
 | |
| 	if err != nil {
 | |
| 		fmt.Printf("插入公司章失败: %v\n", err)
 | |
| 		return errors.New("error inserting signature")
 | |
| 	}
 | |
| 
 | |
| 	pdf.SetX(320)
 | |
| 	pdf.SetY(635)
 | |
| 	pdf.Cell(nil, time.Now().Format("2006-01-02"))
 | |
| 
 | |
| 	// 生成新的 PDF
 | |
| 	if err = pdf.WritePdf(outputPath); err != nil {
 | |
| 		fmt.Printf("写入 PDF 失败: %v\n", err)
 | |
| 		return errors.New("error writing final PDF")
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func DownloadFile(filepath string, url string) error {
 | |
| 	resp, err := http.Get(url)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	out, err := os.Create(filepath)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer out.Close()
 | |
| 
 | |
| 	_, err = io.Copy(out, resp.Body)
 | |
| 	return err
 | |
| }
 |