Kaynağa Gözat

增加报表单据批量下载

sun-pc-linux 7 ay önce
ebeveyn
işleme
ad7dcb442b
4 değiştirilmiş dosya ile 329 ekleme ve 10 silme
  1. 0 10
      boxcost/api/aa.txe
  2. 10 0
      boxcost/api/aadiffupdatetest.http
  3. 295 0
      boxcost/api/report.go
  4. 24 0
      boxcost/api/utils.go

+ 0 - 10
boxcost/api/aa.txe

@@ -1,10 +0,0 @@
-
-planId:ObjectID("66ab02b8404fc9b5a1a2b28f")
-stage:&model.ComponentStage{Id:"668d03c823830800fd775d08H9xzN1reOj60rCfxkeqlj",
- TypeId:primitive.ObjectID{0x66, 0x8d, 0x3, 0xc8, 0x23, 0x83, 0x8, 0x0, 0xfd, 0x77, 0x5d, 0x8}, 
- OrderPrice:0.2, OrderCount:5000, 
- IsFix:(*bool)(0xc00031be55), ConfirmCount:0, DeliveryTime:time.Date(2024, time.August, 2, 0, 0, 0, 0, time.UTC), 
- SupplierInfo:(*model.Supplier)(0xc000323a40), 
- BatchCount:(*float64)(0xc00031bf30), BatchSizeWidth:20,
-  BatchSizeHeight:10, Remark:"2.0双灰", Size:"", Name:"工业纸板", Category:"成品采购", Price:0,
-  Unit:"吨", Norm:"1180g", CreateTime:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), UpdateTime:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), Type:1, Group:"", BillId:"66acb50b99f1d056d5e2519d", BillType:1}

+ 10 - 0
boxcost/api/aadiffupdatetest.http

@@ -25,6 +25,16 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjI4MjU4O
  
 }
 
+###
+# 下载报表查询的单据
+# 
+POST http://{{host}}/boxcost/report/bills/download HTTP/1.1
+Content-Type: application/json
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjI4MjU4OTIsImlkIjoiNjQyYTUyNGY1ZjUwYmM5MDNmOTg2Mzk0Iiwia2V5IjoiYm94Y29zdCIsIm5hbWUiOiLlrZnog5wiLCJvcmlnX2lhdCI6MTcyMjIyMTA5MiwicGFyZW50IjoiNjQyYTUyNGY1ZjUwYmM5MDNmOTg2Mzk0IiwicGhvbmUiOiIxMzQwODU0NzgyMyIsInJvbGUiOiIiLCJzdGF0ZSI6MSwidXNlclR5cGUiOjJ9.Cz7qI3-Vah0io5ZLIFDyVOf8qsqdmbT8WHdprrjbdXw
+
+{
+ 
+}
 
 
 

+ 295 - 0
boxcost/api/report.go

@@ -1,16 +1,23 @@
 package api
 
 import (
+	"archive/zip"
 	"box-cost/db/model"
 	"box-cost/db/repo"
 	"errors"
 	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"regexp"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/gin-gonic/gin"
 	"github.com/go-redis/redis/v8"
 	"github.com/xuri/excelize/v2"
+	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
@@ -23,6 +30,294 @@ func Report(r *GinRouter) {
 	r.GETJWT("/report/product/list", ReportProductList)
 	r.GETJWT("/report/list", ReportList)
 	r.GETJWT("/report/download", ReportListDownload)
+	//
+	r.GETJWT("/report/bills/download", ReportBillsDownload)
+}
+
+// 下载单据
+func ReportBillsDownload(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	_, _, query := UtilQueryPageSize(c)
+	// supplierId := primitive.NilObjectID
+	// if _supplierId, ok := query["supplierId"]; ok {
+	// 	supplierId, _ = primitive.ObjectIDFromHex(_supplierId.(string))
+	// }
+	_isPdf := "false"
+	if isPdf, ok := query["isPdf"]; ok {
+		delete(query, "isPdf")
+		if isPdf.(string) == "true" {
+			_isPdf = "true"
+		}
+	}
+
+	// timeRange := []interface{}{}
+	// if _timeRange, ok := query["timeRange"]; ok {
+	// 	timeRange, _ = _timeRange.([]interface{})
+	// }
+	filtter := handleReportQuery(query)
+
+	purchases := []*model.PurchaseBill{}
+	produces := []*model.ProduceBill{}
+	products := []*model.ProductBill{}
+
+	repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionBillPurchase,
+		Query:       filtter,
+	}, &purchases)
+	repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionBillProduce,
+		Query:       filtter,
+	}, &produces)
+	repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+		CollectName: repo.CollectionBillProduct,
+		Query:       filtter,
+	}, &products)
+
+	// 加入redis有序集合中进行排序
+	// 把对应type_id:对象存入map中避免再次查询表
+	typeBills := map[string]interface{}{}
+	redisCli := apictx.Svc.Redis
+	reportBillKey := "report-bill-list:" + apictx.User.Parent
+	isExist := redisCli.Exists(apictx.CreateRepoCtx().Ctx, reportBillKey).Val()
+	// 不存在这个key时
+	if isExist < 1 {
+		if len(purchases) > 0 {
+			for _, purchase := range purchases {
+				member := "purchase_" + purchase.Id.Hex()
+				score := purchase.CompleteTime.Unix()
+				redisCli.ZAdd(apictx.CreateRepoCtx().Ctx, reportBillKey, &redis.Z{Score: float64(score), Member: member})
+				typeBills[member] = purchase
+			}
+		}
+		if len(produces) > 0 {
+			for _, produce := range produces {
+				member := "produce_" + produce.Id.Hex()
+				score := produce.CompleteTime.Unix()
+				redisCli.ZAdd(apictx.CreateRepoCtx().Ctx, reportBillKey, &redis.Z{Score: float64(score), Member: member})
+				typeBills[member] = produce
+			}
+		}
+		if len(products) > 0 {
+			for _, product := range products {
+				member := "product_" + product.Id.Hex()
+				score := product.CompleteTime.Unix()
+				redisCli.ZAdd(apictx.CreateRepoCtx().Ctx, reportBillKey, &redis.Z{Score: float64(score), Member: member})
+				typeBills[member] = product
+			}
+		}
+		// 设置过期时间
+		redisCli.Expire(apictx.CreateRepoCtx().Ctx, reportBillKey, 1*time.Second)
+
+	}
+
+	total, err := redisCli.ZCard(apictx.CreateRepoCtx().Ctx, reportBillKey).Uint64()
+	fmt.Println("单据总数量: ", total)
+	if err != nil {
+		fmt.Println(err)
+		return nil, err
+	}
+
+	reports, err := redisCli.ZRevRange(apictx.CreateRepoCtx().Ctx, reportBillKey, 0, -1).Result()
+	if err != nil {
+		return nil, err
+	}
+
+	if len(reports) < 1 {
+		return nil, errors.New("没有单据信息")
+	}
+
+	// 获取供应商名称
+	// supplier := &model.Supplier{}
+	// repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 	CollectName: repo.CollectionSupplier,
+	// 	Query:       repo.Map{"_id": supplierId},
+	// 	Project:     []string{"name"},
+	// }, supplier)
+
+	r := regexp.MustCompile(`/`)
+	ct := time.Now().Format("20060102150405")
+	zipName := fmt.Sprintf("统计单据_%s", ct)
+	// 获取订单信息下载
+	saveTmpDir := fmt.Sprintf("tmp1/report/%s", zipName)
+
+	if isExistDir(saveTmpDir) {
+		os.RemoveAll(saveTmpDir)
+	}
+
+	// 记录文件数量
+	var wg sync.WaitGroup
+	c1 := make(chan int, 10)
+	for _, tId := range reports {
+		productName := ""
+		supplierName := ""
+		serialNumber := ""
+		f := excelize.NewFile()
+		index := f.NewSheet("Sheet1")
+		f.SetActiveSheet(index)
+		f.SetDefaultFont("宋体")
+
+		tidArr := strings.Split(tId, "_")
+		var billExcel IExcel
+		// 采购
+		if tidArr[0] == "purchase" {
+			purchase := typeBills[tId].(*model.PurchaseBill)
+
+			billExcel = NewPurchaseBill(f)
+			// 获取签名信息
+			if purchase.Reviewed == 1 {
+				if len(purchase.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+				}
+			}
+			// 获取计划名
+			plan := &model.ProductPlan{}
+			repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionProductPlan,
+				Query:       repo.Map{"_id": purchase.PlanId},
+				Project:     []string{"name"},
+			}, plan)
+			productName = purchase.ProductName
+			supplierName = purchase.Supplier
+			serialNumber = purchase.SerialNumber
+			billExcel.SetContent(purchase)
+			billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", plan.Name))
+
+		}
+		// 工艺
+		if tidArr[0] == "produce" {
+			produce := typeBills[tId].(*model.ProduceBill)
+			billExcel = NewProduceBill(f)
+			if produce.Reviewed == 1 {
+				if len(produce.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+				}
+
+			}
+			// 获取计划名
+			plan := &model.ProductPlan{}
+			repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionProductPlan,
+				Query:       repo.Map{"_id": produce.PlanId},
+				Project:     []string{"name"},
+			}, plan)
+			productName = produce.ProductName
+			supplierName = produce.Supplier
+			serialNumber = produce.SerialNumber
+			billExcel.SetContent(produce)
+			billExcel.SetTitle(fmt.Sprintf("%s加工单", plan.Name))
+		}
+		// 成品采购
+		if tidArr[0] == "product" {
+			product := typeBills[tId].(*model.ProductBill)
+			billExcel = NewProductBill(f)
+			if product.Reviewed == 1 {
+				if len(product.SignUsers) > 0 {
+					signs := []*model.Signature{}
+					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
+						CollectName: repo.CollectionSignature,
+						Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
+						Sort:        bson.M{"sort": 1},
+					}, &signs)
+					billExcel.SetSignatures(signs)
+				}
+			}
+			// 获取计划名
+			plan := &model.ProductPlan{}
+			repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+				CollectName: repo.CollectionProductPlan,
+				Query:       repo.Map{"_id": product.PlanId},
+				Project:     []string{"name"},
+			}, plan)
+			productName = product.ProductName
+			supplierName = product.Supplier
+			serialNumber = product.SerialNumber
+			billExcel.SetContent(product)
+			billExcel.SetTitle(plan.Name)
+		}
+		billExcel.SetIsPdf(_isPdf)
+		billExcel.Draws()
+		buf, _ := f.WriteToBuffer()
+		ft := "xlsx"
+		if _isPdf == "true" {
+			ft = "pdf"
+		}
+
+		_productName := r.ReplaceAllString(productName, `&`)
+		_supplierName := r.ReplaceAllString(supplierName, `&`)
+		targePdfName := fmt.Sprintf("%s-%s-%s.%s", _supplierName, _productName, serialNumber, ft)
+
+		wg.Add(1)
+		if _isPdf == "true" {
+			go toPdfAndSaveTask(buf, apictx.Svc.Conf.PdfApiAddr, saveTmpDir, targePdfName, c1, &wg)
+		} else {
+			go saveExcelToTmp(saveTmpDir, targePdfName, buf.Bytes(), c1, &wg)
+		}
+
+	}
+	go func() {
+		// 等待所有goroutine
+		wg.Wait()
+		// 关闭channel
+		close(c1)
+	}()
+	// channel关闭后结束后停止遍历接收channel中的值
+	for n := range c1 {
+		if n == -1 {
+			return nil, errors.New("下载失败,请重试")
+		}
+	}
+
+	c.Header("Content-Type", "application/octet-stream")
+	c.Header("Content-Disposition", "attachment; filename="+zipName+".zip")
+	c.Header("Content-Transfer-Encoding", "binary")
+
+	archive := zip.NewWriter(c.Writer)
+	defer archive.Close()
+	// 遍历路径信息
+	filepath.Walk(saveTmpDir, func(path string, info os.FileInfo, _ error) error {
+
+		// 如果是源路径,提前进行下一个遍历
+		if path == saveTmpDir {
+			return nil
+		}
+
+		// 获取:文件头信息
+		header, _ := zip.FileInfoHeader(info)
+		header.Name = strings.TrimPrefix(path, saveTmpDir+`/`)
+
+		// 判断:文件是不是文件夹
+		if info.IsDir() {
+			header.Name += `/`
+		} else {
+			// 设置:zip的文件压缩算法
+			header.Method = zip.Deflate
+		}
+
+		// 创建:压缩包头部信息
+		writer, _ := archive.CreateHeader(header)
+		if !info.IsDir() {
+			file, _ := os.Open(path)
+			defer file.Close()
+			io.Copy(writer, file)
+		}
+		return nil
+	})
+
+	// 删除缓存目录
+	os.RemoveAll(saveTmpDir)
+
+	return nil, nil
 }
 
 // 加工单

+ 24 - 0
boxcost/api/utils.go

@@ -10,6 +10,7 @@ import (
 	"math/rand"
 	"net/http"
 	"os"
+	"sync"
 	"time"
 	"unsafe"
 
@@ -83,6 +84,29 @@ func savePdfToTmp(saveTmpDir, fileName string, data []byte) error {
 
 }
 
+// 保存文件
+func saveExcelToTmp(saveTmpDir, fileName string, data []byte, toExcelResult chan<- int, wg *sync.WaitGroup) {
+
+	err := os.MkdirAll(saveTmpDir, os.ModePerm)
+	if err != nil {
+		log.Error(err)
+		fmt.Println(err)
+		toExcelResult <- -1
+		wg.Done()
+		return
+	}
+	fullFileName := fmt.Sprintf("%s/%s", saveTmpDir, fileName)
+	if err := os.WriteFile(fullFileName, data, 0666); err != nil {
+		log.Error(err)
+		fmt.Println(err)
+		toExcelResult <- -1
+		wg.Done()
+		return
+	}
+	toExcelResult <- 1
+	wg.Done()
+}
+
 func isExistDir(dir string) bool {
 	_, err := os.Stat(dir)
 	if err != nil {