diff --git a/business/jxstore/cms/cms.go b/business/jxstore/cms/cms.go index 2e8ad0f10..5aa6154da 100644 --- a/business/jxstore/cms/cms.go +++ b/business/jxstore/cms/cms.go @@ -103,10 +103,7 @@ func GetServiceInfo(ctx *jxcontext.Context) interface{} { func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTokenInfo map[string]interface{}, err error) { imgURL := "" if hashCode != "" { - db := dao.GetDB() - if skuName, err := dao.GetSkuNameByHashCode(db, hashCode); err == nil { - imgURL = skuName.Img - } + imgURL, _ = GetDataResource(ctx, hashCode) } putPolicy := storage.PutPolicy{ @@ -123,6 +120,51 @@ func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTok return upTokenInfo, err } +func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, hashCode string) (dataRes *model.DataResource, err error) { + if model.ValideMimeTypes[mimeType] == 0 { + return nil, fmt.Errorf("MIME type:%s非法", mimeType) + } + dataRes = &model.DataResource{ + Name: name, + HashCode: hashCode, + ResoureType: mimeType, + MainURL: resourceURL, + } + vendorID := jxutils.GuessDataResourceVendor(resourceURL) + switch vendorID { + case model.VendorIDQiNiuCloud: + dataRes.QiniuURL = resourceURL + case model.VendorIDEBAI: + dataRes.EbaiURL = resourceURL + case model.VendorIDMTWM: + dataRes.MtwmURL = resourceURL + } + + dao.WrapAddIDCULEntity(dataRes, ctx.GetUserName()) + if err = dao.CreateEntity(dao.GetDB(), dataRes); err != nil { + dataRes = nil + } + return dataRes, err +} + +func GetDataResource(ctx *jxcontext.Context, hashCode string) (resourceURL string, err error) { + db := dao.GetDB() + dataRes, err := dao.GetDataResource(db, hashCode, "") + if err != nil { + if dao.IsNoRowsError(err) { + skuName, err2 := dao.GetSkuNameByHashCode(db, hashCode) + if err = err2; err == nil { + resourceURL = skuName.Img + } else if dao.IsNoRowsError(err) { + err = nil + } + } + } else { + resourceURL = dataRes.MainURL + } + return resourceURL, err +} + func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, params map[string]interface{}) ([]*model.Place, error) { sql := ` SELECT * diff --git a/business/jxstore/cms/cms_test.go b/business/jxstore/cms/cms_test.go index 8ff644fc3..c874fdc6f 100644 --- a/business/jxstore/cms/cms_test.go +++ b/business/jxstore/cms/cms_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/globals/api2" "git.rosy.net.cn/jx-callback/globals/testinit" @@ -27,3 +28,11 @@ func TestGetQiniuUploadToken(t *testing.T) { } fmt.Print(token) } + +func TestGetDataResource(t *testing.T) { + dataRes, err := GetDataResource(jxcontext.AdminCtx, "1D3E4A8259F359FB4CF47D541843950D") + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(dataRes, false)) +} diff --git a/business/jxstore/cms/store.go b/business/jxstore/cms/store.go index f9d3ea169..9a9aa6940 100644 --- a/business/jxstore/cms/store.go +++ b/business/jxstore/cms/store.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "sort" "strconv" "strings" "time" @@ -1561,6 +1562,28 @@ func getAllUsers4Store(ctx *jxcontext.Context, db *dao.DaoDB, store *model.Store return userList } +type tStoreIDList struct { + StoreIDList []int + StoreMap map[int]*model.Store +} + +func (l *tStoreIDList) Len() int { + return len(l.StoreIDList) +} + +func (l *tStoreIDList) Less(i, j int) bool { + storei := l.StoreMap[l.StoreIDList[i]] + storej := l.StoreMap[l.StoreIDList[j]] + if storei.CityCode == storej.CityCode { + return storei.ID < storej.ID + } + return storei.CityCode < storej.CityCode +} + +func (l *tStoreIDList) Swap(i, j int) { + l.StoreIDList[i], l.StoreIDList[j] = l.StoreIDList[j], l.StoreIDList[i] +} + func SendAlarmVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, prevSnapshotList, curSnapshotList []*model.VendorStoreSnapshot) (err error) { if len(prevSnapshotList) == 0 { return nil @@ -1646,17 +1669,37 @@ func SendAlarmVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, p } if len(userList) > 0 { + allStores, err := dao.GetStoreList(db, nil, nil, "") + if err != nil { + return err + } + allStoreMap := make(map[int]*model.Store) + for _, v := range allStores { + allStoreMap[v.ID] = v + } + const fixTitle = "门店状态变化" title := fmt.Sprintf("%s:%s-->%s", fixTitle, utils.Time2Str(prevSnapshotList[0].SnapshotAt), utils.Time2Str(curSnapshotList[0].SnapshotAt)) task := tasksch.NewParallelTask("SendAlarmVendorSnapshot", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { user := batchItemList[0].(*model.User) + if user.GetMobile() != "18048531223" { + return nil, nil + } var excelURL string if user.Type&model.UserTypeOperator != 0 { var dataList []map[string]interface{} captionList := []string{"京西门店ID", "门店名", "城市"} isFirstRow := true + + storeIDList := &tStoreIDList{ + StoreMap: allStoreMap, + } for storeID := range userMap[user.GetID()] { + storeIDList.StoreIDList = append(storeIDList.StoreIDList, storeID) + } + sort.Sort(storeIDList) + for _, storeID := range storeIDList.StoreIDList { prevAlarmMap := prevSnapshotMap2[storeID] curAlarmMap := curSnapshotMap2[storeID] data := map[string]interface{}{ @@ -1789,7 +1832,6 @@ func SaveAndSendAlarmVendorSnapshot(ctx *jxcontext.Context, vendorIDs, storeIDs err = SaveStoresVendorSnapshot(db, curSnapshotAt, curSnapshotList) case 2: prevSnapshotList, err = dao.GetVendorStoreSnapshot(db, prevSnapshotAt) - curSnapshotList, err = dao.GetVendorStoreSnapshot(db, curSnapshotAt) // 因为排序的原因,重新取一下 case 3: err = SendAlarmVendorSnapshot(ctx, task, prevSnapshotList, curSnapshotList) } diff --git a/business/jxstore/cms/store_test.go b/business/jxstore/cms/store_test.go index 20f9b7bc5..e934e2886 100644 --- a/business/jxstore/cms/store_test.go +++ b/business/jxstore/cms/store_test.go @@ -20,8 +20,8 @@ func TestGetStoresVendorSnapshot(t *testing.T) { func TestSendAlarmVendorSnapshot(t *testing.T) { db := dao.GetDB() - prevSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-09 10:00:00")) - curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-09 11:00:00")) + prevSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-17 08:00:00")) + curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-17 10:00:00")) err := SendAlarmVendorSnapshot(jxcontext.AdminCtx, nil, prevSnapshotList, curSnapshotList) if err != nil { t.Fatal(err) diff --git a/business/jxstore/cms/sync_store_sku.go b/business/jxstore/cms/sync_store_sku.go index 374999657..dcee5ff63 100644 --- a/business/jxstore/cms/sync_store_sku.go +++ b/business/jxstore/cms/sync_store_sku.go @@ -336,8 +336,12 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo offlineList = append(offlineList, bareSku) } } else { - if sku.MergedStatus == model.SkuStatusNormal /*&& !dao.IsVendorThingIDEmpty(sku.VendorCatID)*/ { - createList = append(createList, sku) + if sku.MergedStatus == model.SkuStatusNormal { + if dao.IsVendorThingIDEmpty(sku.VendorCatID) { + globals.SugarLogger.Warnf("syncStoreSkuNew 创建门店:%d商品:%d,但没有平台分类ID", sku.StoreID, sku.SkuID) + } else { + createList = append(createList, sku) + } } } isNeedReorder = true @@ -351,9 +355,13 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo } else { isAdded2Update := false // 修改商品信息时不改价(以免活动引起的失败),而用单独的改价来改 - if (model.IsSyncStatusUpdate(sku.StoreSkuSyncStatus) || (model.IsSyncStatusSec(sku.StoreSkuSyncStatus) && reorderHandler == nil)) && singleStoreHandler != nil { - isAdded2Update = true - updateList = append(updateList, calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) + if (model.IsSyncStatusUpdate(sku.StoreSkuSyncStatus) || (model.IsSyncStatusSeq(sku.StoreSkuSyncStatus) && reorderHandler == nil)) && singleStoreHandler != nil { + if dao.IsVendorThingIDEmpty(sku.VendorCatID) { + globals.SugarLogger.Warnf("syncStoreSkuNew 修改门店:%d商品:%d,但没有平台分类ID", sku.StoreID, sku.SkuID) + } else { + isAdded2Update = true + updateList = append(updateList, calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) + } } if model.IsSyncStatusPrice(sku.StoreSkuSyncStatus) { bareSku = storeSkuSyncInfo2Bare(calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) @@ -378,7 +386,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo } } } - isNeedReorder = model.IsSyncStatusSec(sku.StoreSkuSyncStatus) + isNeedReorder = model.IsSyncStatusSeq(sku.StoreSkuSyncStatus) } } if isNeedReorder && reorderHandler != nil && sku.VendorCatID != "" { diff --git a/business/jxutils/jxutils.go b/business/jxutils/jxutils.go index 1366dd7da..4ea6c2da8 100644 --- a/business/jxutils/jxutils.go +++ b/business/jxutils/jxutils.go @@ -27,6 +27,21 @@ import ( var ( routinePool *routinepool.Pool skuNamePat *regexp.Regexp + + resourceTypeMap = map[int][]string{ + model.VendorIDQiNiuCloud: []string{ + "image.jxc4.com", + }, + model.VendorIDJD: []string{ + "img30.360buyimg.com", + }, + model.VendorIDMTWM: []string{ + "", + }, + model.VendorIDEBAI: []string{ + "image-star.elemecdn.com", + }, + } ) type OrderSkuList []*model.OrderSku @@ -692,3 +707,19 @@ func GetAuthType4Vendor(vendorID int) (authType string) { } return authType } + +func GuessDataResourceVendor(resourceURL string) (vendorID int) { + vendorID = -1 + for tmpVendorID, urlList := range resourceTypeMap { + for _, v := range urlList { + if strings.Index(resourceURL, "//"+v) >= 0 { + vendorID = tmpVendorID + break + } + } + if vendorID >= 0 { + break + } + } + return vendorID +} diff --git a/business/model/api_config.go b/business/model/api_config.go index 65b682864..3ec830c3e 100644 --- a/business/model/api_config.go +++ b/business/model/api_config.go @@ -72,6 +72,8 @@ var ( VendorIDXiaoWM: "XiaoWM", VendorIDYiLianYun: "Yilianyun", VendorIDZhongWu: "ZhongWu", + + VendorIDQiNiuCloud: "Qiniu", } VendorTypeName = map[int]string{ diff --git a/business/model/common.go b/business/model/common.go index 5745c8662..6f1f886c3 100644 --- a/business/model/common.go +++ b/business/model/common.go @@ -13,19 +13,13 @@ var ( type DataResource struct { ModelIDCUL - HashCode string `orm:"size(48);unique" json:"hash_code"` - ResoureType string // 资料的mime type + HashCode string `orm:"size(48);unique" json:"hashCode"` - Name string `orm:"size(48)" json:"name"` + ResoureType string `orm:"size(48)" json:"resoureType"` // 资料的mime type + Name string `orm:"size(48);index" json:"name"` - MainURL string - QiniuURL string - EbaiURL string - MtwmURL string -} - -func (*DataResource) TableUnique() [][]string { - return [][]string{ - []string{"HashCode", "ResoureType"}, - } + MainURL string `orm:"size(1024);column(main_url);index" json:"mainURL"` + QiniuURL string `orm:"size(1024);column(qiniu_url);index" json:"qiniuURL"` + EbaiURL string `orm:"size(1024);column(ebai_url);index" json:"ebaiURL"` + MtwmURL string `orm:"size(1024);column(mtwm_url);index" json:"mtwmURL"` } diff --git a/business/model/dao/common.go b/business/model/dao/common.go new file mode 100644 index 000000000..6c01fdadb --- /dev/null +++ b/business/model/dao/common.go @@ -0,0 +1,23 @@ +package dao + +import ( + "git.rosy.net.cn/jx-callback/business/model" +) + +func GetDataResource(db *DaoDB, hashCode, fullURL string) (dataRes *model.DataResource, err error) { + sql := ` + SELECT t1.* + FROM data_resource t1 + WHERE 1 = 1` + sqlParams := []interface{}{} + if hashCode != "" { + sql += " AND t1.hash_code = ?" + sqlParams = append(sqlParams, hashCode) + } + if fullURL != "" { + sql += " AND (t1.main_url = ? OR t1.qiniu_url = ? OR t1.ebai_url = ? OR t1.mtwm_url = ?)" + sqlParams = append(sqlParams, fullURL, fullURL, fullURL, fullURL) + } + err = GetRow(db, &dataRes, sql, sqlParams...) + return dataRes, err +} diff --git a/business/model/dao/food_recipe.go b/business/model/dao/food_recipe.go index 1f44d6887..bac34ad31 100644 --- a/business/model/dao/food_recipe.go +++ b/business/model/dao/food_recipe.go @@ -1,2 +1,108 @@ package dao +import ( + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/model" +) + +type FoodRecipeWithAction struct { + model.FoodRecipe + ActionType int8 `json:"actionType"` +} + +func QueryFoodRecipes(db *DaoDB, keyword string, recipeID int, authorID, userID string) (recipeList []*FoodRecipeWithAction, err error) { + var sql string + var sqlParams []interface{} + if userID != "" { + sql = ` + SELECT t1.*, t2.action_type + FROM food_recipe t1 + LEFT JOIN food_recipe_user t2 ON t2.recipe_id = t1.id AND t2.user_id = ? AND t2.deleted_at = ? + WHERE t1.deleted_at = ?` + sqlParams = []interface{}{ + userID, + utils.DefaultTimeValue, + utils.DefaultTimeValue, + } + } else { + sql = ` + SELECT t1.* + FROM food_recipe t1 + WHERE t1.deleted_at = ?` + sqlParams = []interface{}{ + utils.DefaultTimeValue, + } + } + if keyword != "" { + keywordLike := "%" + keyword + "%" + sql += " AND (t1.name LIKE ?" + sqlParams = append(sqlParams, keywordLike) + sql += ")" + } + if recipeID > 0 { + sql += " AND t1.id = ?" + sqlParams = append(sqlParams, recipeID) + } + if authorID != "" { + sql += " AND t1.author_id = ?" + sqlParams = append(sqlParams, authorID) + } + err = GetRows(db, &recipeList, sql, sqlParams...) + return recipeList, err +} + +func GetRecommendFoodRecipes(db *DaoDB, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { + if list, err := QueryFoodRecipes(db, keyword, 0, userID, ""); err == nil { + recipeList = FoodRecipeWithActionList2Recipe(list) + } + return recipeList, err +} + +func FoodRecipeWithActionList2Recipe(recipeList []*FoodRecipeWithAction) (outRecipeList []*model.FoodRecipe) { + for _, v := range recipeList { + outRecipeList = append(outRecipeList, &v.FoodRecipe) + } + return outRecipeList +} + +func QueryFoodRecipesItems(db *DaoDB, recipeID int) (recipeItemList []*model.FoodRecipeItem, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_item t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index" + err = GetRows(db, &recipeItemList, sql, sqlParams...) + return recipeItemList, err +} + +func QueryFoodRecipesItemChoices(db *DaoDB, recipeID int) (recipeItemChoiceList []*model.FoodRecipeItemChoice, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_item_choice t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index, t1.choice_index" + err = GetRows(db, &recipeItemChoiceList, sql, sqlParams...) + return recipeItemChoiceList, err +} + +func QueryFoodRecipesSteps(db *DaoDB, recipeID int) (recipeStepList []*model.FoodRecipeStep, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_step t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index" + err = GetRows(db, &recipeStepList, sql, sqlParams...) + return recipeStepList, err +} diff --git a/business/model/dao/food_recipe_test.go b/business/model/dao/food_recipe_test.go new file mode 100644 index 000000000..689ee2439 --- /dev/null +++ b/business/model/dao/food_recipe_test.go @@ -0,0 +1,40 @@ +package dao + +import ( + "testing" +) + +func TestQueryRecipes(t *testing.T) { + db := GetDB() + recipeList, err := QueryFoodRecipes(db, "", -1, "", "") + if err != nil { + t.Fatal(err) + } + if len(recipeList) > 0 { + t.Fatal("should not return list") + } + + recipeItemList, err := QueryFoodRecipesItems(db, -1) + if err != nil { + t.Fatal(err) + } + if len(recipeItemList) > 0 { + t.Fatal("should not return list") + } + + choiceList, err := QueryFoodRecipesItemChoices(db, -1) + if err != nil { + t.Fatal(err) + } + if len(choiceList) > 0 { + t.Fatal("should not return list") + } + + stepList, err := QueryFoodRecipesSteps(db, -1) + if err != nil { + t.Fatal(err) + } + if len(stepList) > 0 { + t.Fatal("should not return list") + } +} diff --git a/business/model/dao/store_test.go b/business/model/dao/store_test.go index 71e84092b..d0455d9e6 100644 --- a/business/model/dao/store_test.go +++ b/business/model/dao/store_test.go @@ -32,7 +32,7 @@ func TestFormalizeStoreStatus(t *testing.T) { } func TestGetStoreList4Role(t *testing.T) { - storeList, err := GetStoreList(GetDB(), "NiuBi") + storeList, err := GetStoreList(GetDB(), nil, nil, "NiuBi") t.Log(utils.Format4Output(storeList, false)) if err != nil { t.Fatal(err) diff --git a/business/model/model.go b/business/model/model.go index d49ac05bc..e52ed3b1d 100644 --- a/business/model/model.go +++ b/business/model/model.go @@ -114,7 +114,7 @@ func IsSyncStatusPrice(syncStatus int8) bool { return (syncStatus & SyncFlagPriceMask) != 0 } -func IsSyncStatusSec(syncStatus int8) bool { +func IsSyncStatusSeq(syncStatus int8) bool { return (syncStatus & SyncFlagSeqMask) != 0 } diff --git a/business/userstore/food_recipe.go b/business/userstore/food_recipe.go index 4eadec204..c1639cf96 100644 --- a/business/userstore/food_recipe.go +++ b/business/userstore/food_recipe.go @@ -19,7 +19,76 @@ type FoodRecipeStepParam struct { Img string `orm:"size(48)" json:"img"` } +type FoodRecipeItem struct { + model.FoodRecipeItem + ItemChoiceList []*model.FoodRecipeItemChoice +} + +type FoodRecipeDetail struct { + model.FoodRecipe + ItemList []*FoodRecipeItem + StepList []*model.FoodRecipeStep +} + +func updateFoodRecipeItemAndStep(ctx *jxcontext.Context, db *dao.DaoDB, recipeID int, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + for k, v := range itemList { + if len(v.SkuIDs) == 0 { + return fmt.Errorf("SkuIDs必须要有值") + } + skuIDs, err2 := dao.GetSkus(db, v.SkuIDs, nil, nil, nil) + if err = err2; err != nil { + return err + } + if len(v.SkuIDs) != len(skuIDs) { + return fmt.Errorf("某些SkuID不存在") + } + + item := &model.FoodRecipeItem{ + RecipeID: recipeID, + Index: int8(k + 1), + Name: v.Name, + } + dao.WrapAddIDCULEntity(item, ctx.GetUserName()) + if err = dao.CreateEntity(db, item); err != nil { + return err + } + for k2, v2 := range v.SkuIDs { + choice := &model.FoodRecipeItemChoice{ + RecipeID: recipeID, + Index: item.Index, + SkuID: v2, + + ChoiceIndex: int8(k2 + 1), + } + dao.WrapAddIDCULEntity(choice, ctx.GetUserName()) + if err = dao.CreateEntity(db, choice); err != nil { + return err + } + } + } + + for k, v := range stepList { + step := &model.FoodRecipeStep{ + RecipeID: recipeID, + Index: int8(k + 1), + Name: v.Name, + } + dao.WrapAddIDCULEntity(step, ctx.GetUserName()) + if err = dao.CreateEntity(db, step); err != nil { + return err + } + } + return err +} + func CreateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + if len(itemList) == 0 { + return fmt.Errorf("必须要有配料") + } + if len(stepList) == 0 { + return fmt.Errorf("必须要有操作步骤") + } + db := dao.GetDB() dao.Begin(db) defer func() { @@ -36,58 +105,195 @@ func CreateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, item if err = dao.CreateEntity(db, foodRecipe); err != nil { return err } - for k, v := range itemList { - if len(v.SkuIDs) == 0 { - return fmt.Errorf("SkuIDs必须要有值") - } - skuIDs, err2 := dao.GetSkus(db, v.SkuIDs, nil, nil, nil) - if err = err2; err != nil { - return err - } - if len(v.SkuIDs) != len(skuIDs) { - return fmt.Errorf("某些SkuIDs不存在") - } - - item := &model.FoodRecipeItem{ - RecipeID: foodRecipe.ID, - Index: int8(k + 1), - Name: v.Name, - } - dao.WrapAddIDCULEntity(item, ctx.GetUserName()) - if err = dao.CreateEntity(db, item); err != nil { - return err - } - for k2, v2 := range v.SkuIDs { - choice := &model.FoodRecipeItemChoice{ - RecipeID: foodRecipe.ID, - Index: item.Index, - SkuID: v2, - - ChoiceIndex: int8(k2 + 1), - } - dao.WrapAddIDCULEntity(choice, ctx.GetUserName()) - if err = dao.CreateEntity(db, choice); err != nil { - return err - } - } + if err = updateFoodRecipeItemAndStep(ctx, db, foodRecipe.ID, itemList, stepList); err != nil { + return err } - for k, v := range stepList { - step := &model.FoodRecipeStep{ - RecipeID: foodRecipe.ID, - Index: int8(k + 1), - Name: v.Name, + dao.Commit(db) + return err +} + +func UpdateFoodRecipe(ctx *jxcontext.Context, recipeID int, mapData map[string]interface{}, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + db := dao.GetDB() + + localRecipe := &model.FoodRecipe{} + localRecipe.ID = recipeID + if err = dao.GetEntity(db, localRecipe); err != nil { + return err + } + valid := dao.StrictMakeMapByStructObject(mapData, localRecipe, ctx.GetUserName()) + + dao.Begin(db) + defer func() { + if r := recover(); r != nil || err != nil { + dao.Rollback(db) + if r != nil { + panic(r) + } } - dao.WrapAddIDCULEntity(step, ctx.GetUserName()) - if err = dao.CreateEntity(db, step); err != nil { + }() + + if len(valid) > 0 { + if _, err = dao.UpdateEntityLogically(db, localRecipe, valid, ctx.GetUserName(), nil); err != nil { return err } } + if len(itemList) > 0 { + choice := &model.FoodRecipeItemChoice{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, choice, "RecipeID"); err != nil { + return err + } + + recipeItems := &model.FoodRecipeItem{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, recipeItems, "RecipeID"); err != nil { + return err + } + } + if len(stepList) > 0 { + recipeSteps := &model.FoodRecipeStep{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, recipeSteps, "RecipeID"); err != nil { + return err + } + } + if err = updateFoodRecipeItemAndStep(ctx, db, recipeID, itemList, stepList); err != nil { + return err + } dao.Commit(db) return err } -func UpdateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { +func QueryFoodRecipes(ctx *jxcontext.Context, keyword, authorID string) (recipeList []*dao.FoodRecipeWithAction, err error) { + _, userID := ctx.GetMobileAndUserID() + recipeList, err = dao.QueryFoodRecipes(dao.GetDB(), keyword, 0, authorID, userID) + return recipeList, err +} + +func GetRecommendFoodRecipes(ctx *jxcontext.Context, keyword string) (recipeList []*model.FoodRecipe, err error) { + _, userID := ctx.GetMobileAndUserID() + recipeList, err = dao.GetRecommendFoodRecipes(dao.GetDB(), keyword, userID) + return recipeList, err +} + +func GetRecipeDetail(ctx *jxcontext.Context, recipeID int) (recipeDetail *FoodRecipeDetail, err error) { + _, userID := ctx.GetMobileAndUserID() + db := dao.GetDB() + recipeList, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID) + if err != nil { + return nil, err + } + if len(recipeList) == 0 { + return nil, fmt.Errorf("找不到菜谱:%d", recipeID) + } + + itemList, err := dao.QueryFoodRecipesItems(db, recipeID) + if err != nil { + return nil, err + } + + itemChoiceList, err := dao.QueryFoodRecipesItemChoices(db, recipeID) + if err != nil { + return nil, err + } + choiceMap := make(map[int8][]*model.FoodRecipeItemChoice) + for _, v := range itemChoiceList { + choiceMap[v.Index] = append(choiceMap[v.Index], v) + } + + stepList, err := dao.QueryFoodRecipesSteps(db, recipeID) + if err != nil { + return nil, err + } + + recipeDetail = &FoodRecipeDetail{ + FoodRecipe: recipeList[0].FoodRecipe, + StepList: stepList, + } + for _, v := range itemList { + recipeDetail.ItemList = append(recipeDetail.ItemList, &FoodRecipeItem{ + FoodRecipeItem: *v, + ItemChoiceList: choiceMap[v.Index], + }) + } + return recipeDetail, err +} + +func VoteFoodRecipe(ctx *jxcontext.Context, recipeID, voteType int) (err error) { + _, userID := ctx.GetMobileAndUserID() + if userID == "" { + return fmt.Errorf("内部错误,找不到用户") + } + + db := dao.GetDB() + recipeList, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID) + if err != nil { + return err + } + if len(recipeList) == 0 { + return fmt.Errorf("找不到菜谱:%d", recipeID) + } + recipe := &recipeList[0].FoodRecipe + + dao.Begin(db) + defer func() { + if r := recover(); r != nil || err != nil { + dao.Rollback(db) + if r != nil { + panic(r) + } + } + }() + + recipeUser := &model.FoodRecipeUser{ + RecipeID: recipeID, + UserID: userID, + } + if err = dao.GetEntity(db, recipeUser); err != nil { + if dao.IsNoRowsError(err) { + return err + } + } + + actionMask := int8(0) + if voteType > 0 { + recipe.UpvoteCount++ + actionMask = model.RecipeActionUpvote + } else if voteType < 0 { + recipe.DownvoteCount++ + actionMask = model.RecipeActionDownvote + } + if recipeUser.ActionType&model.RecipeActionUpvote != 0 { + recipe.UpvoteCount-- + } else if recipeUser.ActionType&model.RecipeActionDownvote != 0 { + recipe.DownvoteCount-- + } + + if recipeUser.ActionType&actionMask != 0 || recipeUser.ActionType&(model.RecipeActionUpvote|model.RecipeActionDownvote) == 0 { + return fmt.Errorf("已经做过此操作了") + } + + recipeUser.ActionType &= actionMask | ^(model.RecipeActionUpvote | model.RecipeActionDownvote) + if recipeUser.ID == 0 { + dao.WrapAddIDCULDEntity(recipeUser, ctx.GetUserName()) + err = dao.CreateEntity(db, recipeUser) + } else { + recipeUser.LastOperator = ctx.GetUserName() + _, err = dao.UpdateEntity(db, recipeUser) + } + if err != nil { + return err + } + _, err = dao.UpdateEntity(db, recipe, "UpvoteCount", "DownvoteCount") + if err != nil { + return err + } + + dao.Commit(db) return err } diff --git a/controllers/cmd_food_recipe.go b/controllers/cmd_food_recipe.go index f0ace9f50..95637cbb5 100644 --- a/controllers/cmd_food_recipe.go +++ b/controllers/cmd_food_recipe.go @@ -11,8 +11,8 @@ type FoodRecipeController struct { beego.Controller } -// @Title 创建活动 -// @Description 创建活动 +// @Title 创建菜谱 +// @Description 创建菜谱 // @Param token header string true "认证token" // @Param name formData string true "菜谱名" // @Param description formData string true "菜谱描述" @@ -41,3 +41,87 @@ func (c *FoodRecipeController) CreateFoodRecipe() { return retVal, "", err }) } + +// @Title 编辑菜谱 +// @Description 编辑菜谱 +// @Param token header string true "认证token" +// @Param recipeID formData int true "菜谱ID" +// @Param name formData string false "菜谱名" +// @Param description formData string false "菜谱描述" +// @Param img formData string false "图片"" +// @Param timeInMinute formData string false "大约需要时间(分钟)" +// @Param recipeItems formData string false "菜谱配料" +// @Param recipeSteps formData string false "菜谱步骤" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /UpdateFoodRecipe [put] +func (c *FoodRecipeController) UpdateFoodRecipe() { + c.callUpdateFoodRecipe(func(params *tFoodrecipeUpdateFoodRecipeParams) (retVal interface{}, errCode string, err error) { + var ( + itemList []*userstore.FoodRecipeItemParam + stepList []*userstore.FoodRecipeStepParam + ) + if err = jxutils.Strings2Objs(params.RecipeItems, &itemList, params.RecipeSteps, &stepList); err == nil { + err = userstore.UpdateFoodRecipe(params.Ctx, params.RecipeID, params.MapData, itemList, stepList) + } + return retVal, "", err + }) +} + +// @Title 查询菜谱列表 +// @Description 查询菜谱列表 +// @Param token header string true "认证token" +// @Param keyword query string false "关键字" +// @Param userID query string false "厂商ID" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /QueryFoodRecipes [get] +func (c *FoodRecipeController) QueryFoodRecipes() { + c.callQueryFoodRecipes(func(params *tFoodrecipeQueryFoodRecipesParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.QueryFoodRecipes(params.Ctx, params.Keyword, params.UserID) + return retVal, "", err + }) +} + +// @Title 得到我的推荐菜谱列表 +// @Description 得到我的推荐菜谱列表 +// @Param token header string true "认证token" +// @Param keyword query string false "关键字" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetRecommendFoodRecipes [get] +func (c *FoodRecipeController) GetRecommendFoodRecipes() { + c.callGetRecommendFoodRecipes(func(params *tFoodrecipeGetRecommendFoodRecipesParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.GetRecommendFoodRecipes(params.Ctx, params.Keyword) + return retVal, "", err + }) +} + +// @Title 得到菜谱详情 +// @Description 得到菜谱详情 +// @Param token header string true "认证token" +// @Param recipeID query int true "菜谱ID" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetRecipeDetail [get] +func (c *FoodRecipeController) GetRecipeDetail() { + c.callGetRecipeDetail(func(params *tFoodrecipeGetRecipeDetailParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.GetRecipeDetail(params.Ctx, params.RecipeID) + return retVal, "", err + }) +} + +// @Title 对菜谱投票 +// @Description 对菜谱投票 +// @Param token header string true "认证token" +// @Param recipeID formData int true "菜谱ID" +// @Param voteType formData int true "-1:踩,0:取消之前的投票,1:赞" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /VoteFoodRecipe [post] +func (c *FoodRecipeController) VoteFoodRecipe() { + c.callVoteFoodRecipe(func(params *tFoodrecipeVoteFoodRecipeParams) (retVal interface{}, errCode string, err error) { + err = userstore.VoteFoodRecipe(params.Ctx, params.RecipeID, params.VoteType) + return retVal, "", err + }) +} diff --git a/controllers/cms.go b/controllers/cms.go index 325f588de..94dcd24dd 100644 --- a/controllers/cms.go +++ b/controllers/cms.go @@ -17,7 +17,7 @@ type CmsController struct { // @Title 得到地点(省,城市,区)信息 // @Description 得到地点(省,城市,区)信息。 -// @Param token header string true "认证token"// @Param keyword query string false "查询关键字(可以为空,为空表示不限制)" +// @Param token header string true "认证token" // @Param keyword query string false "查询关键字(可以为空,为空表示不限制)" // @Param parentCode query int false "上级地点code,这个指的是国家标准CODE(中国为:100000,北京为:110000,北京市为:110100),不是数据库中的ID" // @Param level query int false "地点级别:省为1,市为2,区为3,注意直辖市也要分省与市级" @@ -104,6 +104,23 @@ func (c *CmsController) GetQiniuUploadToken() { }) } +// @Title 注册数据资源 +// @Description 注册数据资源 +// @Param token header string true "认证token" +// @Param hashCode formData string true "md5" +// @Param resourceURL formData string true "资源URL" +// @Param mimeType formData string true "资源MIME类型,当前只支持:image/jpeg,image/png,video/mpeg,video/mp4" +// @Param name formData string faslse "资源名" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /RegisterDataResource [post] +func (c *CmsController) RegisterDataResource() { + c.callRegisterDataResource(func(params *tCmsRegisterDataResourceParams) (retVal interface{}, errCode string, err error) { + retVal, err = cms.RegisterDataResource(params.Ctx, params.Name, params.ResourceURL, params.MimeType, params.HashCode) + return retVal, "", err + }) +} + // @Title 根据坐标得到区码 // @Description 根据坐标得到区码,坐标都为火星坐标(有些市是没有区的,比如东莞,这种情况下返回的区码是一个假的区域,即市的编码加上9000000) // @Param token header string true "认证token" @@ -295,4 +312,4 @@ func (c *CmsController) CreateQrOrBarCode() { retVal, err = jxutils.CreateQrOrBarCode(params.Width, params.Height, params.Codetype, params.SrcData) return retVal, "", err }) -} \ No newline at end of file +} diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 70da7febd..dded52e0e 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -56,6 +56,7 @@ func Init() { orm.RegisterModel(&model.SensitiveWord{}) orm.RegisterModel(&model.StoreScore{}) orm.RegisterModel(&model.FoodRecipe{}, &model.FoodRecipeStep{}, &model.FoodRecipeItem{}, &model.FoodRecipeItemChoice{}, &model.FoodRecipeUser{}) + orm.RegisterModel(&model.DataResource{}) // create table orm.RunSyncdb("default", false, true) diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 86bd3bf1f..0ae93a495 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -385,6 +385,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"], + beego.ControllerComments{ + Method: "RegisterDataResource", + Router: `/RegisterDataResource`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"], beego.ControllerComments{ Method: "SendMsg2Somebody", @@ -484,6 +493,51 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"], + beego.ControllerComments{ + Method: "GetRecipeDetail", + Router: `/GetRecipeDetail`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"], + beego.ControllerComments{ + Method: "GetRecommendFoodRecipes", + Router: `/GetRecommendFoodRecipes`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"], + beego.ControllerComments{ + Method: "QueryFoodRecipes", + Router: `/QueryFoodRecipes`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"], + beego.ControllerComments{ + Method: "UpdateFoodRecipe", + Router: `/UpdateFoodRecipe`, + AllowHTTPMethods: []string{"put"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"], + beego.ControllerComments{ + Method: "VoteFoodRecipe", + Router: `/VoteFoodRecipe`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"], beego.ControllerComments{ Method: "BuildSkuFromEbaiStore",