package jd import ( "fmt" "strings" "time" "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/dao" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils/errlist" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/globals/api" ) const ( VendorStorePrefix = "京西菜市" ) type tJdStoreInfo struct { model.Store VendorOrgCode string `orm:"size(32)" json:"vendorOrgCode"` // 同一平台下不同的商户代码,如果只有一个,可以为空 FreightDeductionPack string `orm:"size(32)" json:"freightDeductionPack"` // JdCityCode int JdDistrictCode int JdStoreStatus int VendorStoreID string `orm:"column(vendor_store_id)"` RealLastOperator string SyncStatus int VendorStoreName string } var ( specialDistrictMap = map[int]int{ 13989: 310032, } ) func (p *PurchaseHandler) ReadStore(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID string) (*dao.StoreDetail, error) { a := getAPI(vendorOrgCode) result, err := a.GetStoreInfoByStationNo2(vendorStoreID) if err == nil { retVal := &dao.StoreDetail{ Store: model.Store{ Address: result.StationAddress, OpenTime1: JdOperationTime2JxOperationTime(result.ServiceTimeStart1), CloseTime1: JdOperationTime2JxOperationTime(result.ServiceTimeEnd1), OpenTime2: JdOperationTime2JxOperationTime(result.ServiceTimeStart2), CloseTime2: JdOperationTime2JxOperationTime(result.ServiceTimeEnd2), Status: JdStoreStatus2JxStatus(int(result.Yn), result.CloseStatus), Tel1: result.Phone, }, } if result.IsAutoOrder == 0 { retVal.IsAutoOrder = 1 } else { retVal.IsAutoOrder = -1 } retVal.OriginalName = result.StationName _, retVal.Name = jxutils.SplitStoreName(retVal.OriginalName, partner.StoreNameSeparator, VendorStorePrefix) retVal.DeliveryType = JdDeliveryType2Jx(result.CarrierNo) tel2 := result.Mobile if tel2 != "" && tel2 != retVal.Tel1 { retVal.Tel2 = tel2 } lng := result.Lng lat := result.Lat retVal.Lng = jxutils.StandardCoordinate2Int(lng) retVal.Lat = jxutils.StandardCoordinate2Int(lat) db := dao.GetDB() cityCode := result.City if cityCode != 0 { if city, err2 := dao.GetPlaceByJdCode(db, cityCode); err2 == nil { retVal.CityCode = city.Code retVal.CityName = utils.Interface2String(result.CityName) districtName := result.CountyName // 京东的市区号码与通用数据完全无法关联,只有通过名字来关联 if retVal.CityCode != 0 && districtName != "" { if district, err2 := dao.GetPlaceByName(db, districtName, 3, 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(result.OutSystemID, 0)) deliveryRange, err2 := a.GetDeliveryRangeByStationNo2(vendorStoreID) if err = err2; err == nil { retVal.DeliveryRangeType = int8(deliveryRange.DeliveryRangeType) if retVal.DeliveryRangeType == model.DeliveryRangeTypePolygon { retVal.DeliveryRange = strings.Trim(deliveryRange.DeliveryRange, ";") } else { retVal.DeliveryRange = utils.Int2Str(deliveryRange.DeliveryRangeRadius) } return retVal, nil } } return nil, err } // stoerIDs为nil表示所有 func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName string) (err error) { var stores []*tJdStoreInfo sql := ` SELECT t1.*, city.jd_code jd_city_code, district.jd_code jd_district_code, t2.status jd_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, t2.freight_deduction_pack, t2.vendor_org_code, 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 = ?) LEFT JOIN place city ON t1.city_code = city.code LEFT JOIN place district ON t1.district_code = district.code WHERE t1.id = ? ORDER BY t2.updated_at ` if err = dao.GetRows(db, &stores, sql, model.VendorIDJD, utils.DefaultTimeValue, storeID); err == nil { for _, store := range stores { a := getAPI(store.VendorOrgCode) storeParams := &jdapi.OpStoreParams{ StationNo: store.VendorStoreID, Operator: userName, Phone: store.Tel1, Mobile: store.Tel2, } if store.SyncStatus&model.SyncFlagDeletedMask == 0 { storeParams.OutSystemID = utils.Int2Str(int(store.ID)) } else { storeParams.OutSystemID = store.VendorStoreID } if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 { if store.VendorStoreName != "" { storeParams.StationName = store.VendorStoreName } else { storeParams.StationName = jxutils.ComposeStoreName(store.Name, model.VendorIDJD) } storeParams.StationName = utils.LimitUTF8StringLen(storeParams.StationName, jdapi.MaxStoreNameLen) } if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreAddress) != 0 { storeParams.StationAddress = store.Address storeParams.CoordinateType = jdapi.CoordinateTypeAutonavi // 一直用高德 storeParams.Lng = jxutils.IntCoordinate2Standard(store.Lng) storeParams.Lat = jxutils.IntCoordinate2Standard(store.Lat) if store.JdCityCode != 0 { storeParams.City = store.JdCityCode } if store.JdDistrictCode != 0 { storeParams.County = store.JdDistrictCode } // storeParams.DeliveryRangeType = store.DeliveryRangeType // if store.DeliveryRangeType == model.DeliveryRangeTypePolygon { // storeParams.CoordinatePoints = store.DeliveryRange // } else { // storeParams.DeliveryRangeRadius = int(utils.Str2Int64WithDefault(store.DeliveryRange, 0)) // } } if specialDistrictMap[storeParams.County] != 0 { storeParams.City = storeParams.County storeParams.County = specialDistrictMap[storeParams.County] } storeParams.StoreNotice = store.PromoteInfo modifyCloseStatus := false if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagDeletedMask|model.SyncFlagStoreStatus) != 0 { modifyCloseStatus = true _, storeParams.CloseStatus = JxStoreStatus2JdStatus(jxutils.MergeStoreStatus(store.Status, store.JdStoreStatus)) } fillOpTimeParams(storeParams, store.GetOpTimeList()) // globals.SugarLogger.Debug(utils.Format4Output(storeParams, false)) errList := errlist.New() if globals.EnableJdStoreWrite { errList.AddErr(a.UpdateStoreInfo4Open2(storeParams, modifyCloseStatus)) } if store.FreightDeductionPack != "" { storeDetail, err2 := dao.GetStoreDetail(db, store.ID, model.VendorIDJD) if err2 == nil { if storeDetail.FreightDeductionPackObj != nil { freightParams := &jdapi.UpdateStoreFreightParam{ StationNo: store.VendorStoreID, UserPin: userName, OpenDistanceFreight: true, StartCharge: int64(storeDetail.FreightDeductionPackObj.StartPrice), } if len(storeDetail.FreightDeductionPackObj.FreightDeductionList) > 0 { for _, v := range storeDetail.FreightDeductionPackObj.FreightDeductionList { if v.DeductFreight > 0 { freightParams.FreeFreightInfoList = append(freightParams.FreeFreightInfoList, &jdapi.FreeFreightInfo{ FullFreeMoney: int64(v.BeginPrice), FreeType: jdapi.FreightFreeTypePartBase, FreeMoney: int64(v.DeductFreight), FreeFreightTimes: []*jdapi.FreeFreightTime{ &jdapi.FreeFreightTime{ FreeBeginTime: utils.Time2Str(time.Now()), FreeEndTime: utils.Time2Str(time.Now().Add(24 * time.Hour * 365 * 2)), }, }, }) break // 京东只能设置一个满减,之前理解有误 } } } freightParams.IsFullFree = len(freightParams.FreeFreightInfoList) > 0 // globals.SugarLogger.Debug(utils.Format4Output(freightParams, false)) if globals.EnableJdStoreWrite { errList.AddErr(a.UpdateStoreFreightConfigNew(freightParams)) } } } } err = errList.GetErrListAsOne() } } return err } func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask tasksch.ITask, isAsync bool) (hint string, err error) { globals.SugarLogger.Debugf("jd RefreshAllStoresID") const stepCount = 3 var stores []*tJdStoreInfo db := dao.GetDB() rootTask := tasksch.NewSeqTask("jd 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.VendorIDJD, utils.DefaultTimeValue) default: taskName := "jd RefreshAllStoresID update outSystemId" if step != stepCount-1 { taskName = "jd RefreshAllStoresID update to uuid" } task1 := tasksch.NewParallelTask(taskName, nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*tJdStoreInfo) storeParams := &jdapi.OpStoreParams{ StationNo: store.VendorStoreID, Operator: ctx.GetUserName(), OutSystemID: utils.Int2Str(int(store.ID)), } if step != stepCount-1 { storeParams.OutSystemID = store.VendorStoreID } if globals.EnableJdStoreWrite { err = getAPI(store.VendorOrgCode).UpdateStoreInfo4Open2(storeParams, false) } 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 } func JdDeliveryType2Jx(deliveryType int) int8 { if deliveryType == jdapi.CarrierNoSelfDelivery { return scheduler.StoreDeliveryTypeByStore } else if deliveryType == jdapi.CarrierNoCrowdSourcing { return scheduler.StoreDeliveryTypeCrowdSourcing } return scheduler.StoreDeliveryTypeByPlatform } func (p *PurchaseHandler) GetStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string) (storeStatus int, err error) { result, err := getAPI(vendorOrgCode).GetStoreInfoByStationNo2(vendorStoreID) if err == nil { storeStatus = JdStoreStatus2JxStatus(int(result.Yn), result.CloseStatus) } return storeStatus, err } // 当前京东的storeCrud消息不会在门店状态改变时发送,所以意义不大,先放在这里 func (c *PurchaseHandler) OnStoreMsg(vendorOrgCode string, msg *jdapi.CallbackOrderMsg) (response *jdapi.CallbackResponse) { var err error // if msg.StatusID == jdapi.StatusIDUpdateStore { // var storeStatus int // vendorStoreID := msg.BillID // if storeStatus, err = c.GetStoreStatus(jxcontext.AdminCtx, vendorStoreID); err == nil { // err = partner.CurStoreManager.OnStoreStatusChanged(vendorStoreID, model.VendorIDJD, storeStatus) // } else { // // 可能在门店删除的情况下会出查不到门店的错误 // if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == 4 { // err = nil // } // } // } return jdapi.Err2CallbackResponse(err, "") } func (p *PurchaseHandler) EnableAutoAcceptOrder(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, isSetEnable bool) (err error) { _, err = getAPI(vendorOrgCode).UpdateStoreConfig4Open(vendorStoreID, isSetEnable) return err } func (c *PurchaseHandler) UpdateStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, status int) (err error) { _, closeStatus := JxStoreStatus2JdStatus(status) if globals.EnableJdStoreWrite { err = getAPI(vendorOrgCode).UpdateStoreInfo4Open2(&jdapi.OpStoreParams{ StationNo: vendorStoreID, Operator: ctx.GetUserName(), CloseStatus: closeStatus, }, true) } return err } func fillOpTimeParams(params *jdapi.OpStoreParams, opTimeList []int16) { index := 1 opTimeListLen := len(opTimeList) if opTimeListLen > 4 { opTimeListLen = 4 } opTimeListLen = opTimeListLen / 2 * 2 for k := 0; k < len(opTimeList); k += 2 { if opTimeList[k] != 0 { if index == 1 { params.ServiceTimeStart1 = int(JxOperationTime2JdOperationTime(int16(opTimeList[k]))) params.ServiceTimeEnd1 = int(JxOperationTime2JdOperationTime(int16(opTimeList[k+1]))) } else { params.ServiceTimeStart2 = int(JxOperationTime2JdOperationTime(int16(opTimeList[k]))) params.ServiceTimeEnd2 = int(JxOperationTime2JdOperationTime(int16(opTimeList[k+1]))) } } else { break } index++ } } func (c *PurchaseHandler) UpdateStoreOpTime(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, opTimeList []int16) (err error) { params := &jdapi.OpStoreParams{ StationNo: vendorStoreID, Operator: ctx.GetUserName(), } fillOpTimeParams(params, opTimeList) if globals.EnableJdStoreWrite { err = getAPI(vendorOrgCode).UpdateStoreInfo4Open2(params, false) } return err } func (c *PurchaseHandler) GetAllStoresVendorID(ctx *jxcontext.Context, vendorOrgCode string) (vendorStoreIDs []string, err error) { vendorStoreIDs, err = getAPI(vendorOrgCode).GetStationsByVenderId() return vendorStoreIDs, err } func (c *PurchaseHandler) storeUploadImgByURL(vendorOrgCode, inImgURL string) (imgURL string, err error) { if globals.EnableJdStoreWrite { if vendorOrgCode == globals.JdOrgCode { imgURL, err = api.JdPageAPI.StoreUploadImgByURL(inImgURL) } else { imgURL, err = getAPI(vendorOrgCode).StoreUploadImgByURL(inImgURL) } } else { imgURL = utils.GetUUID() } return imgURL, err } func addStoreInfo2Err(err error, storeID int) error { if err != nil { errExt, _ := err.(*utils.ErrorWithCode) if errExt == nil { errExt = utils.NewErrorCode(err.Error(), "999") } errExt.AddPrefixMsg(fmt.Sprintf("门店%d", storeID)) err = errExt } return err } func (c *PurchaseHandler) SyncQualify(ctx *jxcontext.Context, storeDetail *dao.StoreDetail) (err error) { if storeDetail.LicenceCode == "" || storeDetail.Licence == "" { return addStoreInfo2Err(fmt.Errorf("营业执照信息不全"), storeDetail.ID) } if storeDetail.IDCode == "" || storeDetail.IDCardFront == "" || storeDetail.IDCardBack == "" || storeDetail.IDValid == "" { return addStoreInfo2Err(fmt.Errorf("个人信息不全"), storeDetail.ID) } var qualifyList []*jdapi.QualifyItem licenceDetail, err := api.JdPageAPI.GetCorporationInfo(storeDetail.VendorStoreID, storeDetail.LicenceCode) if err != nil { return addStoreInfo2Err(err, storeDetail.ID) } licenceURL, err := c.storeUploadImgByURL(storeDetail.VendorOrgCode, storeDetail.Licence) if err != nil { return addStoreInfo2Err(err, storeDetail.ID) } expireStart, err := utils.TryStr2Time(licenceDetail.StartDate) if err != nil { return addStoreInfo2Err(fmt.Errorf("执照有效开始时间:%s非法,请手动处理", licenceDetail.StartDate), storeDetail.ID) } qualifyList = append(qualifyList, &jdapi.QualifyItem{ QualifyType: jdapi.QualifyTypeCompany, QualifyURL: licenceURL, QualifyExpireStart: utils.Time2Str(expireStart), QualifyExpireForever: 0, QualifyName: licenceDetail.OperName, LicenceType: "-1", QualifyNumber: storeDetail.LicenceCode, QualifyAddress: licenceDetail.Address, LicenceName: licenceDetail.Name, EconKind: licenceDetail.EconKind, Scope: licenceDetail.Scope, }) idFrondURL, err := c.storeUploadImgByURL(storeDetail.VendorOrgCode, storeDetail.IDCardFront) if err != nil { return addStoreInfo2Err(err, storeDetail.ID) } // 个体经营,个体工商户 if storeDetail.LicenceType == 0 { // 个人 personQualify := &jdapi.QualifyItem{ QualifyType: jdapi.QualifyTypePerson, QualifyURL: idFrondURL, QualifyExpireStart: utils.Time2Str(utils.Str2Time(storeDetail.IDValid)), QualifyExpireForever: 0, QualifyNumber: storeDetail.IDCode, QualifyOwner: storeDetail.LicenceOwnerName, } if storeDetail.IDExpire != "" { personQualify.QualifyExpireForever = 1 personQualify.QualifyExpireEnd = utils.Time2Str(utils.Str2Time(storeDetail.IDExpire)) } qualifyList = append(qualifyList, personQualify) } else { addInfo := &jdapi.QualifyItem{ QualifyType: jdapi.QualifyTypeAddInfo, QualifyURL: idFrondURL, QualifyExpireStart: utils.Time2Str(utils.Str2Time(storeDetail.IDValid)), QualifyExpireForever: 0, } if storeDetail.IDExpire != "" { addInfo.QualifyExpireForever = 1 addInfo.QualifyExpireEnd = utils.Time2Str(utils.Str2Time(storeDetail.IDExpire)) } qualifyList = append(qualifyList, addInfo) } if storeDetail.IDExpire == "" { idBackURL, err := c.storeUploadImgByURL(storeDetail.VendorOrgCode, storeDetail.IDCardBack) if err != nil { return addStoreInfo2Err(err, storeDetail.ID) } qualifyList = append(qualifyList, &jdapi.QualifyItem{ QualifyType: jdapi.QualifyTypeAddInfo, QualifyURL: idBackURL, QualifyExpireStart: utils.Time2Str(utils.Str2Time(storeDetail.IDValid)), QualifyExpireForever: 0, }) } globals.SugarLogger.Debug(utils.Format4Output(qualifyList, false)) if globals.EnableJdStoreWrite { err = api.JdPageAPI.SaveQualify(storeDetail.VendorStoreID, jdapi.SaveQualifyActionTypeCommit, qualifyList) // err = api.JdPageAPI.SaveQualify(storeDetail.VendorStoreID, jdapi.SaveQualifyActionTypeSave, qualifyList) } return addStoreInfo2Err(err, storeDetail.ID) } func (c *PurchaseHandler) UpdateStoreCustomID(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID string, storeID int64) (err error) { if globals.EnableJdStoreWrite { err = getAPI(vendorOrgCode).UpdateStoreInfo4Open2( &jdapi.OpStoreParams{ StationNo: vendorStoreID, Operator: ctx.GetUserName(), OutSystemID: utils.Int2Str(int(storeID)), }, false) } return err }