package api import ( "archive/zip" "box-cost/db/model" "box-cost/db/repo" "box-cost/log" "bytes" "errors" "fmt" "io" "os" "path/filepath" "regexp" "strings" "sync" "time" "github.com/gin-gonic/gin" "github.com/xuri/excelize/v2" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) // 生产计划管理 func ProductPlan(r *GinRouter) { // 创建生产计划 r.POST("/plan/create", CreateProductPlan) // 获取生产计划详情 r.GET("/plan/detail/:id", GetProductPlan) // 获取生产计划列表 r.GET("/plan/list", GetProductPlans) // 更新生产计划 r.POST("/plan/update", UpdateProductPlan) // 删除生产计划 r.POST("/plan/delete/:id", DelProductPlan) // 下载部件单据 // r.GET("/bill/plan/download", DownLoadCompBills) r.GET("/bill/plan/download", DownLoadPlanBills) r.GET("/bill/plan/downloadPdf", DownLoadPlanBillsPdf) // 生产成本表 r.GET("/plan/cost/download", DownLoadPlanCost) } type SupplierPlanCost struct { *model.ProductPlan SupplierId string } func DownLoadPlanCost(c *gin.Context, apictx *ApiSession) (interface{}, error) { _planId := c.Query("id") supplierId := c.Query("supplierId") planId, _ := primitive.ObjectIDFromHex(_planId) if planId.IsZero() { return nil, errors.New("planId错误") } supplierPlanCost := &SupplierPlanCost{} plan := model.ProductPlan{} found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": planId}, }, &plan) if !found || err != nil { return nil, errors.New("数据未找到") } supplierPlanCost.ProductPlan = &plan supplierPlanCost.SupplierId = supplierId f := excelize.NewFile() index := f.NewSheet("Sheet1") f.SetActiveSheet(index) f.SetDefaultFont("宋体") planCostExcel := NewPlanCostExcel(f) planCostExcel.Title = fmt.Sprintf("生产成本表(%s)%d盒", plan.Name, plan.Total) planCostExcel.Content = supplierPlanCost planCostExcel.Draws() c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", "attachment; filename="+"planCost.xlsx") c.Header("Content-Transfer-Encoding", "binary") err = f.Write(c.Writer) if err != nil { return nil, err } return nil, nil } func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error) { _planId := c.Query("id") planId, err := primitive.ObjectIDFromHex(_planId) if err != nil { return nil, errors.New("planId错误") } plan := model.ProductPlan{} found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": planId}, }, &plan) if !found || err != nil { return nil, errors.New("数据未找到") } // 获取所有stages单据id billIds := make([]string, 0) for _, comp := range plan.Pack.Components { if comp.Id == "" || len(comp.Stages) == 0 { continue } for _, stage := range comp.Stages { billId, _ := primitive.ObjectIDFromHex(stage.BillId) if !billId.IsZero() { billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId)) } } } // 去重单据号 typeBillIds := removeDuplicationSort(billIds) if len(typeBillIds) < 1 { return nil, errors.New("未找到单据信息") } f := excelize.NewFile() index := f.NewSheet("Sheet1") f.SetActiveSheet(index) f.SetDefaultFont("宋体") companyName := getCompanyName(apictx) row := 0 for _, tId := range typeBillIds { tidArr := strings.Split(tId, "_") var billExcel IExcel // 采购 billId, _ := primitive.ObjectIDFromHex(tidArr[1]) if tidArr[0] == "1" { purchase := model.PurchaseBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillPurchase, Query: repo.Map{"_id": billId}, }, &purchase) if found { 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) } } billExcel.SetContent(&purchase) billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName)) } } // 工艺 if tidArr[0] == "2" { produce := model.ProduceBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduce, Query: repo.Map{"_id": billId}, }, &produce) if found { 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) } } billExcel.SetContent(&produce) billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName)) } } // 成品采购 if tidArr[0] == "3" { product := model.ProductBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduct, Query: repo.Map{"_id": billId}, }, &product) if found { 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) } } billExcel.SetContent(&product) billExcel.SetTitle(companyName) } } if billExcel == nil { continue } billExcel.SetRow(row) billExcel.Draws() row = billExcel.GetRow() + 5 } c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx") c.Header("Content-Transfer-Encoding", "binary") err = f.Write(c.Writer) if err != nil { return nil, err } return nil, nil } func DownLoadPlanBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, error) { _planId := c.Query("id") planId, err := primitive.ObjectIDFromHex(_planId) if err != nil { return nil, errors.New("planId错误") } plan := model.ProductPlan{} found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": planId}, }, &plan) if !found || err != nil { return nil, errors.New("数据未找到") } // 获取所有stages单据id billIds := make([]string, 0) for _, comp := range plan.Pack.Components { if comp.Id == "" || len(comp.Stages) == 0 { continue } for _, stage := range comp.Stages { billId, _ := primitive.ObjectIDFromHex(stage.BillId) if !billId.IsZero() { billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId)) } } } // 去重单据号 typeBillIds := removeDuplicationSort(billIds) if len(typeBillIds) < 1 { return nil, errors.New("未找到单据信息") } companyName := getCompanyName(apictx) _planName := plan.Name r := regexp.MustCompile(`/`) planName := r.ReplaceAllString(_planName, `&`) // fmt.Println(planName) // 打包pdf的缓存目录 saveTmpDir := fmt.Sprintf("tmp1/%s", planName) if isExistDir(saveTmpDir) { os.RemoveAll(saveTmpDir) } // 记录文件数量 var wg sync.WaitGroup c1 := make(chan int) for _, tId := range typeBillIds { productName := "" supplierName := "" f := excelize.NewFile() index := f.NewSheet("Sheet1") f.SetActiveSheet(index) f.SetDefaultFont("宋体") tidArr := strings.Split(tId, "_") var billExcel IExcel // 采购 billId, _ := primitive.ObjectIDFromHex(tidArr[1]) if tidArr[0] == "1" { purchase := model.PurchaseBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillPurchase, Query: repo.Map{"_id": billId}, }, &purchase) if found { 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) } } productName = purchase.ProductName supplierName = purchase.Supplier billExcel.SetContent(&purchase) billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName)) } } // 工艺 if tidArr[0] == "2" { produce := model.ProduceBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduce, Query: repo.Map{"_id": billId}, }, &produce) if found { 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) } } productName = produce.ProductName supplierName = produce.Supplier billExcel.SetContent(&produce) billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName)) } } // 成品采购 if tidArr[0] == "3" { product := model.ProductBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduct, Query: repo.Map{"_id": billId}, }, &product) if found { 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) } } productName = product.ProductName supplierName = product.Supplier billExcel.SetContent(&product) billExcel.SetTitle(companyName) } } if billExcel == nil { continue } billExcel.SetIsPdf("true") billExcel.Draws() buf, _ := f.WriteToBuffer() // r := regexp.MustCompile(`/`) _productName := r.ReplaceAllString(productName, `&`) _supplierName := r.ReplaceAllString(supplierName, `&`) targePdfName := fmt.Sprintf("%s-%s.pdf", _productName, _supplierName) // fmt.Println(targePdfName) wg.Add(1) go toPdfAndSaveTask(buf, apictx.Svc.Conf.PdfApiAddr, saveTmpDir, targePdfName, 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="+planName+".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 } type ToPdfResult struct { IsSucc bool Err error } func toPdfAndSaveTask(buf *bytes.Buffer, toPdfAddr, saveTmpDir, targetPdfName string, toPdfResult chan<- int, wg *sync.WaitGroup) { if buf.Len() < 1<<10 { fmt.Println("execl内容为空") log.Error("execl内容为空") toPdfResult <- -1 wg.Done() return } res, err := excelToPdf(buf, toPdfAddr) if err != nil { fmt.Println(err) log.Error(err) // pdfRes := ToPdfResult{ // IsSucc: false, // Err: err, // } // toPdfResult <- pdfRes toPdfResult <- -1 wg.Done() return } byteData, err := io.ReadAll(res.Body) if err != nil { fmt.Println(err) // pdfRes := ToPdfResult{ // IsSucc: false, // Err: err, // } // toPdfResult <- pdfRes toPdfResult <- -1 wg.Done() return } if len(byteData) < 1 { fmt.Println("pdf内容为空") log.Error("pdf内容为空") toPdfResult <- -1 wg.Done() return } defer res.Body.Close() err = savePdfToTmp(saveTmpDir, targetPdfName, byteData) if err != nil { // pdfRes := ToPdfResult{ // IsSucc: false, // Err: err, // } // toPdfResult <- pdfRes toPdfResult <- -1 wg.Done() return } // pdfRes := ToPdfResult{ // IsSucc: true, // Err: err, // } // toPdfResult <- pdfRes toPdfResult <- 1 wg.Done() } func DownLoadCompBills(c *gin.Context, apictx *ApiSession) (interface{}, error) { _planId := c.Query("id") compId := c.Query("compId") planId, err := primitive.ObjectIDFromHex(_planId) if err != nil { return nil, errors.New("planId错误") } plan := model.ProductPlan{} found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": planId}, }, &plan) if !found || err != nil { return nil, errors.New("数据未找到") } // 获取部件单据 curComp := &model.PackComponent{} for _, comp := range plan.Pack.Components { if comp.Id == compId { curComp = comp } } if curComp.Id == "" { return nil, errors.New("该组件不存在") } // 获取bill if len(curComp.Stages) == 0 { return nil, errors.New("该组件数据不存在") } // 获取不同类型的单据id billIds := make([]string, 0) for _, stage := range curComp.Stages { billId, _ := primitive.ObjectIDFromHex(stage.BillId) if !billId.IsZero() { billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId)) } } // 去重单据号 typeBillIds := removeDuplicationSort(billIds) if len(typeBillIds) < 1 { return nil, errors.New("未找到单据信息") } f := excelize.NewFile() index := f.NewSheet("Sheet1") f.SetActiveSheet(index) f.SetDefaultFont("宋体") companyName := getCompanyName(apictx) row := 0 for _, tId := range typeBillIds { tidArr := strings.Split(tId, "_") var billExcel IExcel // 采购 billId, _ := primitive.ObjectIDFromHex(tidArr[1]) if tidArr[0] == "1" { purchase := model.PurchaseBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillPurchase, Query: repo.Map{"_id": billId}, }, &purchase) if found { 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) } } billExcel.SetContent(&purchase) billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName)) } } // 工艺 if tidArr[0] == "2" { produce := model.ProduceBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduce, Query: repo.Map{"_id": billId}, }, &produce) if found { 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) } } billExcel.SetContent(&produce) billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName)) } } // 成品采购 if tidArr[0] == "3" { product := model.ProductBill{} found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduct, Query: repo.Map{"_id": billId}, }, &product) if found { 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) } } billExcel.SetContent(&product) billExcel.SetTitle(companyName) } } if billExcel == nil { continue } billExcel.SetRow(row) billExcel.Draws() row = billExcel.GetRow() + 5 } c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx") c.Header("Content-Transfer-Encoding", "binary") err = f.Write(c.Writer) if err != nil { return nil, err } return nil, nil } // 创建生产计划 func CreateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) { var plan model.ProductPlan err := c.ShouldBindJSON(&plan) if err != nil { fmt.Println(err) return nil, errors.New("参数错误!") } if plan.Name == "" { return nil, errors.New("生产计划名为空") } if plan.Total == 0 { return nil, errors.New("生产计划数应不为0") } plan.Status = "process" // 进行中 plan.CreateTime = time.Now() plan.UpdateTime = time.Now() result, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, &plan) return result, err } // 获取生产计划信息 func GetProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) { planId := c.Param("id") id, err := primitive.ObjectIDFromHex(planId) if err != nil { return nil, errors.New("非法id") } var plan model.ProductPlan option := &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": id}, } found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), option, &plan) if !found || err != nil { log.Info(err) return nil, errors.New("数据未找到") } billStates := map[string]string{} if plan.Pack != nil && plan.Pack.Components != nil { for _, comp := range plan.Pack.Components { if comp.Stages != nil { for _, stage := range comp.Stages { if len(stage.BillId) > 0 { collectName := "" // 材料 if stage.BillType == 1 { collectName = repo.CollectionBillPurchase } // 工艺 if stage.BillType == 2 { collectName = repo.CollectionBillProduce } // 成品 if stage.BillType == 3 { collectName = repo.CollectionBillProduct } ok, state := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{CollectName: collectName, Query: repo.Map{"_id": stage.BillId}, Project: []string{"status"}}) if ok { billStates[stage.BillId] = state["status"].(string) } } } } } } return map[string]interface{}{ "plan": plan, "billStates": billStates, }, nil } // 获取生产计划列表 func GetProductPlans(c *gin.Context, apictx *ApiSession) (interface{}, error) { page, size, query := UtilQueryPageSize(c) if _packId, ok := query["packId"]; ok { packId, _ := primitive.ObjectIDFromHex(_packId.(string)) query["pack._id"] = packId delete(query, "packId") } if _name, ok := query["name"]; ok { delete(query, "name") query["name"] = bson.M{"$regex": _name.(string)} } option := &repo.PageSearchOptions{ CollectName: repo.CollectionProductPlan, Query: query, Page: page, Size: size, Sort: bson.M{"createTime": -1}, Project: []string{"_id", "thumbnail", "name", "updateTime", "createTime", "createUser", "total", "totalPrice", "status"}, } return repo.RepoPageSearch(apictx.CreateRepoCtx(), option) } // 更新生产计划 func UpdateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) { var plan model.ProductPlan err := c.ShouldBindJSON(&plan) if err != nil { return nil, errors.New("参数错误") } if plan.Id.Hex() == "" { return nil, errors.New("id的为空") } plan.UpdateTime = time.Now() return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, plan.Id.Hex(), &plan) } // 删除生产计划 func DelProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) { planId := c.Param("id") if planId == "" { return nil, errors.New("id为空") } return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId) }