package api import ( "box-cost/db/model" "box-cost/db/repo" "box-cost/log" "errors" "fmt" "regexp" "strings" "time" "github.com/gin-gonic/gin" "github.com/xuri/excelize/v2" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) type PlanIds struct { Ids []primitive.ObjectID `json:"ids"` } var PlanSearchFields = []string{"_id", "name", "createUser", "total", "status", "totalPrice", "updateTime", "createTime"} // 计划追踪管理 func PlanTrack(r *GinRouter) { // 新增计划追踪 r.POSTJWT("/planTrack/create", CreatePlanTrack) // 获取计划追踪信息 r.GETJWT("/planTrack/detail/:id", GetPlanTrack) // 获取计划追踪列表 r.GETJWT("/planTrack/list", GetPlanTracks) // 计划追踪列表 r.POSTJWT("/planTrack/update", UpdatePlanTrack) r.POSTJWT("/planTrack/delete/:id", DelPlanTrack) } func CreatePlanTrack(c *gin.Context, apictx *ApiSession) (interface{}, error) { var PlanTrack model.PlanTrack err := c.ShouldBindJSON(&PlanTrack) if err != nil { return nil, errors.New("参数错误!") } ctx := apictx.CreateRepoCtx() if len(PlanTrack.Name) < 1 { return nil, errors.New("计划追踪名为空") } if len(PlanTrack.PlanIds) < 1 { return nil, errors.New("计划追踪id为空") } PlanTrack.CreateTime = time.Now() PlanTrack.UpdateTime = time.Now() PlanTrack.UserId, _ = primitive.ObjectIDFromHex(apictx.User.ID) result, err := repo.RepoAddDoc(ctx, repo.CollectionPlanTrack, &PlanTrack) return result, err } // 获取计划追踪信息 func GetPlanTrack(c *gin.Context, apictx *ApiSession) (interface{}, error) { PlanTrackId := c.Param("id") id, err := primitive.ObjectIDFromHex(PlanTrackId) if err != nil { return nil, errors.New("非法id") } var PlanTrack model.PlanTrack found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionPlanTrack, Query: repo.Map{"_id": id}, }, &PlanTrack) if !found || err != nil { log.Info(err) return nil, errors.New("数据未找到") } userId, _ := primitive.ObjectIDFromHex(apictx.User.ID) // 获取用户信息 userInfo, err := getUserById(apictx, userId) if err != nil { return nil, errors.New("用户信息获取失败") } PlanTrack.UserInfo = userInfo // 查询包含计划的详细信息 for _, planId := range PlanTrack.PlanIds { plan := &model.ProductPlan{} _, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": planId}, Project: PlanSearchFields, }, plan) if err != nil { log.Info(err) continue } PlanTrack.Plans = append(PlanTrack.Plans, plan) } return PlanTrack, nil } // 获取计划追踪列表 func GetPlanTracks(c *gin.Context, apictx *ApiSession) (interface{}, error) { page, size, query := UtilQueryPageSize(c) if _name, ok := query["name"]; ok { delete(query, "name") query["name"] = bson.M{"$regex": _name.(string)} } option := &repo.PageSearchOptions{ CollectName: repo.CollectionPlanTrack, Query: query, Page: page, Size: size, Sort: bson.M{"createTime": -1}, } return repo.RepoPageSearch(apictx.CreateRepoCtx(), option) } func UpdatePlanTrack(c *gin.Context, apictx *ApiSession) (interface{}, error) { var pt model.PlanTrack err := c.ShouldBindJSON(&pt) if err != nil { return nil, errors.New("参数错误") } if pt.Id == primitive.NilObjectID { return nil, errors.New("id的为空") } pt.UpdateTime = time.Now() return repo.RepoUpdateSetDoc1(apictx.CreateRepoCtx(), repo.CollectionPlanTrack, pt.Id.Hex(), &pt, &repo.RecordLogReq{ Path: c.Request.URL.Path, TargetId: pt.Id.Hex(), }) } // 删除计划追踪 func DelPlanTrack(c *gin.Context, apictx *ApiSession) (interface{}, error) { PlanTrackId := c.Param("id") if PlanTrackId == "" { return nil, errors.New("id为空") } return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionPlanTrack, PlanTrackId) } func DownloadPlanTrack(c *gin.Context, apictx *ApiSession) (interface{}, error) { var form PlanIds err := c.ShouldBindJSON(&form) if err != nil { return nil, err } if len(form.Ids) == 0 { return nil, errors.New("ids为空") } plans := []*model.ProductPlan{} for _, objId := range form.Ids { fmt.Println(objId.Hex()) plan := &model.ProductPlan{} _, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionProductPlan, Query: repo.Map{"_id": objId}, }, plan) if err != nil { fmt.Println(err) log.Info(err) continue } plans = append(plans, plan) } f := excelize.NewFile() index := f.NewSheet("Sheet1") f.SetActiveSheet(index) f.SetDefaultFont("宋体") planTrackExcel := NewPlanProcessTrackExcel(f) planStatusInfos, err := handlPlanStatus(apictx, plans) if err != nil { return nil, err } planTrackExcel.Content = planStatusInfos //设置对应的数据 planTrackExcel.Draws() date := time.Now().Format("2006年01月02日_150405") fileName := fmt.Sprintf("礼盒加工追踪表_%s.xlsx", date) c.Header("Content-Type", "application/octet-stream") c.Header("Content-Disposition", "attachment; filename="+fileName) c.Header("Content-Transfer-Encoding", "binary") err = f.Write(c.Writer) if err != nil { return nil, err } return nil, nil } const ( EXCEL_TMPLATE_FILE = "tmplate/tmplate.xlsx" CELL_BACKGROUND = "808080" ) // var needChangeCol = map[string]string{ // "部件": "E", // "下单": "F", // "纸张": "G", // "印刷": "H", // "覆膜": "I", // "烫金": "J", // "丝印": "K", // "对裱": "L", // "压纹": "M", // "裱瓦": "N", // "模切": "O", // "粘盒": "P", // "组装": "Q", // "交货": "R", // } func MatchString(targetStr string, regexPattern string) bool { // 编译正则表达式 re := regexp.MustCompile(regexPattern) // 检查目标字符串是否包含匹配的子串 return re.MatchString(targetStr) } type PlanStatusInfo struct { PlanName string `json:"planName"` PlanUnit string `json:"planUnit"` PlanCount int `json:"planCount"` PlanRowStart int `json:"planRowStart"` PlanRowEnd int `json:"planRowEnd"` PlanCompStatus []map[string]string `json:"planCompStatus"` } func handlPlanStatus(apictx *ApiSession, plans []*model.ProductPlan) ([]*PlanStatusInfo, error) { row := 5 planStatusInfos := make([]*PlanStatusInfo, 0) planCompStatus := []map[string]string{} for _, plan := range plans { startRow := row for _, comp := range plan.Pack.Components { // ""代表该部件没有该工艺 "〇"代表正在进行的工艺 // "808080"背景颜色代表部件所含未进行工艺 √代表已完成工序 compStatus := map[string]string{ "部件": " ", "行数": "5", // 部件中只要有一个订单就说明下单了 "下单": " ", // 采购单中type为纸张 订单对应状态为完成 "纸张": " ", // 遍历工艺单,工序中包含印刷 "印刷": " ", // 遍历工艺单,工序中包含覆膜 "覆膜": " ", // 遍历工艺单,工序中包含烫金 "烫金": " ", // 遍历工艺单,工序中包含丝印 "丝印": " ", // 遍历工艺单,工序中包含对裱 "对裱": " ", // 遍历工艺单,工序中包含压纹 "压纹": " ", // 遍历工艺单,工序中包含裱瓦 "裱瓦": " ", // 遍历工艺单,工序中包含模切 "模切": " ", // 遍历工艺单,工序中包含粘盒 "粘盒": " ", // 遍历工艺单,工序中包含组装 "组装": " ", "交货": " ", } fmt.Println(plan.Name) fmt.Println(comp.Name) fmt.Println(row) fmt.Println("------------------------------------") compStatus["部件"] = comp.Name compStatus["行数"] = fmt.Sprintf("%d", row) row++ // 去重获取所有订单 seen := make(map[string]bool) tbills := make([]string, 0, len(comp.Stages)) // 结果数组,容量初始化为原数组的长度 // ???:1 最后一个工序没下单 // isBill := true // lastStage := comp.Stages[len(comp.Stages)-1] // if len(lastStage.BillId) < 24 { // isBill = false // } for _, stage := range comp.Stages { if len(stage.BillId) > 0 { value := fmt.Sprintf("%d_%s", stage.BillType, stage.BillId) if _, ok := seen[value]; !ok { // 标记为已出现 seen[value] = true // 添加到结果数组 tbills = append(tbills, value) } } else { // 产品中填写了这个工序但是没有下单 黑色背景 for k := range compStatus { if k == "下单" || k == "交货" || k == "部件" || k == "行数" { continue } // 纸张 只要是采购单就设置纸张状态 if stage.Type == 1 { compStatus["纸张"] = CELL_BACKGROUND } // 匹配工艺关键字 匹配到就设置状态为黑背景 if MatchString(stage.Name, k) { compStatus[k] = CELL_BACKGROUND } } } } // fmt.Println(tbills) // 如果tbills为空,说明该部件没有订单 if len(tbills) == 0 { // 该部件没有订单,跳过 planCompStatus = append(planCompStatus, compStatus) continue } // 查询数据库获取bill详细信息 // 最后工序订单号 lastBillType := tbills[len(tbills)-1] for _, billType := range tbills { compStatus["下单"] = "√" bt := strings.Split(billType, "_")[0] billId, _ := primitive.ObjectIDFromHex(strings.Split(billType, "_")[1]) if bt == "1" { // 查询采购单 bill := &model.PurchaseBill{} _, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillPurchase, Query: repo.Map{"_id": billId}, }, bill) if err != nil { fmt.Printf("billId:%s---%s", billId.Hex(), err.Error()) return nil, errors.New("采购单不存在") } // if bill.Type == "纸张" { // compStatus["纸张"] = "〇" // if bill.Status == "complete" { // compStatus["纸张"] = "√" // } // } compStatus["纸张"] = "〇" if bill.Status == "complete" { compStatus["纸张"] = "√" } // if !isBill { // continue // } if lastBillType == billType { for _, paper := range bill.Paper { compStatus["交货"] = fmt.Sprintf("%d", paper.ConfirmCount) } } } if bt == "2" { // 查询工艺单 bill := &model.ProduceBill{} _, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduce, Query: repo.Map{"_id": billId}, }, bill) if err != nil { fmt.Printf("billId:%s---%s", billId.Hex(), err.Error()) return nil, errors.New("工艺单不存在") } for _, produce := range bill.Produces { for k := range compStatus { if k == "下单" || k == "纸张" || k == "交货" || k == "部件" || k == "行数" { continue } if MatchString(produce.Name, k) { compStatus[k] = "〇" if bill.Status == "complete" { compStatus[k] = "√" } } // 直接赋值,如果这个订单是最后一个,则状态覆盖 // ???思考:如果最后一个工序没有生成订单的话,是按最后一个工序还是最后一个订单?这里是最后一个订单 // ???:1 // if !isBill { // continue // } compStatus["交货"] = fmt.Sprintf("%d", produce.ConfirmCount) } } } // 暂时没有状态标定 if bt == "3" { // 查询成品单 bill := &model.ProductBill{} _, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{ CollectName: repo.CollectionBillProduct, Query: repo.Map{"_id": billId}, }, bill) if err != nil { fmt.Printf("billId:%s---%s", billId.Hex(), err.Error()) return nil, errors.New("成品单不存在") } // fmt.Println(bill) // ?? 这里需不需要 影响正确数据吗? // if !isBill { // continue // } if lastBillType == billType { for _, product := range bill.Products { compStatus["交货"] = fmt.Sprintf("%d", product.ConfirmCount) } } } } planCompStatus = append(planCompStatus, compStatus) } // fmt.Println(plan.Name) // fmt.Println("rowstart:", startRow) // fmt.Println("rowend:", row-1) planStatusInfos = append(planStatusInfos, &PlanStatusInfo{ PlanName: plan.Name, // !这个规格好像没有 PlanUnit: "", PlanCount: plan.Total, PlanRowStart: startRow, PlanRowEnd: row - 1, PlanCompStatus: planCompStatus, }) } return planStatusInfos, nil }