diff --git a/business/jxstore/cms/store.go b/business/jxstore/cms/store.go index e4162a022..1d894e076 100644 --- a/business/jxstore/cms/store.go +++ b/business/jxstore/cms/store.go @@ -617,8 +617,8 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa if err = dao.GetEntity(db, store); err != nil { return 0, err } - - valid := dao.StrictMakeMapByStructObject(payload, store, userName) + var outStore *model.Store + valid := dao.StrictMakeMapByStructObject2(payload, store, &outStore, userName) if err = checkStoreDeliveryRange(utils.Interface2String(valid["deliveryRange"])); err != nil { return 0, err } @@ -733,6 +733,31 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa if valid["deliveryRange"] != nil { valid["deliveryRange"] = strings.Trim(valid["deliveryRange"].(string), ";") } + + //时间校验 + if outStore.OpenTime1 != 0 && outStore.CloseTime1 != 0 { + if err := ValidateStructPartial(outStore, "OpenTime1", "CloseTime1"); err != nil { + return 0, errors.New(fmt.Sprintf("门店营业时间1设置不合法!时间范围1 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1)) + } + } + if (outStore.OpenTime1 == 0 && outStore.CloseTime1 != 0) || (outStore.OpenTime1 != 0 && outStore.CloseTime1 == 0) { + return 0, errors.New(fmt.Sprintf("门店营业时间1设置不合法!时间范围1 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1)) + } + if outStore.OpenTime2 != 0 && outStore.CloseTime2 != 0 { + if err := ValidateStructPartial(outStore, "OpenTime2", "CloseTime2"); err != nil { + return 0, errors.New(fmt.Sprintf("门店营业时间2设置不合法!时间范围2 :[%v] 至 [%v]", outStore.OpenTime2, outStore.CloseTime2)) + } + } + if (outStore.OpenTime2 == 0 && outStore.CloseTime2 != 0) || (outStore.OpenTime2 != 0 && outStore.CloseTime2 == 0) { + return 0, errors.New(fmt.Sprintf("门店营业时间2设置不合法!时间范围2 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1)) + } + if outStore.OpenTime2 > outStore.OpenTime1 { + return 0, errors.New(fmt.Sprintf("门店营业时间设置不合法!第二段营业时间应该在第一段营业时间之后!")) + } + if beginAt, endAt := GetTimeMixByInt(outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2); beginAt != 0 && endAt != 0 { + return 0, errors.New(fmt.Sprintf("两段门店营业时间不可交叉!时间范围1 :[%v] 至 [%v], 时间范围2 :[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2)) + } + // districtCode := 0 // if valid["districtCode"] != nil { // districtCode = int(utils.MustInterface2Int64(valid["districtCode"])) @@ -779,7 +804,7 @@ func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interfa notifyStoreOperatorChanged(store, valid["operatorPhone"]) if err == nil { if valid["openTime1"] != 0 || valid["closeTime1"] != 0 || valid["openTime2"] != 0 || valid["closeTime2"] != 0 { - err = CurVendorSync.ChangeStoreSkuSaleStatus(ctx, storeID, true, false) + err = CurVendorSync.ChangeStoreSkuSaleStatus(ctx, storeID, true, true) } } } diff --git a/business/jxstore/cms/store_sku.go b/business/jxstore/cms/store_sku.go index 6e54f624d..f026cc107 100644 --- a/business/jxstore/cms/store_sku.go +++ b/business/jxstore/cms/store_sku.go @@ -122,8 +122,8 @@ type StoreSkuBindInfo struct { IsFocus int `json:"isFocus"` // -1:不关注,0:忽略,1:关注 IsSale int `json:"isSale"` // -1:不可售,0:忽略,1:可售 SubStoreID int `json:"subStoreID,omitempty"` - StatusSaleBegin int16 `json:"statusSaleBegin"` //商品可售时间范围 - StatusSaleEnd int16 `json:"statusSaleEnd"` + StatusSaleBegin int16 `json:"statusSaleBegin" validate:"max=2359,min=1,ltfield=StatusSaleEnd"` //商品可售时间范围 + StatusSaleEnd int16 `json:"statusSaleEnd" validate:"max=2359,min=1"` Skus []*StoreSkuBindSkuInfo `json:"skus,omitempty"` } @@ -1082,15 +1082,10 @@ func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs updateFieldMap["JxPrice"] = 1 } if skuBindInfo.StatusSaleBegin != 0 && skuBindInfo.StatusSaleEnd != 0 { - if skuBindInfo.StatusSaleBegin < 0 || skuBindInfo.StatusSaleBegin > 2359 || - skuBindInfo.StatusSaleEnd < 0 || skuBindInfo.StatusSaleEnd > 2359 { + if err := ValidateStruct(skuBindInfo); err != nil { dao.Rollback(db) return nil, fmt.Errorf("更改商品:%s, 可售时间不合法!时间范围:[%v] 至 [%v]", allBinds[0].Name, skuBindInfo.StatusSaleBegin, skuBindInfo.StatusSaleEnd) } - if skuBindInfo.StatusSaleBegin >= skuBindInfo.StatusSaleEnd { - dao.Rollback(db) - return nil, fmt.Errorf("更改商品:%s, 可售时间不允许交叉!时间范围:[%v] 至 [%v]", allBinds[0].Name, skuBindInfo.StatusSaleBegin, skuBindInfo.StatusSaleEnd) - } updateFieldMap["StatusSaleBegin"] = int(skuBindInfo.StatusSaleBegin) updateFieldMap["StatusSaleEnd"] = int(skuBindInfo.StatusSaleEnd) skuBind.StatusSaleBegin = skuBindInfo.StatusSaleBegin diff --git a/business/jxstore/cms/validation.go b/business/jxstore/cms/validation.go index fc6045e38..4797f8d39 100644 --- a/business/jxstore/cms/validation.go +++ b/business/jxstore/cms/validation.go @@ -3,6 +3,8 @@ package cms import ( "fmt" + "gopkg.in/go-playground/validator.v9" + "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/model" ) @@ -10,6 +12,7 @@ import ( var ( unitNamesMap map[string]int specUnitNamesMap map[string]int + validate = validator.New() ) func init() { @@ -33,3 +36,15 @@ func ValidateUnit(value interface{}) (err error) { func ValidateSpecUnit(value interface{}) (err error) { return validateStringInMap("SpecUnit", value, specUnitNamesMap) } + +func ValidateStruct(value interface{}) (err error) { + return validate.Struct(value) +} + +func ValidateVar(value interface{}, tag string) (err error) { + return validate.Var(value, tag) +} + +func ValidateStructPartial(value interface{}, fields ...string) (err error) { + return validate.StructPartial(value, fields...) +} diff --git a/business/jxstore/misc/misc.go b/business/jxstore/misc/misc.go index b6b83280b..ffbf0b5c0 100644 --- a/business/jxstore/misc/misc.go +++ b/business/jxstore/misc/misc.go @@ -133,8 +133,10 @@ func Init() { }, []string{ "04:05:06", }) + } + if beego.BConfig.RunMode == "alpha" { ScheduleTimerFunc("ChangeStoreSkuSaleStatus", func() { - cms.CurVendorSync.ChangeStoreSkuSaleStatus(jxcontext.AdminCtx, 0, true, false) + cms.CurVendorSync.ChangeStoreSkuSaleStatus(jxcontext.AdminCtx, 0, true, true) }, ChangeStoreSkuSaleStatusList) } } diff --git a/business/jxstore/yonghui/yonghui.go b/business/jxstore/yonghui/yonghui.go index 536c12c77..724bb5e14 100644 --- a/business/jxstore/yonghui/yonghui.go +++ b/business/jxstore/yonghui/yonghui.go @@ -84,18 +84,18 @@ const ( UpdateGoodsShelfStatusCount = 50 //微盟下架商品api限制一次50个 ) -func LoadExcelByYongHui(ctx *jxcontext.Context, files []*multipart.FileHeader, isAsync bool) (hint string, err error) { +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() - hint, err = LoadExcelBinByYongHui(ctx, file, isAsync) + hint, err = LoadExcelBinByYongHui(ctx, file, true, true) file.Close() return hint, err } -func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync 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) errMsg string @@ -121,7 +121,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync boo } GetCellIntoMap(sheetParam.SkuIDCol, sheetParam.SkuPriceCol, sheetParam.OrgSkuIdCol, sheetParam.OrgSkuPriceCol, skuMap, row, k, rowNum) if len(skuMap) < 1 { - errMsg += fmt.Sprintf("读取Excel数据失败,Excel格式排版可能发生了变化!sheetName: [%v]", k) + errMsg += fmt.Sprintf("读取Excel数据失败,Excel格式排版可能发生了变化!sheetName: [%v]\n", k) } } if errMsg != "" { @@ -169,7 +169,7 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync boo //获取京西库商品 skuList, _ := dao.GetSkus(db, nil, []int{int(utils.Str2Int64(goodsDetail.SkuMap.SingleSku.OuterSkuCode))}, nil, nil) if len(skuList) == 0 { - errMsg += fmt.Sprintf("在京西库中未找到该商品!name_id : [%v] \n", goodsDetail.SkuMap.SingleSku.OuterSkuCode) + 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 { @@ -191,15 +191,15 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync boo } return retVal, err } - taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc3, goodsList) + taskParallel3 := tasksch.NewParallelTask("根据获取的微盟所有商品并更新", tasksch.NewParallelConfig().SetParallelCount(parallelCount).SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc3, goodsList) tasksch.HandleTask(taskParallel3, task, true).Run() goodsIDListForPutAwayInterface, err2 := taskParallel3.GetResult(0) if err = err2; err != nil { return "", err } - // 批量下架微盟商品 goodsIDListForPutAway = goodsIDListForPutAwayInterface case 3: + // 批量下架微盟商品 // if errMsg == "" { taskFunc4 := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { int64Slice := []int64{} @@ -214,9 +214,12 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync boo _, err = taskParallel4.GetResult(0) // } } + if errMsg != "" { + return result, errors.New(errMsg) + } return result, err } - taskSeq := tasksch.NewSeqTask("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, taskSeqFunc, 4) + taskSeq := tasksch.NewSeqTask2("读取永辉Excel文件修改微盟商品价格可售状态-序列任务", ctx, isContinueWhenError, taskSeqFunc, 4) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) @@ -224,16 +227,12 @@ func LoadExcelBinByYongHui(ctx *jxcontext.Context, reader io.Reader, isAsync boo } else { hint = taskSeq.GetID() } - if errMsg != "" { - baseapi.SugarLogger.Debugf(errMsg) - return "", errors.New(errMsg) - } return hint, err } func PutAwayWeiMobSku(goodsIDListForPutAway []int64) (err error) { if globals.EnableStoreWrite { - // err = api.WeimobAPI.UpdateGoodsShelfStatus(goodsIDListForPutAway, false) + err = api.WeimobAPI.UpdateGoodsShelfStatus(goodsIDListForPutAway, false) } if err != nil { baseapi.SugarLogger.Errorf("UpdateGoodsShelfStatus error:%v", err) @@ -293,7 +292,7 @@ func updateWeiMobGoods(costPrice, salePrice float64, goodsDetail *weimobapi.Good SalePrice: salePrice, CostPrice: costPrice, SkuID: skuListInfo.SkuID, - EditStockNum: 9999 - skuListInfo.EditStockNum, + EditStockNum: 9999 - skuListInfo.AvailableStockNum, OuterSkuCode: skuListInfo.OuterSkuCode, B2CSku: b2CSku, } diff --git a/business/model/dao/dao_utils.go b/business/model/dao/dao_utils.go index b45b4386c..736a463c7 100644 --- a/business/model/dao/dao_utils.go +++ b/business/model/dao/dao_utils.go @@ -13,7 +13,7 @@ import ( func IDCULDFilterMapByStructObject(mapData map[string]interface{}, obj interface{}, isCheckValue bool) (valid map[string]interface{}, invalid map[string]interface{}) { // 这里必须用首字母小写,因为是用于访问map,是需要完全匹配的 - return refutil.FilterMapByStructObject(mapData, obj, []string{"id", "createdAt", "updatedAt", "finishedAt", "deletedAt", "syncStatus", "lastOperator"}, isCheckValue) + return refutil.FilterMapByStructObject(mapData, obj, nil, []string{"id", "createdAt", "updatedAt", "finishedAt", "deletedAt", "syncStatus", "lastOperator"}, isCheckValue) } func NormalMakeMapByStructObject(mapData map[string]interface{}, obj interface{}, userName string) (retVal map[string]interface{}) { @@ -26,6 +26,16 @@ func StrictMakeMapByStructObject(mapData map[string]interface{}, obj interface{} return retVal } +func IDCULDFilterMapByStructObject2(mapData map[string]interface{}, obj interface{}, objPtr interface{}, isCheckValue bool) (valid map[string]interface{}, invalid map[string]interface{}) { + return refutil.FilterMapByStructObject(mapData, obj, objPtr, []string{"id", "createdAt", "updatedAt", "finishedAt", "deletedAt", "syncStatus", "lastOperator"}, isCheckValue) +} + +//根据传进来的objPtr去修改它的值 +func StrictMakeMapByStructObject2(mapData map[string]interface{}, obj interface{}, objPtr interface{}, userName string) (retVal map[string]interface{}) { + retVal, _ = IDCULDFilterMapByStructObject2(mapData, obj, objPtr, true) + return retVal +} + func NormalMakeMapByFieldList(mapData map[string]interface{}, fields []string, userName string) (retVal map[string]interface{}) { retVal, _ = refutil.FilterMapByFieldList(mapData, fields) return retVal diff --git a/business/model/store.go b/business/model/store.go index 6319b6cad..8b549576a 100644 --- a/business/model/store.go +++ b/business/model/store.go @@ -252,14 +252,14 @@ type Store struct { Address string `orm:"size(255)" json:"address"` Tel1 string `orm:"size(32);index" json:"tel1"` Tel2 string `orm:"size(32);index" json:"tel2"` - OpenTime1 int16 `json:"openTime1"` // 930就表示9点半,用两个的原因是为了支持中午休息,1与2的时间段不能交叉,为0表示没有 - CloseTime1 int16 `json:"closeTime1"` // 格式同上 - OpenTime2 int16 `json:"openTime2"` // 格式同上 - CloseTime2 int16 `json:"closeTime2"` // 格式同上 - Lng int `json:"-"` // 乘了10的6次方 - Lat int `json:"-"` // 乘了10的6次方 - DeliveryRangeType int8 `json:"deliveryRangeType"` // 参见相关常量定义 - DeliveryRange string `orm:"type(text)" json:"deliveryRange"` // 如果DeliveryRangeType为DeliveryRangeTypePolygon,则为逗号分隔坐标,分号分隔的坐标点(坐标与Lng和Lat一样,都是整数),比如 121361504,31189308;121420555,31150238。否则为半径,单位为米 + OpenTime1 int16 `json:"openTime1" validate:"max=2359,min=1,ltfield=CloseTime1"` // 930就表示9点半,用两个的原因是为了支持中午休息,1与2的时间段不能交叉,为0表示没有 + CloseTime1 int16 `json:"closeTime1" validate:"max=2359,min=1` // 格式同上 + OpenTime2 int16 `json:"openTime2" validate:"max=2359,min=1,ltfield=CloseTime2"` // 格式同上 + CloseTime2 int16 `json:"closeTime2" validate:"max=2359,min=1` // 格式同上 + Lng int `json:"-"` // 乘了10的6次方 + Lat int `json:"-"` // 乘了10的6次方 + DeliveryRangeType int8 `json:"deliveryRangeType"` // 参见相关常量定义 + DeliveryRange string `orm:"type(text)" json:"deliveryRange"` // 如果DeliveryRangeType为DeliveryRangeTypePolygon,则为逗号分隔坐标,分号分隔的坐标点(坐标与Lng和Lat一样,都是整数),比如 121361504,31189308;121420555,31150238。否则为半径,单位为米 Status int `json:"status"` AutoEnableAt *time.Time `orm:"type(datetime);null" json:"autoEnableAt"` // 自动营业时间(临时休息用) ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核 diff --git a/controllers/financial.go b/controllers/financial.go index 893529fcb..e4eb65fde 100644 --- a/controllers/financial.go +++ b/controllers/financial.go @@ -46,7 +46,7 @@ func (c *FinancialController) SendFilesToStores() { if params.Title != "永辉" { retVal, err = financial.SendFilesToStores(params.Ctx, files, params.Title, params.ShopName, params.IsAsync, params.Ctx.GetUserName()) } else { - retVal, err = yonghui.LoadExcelByYongHui(params.Ctx, files, params.IsAsync) + retVal, err = yonghui.LoadExcelByYongHui(params.Ctx, files, params.IsAsync, true) } return retVal, "", err }) diff --git a/controllers/yonghui.go b/controllers/yonghui.go index d714ea019..99a397640 100644 --- a/controllers/yonghui.go +++ b/controllers/yonghui.go @@ -14,8 +14,9 @@ type YongHuiController struct { // @Title 读取永辉excel文件 // @Description 读取永辉excel文件 -// @Param token header string false "认证token" -// @Param isAsync query bool false "是否异步,缺省是同步" +// @Param token header string false "认证token" +// @Param isAsync query bool true "是否异步,缺省是同步" +// @Param isContinueWhenError query bool true "单个同步失败是否继续,缺省false" // @Success 200 {object} controllers.CallResult // @Failure 200 {object} controllers.CallResult // @router /LoadExcelByYongHui [post,get] @@ -36,7 +37,7 @@ func (c *YongHuiController) LoadExcelByYongHui() { c.callLoadExcelByYongHui(func(params *tYonghuiLoadExcelByYongHuiParams) (retVal interface{}, errCode string, err error) { r := c.Ctx.Request files := r.MultipartForm.File["userfiles"] - retVal, err = yonghui.LoadExcelByYongHui(params.Ctx, files, params.IsAsync) + retVal, err = yonghui.LoadExcelByYongHui(params.Ctx, files, params.IsAsync, params.IsContinueWhenError) return retVal, "", err }) } diff --git a/globals/refutil/refutil.go b/globals/refutil/refutil.go index 70ed494ff..c28475bc2 100644 --- a/globals/refutil/refutil.go +++ b/globals/refutil/refutil.go @@ -53,7 +53,7 @@ func DeSerializeData(strValue string, dataPtr interface{}) (err error) { } // todo 这里看是否需要将key值转换成标准格式(即字母大写),因为beego orm不区分,不转换也可以 -func FilterMapByStructObject(mapData map[string]interface{}, obj interface{}, excludedFields []string, isCheckValue bool) (valid map[string]interface{}, invalid map[string]interface{}) { +func FilterMapByStructObject(mapData map[string]interface{}, obj interface{}, objPtr interface{}, excludedFields []string, isCheckValue bool) (valid map[string]interface{}, invalid map[string]interface{}) { excludedMap := make(map[string]int) for _, v := range excludedFields { excludedMap[v] = 1 @@ -64,10 +64,14 @@ func FilterMapByStructObject(mapData map[string]interface{}, obj interface{}, ex for k, v := range mapData { if m[k] != nil && excludedMap[k] == 0 && v != nil && (!isCheckValue || !IsValueEqual(m[k], v)) { valid[k] = v + m[k] = v } else { invalid[k] = v } } + if objPtr != nil { + utils.Map2StructByJson(m, objPtr, true) + } return valid, invalid }