Files
jx-callback/business/partner/purchase/ebai/store.go
2020-08-10 09:02:30 +08:00

553 lines
20 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package ebai
import (
"fmt"
"strings"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxcallback/scheduler"
"git.rosy.net.cn/jx-callback/business/jxutils"
"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"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
type tEbaiStoreInfo struct {
model.Store
VendorOrgCode string `orm:"size(32)" json:"vendorOrgCode"` // 同一平台下不同的商户代码,如果只有一个,可以为空
VendorStoreID string `orm:"column(vendor_store_id)"`
RealLastOperator string
EbaiStoreStatus int
SyncStatus int
ProvinceID int `orm:"column(province_id)"`
CityID int `orm:"column(city_id)"`
DistrictID int `orm:"column(district_id)"`
VendorStoreName string
}
func (p *PurchaseHandler) CreateStore(db *dao.DaoDB, storeID int, userName string) (vendorStoreID string, err error) {
var store tEbaiStoreInfo
sql := `
SELECT t1.*, t2.status ebai_store_status, t2.vendor_store_id,
IF(t1.updated_at > t2.updated_at, t1.last_operator, t2.last_operator) real_last_operator, t2.sync_status,
province.ebai_code province_id, city.ebai_code city_id, district.ebai_code district_id
FROM store t1
LEFT JOIN store_map t2 ON t1.id = t2.store_id AND t2.vendor_id = ? AND t2.deleted_at = ?
JOIN place district ON t1.district_code = district.code
JOIN place city ON t1.city_code = city.code
JOIN place province ON city.parent_code = province.code
WHERE t1.id = ? AND t2.id IS NULL;
`
if err = dao.GetRow(db, &store, sql, model.VendorIDEBAI, utils.DefaultTimeValue, storeID); err == nil {
params := genStoreMapFromStore(&store)
params["shop_id"] = store.ID
params["business_form_id"] = "179"
params["service_phone"] = store.Tel1
params["invoice_support"] = 2
params["package_box_price"] = 0
params["encrypt"] = ""
params["category1"] = ""
params["category2"] = ""
params["category3"] = ""
if globals.EnableEbaiStoreWrite {
intVendorStoreID, err2 := api.EbaiAPI.ShopCreate(params)
if err = err2; err == nil {
return utils.Int64ToStr(intVendorStoreID), err
}
} else {
return utils.Int64ToStr(jxutils.GenFakeID()), nil
}
}
return "", err
}
// 2019-07-26 疑似饿百将门店返回的坐标类型从float64改为string了
func getCoordintate(data interface{}) float64 {
if str, ok := data.(string); ok {
return utils.Str2Float64WithDefault(str, 0)
}
return utils.MustInterface2Float64(data)
}
func (p *PurchaseHandler) ReadStore(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID string) (*dao.StoreDetail, error) {
baiduShopID := utils.Str2Int64WithDefault(vendorStoreID, 0)
if baiduShopID == 0 {
return nil, fmt.Errorf("饿百门店ID:%s非法应该是一个整数", vendorStoreID)
}
result, err := api.EbaiAPI.ShopGet("", baiduShopID)
if err == nil {
// globals.SugarLogger.Debug(utils.Format4Output(result, false))
retVal := &dao.StoreDetail{
Store: model.Store{
Address: utils.Interface2String(result["address"]),
Tel1: utils.Interface2String(result["phone"]),
},
}
retVal.OriginalName = utils.Interface2String(result["name"])
_, retVal.Name = jxutils.SplitStoreName(retVal.OriginalName, partner.StoreNameSeparator, globals.StoreNameEbai)
retVal.DeliveryType = EbaiDeliveryType2Jx(utils.Interface2String(result["delivery_type"]))
retVal.SetOpTime(ebaiOpTime2Jx(result["business_time"]))
retVal.Status, _ = p.GetStoreStatus(ctx, vendorOrgCode, 0, vendorStoreID)
tel2 := utils.Interface2String(result["ivr_phone"])
if tel2 != "" && tel2 != retVal.Tel1 {
retVal.Tel2 = tel2
}
lng := getCoordintate(result["longitude"])
lat := getCoordintate(result["latitude"])
if utils.Interface2String(result["coord_type"]) == ebaiapi.CoordTypeBaidu {
var err2 error
if lng, lat, err2 = api.AutonaviAPI.CoordinateConvert(lng, lat, autonavi.CoordSysBaidu); err2 != nil {
return nil, err2
}
}
retVal.Lng = jxutils.StandardCoordinate2Int(lng)
retVal.Lat = jxutils.StandardCoordinate2Int(lat)
db := dao.GetDB()
if city, err2 := dao.GetPlaceByName(db, utils.Interface2String(result["city"]), model.PlaceLevelCity, 0); err2 == nil {
retVal.CityCode = city.Code
retVal.CityName = utils.Interface2String(result["city"])
districtName := utils.Interface2String(result["county"])
if retVal.CityCode != 0 && districtName != "" {
if district, err2 := dao.GetPlaceByName(db, utils.Interface2String(result["county"]), model.PlaceLevelDistrict, city.Code); err2 == nil {
retVal.DistrictCode = district.Code
}
}
}
if retVal.DistrictCode == 0 {
retVal.DistrictCode = api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat)
if retVal.CityCode == 0 {
if district, err := dao.GetPlaceByCode(db, retVal.DistrictCode); err == nil {
retVal.CityCode = district.ParentCode
}
}
}
retVal.VendorStoreID = vendorStoreID
retVal.ID = int(utils.Str2Int64WithDefault(utils.Interface2String(result["shop_id"]), 0))
retVal.DeliveryRangeType = model.DeliveryRangeTypePolygon
retVal.DeliveryRange = EbaiDeliveryRegion2Jx(result["delivery_region"])
return retVal, nil
}
return nil, err
}
func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName string) (err error) {
globals.SugarLogger.Debugf("ebai UpdateStore storeID:%d, userName:%s", storeID, userName)
var stores []*tEbaiStoreInfo
sql := `
SELECT
t1.*,
t2.status ebai_store_status, t2.vendor_store_id, t2.vendor_org_code,
IF(t1.updated_at > t2.updated_at, t1.last_operator, t2.last_operator) real_last_operator, t2.sync_status, t2.vendor_store_name
FROM store t1
JOIN store_map t2 ON t1.id = t2.store_id AND t2.vendor_id = ? AND (t2.deleted_at = ?)
WHERE t1.id = ?
ORDER BY t2.updated_at
`
if err = dao.GetRows(db, &stores, sql, model.VendorIDEBAI, utils.DefaultTimeValue, storeID); err == nil {
for _, store := range stores {
if globals.EnableEbaiStoreWrite {
shopID := 0
if store.SyncStatus&model.SyncFlagDeletedMask == 0 {
shopID = store.ID
}
store2, err2 := p.ReadStore(jxcontext.AdminCtx, store.VendorOrgCode, store.VendorStoreID)
// globals.SugarLogger.Debugf("ebai UpdateStore2 store2:%s, err2:%v", utils.Format4Output(store2, true), err2)
if err = err2; err == nil {
if store2.ID == store.ID {
shopID = -1
}
}
if err == nil {
if shopID > 0 {
err = p.UpdateStoreCustomID(jxcontext.AdminCtx, "", store.VendorStoreID, int64(shopID))
} else if shopID == 0 {
// todo remove out shop id
}
}
if err == nil {
if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreStatus) != 0 {
mergeStatus := jxutils.MergeStoreStatus(store.Status, store.EbaiStoreStatus)
if err = p.UpdateStoreStatus(jxcontext.AdminCtx, store.VendorOrgCode, storeID, store.VendorStoreID, mergeStatus); err != nil {
return err
}
}
params := genStoreMapFromStore(store)
if err = api.EbaiAPI.ShopUpdate(params); err == nil {
if store.PromoteInfo != "" {
err = api.EbaiAPI.ShopAnnouncementSet("", utils.Str2Int64(store.VendorStoreID), store.PromoteInfo)
}
}
}
}
}
}
return err
}
func isStoreStatusSame(status1, status2 int) bool {
if status1 == model.StoreStatusHaveRest {
status1 = model.StoreStatusClosed
}
if status2 == model.StoreStatusHaveRest {
status2 = model.StoreStatusClosed
}
return status1 == status2
}
func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask tasksch.ITask, isAsync bool) (hint string, err error) {
globals.SugarLogger.Debugf("ebai RefreshAllStoresID")
const batchSize = 50
const stepCount = 3
var stores []*tEbaiStoreInfo
db := dao.GetDB()
rootTask := tasksch.NewSeqTask("ebai RefreshAllStoresID", ctx,
func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
switch step {
case 0:
err = dao.GetRows(db, &stores, `
SELECT t1.*, t2.vendor_store_id
FROM store t1
JOIN store_map t2 ON t1.id = t2.store_id AND t2.deleted_at = ? AND t2.vendor_id = ?
WHERE t1.deleted_at = ?
`, utils.DefaultTimeValue, model.VendorIDEBAI, utils.DefaultTimeValue)
default:
taskName := "ebai RefreshAllStoresID update to custom id"
if step != stepCount-1 {
taskName = "ebai RefreshAllStoresID update to uuid"
}
task1 := tasksch.NewParallelTask(taskName, tasksch.NewParallelConfig().SetBatchSize(batchSize), ctx,
func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
baiduShopIDs := make([]string, len(batchItemList))
shopIDs := make([]string, len(batchItemList))
for k, v := range batchItemList {
store := v.(*tEbaiStoreInfo)
baiduShopIDs[k] = store.VendorStoreID
shopIDs[k] = utils.Int2Str(store.ID)
if step != stepCount-1 {
shopIDs[k] = utils.GetUUID()
}
}
if globals.EnableEbaiStoreWrite {
err = api.EbaiAPI.ShopIDBatchUpdate(baiduShopIDs, shopIDs)
}
return nil, err
}, stores)
task.AddChild(task1).Run()
_, err = task1.GetResult(0)
}
return nil, err
}, stepCount)
tasksch.HandleTask(rootTask, parentTask, false).Run()
if !isAsync {
_, err = rootTask.GetResult(0)
}
return rootTask.ID, err
}
// todo 此函数只考虑了在饿了么侧开店的情况
func EbaiDeliveryType2Jx(deliveryType string) int8 {
spIndex := strings.Index(deliveryType, "|")
elmDeliveryType := utils.Str2Int64(deliveryType[:spIndex])
switch elmDeliveryType {
case ebaiapi.DeliveryTypeElmFengNiaoZS,
ebaiapi.DeliveryTypeElmFengNiaoZSKA,
ebaiapi.DeliveryTypeElmFengNiaoKS,
ebaiapi.DeliveryTypeElmNewRetail,
ebaiapi.DeliveryTypeElmEPeiSong,
ebaiapi.DeliveryTypeElmFengNiaoHybrid,
ebaiapi.DeliveryTypeElmFengNiaoNiubee:
return scheduler.StoreDeliveryTypeByPlatform
case ebaiapi.DeliveryTypeElmXingHuoZBTrial,
ebaiapi.DeliveryTypeElmXingHuoZB,
ebaiapi.DeliveryTypeElmXingHuoZBKA:
return scheduler.StoreDeliveryTypeCrowdSourcing
case ebaiapi.DeliveryTypeElmNone,
ebaiapi.DeliveryTypeElmXingHuoTrial,
ebaiapi.DeliveryTypeElmXingHuo,
ebaiapi.DeliveryTypeElmEBase,
ebaiapi.DeliveryTypeElmXingHuoKA:
return scheduler.StoreDeliveryTypeByStore
default:
return scheduler.StoreDeliveryTypeCrowdSourcing
}
}
func EbaiDeliveryRegion2Jx(deliveryRegion interface{}) string {
realDeliveryRegion := deliveryRegion.([]interface{})
if len(realDeliveryRegion) > 0 {
region := deliveryRegion.([]interface{})[0].(map[string]interface{})["region"].([]interface{})[0].([]interface{})
coords := make([]string, len(region))
for k, v := range region {
mapV := v.(map[string]interface{})
coords[k] = fmt.Sprintf("%.6f,%.6f", utils.MustInterface2Float64(mapV["longitude"]), utils.MustInterface2Float64(mapV["latitude"]))
}
return strings.Join(coords, ";")
}
return ""
}
func JxDeliveryRegion2Ebai(store *model.Store) (deliveryRegion interface{}) {
rangeStr := strings.Trim(store.DeliveryRange, ";")
if store.DeliveryRangeType == model.DeliveryRangeTypeRadius {
if utils.Str2Int64WithDefault(store.DeliveryRange, 0) > 100 { // todo 如果小于100米表示禁用不更新
rangeStr = jxutils.GetPolygonFromCircleStr(jxutils.IntCoordinate2Standard(store.Lng), jxutils.IntCoordinate2Standard(store.Lat), utils.Str2Float64(store.DeliveryRange), 8)
} else {
rangeStr = ""
}
}
rangeStr = "" // todo 暂时禁止同步配送区域
if rangeStr != "" {
pointPairs := strings.Split(rangeStr, ";")
region := make([]map[string]interface{}, 0)
for _, v := range pointPairs {
pointPair := strings.Split(v, ",")
if len(pointPair) == 2 {
region = append(region, map[string]interface{}{
"longitude": utils.Str2Float64(pointPair[0]),
"latitude": utils.Str2Float64(pointPair[1]),
})
}
}
deliveryRegion = []interface{}{
map[string]interface{}{
"name": "主要配送区",
"delivery_fee": 600,
"delivery_time": "60",
"min_buy_free": "0",
"min_order_price": "0",
"region": []interface{}{
region,
},
},
}
}
return deliveryRegion
}
func fillOpTimeParams(params map[string]interface{}, opTimeList []int16) map[string]interface{} {
if params == nil {
params = make(map[string]interface{})
}
var pairList []map[string]string
opTimeListLen := len(opTimeList)
if opTimeListLen > 4 {
opTimeListLen = 4
}
opTimeListLen = opTimeListLen / 2 * 2
for k := 0; k < len(opTimeList); k += 2 {
if opTimeList[k] != 0 {
pairList = append(pairList, map[string]string{
"start": jxutils.JxOperationTime2StrTime(opTimeList[k]),
"end": jxutils.JxOperationTime2StrTime(opTimeList[k+1]),
})
} else {
break
}
}
params["business_time"] = pairList
return params
}
func ebaiOpTime2Jx(businessTime interface{}) (opTimeList []int16) {
businessTimeList, _ := businessTime.([]interface{})
for _, v := range businessTimeList {
vMap := v.(map[string]interface{})
opTimeList = append(opTimeList, jxutils.StrTime2JxOperationTime(utils.Interface2String(vMap["start"])+":00", 700),
jxutils.StrTime2JxOperationTime(utils.Interface2String(vMap["end"])+":00", 2000))
}
return opTimeList
}
func genStoreMapFromStore(store *tEbaiStoreInfo) map[string]interface{} {
params := fillOpTimeParams(nil, store.GetOpTimeList())
// tel := store.Tel1
// if tel == "" {
// tel = store.Tel2
// }
// if tel != "" {
// // params["phone"] = tel // 外卖客服联系电话,这个有时能修改,有时不能修改,暂时统一不改
// params["ivr_phone"] = tel // 订单提醒电话
// }
phone := ""
if store.MarketManPhone != "" {
phone = store.MarketManPhone
} else {
phone = model.VendorStoreTel
}
params["ivr_phone"] = phone //统一改为这个电话
if store.VendorStoreID != "" {
params["baidu_shop_id"] = store.VendorStoreID
}
if false { //store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 {
if store.VendorStoreName != "" {
params["name"] = store.VendorStoreName
} else {
params["name"] = jxutils.ComposeStoreName(store.Name, model.VendorIDEBAI)
}
}
boxFee, _ := dao.GetSysConfigAsInt64(dao.GetDB(), model.ConfigSysEbaiBoxFee)
params["package_box_price"] = boxFee
params["service_phone"] = store.Tel1
params["address"] = store.Address
// todo 饿百 开店审核通过后不允许修改商户信息
if store.SyncStatus&(model.SyncFlagNewMask /*|model.SyncFlagStoreAddress*/) != 0 {
// todo 这里应该要做坐标转换吧
params["longitude"] = jxutils.IntCoordinate2Standard(store.Lng)
params["latitude"] = jxutils.IntCoordinate2Standard(store.Lat)
params["coord_type"] = ebaiapi.CoordTypeAutonavi
// if deliveryRegion := JxDeliveryRegion2Ebai(&store.Store); deliveryRegion != nil {
// params["delivery_region"] = deliveryRegion
// }
if store.ProvinceID != 0 {
params["province"] = store.ProvinceID
}
if store.CityID != 0 {
params["city"] = store.CityID
}
if store.DistrictID != 0 {
params["county"] = store.DistrictID
}
}
return params
}
func (p *PurchaseHandler) GetStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string) (storeStatus int, err error) {
ebaiStatus, err := api.EbaiAPI.ShopBusStatusGet("", utils.Str2Int64(vendorStoreID), ebaiapi.PlatformFlagElm)
if err == nil {
storeStatus = EbaiBusStatus2JxStatus(ebaiStatus)
}
return storeStatus, err
}
func (c *PurchaseHandler) onShopMsgPush(msg *ebaiapi.CallbackMsg) (response *ebaiapi.CallbackResponse) {
var err error
vendorStoreID := utils.Interface2String(msg.Body["baidu_shop_id"])
storeID := int(utils.Interface2Int64WithDefault(msg.Body["shop_id"], 0))
storeStatus := model.StoreStatusOpened
switch utils.Interface2String(msg.Body["msg_type"]) {
case "online", "offline":
storeStatus, err = c.GetStoreStatus(jxcontext.AdminCtx, "", storeID, vendorStoreID)
case "shop_open", "shop_pause", "shop_close":
if int(utils.ForceInterface2Int64(msg.Body["business_ele"])) == 1 {
storeStatus = model.StoreStatusOpened
} else {
storeStatus = model.StoreStatusClosed
}
}
if err == nil {
err = partner.CurStoreManager.OnStoreStatusChanged(vendorStoreID, model.VendorIDEBAI, storeStatus)
}
return api.EbaiAPI.Err2CallbackResponse(msg.Cmd, err, nil)
}
func (c *PurchaseHandler) GetShopHealthInfo(vendorShopID string) (shopHealthInfo map[string]interface{}, err error) {
result, err := api.EbaiAPI.GetShopHealthByDetail(utils.Str2Int64(vendorShopID))
if err == nil {
shopHealthInfo = utils.Struct2FlatMap(result)
}
return shopHealthInfo, err
}
func (p *PurchaseHandler) EnableAutoAcceptOrder(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, isSetEnable bool) (err error) {
return err
}
func (c *PurchaseHandler) UpdateStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, status int) (err error) {
if globals.EnableEbaiStoreWrite {
if status == model.StoreStatusOpened {
err = api.EbaiAPI.ShopOpen("", utils.Str2Int64(vendorStoreID))
} else if status == model.StoreStatusHaveRest || status == model.StoreStatusClosed {
err = api.EbaiAPI.ShopClose("", utils.Str2Int64(vendorStoreID))
} else if status == model.StoreStatusDisabled {
err = api.EbaiAPI.ShopClose("", utils.Str2Int64(vendorStoreID))
// err = api.EbaiAPI.ShopOffline("", utils.Str2Int64(vendorStoreID))
}
if err != nil {
if remoteStatus, err2 := c.GetStoreStatus(ctx, vendorOrgCode, storeID, vendorStoreID); err2 == nil && remoteStatus == status {
err = nil
} else if intErr, ok := err.(*utils.ErrorWithCode); ok && intErr.IntCode() == 201100 || intErr.IntCode() == 201101 { // 兼容假错误:商户开业饿了么侧成功
err = nil
}
}
}
return err
}
func (c *PurchaseHandler) UpdateStoreOpTime(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, opTimeList []int16) (err error) {
params := map[string]interface{}{
ebaiapi.KeyBaiduShopID: vendorStoreID,
}
fillOpTimeParams(params, opTimeList)
if globals.EnableEbaiStoreWrite {
api.EbaiAPI.ShopUpdate(params)
}
return err
}
func (c *PurchaseHandler) GetAllStoresVendorID(ctx *jxcontext.Context, vendorOrgCode string) (vendorStoreIDs []string, err error) {
shopList, err := api.EbaiAPI.ShopList(ebaiapi.SysStatusAll)
if err == nil && len(shopList) > 0 {
vendorStoreIDs = make([]string, len(shopList))
for k, v := range shopList {
vendorStoreIDs[k] = utils.Int64ToStr(v.BaiduShopID)
}
}
return vendorStoreIDs, err
}
func (c *PurchaseHandler) UpdateStoreCustomID(ctx *jxcontext.Context, vendorOrgCode string, vendorStoreID string, storeID int64) (err error) {
if globals.EnableJdStoreWrite {
err = api.EbaiAPI.ShopIDBatchUpdate([]string{vendorStoreID}, []string{utils.Int64ToStr(storeID)})
}
return err
}
func (c *PurchaseHandler) GetShopListByPage(status, proxy_business_state int) (shopList []*ebaiapi.ShopList, err error) {
var (
pageCount = 500
pageNum = 1
)
for {
shopListPage, _, err2 := api.EbaiAPI.GetShopListByPage(status, proxy_business_state, pageCount, pageNum)
err = err2
shopList = append(shopList, shopListPage...)
if len(shopList) < pageCount {
break
}
pageNum++
}
return shopList, err
}
func (c *PurchaseHandler) GetShopIDsByPage() (vendorStoreIDs []string, err error) {
shopList, err := c.GetShopListByPage(ebaiapi.ShopStatusOnLine, ebaiapi.ProxyBusinessState)
for _, v := range shopList {
vendorStoreIDs = append(vendorStoreIDs, v.Wid)
}
return vendorStoreIDs, err
}
func (p *PurchaseHandler) CreateStore2(db *dao.DaoDB, storeID int, userName string) (vendorStoreID string, err error) {
return vendorStoreID, err
}
func (p *PurchaseHandler) DeleteStore(db *dao.DaoDB, storeID int, userName string) (err error) {
return err
}