|
@@ -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
|
|
|
}
|
|
|
|
|
|
// 加工单
|