- refactor bad comment

This commit is contained in:
gazebo
2019-03-12 15:40:08 +08:00
parent 87a25c893b
commit c46f1597f1
15 changed files with 417 additions and 9 deletions

View File

@@ -0,0 +1,174 @@
package orderman
import (
"math/rand"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils"
"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"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
)
const (
COMMENT_NOT_RESOLVED = 0 //未解决的差评状态
COMMENT_RESOLVED = 1 //已解决的差评状态
JX_BAD_COMMENTS_MAX_LEVEL = 2
COMMENTS_SCORE_ONE_ORTWO_BEGIN_DELAY_TIME = 1 * 60 //评论回复一星或二星回复延迟开始时间区间
COMMENTS_SCORE_ONE_ORTWO_END_DELAY_TIME = 3 * 60 //评论回复一星或二星回复延迟结束时间区间
COMMENTS_SCORE_THREE_BEGIN_DELAY_TIME = 2 * 60 //评论回复三星回复延迟开始时间区间
COMMENTS_SCORE_THREE_END_DELAY_TIME = 4 * 60 //评论回复三星回复延迟结束时间区间
COMMENTS_SCORE_FOUR_ORFIVE_BEGIN_DELAY_TIME = 2 * 60 //评论回复四星或五星回复延迟开始时间区间
COMMENTS_SCORE_FOUR_ORFIVE_END_DELAY_TIME = 5 * 60 //评论回复四星或五星回复延迟结束时间区间
MAX_REAPLY_TIME = 4 * time.Hour
)
type tReplyConfig struct {
delayGapBegin int
delayGapEnd int
comments []string
}
var (
replyConfig = map[int]*tReplyConfig{
1: &tReplyConfig{
delayGapBegin: COMMENTS_SCORE_ONE_ORTWO_BEGIN_DELAY_TIME,
delayGapEnd: COMMENTS_SCORE_ONE_ORTWO_END_DELAY_TIME,
comments: []string{
"非常抱歉让您没有得到十分满意的购物体验,我们会及时与您联系进行确认并解决问题!",
},
},
3: &tReplyConfig{
delayGapBegin: COMMENTS_SCORE_THREE_BEGIN_DELAY_TIME,
delayGapEnd: COMMENTS_SCORE_THREE_END_DELAY_TIME,
comments: []string{
"感谢您对我们的肯定,祝您生活愉快!欢迎再次光临,谢谢!",
"感谢您对京西菜市的关照,我们会更加精益求精。",
"感谢您的光临,您的支持是我们前进的动力!",
},
},
4: &tReplyConfig{
delayGapBegin: COMMENTS_SCORE_FOUR_ORFIVE_BEGIN_DELAY_TIME,
delayGapEnd: COMMENTS_SCORE_FOUR_ORFIVE_END_DELAY_TIME,
comments: []string{
"感谢您的信赖!我们会不断提升菜品质量以及优质的服务,期待与您的再次相遇!",
"感谢您的支持,愿您天天好心情!",
"感谢您的认可!您的支持是我们前进的动力。",
"感谢您的肯定与支持!我们会坚持把最好的服务带给您,期待和您的再次相遇!",
},
},
}
)
func (c *OrderManager) OnOrderComments(orderCommentList []*model.OrderComment) (err error) {
globals.SugarLogger.Debug("OnOrderComments")
db := dao.GetDB()
for _, orderComment := range orderCommentList {
globals.SugarLogger.Debugf("OnOrderComments, orderID:%s", orderComment.VendorOrderID)
comment2 := &legacymodel.JxBadComments{
OrderId: orderComment.VendorOrderID,
}
err := dao.GetEntity(db, &comment2, model.FieldVendoOrderID)
if err == nil || dao.IsNoRowsError(err) {
isNewComment := false
if dao.IsNoRowsError(err) {
isNewComment = true
if orderComment.IsReplied == 0 && time.Now().Sub(orderComment.CommentCreatedAt) < MAX_REAPLY_TIME {
c.replyOrderComment(orderComment)
}
}
if orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL || !isNewComment { // 如果是直接非差评,忽略
if isNewComment || orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL {
if isNewComment {
comment2.Createtime = utils.Time2Str(orderComment.CommentCreatedAt)
comment2.Msg = orderComment.OriginalMsg
comment2.Score = int(orderComment.Score)
comment2.Scorecontent = orderComment.Content
comment2.Vendertags = orderComment.TagList
comment2.Status = COMMENT_NOT_RESOLVED
comment2.OrderFlag = utils.Int2Str(orderComment.VendorID)
comment2.LastPushTime = utils.Time2Str(time.Now())
comment2.PushNo = 1
comment2.Maxmodifytime = int(orderComment.ModifyDuration)
order, err2 := partner.CurOrderManager.LoadOrder(orderComment.VendorOrderID, orderComment.VendorID)
if err = err2; err == nil {
orderComment.StoreID = jxutils.GetSaleStoreIDFromOrder(order)
comment2.Userphone = order.ConsigneeMobile
if orderComment.StoreID > 0 && orderComment.Score <= JX_BAD_COMMENTS_MAX_LEVEL {
if globals.ReallyCallPlatformAPI {
// weixinmsg.PushJDBadCommentToWeiXin(comment)
}
}
}
} else { // 修改评价但是仍然低于或等于JX_BAD_COMMENTS_MAX_LEVEL
comment2 = nil
}
} else { // 修改评价高于JX_BAD_COMMENTS_MAX_LEVEL
if orderComment.CommentCreatedAt.Sub(str2Time(comment2.Createtime)) == 0 ||
orderComment.CommentCreatedAt.Sub(str2Time(comment2.Updatetime)) == 0 {
comment2 = nil // 重复
} else {
comment2.Updatetime = utils.Time2Str(orderComment.CommentCreatedAt)
comment2.UpdatedMsg = orderComment.OriginalMsg
comment2.UpdatedScore = int(orderComment.Score)
comment2.UpdatedScorecontent = orderComment.Content
comment2.UpdatedVendertags = orderComment.TagList
comment2.Status = COMMENT_RESOLVED
}
}
if err == nil {
if isNewComment {
// dao.WrapAddIDCULEntity(orderComment, "admin")
err = dao.CreateEntity(db, comment2)
} else if comment2 != nil {
_, err = dao.UpdateEntity(db, comment2)
}
}
}
}
if err != nil {
globals.SugarLogger.Warnf("OnOrderComments orderID:%s failed with error:%v", orderComment.VendorOrderID, err)
}
if err != nil {
break
}
}
return err
}
func (c *OrderManager) replyOrderComment(orderComment *model.OrderComment) (err error) {
score := int(orderComment.Score)
if score <= 2 {
score = 1
} else if score >= 5 {
score = 4
}
config := replyConfig[score]
delaySeconds := config.delayGapBegin + rand.Intn(config.delayGapEnd-config.delayGapBegin)
content := config.comments[rand.Intn(len(config.comments))]
globals.SugarLogger.Debugf("replyOrderComment orderID:%s, delaySeconds:%d, content:%s", orderComment.VendorOrderID, delaySeconds, content)
time.AfterFunc(time.Duration(delaySeconds)*time.Second, func() {
if globals.ReallySendWeixinMsg {
// err = partner.GetPurchasePlatformFromVendorID(orderComment.VendorID).ReplyOrderComment(nil, orderComment, content)
if err != nil {
globals.SugarLogger.Debugf("replyOrderComment orderID:%s, error:%v", orderComment.VendorOrderID, err)
}
}
})
return err
}
func str2Time(timeStr string) time.Time {
if timeStr == "" {
return utils.DefaultTimeValue
}
return utils.Str2Time(timeStr)
}

View File

@@ -20,6 +20,7 @@ import (
"git.rosy.net.cn/jx-callback/business/model/legacymodel"
"git.rosy.net.cn/jx-callback/business/model/legacymodel2"
"git.rosy.net.cn/jx-callback/business/partner"
"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"
@@ -804,8 +805,6 @@ func ReProcessJdBadComment(ctx *jxcontext.Context, isForce, isAsync, isContinueW
if len(comment2) > 0 {
badComment.Updatetime = utils.Timestamp2Str(utils.MustInterface2Int64(comment2["createTime"].(map[string]interface{})["time"]) / 1000)
badComment.UpdatedMsg = string(utils.MustMarshal(comment2))
} else if badComment.UpdatedMsg != "" {
badComment.OrderFlag = "1"
}
_, err = dao.UpdateEntity(db, badComment)
}
@@ -847,3 +846,29 @@ func unmarshalCommentText(commentStr string) (retVal map[string]interface{}, isN
}
}
}
func RefreshEbaiBadComment(ctx *jxcontext.Context, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error) {
if jxutils.IsTimeEmpty(fromTime) {
fromTime = utils.Str2Time("2018-05-03 00:00:00")
}
if jxutils.IsTimeEmpty(toTime) {
toTime = time.Now().Add(-3 * time.Hour)
}
days := int(toTime.Sub(fromTime)/24*time.Hour + 1)
rootTask := tasksch.NewSeqTask("RefreshEbaiBadComment", ctx.GetUserName(), func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
batchFromTime := fromTime.Add(time.Duration(step) * 24 * time.Hour)
batchToTime := batchFromTime.Add(24 * time.Hour)
if batchToTime.Sub(toTime) > 0 {
batchToTime = toTime
}
err = ebai.CurPurchaseHandler.RefreshComment(batchFromTime, batchToTime)
return nil, err
}, days)
tasksch.ManageTask(rootTask).Run()
if !isAsync {
_, err = rootTask.GetResult(0)
} else {
hint = rootTask.ID
}
return hint, err
}

View File

@@ -28,3 +28,26 @@ type JxBadComments struct {
func (*JxBadComments) TableName() string {
return "jx_bad_comments"
}
type JxBadComments2 struct {
Id int `json:"id" orm:"column(id)"`
CreatedAt time.Time `orm:"auto_now_add;type(datetime);null" json:"createdAt"`
OrderId string `json:"order_id" orm:"column(order_id);size(25);unique" description:"订单ID"`
Jxstoreid string `json:"jxstoreid" orm:"column(jxstoreid);size(11);index" description:"京西门店ID"`
Userphone string `json:"userPhone" orm:"column(userphone);size(255);null" description:"评价的用户的联系方式"`
Status int `json:"status" orm:"column(status)" description:"当前评论的状态(0:未解决 1:已解决)"`
Createtime string `json:"createTime" orm:"column(createtime);size(255);null" description:"评论的创建时间"`
Maxmodifytime int `json:"maxModifyTime" orm:"column(maxmodifytime);null" description:"评论可修改的最大时间"`
Score int `json:"score4" orm:"column(score)" description:"评论的星级"`
Scorecontent string `json:"score4Content" orm:"column(scorecontent);size(255);null" description:"评论的内容"`
Vendertags string `json:"venderTags" orm:"column(vendertags);size(255);null" description:"评论的标签"`
Updatetime string `json:"updateTime" orm:"column(updatetime);size(255);null" description:"评论的修改时间"`
UpdatedScore int `json:"updatedScore" orm:"column(updated_score);null" description:"更改后的分数"`
UpdatedScorecontent string `json:"updatedScoreContent" orm:"column(updated_scorecontent);size(255);null" description:"更改后的评论信息"`
UpdatedVendertags string `json:"updatedVenderTags" orm:"column(updated_vendertags);size(255);null" description:"更改后的标签信息"`
OrderFlag string `json:"order_flag" orm:"column(order_flag);size(255);null" description:"订单类别(0:京东 1:美团 2:饿了么)"`
Msg string `json:"-" orm:"column(msg);type(text)" description:"未解决差评的原始信息"`
UpdatedMsg string `json:"-" orm:"column(updated_msg);type(text);null" description:"解决后的差评的原始信息"`
LastPushTime string `json:"-" orm:"column(last_push_time);size(255);null" description:"上一次推送的时间"`
PushNo int `json:"-" orm:"column(push_no);null" description:"推送次数"`
}

View File

@@ -42,6 +42,8 @@ const (
FieldSpecUnit = "SpecUnit"
FieldName = "Name"
FieldRemark = "Remark"
FieldVendoOrderID = "VendoOrderID"
)
type ModelIDCUL struct {

View File

@@ -216,17 +216,24 @@ func (o *OrderStatus) GetStatusTime() time.Time {
type OrderComment struct {
ModelIDCUL
VendorOrderID string `orm:"column(vendor_order_id);size(48)" json:"vendorOrderID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
StoreID int
VendorOrderID string `orm:"column(vendor_order_id);size(48);unique" json:"vendorOrderID"`
VendorID int `orm:"column(vendor_id)" json:"vendorID"`
VendorStoreID string `orm:"column(vendor_store_id);size(48)" json:"vendorStoreID"`
StoreID int `orm:"column(store_id)" json:"storeID"` // 外部系统里记录的 jxstoreid
UserCommentID string `orm:"column(user_comment_id);size(48)" json:"userCommentID"`
IsReplied int8
Status int8
ModifyDuration int8 // 改评价的小时数
TagList string
Score int8
Content string
CommentCreatedAt time.Time
OriginalMsg string `orm:"type(text)" json:"-"`
UpdatedTagList string
UpdatedScore int8
UpdatedContent string
CommentUpdatedAt time.Time
UpdatedTagList string
UpdatedScore int8
UpdatedContent string
CommentUpdatedAt time.Time
UpdatedOriginalMsg string `orm:"type(text)" json:"-"`
}

View File

@@ -56,6 +56,7 @@ type IOrderManager interface {
UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error)
LoadWaybill(vendorWaybillID string, waybillVendorID int) (bill *model.Waybill, err error)
OnOrderComments(orderCommentList []*model.OrderComment) (err error)
}
// purchase handler中
@@ -99,6 +100,8 @@ type IPurchasePlatformHandler interface {
DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error)
GetVendorID() int
RefreshRealMobile(ctx *jxcontext.Context, fromTime, toTime time.Time, isAsync, isContinueWhenError bool) (hint string, err error)
ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error)
}
// db *dao.DaoDB,

View File

@@ -0,0 +1,71 @@
package ebai
import (
"time"
"git.rosy.net.cn/baseapi/platformapi/ebaiapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
const (
RefreshCommentTime = 4 * time.Hour
RefreshCommentTimeInterval = 10 * time.Minute
)
func (c *PurchaseHandler) StartRefreshComment() {
c.RefreshComment(time.Now().Add(-RefreshCommentTime), time.Now())
time.AfterFunc(RefreshCommentTimeInterval, func() {
c.StartRefreshComment()
})
}
func (c *PurchaseHandler) RefreshComment(fromTime, toTime time.Time) (err error) {
globals.SugarLogger.Debugf("RefreshComment fromTime:%s, toTime:%s", utils.Time2Str(fromTime), utils.Time2Str(toTime))
var orderCommentList []*model.OrderComment
stepGap := 24 * time.Hour
stepFromTime := fromTime
for {
stepToTime := stepFromTime.Add(stepGap - time.Second)
if stepToTime.Sub(toTime) > 0 {
stepToTime = toTime
}
resultList, err2 := api.EbaiAPI.GetEleCommentList(stepFromTime, stepToTime, "", "", ebaiapi.ReplyStatusAll, ebaiapi.CommentLevelAll, ebaiapi.CommentContentAll)
if err = err2; err == nil {
for _, result := range resultList {
orderComment := &model.OrderComment{
VendorOrderID: utils.Interface2String(result["order_id"]),
VendorID: model.VendorIDEBAI,
UserCommentID: utils.Interface2String(result["comment_id"]),
VendorStoreID: utils.Int64ToStr(utils.MustInterface2Int64(result["shop_id"])),
TagList: "",
Score: int8(utils.MustInterface2Int64(result["service_rating"])),
Content: utils.Interface2String(result["content"]),
CommentCreatedAt: utils.Str2Time(utils.Interface2String(result["create_time"])),
IsReplied: int8(1 - utils.MustInterface2Int64(result["can_reply"])),
ModifyDuration: 24,
OriginalMsg: string(utils.MustMarshal(result)),
}
orderCommentList = append(orderCommentList, orderComment)
}
} else {
globals.SugarLogger.Warnf("RefreshComment stepFromTime:%s, stepToTime:%s failed with error:%v", utils.Time2Str(stepFromTime), utils.Time2Str(stepToTime), err)
break //?
}
if stepToTime.Sub(toTime) == 0 {
break
}
}
if err == nil && len(orderCommentList) > 0 {
err = partner.CurOrderManager.OnOrderComments(orderCommentList)
}
return err
}
func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error) {
return api.EbaiAPI.OrderRatesReply("", utils.Str2Int64(orderComment.VendorStoreID), orderComment.UserCommentID, replyComment)
}

View File

@@ -0,0 +1,10 @@
package elm
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
)
func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error) {
return err
}

View File

@@ -0,0 +1,43 @@
package jd
import (
"git.rosy.net.cn/baseapi/platformapi/jdapi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/partner"
"git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
)
func (c *PurchaseHandler) onOrderComment2(msg *jdapi.CallbackOrderMsg) (err error) {
intOrderID := utils.Str2Int64(msg.BillID)
result, err := api.JdAPI.GetCommentByOrderId(intOrderID)
if err == nil {
globals.SugarLogger.Debugf("onOrderComment comment:%s", utils.Format4Output(result, true))
orderCommend := &model.OrderComment{
VendorOrderID: utils.Int64ToStr(utils.MustInterface2Int64(result["orderId"])),
VendorID: model.VendorIDJD,
UserCommentID: "",
VendorStoreID: utils.Int64ToStr(utils.MustInterface2Int64(result["storeId"])),
TagList: string(utils.MustMarshal(result["venderTags"])),
Score: int8(utils.MustInterface2Int64(result["score4"])),
Content: utils.Interface2String(result["score4Content"]),
CommentCreatedAt: utils.Timestamp2Time(utils.MustInterface2Int64(result["createTime"].(map[string]interface{})["time"]) / 1000),
ModifyDuration: JDDJ_BAD_COMMENTS_MAX_MODIFY_TIME,
OriginalMsg: string(utils.MustMarshal(result)),
}
if result["orgCommentContent"] != nil {
orderCommend.IsReplied = 1
}
err = partner.CurOrderManager.OnOrderComments([]*model.OrderComment{orderCommend})
}
if err != nil {
globals.SugarLogger.Warnf("onOrderComment orderID:%s failed with error:%v", msg.BillID, err)
}
return err
}
func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error) {
return api.JdAPI.OrgReplyComment(utils.Str2Int64(orderComment.VendorOrderID), orderComment.VendorStoreID, replyComment, ctx.GetUserName())
}

View File

@@ -72,6 +72,7 @@ var (
func (c *PurchaseHandler) onOrderComment(msg *jdapi.CallbackOrderMsg) (err error) {
globals.SugarLogger.Debugf("onOrderComment orderID:%s", msg.BillID)
go func() error {
c.onOrderComment2(msg)
intOrderID := utils.Str2Int64(msg.BillID)
result, err := api.JdAPI.GetCommentByOrderId(intOrderID)
if err == nil {

View File

@@ -0,0 +1,10 @@
package mtwm
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
)
func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error) {
return err
}

View File

@@ -0,0 +1,10 @@
package wsc
import (
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model"
)
func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, orderComment *model.OrderComment, replyComment string) (err error) {
return err
}

View File

@@ -143,3 +143,22 @@ func (c *InitDataController) ReProcessJdBadComment() {
return retVal, "", err
})
}
// @Title 刷新
// @Description 重新处理京东差评
// @Param token header string true "认证token"
// @Param fromTime formData string false "起始时间"
// @Param toTime formData string false "结束时间"
// @Param isAsync formData bool false "是否异步操作"
// @Param isContinueWhenError formData bool false "单个同步失败是否继续缺省false"
// @Success 200 {object} controllers.CallResult
// @Failure 200 {object} controllers.CallResult
// @router /RefreshEbaiBadComment [post]
func (c *InitDataController) RefreshEbaiBadComment() {
c.callRefreshEbaiBadComment(func(params *tInitdataRefreshEbaiBadCommentParams) (retVal interface{}, errCode string, err error) {
if timeList, err := jxutils.BatchStr2Time(params.FromTime, params.ToTime); err == nil {
retVal, err = tempop.RefreshEbaiBadComment(params.Ctx, timeList[0], timeList[1], params.IsAsync, params.IsContinueWhenError)
}
return retVal, "", err
})
}

View File

@@ -37,6 +37,8 @@ func Init() {
orm.RegisterModel(&model.Promotion{}, &model.PromotionStore{}, &model.PromotionSku{})
orm.RegisterModel(&model.AuthBind{}, &model.User{})
orm.RegisterModel(&legacymodel.JxBadComments2{})
if globals.EnablePendingChange {
orm.RegisterModel(&model.StoreOpRequest{})
}

View File

@@ -359,6 +359,14 @@ func init() {
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"],
beego.ControllerComments{
Method: "RefreshEbaiBadComment",
Router: `/RefreshEbaiBadComment`,
AllowHTTPMethods: []string{"post"},
MethodParams: param.Make(),
Params: nil})
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"],
beego.ControllerComments{
Method: "TransferLegacyJdOrder",