package act

import (
	"fmt"
	"math"
	"sort"
	"sync"
	"time"

	"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"

	"git.rosy.net.cn/jx-callback/business/authz"

	"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"

	// "github.com/astaxie/beego/adapter/plugins/authz"

	"git.rosy.net.cn/jx-callback/business/authz/autils"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
	"git.rosy.net.cn/jx-callback/globals/api"

	"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/baseapi/utils/errlist"
	"git.rosy.net.cn/jx-callback/business/jxstore/cms"
	"git.rosy.net.cn/jx-callback/business/jxstore/permission"
	"git.rosy.net.cn/jx-callback/business/jxutils"
	"git.rosy.net.cn/jx-callback/business/partner"
	"git.rosy.net.cn/jx-callback/globals"
	"github.com/360EntSecGroup-Skylar/excelize"

	"git.rosy.net.cn/jx-callback/business/jxutils/jsonerr"
	"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"
	"git.rosy.net.cn/jx-callback/business/model/dao"
)

const (
	DefActSkuStock = 200 // 缺省活动库存
)

type ActOrderRuleParam struct {
	SalePrice   int64 `orm:"" json:"salePrice"`   // 满的价格
	DeductPrice int64 `orm:"" json:"deductPrice"` // 减的价格
}

type ActStoreSkuParam struct {
	model.ActStoreSku

	ActualActPrice int64  `json:"actualActPrice,omitempty"` // 单品级活动用，创建活动时商品的活动价格
	VendorPrice    int64  `json:"vendorPrice,omitempty"`    // 创建活动时的平台价格
	ErrMsg         string `json:"errMsg,omitempty"`
}

type ActDetail struct {
	model.Act2
}

type tPreCreateActVendorInfo struct {
	VendorID int

	VendorPrice    int64 `orm:"" json:"vendorPrice"`    // 单品级活动用，创建活动时商品的原始平台价
	ActualActPrice int64 `orm:"" json:"actualActPrice"` // 单品级活动用，创建活动时商品的活动价格
}

type tPreCreateActStoreSku struct {
	model.ActStoreSku
	VendorInfoList []*tPreCreateActVendorInfo `json:"vendorInfoList"`
}

type tPreCreateActInfo struct {
	model.Act

	ValidVendorIDs []int
	ActStoreSku    []*tPreCreateActStoreSku `json:"actStoreSku"`
}

type tActRuleInfo struct {
	MinDiscount int
	MaxDiscount int
}

type ActManager struct {
}

type SheetParamAct struct {
	StoreIDCol            int
	SkuIDCol              int
	SkuPricePercentageCol int
	ActPriceCol           int
	EarningPriceCol       int
	StockCol              int
	ActTypeCol            int
	ActPricePercentageCol int
	ActNameCol            int
	BeginTimeCol          int
	EndTimeCol            int
}

var (
	FixedActManager *ActManager

	actRuleMap = map[int]map[int]*tActRuleInfo{
		model.VendorIDJD: map[int]*tActRuleInfo{
			model.ActSkuFake: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 500,
			},
			model.ActSkuDirectDown: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 99,
			},
			model.ActSkuSecKill: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 80,
			},
		},
		model.VendorIDMTWM: map[int]*tActRuleInfo{
			model.ActSkuFake: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 500,
			},
			model.ActSkuDirectDown: &tActRuleInfo{
				MinDiscount: 30,
				MaxDiscount: 99,
			},
			model.ActSkuSecKill: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 30,
			},
		},
		model.VendorIDEBAI: map[int]*tActRuleInfo{
			model.ActSkuFake: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 500,
			},
			model.ActSkuDirectDown: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 99,
			},
		},
		model.VendorIDJX: map[int]*tActRuleInfo{
			model.ActSkuFake: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 500,
			},
			model.ActSkuDirectDown: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 99,
			},
			model.ActSkuSecKill: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 80,
			},
			model.ActSkuDiscount: &tActRuleInfo{
				MinDiscount: 100,
				MaxDiscount: 100,
			},
		},
		model.VendorIDTaoVegetable: map[int]*tActRuleInfo{
			model.ActSkuFake: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 500,
			},
			model.ActSkuDirectDown: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 99,
			},
			model.ActSkuSecKill: &tActRuleInfo{
				MinDiscount: 0,
				MaxDiscount: 30,
			},
			model.ActSkuDiscount: &tActRuleInfo{
				MinDiscount: 100,
				MaxDiscount: 100,
			},
		},
	}
)

func init() {
	FixedActManager = &ActManager{}
	partner.InitActManager(FixedActManager)
}

func getVendorPriceFromStoreSkuBind(bind *model.StoreSkuBind, vendorID int) (vendorPrice int) {
	switch vendorID {
	case model.VendorIDJD:
		vendorPrice = bind.JdPrice
	case model.VendorIDMTWM:
		vendorPrice = bind.MtwmPrice
	case model.VendorIDEBAI:
		vendorPrice = bind.EbaiPrice
	case model.VendorIDJX:
		vendorPrice = bind.JxPrice
	case model.VendorIDDD:
		vendorPrice = bind.DdPrice
	case model.VendorIDTaoVegetable:
		vendorPrice = bind.TaoPrice
	}
	return vendorPrice
}

func ActStoreSkuParam2Model(ctx *jxcontext.Context, db *dao.DaoDB, act *model.Act, vendorIDs []int, actStoreSku []*ActStoreSkuParam) (
	validVendorIDs []int, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap, conflictActStoreSku []*model.ActStoreSku2, err error) {
	wholeValidVendorMap := make(map[int]int)
	if len(actStoreSku) > 0 {
		storeIDMap := make(map[int]int)
		skuIDMap := make(map[int]int)
		storeSkuParamMap := make(map[int][]*ActStoreSkuParam)

		actStoreSkuMap := make(map[int64]bool)
		var wrongSkuList []*ActStoreSkuParam
		for _, v := range actStoreSku {
			if act.Type == model.ActSkuFake && v.EarningPrice == 0 {
				wrongSkuList = append(wrongSkuList, v)
			} else {
				storeIDMap[v.StoreID] = 1
				skuIDMap[v.SkuID] = 1
				storeSkuParamMap[v.StoreID] = append(storeSkuParamMap[v.StoreID], v)

				actStoreSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = true
			}
		}
		if len(wrongSkuList) > 0 {
			return nil, nil, nil, nil, jsonerr.New(wrongSkuList, model.ErrCodeJsonActEarningPriceIsZero)
		}
		storeIDs := jxutils.IntMap2List(storeIDMap)
		skuIDs := jxutils.IntMap2List(skuIDMap)
		// 判断活动是否重叠的检查，当前忽略京东平台及所有结算信息
		if act.Type != model.ActSkuFake {
			effectActStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, 0, vendorIDs, act.Type, storeIDs, skuIDs, act.BeginAt, act.EndAt)
			if err != nil {
				return nil, nil, nil, nil, err
			}
			if len(effectActStoreSkuList) > 0 {
				for _, v := range effectActStoreSkuList {
					if actStoreSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] {
						conflictActStoreSku = append(conflictActStoreSku, v)
					}
				}
			}
		}

		storeSkuList, err2 := dao.GetStoresSkusInfo(db, storeIDs, skuIDs)
		if err = err2; err != nil {
			return nil, nil, nil, nil, err
		}
		storeSkuMap := make(map[int64]*model.StoreSkuBind)
		for _, v := range storeSkuList {
			storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v
		}

		for storeID, oneStoreSkuParam := range storeSkuParamMap {
			validVendorMap := make(map[int]int)
			validSkuMap := make(map[int]int)
			for _, vendorID := range vendorIDs {
				storeDetail, err2 := dao.GetStoreDetail(db, storeID, vendorID, "")
				if err = err2; err == nil {
					if storeDetail.IsSync != 0 {
						if act.Type == model.ActSkuFake ||
							storeDetail.Status != model.StoreStatusDisabled && storeDetail.VendorStatus != model.StoreStatusDisabled {
							for _, v := range oneStoreSkuParam {
								validVendorMap[vendorID] = 1
								validSkuMap[v.SkuID] = 1
								v.ActID = act.ID
								actSkuMap := &model.ActStoreSkuMap{
									ActID:    act.ID,
									StoreID:  storeID,
									SkuID:    v.SkuID,
									VendorID: vendorID,
								}
								storeSkuInfo := storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)]
								if storeSkuInfo != nil {
									jxPrice := storeSkuInfo.Price
									actSkuMap.VendorPrice = int64(getVendorPriceFromStoreSkuBind(storeSkuInfo, vendorID))
									v.OriginalPrice = int64(jxPrice)
								}
								var err2 error
								if act.Type != model.ActSkuFake { // 非结算，要计算实际活动价格
									if storeSkuInfo == nil {
										v.ErrMsg = fmt.Sprintf("门店:%d没有关注商品:%d", v.StoreID, v.SkuID)
										wrongSkuList = append(wrongSkuList, v)
										continue
									}
									if !(vendorID == model.VendorIDJX || act.Type == model.ActSkuFake) {
										actSkuMap.SyncStatus = model.SyncFlagNewMask
									}
									if v.ActPrice != 0 {
										actSkuMap.ActualActPrice = v.ActPrice
									} else {
										percentage := act.PricePercentage
										if v.PricePercentage != 0 {
											percentage = v.PricePercentage
										}
										actSkuMap.ActualActPrice = int64(jxutils.CaculateSkuActVendorPrice(int(actSkuMap.VendorPrice), percentage, 0))
										if actSkuMap.ActualActPrice > 10 {
											actSkuMap.ActualActPrice = int64(math.Floor(float64(actSkuMap.ActualActPrice)/10) * 10)
										}
									}
									if actSkuMap.ActualActPrice <= 0 {
										actSkuMap.ActualActPrice = 1
									}
									if err2 = checkDiscountValidation(vendorIDs, act.Type, float64(actSkuMap.ActualActPrice)*100/float64(actSkuMap.VendorPrice)); err2 != nil {
										v.ErrMsg = err2.Error()
										v.ActualActPrice = actSkuMap.ActualActPrice
										v.VendorPrice = actSkuMap.VendorPrice
										wrongSkuList = append(wrongSkuList, v)
									}
								}
								if err2 == nil {
									actSkuMap.EarningPrice = v.EarningPrice
									dao.WrapAddIDCULDEntity(actSkuMap, ctx.GetUserName())
									actStoreSkuMapList = append(actStoreSkuMapList, actSkuMap)
								}
							}
							wholeValidVendorMap[vendorID] = 1
						}
					}
				} else if !dao.IsNoRowsError(err) {
					return nil, nil, nil, nil, err
				} else {
					err = nil
				}
			}
			if len(wrongSkuList) == 0 {
				for _, v := range oneStoreSkuParam {
					if validSkuMap[v.SkuID] == 1 { // todo 这里是否需要判断
						storeSku := &v.ActStoreSku
						dao.WrapAddIDCULDEntity(storeSku, ctx.GetUserName())
						actStoreSkuList = append(actStoreSkuList, storeSku)
					}
				}
			}
		}
		if len(wrongSkuList) > 0 {
			return nil, nil, nil, nil, jsonerr.New(wrongSkuList, model.ErrCodeJsonActPriceTooLarger)
		}
	}
	return jxutils.IntMap2List(wholeValidVendorMap), actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err
}

func addActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actStoreSkuList []*model.ActStoreSku, actStoreSkuMapList []*model.ActStoreSkuMap) (err error) {
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()

	storeSkuMap := make(map[int64]int)
	for _, v := range actStoreSkuList {
		if v.Stock == 0 {
			v.Stock = DefActSkuStock
		}
		err = dao.CreateEntity(db, v)
		if err != nil {
			dao.Rollback(db, txDB)
			return err
		}
		storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v.ID
	}
	for _, v := range actStoreSkuMapList {
		v.BindID = storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)]
	}

	if len(actStoreSkuMapList) > 0 {
		err = dao.CreateMultiEntities(db, actStoreSkuMapList)
		if err != nil {
			dao.Rollback(db, txDB)
			return err
		}
	}
	dao.Commit(db, txDB)
	return err
}

func checkActUpdate(actID int, actMap map[int]*model.Act2) (err error) {
	if len(actMap) == 0 {
		return fmt.Errorf("活动%d不存在或已被取消", actID)
	}
	errList := errlist.New()
	for vendorID, act := range actMap {
		if vendorID == model.VendorIDEBAI && act.CreateType != model.ActCreateTypeAPI {
			errList.AddErr(fmt.Errorf("饿百平台不支持修改或取消网页活动"))
		}
	}
	return errList.GetErrListAsOne()
}

func AddActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSku []*ActStoreSkuParam) (err error) {
	actMap, err := dao.GetActVendorInfo(db, actID, nil)
	if err != nil {
		return err
	}
	if err = checkActUpdate(actID, actMap); err != nil {
		return err
	}

	vendorIDs := partner.GetVendorIDsFromActMap(actMap)

	var act *model.Act
	if len(vendorIDs) > 0 {
		act = &actMap[vendorIDs[0]].Act
	} else {
		act = &model.Act{}
		act.ID = actID
		if err = dao.GetEntity(db, act); err != nil {
			return err
		}
	}
	if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 {
		return fmt.Errorf("当前活动状态:%s不能进行此操作，或已过期", model.ActStatusName[act.Status])
	}

	// TODO conflictActStoreSku的处理
	_, actStoreSkuList, actStoreSkuMapList, _, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
	if err != nil {
		return err
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
		return err
	}
	if act.Type != model.ActSkuFake {
		for _, act := range actMap {
			if act.VendorID != model.VendorIDJX {
				if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act),
					map[string]interface{}{
						model.FieldSyncStatus: act.SyncStatus | model.SyncFlagModifiedMask,
					}, ctx.GetUserName(), nil); err != nil {
					return err
				}
			}
		}
	}
	dao.Commit(db, txDB)
	return err
}

func getActRule(vendorID, actType int) (actRule *tActRuleInfo, err error) {
	if actRuleMap[vendorID] != nil {
		actRule = actRuleMap[vendorID][actType]
	}
	if actRule == nil {
		err = fmt.Errorf("%s不支持%s活动", model.VendorChineseNames[vendorID], model.ActTypeName[actType])
	}
	return actRule, err
}

func checkDiscountValidation(vendorIDs []int, actType int, pricePercentage float64) (err error) {
	pricePercentageMin := int(math.Floor(pricePercentage))
	pricePercentageMax := int(math.Ceil(pricePercentage))
	errList := errlist.New()
	for _, vendorID := range vendorIDs {
		actRule, err2 := getActRule(vendorID, actType)
		if err2 == nil {
			if pricePercentageMin < actRule.MinDiscount {
				errList.AddErr(fmt.Errorf("%s%s活动折扣必须大于:%d", model.VendorChineseNames[vendorID], model.ActTypeName[actType], actRule.MinDiscount))
			} else if pricePercentageMax > actRule.MaxDiscount {
				errList.AddErr(fmt.Errorf("%s%s活动折扣必须小于:%d", model.VendorChineseNames[vendorID], model.ActTypeName[actType], actRule.MaxDiscount))
			}
		} else {
			errList.AddErr(err2)
		}
	}
	return errList.GetErrListAsOne()
}

func checkActValidation(act *model.Act, vendorIDs []int) (err error) {
	errList := errlist.New()
	if utils.IsTimeZero(act.BeginAt) || utils.IsTimeZero(act.EndAt) {
		errList.AddErr(fmt.Errorf("活动开始与结束时间必须指定"))
	} else if act.EndAt.Sub(act.BeginAt) < 0 {
		errList.AddErr(fmt.Errorf("活动开始时间必须小于活动结束时间"))
	}
	// vendorIDMap := make(map[int]int)
	// for _, vendorID := range vendorIDs {
	// 	vendorIDMap[vendorID] = 1
	// }

	if act.Type == model.ActSkuDirectDown || act.Type == model.ActSkuSecKill {
		if act.PricePercentage == 0 {
			errList.AddErr(fmt.Errorf("必须指定缺省活动折扣"))
		} else if err = checkDiscountValidation(vendorIDs, act.Type, float64(act.PricePercentage)); err != nil {
			errList.AddErr(err)
		}
	} else if act.Type == model.ActSkuFake {

	} else if act.Type == model.ActSkuDiscount {
		if act.DiscountType == 0 {
			errList.AddErr(fmt.Errorf("折扣活动必须选择折扣类型"))
		}
		if act.DiscountValue1 == 0 || act.DiscountValue2 == 0 {
			errList.AddErr(fmt.Errorf("折扣活动必须填入两档"))
		}
	} else {
		errList.AddErr(fmt.Errorf("当前只支持%s与%s活动", model.ActTypeName[model.ActSkuDirectDown], model.ActTypeName[model.ActSkuSecKill]))
	}
	err = errList.GetErrListAsOne()
	return err
}

func setActDefault(act *model.Act) {
	if act.LimitCount == 0 {
		act.LimitCount = 1 // 缺省限购一份，如果确定不限，明确给一个很大的值
	}
	if act.LimitUser == 0 {
		act.LimitUser = 1
	}
	act.Status = model.ActStatusCreated
}

func PreCreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam) (preCreateActInfo *tPreCreateActInfo, err error) {
	if err = checkActValidation(act, vendorIDs); err != nil {
		return nil, err
	}
	setActDefault(act)

	db := dao.GetDB()
	validVendorIDs, actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
	if err != nil {
		return nil, err
	}
	if len(validVendorIDs) == 0 {
		return nil, fmt.Errorf("没有一个合法平台可以创建活动")
	}
	if act.OverlapRule == model.OverlapRuleNormal && len(conflictActStoreSku) > 0 {
		return nil, jsonerr.New(conflictActStoreSku, model.ErrCodeJsonActSkuConflict)
	}

	preCreateActInfo = &tPreCreateActInfo{
		ValidVendorIDs: validVendorIDs,
	}
	storeSkuMap := make(map[int64]*tPreCreateActStoreSku)
	for _, v := range actStoreSkuList {
		tmp := &tPreCreateActStoreSku{
			ActStoreSku: *v,
		}
		storeSkuMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = tmp
		preCreateActInfo.ActStoreSku = append(preCreateActInfo.ActStoreSku, tmp)
	}

	for _, v := range actStoreSkuMapList {
		index := jxutils.Combine2Int(v.StoreID, v.SkuID)
		storeSkuMap[index].VendorInfoList = append(storeSkuMap[index].VendorInfoList, &tPreCreateActVendorInfo{
			VendorID:       v.VendorID,
			VendorPrice:    v.VendorPrice,
			ActualActPrice: v.ActualActPrice,
		})
	}
	return preCreateActInfo, nil
}

func CreateAct(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, vendorOrgCode string, actRules []*ActOrderRuleParam, actStoreSku []*ActStoreSkuParam, isAsync bool) (hint string, err error) {
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			var actStoreSku2 []*ActStoreSkuParam
			for _, v := range actStoreSku {
				if storeIDsMap[v.StoreID] != 0 {
					actStoreSku2 = append(actStoreSku2, v)
				}
			}
			actStoreSku = nil
			actStoreSku = actStoreSku2
		}
	}
	if err = checkActValidation(act, vendorIDs); err != nil {
		return "", err
	}
	act.VendorMask = model.GetVendorMask(vendorIDs...)
	if act.VendorMask&(^model.GetVendorMask(model.VendorIDJX)) != 0 &&
		vendorOrgCode == "" {
		return "", fmt.Errorf("必须指定平台分账号信息")
	}
	setActDefault(act)

	db := dao.GetDB()
	// TODO
	// 事务应该从ActStoreSkuParam2Model之后开始
	// 创建Act也应该往后放
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
	err = dao.CreateEntityTx(txDB, act)
	if err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}

	validVendorIDs, actStoreSkuList, actStoreSkuMapList, conflictActStoreSku, err := ActStoreSkuParam2Model(ctx, db, act, vendorIDs, actStoreSku)
	if err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}
	if len(validVendorIDs) == 0 {
		dao.Rollback(db, txDB)
		return "", fmt.Errorf("没有一个合法平台可以创建活动")
	}
	var neeSyncActIDs []int
	if len(conflictActStoreSku) > 0 {
		if act.OverlapRule == model.OverlapRuleNormal {
			dao.Rollback(db, txDB)
			return "", jsonerr.New(conflictActStoreSku, model.ErrCodeJsonActSkuConflict)
		}
		if neeSyncActIDs, err = DeleteActStoreSkuList(ctx, db, conflictActStoreSku); err != nil {
			dao.Rollback(db, txDB)
			return "", err
		}
	}

	var actMapList []*model.ActMap
	for _, vendorID := range validVendorIDs {
		actMap := &model.ActMap{
			ActID:         act.ID,
			VendorID:      vendorID,
			VendorOrgCode: vendorOrgCode,
		}
		if !(vendorID == model.VendorIDJX || act.Type == model.ActSkuFake) {
			actMap.SyncStatus = model.SyncFlagNewMask
		}
		dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
		actMapList = append(actMapList, actMap)
	}
	err = dao.CreateMultiEntities(db, actMapList)
	if err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}

	if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}
	dao.Commit(db, txDB)

	neeSyncActIDs = append(neeSyncActIDs, act.ID)
	task := tasksch.NewParallelTask(fmt.Sprintf("处理活动创建%d", act.ID), tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			actID := batchItemList[0].(int)
			_, err = SyncAct(ctx, task, actID, nil, false)
			return retVal, err
		}, neeSyncActIDs)

	tasksch.HandleTask(task, nil, true).Run()
	if !isAsync {
		hint = utils.Int2Str(act.ID)
	} else {
		_, err = task.GetResult(0)
		hint = utils.Int2Str(len(neeSyncActIDs))
	}
	return hint, err
}

func CreateActForMtByOrder(ctx *jxcontext.Context, act *model.Act, vendorIDs []int, vendorOrgCode string, actStoreSku []*ActStoreSkuParam) (err error) {
	var (
		db             = dao.GetDB()
		actStoreSkuMap = make(map[int][]*model.StoreSkuBind)
		err2           = ""
		vendorID       int
		actType        int = 0
	)
	if len(vendorIDs) > 1 {
		return fmt.Errorf("只允许单平台创建！")
	}
	vendorID = vendorIDs[0]
	if vendorID != model.VendorIDMTWM {
		return fmt.Errorf("此接口只支持美团使用！")
	}
	if act.Type != model.ActSkuDirectDown {
		//return fmt.Errorf("此接口只支持创建直降！")
		actType = mtwmapi.RetailActTypeSecKill
	} else {
		actType = mtwmapi.RetailActTypeDirectDown
	}
	setActDefault(act)
	//处理actStoreSku
	//参数有可能传多个店多个商品，先分好组：map = [门店ID1][]{商品ID1,商品ID2},[门店ID2][]{商品ID1,商品ID3,商品ID4}
	for _, v := range actStoreSku {
		if resultList, _ := dao.GetStoresSkusInfo(db, []int{v.StoreID}, []int{v.SkuID}); len(resultList) > 0 {
			var actPrice int64
			if v.ActPrice != 0 {
				actPrice = v.ActPrice
			} else {
				actPrice = int64(math.Floor(float64(resultList[0].MtwmPrice*act.PricePercentage/100)/10) * 10)
			}
			actStoreSkuMap[v.StoreID] = append(actStoreSkuMap[v.StoreID], &model.StoreSkuBind{
				SkuID:     v.SkuID,
				MtwmPrice: resultList[0].MtwmPrice,
				Price:     resultList[0].Price,
				JdsPrice:  int(actPrice), //暂时把这个jdsprice当成实际活动价
			})
		}
	}
	//根据平台价排序
	//for _, storeSkus := range actStoreSkuMap {
	//	for i := 0; i < len(storeSkus)-1; i++ {
	//		for j := 0; j < len(storeSkus)-i-1; j++ {
	//			if storeSkus[j].JdsPrice < storeSkus[j+1].JdsPrice {
	//				temp := storeSkus[j]
	//				storeSkus[j] = storeSkus[j+1]
	//				storeSkus[j+1] = temp
	//			}
	//		}
	//	}
	//}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
	err = dao.CreateEntityTx(txDB, act)
	if err != nil {
		dao.Rollback(db, txDB)
		return err
	}
	actMap := &model.ActMap{
		ActID:         act.ID,
		VendorID:      vendorID,
		VendorOrgCode: vendorOrgCode,
	}
	dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
	err = dao.CreateEntityTx(txDB, actMap)
	if err != nil {
		dao.Rollback(db, txDB)
		return err
	}
	for storeID, storeSkus := range actStoreSkuMap {
		for _, storeSku := range storeSkus {
			actStoreSkuModel := &model.ActStoreSku{
				ActID:         act.ID,
				OriginalPrice: int64(storeSku.Price),
				StoreID:       storeID,
				SkuID:         storeSku.SkuID,
				Stock:         200,
				ActPrice:      int64(storeSku.JdsPrice),
			}
			dao.WrapAddIDCULDEntity(actStoreSkuModel, ctx.GetUserName())
			err = dao.CreateEntityTx(txDB, actStoreSkuModel)
			if err != nil {
				dao.Rollback(db, txDB)
				return err
			}
			actStoreSkuMapModel := &model.ActStoreSkuMap{
				ActID:          act.ID,
				BindID:         actStoreSkuModel.ID,
				StoreID:        storeID,
				SkuID:          storeSku.SkuID,
				VendorID:       vendorID,
				VendorPrice:    int64(storeSku.MtwmPrice),
				ActualActPrice: int64(storeSku.JdsPrice),
			}
			dao.WrapAddIDCULDEntity(actStoreSkuMapModel, ctx.GetUserName())
			err = dao.CreateEntityTx(txDB, actStoreSkuMapModel)
			if err != nil {
				dao.Rollback(db, txDB)
				return err
			}
		}
	}
	dao.Commit(db, txDB)
	if !globals.IsProductEnv() {
		return err
	}
	//同步建到美团
	for storeID, storeSkus := range actStoreSkuMap {
		if storeDetail, _ := dao.GetStoreDetail(db, storeID, vendorID, vendorOrgCode); storeDetail != nil {
			var actData []*mtwmapi.RetailDiscountActData
			for _, v := range storeSkus {
				actData = append(actData, &mtwmapi.RetailDiscountActData{
					AppFoodCode: utils.Int2Str(v.SkuID),
					StartTime:   act.BeginAt.Unix(),
					EndTime:     act.EndAt.Unix(),
					OrderLimit:  -1,
					DayLimit:    -1,
					SettingType: mtwmapi.SettingTypeAsPrice,
					//DiscountCoefficient: float64(act.PricePercentage) / float64(10),
					ActPrice: jxutils.IntPrice2Standard(int64(v.JdsPrice)),
				})
			}
			actIdResult := make(map[string]int64, 0)
			mtApi := mtwm.GetAPI(vendorOrgCode, storeID, storeDetail.VendorStoreID)
			// 创建活动
			actResult, fileInfoList, _ := mtApi.RetailDiscountBatchSave2(storeDetail.VendorStoreID, actType, actData)
			for _, v := range actResult {
				var (
					actStoreSkuMapUpdate *model.ActStoreSkuMap
				)
				sql := `
					SELECT * FROM act_store_sku_map WHERE act_id = ? AND store_id = ? AND sku_id = ?
				`
				sqlParams := []interface{}{act.ID, storeID, utils.Str2Int(v.AppFoodCode)}
				if err = dao.GetRow(db, &actStoreSkuMapUpdate, sql, sqlParams); err == nil && actStoreSkuMapUpdate != nil {
					actStoreSkuMapUpdate.VendorActID = utils.Int64ToStr(v.ActID)
					dao.UpdateEntity(db, actStoreSkuMapUpdate, "VendorActID")
				}
				actIdResult[v.AppFoodCode] = v.ActID
			}
			// RetailDiscountBatchSave2 创建时无法使用排序,只能更时排序生效所以这样写
			// RetailDiscountBatchSave2 创建时无法使用排序,只能更时排序生效所以这样写,美团恢复可以删除
			actData2 := make([]*mtwmapi.RetailDiscountActData, 0, 0)
			if len(actIdResult) != model.NO {
				for _, v := range actData {
					if actIdResult[v.AppFoodCode] != model.NO {
						v.ItemID = actIdResult[v.AppFoodCode]
					}
					actData2 = append(actData2, v)
				}
				sort.Slice(actData2, func(i, j int) bool {
					return actData2[i].ActPrice < actData2[j].ActPrice
				})
				for i := 1; i <= len(actData2); i++ {
					actData2[i-1].Sequence = i
					actData2[i-1].OrderLimit = 1
				}
				mtApi.RetailDiscountBatchSave2(storeDetail.VendorStoreID, actType, actData2)
			}
			for _, v := range fileInfoList {
				err2 += fmt.Sprintf("创建失败！门店ID：[%v]，商品ID：[%v]，原因：[%v]", storeID, v.AppFoodCode, v.ErrorMsg)
			}
		}
	}
	if err2 != "" {
		return fmt.Errorf(err2)
	}
	return 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
	}
	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
	}
	storeSkuMap2 := make(map[int64]int)
	for _, v := range actStoreSku {
		if storeSkuInfo := storeSkuMap[v.VendorStoreID+"/"+v.VendorSkuID]; storeSkuInfo != nil {
			index := jxutils.Combine2Int(storeSkuInfo.StoreID, storeSkuInfo.SkuID)
			if storeSkuMap2[index] == 0 {
				storeSkuMap2[index] = 1
				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) {
	db := dao.GetDB()
	return createActFromVendor(ctx, db, act2, actStoreSku)
}

func createActFromVendor(ctx *jxcontext.Context, db *dao.DaoDB, act2 *model.Act2, actStoreSku []*model.ActStoreSku2) (actID int, err error) {
	actMap := &model.ActMap{
		VendorID:      act2.VendorID,
		VendorOrgCode: act2.VendorOrgCode,
		VendorActID:   act2.VendorActID,
		SyncStatus:    0,
	}
	dao.WrapAddIDCULDEntity(actMap, ctx.GetUserName())
	if actMap.VendorActID != "" {
		if err = dao.GetEntity(db, actMap, model.FieldVendorActID, model.FieldVendorID, model.FieldDeletedAt); err == nil {
			return actMap.ActID, nil
		} else if !dao.IsNoRowsError(err) {
			return 0, err
		}
		err = nil
	}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	act := &act2.Act
	act.VendorMask = model.GetVendorMask(act2.VendorID)
	dao.WrapAddIDCULDEntity(act, ctx.GetUserName())
	err = dao.CreateEntity(db, act)
	if err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}

	actMap.ActID = act.ID
	err = dao.CreateEntity(db, actMap)
	if err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}
	actStoreSkuList, actStoreSkuMapList, err := vendorActInfo2Model(ctx, db, act2, actStoreSku)
	if err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}
	if err = addActStoreSkuBind(ctx, db, actStoreSkuList, actStoreSkuMapList); err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}
	dao.Commit(db, txDB)

	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.DefaultTimeValue
	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, syncStatus int, keyword string, vendorID int, statusList, actTypeList, createTypeList []int, storeID, skuID, cityCode int, beginAt, endAt, createdAtFrom, createdAtTo time.Time) (pagedInfo *dao.PagedActListInfo, err error) {
	var skuIDs []int
	if skuID > 0 {
		skuIDs = []int{skuID}
	}
	return dao.QueryActs(dao.GetDB(), actID, offset, pageSize, syncStatus, keyword, vendorID, statusList, actTypeList, createTypeList, storeID, skuIDs, cityCode, beginAt, endAt, createdAtFrom, createdAtTo)
}

func GetActStoreSkuInfo(ctx *jxcontext.Context, actID int, vendorIDs []int, keyword string, offset, pageSize int) (retVal interface{}, err error) {
	db := dao.GetDB()
	totalCount, actStoreSkuList, err := dao.GetActStoreSkuVendorList(db, actID, vendorIDs, nil, nil, keyword, offset, pageSize)
	if err != nil {
		return nil, err
	}
	for _, v := range actStoreSkuList {
		v.SkuName = jxutils.ComposeSkuName(v.Prefix, v.SkuNameName, v.Comment, v.Unit, v.SpecQuality, v.SpecUnit, 0, v.ExPrefix, v.ExPrefixBegin, v.ExPrefixEnd)
	}
	if pageSize > 0 && pageSize != model.UnlimitedPageSize {
		pagedInfo := &model.PagedInfo{
			TotalCount: totalCount,
			Data:       actStoreSkuList,
		}
		retVal = pagedInfo
	} else {
		retVal = actStoreSkuList
	}
	return retVal, err
}

func CancelAct(ctx *jxcontext.Context, actID int) (err error) {
	db := dao.GetDB()
	if _, err = DeleteActStoreSkuBind(ctx, db, actID, nil); err != nil {
		return err
	}
	_, err = SyncAct(ctx, nil, actID, nil, false)
	act := &model.Act{}
	act.ID = actID
	if err == nil {
		if _, err = dao.UpdateEntityLogically(db, act,
			map[string]interface{}{
				model.FieldStatus: model.ActStatusCanceled,
			}, ctx.GetUserName(), nil); err != nil {
			return err
		}
	}
	return err
}

// actStoreSkuParam为空，不会删除act_store_sku，但会删除act_store_sku_map
func DeleteActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSkuParam []*ActStoreSkuParam) (originSyncStatus int8, err error) {
	actMap, err := dao.GetActVendorInfo(db, actID, nil)
	if err != nil {
		return 0, err
	}
	if err = checkActUpdate(actID, actMap); err != nil {
		return 0, err
	}

	//if len(actMap) == 0 {
	//
	//	return 0, fmt.Errorf("活动%d不存在或已被取消", actID)
	//}
	//errList := errlist.New()
	//for vendorID, act := range actMap {
	//	if vendorID == model.VendorIDEBAI && act.CreateType != model.ActCreateTypeAPI {
	//		errList.AddErr(fmt.Errorf("饿百平台不支持修改或取消网页活动"))
	//	}
	//}
	//if errList.GetErrListAsOne() != nil {
	//	return 0, errList.GetErrListAsOne()
	//}

	actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil)
	if err != nil {
		return 0, err
	}
	act := actMap[partner.GetVendorIDsFromActMap(actMap)[0]]
	if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 {
		return 0, fmt.Errorf("当前活动状态:%s，不能进行此操作，或已过期", model.ActStatusName[act.Status])
	}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	actStoreSkuParamMap := make(map[int64]*ActStoreSkuParam)
	for _, v := range actStoreSkuParam {
		actStoreSkuParamMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v
		if _, err = dao.DeleteEntityLogically(db, &model.ActStoreSku{}, nil, ctx.GetUserName(),
			map[string]interface{}{
				model.FieldActID:   actID,
				model.FieldStoreID: v.StoreID,
				model.FieldSkuID:   v.SkuID,
			}); err != nil {
			return 0, err
		}
	}
	isNeedCancelAct := true
	for vendorID, act := range actMap {
		originSyncStatus |= act.SyncStatus
		isDeleteAll := true
		isDeleteAtLeastOne := false
		if true { //actStoreSkuParam != nil {
			actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuMap[vendorID])
			for storeID := range actStoreSkuMap {
				for _, actStoreSku := range actStoreSkuMap[storeID] {
					if actStoreSkuParam == nil || actStoreSkuParamMap[jxutils.Combine2Int(actStoreSku.StoreID, actStoreSku.SkuID)] != nil {
						if act.Type == model.ActSkuFake {
							_, err = dao.DeleteEntityLogically(db, &model.ActStoreSkuMap{}, nil, ctx.GetUserName(),
								map[string]interface{}{
									model.FieldActID:   actID,
									model.FieldStoreID: actStoreSku.StoreID,
									model.FieldSkuID:   actStoreSku.SkuID,
								})
						} else {
							_, err = dao.UpdateEntityLogically(db, partner.ActStoreSku2ActStoreSkuMap(actStoreSku),
								map[string]interface{}{
									model.FieldSyncStatus: actStoreSku.SyncStatus | model.SyncFlagDeletedMask,
								}, ctx.GetUserName(), nil)
						}
						if err != nil {
							return 0, err
						}
						isDeleteAtLeastOne = true
					} else {
						isNeedCancelAct = false
						isDeleteAll = false
					}
				}
			}
		} else {
			isDeleteAll = true
			isDeleteAtLeastOne = true
		}
		if isDeleteAll || isDeleteAtLeastOne {
			syncStatus := int8(model.SyncFlagModifiedMask)
			if isDeleteAll {
				syncStatus = model.SyncFlagDeletedMask
			}
			syncStatus |= act.SyncStatus
			if act.Type != model.ActSkuFake && vendorID != model.VendorIDJX {
				if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act),
					map[string]interface{}{
						model.FieldSyncStatus: syncStatus,
					}, ctx.GetUserName(), nil); err != nil {
					return 0, err
				}
			}
		}
		if isDeleteAll != isNeedCancelAct {
			globals.SugarLogger.Warnf("deleteActStoreBind, actID:%d isDeleteAll:%t != isNeedCancelAct:%t", act.ID, isDeleteAll, isNeedCancelAct)
		}
	}

	if isNeedCancelAct {
		// act := &model.Act{}
		// act.ID = actID
		// if err == nil {
		// 	if _, err = dao.UpdateEntityLogically(db, act,
		// 		map[string]interface{}{
		// 			model.FieldStatus: model.ActStatusCanceled,
		// 		}, ctx.GetUserName(), nil); err != nil {
		// 		return 0, err
		// 	}
		// }
	}
	dao.Commit(db, txDB)
	return originSyncStatus, err
}

func DeleteActStoreSkuList(ctx *jxcontext.Context, db *dao.DaoDB, actStoreSkuList []*model.ActStoreSku2) (actIDs []int, err error) {
	if globals.IsStoreSkuAct {
		actStoreSkuParamMap := make(map[int][]*ActStoreSkuParam)
		for _, v := range actStoreSkuList {
			param := &ActStoreSkuParam{
				ActStoreSku: model.ActStoreSku{
					ActID:   v.ActID,
					StoreID: v.StoreID,
					SkuID:   v.SkuID,
				},
			}
			actStoreSkuParamMap[v.ActID] = append(actStoreSkuParamMap[v.ActID], param)
		}
		for actID := range actStoreSkuParamMap {
			if _, err = DeleteActStoreSkuBind(ctx, db, actID, actStoreSkuParamMap[actID]); err != nil {
				return nil, err
			}
		}

		for actID := range actStoreSkuParamMap {
			actIDs = append(actIDs, actID)
		}
	}
	return actIDs, nil
}

// todo 当前逻辑要求传入活动的全部SKU信息（以便低层做一些判断，比如全部删除时要取消，所以暂时删除storeIDs与skuIDs这两个参数
func SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, actID int, vendorIDs /*, storeIDs, skuIDs */ []int, isAsync bool) (hint string, err error) {
	db := dao.GetDB()
	actMap, err := dao.GetActVendorInfo(db, actID, vendorIDs)
	if err != nil {
		return "", err
	}
	actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil)
	if err != nil {
		return "", err
	}
	if vendorIDs == nil {
		vendorIDs = partner.GetVendorIDsFromActMap(actMap)
	}
	if len(vendorIDs) == 0 || vendorIDs[0] == model.VendorIDJX || actMap[vendorIDs[0]].Type == model.ActSkuFake {
		return "", nil
	}
	task := tasksch.NewParallelTask("SyncAct", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			vendorID := batchItemList[0].(int)
			if handler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformActHandler); handler != nil {
				tmpActMap := &model.ActMap{}
				tmpActMap.ID = actMap[vendorID].MapID
				if !globals.IsStoreSkuAct || actMap[vendorID].IsSpecial == 0 {
					if err = handler.SyncAct(ctx, task, actMap[vendorID], nil, actStoreSkuMap[vendorID]); err == nil {
						retVal = []int{1}
					} else {
						tmpActMap.Remark = utils.LimitUTF8StringLen(err.Error(), 1024)
					}
					// 保存最后一次同步错误信息
					dao.UpdateEntity(db, tmpActMap, "Remark")
				} else {
					err = SyncSpecialAct(ctx, task, actMap[vendorID], nil, actStoreSkuMap[vendorID])
				}
			} else {
				globals.SugarLogger.Warnf("SyncAct strange actID:%d, vendorID:%d", actID, vendorID)
			}
			return retVal, err
		}, vendorIDs)
	tasksch.HandleTask(task, parentTask, true).Run()
	if !isAsync {
		result, err2 := task.GetResult(0)
		if err = err2; err == nil {
			hint = utils.Int2Str(len(result))
		}
	} else {
		hint = task.GetID()
	}
	return hint, err
}

func RefreshPageActs(ctx *jxcontext.Context, vendorIDs []int, createdFrom time.Time, isAsync bool) (hint string, err error) {
	task := tasksch.NewParallelTask("RefreshPageActs", nil, ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			vendorID := batchItemList[0].(int)
			if handler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformPageActHandler); handler != nil {
				db := dao.GetDB()
				actList, err2 := handler.GetPageActList(ctx, createdFrom)
				if err = err2; err != nil {
					return nil, err
				}
				vendorActIDs, err2 := dao.GetExistVendorActIDs(db, vendorID)
				if err = err2; err != nil {
					return nil, err
				}
				localActMap := jxutils.StringList2Map(vendorActIDs)
				var needAddActList []*model.Act2
				for _, v := range actList {
					localAct := localActMap[v.VendorActID]
					if localAct == 0 {
						if v.Status == model.ActStatusCreated {
							needAddActList = append(needAddActList, v)
						}
					}
					// else if v.Status != localAct.Status {
					// 	localAct.Status = v.Status
					// 	needUpdateActList = append(needUpdateActList, &localAct.Act)
					// }
				}
				subTask := tasksch.NewParallelTask("RefreshPageAct Sub", nil, ctx,
					func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
						act2 := batchItemList[0].(*model.Act2)
						actStoreSkuList, err := handler.GetPageActSkuList(ctx, act2.VendorActID)
						if err == nil {
							retVal = actStoreSkuList
						}
						return retVal, err
					}, needAddActList)
				tasksch.AddChild(task, subTask).Run()

				skuList, err2 := subTask.GetResult(0)
				if err = err2; err != nil {
					return nil, err
				}
				needAddActSkuMap := make(map[string][]*model.ActStoreSku2)
				for _, v := range skuList {
					actStoreSku := v.(*model.ActStoreSku2)
					needAddActSkuMap[actStoreSku.VendorActID] = append(needAddActSkuMap[actStoreSku.VendorActID], actStoreSku)
				}

				txDB, _ := dao.Begin(db)
				defer func() {
					if r := recover(); r != nil || err != nil {
						dao.Rollback(db, txDB)
						if r != nil {
							panic(r)
						}
					}
				}()

				for _, v := range needAddActList {
					if len(needAddActSkuMap[v.VendorActID]) > 0 {
						if _, err = createActFromVendor(ctx, db, v, needAddActSkuMap[v.VendorActID]); err != nil {
							return nil, err
						}
					}
				}
				// for _, v := range needUpdateActList {
				// 	if _, err = dao.UpdateEntity(db, v, model.FieldStatus); err != nil {
				// 		return nil, err
				// 	}
				// }
				dao.Commit(db, txDB)
			}
			return retVal, err
		}, vendorIDs)
	tasksch.HandleTask(task, nil, true).Run()
	if !isAsync {
		hint = "1"
		_, err = task.GetResult(0)
	} else {
		hint = task.GetID()
	}
	return hint, err
}

func DeleteStoresFromAct(ctx *jxcontext.Context, vendorID int, actTypes, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	task := tasksch.NewParallelTask("将门店从所有活动中删除", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeID := batchItemList[0].(int)
			acts, _ := dao.QueryActs(db, 0, 0, 50, -1, "", vendorID, []int{1}, actTypes, nil, storeID, nil, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -3, 0), time.Now())
			for _, v := range acts.Data {
				var actStoreSkuParam []*ActStoreSkuParam
				_, actStoreSkus, _ := dao.GetActStoreSkuVendorList(db, v.ID, []int{vendorID}, nil, nil, "", 0, 99999)
				for _, actStoreSku := range actStoreSkus {
					if actStoreSku.StoreID == storeID {
						aa := &ActStoreSkuParam{
							ActStoreSku: model.ActStoreSku{
								StoreID: storeID,
								SkuID:   actStoreSku.SkuID,
								ActID:   v.ID,
							},
						}
						actStoreSkuParam = append(actStoreSkuParam, aa)
					}
				}
				_, err = DeleteActStoreSkuBind(ctx, db, v.ID, actStoreSkuParam)
				if err == nil {
					_, err = SyncAct(ctx, nil, v.ID, nil, true)
				}
			}
			return retVal, err
		}, storeIDs)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		resultList, err2 := task.GetResult(0)
		if err = err2; err == nil {
			hint = utils.Int2Str(len(resultList))
		}
	}
	return hint, err
}

func DeleteSkusFromAct(ctx *jxcontext.Context, vendorID int, actTypes, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	actMap := make(map[int]*model.Act)
	pagedInfo, err2 := dao.QueryActs(db, 0, 0, -1, -1, "", vendorID, []int{model.ActStatusCreated}, actTypes, nil, 0, skuIDs, 0,
		utils.DefaultTimeValue, utils.DefaultTimeValue, time.Now().Add(-24*30*3*time.Hour), utils.DefaultTimeValue)
	if err = err2; err != nil {
		return "", err
	}
	for _, v := range pagedInfo.Data {
		actMap[v.Act.ID] = &v.Act
	}

	if len(actMap) == 0 {
		return "", nil
	}
	skuIDMap := jxutils.IntList2Map(skuIDs)
	var actIDList []int
	for k := range actMap {
		actIDList = append(actIDList, k)
	}
	task := tasksch.NewParallelTask("将SKU从所有活动中删除", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			actID := batchItemList[0].(int)
			_, actStoreSkuList, err := dao.GetActStoreSkuVendorList(db, actID, []int{-1}, nil, skuIDs, "", 0, -1)
			if err == nil {
				// db := dao.GetDB()
				var deleteList []*ActStoreSkuParam
				for _, v := range actStoreSkuList {
					if skuIDMap[v.SkuID] == 1 {
						deleteList = append(deleteList, &ActStoreSkuParam{
							ActStoreSku: model.ActStoreSku{
								StoreID: v.StoreID,
								SkuID:   v.SkuID,
							},
						})
					}
				}
				if len(deleteList) > 0 {
					originSyncStatus, err2 := DeleteActStoreSkuBind(ctx, db, actID, deleteList)
					if err = err2; err == nil && originSyncStatus == 0 {
						if _, err = SyncAct(ctx, task, actID, nil, false); err == nil {
							retVal = deleteList
						}
					}
				}
			}
			return retVal, err
		}, actIDList)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		resultList, err2 := task.GetResult(0)
		if err = err2; err == nil {
			hint = utils.Int2Str(len(resultList))
		}
	}
	return hint, err
}

func ForceUpdateVendorPrice(ctx *jxcontext.Context, vendorID int, actType int, storeSkuList []*ActStoreSkuParam, lockTime time.Time, isAsync bool) (hint string, err error) {
	var wrongSkuList []*ActStoreSkuParam
	var storeSkuBindList []*model.StoreSkuBind

	actRule, _ := getActRule(vendorID, actType)
	db := dao.GetDB()
	errList := errlist.New()
	for _, v := range storeSkuList {
		storeSkuBind := &model.StoreSkuBind{
			StoreID: v.StoreID,
			SkuID:   v.SkuID,
		}
		storeSkuBind.DeletedAt = utils.DefaultTimeValue
		if err = dao.GetEntity(db, storeSkuBind, model.FieldStoreID, model.FieldSkuID, model.FieldDeletedAt); err == nil {
			vendorPrice := int(v.VendorPrice)
			if vendorPrice != 0 {
				if err2 := checkDiscountValidation([]int{vendorID}, actType, float64(v.ActPrice)*100/float64(v.VendorPrice)); err2 != nil {
					v.ErrMsg = err2.Error()
					v.ActualActPrice = v.ActPrice
					wrongSkuList = append(wrongSkuList, v)
					storeSkuBind = nil
				}
			} else {
				vendorPrice = dao.GetStoreSkuBindVendorPrice(storeSkuBind, vendorID)
				if checkDiscountValidation([]int{vendorID}, actType, float64(v.ActPrice)*100/float64(vendorPrice)) != nil {
					vendorPrice = int(v.ActPrice)*100/actRule.MaxDiscount + 10
				} else {
					storeSkuBind = nil
				}
			}
			if storeSkuBind != nil {
				dao.SetStoreSkuBindVendorPrice(storeSkuBind, vendorID, vendorPrice, lockTime)
				if vendorID != model.VendorIDJX {
					dao.SetStoreSkuBindSyncStatus(storeSkuBind, vendorID, dao.GetStoreSkuBindSyncStatus(storeSkuBind, vendorID)|model.SyncFlagPriceMask)
				}
				storeSkuBind.LastOperator = ctx.GetUserName()
				storeSkuBindList = append(storeSkuBindList, storeSkuBind)
			}
		} else if dao.IsNoRowsError(err) { // 忽略不存在错
			err = nil
		} else {
			errList.AddErr(fmt.Errorf("获取门店:%d商品:%d出错:%s", v.StoreID, v.SkuID, err))
		}
	}
	if err = errList.GetErrListAsOne(); err != nil {
		return "", err
	}
	if len(wrongSkuList) > 0 {
		return "", jsonerr.New(wrongSkuList, model.ErrCodeJsonActPriceTooLarger)
	}

	storeVendorIDMap := make(map[int]string)
	storeSkuIDMap := make(map[int][]int)

	task := tasksch.NewSeqTask2(fmt.Sprintf("强制刷新门店商品%s平台价", model.VendorChineseNames[vendorID]), ctx, false,
		func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
			switch step {
			case 0:
				errList := errlist.New()
				for _, storeSkuBind := range storeSkuBindList {
					if _, err2 := dao.UpdateEntity(db, storeSkuBind); err2 == nil {
						if storeVendorIDMap[storeSkuBind.StoreID] == "" {
							if storeDetail, err2 := dao.GetStoreDetail(db, storeSkuBind.StoreID, vendorID, ""); err2 == nil {
								storeVendorIDMap[storeSkuBind.StoreID] = storeDetail.VendorStoreID
							}
						}
						storeSkuIDMap[storeSkuBind.StoreID] = append(storeSkuIDMap[storeSkuBind.StoreID], storeSkuBind.SkuID)
						errList.AddErr(err2)
					}
				}
				err = errList.GetErrListAsOne()
			case 1:
				if vendorID != model.VendorIDJX && len(storeVendorIDMap) > 0 {
					var storeIDs []int
					for storeID := range storeVendorIDMap {
						storeIDs = append(storeIDs, storeID)
					}
					subTask := tasksch.NewParallelTask("同步平台价格", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
						func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
							storeID := batchItemList[0].(int)
							_, err = cms.SyncStoreSkuNew2(ctx, task, 0 /*model.SyncFlagPriceMask*/, vendorID, storeID, storeVendorIDMap[storeID], "", nil, storeSkuIDMap[storeID], nil, true, false, true)
							return retVal, err
						}, storeIDs)
					tasksch.HandleTask(subTask, task, true).Run()
					_, err = subTask.GetResult(0)
				}
			}
			return result, err
		}, 2)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		_, err2 := task.GetResult(0)
		if err = err2; err == nil {
			hint = utils.Int2Str(1)
		}
	}
	return hint, err
}

func SyncActStoreSku2StoreSkuAct(ctx *jxcontext.Context, db *dao.DaoDB, parentTask tasksch.ITask, act *model.Act2) (err error) {
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()

	// 创建时，会直接覆盖，后续修改时，会判断是否是相同的act_id
	// MySql的SET语句，顺序是有关系的，如果当前SET语句中有判断，且依赖于前一条SET过的字段，是用的是SET后的值的
	sql := `
	UPDATE store_sku_act t1
	JOIN act_store_sku_map t2 ON t2.store_id = t1.store_id AND t2.sku_id = t1.sku_id AND t2.vendor_id = t1.vendor_id AND t2.act_id = ? AND t2.sync_status <> 0
	JOIN act_store_sku t3 ON t3.store_id = t1.store_id AND t3.sku_id = t1.sku_id AND t3.act_id = t2.act_id
	JOIN act_map t4 ON t4.act_id = t2.act_id AND t4.vendor_id = t2.vendor_id
	SET
		t1.sync_status = IF(t1.act_percentage = IF(t2.sync_status & ? = 0 AND t3.deleted_at = ?, t3.price_percentage, 0), 0, ?),
		t1.act_percentage = IF(t2.sync_status & ? = 0 AND t3.deleted_at = ?, t3.price_percentage, 0),
		t1.updated_at = NOW(),
		t1.last_operator = ?,
		t1.hint_act_id = t2.act_id
	WHERE t1.vendor_id = ? AND (t4.sync_status & ? <> 0 OR t1.hint_act_id = t2.act_id)
	`
	sqlParams := []interface{}{
		act.ID,
		model.SyncFlagDeletedMask, utils.DefaultTimeValue, model.SyncFlagModifiedMask,
		model.SyncFlagDeletedMask, utils.DefaultTimeValue,
		ctx.GetUserName(),
		act.VendorID, model.SyncFlagNewMask,
	}
	_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
	if err != nil {
		return err
	}

	sql = `
	INSERT INTO store_sku_act(id, created_at, updated_at, last_operator, store_id, sku_id, vendor_id, act_percentage, sync_status, hint_act_id)
	SELECT 0, NOW(), NOW(), ?, t2.store_id, t2.sku_id, t2.vendor_id, t3.price_percentage, ?, t2.act_id
	FROM act_store_sku_map t2
	JOIN act_store_sku t3 ON t3.store_id = t2.store_id AND t3.sku_id = t2.sku_id AND t3.act_id = t2.act_id
	LEFT JOIN store_sku_act t1 ON t2.store_id = t1.store_id AND t2.sku_id = t1.sku_id AND t2.vendor_id = t1.vendor_id
	WHERE t2.vendor_id = ? AND t2.act_id = ? AND t2.sync_status <> 0 AND t1.id IS NULL
	`
	sqlParams = []interface{}{
		ctx.GetUserName(), model.SyncFlagModifiedMask,
		act.VendorID, act.ID,
	}
	_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
	if err != nil {
		return err
	}

	sql = `
	UPDATE act_store_sku_map t2
	SET
		t2.deleted_at = IF(t2.sync_status & ? = 0, t2.deleted_at, NOW()),
		t2.sync_status = 0
	WHERE t2.vendor_id = ? AND t2.act_id = ?
	`
	sqlParams = []interface{}{
		model.SyncFlagDeletedMask,
		act.VendorID, act.ID,
	}
	_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
	if err != nil {
		return err
	}

	sql = `
	UPDATE act_map t4
	SET
		t4.sync_status = 0
	WHERE t4.vendor_id = ? AND t4.act_id = ?
	`
	sqlParams = []interface{}{
		act.VendorID, act.ID,
	}
	_, err = dao.ExecuteSQL(dao.GetDB(), sql, sqlParams...)
	if err != nil {
		return err
	}

	dao.Commit(db, txDB)
	return err
}

func SyncSpecialAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) {
	db := dao.GetDB()
	if err = SyncActStoreSku2StoreSkuAct(ctx, db, parentTask, act); err == nil {
		err = cms.FullSyncStoreSkuBindAct(ctx, parentTask, act.ID, nil, nil)
	}
	return err
}

func CreateActByExcel(ctx *jxcontext.Context, files string, vendorID int, vendorOrgCode string, mixType int, isFocus, isSync, isAsync, isContinueWhenError bool) (hint string, err error) {
	// if len(files) == 0 {
	// 	return "", errors.New("没有文件上传！")
	// }
	// fileHeader := files[0]
	// file, err := fileHeader.Open()
	hint, err = CreateActByExcelBin(ctx, files, vendorID, vendorOrgCode, mixType, isFocus, isSync, isAsync, isContinueWhenError)
	// file.Close()
	return hint, err
}

func CreateActByExcelBin(ctx *jxcontext.Context, reader string, vendorID int, vendorOrgCode string, mixType int, isFocus, isSync, isAsync, isContinueWhenError bool) (hint string, err error) {
	sheetParam := &SheetParamAct{
		StoreIDCol:            0,
		SkuIDCol:              2,
		SkuPricePercentageCol: 4,
		ActPriceCol:           5,
		EarningPriceCol:       6,
		StockCol:              7,
		ActTypeCol:            8,
		ActPricePercentageCol: 9,
		ActNameCol:            10,
		BeginTimeCol:          11,
		EndTimeCol:            12,
	}
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		var (
			actObj             = &model.Act{}
			actStoreSkuList    []*ActStoreSkuParam
			actStoreSkusResult []*ActStoreSkuParam
			storeIDs           []int
		)
		switch step {
		case 0:
			//读取excel文件
			xlsx, err := excelize.OpenFile("111.xlsx")
			// xlsx, err := excelize.OpenReader(reader)
			if err != nil {
				return result, err
			}
			rows, _ := xlsx.GetRows(xlsx.GetSheetName(1))
			for rowNum, row := range rows {
				if rowNum < 1 {
					continue
				}
				loadExcelForCreateAct(rowNum, mixType, row, sheetParam, actObj, actStoreSkuList, storeIDs)
			}
		case 1:
			//叉乘
			if mixType == 2 {
				for _, v := range storeIDs {
					for _, vv := range actStoreSkuList {
						actStoreSkus := &ActStoreSkuParam{}
						actStoreSkus = vv
						actStoreSkus.StoreID = v
						actStoreSkusResult = append(actStoreSkusResult, actStoreSkus)
					}
				}
				_, err = CreateAct(ctx, actObj, []int{vendorID}, vendorOrgCode, nil, actStoreSkusResult, isAsync)
			} else if mixType == 1 {
				_, err = CreateAct(ctx, actObj, []int{vendorID}, vendorOrgCode, nil, actStoreSkuList, isAsync)
			}
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据Excel创建活动-序列任务", ctx, isContinueWhenError, taskSeqFunc, 5)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func loadExcelForCreateAct(rowNum, mixType int, row []string, sheetParam *SheetParamAct, actObj *model.Act, actStoreSkuList []*ActStoreSkuParam, storeIDs []int) {
	actStoreSku := &ActStoreSkuParam{}
	for k, cell := range row {
		if rowNum == 1 {
			if k == sheetParam.ActTypeCol {
				var actType int
				for k, v := range model.ActTypeName {
					if cell == v {
						actType = k
						break
					}
				}
				actObj.Type = actType
			}
			if k == sheetParam.ActPricePercentageCol {
				actObj.PricePercentage = int(utils.Str2Float64(cell) * 10)
			}
			if k == sheetParam.ActNameCol {
				actObj.Name = cell
			}
			if k == sheetParam.BeginTimeCol {
				begin, err := time.Parse("2006年1月2日 15:04:05", cell)
				if err != nil {
					begin = utils.ZeroTimeValue
				}
				actObj.BeginAt = begin
			}
			if k == sheetParam.EndTimeCol {
				end, err := time.Parse("2006年1月2日 15:04:05", cell)
				if err != nil {
					end = utils.ZeroTimeValue
				}
				actObj.EndAt = end
			}
		}
		if k == sheetParam.StoreIDCol {
			//一行一行
			if mixType == 1 {
				actStoreSku.StoreID = int(utils.Str2Int64(cell))
			} else if mixType == 2 {
				storeIDs = append(storeIDs, int(utils.Str2Int64(cell)))
			}
		}
		if k == sheetParam.SkuIDCol {
			actStoreSku.SkuID = int(utils.Str2Int64(cell))
		}
		if k == sheetParam.SkuPricePercentageCol {
			actStoreSku.PricePercentage = int(utils.Str2Float64WithDefault(cell, 0) * 10)
		}
		if k == sheetParam.ActPriceCol {
			actStoreSku.ActPrice = int64(utils.Str2Float64(cell) * 100)
		}
		if k == sheetParam.EarningPriceCol {
			actStoreSku.EarningPrice = int64(utils.Str2Float64(cell) * 100)
		}
		if k == sheetParam.StockCol {
			actStoreSku.Stock = int(utils.Str2Int64(cell))
		}
	}
	actStoreSkuList = append(actStoreSkuList, actStoreSku)
}

func ChangeJxPriceByDiscountAct(ctx *jxcontext.Context) {
	var (
		db       = dao.GetDB()
		pageSize = 9999
	)
	page, err := dao.QueryActs(db, 0, 0, pageSize, -1, "", model.VendorIDJX, []int{model.YES}, []int{model.ActSkuDiscount}, nil, 0, nil, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -3, 0), time.Now())
	if err != nil {
		return
	}
	if len(page.Data) == 0 {
		return
	}
	for _, act := range page.Data {
		_, actStoreSkus, _ := dao.GetActStoreSkuVendorList(db, act.ID, nil, nil, nil, "", 0, pageSize)
		if len(actStoreSkus) == 0 {
			continue
		}
		for _, actStoreSku2 := range actStoreSkus {
			actStoreSkuMap := &model.ActStoreSkuMap{
				ModelIDCULD: model.ModelIDCULD{
					ID: actStoreSku2.MapID,
				},
				ActID:          actStoreSku2.ActID,
				StoreID:        actStoreSku2.StoreID,
				SkuID:          actStoreSku2.SkuID,
				TrendPrice:     actStoreSku2.TrendPrice,
				TrendType:      actStoreSku2.TrendType,
				ActualActPrice: actStoreSku2.ActualActPrice,
				VendorPrice:    actStoreSku2.VendorPrice,
			}
			storeSkus, _ := dao.GetStoresSkusInfo(db, []int{actStoreSkuMap.StoreID}, []int{actStoreSkuMap.SkuID})
			if len(storeSkus) == 0 {
				continue
			}
			var (
				storeSku        = storeSkus[0]
				shouldStockOut  = utils.Float64TwoInt64(math.Ceil(float64(storeSku.Stock) / float64(60))) //每个时间点应出货 = 总库存/60 = N
				actualStockOut  int64                                                                     //每个时间点实际出货 = C
				pricePercentage float64                                                                   //每次涨跌值为 原价的 2%，即原价100，每个时间点降价为2，涨价也为2
				actualPrice     int64
				minJxPrice      int64
			)
			if storeSku.Stock == 0 {
				continue
			}
			actualStockOut, err = dao.GetOrderStoreSkusCount(db, actStoreSkuMap.StoreID, actStoreSkuMap.SkuID, time.Now().Add(-time.Minute*10), time.Now())
			txDB, _ := dao.Begin(db)
			defer func() {
				if r := recover(); r != nil || err != nil {
					dao.Rollback(db, txDB)
					if r != nil {
						panic(r)
					}
				}
			}()
			//第一档时间内
			if (time.Now().Hour() >= 10 && time.Now().Hour() < 20) || (time.Now().Hour() == 20 && time.Now().Minute() < 1) {
				pricePercentage = 0.02
				actualPrice = utils.Float64TwoInt64(float64(actStoreSkuMap.VendorPrice) * pricePercentage)
				if actStoreSkuMap.TrendType == model.TrendTypeUp {
					actStoreSkuMap.ActualActPrice = actStoreSkuMap.ActualActPrice + actualPrice
					// storeSku.JxPrice = storeSku.JxPrice + int(actualPrice)
				} else if actStoreSkuMap.TrendType == model.TrendTypeDown {
					actStoreSkuMap.ActualActPrice = actStoreSkuMap.ActualActPrice - actualPrice
					//判断活动的折扣类型是最低价还是最低折扣(第一档)
					if act.DiscountType == model.ActDiscountTypePrice {
						minJxPrice = int64(act.DiscountValue1)
					} else if act.DiscountType == model.ActDiscountTypePercentage {
						minJxPrice = actStoreSkuMap.VendorPrice * int64(act.DiscountValue1) / 100
					}
					if actStoreSkuMap.ActualActPrice <= minJxPrice {
						actStoreSkuMap.ActualActPrice = minJxPrice
					}
					// storeSku.JxPrice = storeSku.JxPrice - int(actualPrice)
					stock := checkPriceDefendOrderByPrice(db, storeSku.StoreID, storeSku.SkuID, storeSku.Stock, int(actStoreSkuMap.ActualActPrice))
					if stock != -1 {
						storeSku.Stock = stock
						if _, err = dao.UpdateEntity(db, storeSku, "Stock"); err != nil {
							dao.Rollback(db, txDB)
						}
					}
				}
				if actStoreSkuMap.ActualActPrice >= actStoreSkuMap.VendorPrice {
					actStoreSkuMap.ActualActPrice = actStoreSkuMap.VendorPrice
				}
				// if _, err = dao.UpdateEntity(db, actStoreSkuMap, "ActualActPrice"); err != nil {
				// 	dao.Rollback(db, txDB)
				// }
				//C >= 2N  涨价趋势，最高涨价到无折扣
				if actualStockOut >= 2*shouldStockOut {
					actStoreSkuMap.TrendType = model.TrendTypeUp
					if actStoreSkuMap.ActualActPrice == actStoreSkuMap.VendorPrice {
						actStoreSkuMap.TrendType = model.TrendTypeNothing
					}
				} else if actualStockOut < utils.Float64TwoInt64(math.Ceil(float64(shouldStockOut)/float64(2))) { //C <= N/2 降价趋势，最低降价到设置到最低折扣
					actStoreSkuMap.TrendType = model.TrendTypeDown
					if actStoreSkuMap.ActualActPrice == minJxPrice {
						actStoreSkuMap.TrendType = model.TrendTypeNothing
					}
				} else {
					actStoreSkuMap.TrendType = model.TrendTypeNothing
				}
				actStoreSkuMap.TrendPrice = int(actualPrice)
				if _, err = dao.UpdateEntity(db, actStoreSkuMap, "ActualActPrice", "TrendType", "TrendPrice"); err != nil {
					dao.Rollback(db, txDB)
				}
			} else { //第二档时间内
				pricePercentage = 0.1
				actualPrice = utils.Float64TwoInt64(float64(actStoreSkuMap.VendorPrice) * pricePercentage)
				//判断活动的折扣类型是最低价还是最低折扣（第二档）
				if act.DiscountType == model.ActDiscountTypePrice {
					minJxPrice = int64(act.DiscountValue2)
				} else if act.DiscountType == model.ActDiscountTypePercentage {
					minJxPrice = actStoreSkuMap.VendorPrice * int64(act.DiscountValue2) / 100
				}
				if actStoreSkuMap.ActualActPrice == minJxPrice {
					actStoreSkuMap.TrendType = model.TrendTypeNothing
				} else {
					actStoreSkuMap.TrendType = model.TrendTypeDown
				}
				actStoreSkuMap.TrendPrice = int(actualPrice)
				// if _, err = dao.UpdateEntity(db, actStoreSkuMap, "TrendType", "TrendPrcie"); err != nil {
				// 	dao.Rollback(db, txDB)
				// }
				actStoreSkuMap.ActualActPrice = actStoreSkuMap.ActualActPrice - actualPrice
				if actStoreSkuMap.ActualActPrice <= minJxPrice {
					actStoreSkuMap.ActualActPrice = minJxPrice
				}
				stock := checkPriceDefendOrderByPrice(db, storeSku.StoreID, storeSku.SkuID, storeSku.Stock, int(actStoreSkuMap.ActualActPrice))
				if stock != -1 {
					storeSku.Stock = stock
					if _, err = dao.UpdateEntity(db, storeSku, "Stock"); err != nil {
						dao.Rollback(db, txDB)
					}
				}
				//22:00 恢复库存为100,价格恢复原价
				if time.Now().Hour() == 22 && time.Now().Minute() < 1 {
					storeSku.Stock = 100
					if _, err = dao.UpdateEntity(db, storeSku, "Stock"); err != nil {
						dao.Rollback(db, txDB)
					}
					actStoreSkuMap.ActualActPrice = actStoreSkuMap.VendorPrice
					actStoreSkuMap.TrendType = model.TrendTypeNothing
					actStoreSkuMap.TrendPrice = 0
				}
				if _, err = dao.UpdateEntity(db, actStoreSkuMap, "ActualActPrice", "TrendType", "TrendPrice"); err != nil {
					dao.Rollback(db, txDB)
				}
			}
			dao.Commit(db, txDB)
		}
	}
}

func checkPriceDefendOrderByPrice(db *dao.DaoDB, storeID, skuID, stock, jxPrice int) (realStock int) {
	priceDefends, _ := dao.GetPriceDefendOrder(db, "", []int{storeID}, []int{skuID}, []int{jxutils.GetDefendPriceIssue()}, 0, 0, 0, 1, "", utils.ZeroTimeValue, utils.ZeroTimeValue, false)
	if len(priceDefends) == 0 {
		return -1
	}
	for _, v := range priceDefends {
		//如果刚好守的价和降的价一样，再判断库存够不够
		if v.DefendPrice > int64(jxPrice) {
			if v.Count <= stock {
				stock -= v.Count
				v.IsSuccess = model.YES
				v.RealPrice = int64(jxPrice)
				dao.UpdateEntity(db, v, "IsSuccess", "RealPrice")
			} else {
				continue
			}
		} else {
			continue
		}
	}
	return stock
}

func GetVendorPopActs(ctx *jxcontext.Context, vendorID, storeID int) (result interface{}, err error) {
	if vendorID == model.VendorIDEBAI {
		result, err = api.EbaiAPI.GetMainActivityList(utils.Str2Int(ebai.EbaiSupplierIDhc))
	} else if vendorID == model.VendorIDMTWM {
		if storeID == 0 {
			return nil, fmt.Errorf("要选门店才能查美团的活动")
		}
		store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, vendorID, "")
		result, err = api.MtwmAPI.GetCenterList(store.VendorStoreID)
	} else {
		return nil, fmt.Errorf("暂不支持此平台")
	}
	return result, err
}

func GetVendorPopActDetail(ctx *jxcontext.Context, vendorID, storeID, actID int) (result interface{}, err error) {
	if vendorID == model.VendorIDEBAI {
		results, err2 := api.EbaiAPI.GetMainActivityDetail(actID)
		err = err2
		for _, v := range results {
			results2, _ := api.EbaiAPI.GetSubActivityDetail(int(v.ID))
			v.GetSubActivityDetailResult = results2
		}
		result = results
	} else if vendorID == model.VendorIDMTWM {
		if storeID == 0 {
			return nil, fmt.Errorf("要选门店才能查美团的活动")
		}
		store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, vendorID, "")
		result, err = api.MtwmAPI.GetInviteDetail(actID, store.VendorStoreID)
	} else {
		return nil, fmt.Errorf("暂不支持此平台")
	}
	return result, err
}

func GetNewVendorPopActs(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)
	actList, err := api.EbaiAPI.GetMainActivityList(utils.Str2Int(ebai.EbaiSupplierIDhc))
	if err != nil {
		return err
	}
	send := func(vendorID int, name1, name2 string) {
		var (
			roleList []*authz.RoleInfo
			opName   = ""
			userIDs  []string
		)
		if vendorID == model.VendorIDEBAI {
			opName = "lirongwei"
		} else {
			opName = "xiaoqiu"
		}
		roleList = append(roleList, autils.NewRole(opName, 0))
		if userIDMap, err := cms.GetRolesUserList(jxcontext.AdminCtx, roleList); err == nil {
			for _, v := range userIDMap {
				for _, vv := range v {
					userIDs = append(userIDs, vv)
				}
			}
		}
		for _, v := range userIDs {
			ddmsg.SendUserMessage(dingdingapi.MsgTyeText, v, model.VendorNames[vendorID]+"有新平台流量活动了！", model.VendorNames[vendorID]+"有未报名的平台流量活动，请查看！活动名："+name1+"->"+name2)
		}
	}
	for _, v := range actList {
		results, _ := api.EbaiAPI.GetMainActivityDetail(int(v.ActivityID))
		for _, vv := range results {
			//local, _ := time.LoadLocation("Local")
			//t, _ := time.ParseInLocation("2006-01-02 15:04:05", vv.SigninEndTime, local)
			//if time.Now().Sub(t) < 0 {
			result, _ := api.EbaiAPI.GetSubActivityDetail(int(vv.ID))
			if result.Status.Value == "NO_SIGN_UP" {
				send(model.VendorIDEBAI, vv.Name, result.Activity.Name)
			}
			//}
		}
	}
	storeMaps, _ := dao.GetStoresMapList(db, []int{model.VendorIDMTWM}, nil, []int{model.StoreStatusOpened, model.StoreStatusClosed, model.StoreStatusHaveRest}, model.StoreStatusOpened, model.YES, "", "", "")
	for _, v := range storeMaps {
		cList, _ := api.MtwmAPI.GetCenterList(v.VendorStoreID)
		for _, vv := range cList {
			if vv != nil {
				result, _ := api.MtwmAPI.GetInviteDetail(vv.ID, v.VendorStoreID)
				if result != nil {
					if result.Status != 2 {
						send(model.VendorIDMTWM, vv.Name, result.BriefIntroduction)
					}
				}
			}
		}
	}
	return err
}

func checkActStoreSkuExist(db *dao.DaoDB, storeID, skuID, vendorID int, beginAt, endAt time.Time) bool {
	var (
		act *model.Act
	)
	//如果开始时间和结束时间重合，就默认是从京西上建的活动，需要过滤掉
	sql := `
		SELECT b.*
		  FROM act_store_sku a
	      JOIN act b ON a.act_id = b.id
		  JOIN act_map c ON c.act_id = b.id
		 WHERE a.store_id = ? AND a.sku_id = ? AND b.begin_at = ? AND b.end_at = ? AND c.vendor_id = ?
	`
	sqlParams := []interface{}{storeID, skuID, vendorID, beginAt, endAt}
	if err := dao.GetRow(db, &act, sql, sqlParams); err == nil && act != nil {
		return true
	}
	return false
}

func RrefreshMtwmVendorAct(ctx *jxcontext.Context) (err error) {
	var (
		db         = dao.GetDB()
		actTypeMap = map[int]int{
			mtwmapi.RetailActTypeSecKill:    model.ActSkuSecKill,
			mtwmapi.RetailActTypeDirectDown: model.ActSkuDirectDown,
		}
	)
	//直接做删除，把新的加进来
	dao.ExecuteSQL(db, `TRUNCATE act_mtwm_vendor`)
	storeMaps, _ := dao.GetStoresMapList(db, []int{model.VendorIDMTWM}, nil, []int{model.StoreStatusOpened, model.StoreStatusHaveRest, model.StoreStatusClosed},
		model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	task := tasksch.NewParallelTask("刷新美团活动", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeMap := batchItemList[0].(*model.StoreMap)
			mtapi := mtwm.GetAPI(storeMap.VendorOrgCode, storeMap.StoreID, "")
			for k, v := range actTypeMap {
				// 查询所有美团门店的折扣和爆款商品
				actList, _ := mtapi.RetailDiscountList(storeMap.VendorStoreID, k)
				if len(actList) > 0 {
					for _, act := range actList {
						if act.Status > 0 {
							if checkActStoreSkuExist(db, storeMap.StoreID, utils.Str2Int(act.AppFoodCode), model.VendorIDMTWM, utils.Timestamp2Time(act.StartTime), utils.Timestamp2Time(act.EndTime)) {
								actVendor := &model.ActMtwmVendor{
									StoreID:             storeMap.StoreID,
									SkuID:               utils.Str2Int(act.AppFoodCode),
									VendorStoreID:       storeMap.VendorStoreID,
									ActType:             v,
									BeginAt:             utils.Timestamp2Time(act.StartTime),
									EndAt:               utils.Timestamp2Time(act.EndTime),
									SkuName:             act.Name,
									OriginPrice:         act.OriginalPrice,
									ActPrice:            act.ActPrice,
									DiscountCoefficient: act.DiscountCoefficient,
									Status:              act.Status,
									ItemID:              utils.Int64ToStr(act.ItemID),
									OrderLimit:          act.OrderLimit,
									Period:              act.Period,
									WeeksTime:           act.WeeksTime,
									SettingType:         act.SettingType,
								}
								dao.CreateEntity(db, actVendor)
							}
						}
					}
				}
				// 查询门店爆款商品,当门店存在爆款时,取消之前的力荐商品,将新的爆款设置为力荐商品
				// 商品为手动设置力荐或者爆款活动在取消力荐之前被删除,那么无法取消被力荐的商品!
				if k == mtwmapi.RetailActTypeSecKill && len(actList) > 0 && storeMap.VendorID == model.VendorIDMTWM {
					//allActivitySkuIdList := make([]string, 0, 0)                       // 此门店全部的折扣(爆款)活动商品
					//activationActivitySkuIdList := make([]*partner.StoreSkuInfo, 0, 0) // 此门店正在进行的折扣(爆款)活动商品
					//loseActivitySkuIdList := make([]*partner.StoreSkuInfo, 0, 0)       // 此门店已经结束的折扣(爆款)活动商品
					//for _, ac := range actList {
					//	allActivitySkuIdList = append(allActivitySkuIdList, utils.Int64ToStr(ac.ItemID))
					//	// 已经生效的爆款活动
					//	if ac.Status == 1 && ac.SkuId != "" {
					//		activationActivitySkuIdList = append(activationActivitySkuIdList, &partner.StoreSkuInfo{
					//			SkuID:       utils.Str2Int(ac.SkuId),
					//			IsSpecialty: 1,
					//		})
					//	}
					//	// 已经失效的爆款活动
					//	if ac.Status == 0 && ac.SkuId != "" {
					//		loseActivitySkuIdList = append(loseActivitySkuIdList, &partner.StoreSkuInfo{
					//			SkuID:       utils.Str2Int(ac.SkuId),
					//			IsSpecialty: 0,
					//		})
					//	}
					//}
					//// 取消当前力荐商品
					//partner.GetPurchasePlatformFromVendorID(storeMap.VendorID).(partner.ISingleStoreStoreSkuHandler).UpdateStoreSkusSpecTag(ctx, storeMap.VendorOrgCode, storeMap.StoreID, storeMap.VendorStoreID, loseActivitySkuIdList)
					//// 重新推荐力荐商品
					//partner.GetPurchasePlatformFromVendorID(storeMap.VendorID).(partner.ISingleStoreStoreSkuHandler).UpdateStoreSkusSpecTag(ctx, storeMap.VendorOrgCode, storeMap.StoreID, storeMap.VendorStoreID, activationActivitySkuIdList)
				}
			}
			return retVal, err
		}, storeMaps)
	tasksch.HandleTask(task, nil, true).Run()
	task.GetID()
	return err
}

func RrefreshEbaiVendorAct(ctx *jxcontext.Context) (err error) {
	var (
		db     = dao.GetDB()
		actMap = struct {
			idMap map[int64]int
			*sync.RWMutex
		}{}
	)
	actMap.idMap = make(map[int64]int)
	actMap.RWMutex = new(sync.RWMutex)
	//直接做删除，把新的加进来
	dao.ExecuteSQL(db, `TRUNCATE act_ebai_vendor`)
	dao.ExecuteSQL(db, `TRUNCATE act_ebai_vendor_sku`)
	storeMaps, _ := dao.GetStoresMapList(db, []int{model.VendorIDEBAI}, nil, []int{model.StoreStatusOpened, model.StoreStatusHaveRest, model.StoreStatusClosed},
		model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	task := tasksch.NewParallelTask("刷新饿百活动", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeMap := batchItemList[0].(*model.StoreMap)
			actIDs, _ := api.EbaiAPI.ActMultiChannelQueryIDs("", utils.Str2Int64(storeMap.VendorStoreID), "2")
			for _, v := range actIDs {
				if result, err := api.EbaiAPI.ActMultiChannelQueryInfos(utils.Int64ToStr(v), "", utils.Str2Int64(storeMap.VendorStoreID)); err == nil {
					if result.Status < 2 {
						if actMap.idMap[v] == 0 {
							actMap.Lock()
							actMap.idMap[v] = 1
							actMap.Unlock()
							actVendor := &model.ActEbaiVendor{
								BeginAt:   utils.Timestamp2Time(result.Starttime),
								EndAt:     utils.Timestamp2Time(result.Endtime),
								ActType:   model.ActSkuDirectDown,
								Status:    result.Status,
								ActID:     utils.Int64ToStr(result.Activityid),
								DayLimit:  result.Limitruleapi.Userdaycountlimit,
								Period:    result.Periodsandruleapilist[0].Periods[0].Opentime + "-" + result.Periodsandruleapilist[0].Periods[0].Closetime,
								WeeksTime: result.Periodsandruleapilist[0].Periods[0].Weekday,
								ActName:   result.Activityname,
							}
							dao.CreateEntity(db, actVendor)
						}
						for _, sku := range result.Skulist {
							actVendorSku := &model.ActEbaiVendorSku{
								ActID:         utils.Int64ToStr(result.Activityid),
								StoreID:       storeMap.StoreID,
								EbaiID:        sku.Skuid,
								VendorStoreID: storeMap.VendorStoreID,
								SkuName:       sku.Upcname,
								ActPrice:      jxutils.IntPrice2Standard(int64(sku.Periodsandruleapilist[0].Ruleapi.Discount)),
							}
							var storeSkuBind *model.StoreSkuBind
							sql := `
									SELECT * FROM store_sku_bind WHERE store_id = ? AND ebai_id = ?
								`
							sqlParams := []interface{}{storeMap.StoreID, sku.Skuid}
							if err = dao.GetRow(db, &storeSkuBind, sql, sqlParams); err == nil && storeSkuBind != nil {
								if checkActStoreSkuExist(db, storeMap.StoreID, storeSkuBind.SkuID, model.VendorIDEBAI, utils.Timestamp2Time(result.Starttime), utils.Timestamp2Time(result.Endtime)) {
									actVendorSku.SkuID = storeSkuBind.SkuID
									actVendorSku.OriginPrice = jxutils.IntPrice2Standard(int64(storeSkuBind.Price))
								}
							}
							dao.CreateEntity(db, actVendorSku)
						}
					}
				}
			}
			return retVal, err
		}, storeMaps)
	tasksch.HandleTask(task, nil, true).Run()
	task.GetResult(0)
	return err
}

// GetActMtwmVendor 查询美团平台活动
func GetActMtwmVendor(ctx *jxcontext.Context, storeIDs, skuIDs []int, keyword, beginAt, endAt string, actType, offset, pageSize int) (page *model.PagedInfo, err error) {
	return dao.GetActMtwmVendorPage(dao.GetDB(), storeIDs, skuIDs, keyword, utils.Str2Time(beginAt), utils.Str2Time(endAt), actType, offset, pageSize)
}

// GetStoreNotHaveSku 根据输入的商品id判断,那些门店没有参数该活动
func GetStoreNotHaveSku(skuId int, offSet, pageSize int) (interface{}, error) {
	var (
		db = dao.GetDB()
	)
	haveActSku, err := dao.GetActMtwmVendorPage(db, nil, []int{skuId}, "", time.Time{}, time.Time{}, 0, 1, 20000)
	if err != nil {
		return nil, err
	}

	if haveActSku == nil || haveActSku.TotalCount == 0 {
		return nil, fmt.Errorf("未查询到活动商品")
	}

	// 拥有此商品的门店
	storeIds := make([]int, 0, 0)
	for _, v := range haveActSku.Data.([]*model.ActMtwmVendor) {
		storeIds = append(storeIds, v.StoreID)
	}

	return dao.GetDontHaveSku(db, storeIds, offSet, pageSize)
}

func GetActMtwmVendorSku(ctx *jxcontext.Context, storeID int, keyword string, actType, offset, pageSize int) (page *model.PagedInfo, err error) {
	return dao.GetActMtwmVendorSkuPage(dao.GetDB(), storeID, keyword, actType, offset, pageSize)
}

func DeleteActStoreSkuVendor(ctx *jxcontext.Context, db *dao.DaoDB, actStoreSkuParam []*ActStoreSkuParam) (err error) {

	return fmt.Errorf("暂不支持直接取消平台活动，开发中……")
}

func GetActEbaiVendor(ctx *jxcontext.Context, storeIDs, skuIDs []int, keyword, beginAt, endAt string, actType, offset, pageSize int) (page *model.PagedInfo, err error) {
	return dao.GetActEbaiVendorPage(dao.GetDB(), storeIDs, skuIDs, keyword, utils.Str2Time(beginAt), utils.Str2Time(endAt), actType, offset, pageSize)
}

func GetActEbaiVendorSku(ctx *jxcontext.Context, actID, keyword string, offset, pageSize int) (page *model.PagedInfo, err error) {
	return dao.GetActEbaiVendorSkuPage(dao.GetDB(), actID, keyword, offset, pageSize)
}
