package logic import ( "context" "errors" "fmt" "io" "lone-services/pkg/modelbase" "lone-services/pkg/utils" "lone-services/rpc/express/pb" order "lone-services/rpc/order/pb" product "lone-services/rpc/product/pb" "lone-services/services/express/internal/dao" "lone-services/services/express/internal/model" "lone-services/services/express/internal/svc" "lone-services/services/express/internal/transit" "lone-services/services/express/validator" "math" "math/rand" "net/http" "os" "path/filepath" "strconv" "strings" "time" "github.com/zeromicro/go-zero/core/logx" ) type ExpressDeliveringLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger BaseLogic } func NewExpressDeliveringLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ExpressDeliveringLogic { return &ExpressDeliveringLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } // ExpressDelivering 发货 func (l *ExpressDeliveringLogic) ExpressDelivering(in *express.DeliveringRequest) (*express.Response, error) { var v validator.DeliveringValidator if fail := l.checkParams(in, &v); fail != nil { return fail, nil } adminInfo := utils.GetUserFromCtx(l.ctx) if adminInfo.ID < utils.NumberOne { return l.fail(utils.ErrorNoLoginInfo) } var itemsEdit []dao.DeliveryOrderPrintEdit w := modelbase.Params{ Eq: map[string]string{"id": strconv.FormatInt(in.Id, utils.NumberTen)}} modelObj := model.DeliveryOrderModel{}.Init() err := modelObj.Items(w, &itemsEdit) if err != nil { l.Logger.Error(err) return l.fail(utils.Fail) } if len(itemsEdit) < utils.NumberOne { return l.fail(utils.ErrorNotFund) } if itemsEdit[utils.NumberZero].Status == dao.SendStatusOk || itemsEdit[utils.NumberZero].Status == dao.SendStatusOver { return l.fail(utils.ErrorOrderOver) } tw := modelbase.Params{Eq: map[string]string{"id": strconv.FormatInt(in.TransitId, utils.NumberTen)}} var transitInfo dao.ExpressSend err = model.ExpressModel{}.Init().GetOne(tw, &transitInfo) if err != nil { l.Logger.Error("发货地址信息没有找到", in.TransitId, err) return l.out(utils.ErrorNotFund, "没有查询到发货信息") } if transitInfo.Id < utils.NumberOne { return l.out(utils.ErrorNotFund, "没有查询到发货信息") } var items []dao.DeliveryOrderPrint items = append(items, dao.DeliveryOrderPrint{ DeliveryOrderPrintEdit: itemsEdit[utils.NumberZero], }) gStatus := l.getOrderInfo(&items) if gStatus != utils.Ok { return l.fail(gStatus) } info := itemsEdit[utils.NumberZero] //更新使用的快递类型 info.TransitType = transitInfo.TransitId info.OrderSn = l.getOrderSn(info.TransitType, int(adminInfo.ID)) items[utils.NumberZero].OrderSn = info.OrderSn status := info.Status client, res, cErr := l.createTransit(items[utils.NumberZero], transitInfo) if cErr != utils.Ok { return l.fail(cErr) } info.TransitInfo = res.PostInfo info.TransitContent = res.RequestInfo info.TransitSn = strings.Join(res.WaybillNo, utils.DecollatorComma) items[utils.NumberZero].AdminName = adminInfo.Name res2, gErr := l.getDown(client, res.WaybillNo, items[utils.NumberZero]) if gErr != nil { l.cancel(client, w, info, status) l.Logger.Error(res2, gErr) return l.fail(utils.SetError(utils.ErrorNotFund, "下载发货单出错")) } info.Express = utils.StructToJson(res2.Files) info.PrintStatus = utils.StatusOk info.AdminId = int(adminInfo.ID) info.AdminName = adminInfo.Name info.TransitId = int(in.TransitId) info.TransitName = transitInfo.Name info.Status = dao.SendStatusOk info.SendTime = utils.Now() items[utils.NumberZero].PrintStatus = info.PrintStatus items[utils.NumberZero].AdminId = info.AdminId items[utils.NumberZero].AdminName = info.AdminName items[utils.NumberZero].TransitId = info.TransitId items[utils.NumberZero].TransitInfo = info.TransitInfo items[utils.NumberZero].TransitContent = info.TransitContent items[utils.NumberZero].TransitSn = info.TransitSn items[utils.NumberZero].TransitName = info.TransitName items[utils.NumberZero].Status = info.Status items[utils.NumberZero].Express = info.Express var history dao.DeliveryHistoryCreate history.DeliverId = info.Id history.TransitName = dao.DeliverySendType[info.TransitType] history.TransitInfo = info.TransitInfo history.TransitContent = info.TransitContent history.TransitType = info.TransitType history.OrderSn = info.OrderSn var exps []dao.DeliveryExpCreate for i := 0; i < len(res.WaybillNo); i++ { exps = append(exps, dao.DeliveryExpCreate{ DeliveryOrderId: info.Id, TransitType: info.TransitType, ExpSn: res.WaybillNo[i], OrderSn: info.OrderSn, }) } sErr := l.saveDelever(w, &info, &history, &exps) if sErr != utils.Ok { l.cancel(client, w, info, status) l.Logger.Error(res2, sErr) return l.fail(sErr) } return l.ok(items[utils.NumberZero]) } func (l *ExpressDeliveringLogic) saveDelever(w modelbase.Params, info *dao.DeliveryOrderPrintEdit, history *dao.DeliveryHistoryCreate, exps *[]dao.DeliveryExpCreate) utils.Status { modelObj := model.DeliveryOrderModel{}.Init() modelObj.Begin() edit, err := modelObj.Edit(w, info) if err != nil || edit < utils.NumberOne { modelObj.Rollback() l.Logger.Error("save deliver goods err ", err) return utils.SetError(utils.Fail, "生成快递单失败") } err = model.DeliveryOrderExpModel{}.Init().CreateMulti(exps) if err != nil { modelObj.Rollback() l.Logger.Error("save exp err ", err) return utils.SetError(utils.Fail, "生成EXP失败") } err = model.DeliveryHistoryModel{}.Init().Create(history) if err != nil { modelObj.Rollback() l.Logger.Error("save history err ", err) return utils.SetError(utils.Fail, "生成订单历史失败") } if info.Type == dao.DeliveryTypeOrder { uSt := l.updateOrderDeliverStatus(info.ObjId, int32(info.Status)) if uSt != utils.Ok { modelObj.Rollback() l.Logger.Error("cancel update order status err ", info.ObjId, info.Status) return utils.SetError(utils.Fail, "更新订单状态失败") } } modelObj.Commit() return utils.Ok } func (l *ExpressDeliveringLogic) getOrderInfo(items *[]dao.DeliveryOrderPrint) utils.Status { for value, item := range *items { obj := &(*items)[value] mobile, aErr := utils.Crypto{}.AESDecryptECB(item.Mobile) if aErr == nil { obj.Mobile = mobile } obj.Url = utils.GetConfigString("transit.url") products, status := l.getProducts(item.ProductInfo) if status != utils.Ok { return status } obj.Number, obj.Weight, obj.ProductItems = l.getProductInfo(item.ObjSn, item.ProductInfo, &products) } return utils.Ok } func (l *ExpressDeliveringLogic) createTransit(info dao.DeliveryOrderPrint, transitInfo dao.ExpressSend) (transit.Transit, transit.TransitResult, utils.Status) { var client transit.Transit switch transitInfo.TransitId { case dao.ExpressCompanySf: client = transit.NewSfClient(transitInfo) case dao.ExpressCompanyJd: client = transit.NewJdClient(transitInfo) default: return client, transit.TransitResult{}, utils.SetError(utils.Fail, "不支持的快递公司") } res, err := client.Create(info) if err != nil || res.ErrorCode != utils.StringStatusOk { l.Logger.Error("create transit err", err, res) return client, res, utils.SetError(utils.Fail, res.ApiErrorMsg) } return client, res, utils.Ok } func (l *ExpressDeliveringLogic) getDown(client transit.Transit, orders []string, info dao.DeliveryOrderPrint) (transit.TransitResult, error) { ret, err := client.DownSheet(orders, info) if err != nil || len(ret.Files) < utils.NumberOne { l.Logger.Error("get down err ", orders, err, ret) if len(ret.Files) < utils.NumberOne { err = errors.New("files is empty") } return ret, err } path := utils.GetConfigString("transit.path") + "/" var tmpFiles []transit.TransitFiles for i := 0; i < len(ret.Files); i++ { tName := ret.Files[i].WaybillNo + ".pdf" err := l.downloadSfPDF(ret.Files[i], path+tName) if err != nil { l.Logger.Error("get down err ", orders, err, ret) return ret, err } tmpFiles = append(tmpFiles, transit.TransitFiles{ File: tName, WaybillNo: ret.Files[i].WaybillNo, }) } ret.Files = tmpFiles return ret, nil } func (l *ExpressDeliveringLogic) downloadSfPDF(file transit.TransitFiles, path string) error { req, err := http.NewRequest("GET", file.Url, nil) if err != nil { return err } // 2. 必须加这个请求头(顺丰强制) req.Header.Add("X-Auth-token", file.Token) // 3. 发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { l.Logger.Error(err) } }(resp.Body) // 4. 创建目录 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return err } // 5. 创建文件并保存 outFile, err := os.Create(path) if err != nil { return err } defer func(outFile *os.File) { err = outFile.Close() if err != nil { l.Logger.Error(err) } }(outFile) // 6. 拷贝数据 _, err = io.Copy(outFile, resp.Body) return err } func (l *ExpressDeliveringLogic) cancel(client transit.Transit, w modelbase.Params, info dao.DeliveryOrderPrintEdit, status uint8) { sn := info.OrderSn if info.TransitType == dao.ExpressCompanyJd { sn = info.TransitSn } ret, canErr := client.Cancel(sn) if canErr != nil || ret.ErrorCode != utils.StringStatusOk { l.Logger.Error("cancel order err ", sn, canErr, ret) return } var history dao.DeliveryHistoryCreate history.DeliverId = info.Id history.TransitName = dao.DeliverySendType[info.TransitType] history.TransitInfo = ret.PostInfo history.TransitContent = ret.RequestInfo history.TransitType = info.TransitType history.OrderSn = info.OrderSn info.OrderSn = utils.StringEmpty info.Status = status info.Express = utils.StringEmpty mobdelObj := model.DeliveryOrderModel{}.Init() mobdelObj.Begin() _, err := mobdelObj.Edit(w, &info) err = model.DeliveryHistoryModel{}.Init().Create(&history) if err != nil { mobdelObj.Rollback() l.Logger.Error("cancel up order err ", info.Id, err) return } ww := modelbase.Params{Eq: map[string]string{"deliver_goods_id": strconv.Itoa(info.Id)}} editExp := dao.DeliveryExpStatus{ Status: utils.StatusFail, } _, err = model.DeliveryOrderExpModel{}.Init().Edit(ww, &editExp) if err != nil { mobdelObj.Rollback() l.Logger.Error("cancel up order err ", info.Id, err) return } if info.Type == dao.DeliveryTypeOrder { uSt := l.updateOrderDeliverStatus(info.ObjId, int32(status)) if uSt != utils.Ok { mobdelObj.Rollback() l.Logger.Error("cancel update order status err ", info.ObjId, status) return } } mobdelObj.Commit() } func (l *ExpressDeliveringLogic) updateOrderDeliverStatus(id string, status int32) utils.Status { cli, err := svc.GetRpcClient(l.svcCtx.OrderSvcName) if err != nil { logx.Errorf("get order err: %v", err) return utils.ErrorInternalServer } client := order.NewOrderClient(cli.Conn()) _, err = client.UpdateOrderDeliverStatus(l.ctx, &order.UpdateOrderDeliverStatusRequest{Id: id, DeliverStatus: status}) if err != nil { logx.Errorf("get order err: %v", err) return utils.ErrorInternalServer } return utils.Ok } func (l *ExpressDeliveringLogic) getProducts(info string) (map[int]dao.ProductPrint, utils.Status) { var productInfo []express.DeliverOrderProductInfo ok := utils.JsonStringToStruct(info, &productInfo) if !ok { return nil, utils.ErrorJsonDataError } var ids []int64 for _, item := range productInfo { ids = append(ids, item.ProductId) } cli, err := svc.GetRpcClient(l.svcCtx.ProductSvcName) if err != nil { l.Logger.Error(err) return nil, utils.ErrorInternalServer } productClient := product.NewProductClient(cli.Conn()) productItems, err := productClient.ItemsByIds(l.ctx, &product.ItemsByIdsReq{Ids: ids}) if err != nil { logx.Errorf("get products err: %v", err) return nil, utils.SetError(utils.Fail, "获取产品信息错误") } products := make(map[int]dao.ProductPrint) for _, item := range productItems.Items { products[int(item.Id)] = dao.ProductPrint{ Id: int(item.Id), Name: item.Name, Weight: float32(item.Weight), Cubage: item.Cubage, Waybill: item.Waybill, } } return products, utils.Ok } func (l *ExpressDeliveringLogic) getProductInfo(orderSn string, info string, product *map[int]dao.ProductPrint) (int, float32, []dao.DeliveryOrderProduct) { var items []dao.DeliveryOrderProduct totalNum := 0 totalWeight := float32(0) if len(info) == utils.NumberZero { return totalNum, totalWeight, items } var productInfo []express.DeliverOrderProductInfo ok := utils.JsonStringToStruct(info, &productInfo) if !ok { return totalNum, totalWeight, items } for _, item := range productInfo { tmp := l.orderInfo(orderSn, item, product) items = append(items, tmp) // 统计总数、总重量 totalNum += tmp.ProductNumber totalWeight += tmp.ProductWeight } return totalNum, totalWeight, items } // 体积 func (l *ExpressDeliveringLogic) getBox(tmp *dao.DeliveryOrderProduct, product *map[int]dao.ProductPrint) { if product == nil { return } p, exists := (*product)[tmp.ProductId] if !exists { return } tmp.ProductWeight = l.toFixed3(p.Weight * float32(tmp.ProductNumber)) cubage := strings.Split(p.Cubage, "*") tmp.ProductLength = l.getBoxInfo(cubage, 0) tmp.ProductWidth = l.getBoxInfo(cubage, 1) tmp.ProductHeight = l.getBoxInfo(cubage, 2) // 计算体积 tmp.ProductCubage = l.toFixed3(tmp.ProductLength * tmp.ProductWidth * tmp.ProductHeight) } // 构建单个商品对象 func (l *ExpressDeliveringLogic) orderInfo(orderSn string, item express.DeliverOrderProductInfo, product *map[int]dao.ProductPrint) dao.DeliveryOrderProduct { tmp := dao.DeliveryOrderProduct{OrderSn: orderSn} l.getProduct(item, &tmp) l.getBox(&tmp, product) return tmp } func (l *ExpressDeliveringLogic) getProduct(itemMap express.DeliverOrderProductInfo, tmp *dao.DeliveryOrderProduct) { tmp.ProductId = int(itemMap.ProductId) tmp.ProductName = itemMap.ProductName tmp.ProductNumber = int(itemMap.Number) tmp.Price = float32(utils.RoundFloat(itemMap.Price)) } // 长度高 func (l *ExpressDeliveringLogic) getBoxInfo(cubage []string, index int) float32 { if index >= len(cubage) { return utils.NumberZero } val, err := strconv.ParseFloat(strings.TrimSpace(cubage[index]), 64) if err != nil { return utils.NumberZero } return l.toFixed3(float32(val)) } // 保留3位小数 func (l *ExpressDeliveringLogic) toFixed3(f float32) float32 { return float32(math.Round(float64(f)*1000) / 1000) } func (l *ExpressDeliveringLogic) getOrderSn(orderType uint8, adminId int) string { first := dao.DeliverySendType[orderType] sn := first + time.Now().Format("20060102150405") sn += fmt.Sprintf("%d%d", orderType, adminId) randomNum := rand.Intn(90000000) + 10000000 sn += fmt.Sprintf("%d", randomNum) return sn } func (l *ExpressDeliveringLogic) getTransitInfo(transitInfo dao.ExpressSend) (transit.Transit, utils.Status) { var client transit.Transit switch transitInfo.TransitId { case dao.ExpressCompanySf: client = transit.NewSfClient(transitInfo) case dao.ExpressCompanyJd: client = transit.NewJdClient(transitInfo) default: return client, utils.SetError(utils.ErrorNotFund, "没有找到快递公司") } return client, utils.Ok } func (l *ExpressDeliveringLogic) getSfMap(items []dao.ExpressQueryItems) map[string]bool { ret := make(map[string]bool) for _, item := range items { ret[item.LastMd5] = true } return ret }