Merge remote-tracking branch 'origin/mark' into lcw

This commit is contained in:
renyutian
2019-04-28 15:36:24 +08:00
44 changed files with 914 additions and 245 deletions

View File

@@ -49,8 +49,9 @@ func (a *Auther) SendVerifyCode(mobileNumber string) error {
response, err := smsClient.Execute(globals.AliKey, globals.AliSecret, mobileNumber, "京西菜市", "SMS_84655036", string(utils.MustMarshal(map[string]interface{}{
"code": code,
})))
a.SaveVerifyCode(mobileNumber, code)
if err == nil && response.Code == aliyunsmsclient.ResponseCodeOk {
a.SaveVerifyCode(mobileNumber, code)
// a.SaveVerifyCode(mobileNumber, code)
} else {
if err == nil {
if warningMap[response.Code] == 1 {

View File

@@ -87,7 +87,7 @@ func GetWeiXinUserInfo(code string, state string) (userInfo *UserInfoExt, err er
SNSUserInfo: *wxUserinfo,
TempPassword: utils.GetUUID(),
}
globals.SugarLogger.Debugf("GetUserInfo code:%s, pwd:%s", code, userInfo.TempPassword)
globals.SugarLogger.Debugf("GetUserInfo code:%s, userInfo:%s", code, utils.Format4Output(userInfo, true))
cacheSNSInfo(wxUserinfo, userInfo.TempPassword, DefTempPasswordDuration)
user, err2 := dao.GetWeiXinUserByIDs(dao.GetDB(), "", wxUserinfo.UnionID, wxUserinfo.OpenID, "")
if err = err2; err == nil {

View File

@@ -346,6 +346,7 @@ func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db *dao.Dao
}
func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db *dao.DaoDB) (isDuplicated bool, order *model.GoodsOrder, err error) {
globals.SugarLogger.Debugf("addOrderStatus refOrderID:%s, orderID:%s", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID)
if db == nil {
db = dao.GetDB()
}
@@ -359,24 +360,30 @@ func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db *dao.Da
if (model.IsOrderLockStatus(orderStatus.Status) || model.IsOrderUnlockStatus(orderStatus.Status)) ||
(model.IsOrderMainStatus(orderStatus.Status) && orderStatus.Status >= order.Status) { // todo 要求status不能回绕
order.VendorStatus = orderStatus.VendorStatus
order.StatusTime = orderStatus.StatusTime
updateFields := []string{
"VendorStatus", "StatusTime",
"VendorStatus",
}
if model.IsOrderMainStatus(orderStatus.Status) {
order.LockStatus = model.OrderStatusUnknown
order.Status = orderStatus.Status
updateFields = append(updateFields, "Status", "LockStatus")
order.StatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "Status", "StatusTime")
if order.LockStatus != model.OrderStatusUnknown {
order.LockStatus = model.OrderStatusUnknown
order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime")
}
} else {
if model.IsOrderUnlockStatus(orderStatus.Status) {
order.LockStatus = model.OrderStatusUnknown
updateFields = append(updateFields, "LockStatus")
} else if model.IsOrderLockStatus(orderStatus.Status) {
} else {
if order.LockStatus != model.OrderStatusUnknown {
globals.SugarLogger.Warnf("addOrderStatus refOrderID:%s, orderID:%s, order.LockStatus:%d, status.LockStatus:%d", orderStatus.RefVendorOrderID, orderStatus.VendorOrderID, order.LockStatus, orderStatus.Status)
}
order.LockStatus = orderStatus.Status
updateFields = append(updateFields, "LockStatus")
}
order.LockStatusTime = orderStatus.StatusTime
updateFields = append(updateFields, "LockStatus", "LockStatusTime")
}
// orderStatus.LockStatus = order.LockStatus
if model.IsOrderFinalStatus(orderStatus.Status) {
order.OrderFinishedAt = orderStatus.StatusTime
updateFields = append(updateFields, "OrderFinishedAt")

View File

@@ -133,6 +133,13 @@ func (c *BaseScheduler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, ord
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AcceptOrRefuseFailedGetOrder(ctx, order, isAcceptIt)
}
if err == nil {
flag := int8(model.OrderFlagAgreeFailedGetGoods)
if !isAcceptIt {
flag = model.OrderFlagRefuseFailedGetGoods
}
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag)
}
return err
}
@@ -140,6 +147,9 @@ func (c *BaseScheduler) CallPMCourier(ctx *jxcontext.Context, order *model.Goods
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).CallCourier(ctx, order)
}
if err == nil {
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskCallPMCourier)
}
return err
}
@@ -147,6 +157,9 @@ func (c *BaseScheduler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).ConfirmReceiveGoods(ctx, order)
}
if err == nil {
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, model.OrderFlagMaskFailedDeliver)
}
return err
}
@@ -154,5 +167,12 @@ func (c *BaseScheduler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model
if c.IsReallyCallPlatformAPI {
err = partner.GetPurchasePlatformFromVendorID(order.VendorID).AgreeOrRefuseCancel(ctx, order, isAcceptIt, reason)
}
if err == nil {
flag := int8(model.OrderFlagAgreeUserApplyCancel)
if !isAcceptIt {
flag = model.OrderFlagRefuseUserApplyCancel
}
dao.SetOrderFlag(dao.GetDB(), ctx.GetUserName(), order.VendorOrderID, order.VendorID, flag)
}
return err
}

View File

@@ -113,11 +113,12 @@ func (s *WatchOrderInfo) updateOrderStoreFeature(order *model.GoodsOrder) (err e
}
s.autoPickupTimeoutMinute = int(storeMap.AutoPickup)
s.storeDeliveryType = FixedScheduler.GetStoreDeliveryType(order, storeMap)
if s.storeDeliveryType == scheduler.StoreDeliveryTypeByStore && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0 {
order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
}
globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d, order.DeliveryFlag:%d", order.VendorOrderID, s.storeDeliveryType, order.DeliveryFlag)
globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d", order.VendorOrderID, s.storeDeliveryType)
// if s.storeDeliveryType == scheduler.StoreDeliveryTypeByStore && (order.DeliveryFlag&model.OrderDeliveryFlagMaskPurcahseDisabled) == 0 {
// order.DeliveryFlag |= model.OrderDeliveryFlagMaskPurcahseDisabled
// err = partner.CurOrderManager.UpdateOrderStatusAndFlag(order)
// }
// globals.SugarLogger.Debugf("updateOrderStoreFeature orderID:%s, s.storeDeliveryType:%d, order.DeliveryFlag:%d", order.VendorOrderID, s.storeDeliveryType, order.DeliveryFlag)
}
return err
}
@@ -409,20 +410,7 @@ func (s *DefScheduler) OnWaybillStatusChanged(bill *model.Waybill, isPending boo
// s.ProxyCancelWaybill(order, bill, partner.CancelWaybillReasonNotAcceptIntime, partner.CancelWaybillReasonStrNotAcceptIntime)
globals.SugarLogger.Infof("OnWaybillStatusChanged CourierArrived order(%d, %s) bill(%d, %s), bill:%v shouldn't get here", order.WaybillVendorID, order.VendorWaybillID, bill.WaybillVendorID, bill.VendorWaybillID, bill)
}
case model.WaybillStatusFailed: // todo WaybillStatusFailed理解成订单整个失败了不需要再尝试创建运单了注意这里应该加个zabbix日志的报警
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if s.isBillCandidate(order, bill) {
s.resetTimer(savedOrderInfo, bill, isPending)
if !isPending {
globals.SugarLogger.Infof("OnWaybillStatusChanged WaybillStatusFailed, bill:%v", bill)
bill.WaybillVendorID = model.VendorIDUnknown
s.updateOrderByBill(order, bill, false)
}
} else {
// 创建运单失败时可能到这里来比如818874313000121
globals.SugarLogger.Infof("OnWaybillStatusChanged Failed bill:%v shouldn't got here, order details:%v", bill, order)
}
case model.WaybillStatusCanceled:
case model.WaybillStatusCanceled, model.WaybillStatusFailed:
s.removeWaybillFromMap(savedOrderInfo, bill.WaybillVendorID)
if s.isBillCandidate(order, bill) || order.WaybillVendorID == model.VendorIDUnknown {
s.resetTimer(savedOrderInfo, nil, isPending)

View File

@@ -54,6 +54,7 @@ func InitServiceInfo(version string, buildTime time.Time, gitCommit string) {
"storeMsgSendStatusName": model.StoreMsgSendStatusName,
"shopChineseNames": model.ShopChineseNames,
"printerVendorInfo": model.PrinterVendorInfo,
"purchaseVendorInfo": model.PurchaseVendorInfo,
},
}
Init()

View File

@@ -63,7 +63,7 @@ func (s *StoreManager) OnStoreStatusChanged(vendorStoreID string, vendorID int,
}
}()
if storeKV != nil {
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeKV:%d", vendorStoreID, utils.Format4Output(storeKV, true))
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeKV:%s", vendorStoreID, utils.Format4Output(storeKV, true))
store := &model.Store{}
store.ID = storeDetail.Store.ID
if err = utils.CallFuncLogError(func() error {
@@ -74,7 +74,7 @@ func (s *StoreManager) OnStoreStatusChanged(vendorStoreID string, vendorID int,
}
}
if storeMapKV != nil {
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeMapKV:%d", vendorStoreID, utils.Format4Output(storeMapKV, true))
globals.SugarLogger.Debugf("OnStoreStatusChanged venvendorStoreID:%s, storeMapKV:%s", vendorStoreID, utils.Format4Output(storeMapKV, true))
if err = utils.CallFuncLogError(func() error {
_, err = dao.UpdateEntityLogically(db, &model.StoreMap{}, storeMapKV, model.AdminName, map[string]interface{}{
model.FieldStoreID: storeDetail.Store.ID,

View File

@@ -242,13 +242,28 @@ func (v *VendorSync) SyncStore(ctx *jxcontext.Context, db *dao.DaoDB, vendorID,
}
func (v *VendorSync) SyncSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuID int, isAsync, isContinueWhenError bool, userName string) (hint string, err error) {
globals.SugarLogger.Debugf("SyncSku trackInfo:%s, nameID:%d, skuID:%d, userName:%s", ctx.GetTrackInfo(), nameID, skuID, userName)
return v.LoopMultiStoresVendors(ctx, db, fmt.Sprintf("同步商品信息, nameID:%d, skuID:%d", nameID, skuID), isAsync, userName,
var (
nameIDs []int
skuIDs []int
)
if nameID != -1 {
nameIDs = []int{nameID}
}
if skuID != -1 {
skuIDs = []int{skuID}
}
return v.SyncSkus(ctx, db, nameIDs, skuIDs, isAsync, isContinueWhenError, userName)
}
func (v *VendorSync) SyncSkus(ctx *jxcontext.Context, db *dao.DaoDB, nameIDs []int, skuIDs []int, isAsync, isContinueWhenError bool, userName string) (hint string, err error) {
globals.SugarLogger.Debugf("SyncSku trackInfo:%s, nameIDs:%v, skuIDs:%v, userName:%s", ctx.GetTrackInfo(), nameIDs, skuIDs, userName)
return v.LoopMultiStoresVendors(ctx, db, fmt.Sprintf("同步商品信息, nameIDs:%v, skuIDs:%v", nameIDs, skuIDs), isAsync, userName,
func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
vendorID := batchItemList[0].(int)
multiStoresHandler := v.GetMultiStoreHandler(vendorID)
syncStatusFieldName := dao.GetSyncStatusStructField(model.VendorNames[multiStoresHandler.GetVendorID()])
dbField := dao.ConvertDBFieldPrefix(model.VendorNames[multiStoresHandler.GetVendorID()])
skuMap := make(map[int]bool)
sql := fmt.Sprintf(`
SELECT DISTINCT t2.*
FROM sku t1
@@ -256,14 +271,23 @@ func (v *VendorSync) SyncSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuI
WHERE t1.%s_sync_status <> 0
`, dbField)
sqlParams := []interface{}{}
if nameID != -1 {
sql += " AND t1.name_id = ?"
sqlParams = append(sqlParams, nameID)
if len(nameIDs) > 1 {
sql += " AND t1.name_id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")"
sqlParams = append(sqlParams, nameIDs)
} else if len(nameIDs) == 1 {
sql += " AND t1.name_id = ? "
sqlParams = append(sqlParams, nameIDs[0])
}
if skuID != -1 {
sql += " AND t1.id = ?"
sqlParams = append(sqlParams, skuID)
if len(skuIDs) > 0 {
sql += " AND t1.id IN(" + dao.GenQuestionMarks(len(skuIDs)) + ")"
sqlParams = append(sqlParams, skuIDs)
} else if len(skuIDs) == 1 {
sql += " AND t1.id = ? "
sqlParams = append(sqlParams, skuIDs[0])
}
for _, v := range skuIDs {
skuMap[v] = true
}
sql += " ORDER BY t2.id"
@@ -284,7 +308,7 @@ func (v *VendorSync) SyncSku(ctx *jxcontext.Context, db *dao.DaoDB, nameID, skuI
for _, sku := range skuList {
syncStatus := refutil.GetObjFieldByName(sku, syncStatusFieldName).(int8)
globals.SugarLogger.Debugf("SyncSku trackInfo:%s, skuID:%d, syncStatus:%d", ctx.GetTrackInfo(), sku.ID, syncStatus)
if (skuID == -1 || skuID == sku.ID) && (syncStatus != 0) {
if (len(skuIDs) == 0 || skuMap[sku.ID]) && (syncStatus != 0) {
updateFields := []string{syncStatusFieldName}
if syncStatus&model.SyncFlagDeletedMask != 0 { // 删除
if syncStatus&model.SyncFlagNewMask == 0 {
@@ -543,3 +567,26 @@ func isSyncError(err error) bool {
_, ok := err.(*SyncError)
return ok
}
func (v *VendorSync) SyncSkuNames(ctx *jxcontext.Context, nameIDs []int, isForce, isAsync, isContinueWhenError bool) (hint string, err error) {
db := dao.GetDB()
if isForce {
sql := `
UPDATE sku t1
SET t1.jd_sync_status = t1.jd_sync_status | ?
WHERE t1.deleted_at = ?
`
sqlParams := []interface{}{
model.SyncFlagModifiedMask,
utils.DefaultTimeValue,
}
if len(nameIDs) > 0 {
sql += " AND t1.name_id IN(" + dao.GenQuestionMarks(len(nameIDs)) + ")"
sqlParams = append(sqlParams, nameIDs)
}
if _, err = dao.ExecuteSQL(db, sql, sqlParams...); err != nil {
return "", err
}
}
return v.SyncSkus(ctx, db, nameIDs, nil, isAsync, isContinueWhenError, ctx.GetUserName())
}

View File

@@ -46,7 +46,9 @@ func GetStoreUsers(ctx *jxcontext.Context, storeID int) (storeUserInfos []*dao.S
}
func GetUserInfo(ctx *jxcontext.Context, mobile string) (storeUserInfo *dao.StoreUserInfo, err error) {
return dao.GetUserStoreInfo(dao.GetDB(), "tel", mobile)
storeUserInfo, err = dao.GetUserStoreInfo(dao.GetDB(), "tel", mobile)
globals.SugarLogger.Debugf("GetUserInfo:%s, token:%s, mobile:%s, storeUserInfo:%s, err:%v", ctx.GetTrackInfo(), ctx.GetToken(), mobile, utils.Format4Output(storeUserInfo, true), err)
return storeUserInfo, err
}
func GetSelfInfo(ctx *jxcontext.Context) (storeUserInfo *dao.StoreUserInfo, err error) {
@@ -58,7 +60,9 @@ func GetSelfInfo(ctx *jxcontext.Context) (storeUserInfo *dao.StoreUserInfo, err
if fieldName == "" {
return nil, auth.ErrIllegalLoginType
}
return dao.GetUserStoreInfo(dao.GetDB(), fieldName, loginInfo.GetAuthID())
storeUserInfo, err = dao.GetUserStoreInfo(dao.GetDB(), fieldName, loginInfo.GetAuthID())
globals.SugarLogger.Debugf("GetSelfInfo:%s, token:%s, storeUserInfo:%s, err:%v", ctx.GetTrackInfo(), ctx.GetToken(), utils.Format4Output(storeUserInfo, true), err)
return storeUserInfo, err
}
func UnbindMobile(ctx *jxcontext.Context, mobile string) (num int64, err error) {

View File

@@ -73,14 +73,14 @@ func SendFilesToStores(ctx *jxcontext.Context, files []*multipart.FileHeader, ti
db := dao.GetDB()
billRec := &legacymodel.StoreBill{
Date: time.Now(),
Url: jxutils.ComposeQiniuResURL(ret.Key),
Url: jxutils.ComposeQiniuResURL(strings.Replace(ret.Key, "http://", "https://", -1)),
StoreId: storeID,
BillName: fileHeader.Filename,
ShopName: shopName,
BillTitle: title,
}
if err = dao.CreateEntity(db, billRec); err == nil {
err = weixinmsg.NotifySaleBill(storeID, title, shopName, "http://www.jxc4.com/billshow/?path="+billRec.Url)
err = weixinmsg.NotifySaleBill(storeID, title, shopName, fmt.Sprintf("%s/billshow/?path=%s", globals.BackstageHost, billRec.Url))
if err != nil {
globals.SugarLogger.Infof("SendFilesToStores NotifySaleBill file:%s error:%v", fileHeader.Filename, err)
}

View File

@@ -9,6 +9,8 @@ import (
"net/http"
"strings"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
@@ -153,11 +155,15 @@ func InitSkuName(ctx *jxcontext.Context, isForce, isAsync, isContinueWhenError b
response, err := http.Get(skuName.Img)
if err == nil {
defer response.Body.Close()
data, err2 := ioutil.ReadAll(response.Body)
if err = err2; err == nil {
skuName.ImgHashCode = fmt.Sprintf("%X", md5.Sum(data))
db := dao.GetDB()
_, err = dao.UpdateEntity(db, skuName, "ImgHashCode")
if response.StatusCode == http.StatusOK {
data, err2 := ioutil.ReadAll(response.Body)
if err = err2; err == nil {
skuName.ImgHashCode = fmt.Sprintf("%X", md5.Sum(data))
db := dao.GetDB()
_, err = dao.UpdateEntity(db, skuName, "ImgHashCode")
}
} else {
err = platformapi.ErrHTTPCodeIsNot200
}
}
}
@@ -423,7 +429,7 @@ func BuildSkuFromEbaiStore(ctx *jxcontext.Context, baiduShopID int64, isAsync, i
err = nil
continue
}
qiniuImgURL := "http://image.jxc4.com/" + key + "?imageMogr2/thumbnail/x800/gravity/Center/crop/800x800"
qiniuImgURL := jxutils.ComposeQiniuResURL(key) + "?imageMogr2/thumbnail/x800/gravity/Center/crop/800x800"
for _, sku := range skuNameExt.Skus {
jdCatID := 22410 // 其他国产水果
if cat.Name == "进口水果" {

View File

@@ -23,7 +23,6 @@ import (
"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/astaxie/beego/orm"
)
var innerDataPat *regexp.Regexp
@@ -605,8 +604,7 @@ func CreateOrderFromOriginal(ctx *jxcontext.Context, isAsync, isContinueWhenErro
WHERE t2.id IS NULL
LIMIT ?;
`
rawDB := orm.NewOrm()
db := dao.WrapDB(rawDB)
db := dao.GetDB()
var orderList []*GoodsOrderOriginalEx
if err = dao.GetRows(db, &orderList, sql, sqlBatchCount); err != nil {
return nil, err

View File

@@ -0,0 +1,262 @@
package eventhub
import (
"fmt"
"strings"
"sync"
"time"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/baseapi/utils"
)
const (
cmdRegisterConsumer = 1
cmdUnregisterConsumer = 2
cmdNewEvent = 3
)
const (
maxGetOrderTimeDuration = 24 * time.Hour
minPollingDuration = 1 * time.Minute
defPollingDuration = 5 * time.Minute
maxPollingDuration = 10 * time.Minute
)
const (
cmdChanLen = 100
)
const (
allEventType = ""
)
type IEventProducer interface {
IsCriteriaMatch(eventInfo *EventInfo, criteria interface{}) bool
}
type EventInfo struct {
Type string
Data interface{}
}
type tRegisterInfo struct {
notifyChan chan *EventInfo
eventCategory string
eventTypeList []string
criteria interface{}
}
type tUnregisterInfo struct {
notifyChan chan *EventInfo
closeChan chan struct{}
}
type tCmd struct {
cmdType int
data interface{}
// regisgerInfo *RegisterInfo // CmdRegisterConsumer
// unregisterInfo *UnregisterInfo // CmdUnregisterConsumer
// eventInfo *EventInfo // CmdNewEvent
}
type EventHub struct {
cmdChan chan *tCmd
chanMap map[chan *EventInfo]*tRegisterInfo
typeChanMap map[string]map[chan *EventInfo]bool
eventCatMap map[string]IEventProducer
locker sync.RWMutex
}
func eventRoutine(eventHub *EventHub) {
eventHub.eventRoutine()
}
func New() (eventHub *EventHub) {
eventHub = &EventHub{
cmdChan: make(chan *tCmd, cmdChanLen),
chanMap: make(map[chan *EventInfo]*tRegisterInfo),
typeChanMap: make(map[string]map[chan *EventInfo]bool),
eventCatMap: make(map[string]IEventProducer),
}
utils.CallFuncAsync(func() {
eventRoutine(eventHub)
})
return eventHub
}
func (e *EventHub) eventRoutine() {
for {
cmd, ok := <-e.cmdChan
if ok {
switch cmd.cmdType {
case cmdRegisterConsumer:
regisgerInfo := cmd.data.(*tRegisterInfo)
e.chanMap[regisgerInfo.notifyChan] = regisgerInfo
for _, eventType := range regisgerInfo.eventTypeList {
if e.typeChanMap[eventType] == nil {
e.typeChanMap[eventType] = make(map[chan *EventInfo]bool)
}
e.typeChanMap[eventType][regisgerInfo.notifyChan] = true
}
case cmdUnregisterConsumer:
unregisgerInfo := cmd.data.(*tUnregisterInfo)
e.removeChan(unregisgerInfo.notifyChan)
close(unregisgerInfo.closeChan)
case cmdNewEvent:
eventInfo := cmd.data.(*EventInfo)
typeList := []string{allEventType, eventInfo.Type}
tmpChanMap := make(map[chan *EventInfo]*tRegisterInfo)
for _, eventType := range typeList {
for notifyChan := range e.typeChanMap[eventType] {
tmpChanMap[notifyChan] = e.chanMap[notifyChan]
}
}
for notifyChan, registerInfo := range tmpChanMap {
eventCategory, _ := splitEventType(eventInfo.Type)
if eventProducer := e.getEventProducer(eventCategory); eventProducer != nil {
if eventProducer.IsCriteriaMatch(eventInfo, registerInfo.criteria) {
notifyChan <- eventInfo
e.removeChan(notifyChan)
}
} else {
globals.SugarLogger.Warnf("eventRoutine, eventCategory:%s producer is nil", eventCategory)
}
}
}
} else {
break
}
}
}
func (e *EventHub) removeChan(notifyChann chan *EventInfo) {
typeList := e.chanMap[notifyChann].eventTypeList
for _, eventType := range typeList {
delete(e.typeChanMap[eventType], notifyChann)
}
delete(e.chanMap, notifyChann)
}
func (e *EventHub) Close() {
close(e.cmdChan)
}
func (e *EventHub) RegisterProducer(eventCategory string, producer IEventProducer) (err error) {
e.locker.Lock()
if e.eventCatMap[eventCategory] != nil {
err = fmt.Errorf("eventCategory:%s已经被注册了", eventCategory)
} else {
e.eventCatMap[eventCategory] = producer
}
e.locker.Unlock()
return err
}
// 正常不应该是动态的经常调用注册与反注册Producer只是退出进程前礼貌的调用
func (e *EventHub) UnregisterProducer(eventCategory string) (err error) {
e.locker.Lock()
if e.eventCatMap[eventCategory] == nil {
err = fmt.Errorf("eventCategory:%s没有注册", eventCategory)
} else {
delete(e.eventCatMap, eventCategory)
}
e.locker.Unlock()
return err
}
func (e *EventHub) registerConsumer(eventCategory string, eventTypeList []string, criteria interface{}) (notifyChan chan *EventInfo) {
if len(eventTypeList) == 0 {
eventTypeList = []string{allEventType}
}
realEventTypeList := make([]string, len(eventTypeList))
for index, eventType := range eventTypeList {
realEventTypeList[index] = composeEventType(eventCategory, eventType)
}
info := &tRegisterInfo{
eventCategory: eventCategory,
eventTypeList: eventTypeList,
notifyChan: make(chan *EventInfo, 1),
criteria: criteria,
}
e.cmdChan <- &tCmd{
cmdType: cmdRegisterConsumer,
data: info,
}
return info.notifyChan
}
func (e *EventHub) unregisterConsumer(notifyChan chan *EventInfo) {
info := &tUnregisterInfo{
notifyChan: notifyChan,
closeChan: make(chan struct{}),
}
e.cmdChan <- &tCmd{
cmdType: cmdUnregisterConsumer,
data: info,
}
<-info.closeChan
}
func (e *EventHub) PostNewEvent(eventCategory string, event *EventInfo) {
utils.CallFuncAsync(func() {
newEvent := *event
newEvent.Type = composeEventType(eventCategory, event.Type)
e.cmdChan <- &tCmd{
cmdType: cmdNewEvent,
data: &newEvent,
}
})
}
func (e *EventHub) GetEvent(eventCategory string, eventTypeList []string, criteria interface{}, waitTime time.Duration) (event *EventInfo, err error) {
notifyChan := e.registerConsumer(eventCategory, eventTypeList, criteria)
pollingDuration := defPollingDuration
if waitTime != 0 {
pollingDuration = waitTime
if globals.IsProductEnv() {
if pollingDuration > maxPollingDuration {
pollingDuration = maxPollingDuration
} else if pollingDuration < minPollingDuration {
pollingDuration = minPollingDuration
}
}
}
globals.SugarLogger.Debugf("GetEvent pollingDuration:%d seconds", pollingDuration/time.Second)
timer := time.NewTimer(pollingDuration)
select {
case tmpEvent, ok := <-notifyChan:
timer.Stop()
if ok {
event = tmpEvent
}
case <-timer.C:
e.unregisterConsumer(notifyChan)
}
close(notifyChan)
return event, err
}
func (e *EventHub) getEventProducer(eventCategory string) IEventProducer {
e.locker.RLock()
defer e.locker.RUnlock()
return e.eventCatMap[eventCategory]
}
func composeEventType(eventCategory, eventType string) string {
return eventCategory + "/" + eventType
}
func splitEventType(eventType string) (eventCategory, pureEventType string) {
typeList := strings.Split(eventType, "/")
eventCategory = typeList[0]
if len(typeList) > 1 {
pureEventType = typeList[1]
} else {
globals.SugarLogger.Warnf("splitEventType eventType:%s", eventType)
}
return eventCategory, pureEventType
}

View File

@@ -0,0 +1,76 @@
package syseventhub
import (
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/business/jxutils/eventhub"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
EventCategory = "sys"
EventTypeWXToken = "wxtoken"
)
type Hub struct {
eventHub *eventhub.EventHub
}
type Criteria struct {
}
type WXTokenInfo struct {
IsNew bool `json:"isNew"`
Token string `json:"token"`
}
var (
SysEventHub *Hub
)
func init() {
SysEventHub = New()
}
func New() (hub *Hub) {
hub = &Hub{
eventHub: eventhub.New(),
}
hub.eventHub.RegisterProducer(EventCategory, hub)
return hub
}
func (h *Hub) IsCriteriaMatch(eventInfo *eventhub.EventInfo, criteria interface{}) bool {
return true
}
func (h *Hub) OnNewWXToken(token string) {
h.eventHub.PostNewEvent(EventCategory, &eventhub.EventInfo{
Type: EventTypeWXToken,
Data: token,
})
}
func (h *Hub) GetWXToken(oldToken string, waitTime time.Duration) (tokenInfo *WXTokenInfo) {
token := api.WeixinAPI.CBGetToken()
if token != oldToken {
tokenInfo = &WXTokenInfo{
IsNew: false,
Token: token,
}
} else {
eventInfo, err := h.eventHub.GetEvent(EventCategory, []string{EventTypeWXToken}, nil, waitTime)
if err == nil && eventInfo != nil {
tokenInfo = &WXTokenInfo{
IsNew: true,
Token: eventInfo.Data.(string),
}
}
}
globals.SugarLogger.Debugf("GetWXToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
return tokenInfo
}

View File

@@ -270,9 +270,7 @@ func (s SkuList) Swap(i, j int) {
func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err error) {
response, err := http.Get(fileURL)
if err == nil {
defer func() {
response.Body.Close()
}()
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
if bodyData, err = ioutil.ReadAll(response.Body); err == nil {
fileMD5 = fmt.Sprintf("%X", md5.Sum(bodyData))

View File

@@ -54,10 +54,12 @@ func PrintOrderByOrder(ctx *jxcontext.Context, order *model.GoodsOrder) (printRe
return &partner.PrinterStatus{
PrintResult: partner.PrintResultNoPrinter,
}, nil
} else {
if globals.EnableStoreWrite {
printResult, err = handler.PrintOrder(ctx, store, order)
}
}
if globals.EnableStoreWrite {
printResult, err = handler.PrintOrder(ctx, store, order)
}
if err == nil {
dao.SetOrderPrintFlag(db, ctx.GetUserName(), order.VendorOrderID, order.VendorID, true)
}
}
if err != nil {

View File

@@ -1,8 +1,14 @@
package tasks
import (
"fmt"
"io/ioutil"
"net/http"
"time"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/jx-callback/business/jxutils/eventhub/syseventhub"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
@@ -22,8 +28,11 @@ const (
dingdingTokenExpires = 7200 * time.Second
elmTokenExpires = 20 * 24 * 3600 * time.Second
weimobTokenExpires = 7200 * time.Second
maxRefreshGap = 5 * 60 * time.Second
yilianyunTokenExpires = 30 * 24 * 3600 * time.Second
maxRefreshGap = 5 * 60 * time.Second
errRefreshGap = 10 * time.Second
minRefreshGap = 1 * time.Second
)
type ElmTokenForCompatible struct {
@@ -36,13 +45,21 @@ type ElmTokenForCompatible struct {
Success bool `json:"success"`
}
type CallResult struct {
Code string `json:"code"`
Desc string `json:"desc"`
Data string `json:"data"`
}
func RefreshConfig(configKey string, expiresTime time.Duration, configGetter func() (string, string), configSetter func(value string)) error {
sleepGap := expiresTime / 10
needRefreshGap := expiresTime * 8 / 10
sleepGap := expiresTime / 10
if sleepGap > maxRefreshGap {
sleepGap = maxRefreshGap
}
refreshFunc := func() (string, error) {
refreshFunc := func() (sleepDuration time.Duration) {
sleepDuration = sleepGap
curConfig := &legacymodel.Config{
Thirdparty: configKey,
}
@@ -52,7 +69,7 @@ func RefreshConfig(configKey string, expiresTime time.Duration, configGetter fun
if err := db.Read(curConfig, "Thirdparty"); err != nil {
if err != orm.ErrNoRows {
globals.SugarLogger.Errorf("db error:%v, curConfig:%v", err, curConfig)
return "", err
return errRefreshGap
}
handleType = 2
} else {
@@ -65,15 +82,17 @@ func RefreshConfig(configKey string, expiresTime time.Duration, configGetter fun
if curConfig.Token, curConfig.Date = configGetter(); curConfig.Token == "" {
if beego.BConfig.RunMode == "prod" {
globals.SugarLogger.Errorf("RefreshConfig %s get empty token", configKey)
sleepDuration = errRefreshGap
} else {
globals.SugarLogger.Infof("RefreshConfig %s get empty token", configKey)
}
return "", nil
return sleepDuration
}
globals.SugarLogger.Debugf("RefreshConfig refresh %s, value:%s", configKey, curConfig.Token)
configSetter(curConfig.Token)
if curConfig.Date == "" {
curConfig.Date = utils.GetCurTimeStr()
} else {
sleepDuration = expiresTime - time.Now().Sub(utils.Str2Time(curConfig.Date))
}
var num int64
var err error
@@ -85,88 +104,103 @@ func RefreshConfig(configKey string, expiresTime time.Duration, configGetter fun
}
if err != nil || num == 0 {
globals.SugarLogger.Errorf("db error:%v, num:%d, curConfig:%v", err, num, curConfig)
return "", err
}
configSetter(curConfig.Token)
}
return curConfig.Token, nil
if sleepDuration < minRefreshGap {
sleepDuration = minRefreshGap
} else if sleepDuration > maxRefreshGap {
sleepDuration = maxRefreshGap
}
return sleepDuration
}
token, err := refreshFunc() // 这样写的目的是强制第一次调用时要刷新一次
if err == nil {
if token != "" {
configSetter(token)
utils.CallFuncAsync(func() {
for {
sleepGap := refreshFunc()
time.Sleep(sleepGap)
}
utils.CallFuncAsync(func() {
for {
time.Sleep(sleepGap)
refreshFunc()
})
return nil
}
func RefreshWeixinToken() (err error) {
if api.WeixinAPI != nil {
err = RefreshConfig("wechat", weixinTokenExpires, func() (token string, expireTimeStr string) {
globals.SugarLogger.Debugf("RefreshWeixinToken RunMode:%s", beego.BConfig.RunMode)
if globals.IsProductEnv() {
if globals.IsMainProductEnv() {
if tokenInfo, err := api.WeixinAPI.CBRetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshWeixinToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
token = tokenInfo.AccessToken
} else {
globals.SugarLogger.Errorf("RefreshWeixinToken RefreshToken failed with error:%v", err)
}
} else {
if tokenInfo := getWXTokenFromRemote(api.WeixinAPI.CBGetToken()); tokenInfo != nil {
expireTimeStr = utils.Time2Str(time.Now().Add(-weixinTokenExpires))
token = tokenInfo.Token
}
}
}
return token, expireTimeStr
}, func(value string) {
globals.SugarLogger.Debugf("RefreshWeixinToken setter value:%s", value)
syseventhub.SysEventHub.OnNewWXToken(value)
api.WeixinAPI.CBSetToken(value)
})
}
return err
}
func RefreshElmToken() (err error) {
if api.ElmAPI != nil {
err = RefreshConfig("eleme", elmTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.ElmAPI.RefreshTokenIndividual(); err == nil {
tokenInfo2 := &ElmTokenForCompatible{
Error: "",
ErrorDescription: "",
AccessToken: tokenInfo.AccessToken,
TokenType: tokenInfo.TokenType,
Expires: tokenInfo.ExpiresIn,
RefreshToken: "",
Success: true,
}
return string(utils.MustMarshal(tokenInfo2)), ""
}
}
return "", ""
}, func(value string) {
var tokenInfo ElmTokenForCompatible
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.ElmAPI.SetToken(tokenInfo.AccessToken)
}
})
}
return err
}
func RefreshWeixinToken() error {
return RefreshConfig("wechat", weixinTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshWeixinToken RunMode:%s", beego.BConfig.RunMode)
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.WeixinAPI.CBRetrieveToken(); err == nil {
globals.SugarLogger.Debugf("RefreshWeixinToken tokenInfo:%s", utils.Format4Output(tokenInfo, true))
return tokenInfo.AccessToken, ""
} else {
globals.SugarLogger.Errorf("RefreshWeixinToken RefreshToken failed with error:%v", err)
}
}
return "", ""
}, func(value string) {
api.WeixinAPI.CBSetToken(value)
})
}
func RefreshElmToken() error {
return RefreshConfig("eleme", elmTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.ElmAPI.RefreshTokenIndividual(); err == nil {
tokenInfo2 := &ElmTokenForCompatible{
Error: "",
ErrorDescription: "",
AccessToken: tokenInfo.AccessToken,
TokenType: tokenInfo.TokenType,
Expires: tokenInfo.ExpiresIn,
RefreshToken: "",
Success: true,
func RefreshWeimobToken() (err error) {
if api.WeimobAPI != nil {
err = RefreshConfig("weimob", weimobTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.WeimobAPI.RefreshTokenByRefreshToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), utils.Time2Str(time.Now().Add((time.Duration(tokenInfo.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second))
}
return string(utils.MustMarshal(tokenInfo2)), ""
}
}
return "", ""
}, func(value string) {
var tokenInfo ElmTokenForCompatible
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.ElmAPI.SetToken(tokenInfo.AccessToken)
}
})
return "", ""
}, func(value string) {
var tokenInfo *weimobapi.TokenInfo
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.WeimobAPI.SetToken(tokenInfo)
}
})
}
return err
}
func RefreshWeimobToken() error {
return RefreshConfig("weimob", weimobTokenExpires, func() (string, string) {
if beego.BConfig.RunMode == "prod" {
if tokenInfo, err := api.WeimobAPI.RefreshTokenByRefreshToken(); err == nil {
return string(utils.MustMarshal(tokenInfo)), utils.Time2Str(time.Now().Add((time.Duration(tokenInfo.ExpiresIn) - weimobTokenExpires/time.Second) * time.Second))
}
}
return "", ""
}, func(value string) {
var tokenInfo *weimobapi.TokenInfo
err := utils.UnmarshalUseNumber([]byte(value), &tokenInfo)
if err == nil {
api.WeimobAPI.SetToken(tokenInfo)
}
})
}
func RefreshDingDingToken() error {
func RefreshDingDingToken() (err error) {
api.DingDingAPI.RetrieveToken()
return RefreshConfig("dingding", dingdingTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshDingDingToken RunMode:%s", beego.BConfig.RunMode)
@@ -195,7 +229,7 @@ func SaveWeimobToken(token *weimobapi.TokenInfo) (err error) {
return dao.CreateOrUpdate(db, config)
}
func RefreshYilianyunToken() error {
func RefreshYilianyunToken() (err error) {
return RefreshConfig("yilianyun", yilianyunTokenExpires, func() (string, string) {
globals.SugarLogger.Debugf("RefreshYilianyunToken RunMode:%s", beego.BConfig.RunMode)
if beego.BConfig.RunMode == "prod" {
@@ -214,3 +248,46 @@ func RefreshYilianyunToken() error {
}
})
}
func getWXTokenFromRemote(oldToken string) (tokenInfo *syseventhub.WXTokenInfo) {
if IsGetWXTokenFromRemote() {
for {
waitSecond := 5 * 60
globals.SugarLogger.Debugf("getWXTokenFromProd1")
response, err := http.Get(fmt.Sprintf("%s?accessKey=%s&oldToken=%s&waitSecond=%d", globals.GetWeixinTokenURL, globals.GetWeixinTokenKey, oldToken, waitSecond))
globals.SugarLogger.Debugf("getWXTokenFromProd2 error:%v", err)
if err == nil {
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
data, err2 := ioutil.ReadAll(response.Body)
if err = err2; err == nil {
var result CallResult
if err = utils.UnmarshalUseNumber(data, &result); err == nil {
if result.Code == "0" {
if result.Data != "" {
if err = utils.UnmarshalUseNumber([]byte(result.Data), &tokenInfo); err == nil && tokenInfo != nil {
globals.SugarLogger.Debugf("getWXTokenFromProd:%s", utils.Format4Output(tokenInfo, false))
break
}
}
} else {
err = fmt.Errorf("return code is:%s", result.Code)
}
}
}
} else {
err = platformapi.ErrHTTPCodeIsNot200
}
}
globals.SugarLogger.Infof("getWXTokenFromProd failed with error:%v", err)
if err != nil {
time.Sleep(errRefreshGap)
}
}
}
return tokenInfo
}
func IsGetWXTokenFromRemote() bool {
return !globals.IsMainProductEnv() && globals.GetWeixinTokenKey != "" && globals.GetWeixinTokenURL != ""
}

View File

@@ -26,7 +26,7 @@ const (
)
const (
MaxTaskNameLen = 30
MaxTaskNameLen = 50
)
var (

View File

@@ -15,12 +15,10 @@ import (
)
const (
// WX_TO_ORDER_PAGE_URL = "http://www.jxc4.com/jx/h5/#/?jxStoreId="
// WX_TO_SHOW_COMMENTS_DETAIL_URL = "http://www.jxc4.com/jx/h5/#/assess-list?jxStoreId=" //展示差评详情的页面
WX_TO_ORDER_PAGE_URL = "http://wx.jxc4.com/#/?jxStoreId="
WX_TO_STORE_SKU_PAGE_URL = "http://wx.jxc4.com/#/shop?jxStoreId="
WX_TO_SHOW_COMMENTS_DETAIL_URL = "http://wx.jxc4.com/#/assess-list?jxStoreId=" //展示差评详情的页面
WX_TO_SHOW_MSG = "http://wx.jxc4.com/#/message-detail?msgID=%d&msgStatusID=%d"
WX_TO_ORDER_PAGE_URL = "/#/?jxStoreId="
WX_TO_STORE_SKU_PAGE_URL = "/#/shop?jxStoreId="
WX_TO_SHOW_COMMENTS_DETAIL_URL = "/#/assess-list?jxStoreId=" //展示差评详情的页面
WX_TO_SHOW_MSG = "/#/message-detail?msgID=%d&msgStatusID=%d"
WX_MINI_TO_ORDER_PAGE_URL = "pages/order-manager/main"
WX_MINI_TO_STORE_SKU_PAGE_URL = "pages/goods-manager/main"
@@ -205,7 +203,7 @@ func NotifyNewOrder(order *model.GoodsOrder) (err error) {
},
}
storeID := jxutils.GetSaleStoreIDFromOrder(order)
return SendMsgToStore(storeID, WX_NEWORDER_TEMPLATE_ID, fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
return SendMsgToStore(storeID, WX_NEWORDER_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
}
func NotifyWaybillStatus(bill *model.Waybill, order *model.GoodsOrder, isBillAlreadyCandidate bool) error {
@@ -340,7 +338,7 @@ func PushJDBadCommentToWeiXin(comment *legacymodel.JxBadComments) (err error) {
},
}
storeID := int(utils.Str2Int64(comment.Jxstoreid))
return SendMsgToStore(storeID, WX_BAD_COMMENT_PUSH_TEMPLATE_ID, fmt.Sprintf("%s%d", WX_TO_SHOW_COMMENTS_DETAIL_URL, storeID), WX_MINI_TO_SHOW_COMMENTS_DETAIL_URL, data)
return SendMsgToStore(storeID, WX_BAD_COMMENT_PUSH_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_SHOW_COMMENTS_DETAIL_URL, storeID), WX_MINI_TO_SHOW_COMMENTS_DETAIL_URL, data)
}
func NotifySaleBill(storeID int, title, shopName, fileURL string) (err error) {
@@ -379,7 +377,7 @@ func NotifySaleBill(storeID int, title, shopName, fileURL string) (err error) {
func NotifyStoreOpRequestStatus(isAccepted bool, storeID, nameID int, spuName string, originalUnitPrice, unitPrice int, rejectReason string) (err error) {
globals.SugarLogger.Debugf("NotifyStoreOpRequestStatus isAccepted:%t, storeID:%d, nameID:%d, spuName:%s, originalUnitPrice:%d, unitPrice:%d, rejectReason:%s", isAccepted, storeID, nameID, spuName, originalUnitPrice, unitPrice, rejectReason)
templateID := ""
fileURL := fmt.Sprintf("%s%d", WX_TO_STORE_SKU_PAGE_URL, storeID)
fileURL := globals.WxBackstageHost + fmt.Sprintf("%s%d", WX_TO_STORE_SKU_PAGE_URL, storeID)
data := make(map[string]interface{})
if isAccepted {
templateID = WX_CHANGE_APPROVED_TEMPLATE_ID
@@ -426,7 +424,7 @@ func NotifyStoreOpRequestStatus(isAccepted bool, storeID, nameID int, spuName st
func NotifyStoreMessage(storeID, msgID, msgStatusID int, title, content string) (err error) {
globals.SugarLogger.Debugf("NotifyStoreMessage storeID:%d, msgID:%d, title:%s, content:%s", storeID, msgID, title, content)
templateID := WX_NORMAL_STORE_MSG_TEMPLATE_ID
fileURL := fmt.Sprintf(WX_TO_SHOW_MSG, msgID, msgStatusID)
fileURL := globals.WxBackstageHost + fmt.Sprintf(WX_TO_SHOW_MSG, msgID, msgStatusID)
data := map[string]interface{}{
"first": map[string]interface{}{
"value": content,

View File

@@ -1,5 +1,11 @@
package model
import (
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
)
// VendorIDJD, VendorIDMTWM与VendorIDELM的定义和老系统是兼容的
const (
VendorIDUnknown = -1
@@ -42,6 +48,7 @@ var (
VendorIDYiLianYun: "Yilianyun",
VendorIDZhongWu: "ZhongWu",
}
VendorChineseNames = map[int]string{
VendorIDJD: "京东到家",
VendorIDMTWM: "美团外卖",
@@ -58,6 +65,21 @@ var (
VendorIDZhongWu: "中午云",
}
PurchaseVendorInfo = map[int]map[string]interface{}{
VendorIDJD: map[string]interface{}{
"chineseName": VendorChineseNames[VendorIDJD],
"userApplyCancelWaitMinute": jdapi.UserApplyCancelWaitMinute,
},
VendorIDMTWM: map[string]interface{}{
"chineseName": VendorChineseNames[VendorIDMTWM],
"userApplyCancelWaitMinute": mtwmapi.UserApplyCancelWaitMinute,
},
VendorIDEBAI: map[string]interface{}{
"chineseName": VendorChineseNames[VendorIDEBAI],
"userApplyCancelWaitMinute": ebaiapi.UserApplyCancelWaitMinute,
},
}
ShopChineseNames = map[int]string{
VendorIDJD: "京西菜市",
VendorIDMTWM: "美好菜市",
@@ -215,7 +237,7 @@ const (
WaybillStatusEndBegin = 100 // 以下的状态就是结束状态
WaybillStatusDelivered = 105 // todo 这个应该改为110与订单对应
WaybillStatusCanceled = 115
WaybillStatusFailed = 120 // todo 这个应该要去掉
WaybillStatusFailed = 120 // 这个状态存在的意义是区分于WaybillStatusCanceled比如达达平台在这种状态下再次创建运单的方式不一样
WaybillStatusNeverSend = 125 // 这个状态指的是平台方不愿意配送门店自己想办法。与WaybillStatusAcceptCanceled不一样WaybillStatusAcceptCanceled可能之后还会尝试配送
)
@@ -240,8 +262,20 @@ const (
OrderDeliveryFlagMaskScheduleDisabled = 1 // 禁止三方配送调度
OrderDeliveryFlagMaskPurcahseDisabled = 2 // 购物平台已不配送(一般为门店配送类型本身为自配送,或已经转自配送)
)
const (
OrderFlagMaskPrinted = 1 // 已经打印
OrderFlagMaskUserApplyCancel = 6
OrderFlagAgreeUserApplyCancel = 2
OrderFlagRefuseUserApplyCancel = 6
OrderFlagMaskFailedGetGoods = 24
OrderFlagAgreeFailedGetGoods = 8
OrderFlagRefuseFailedGetGoods = 24
OrderFlagMaskFailedDeliver = 32
OrderFlagMaskCallPMCourier = 64 // 取货失败后召唤平台配送
)
func IsPurchaseVendorExist(vendorID int) bool {

View File

@@ -59,10 +59,6 @@ func GetDB() *DaoDB {
return &DaoDB{Db: orm.NewOrm()}
}
func WrapDB(ormDb orm.Ormer) *DaoDB {
return &DaoDB{Db: ormDb}
}
func Begin(db *DaoDB) (err error) {
if db.transactionLevel == 0 {
err = db.Db.Begin()

View File

@@ -25,27 +25,29 @@ func GetStoreOrderAfterTime(db *DaoDB, storeID int, orderTime time.Time, lastOrd
return orderList, GetRows(db, &orderList, sql, sqlParams...)
}
func SetOrderPrintFlag(db *DaoDB, vendorOrderID string, vendorID int, isPrinted bool) (err error) {
var (
sql string
sqlParams []interface{}
)
func SetOrderPrintFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, isPrinted bool) (err error) {
if isPrinted {
sql = `
UPDATE goods_order
SET flag = flag | ?
WHERE vendor_order_id = ? AND vendor_id = ?
`
sqlParams = append(sqlParams, model.OrderFlagMaskPrinted)
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, model.OrderFlagMaskPrinted)
} else {
sql = `
UPDATE goods_order
SET flag = flag & ?
WHERE vendor_order_id = ? AND vendor_id = ?
`
sqlParams = append(sqlParams, ^model.OrderFlagMaskPrinted)
err = SetOrderFlag(db, userName, vendorOrderID, vendorID, ^int8(model.OrderFlagMaskPrinted))
}
sqlParams = append(sqlParams, vendorOrderID, vendorID)
_, err = ExecuteSQL(db, sql, sqlParams...)
return err
}
func SetOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int8) (err error) {
_, err = ExecuteSQL(db, `
UPDATE goods_order
SET flag = flag | ?
WHERE vendor_order_id = ? AND vendor_id = ?
`, flag, vendorOrderID, vendorID)
return err
}
func ClearOrderFlag(db *DaoDB, userName string, vendorOrderID string, vendorID int, flag int8) (err error) {
_, err = ExecuteSQL(db, `
UPDATE goods_order
SET flag = flag & ?
WHERE vendor_order_id = ? AND vendor_id = ?
`, flag, vendorOrderID, vendorID)
return err
}

View File

@@ -32,7 +32,8 @@ type GoodsOrder struct {
Status int `json:"status"` // 参见OrderStatus*相关的常量定义
VendorStatus string `orm:"size(255)" json:"-"`
LockStatus int `json:"lockStatus"`
OrderSeq int `json:"orderSeq"` // 门店订单序号
LockStatusTime time.Time `orm:"type(datetime);null" json:"-"` // last lock status time
OrderSeq int `json:"orderSeq"` // 门店订单序号
BuyerComment string `orm:"size(255)" json:"buyerComment"`
BusinessType int `json:"businessType"`
ExpectedDeliveredTime time.Time `orm:"type(datetime)" json:"expectedDeliveredTime"` // 预期送达时间

View File

@@ -67,7 +67,7 @@ func routinueFunc() {
registerMsg := msg.MsgData.(*MsgOp)
delete(channelMap[registerMsg.StoreID], registerMsg.Chan2Listen)
close(registerMsg.Chan2Close)
case ServerMsgNewOrder:
case ServerMsgNewOrder, ServerMsgKeyOrderStatusChanged:
globals.SugarLogger.Debugf("msghub routinueFunc, msg:%s", utils.Format4Output(msg, false))
utils.CallFuncAsync(func() {
for chan2Send := range channelMap[msg.StoreID] {
@@ -125,10 +125,12 @@ func GetMsg(ctx *jxcontext.Context, storeID int, lastOrderTime time.Time, lastOr
pollingDuration := defPollingDuration
if waitingSecond != 0 {
pollingDuration = time.Duration(waitingSecond) * time.Second
if pollingDuration > maxPollingDuration {
pollingDuration = maxPollingDuration
} else if pollingDuration < minPollingDuration {
pollingDuration = minPollingDuration
if globals.IsProductEnv() {
if pollingDuration > maxPollingDuration {
pollingDuration = maxPollingDuration
} else if pollingDuration < minPollingDuration {
pollingDuration = minPollingDuration
}
}
}
timer := time.NewTimer(pollingDuration)
@@ -171,7 +173,7 @@ func OnKeyOrderStatusChanged(order *model.GoodsOrder) {
msgChan <- &ServerMsg{
Type: ServerMsgKeyOrderStatusChanged,
StoreID: jxutils.GetSaleStoreIDFromOrder(order),
MsgData: 1,
MsgData: order,
// MsgData: []*model.GoodsOrderExt{
// &model.GoodsOrderExt{
// GoodsOrder: *order,

View File

@@ -15,7 +15,6 @@ import (
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
)
const (
@@ -70,9 +69,9 @@ func (c *DeliveryHandler) onWaybillMsg(msg *dadaapi.CallbackMsg) (retVal *dadaap
order.Status = model.WaybillStatusDelivering
case dadaapi.OrderStatusFinished:
order.Status = model.WaybillStatusDelivered
case dadaapi.OrderStatusCanceled:
case dadaapi.OrderStatusCanceled, dadaapi.OrderStatusExpired:
order.Status = model.WaybillStatusCanceled
case dadaapi.OrderStatusExpired, dadaapi.OrderStatusAddOrderFailed:
case dadaapi.OrderStatusAddOrderFailed:
order.Status = model.WaybillStatusFailed
default:
order.Status = model.WaybillStatusUnknown
@@ -102,7 +101,7 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *dadaapi.CallbackMsg) (retVal
// IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := orm.NewOrm()
db := dao.GetDB()
deliveryFee, addFee, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
if err != nil {
return nil, err
@@ -133,17 +132,18 @@ func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.
if globals.EnableStoreWrite {
// 达达要求第二次创建运单,调用函数不同。所以查找两天内有无相同订单号的运单
var lists []orm.ParamsList
num, err2 := db.Raw(`
SELECT vendor_waybill_id
FROM waybill
WHERE waybill_created_at > DATE_ADD(NOW(), interval -2 day)
AND vendor_order_id = ?
AND waybill_vendor_id = ?
`, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), model.VendorIDDada).ValuesList(&lists)
var waybillList []*model.Waybill
err2 := dao.GetRows(db, &waybillList, `
SELECT *
FROM waybill
WHERE waybill_created_at > DATE_ADD(NOW(), interval -2 day)
AND vendor_order_id = ?
AND waybill_vendor_id = ?
ORDER BY id DESC
`, jxutils.ComposeUniversalOrderID(order.VendorOrderID, order.VendorID), model.VendorIDDada)
var result *dadaapi.CreateOrderResponse
if err = err2; err == nil && num > 0 {
globals.SugarLogger.Debugf("CreateWaybill orderID:%s num=%d use ReaddOrder", order.VendorOrderID, num)
if err = err2; err == nil && len(waybillList) > 0 && waybillList[0].Status != model.WaybillStatusFailed {
globals.SugarLogger.Debugf("CreateWaybill orderID:%s len(waybillList)=%d use ReaddOrder", order.VendorOrderID, len(waybillList))
result, err = api.DadaAPI.ReaddOrder(billParams, addParams)
} else {
if err != nil {
@@ -192,7 +192,7 @@ func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int,
return err
}
func (c *DeliveryHandler) getDataCityCodeFromOrder(order *model.GoodsOrder, db orm.Ormer) (retVal string, err error) {
func (c *DeliveryHandler) getDataCityCodeFromOrder(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
jxStoreID := jxutils.GetSaleStoreIDFromOrder(order)
sql := `
SELECT t2.tel_code
@@ -200,11 +200,10 @@ func (c *DeliveryHandler) getDataCityCodeFromOrder(order *model.GoodsOrder, db o
JOIN place t2 on t1.city_code = t2.code
WHERE t1.id = ?
`
db2 := dao.WrapDB(db)
codeInfo := &struct {
TelCode string
}{}
if err = dao.GetRow(db2, codeInfo, sql, jxStoreID); err != nil {
if err = dao.GetRow(db, codeInfo, sql, jxStoreID); err != nil {
globals.SugarLogger.Errorf("GetDataCityCodeFromOrder can not find store info for vendorID:%d, store:%s, error:%v", order.VendorID, order.VendorStoreID, err)
if err == nil {
err = ErrCanNotFindDadaCityCode
@@ -214,11 +213,10 @@ func (c *DeliveryHandler) getDataCityCodeFromOrder(order *model.GoodsOrder, db o
return codeInfo.TelCode, nil
}
func (c *DeliveryHandler) getDadaShopID(order *model.GoodsOrder, db orm.Ormer) (retVal string, err error) {
func (c *DeliveryHandler) getDadaShopID(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
saleStoreID := jxutils.GetSaleStoreIDFromOrder(order)
db2 := dao.WrapDB(db)
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db2, saleStoreID, model.VendorIDDada)
if err = err2; err != nil && err != orm.ErrNoRows {
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db, saleStoreID, model.VendorIDDada)
if err = err2; err != nil && !dao.IsNoRowsError(err) {
return "", err
}
if len(storeCourierList) == 0 {

View File

@@ -12,7 +12,6 @@ import (
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"github.com/astaxie/beego/orm"
)
const (
@@ -57,10 +56,10 @@ func CallCreateWaybillPolicy(policy partner.CreateWaybillPolicy, refDeliveryFee,
return nil
}
func CalculateOrderDeliveryFee(order *model.GoodsOrder, billTime time.Time, db orm.Ormer) (deliveryFee, addFee int64, err error) {
func CalculateOrderDeliveryFee(order *model.GoodsOrder, billTime time.Time, db *dao.DaoDB) (deliveryFee, addFee int64, err error) {
globals.SugarLogger.Debugf("CalculateOrderDeliveryFee orderID:%s", order.VendorOrderID)
if db == nil {
db = orm.NewOrm()
db = dao.GetDB()
}
jxStoreID := jxutils.GetSaleStoreIDFromOrder(order)
var lng, lat float64
@@ -69,8 +68,7 @@ func CalculateOrderDeliveryFee(order *model.GoodsOrder, billTime time.Time, db o
Lng int
Lat int
}{}
db2 := dao.WrapDB(db)
if err = dao.GetRow(db2, priceInfo, `
if err = dao.GetRow(db, priceInfo, `
SELECT t2.mtps_price price, t1.lng, t1.lat
FROM store t1
JOIN place t2 ON t1.city_code = t2.code

View File

@@ -115,7 +115,7 @@ func (c *DeliveryHandler) callbackMsg2Waybill(msg *mtpsapi.CallbackOrderMsg) (re
// IDeliveryPlatformHandler
func (c *DeliveryHandler) CreateWaybill(order *model.GoodsOrder, policy partner.CreateWaybillPolicy) (bill *model.Waybill, err error) {
db := orm.NewOrm()
db := dao.GetDB()
deliveryFee, addFee, err := delivery.CalculateOrderDeliveryFee(order, time.Now(), db)
if err == nil {
if err = delivery.CallCreateWaybillPolicy(policy, deliveryFee, addFee, deliveryFee, order, model.VendorIDMTPS); err != nil {
@@ -201,16 +201,15 @@ func (c *DeliveryHandler) CancelWaybill(bill *model.Waybill, cancelReasonID int,
return nil
}
func (c *DeliveryHandler) getDeliveryID(order *model.GoodsOrder, db orm.Ormer) (retVal int64, err error) {
func (c *DeliveryHandler) getDeliveryID(order *model.GoodsOrder, db *dao.DaoDB) (retVal int64, err error) {
// jxorder表当前已经有50多万条记录了加100万避免冲突
// 508505
return order.ID + 1000000, nil
}
func (c *DeliveryHandler) getMTPSShopID(order *model.GoodsOrder, db orm.Ormer) (retVal string, err error) {
func (c *DeliveryHandler) getMTPSShopID(order *model.GoodsOrder, db *dao.DaoDB) (retVal string, err error) {
saleStoreID := jxutils.GetSaleStoreIDFromOrder(order)
db2 := dao.WrapDB(db)
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db2, saleStoreID, model.VendorIDMTPS)
storeCourierList, err2 := dao.GetOpenedStoreCouriersByStoreID(db, saleStoreID, model.VendorIDMTPS)
if err = err2; err != nil && err != orm.ErrNoRows {
return "", err
}

View File

@@ -166,6 +166,7 @@ func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName strin
shopID = store.ID
}
store2, err2 := p.ReadStore(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

View File

@@ -387,16 +387,13 @@ func (p *PurchaseHandler) RefreshStoresAllSkusID(ctx *jxcontext.Context, parentT
func genSkuParamsFromStoreSkuInfo(storeSku *tStoreSkuFullInfo) (params map[string]interface{}) {
price := jxutils.CaculateSkuVendorPrice(storeSku.Price, storeSku.PricePercentage)
params = map[string]interface{}{
"name": jxutils.ComposeSkuName(storeSku.Prefix, storeSku.Name, storeSku.Comment, storeSku.Unit, storeSku.SpecQuality, storeSku.SpecUnit, 0),
"status": jxSkuStatus2Ebai(jxutils.MergeSkuStatus(storeSku.SkuStatus, storeSku.Status)),
"left_num": model.MaxStoreSkuStockQty,
"sale_price": price,
"market_price": price,
"category_id": storeSku.CatEbaiID,
"cat1_id": getEbaiCat(storeSku.EbaiCat1ID, 1),
"cat2_id": getEbaiCat(storeSku.EbaiCat2ID, 2),
"cat3_id": getEbaiCat(storeSku.EbaiCat3ID, 3),
"weight": storeSku.Weight,
"name": jxutils.ComposeSkuName(storeSku.Prefix, storeSku.Name, storeSku.Comment, storeSku.Unit, storeSku.SpecQuality, storeSku.SpecUnit, 0),
"left_num": model.MaxStoreSkuStockQty,
"category_id": storeSku.CatEbaiID,
"cat1_id": getEbaiCat(storeSku.EbaiCat1ID, 1),
"cat2_id": getEbaiCat(storeSku.EbaiCat2ID, 2),
"cat3_id": getEbaiCat(storeSku.EbaiCat3ID, 3),
"weight": storeSku.Weight,
"photos": []map[string]interface{}{
map[string]interface{}{
"is_master": true,
@@ -404,6 +401,13 @@ func genSkuParamsFromStoreSkuInfo(storeSku *tStoreSkuFullInfo) (params map[strin
},
},
}
if storeSku.EbaiSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 {
params["sale_price"] = price
params["market_price"] = price
}
if storeSku.EbaiSyncStatus&(model.SyncFlagSaleMask|model.SyncFlagNewMask) != 0 {
params["status"] = jxSkuStatus2Ebai(jxutils.MergeSkuStatus(storeSku.SkuStatus, storeSku.Status))
}
// todo 饿百如果给的UPC是空要报错但如果我要删除UPC怎么弄
if storeSku.Upc != "" {
params["upc"] = storeSku.Upc

View File

@@ -15,11 +15,14 @@ import (
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/astaxie/beego"
)
const (
DefBrandID = 35247
DefJdCategoryID = 20362
DefBrandID = 35247
DefJdCategoryID = 20362
DefJdCategoryID4Jxgy = 22410 // 其他国产水果
)
type tSkuInfoExt struct {
@@ -36,6 +39,13 @@ var (
}
)
func getDefJdCategoryID() int {
if beego.BConfig.RunMode == "jxgy" {
return DefJdCategoryID4Jxgy
}
return DefJdCategoryID
}
func (p *PurchaseHandler) CreateCategory(db *dao.DaoDB, cat *model.SkuCategory, userName string) (err error) {
var jdPid int64
if cat.ParentID != 0 {
@@ -134,7 +144,7 @@ func (p *PurchaseHandler) cuSku(db *dao.DaoDB, sku *model.Sku, handler func(skuE
// shopCategories = append(shopCategories, skuInfoExt.SkuCatID)
// }
if skuInfoExt.JdCategoryID == 0 {
skuInfoExt.JdCategoryID = DefJdCategoryID
skuInfoExt.JdCategoryID = getDefJdCategoryID()
}
if skuInfoExt.BrandID == 0 {
skuInfoExt.BrandID = DefBrandID
@@ -176,6 +186,12 @@ func (p *PurchaseHandler) CreateSku(db *dao.DaoDB, sku *model.Sku, userName stri
if skuExt.IsSpu == 0 {
if globals.EnableStoreWrite {
vendorSkuID, err = api.JdAPI.AddSku(utils.Int2Str(sku.ID), skuExt.JdCategoryID, shopCategories, skuExt.BrandID, skuName, price, jxutils.IntWeight2Float(sku.Weight), []string{skuExt.Img}, jxStatus2jdStatus(sku.Status), true, addParams)
if err != nil {
if jdSkuID := jdapi.GetJdSkuIDFromError(err); jdSkuID > 0 {
vendorSkuID = utils.Int64ToStr(jdSkuID)
err = nil
}
}
}
} else {
vendorSkuID, err = p.syncSkuNameAsSpu(db, sku, skuExt, price, skuName, shopCategories, addParams)

View File

@@ -301,6 +301,7 @@ func (p *PurchaseHandler) GetStoreStatus(ctx *jxcontext.Context, vendorStoreID s
return storeStatus, err
}
// 当前京东的storeCrud消息不会在门店状态改变时发送所以意义不大先放在这里
func (c *PurchaseHandler) onStoreMsg(msg *jdapi.CallbackOrderMsg) (response *jdapi.CallbackResponse) {
var err error
if msg.StatusID == jdapi.StatusIDUpdateStore {
@@ -308,6 +309,11 @@ func (c *PurchaseHandler) onStoreMsg(msg *jdapi.CallbackOrderMsg) (response *jda
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, "")

View File

@@ -3,6 +3,8 @@ package wsc
import (
"testing"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/tasks"
"git.rosy.net.cn/jx-callback/globals"
@@ -19,7 +21,7 @@ func init() {
}
func TestGetVendorCategories(t *testing.T) {
vendorCatList, err := new(PurchaseHandler).GetVendorCategories()
vendorCatList, err := new(PurchaseHandler).GetVendorCategories(jxcontext.AdminCtx)
if err != nil {
t.Fatal(err.Error())
}