diff --git a/business/freshfood/freshfood.go b/business/freshfood/freshfood.go new file mode 100644 index 000000000..f8a389b2e --- /dev/null +++ b/business/freshfood/freshfood.go @@ -0,0 +1,89 @@ +package freshfood + +import ( + "net/http" + "net/url" + "time" + + "git.rosy.net.cn/baseapi/platform/jdapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jd/models" + "go.uber.org/zap" +) + +const ( + URL_FRESHFOOD_PARA_JDORDER = "jdOrder" + + URL_FRESHFOOD_PARA_VENDERID = "venderId" + URL_FRESHFOOD_PARA_ORDERID = "orderId" + URL_FRESHFOOD_PARA_ORDERSTATUS = "orderStatus" + URL_FRESHFOOD_PARA_ORDERSTATUSTIME = "orderStatusTime" + URL_FRESHFOOD_PARA_CITYNAME = "cityName" + + URL_FRESHFOOD_PARA_DELIVERYSTATUS = "deliveryStatus" + URL_FRESHFOOD_PARA_DLIVERYSTATUSTIME = "deliveryStatusTime" + URL_FRESHFOOD_PARA_DELIVERYCARRIERNO = "deliveryCarrierNo" + URL_FRESHFOOD_PARA_DELIVERYCARRIERNAME = "deliveryCarrierName" + URL_FRESHFOOD_PARA_DELIVERYMANNO = "deliveryManNo" + URL_FRESHFOOD_PARA_DELIVERYMANNAME = "deliveryManName" + URL_FRESHFOOD_PARA_DELIVERYMANPHONE = "deliveryManPhone" +) + +type FreshFoodAPI struct { + baseURL string + sugarLogger *zap.SugaredLogger + client http.Client +} + +func NewFreshFoodAPI(baseURL string, zapLogger *zap.Logger) *FreshFoodAPI { + return &FreshFoodAPI{baseURL, zapLogger.Sugar(), http.Client{Timeout: time.Second * 5}} +} + +func (f FreshFoodAPI) AccessFreshFodd(apiStr string, params url.Values) error { + fullURL := f.baseURL + "/" + apiStr + // _, err := f.client.PostForm(fullURL, params) + err := error(nil) + f.sugarLogger.Debug(fullURL) + if err != nil { + f.sugarLogger.Warnf("Call %s error:%v", fullURL, err) + } + return nil +} + +func (f FreshFoodAPI) NewJDOrder(jdorder *models.Jdorder) error { + params := make(url.Values) + params.Set(URL_FRESHFOOD_PARA_JDORDER, string(utils.MustMarshal(jdorder))) + return f.AccessFreshFodd("order", params) +} + +// todo venderId +func (f FreshFoodAPI) JDOrderStatus(jdorder *models.Jdorder, venderId string) error { + params := make(url.Values) + params.Set(URL_FRESHFOOD_PARA_VENDERID, venderId) + params.Set(URL_FRESHFOOD_PARA_ORDERID, utils.Int64ToStr(jdorder.JdOrderId)) + params.Set(URL_FRESHFOOD_PARA_ORDERSTATUS, utils.Int2Str(jdorder.OrderStatus)) + params.Set(URL_FRESHFOOD_PARA_ORDERSTATUSTIME, jdorder.OrderStatusTime) + params.Set(URL_FRESHFOOD_PARA_CITYNAME, jdorder.CityName) + + return f.AccessFreshFodd("order/status", params) +} + +func (f FreshFoodAPI) JDOrderDeliveryStatus(jdOrderDeliveryStatusMsg *jdapi.JDDeliveryStatusMsg, venderId string) error { + params := make(url.Values) + cityName := "all" + + params.Set(URL_FRESHFOOD_PARA_VENDERID, venderId) + params.Set(URL_FRESHFOOD_PARA_ORDERID, jdOrderDeliveryStatusMsg.OrderId) + params.Set(URL_FRESHFOOD_PARA_DELIVERYSTATUS, utils.Int2Str(jdOrderDeliveryStatusMsg.DeliveryStatus)) + params.Set(URL_FRESHFOOD_PARA_DLIVERYSTATUSTIME, jdOrderDeliveryStatusMsg.DeliveryStatusTime) + + params.Set(URL_FRESHFOOD_PARA_DELIVERYCARRIERNO, jdOrderDeliveryStatusMsg.DeliveryCarrierNo) + params.Set(URL_FRESHFOOD_PARA_DELIVERYCARRIERNAME, jdOrderDeliveryStatusMsg.DeliveryCarrierName) + params.Set(URL_FRESHFOOD_PARA_DELIVERYMANNO, jdOrderDeliveryStatusMsg.DeliveryManNo) + params.Set(URL_FRESHFOOD_PARA_DELIVERYMANNAME, jdOrderDeliveryStatusMsg.DeliveryManName) + params.Set(URL_FRESHFOOD_PARA_DELIVERYMANPHONE, jdOrderDeliveryStatusMsg.DeliveryManPhone) + + params.Set(URL_FRESHFOOD_PARA_CITYNAME, cityName) + + return f.AccessFreshFodd("delivery/status", params) +} diff --git a/business/jd/controller/controller.go b/business/jd/controller/controller.go index d7ea65597..6abe84bad 100644 --- a/business/jd/controller/controller.go +++ b/business/jd/controller/controller.go @@ -2,16 +2,30 @@ package controller import ( "git.rosy.net.cn/baseapi/platform/jdapi" + "git.rosy.net.cn/jx-callback/business/jd/models" + "github.com/astaxie/beego/orm" "go.uber.org/zap" ) var ( - gJdapi *jdapi.JDAPI - sugarLogger *zap.SugaredLogger + gJdapi *jdapi.JDAPI + sugarLogger *zap.SugaredLogger + jdSuccessResponse = &jdapi.JDOrderMsgResponse{Code: "0", Msg: "success", Data: ""} ) func init() { logger, _ := zap.NewDevelopment() sugarLogger = logger.Sugar() gJdapi = jdapi.NewJDAPI("91633f2a-c5f5-4982-a925-a220d19095c3", "1dba76d40cac446ca500c0391a0b6c9d", "a88d031a1e7b462cb1579f12e97fe7f4", logger) + + // set default database + orm.RegisterDataBase("default", "mysql", "root:WebServer@1@tcp(127.0.0.1:3306)/jx-callback?charset=utf8&loc=Local", 30) + + // register model + orm.RegisterModel(new(models.Jdorder)) + + // create table + orm.RunSyncdb("default", false, true) + + initOrder() } diff --git a/business/jd/controller/order.go b/business/jd/controller/order.go index 70c6f7278..ef4bad753 100644 --- a/business/jd/controller/order.go +++ b/business/jd/controller/order.go @@ -2,70 +2,243 @@ package controller import ( "encoding/json" - "strconv" + "git.rosy.net.cn/baseapi/platform/jdapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/freshfood" "git.rosy.net.cn/jx-callback/business/jd/models" "git.rosy.net.cn/jx-callback/compat/corm" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" // import your used driver ) -var errChecker corm.DBErrorChecker +const ( + MsgNotHandledCode = "9527" +) -func init() { - errChecker = new(corm.MysqlErrorChecker) +var ( + errChecker corm.DBErrorChecker + orderMsgChan chan *jdapi.JDOrderMsg - // set default database - orm.RegisterDataBase("default", "mysql", "root:WebServer@1@tcp(127.0.0.1:3306)/jx-callback?charset=utf8&loc=Local", 30) - - // register model - orm.RegisterModel(new(models.Jdorder)) - - // create table - orm.RunSyncdb("default", false, true) -} + // freshFoodServerURL = "http://portal.jingxicaishi.com" + freshFoodServerURL = "http://test.jxc4.com" + freshFoodAPI *freshfood.FreshFoodAPI +) type OrderControler struct { } -func (c *OrderControler) NewOrder(order *models.NewOrderMsg) *models.OrderMsgResponse { - db := orm.NewOrm() - jdorderid, _ := strconv.ParseInt(order.BillId, 10, 64) - status, _ := strconv.Atoi(order.StatusId) - rec := &models.Jdorder{ - JdOrderId: jdorderid, - OrderStatus: status, - } +func initOrder() { + errChecker = new(corm.MysqlErrorChecker) + freshFoodAPI = freshfood.NewFreshFoodAPI(freshFoodServerURL, sugarLogger.Desugar()) - if created, _, err := db.ReadOrCreate(rec, "Jdorderid"); err == nil { - if created { - c.AcceptOrder(order) - result, err := gJdapi.LegacyQuerySingleOrder(order.BillId) - if err != nil { - sugarLogger.Warnf("error when query jd order:%s, error:%v", order.BillId, err) - } else { - rec.Code = result.Code - rec.Msg = result.Msg - rec.Success = 1 - rec.CityName = "all" - rec.OrderStatus = result.OrderStatus - rec.OrderStatusTime = result.OrderStatusTime - result.Msg = "成功" - resultByteArr, _ := json.Marshal(result) - rec.Data = string(resultByteArr) - db.Update(rec, "Data", "Code", "Msg", "Success", "CityName", "OrderStatus", "OrderStatusTime") + orderMsgChan = make(chan *jdapi.JDOrderMsg, 128) + go orderMsgHandlerRoutinue() + + // todo 这样操作在有多个进程时,会有问题 + // 另外当前这个模式可能会出现同一个定单的消息,虽然远程推送过来顺序是对的,但经过处理后推送到freshfood时乱序(因为每个消息的处理时间是不确定的) + handlePendingOrderMsg() +} + +func handlePendingOrderMsg() { + var ordersInfo []models.Jdorder + db := orm.NewOrm() + _, err := db.Raw("SELECT * FROM jdorder WHERE code = ?", MsgNotHandledCode).QueryRows(&ordersInfo) + if err != nil { + sugarLogger.Errorf("can not get jdorder from db, error:%v", err) + } else { + for _, jdOrderInfo := range ordersInfo { + orderMsg := &jdapi.JDOrderMsg{ + Id: jdOrderInfo.Id, + BillId: utils.Int64ToStr(jdOrderInfo.JdOrderId), + StatusId: utils.Int2Str(jdOrderInfo.OrderStatus), + Timestamp: jdOrderInfo.OrderStatusTime, } - } else { - sugarLogger.Warnf("duplicated jd orderid:%s", order.BillId) - sugarLogger.Debug(rec) + addOrderMsg(orderMsg) + } + } +} + +func orderMsgHandlerRoutinue() { + for { + msg := <-orderMsgChan + sugarLogger.Debugf("OrderMsgHandlerRoutinue:%v", msg) + go handleOrderMsg(msg) + } +} + +func addOrderMsg(orderMsg *jdapi.JDOrderMsg) { + sugarLogger.Debugf("addOrderMsg:%v", orderMsg) + orderMsgChan <- orderMsg +} + +func handleOrderMsg(orderMsg *jdapi.JDOrderMsg) { + sugarLogger.Debugf("handleOrderMsg:%v", orderMsg) + switch orderMsg.StatusId { + case jdapi.JdOrderStatusNew: + newOrder(orderMsg) + case jdapi.JdOrderStatusAdjust: + adjustOrder(orderMsg) + default: + normalOrderStatus(orderMsg) + } +} + +// -------------- +func (c *OrderControler) OrderStatus(order *jdapi.JDOrderMsg) *jdapi.JDOrderMsgResponse { + if order.StatusId != jdapi.JdOrderStatusNew && order.StatusId != jdapi.JdOrderStatusAdjust { + err := normalOrderStatus(order) + if err != nil { + sugarLogger.Warnf("error in OrderStatus, error:%v", err) + return &jdapi.JDOrderMsgResponse{jdapi.JDerrorCodeAccessFailed, err.Error(), ""} } } else { - sugarLogger.Errorf("error when calling ReadOrCreate:%v", err) + db := orm.NewOrm() + jdorderid := utils.Str2Int64(order.BillId) + status := utils.Str2Int(order.StatusId) + rec := &models.Jdorder{ + Code: MsgNotHandledCode, + JdOrderId: jdorderid, + OrderStatus: status, + OrderStatusTime: order.Timestamp, + } + + if created, _, err := db.ReadOrCreate(rec, "Jdorderid"); err == nil { + order.Id = rec.Id + if created { + isStatusSame := order.StatusId == jdapi.JdOrderStatusNew + if !isStatusSame { + order.StatusId = jdapi.JdOrderStatusNew + sugarLogger.Warnf("order:%s get %s before create", order.BillId, order.StatusId) + } + addOrderMsg(order) + + if !isStatusSame { + order.StatusId = utils.Int2Str(status) + } + } + if rec.OrderStatus != status { + rec.OrderStatus = status + rec.OrderStatusTime = order.Timestamp + rec.Code = MsgNotHandledCode + db.Update(rec, "OrderStatus", "OrderStatusTime", "Code") + + addOrderMsg(order) + } else { + sugarLogger.Warnf("duplicated jd orderid:%s", order.BillId) + sugarLogger.Debug(rec) + } + } else { + sugarLogger.Errorf("error when calling ReadOrCreate:%v", err) + return &jdapi.JDOrderMsgResponse{jdapi.JDerrorCodeAccessFailed, err.Error(), ""} + } + } + return jdSuccessResponse +} + +func (c *OrderControler) OrderDeliveryStatus(jdOrderDeliveryStatusMsg *jdapi.JDDeliveryStatusMsg) *jdapi.JDOrderMsgResponse { + err := freshFoodAPI.JDOrderDeliveryStatus(jdOrderDeliveryStatusMsg, "0") + + if err != nil { + sugarLogger.Errorf("Error when calling JDOrderDeliveryStatus, error:%v", err) + return &jdapi.JDOrderMsgResponse{jdapi.JDerrorCodeAccessFailed, err.Error(), ""} } - return &models.OrderMsgResponse{"0", "success", ""} + return jdSuccessResponse } -func (c *OrderControler) AcceptOrder(order *models.NewOrderMsg) { +//----------- +func acceptOrder(order *jdapi.JDOrderMsg) { + gJdapi.OrderAcceptOperate(order.BillId, true) +} + +func newOrder(order *jdapi.JDOrderMsg) error { + sugarLogger.Debug("NewOrder2") + + result, err := gJdapi.LegacyQuerySingleOrder(order.BillId) + acceptOrder(order) + if err != nil { + sugarLogger.Warnf("error when query jd order:%s, error:%v", order.BillId, err) + } else { + rec := &models.Jdorder{ + Id: order.Id, + } + + rec.Code, _ = result["code"].(string) + rec.Msg, _ = result["msg"].(string) + success, _ := result["success"].(bool) + if success { + rec.Success = 1 + } else { + rec.Success = 0 + } + + // todo + rec.CityName = "all" + + data := result["data"].(map[string]interface{}) + dataResult := data["result"].(map[string]interface{}) + resultList := dataResult["resultList"].([]interface{}) + if len(resultList) == 1 { + resultList0 := resultList[0].(map[string]interface{}) + orderStatus, _ := resultList0["orderStatus"].(json.Number).Int64() + rec.OrderStatus = int(orderStatus) + rec.OrderStatusTime = resultList0["orderStatusTime"].(string) + + resultByteArr, _ := json.Marshal(data) + rec.Data = string(resultByteArr) + err = freshFoodAPI.NewJDOrder(rec) + if err == nil { + db := orm.NewOrm() + _, err = db.Update(rec, "Data", "Code", "Msg", "Success", "CityName", "OrderStatus", "OrderStatusTime") + if err != nil { + sugarLogger.Errorf("update order error:%v", err) + } + } else { + sugarLogger.Errorf("Error when calling NewJDOrder error:%v", err) + } + } else { + sugarLogger.Warnf("can not get jdorder info:%v", order.BillId) + } + } + return err +} + +func adjustOrder(order *jdapi.JDOrderMsg) error { + return newOrder(order) +} + +func normalOrderStatus(order *jdapi.JDOrderMsg) error { + db := orm.NewOrm() + rec := &models.Jdorder{ + JdOrderId: utils.Str2Int64(order.BillId), + } + + err := db.Read(rec, "JdOrderId") + if err != nil { + sugarLogger.Warnf("error when accessing db:%v", err) + return err + } + + if rec.OrderStatus == utils.Str2Int(order.StatusId) { + sugarLogger.Infof("Duplicate message order:%v", order) + return nil + } + + rec.OrderStatus = utils.Str2Int(order.StatusId) + rec.OrderStatusTime = order.Timestamp + err = freshFoodAPI.JDOrderStatus(rec, "0") + if err != nil { + sugarLogger.Warnf("access freshfood failed, error:%v", err) + return err + } + + rec.Code = "0" + _, err = db.Update(rec, "OrderStatus", "OrderStatusTime", "Code") + if err != nil { + sugarLogger.Warnf("error when accessing db:%v", err) + } + + return err } diff --git a/business/jd/models/order.go b/business/jd/models/order.go index 4315c318a..858c4e1cf 100644 --- a/business/jd/models/order.go +++ b/business/jd/models/order.go @@ -1,28 +1,8 @@ package models -// type CommonCallbackMsgFields struct { -// Id int -// Status int `orm:default(0)` -// Count int `orm:default(1)` -// CreatedAt time.Time `orm:"auto_now_add;type(datetime)"` -// UpdatedAt time.Time `orm:"auto_now;type(datetime)"` -// } - -type OrderMsgResponse struct { - Code string - Msg string - Data string -} - -type NewOrderMsg struct { - BillId string - StatusId string - Timestamp string -} - type Jdorder struct { Id int - Code string `orm:"size(2)"` + Code string `orm:"size(8)"` Msg string `orm:"size(100)"` Data string `orm:"type(text)"` Success int8 diff --git a/controllers/jd_order.go b/controllers/jd_order.go index ca7696e21..bb34d124b 100644 --- a/controllers/jd_order.go +++ b/controllers/jd_order.go @@ -5,8 +5,8 @@ import ( "github.com/astaxie/beego/logs" + "git.rosy.net.cn/baseapi/platform/jdapi" "git.rosy.net.cn/jx-callback/business/jd/controller" - "git.rosy.net.cn/jx-callback/business/jd/models" "github.com/astaxie/beego" ) @@ -21,22 +21,126 @@ func (c *JDOrderController) URLMapping() { c.Mapping("NewOrder", c.NewOrder) } -// @Title Create +func (c *JDOrderController) handleJDCallback(obj interface{}, hanlder func() interface{}) { + jdParamJSON := c.GetString(JD_PARAM_JSON) + err := json.Unmarshal([]byte(jdParamJSON), obj) + if err != nil { + logs.Error(err) + c.Data["json"] = jdapi.JDOrderMsgResponse{jdapi.JDerrorCodeMissingMandatoryParam, "jd_param_json format is wrong", jdParamJSON} + } else { + c.Data["json"] = hanlder() + } + c.ServeJSON() +} + +func (c *JDOrderController) orderStatus() { + var ob jdapi.JDOrderMsg + c.handleJDCallback(&ob, func() interface{} { + cc := controller.OrderControler{} + return cc.OrderStatus(&ob) + }) +} + +// @Title newOrder // @Description create object // @Param jd_param_json formData string true "应用级别输入参数" // @Success 200 {string} models.Object.Id // @Failure 403 body is empty -// @router /NewOrder [post] +// @router /newOrder [post] func (c *JDOrderController) NewOrder() { - var ob models.NewOrderMsg - jd_param_json := c.GetString(JD_PARAM_JSON) - err := json.Unmarshal([]byte(jd_param_json), &ob) - if err != nil { - logs.Error(err) - c.Data["json"] = models.OrderMsgResponse{"10015", "jd_param_json format is wrong", jd_param_json} - } else { - cc := controller.OrderControler{} - c.Data["json"] = cc.NewOrder(&ob) - } - c.ServeJSON() + c.orderStatus() +} + +// @Title AdjustOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /orderAdjust [post] +func (c *JDOrderController) OrderAdjust() { + c.orderStatus() +} + +// @Title orderWaitOutStore +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /orderWaitOutStore [post] +func (c *JDOrderController) OrderWaitOutStore() { + c.orderStatus() +} + +// @Title pickFinishOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /pickFinishOrder [post] +func (c *JDOrderController) PickFinishOrder() { + c.orderStatus() +} + +// @Title deliveryOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /deliveryOrder [post] +func (c *JDOrderController) DeliveryOrder() { + c.orderStatus() +} + +// @Title finishOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /finishOrder [post] +func (c *JDOrderController) FinishOrder() { + c.orderStatus() +} + +// @Title lockOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /lockOrder [post] +func (c *JDOrderController) LockOrder() { + c.orderStatus() +} + +// @Title userCancelOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /userCancelOrder [post] +func (c *JDOrderController) UserCancelOrder() { + c.orderStatus() +} + +// @Title applyCancelOrder +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /applyCancelOrder [post] +func (c *JDOrderController) ApplyCancelOrder() { + c.orderStatus() +} + +// @Title pushDeliveryStatus +// @Description create object +// @Param jd_param_json formData string true "应用级别输入参数" +// @Success 200 {string} models.Object.Id +// @Failure 403 body is empty +// @router /pushDeliveryStatus [post] +func (c *JDOrderController) PushDeliveryStatus() { + var ob jdapi.JDDeliveryStatusMsg + c.handleJDCallback(&ob, func() interface{} { + cc := controller.OrderControler{} + return cc.OrderDeliveryStatus(&ob) + }) } diff --git a/routers/router.go b/routers/router.go index 8dc9b4433..98ae14392 100644 --- a/routers/router.go +++ b/routers/router.go @@ -14,21 +14,9 @@ import ( ) func init() { - ns := beego.NewNamespace("/v1", - beego.NSNamespace("/object", - beego.NSInclude( - &controllers.ObjectController{}, - ), - ), - beego.NSNamespace("/user", - beego.NSInclude( - &controllers.UserController{}, - ), - ), - beego.NSNamespace("/djsw", - beego.NSInclude( - &controllers.JDOrderController{}, - ), + ns := beego.NewNamespace("/djsw", + beego.NSInclude( + &controllers.JDOrderController{}, ), ) beego.AddNamespace(ns)