From 15757c6f3072cd372ea359f30c1c04e838a67564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E5=B0=B9=E5=B2=9A?= <770236076@qq.com> Date: Fri, 22 Nov 2019 16:06:13 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=B0=B8=E8=BE=89Excel?= =?UTF-8?q?=E6=96=B0=E9=9C=80=E6=B1=82=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/jxstore/yonghui/yonghui.go | 331 +++++++++++++++++++++------- 1 file changed, 257 insertions(+), 74 deletions(-) diff --git a/business/jxstore/yonghui/yonghui.go b/business/jxstore/yonghui/yonghui.go index 6e2e5c1c6..255e49447 100644 --- a/business/jxstore/yonghui/yonghui.go +++ b/business/jxstore/yonghui/yonghui.go @@ -7,6 +7,7 @@ import ( "math" "mime/multipart" "strings" + "sync" "time" "unicode" @@ -16,6 +17,8 @@ import ( "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/platformapi/weimobapi" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/excel" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/model/dao" @@ -28,88 +31,160 @@ import ( type SheetParam struct { SkuIDCol int SkuPriceCol int + SkuNameCol int OrgSkuIdCol int OrgSkuPriceCol int + OrgSkuNameCol int SkuRow int } +type DataSuccessLock struct { + dataSuccessList []*DataSuccess + locker sync.RWMutex +} + +type DataFailedLock struct { + dataFailedList []*DataFailed + locker sync.RWMutex +} + +type DataSuccess struct { + NameID string `json:"商品nameID"` + Name string `json:"商品名称"` + Unit string `json:"单位"` + OrgPrice float64 `json:"原价"` + NowPrice float64 `json:"现价"` + MixPrice float64 `json:"涨跌"` +} + +type DataFailed struct { + GoodsID string `json:"商品ID"` + GoodsName string `json:"商品名称"` + Comment string `json:"备注"` +} + +type ExcelParam struct { + SpuCode string + Name string + Price float64 +} + var ( sheetMap = map[string]*SheetParam{ "蔬菜": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 14, + SkuNameCol: 1, OrgSkuIdCol: 5, OrgSkuPriceCol: 8, + OrgSkuNameCol: 6, SkuRow: 2, }, "水果": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 14, + SkuNameCol: 1, OrgSkuIdCol: 5, OrgSkuPriceCol: 8, + OrgSkuNameCol: 6, SkuRow: 2, }, "肉禽": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 12, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 1, }, "净配": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 12, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 1, }, "水产": &SheetParam{ SkuIDCol: 1, SkuPriceCol: 15, + SkuNameCol: 2, OrgSkuIdCol: 6, OrgSkuPriceCol: 9, + OrgSkuNameCol: 7, SkuRow: 1, }, "干货": &SheetParam{ SkuIDCol: 0, SkuPriceCol: 13, + SkuNameCol: 1, OrgSkuIdCol: 4, OrgSkuPriceCol: 7, + OrgSkuNameCol: 5, SkuRow: 2, }, "MINI肉禽价格": &SheetParam{ SkuIDCol: 1, SkuPriceCol: 5, + SkuNameCol: 2, OrgSkuIdCol: -1, OrgSkuPriceCol: -1, + OrgSkuNameCol: -1, SkuRow: 1, }, } + titleListSuccess = []string{ + "商品nameID", + "商品名称", + "单位", + "原价", + "现价", + "涨跌", + } + titleListFailed = []string{ + "商品ID", + "商品名称", + "备注", + } + dataSuccess DataSuccessLock + dataFailed DataFailedLock ) const ( parallelCount = 5 UpdateGoodsShelfStatusCount = 50 //微盟下架商品api限制一次50个 + fileExt = ".xlsx" ) +func (d *DataSuccessLock) AppendData(dataSuccess *DataSuccess) { + d.locker.Lock() + defer d.locker.Unlock() + d.dataSuccessList = append(d.dataSuccessList, dataSuccess) +} + +func (d *DataFailedLock) AppendData2(dataFailed *DataFailed) { + d.locker.Lock() + defer d.locker.Unlock() + d.dataFailedList = append(d.dataFailedList, dataFailed) +} + func LoadExcelByYongHui(ctx *jxcontext.Context, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) { if len(files) == 0 { return "", errors.New("没有文件上传!") } fileHeader := files[0] file, err := fileHeader.Open() - fileName := fileHeader.Filename - hint, err = LoadExcelBinByYongHui(ctx, file, fileName, true, true) + hint, err = LoadExcelBinByYongHui(ctx, file, true, true) file.Close() return hint, err } -func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName string, isAsync, isContinueWhenError bool) (hint string, err error) { +func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) { var ( - skuMap = make(map[string]float64) + skuMap = make(map[string]*ExcelParam) errMsg string costPrice float64 //成本价 goodsList []*weimobapi.GoodsInfo goodsIDListForPutAway []interface{} isCompare bool - skuMap2 = make(map[string]float64) ) db := dao.GetDB() - skuMap2["999386"] = 3.99 taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: @@ -125,7 +200,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st if rowNum < sheetParam.SkuRow { continue } - GetCellIntoMap(sheetParam.SkuIDCol, sheetParam.SkuPriceCol, sheetParam.OrgSkuIdCol, sheetParam.OrgSkuPriceCol, skuMap, row, k, rowNum) + GetCellIntoMap(sheetParam, skuMap, row, k, rowNum) if len(skuMap) < 1 { errMsg += fmt.Sprintf("读取Excel数据失败,Excel格式排版可能发生了变化!sheetName: [%v]\n", k) } @@ -137,7 +212,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st //修改分组名 // 分类名格式为:可定XX日 // XX为上传永辉 提供的 价格表时间 +2天 - isCompare, err = UpdateClassifyAndGetLastClassify(fileName) + isCompare, err = UpdateClassifyAndGetLastClassify() case 1: //获取微盟所有商品 goodsList, err = GetWeiMobGoodsList() @@ -161,12 +236,15 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st for k, _ := range skuMap { //表示excel上有,微盟上没有 if goodsInfoAndDetailMap[k] == nil { - errMsg += fmt.Sprintf("在微盟上未找到该商品!excel商品ID : [%v]\n", k) + outPutData := &DataFailed{ + GoodsID: k, + GoodsName: skuMap[k].Name, + Comment: "在微盟上未找到该商品", + } + dataFailed.AppendData2(outPutData) + // errMsg += fmt.Sprintf("在微盟上未找到该商品xxx", xxx) } } - // if errMsg != "" { - // return "", errors.New(errMsg) - // } case 2: //找出微盟上有,excel上没有的,有就更新,没有就下架 taskFunc3 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { @@ -175,24 +253,48 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st spuCode := goodsDetail.OuterGoodsCode if spuCode != "" { //如果微盟商品里找得到excel中的商品 - if skuMap2[spuCode] != 0 { + if skuMap[spuCode] != nil { //获取京西库商品 skuList, _ := dao.GetSkus(db, nil, []int{int(utils.Str2Int64(goodsDetail.SkuMap.SingleSku.OuterSkuCode))}, nil, nil) if len(skuList) == 0 { - return "", errors.New(fmt.Sprintf("在京西库中未找到该商品!name_id : [%v]\n", goodsDetail.SkuMap.SingleSku.OuterSkuCode)) + outPutData := &DataFailed{ + GoodsID: spuCode, + GoodsName: skuMap[spuCode].Name, + Comment: "在京西库中未找到该商品", + } + dataFailed.AppendData2(outPutData) + // return "", errors.New(fmt.Sprintf("在京西库中未找到该商品!name_id : [%v]\n", goodsDetail.SkuMap.SingleSku.OuterSkuCode)) } else { if skuList[0].Unit == "份" { if goodsDetail.SkuMap.SingleSku.B2CSku.Weight == 0 { - costPrice = skuMap2[spuCode] + costPrice = skuMap[spuCode].Price } else { - costPrice = Float64Round(0.5 / goodsDetail.SkuMap.SingleSku.B2CSku.Weight * skuMap2[spuCode]) + costPrice = Float64Round(0.5 / goodsDetail.SkuMap.SingleSku.B2CSku.Weight * skuMap[spuCode].Price) } } else { - costPrice = skuMap2[spuCode] + costPrice = skuMap[spuCode].Price + } + _, _, err = updateWeiMobGoods(costPrice, skuMap[spuCode].Price, isCompare, goodsDetail) + if err != nil { + if errExt, ok := err.(*utils.ErrorWithCode); ok { + outPutData := &DataFailed{ + GoodsID: spuCode, + GoodsName: skuMap[spuCode].Name, + Comment: errExt.ErrMsg(), + } + dataFailed.AppendData2(outPutData) + } + } else { + outPutData := &DataSuccess{ + NameID: goodsDetail.SkuMap.SingleSku.OuterSkuCode, + Name: goodsDetail.Title, + Unit: skuList[0].Unit, + OrgPrice: goodsDetail.SkuMap.SingleSku.SalePrice, + NowPrice: skuMap[spuCode].Price, + MixPrice: Float64Round(skuMap[spuCode].Price - goodsDetail.SkuMap.SingleSku.SalePrice), + } + dataSuccess.AppendData(outPutData) } - // if errMsg == "" { - _, _, err = updateWeiMobGoods(costPrice, skuMap[spuCode], isCompare, goodsDetail) - // } } } else { //下架微盟商品 @@ -201,7 +303,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st } return retVal, err } - taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount).SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc3, goodsList) + taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc3, goodsList) tasksch.HandleTask(taskParallel3, task, true).Run() goodsIDListForPutAwayInterface, err2 := taskParallel3.GetResult(0) if err = err2; err != nil { @@ -210,26 +312,26 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, fileName st goodsIDListForPutAway = goodsIDListForPutAwayInterface case 3: // 批量下架微盟商品 - // if errMsg == "" { taskFunc4 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { int64Slice := []int64{} for _, v := range batchItemList { int64Slice = append(int64Slice, v.(int64)) } - // PutAwayWeiMobSku(int64Slice) + PutAwayWeiMobSku(int64Slice) return retVal, err } taskParallel4 := tasksch.NewParallelTask("下架微盟商品", tasksch.NewParallelConfig().SetParallelCount(parallelCount).SetBatchSize(UpdateGoodsShelfStatusCount), ctx, taskFunc4, goodsIDListForPutAway) tasksch.HandleTask(taskParallel4, task, true).Run() _, err = taskParallel4.GetResult(0) - // } - } - if errMsg != "" { - return result, errors.New(errMsg) + case 4: + WriteToExcel(task, dataSuccess.dataSuccessList, dataFailed.dataFailedList) } + // if errMsg != "" { + // return result, errors.New(errMsg) + // } return result, err } - taskSeq := tasksch.NewSeqTask2("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, isContinueWhenError, taskSeqFunc, 4) + taskSeq := tasksch.NewSeqTask2("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, isContinueWhenError, taskSeqFunc, 5) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) @@ -418,47 +520,82 @@ func IsChineseChar(str string) bool { return false } -func GetCellIntoMap(skuIDCol, skuPriceCol, orgSkuIDCol, orgSkuPriceCol int, skuMap map[string]float64, row []string, sheetName string, rowNum int) { +func GetCellIntoMap(sheetParam *SheetParam, skuMap map[string]*ExcelParam, row []string, sheetName string, rowNum int) { var ( - skuID string - orgSkuID string - skuPrice float64 - orgSkuPrice float64 + skuID string + orgSkuID string + skuPrice float64 + orgSkuPrice float64 + skuName string + orgSkuName string + skuIDCol = sheetParam.SkuIDCol + skuPriceCol = sheetParam.SkuPriceCol + skuNameCol = sheetParam.SkuNameCol + orgSkuIDCol = sheetParam.OrgSkuIdCol + orgSkuPriceCol = sheetParam.OrgSkuPriceCol + orgSkuNameCol = sheetParam.OrgSkuNameCol ) for k, cell := range row { - if !IsChineseChar(cell) && cell != "" { - if k == skuIDCol && skuIDCol >= 0 { - skuID = cell + if cell != "" { + if !IsChineseChar(cell) { + if k == skuIDCol && skuIDCol >= 0 { + skuID = cell + } + if k == skuPriceCol && skuPriceCol >= 0 { + skuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + } + if k == orgSkuIDCol && orgSkuIDCol >= 0 { + orgSkuID = "0" + cell + } + if k == orgSkuPriceCol && orgSkuPriceCol >= 0 { + orgSkuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + } } - if k == skuPriceCol && skuPriceCol >= 0 { - skuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + if k == skuNameCol && skuNameCol >= 0 { + skuName = cell } - if k == orgSkuIDCol && orgSkuIDCol >= 0 { - orgSkuID = "0" + cell - } - if k == orgSkuPriceCol && orgSkuPriceCol >= 0 { - orgSkuPrice = Float64Round(utils.Str2Float64WithDefault(cell, 0)) + if k == orgSkuNameCol && orgSkuNameCol >= 0 { + orgSkuName = cell } } } - if skuMap[skuID] != 0 && skuMap[skuID] != skuPrice { - if skuPrice > skuMap[skuID] { - skuMap[skuID] = skuPrice + if len(skuMap) > 0 { + if skuMap[skuID] != nil { + if skuMap[skuID].Price != 0 && skuMap[skuID].Price != skuPrice && skuPrice != 0 { + if skuPrice > skuMap[skuID].Price { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + } else { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + } else if skuPrice != 0 { + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + } + if skuMap[orgSkuID] != nil { + if skuMap[orgSkuID].Price != 0 && skuMap[orgSkuID].Price != orgSkuPrice && orgSkuPrice != 0 { + if orgSkuPrice > skuMap[orgSkuID].Price { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) + } + } else if orgSkuPriceCol >= 0 && orgSkuIDCol >= 0 && orgSkuNameCol >= 0 { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) + } + } else if orgSkuPrice != 0 { + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) } - // fmt.Sprintf("读取excel表格出错!有商品ID重复且价格不同,sheetName : [%v] ,行数 : [%v] , 商品编码 :[%v] , 价格:[%v]\n", sheetName, rowNum, skuID, skuPrice) } else { - skuMap[skuID] = skuPrice - } - if skuMap[orgSkuID] != 0 && skuMap[orgSkuID] != orgSkuPrice { - if orgSkuPrice > skuMap[orgSkuID] { - skuMap[orgSkuID] = orgSkuPrice - } - // fmt.Sprintf("读取excel表格出错!有商品ID重复且价格不同,sheetName : [%v] ,行数 : [%v], 商品编码 :[%v] , 价格:[%v]\n", sheetName, rowNum, orgSkuID, orgSkuPrice) - } else if orgSkuPriceCol >= 0 && orgSkuIDCol >= 0 { - skuMap[orgSkuID] = orgSkuPrice + BuildSkuMap(skuID, skuName, skuPrice, skuMap) + BuildSkuMap(orgSkuID, orgSkuName, orgSkuPrice, skuMap) } delete(skuMap, "") } +func BuildSkuMap(id, name string, price float64, skuMap map[string]*ExcelParam) { + excelParam := &ExcelParam{ + SpuCode: id, + Price: price, + Name: name, + } + skuMap[id] = excelParam +} func Float64Round(f float64) (flt float64) { return math.Round(f*100) / 100 @@ -471,29 +608,32 @@ func Map2Int64Slice(m map[int64]int64) (result []int64) { return result } -func UpdateClassifyAndGetLastClassify(fileName string) (isCompare bool, err error) { +func UpdateClassifyAndGetLastClassify() (isCompare bool, err error) { var lastTitle string classfiyList, err := api.WeimobAPI.QueryClassifyInfoList() - for _, v := range classfiyList { - if v.ClassifyID == 1065244148 { - lastTitle = v.Title + if len(classfiyList) > 0 { + for _, v := range classfiyList { + if v.ClassifyID == 1065244148 { + lastTitle = v.Title + } } } title := "可定" now := time.Now() - year := now.Year() - s := fileName[0:strings.LastIndex(fileName, ".")] - fileTimeStr := s[12:len(s)] - fileMonth := fileTimeStr[0:strings.Index(fileTimeStr, ".")] - fileDay := fileTimeStr[strings.Index(fileTimeStr, ".")+1 : len(fileTimeStr)] - fileTime := utils.Str2Time(utils.Int2Str(year) + "-" + fileMonth + "-" + fileDay + " 00:00:00") - title += utils.Int2Str(fileTime.AddDate(0, 0, 2).Day()) + "日" - err = api.WeimobAPI.UpdateClassify(1065244148, title, "") - return lastTitle == title, err + afterDay := now.AddDate(0, 0, 2).Day() + title += utils.Int2Str(afterDay) + "日" + if lastTitle == title { + return true, err + } else { + err = api.WeimobAPI.UpdateClassify(1065244148, title, "") + return false, err + } } -func GetNewSkuTitle(priceMixRound float64, title, outerGoodsCode string) (name string) { +func GetNewSkuTitle(priceMixRound float64, orgTitle, outerGoodsCode string) (name string) { var prefix = "" + title1 := strings.ReplaceAll(orgTitle, "(", "(") + title := strings.ReplaceAll(title1, ")", ")") if outerGoodsCode[0:1] != "0" { prefix += "(精" if priceMixRound > 0 { @@ -513,16 +653,59 @@ func GetNewSkuTitle(priceMixRound float64, title, outerGoodsCode string) (name s return GetReplaceNewTitle(title, prefix) } } - prefix += utils.Float64ToStr(priceMixRound) + ")" + prefix += utils.Float64ToStr(math.Abs(priceMixRound)) + ")" return GetReplaceNewTitle(title, prefix) } func GetReplaceNewTitle(title, prefix string) (newTitle string) { - if title[0:3] == "(" { - return strings.Replace(title, title[0:strings.Index(title, ")")+3], prefix, 1) - } else if title[0:1] == "(" { + if title[0:1] == "(" { return strings.Replace(title, title[0:strings.Index(title, ")")+1], prefix, 1) } else { return prefix + title } } + +func WriteToExcel(task *tasksch.SeqTask, dataSuccess []*DataSuccess, dataFailed []*DataFailed) (err error) { + var sheetList1 []*excel.Obj2ExcelSheetConfig + var sheetList2 []*excel.Obj2ExcelSheetConfig + var downloadURL1, downloadURL2, fileName1, fileName2 string + excelConf1 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: dataSuccess, + CaptionList: titleListSuccess, + } + sheetList1 = append(sheetList1, excelConf1) + excelConf2 := &excel.Obj2ExcelSheetConfig{ + Title: "sheet1", + Data: dataFailed, + CaptionList: titleListFailed, + } + sheetList2 = append(sheetList2, excelConf2) + if excelConf1 != nil { + downloadURL1, fileName1, err = UploadExeclAndPushMsg(sheetList1, "已更新商品") + } else { + baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!") + } + if excelConf2 != nil { + downloadURL2, fileName2, err = UploadExeclAndPushMsg(sheetList2, "缺少商品_微盟") + } else { + baseapi.SugarLogger.Debug("WriteToExcel: dataFailed is nil!") + } + if err != nil { + baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName1, fileName2, err) + } else { + noticeMsg := fmt.Sprintf("[详情点我]%s/billshow/?normal=true&path1=%s, path2=%s \n", globals.BackstageHost, downloadURL1, downloadURL2) + task.SetNoticeMsg(noticeMsg) + baseapi.SugarLogger.Debugf("WriteToExcel:upload %s ,%s success, downloadURL1:%s ,downloadURL2:%s", fileName1, fileName2, downloadURL1, downloadURL2) + } + return err +} + +func UploadExeclAndPushMsg(sheetList []*excel.Obj2ExcelSheetConfig, name string) (downloadURL, fileName string, err error) { + excelBin := excel.Obj2Excel(sheetList) + timeStr := utils.Int64ToStr(time.Now().Unix()) + fileName = name + timeStr + fileExt + baseapi.SugarLogger.Debugf("WriteToExcel:save %s success", fileName) + downloadURL, err = jxutils.UploadExportContent(excelBin, fileName) + return downloadURL, fileName, err +}