diff --git a/business/jxstore/act/act.go b/business/jxstore/act/act.go index 5a41bc9c8..3e4f2abcb 100644 --- a/business/jxstore/act/act.go +++ b/business/jxstore/act/act.go @@ -66,6 +66,18 @@ type tPreCreateActInfo struct { ActStoreSku []*tPreCreateActStoreSku `json:"actStoreSku"` } +type ActManager struct { +} + +var ( + FixedActManager *ActManager +) + +func init() { + FixedActManager = &ActManager{} + partner.InitActManager(FixedActManager) +} + func ActStoreSkuParam2Model(ctx *jxcontext.Context, db *dao.DaoDB, act *model.Act, vendorIDs []int, actStoreSku []*ActStoreSkuParam) (validVendorIDs []int, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, err error) { wholeValidVendorMap := make(map[int]int) if len(actStoreSku) > 0 { @@ -331,6 +343,9 @@ func CreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actRules if err != nil { return "", err } + if len(validVendorIDs) == 0 { + return "", fmt.Errorf("没有一个合法平台可以创建活动") + } var actMapList []*model.ActMap for _, vendorID := range validVendorIDs { @@ -364,6 +379,115 @@ func CreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actRules return hint, err } +func vendorActInfo2Model(ctx *jxcontext.Context, db *dao.DaoDB, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, err error) { + vendorStoreIDMap := make(map[string]int) + vendorSkuIDMap := make(map[string]int) + for _, v := range actStoreSku { + vendorStoreIDMap[v.VendorStoreID] = 1 + vendorSkuIDMap[v.VendorSkuID] = 1 + } + globals.SugarLogger.Debug(utils.Format4Output(vendorStoreIDMap, false)) + globals.SugarLogger.Debug(utils.Format4Output(vendorSkuIDMap, false)) + + vendorID := act2.VendorID + storeSkuList, err2 := dao.GetStoresSkusInfoByVendorInfo(db, vendorID, jxutils.StringMap2List(vendorStoreIDMap), jxutils.StringMap2List(vendorSkuIDMap)) + if err = err2; err != nil { + return nil, nil, err + } + storeSkuMap := make(map[string]*dao.StoreSkuBindWithVendorInfo) + for _, v := range storeSkuList { + storeSkuMap[v.VendorStoreID+"/"+v.VendorSkuID] = v + } + + for _, v := range actStoreSku { + if storeSkuInfo := storeSkuMap[v.VendorStoreID+"/"+v.VendorSkuID]; storeSkuInfo != nil { + actSku := &model.ActStoreSku{ + ActID: act2.ID, + StoreID: storeSkuInfo.StoreID, + SkuID: storeSkuInfo.SkuID, + + Stock: v.Stock, + ActPrice: v.ActualActPrice, + OriginalPrice: int64(storeSkuInfo.Price), + } + dao.WrapAddIDCULDEntity(actSku, ctx.GetUserName()) + actStoreSkuList = append(actStoreSkuList, actSku) + + actSkuMap := &model.ActStoreSkuMap{ + ActID: act2.ID, + VendorActID: act2.VendorActID, + StoreID: storeSkuInfo.StoreID, + SkuID: storeSkuInfo.SkuID, + VendorID: vendorID, + + SyncStatus: 0, + VendorPrice: 0, + ActualActPrice: v.ActualActPrice, + } + dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName()) + actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap) + } + } + return actStoreSkuList, actStoreSkuMapList, err +} + +func (a *ActManager) CreateActFromVendor(ctx *jxcontext.Context, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actID int, err error) { + globals.SugarLogger.Debugf("CreateActFromVendor vendorID:%d, vendorActID:%s", act2.VendorID, act2.VendorActID) + db := dao.GetDB() + dao.Begin(db) + defer func() { + if r := recover(); r != nil { + dao.Rollback(db) + panic(r) + } + }() + act := &act2.Act + dao.WrapAddIDCULDEntity(act, ctx.GetUserName()) + err = dao.CreateEntity(db, act) + if err != nil { + dao.Rollback(db) + return 0, err + } + + actMap := &model.ActMap{ + ActID: act.ID, + VendorID: act2.VendorID, + VendorActID: act2.VendorActID, + SyncStatus: 0, + } + dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName()) + err = dao.CreateEntity(db, actMap) + if err != nil { + dao.Rollback(db) + return 0, err + } + actStoreSkuList, actStoreSkuMapList, err := vendorActInfo2Model(ctx, db, act2, actStoreSku) + if err != nil { + dao.Rollback(db) + return 0, err + } + if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil { + dao.Rollback(db) + return 0, err + } + dao.Commit(db) + + return act.ID, nil +} + +func (a *ActManager) IsVendorActExist(ctx *jxcontext.Context, vendorActID string, vendorID int) (isExist bool) { + db := dao.GetDB() + actMap := &model.ActMap{ + VendorActID: vendorActID, + VendorID: vendorID, + } + actMap.DeletedAt = utils.ZeroTimeValue + if err := dao.GetEntity(db, actMap, "VendorActID", "VendorID", "DeletedAt"); err == nil { + isExist = true + } + return isExist +} + func QueryActs(ctx *jxcontext.Context, actID int, offset, pageSize int, keyword string, statusList []int, actTypeList []int, storeID, skuID, cityCode int, beginAt, endAt, createdAtFrom, createdAtTo time.Time) (pagedInfo *dao.PagedActListInfo, err error) { return dao.QueryActs(dao.GetDB(), actID, offset, pageSize, keyword, statusList, actTypeList, storeID, skuID, cityCode, beginAt, endAt, createdAtFrom, createdAtTo) } diff --git a/business/jxstore/act/act_test.go b/business/jxstore/act/act_test.go index 4e9c6b514..f4e2d398c 100644 --- a/business/jxstore/act/act_test.go +++ b/business/jxstore/act/act_test.go @@ -2,12 +2,10 @@ package act import ( "testing" - "time" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/model/dao" - "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals/testinit" "git.rosy.net.cn/jx-callback/business/model" @@ -68,42 +66,49 @@ func TestCreateActOnAlpha(t *testing.T) { func TestCreateActOnDev(t *testing.T) { actStoreSkuList := []*ActStoreSkuParam{ + // &ActStoreSkuParam{ + // ActStoreSku: model.ActStoreSku{ + // StoreID: 100884, + // SkuID: 22716, + // }, + // }, + // &ActStoreSkuParam{ + // ActStoreSku: model.ActStoreSku{ + // StoreID: 100884, + // SkuID: 22717, + // }, + // }, + // &ActStoreSkuParam{ + // ActStoreSku: model.ActStoreSku{ + // StoreID: 100920, + // SkuID: 22714, + // }, + // }, + // &ActStoreSkuParam{ + // ActStoreSku: model.ActStoreSku{ + // StoreID: 100920, + // SkuID: 22715, + // }, + // }, &ActStoreSkuParam{ ActStoreSku: model.ActStoreSku{ - StoreID: 100884, - SkuID: 22716, - }, - }, - &ActStoreSkuParam{ - ActStoreSku: model.ActStoreSku{ - StoreID: 100884, - SkuID: 22717, - }, - }, - &ActStoreSkuParam{ - ActStoreSku: model.ActStoreSku{ - StoreID: 100920, - SkuID: 22714, - }, - }, - &ActStoreSkuParam{ - ActStoreSku: model.ActStoreSku{ - StoreID: 100920, - SkuID: 22715, + StoreID: 100119, + SkuID: 26595, }, }, } - actID, err := CreateAct(jxcontext.AdminCtx, &model.Act{ - Name: "测试活动", - PricePercentage: 80, - Type: model.ActSkuDirectDown, - BeginAt: time.Now().Add(-24 * time.Hour), - EndAt: time.Now().Add(10 * 24 * time.Hour), - }, []int{model.VendorIDJD, model.VendorIDMTWM /*, model.VendorIDEBAI*/}, nil, actStoreSkuList, false) - if err != nil { - t.Fatal(err) - } - globals.SugarLogger.Debug(actID) + t.Log(utils.Format4Output(actStoreSkuList, true)) + // actID, err := CreateAct(jxcontext.AdminCtx, &model.Act{ + // Name: "测试活动", + // PricePercentage: 80, + // Type: model.ActSkuDirectDown, + // BeginAt: time.Now().Add(-24 * time.Hour), + // EndAt: time.Now().Add(10 * 24 * time.Hour), + // }, []int{model.VendorIDJD, model.VendorIDMTWM, model.VendorIDEBAI}, nil, actStoreSkuList, false) + // if err != nil { + // t.Fatal(err) + // } + // globals.SugarLogger.Debug(actID) } func TestCancelAct(t *testing.T) { diff --git a/business/jxutils/jxutils_cms.go b/business/jxutils/jxutils_cms.go index 50a4c9d9f..7d2800220 100644 --- a/business/jxutils/jxutils_cms.go +++ b/business/jxutils/jxutils_cms.go @@ -155,6 +155,16 @@ func Int64Map2List(int64Map map[int64]int) []int64 { return retVal } +func StringMap2List(stringMap map[string]int) []string { + retVal := make([]string, len(stringMap)) + index := 0 + for k := range stringMap { + retVal[index] = k + index++ + } + return retVal +} + func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) { lowerSpecUnit := strings.ToLower(specUnit) if lowerSpecUnit == "kg" || lowerSpecUnit == "l" { diff --git a/business/model/act.go b/business/model/act.go index 0d642beb5..dec62a1b2 100644 --- a/business/model/act.go +++ b/business/model/act.go @@ -19,6 +19,7 @@ const ( ) const ( + ActStatusNA = 0 // 未知 ActStatusCreated = 1 // 需同步 ActStatusCanceled = 2 // 需同步 ActStatusEnded = 3 // 不需要同步,根据活动时间自动刷新的 @@ -33,6 +34,13 @@ var ( ActSkuDirectDown: "直降", ActSkuSecKill: "秒杀", } + + ActStatusName = map[int]string{ + ActStatusNA: "未知", + ActStatusCreated: "正常", + ActStatusCanceled: "取消", + ActStatusEnded: "结束", + } ) type Act struct { @@ -75,6 +83,7 @@ type ActMap struct { func (*ActMap) TableUnique() [][]string { return [][]string{ []string{"ActID", "VendorID", "DeletedAt"}, + []string{"VendorActID", "VendorID", "DeletedAt"}, } } diff --git a/business/model/const.go b/business/model/const.go index 8ad0cf6ba..1b7da9b68 100644 --- a/business/model/const.go +++ b/business/model/const.go @@ -189,11 +189,6 @@ var ( AfsAppealTypeReturnAndRefund: "退货退款", AfsAppealTypeNewGoods: "重发商品", } - ActStatusName = map[int]string{ - ActStatusCreated: "正常", - ActStatusCanceled: "取消", - ActStatusEnded: "结束", - } ) const ( diff --git a/business/model/dao/store_sku.go b/business/model/dao/store_sku.go index 3d72a58a0..2491f0fd4 100644 --- a/business/model/dao/store_sku.go +++ b/business/model/dao/store_sku.go @@ -83,6 +83,13 @@ type MissingStoreSkuInfo struct { RefPrice int } +type StoreSkuBindWithVendorInfo struct { + model.StoreSkuBind + + VendorStoreID string `orm:"column(vendor_store_id)"` + VendorSkuID string `orm:"column(vendor_sku_id)"` +} + // 单门店模式厂商适用 // 从store_sku_bind中,得到所有依赖的商家分类信息 func GetSkusCategories(db *DaoDB, vendorID, storeID int, skuIDs []int, level int) (cats []*SkuStoreCatInfo, err error) { @@ -393,6 +400,35 @@ func GetStoresSkusInfo(db *DaoDB, storeIDs, skuIDs []int) (storeSkuList []*model return storeSkuList, err } +// vendorID, vendorStoreIDs和vendorSkuIDs都是必须参数 +func GetStoresSkusInfoByVendorInfo(db *DaoDB, vendorID int, vendorStoreIDs, vendorSkuIDs []string) (storeSkuList []*StoreSkuBindWithVendorInfo, err error) { + sql := ` + SELECT t1.*, + %s.%s_id vendor_sku_id, t2.vendor_store_id + FROM store_sku_bind t1 + JOIN store_map t2 ON t2.store_id = t1.store_id AND t2.vendor_id = ? AND t2.deleted_at = ? + JOIN sku t3 ON t3.id = t1.sku_id + WHERE t1.deleted_at = ? AND t2.vendor_store_id IN (` + GenQuestionMarks(len(vendorStoreIDs)) + `) + AND %s.%s_id IN (` + GenQuestionMarks(len(vendorSkuIDs)) + `)` + sqlParams := []interface{}{ + vendorID, + utils.DefaultTimeValue, + utils.DefaultTimeValue, + vendorStoreIDs, + vendorSkuIDs, + } + + isSingleStorePF := model.MultiStoresVendorMap[vendorID] != 1 + tableName := "t1" + if !isSingleStorePF { + tableName = "t3" + } + fieldPrefix := ConvertDBFieldPrefix(model.VendorNames[vendorID]) + sql = fmt.Sprintf(sql, tableName, fieldPrefix, tableName, fieldPrefix) + err = GetRows(db, &storeSkuList, sql, sqlParams...) + return storeSkuList, err +} + func GetMissingStoreSkuFromOrder(db *DaoDB, storeIDs []int, fromTime time.Time) (storeSkuList []*MissingStoreSkuInfo, err error) { if time.Now().Sub(fromTime) > 24*time.Hour*60 { return nil, fmt.Errorf("GetMissingStoreSkuFromOrder,时间超过60天") diff --git a/business/partner/partner_act.go b/business/partner/partner_act.go index e49cb653e..dd4099a40 100644 --- a/business/partner/partner_act.go +++ b/business/partner/partner_act.go @@ -9,6 +9,11 @@ import ( "git.rosy.net.cn/jx-callback/business/model/dao" ) +type IActManager interface { + IsVendorActExist(ctx *jxcontext.Context, vendorActID string, vendorID int) (isExist bool) + CreateActFromVendor(ctx *jxcontext.Context, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actID int, err error) +} + type IPurchasePlatformActHandler interface { // // 如果是单品级活动,actOrderRules为空 // // 如果是订单级活动,actStoreSku可以为空(表示不限制SKU) @@ -20,6 +25,14 @@ type IPurchasePlatformActHandler interface { SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) } +var ( + CurActManager IActManager +) + +func InitActManager(p IActManager) { + CurActManager = p +} + func SplitActStoreSku(actStoreSkuList []*model.ActStoreSku2) (actStoreSkuMap map[int][]*model.ActStoreSku2) { actStoreSkuMap = make(map[int][]*model.ActStoreSku2) for _, v := range actStoreSkuList { diff --git a/business/partner/purchase/jd/act.go b/business/partner/purchase/jd/act.go index 84924602e..a7b995978 100644 --- a/business/partner/purchase/jd/act.go +++ b/business/partner/purchase/jd/act.go @@ -24,6 +24,15 @@ type LogicUpdateInfo struct { Condition map[string]interface{} } +var ( + jdSkuActStatusMap = map[int]int{ + jdapi.PromotionStateNotConfirm: model.ActStatusNA, + jdapi.PromotionStateConfirmed: model.ActStatusCreated, + jdapi.PromotionStateCanceled: model.ActStatusCanceled, + jdapi.PromotionStateEnded: model.ActStatusEnded, + } +) + func splitPromotionSku(skus []*jdapi.PromotionSku, maxCount int) (skusList [][]*jdapi.PromotionSku) { for { skusLen := len(skus) @@ -37,6 +46,19 @@ func splitPromotionSku(skus []*jdapi.PromotionSku, maxCount int) (skusList [][]* return skusList } +func jdSkuActType2Jx(actType int) int { + if actType == jdapi.PromotionTypeDirectDown { + return model.ActSkuDirectDown + } else if actType == jdapi.PromotionTypeSeckill || actType == jdapi.PromotionTypeLimitedTime { + return model.ActSkuSecKill + } + return 0 +} + +func jdSkuActStatus2Jx(jdActState int) int { + return jdSkuActStatusMap[jdActState] +} + func CreatePromotionInfos(promotionType int, name string, beginDate, endDate time.Time, outInfoId, advertising, traceId string) (infoId int64, err error) { if globals.EnableJdStoreWrite { if promotionType == model.ActSkuDirectDown { @@ -273,17 +295,60 @@ func (c *PurchaseHandler) SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITa return err } -func (c *PurchaseHandler) OnActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { +func OnActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { jxutils.CallMsgHandler(func() { - retVal = c.onActMsg(msg) + retVal = curPurchaseHandler.onActMsg(msg) }, jxutils.ComposeUniversalOrderID(msg.BillID, model.VendorIDJD)) return retVal } func (c *PurchaseHandler) onActMsg(msg *jdapi.CallbackOrderMsg) (retVal *jdapi.CallbackResponse) { + if msg.StatusID == jdapi.PromotionStatusSingleOK || msg.StatusID == jdapi.PromotionStatusLimitTimeOK { + promotionID := msg.BillID + if !partner.CurActManager.IsVendorActExist(jxcontext.AdminCtx, promotionID, model.VendorIDJD) { + act, actStoreSkuList, err := getActFromJD(promotionID) + if err == nil { + _, err = partner.CurActManager.CreateActFromVendor(jxcontext.AdminCtx, act, actStoreSkuList) + } + if err != nil { + retVal = jdapi.Err2CallbackResponse(err, promotionID) + } + } + } return retVal } -func getActFromJD(promotionID int64) (act *model.Act, actStoreSkuList []*model.ActStoreSku) { - return act, actStoreSkuList +func getActFromJD(promotionID string) (act *model.Act2, actStoreSkuList []*model.ActStoreSku2, err error) { + result, err := api.JdAPI.QueryPromotionInfo(utils.Str2Int64(promotionID)) + if err == nil && len(result.SkuResultList) > 0 { + act = &model.Act2{ + Act: model.Act{Name: result.Source + "-" + utils.Int64ToStr(result.PromotionInfoID), + Type: jdSkuActType2Jx(result.PromotionType), + Status: jdSkuActStatus2Jx(result.PromotionState), + BeginAt: result.BeginTime.GoTime(), + EndAt: result.EndTime.GoTime(), + Source: result.Source, + CreateType: model.ActCreateTypeCallback, + PricePercentage: 0, + LimitDaily: result.SkuResultList[0].LimitDaily, + LimitCount: 1, + }, + VendorID: model.VendorIDJD, + VendorActID: promotionID, + } + if result.SkuResultList[0].LimitPin == 1 || result.SkuResultList[0].LimitDevice == 1 { + act.LimitUser = 1 + } + + for _, v := range result.SkuResultList { + if v.PromotionState != jdapi.PromotionStateCanceled { + actStoreSkuList = append(actStoreSkuList, &model.ActStoreSku2{ + VendorStoreID: utils.Int64ToStr(v.StationNo), + VendorSkuID: utils.Int64ToStr(v.SkuID), + ActualActPrice: int64(v.PromotionPrice), + }) + } + } + } + return act, actStoreSkuList, err } diff --git a/main.go b/main.go index f1ac86e39..248253084 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( _ "git.rosy.net.cn/jx-callback/business/partner/printer/xiaowm" _ "git.rosy.net.cn/jx-callback/business/partner/printer/yilianyun" _ "git.rosy.net.cn/jx-callback/business/partner/printer/zhongwu" + + _ "git.rosy.net.cn/jx-callback/business/jxstore/act" "github.com/astaxie/beego" )