From e024203e2f4da08774dff7eb175fc9bf2ee52f87 Mon Sep 17 00:00:00 2001 From: gazebo Date: Thu, 23 May 2019 09:55:11 +0800 Subject: [PATCH] - ExportOrders --- business/jxcallback/orderman/orderman_ext.go | 244 +++++++++++++++++++ business/jxutils/excel/excel.go | 8 +- business/jxutils/tasksch/task.go | 18 ++ business/model/api.go | 12 + controllers/jx_order.go | 24 ++ routers/commentsRouter_controllers.go | 9 + 6 files changed, 313 insertions(+), 2 deletions(-) diff --git a/business/jxcallback/orderman/orderman_ext.go b/business/jxcallback/orderman/orderman_ext.go index 7d356fdc2..01b6090ae 100644 --- a/business/jxcallback/orderman/orderman_ext.go +++ b/business/jxcallback/orderman/orderman_ext.go @@ -1,10 +1,15 @@ package orderman import ( + "bytes" + "context" "fmt" "strconv" + "strings" "time" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils/excel" @@ -13,7 +18,9 @@ import ( "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" "github.com/astaxie/beego/orm" + "github.com/qiniu/api.v7/storage" ) const ( @@ -369,6 +376,243 @@ func (c *OrderManager) GetOrders(ctx *jxcontext.Context, fromDateStr, toDateStr return pagedInfo, err } +func (c *OrderManager) ExportOrders(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}) (hint string, err error) { + globals.SugarLogger.Debugf("ExportOrders from:%s to:%s", fromDateStr, toDateStr) + sql := ` + SELECT t1.*, + t2.status waybill_status, t2.courier_name, t2.courier_mobile, + t2.actual_fee, t2.desired_fee, t2.waybill_created_at, t2.waybill_finished_at, + t3.sku_id, t3.count sku_count2, t3.shop_price sku_shop_price, t3.sale_price sku_sale_price + FROM goods_order t1 + LEFT JOIN waybill t2 ON t1.vendor_waybill_id = t2.vendor_waybill_id AND t1.waybill_vendor_id = t2.waybill_vendor_id + JOIN order_sku t3 ON t3.vendor_order_id = t1.vendor_order_id AND t3.vendor_id = t1.vendor_id + ` + var ( + sqlWhere string + sqlParams []interface{} + ) + // 如果搜索关键字可能为订单号,则当成订单号查询 + if params["keyword"] != nil { + if jxutils.GetPossibleVendorIDFromVendorOrderID(params["keyword"].(string)) > model.VendorIDUnknown { + params["vendorOrderID"] = params["keyword"] + } + } + if params["orderID"] != nil || params["vendorOrderID"] != nil { + sqlWhere = " WHERE (t1.vendor_order_id = ? OR t1.vendor_order_id2 = ?)" + vendorOrderID := params["vendorOrderID"] + if vendorOrderID == nil { + vendorOrderID = params["orderID"] + } + sqlParams = []interface{}{ + vendorOrderID, + vendorOrderID, + } + } else { + fromDate, err2 := utils.TryStr2Time(fromDateStr) + if err = err2; err != nil { + return "", err + } + if toDateStr == "" { + toDateStr = fromDateStr + } + toDate, err2 := utils.TryStr2Time(toDateStr) + if err = err2; err != nil { + return "", err + } + toDate = toDate.Add(24 * time.Hour) + + sqlWhere = ` + WHERE t1.order_created_at >= ? AND t1.order_created_at < ? + ` + sqlParams = []interface{}{ + fromDate, + toDate, + } + if params["keyword"] != nil { + keyword := params["keyword"].(string) + keywordLike := "%" + keyword + "%" + sqlWhere += ` + AND (t1.store_name LIKE ? OR t1.vendor_order_id LIKE ? OR t1.vendor_store_id LIKE ? + OR t1.consignee_name LIKE ? OR t1.consignee_mobile LIKE ? OR t1.consignee_mobile2 LIKE ? OR t1.consignee_address LIKE ? + OR t2.vendor_waybill_id LIKE ? OR t2.courier_name LIKE ? OR t2.courier_mobile LIKE ? + ` + sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike) + if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { + sqlWhere += " OR t1.store_id = ? OR t1.jx_store_id = ?" + sqlParams = append(sqlParams, keywordInt64, keywordInt64) + } + sqlWhere += ")" + } + if params["waybillVendorIDs"] != nil { + var waybillVendorIDs []int + if err = utils.UnmarshalUseNumber([]byte(params["waybillVendorIDs"].(string)), &waybillVendorIDs); err != nil { + return "", err + } + if len(waybillVendorIDs) > 0 { + sqlWhere += " AND t1.waybill_vendor_id IN (" + dao.GenQuestionMarks(len(waybillVendorIDs)) + ")" + sqlParams = append(sqlParams, waybillVendorIDs) + } + } + if params["storeIDs"] != nil { + var storeIDs []int + if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err != nil { + return "", err + } + if len(storeIDs) > 0 { + sqlWhere += " AND IF(t1.vendor_id = ?, t1.store_id, IF(t1.jx_store_id != 0, t1.jx_store_id, t1.store_id) ) IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" + sqlParams = append(sqlParams, model.VendorIDWSC, storeIDs) + } + } + if params["statuss"] != nil { + var statuss []int + if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statuss); err != nil { + return "", err + } + if len(statuss) > 0 { + sqlWhere += " AND t1.status IN (" + dao.GenQuestionMarks(len(statuss)) + ")" + sqlParams = append(sqlParams, statuss) + } + } + if params["lockStatuss"] != nil { + var lockStatuss []int + if err = utils.UnmarshalUseNumber([]byte(params["lockStatuss"].(string)), &lockStatuss); err != nil { + return "", err + } + if len(lockStatuss) > 0 { + sqlWhere += " AND t1.lock_status IN (" + dao.GenQuestionMarks(len(lockStatuss)) + ")" + sqlParams = append(sqlParams, lockStatuss) + } + } + if params["cities"] != nil { + var cities []int + if err = utils.UnmarshalUseNumber([]byte(params["cities"].(string)), &cities); err != nil { + return "", err + } + if len(cities) > 0 { + sql += " JOIN store st ON t1.store_id = st.id" + sqlWhere += " AND st.city_code IN (" + dao.GenQuestionMarks(len(cities)) + ")" + sqlParams = append(sqlParams, cities) + } + } + } + if params["vendorIDs"] != nil { + var vendorIDs []int + if err = utils.UnmarshalUseNumber([]byte(params["vendorIDs"].(string)), &vendorIDs); err != nil { + return "", err + } + if len(vendorIDs) > 0 { + sqlWhere += " AND t1.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")" + sqlParams = append(sqlParams, vendorIDs) + } + } + sql += sqlWhere + sql += ` + ORDER BY t1.id DESC + ` + + var ( + orders, orders2 []*model.GoodsOrderExtAndSku + order *model.GoodsOrderExtAndSku + excelBin []byte + ) + task := tasksch.NewSeqTask("导出订单SKU信息", ctx, + func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { + switch step { + case 0: + db := dao.GetDB() + err = dao.GetRows(db, &orders, sql, sqlParams...) + case 1: + for _, v := range orders { + skuStr := strings.Join([]string{ + utils.Int2Str(v.SkuID), + utils.Int2Str(v.SkuCount2), + utils.Int2Str(v.SkuShopPrice), + utils.Int2Str(v.SkuSalePrice), + }, ",") + if order == nil || v.ID != order.ID { + order = v + v.Status2 = model.OrderStatusName[v.Status] + v.SkuInfo = skuStr + orders2 = append(orders2, v) + } else { + order.SkuInfo += ";" + skuStr + } + } + case 2: + excelConf := &excel.Obj2ExcelSheetConfig{ + Title: "订单导出", + Data: orders2, + CaptionList: []string{ + "vendorOrderID", + "vendorID", + "vendorStoreID", + "jxStoreID", + "storeName", + "salePrice", + "shopPrice", + "weight", + "consigneeName", + "consigneeMobile", + "consigneeMobile2", + "consigneeAddress", + "skuCount", + "status", + "orderSeq", + "buyerComment", + "businessType", + "expectedDeliveredTime", + "vendorWaybillID", + "waybillVendorID", + "orderCreatedAt", + "orderFinishedAt", + "courierName", + "courierMobile", + "courierMobile", + "desiredFee", + "waybillCreatedAt", + "waybillFinishedAt", + "status2", + "skuInfo", + }, + } + excelBin = excel.Obj2Excel([]*excel.Obj2ExcelSheetConfig{excelConf}) + case 3: + putPolicy := storage.PutPolicy{ + Scope: globals.QiniuBucket, + Expires: 10 * 60, + DeleteAfterDays: 1, + } + upToken := putPolicy.UploadToken(api.QiniuAPI) + cfg := &storage.Config{} + keyPart := []string{ + ctx.GetUserName(), + } + if fromDateStr != "" { + keyPart = append(keyPart, fromDateStr) + } + if toDateStr != "" { + keyPart = append(keyPart, toDateStr) + } + keyPart = append(keyPart, time.Now().Format("2006-01-02T15:04:05")+".xlsx") + key := strings.Join(keyPart, "_") + formUploader := storage.NewFormUploader(cfg) + ret := storage.PutRet{} + for i := 0; i < 3; i++ { + if err = formUploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(excelBin), int64(len(excelBin)), &storage.PutExtra{}); err == nil { + break + } + } + excelURL := jxutils.ComposeQiniuResURL(key) + task.SetNoticeMsg(excelURL) + globals.SugarLogger.Debugf("导出订单SKU信息excelURL:%s, err:%v", excelURL, err) + } + return nil, err + }, 4) + tasksch.ManageTask(task).Run() + hint = task.GetID() + return hint, err +} + func (c *OrderManager) GetWaybills(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { globals.SugarLogger.Debugf("GetWaybills from:%s to:%s", fromDateStr, toDateStr) diff --git a/business/jxutils/excel/excel.go b/business/jxutils/excel/excel.go index 3e867a9d6..1226d502d 100644 --- a/business/jxutils/excel/excel.go +++ b/business/jxutils/excel/excel.go @@ -77,6 +77,10 @@ func Excel2Slice(reader io.Reader) (contents map[string][][]string) { return contents } -func genAxis(row, col int) string { - return fmt.Sprintf("%c%d", col+65, row+1) +func genAxis(row, col int) (pos string) { + pos, err := excelize.CoordinatesToCellName(col+1, row+1) + if err != nil { + globals.SugarLogger.Debugf("err:%v", err) + } + return pos } diff --git a/business/jxutils/tasksch/task.go b/business/jxutils/tasksch/task.go index 17f3b9507..5520db9fd 100644 --- a/business/jxutils/tasksch/task.go +++ b/business/jxutils/tasksch/task.go @@ -103,6 +103,8 @@ type BaseTask struct { FailedJobCount int `json:"failedJobCount"` Status int `json:"status"` + NoticeMsg string `json:"noticeMsg"` + Result []interface{} `json:"-"` Children TaskList `json:"children"` Err error `json:"err"` @@ -257,6 +259,18 @@ func (t *BaseTask) SetParent(parentTask ITask) { t.parent = parentTask } +func (t *BaseTask) SetNoticeMsg(noticeMsg string) { + t.locker.Lock() + defer t.locker.Unlock() + t.NoticeMsg = noticeMsg +} + +func (t *BaseTask) GetNoticeMsg() string { + t.locker.RLock() + defer t.locker.RUnlock() + return t.NoticeMsg +} + func AddChild(parentTask ITask, task ITask) ITask { if parentTask != nil { return parentTask.AddChild(task) @@ -311,6 +325,10 @@ func (t *BaseTask) run(taskHandler func()) { taskDesc := fmt.Sprintf("你的异步任务[%s],ID[%s],开始于:%s,结束于:%s,", t.Name, t.ID, utils.Time2Str(t.CreatedAt), utils.Time2Str(t.TerminatedAt)) if t.Err == nil { content = fmt.Sprintf("%s执行%s", taskDesc, TaskStatusName[t.Status]) + noticeMsg := t.GetNoticeMsg() + if noticeMsg != "" { + content += ",通知消息:" + noticeMsg + } } else { if t.Status == TaskStatusFinished { content = fmt.Sprintf("%s执行部分失败,%s", taskDesc, t.Err.Error()) diff --git a/business/model/api.go b/business/model/api.go index fe4b973a6..dd5d5fd8c 100644 --- a/business/model/api.go +++ b/business/model/api.go @@ -17,12 +17,24 @@ type GoodsOrderExt struct { CourierMobile string `orm:"size(32)" json:"courierMobile"` CurrentConsigneeMobile string `orm:"-" json:"currentConsigneeMobile"` + Status2 string `json:"status2"` ActualFee int64 `json:"actualFee"` // 实际要支付给快递公司的费用 DesiredFee int64 `json:"desiredFee"` // 运单总费用 WaybillCreatedAt time.Time `orm:"type(datetime);index" json:"waybillCreatedAt"` WaybillFinishedAt time.Time `orm:"type(datetime)" json:"waybillFinishedAt"` } +type GoodsOrderExtAndSku struct { + GoodsOrderExt + + SkuID int `orm:"column(sku_id)" json:"skuID"` + SkuShopPrice int `json:"skuShopPrice"` + SkuSalePrice int `json:"skuSalePrice"` + SkuCount2 int `json:"skuCount2"` + + SkuInfo string `json:"skuInfo"` +} + type OrderSkuExt struct { OrderSku FullSkuName string `json:"fullSkuName"` diff --git a/controllers/jx_order.go b/controllers/jx_order.go index df820a999..63150c715 100644 --- a/controllers/jx_order.go +++ b/controllers/jx_order.go @@ -284,6 +284,30 @@ func (c *OrderController) GetOrders() { }) } +// @Title 导出订单(包括SKU信息) +// @Description 导出订单(包括SKU信息) +// @Param token header string true "认证token" +// @Param orderID query string false "订单号,如果此项不为空,忽略其它所有查询条件(此项会废弃,用vendorOderID)" +// @Param vendorOrderID query string false "订单号,如果此项不为空,忽略其它所有查询条件" +// @Param keyword query string false "查询关键字" +// @Param fromDate query string false "开始日期(包含),格式(2006-01-02),如果订单号为空此项必须要求" +// @Param toDate query string false "结束日期(包含),格式(2006-01-02),如果订单号为空此项必须要求" +// @Param vendorIDs query string false "订单所属厂商列表[1,2,3],缺省不限制" +// @Param waybillVendorIDs query string false "承运人所属厂商列表[1,2,3],缺省不限制" +// @Param storeIDs query string false "京西门店ID列表[1,2,3],缺省不限制" +// @Param statuss query string false "订单状态列表[1,2,3],缺省不限制" +// @Param lockStatuss query string false "订单锁定状态列表[1,2,3],缺省不限制" +// @Param cities query string false "城市code列表[1,2,3],缺省不限制" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /ExportOrders [get] +func (c *OrderController) ExportOrders() { + c.callExportOrders(func(params *tOrderExportOrdersParams) (retVal interface{}, errCode string, err error) { + retVal, err = orderman.FixedOrderManager.ExportOrders(params.Ctx, params.FromDate, params.ToDate, params.MapData) + return retVal, "", err + }) +} + // @Title 查询售后单 // @Description 查询售后单 // @Param token header string true "认证token" diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 235d218d9..5f8946485 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -619,6 +619,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], + beego.ControllerComments{ + Method: "ExportOrders", + Router: `/ExportOrders`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:OrderController"], beego.ControllerComments{ Method: "FinishedPickup",