You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

786 lines
21 KiB

4 years ago
package cron
import (
"encoding/json"
"errors"
"fmt"
"git.oa00.com/go/mysql"
"github.com/golangkit/formatime"
"github.com/jinzhu/gorm"
4 years ago
"github.com/robfig/cron"
4 years ago
"github.com/shopspring/decimal"
gorm2 "gorm.io/gorm"
"log"
"recook/configs"
order3 "recook/internal/api/mobile/order"
"recook/internal/cache"
"recook/internal/dbc"
order4 "recook/internal/model/order"
user2 "recook/internal/model/user"
"recook/internal/mq"
"recook/internal/mq/model"
4 years ago
"recook/internal/v2/hook"
4 years ago
"recook/internal/v2/lib/downloader"
"recook/internal/v2/lib/jcook"
"recook/internal/v2/lib/nuonuo"
after2 "recook/internal/v2/logic/manage/after"
4 years ago
"recook/internal/v2/logic/manage/message"
4 years ago
order2 "recook/internal/v2/logic/manage/order"
"recook/internal/v2/model/recook/after"
goods2 "recook/internal/v2/model/recook/goods"
manage2 "recook/internal/v2/model/recook/manage"
manage "recook/internal/v2/model/recook/order"
"recook/internal/v2/model/recook/user"
"regexp"
"strconv"
"strings"
"time"
)
4 years ago
// Task 定时任务
4 years ago
func Task(c *cron.Cron) {
_ = c.AddFunc("*/10 * * * * ?", func() {
fmt.Println("task live")
})
_ = c.AddFunc("0 */10 * * * ?", func() {
cache.SetSuperPassword()
})
_ = c.AddFunc("0 */10 * * * ?", func() {
if err := cache.FlushNewerGoodsID(); err != nil {
fmt.Println("设置新人特惠集合 出错:", err)
}
})
_ = c.AddFunc("0 */10 * * * ?", func() {
a := nuonuo.NuoNuoBill{}
a.NuoTask()
})
_ = c.AddFunc("0 */10 * * * ?", func() {
NoticeDelete()
})
_ = c.AddFunc("0 */1 * * * ?", func() {
message.SendMessage()
})
_ = c.AddFunc("0 */1 * * * ?", func() {
message.DeliveryTimeOut()
})
_ = c.AddFunc("0 */1 * * * ?", func() {
order3.PlanMonitorTradeSucOrderTask()
})
_ = c.AddFunc("0 */1 * * * ?", func() {
order3.PlanMonitorExpireOrderTask()
})
_ = c.AddFunc("0 */1 * * * ?", func() {
4 years ago
AfterNotToDoTwo()
4 years ago
})
4 years ago
if configs.IsProductionEnv() {
go TaskMq()
}
}
func TaskMq() {
go skuChange()
go skuPrice()
go orderCreate()
go OrderPay()
go OrderStockOut()
4 years ago
go OrderFinished()
4 years ago
go OrderCancel()
}
const maxRetryNum = 3
func OrderCancel() {
q := fmt.Sprintf("order.cancel.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
var m model.OrderCancelMessage
err := json.Unmarshal(data, &m)
var od manage.RecookOrderInfoModel
if err = mysql.Db.First(&od, "jcook_order_id = ?", m.OrderID).Error; err != nil {
if err != gorm2.ErrRecordNotFound {
log.Println(err.Error())
return err
} else {
return nil
}
}
switch m.Status {
case 1:
if od.Status == 0 {
// 未支付订单直接取消
return nil
} else {
tx := dbc.DB.Begin()
{
var asGoods []after.RecookAfterSalesGoodsModel
tx.Find(&asGoods, "order_id = ?", od.Id)
for _, v := range asGoods {
if v.RefundStatus == 1 {
continue
}
t := &after2.UpdateOrderCheckStatusReq{
AsID: v.Id,
}
if err = after2.Refund(tx, v, od, t, manage2.RecookManageUserInfoModel{}); err != nil {
tx.Rollback()
return err
}
}
}
if err = tx.Model(od).Update("deal_status", 11).Error; err != nil {
return err
}
tx.Commit()
}
case 2:
tx := dbc.DB.Begin()
{
od.DealStatus = 12
if err = tx.Save(&od).Error; err != nil {
return err
}
var asGoods []after.RecookAfterSalesGoodsModel
if err = tx.Find(&asGoods, "order_id = ?", od.Id).Error; err != nil {
return err
}
for _, v := range asGoods {
if err = tx.Model(v).Updates(after.RecookAfterSalesGoodsModel{
ReturnStatus: 2,
RejectReason: "系统自动拒绝",
FinishTime: formatime.NewSecondNow(),
}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Model(&manage.RecookOrderGoodsDetailModel{Id: v.OrderGoodsId}).Updates(map[string]interface{}{
"ass_type": 0,
}).Error; err != nil {
tx.Rollback()
return err
}
//这里插入日志
Log1 := after.RecookAfterSalesLogsModel{
AsID: v.Id,
Title: "平台拒绝退款",
Content: "拒绝买家退款申请,如有疑问,请联系客服",
CTime: formatime.NewSecondNow(),
User: "系统",
UserId: 0,
}
tx.Create(&Log1)
Log2 := after.RecookAfterSalesLogsModel{
AsID: v.Id,
Title: "退款关闭",
Content: "系统自动关闭",
CTime: formatime.NewSecondNow(),
User: "系统",
UserId: 0,
}
tx.Create(&Log2)
}
}
tx.Commit()
}
return nil
})
}
func OrderFinished() {
q := fmt.Sprintf("order.finished.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
var m model.OrderFinishMessage
err := json.Unmarshal(data, &m)
if err != nil {
log.Println(err.Error())
return err
}
var od order4.Information
if err = mysql.Db.First(&od, "jcook_order_id = ?", m.OrderID).Error; err != nil {
log.Println(err.Error())
return err
}
4 years ago
od.JCookStatus = 1
if err = mysql.Db.Save(od).Error; err != nil {
log.Println(err.Error())
return err
}
4 years ago
return nil
})
}
func OrderStockOut() {
q := fmt.Sprintf("order.stock.out.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
var m model.OrderStockOutMessage
err := json.Unmarshal(data, &m)
if err != nil {
log.Println(err.Error())
return err
}
var od manage.RecookOrderInfoModel
if err = mysql.Db.First(&od, "jcook_order_id = ?", m.OrderID).Error; err != nil {
log.Println(err.Error())
return err
}
var orderSku []manage.RecookOrderGoodsDetailModel
if err = mysql.Db.Find(&orderSku, "order_id =? ", od.Id).Error; err != nil {
log.Println(err.Error())
return err
}
ep := make([]order2.ExpressInfo, 0)
for _, v := range m.Packages {
ep = append(ep, order2.ExpressInfo{
ExpressCompName: v.LogisticsName,
ExpressNo: v.WaybillCode,
})
}
for index, j := range orderSku {
order2.Expresslogic.Express(j.VendorId, "jcook", orderSku[index], ep)
}
return nil
})
}
func OrderPay() {
q := fmt.Sprintf("order.pay.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
time.Sleep(time.Second)
var m model.OrderPayMessage
err := json.Unmarshal(data, &m)
if err != nil {
log.Println(err.Error())
return err
}
var od manage.RecookOrderInfoModel
if err = mysql.Db.First(&od, "jcook_order_id = ?", m.OrderID).Error; err != nil {
log.Println(err.Error())
return nil
}
if od.Status == 0 {
return errors.New("订单未支付")
}
if od.Status == 1 {
client := jcook.GetClient()
req := jcook.OrderPushReq{
OrderID: uint(m.OrderID),
}
var resp jcook.OrderPushResp
if err = client.Exec(req, &resp); err != nil {
return err
}
}
return nil
})
}
func orderCreate() {
q := fmt.Sprintf("order.create.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
var m model.OrderCreateMessage
err := json.Unmarshal(data, &m)
if err != nil {
log.Println(err.Error())
return err
}
if m.RootOrderID != 0 {
// 发生拆单.
var rod manage.RecookOrderInfoModel
if err := mysql.Db.Preload("OrderSku").First(&rod, "jcook_order_id = ?", m.RootOrderID).Error; err != nil {
return err
}
var pod manage.RecookOrderInfoModel
if err := mysql.Db.Preload("OrderSku").First(&pod, "jcook_order_id = ?", m.ParentOrderID).Error; err != nil {
return err
}
if err := mysql.Db.Transaction(func(tx *gorm2.DB) error {
rod.IsSplit = true
rod.Status = 3
tx.Save(&rod)
pod.IsSplit = true
pod.Status = 3
tx.Save(&pod)
var ids []uint64
for _, v := range m.SkuList {
ids = append(ids, v.SkuID)
}
var orderSku []manage.RecookOrderGoodsDetailModel
var sku []goods2.RecookGoodsSkuModel
sub := tx.Select("id").Find(&sku, "third_party_sku_id in (?)", ids)
if err := tx.Find(&orderSku, "sku_id in (?) AND order_id = ?", sub, rod.Id).Error; err != nil {
log.Println(err.Error())
return err
}
// 拆分拆单运费
unit := m.FreightFee.Div(decimal.NewFromInt(int64(len(orderSku)))).Round(2)
fee := m.FreightFee
unitMap := make(map[uint]decimal.Decimal)
var orderSkuNew []manage.RecookOrderGoodsDetailModel
for i := 0; i < len(orderSku); i++ {
if i == len(orderSku)-1 {
unitMap[orderSku[i].SkuId] = fee
break
}
fee = fee.Sub(unit)
unitMap[orderSku[i].SkuId] = unit
}
// 拆分瑞比抵扣金额
isFinished := false
var parallelOrder []manage.RecookOrderInfoModel
tx.Preload("OrderSku").Find(&parallelOrder, "jcook_parent_id = ?", m.ParentOrderID)
temp := decimal.Zero
count := 0
for _, v := range parallelOrder {
temp = temp.Add(v.CoinTotalAmount)
count += len(v.OrderSku)
}
if count+len(orderSku) == len(pod.OrderSku) {
// 同级拆分完成
isFinished = true
}
coinMap := make(map[uint]decimal.Decimal)
for i := 0; i < len(orderSku); i++ {
acc := orderSku[i].GoodsAmount.Add(unitMap[orderSku[i].SkuId])
coin := acc.Div(pod.GoodsTotalAmount.Add(pod.ExpressTotalFee)).
Mul(pod.CoinTotalAmount).
Round(2)
if coin.GreaterThan(acc) {
coin = acc
}
if i == len(orderSku)-1 && isFinished {
coinMap[orderSku[i].SkuId] = pod.CoinTotalAmount.Sub(temp)
break
}
temp = temp.Add(coin)
coinMap[orderSku[i].SkuId] = coin
}
coinTotal := decimal.Zero
goodsTotalAmount := decimal.Zero
goodsTotalCommission := decimal.Zero
actualAmount := decimal.Zero
for _, v := range orderSku {
goodsTotalAmount = goodsTotalAmount.Add(v.GoodsAmount)
goodsTotalCommission = goodsTotalCommission.Add(v.TotalCommission)
actualAmount = actualAmount.Add(v.GoodsAmount).Add(unitMap[v.SkuId]).Sub(coinMap[v.SkuId])
}
orderNew := manage.RecookOrderInfoModel{
AncestorId: rod.AncestorId,
ParentId: rod.ParentId,
SharerId: rod.SharerId,
LiveId: rod.LiveId,
UserId: rod.UserId,
UserRole: rod.UserRole,
Title: "",
CoinTotalAmount: coinTotal,
ExpressTotalFee: m.FreightFee,
GoodsTotalAmount: goodsTotalAmount,
GoodsTotalCommission: goodsTotalCommission,
ActualTotalAmount: actualAmount,
Channel: rod.Channel,
ShippingMethod: rod.ShippingMethod,
StoreId: rod.StoreId,
BuyerMessage: rod.BuyerMessage,
Status: 1,
ExpressStatus: rod.ExpressStatus,
InvoiceStatus: rod.InvoiceStatus,
CreatedAt: rod.CreatedAt,
ExpireTime: rod.ExpireTime,
PayIP: rod.PayIP,
TradeNo: rod.TradeNo,
PayTime: rod.PayTime,
PayMethod: rod.PayMethod,
RbacId: rod.RbacId,
IsFirst: rod.IsFirst,
VirtualID: rod.VirtualID,
PayType: rod.PayType,
Kind: rod.Kind,
JCookOrderID: uint(m.OrderID),
JCookRootID: uint(m.RootOrderID),
JCookParentID: uint(m.ParentOrderID),
}
var orderAddr manage.RecookOrderAddrModel
tx.First(&orderAddr, "order_id = ?", rod.Id)
if err := tx.Create(&orderNew).Error; err != nil {
log.Println(err.Error())
return err
}
orderAddr.Id = 0
orderAddr.OrderId = orderNew.Id
if err := tx.Create(&orderAddr).Error; err != nil {
log.Println(err.Error())
return err
}
var his user2.RecookUserCoinHistory
if err = tx.First(&his, "order_id = ?", pod.Id).Error; err != nil {
return err
}
his.Status = 2
if err = tx.Save(&his).Error; err != nil {
return err
}
{
his.ID = 0
his.Income = orderNew.GoodsTotalCommission
his.OrderAmount = orderNew.GoodsTotalAmount.Add(orderNew.ExpressTotalFee)
his.CreatedAt = formatime.NewSecondNow()
his.OrderId = orderNew.Id
his.Status = 1
if err = tx.Save(&his).Error; err != nil {
return err
}
}
for _, v := range orderSku {
v.Id = 0
v.OrderId = orderNew.Id
v.ExpressFee = unitMap[v.SkuId]
v.CoinAmount = coinMap[v.SkuId]
v.ActualAmount = v.GoodsAmount.Add(v.ExpressFee).Sub(coinMap[v.SkuId])
coinTotal = coinTotal.Add(coinMap[v.SkuId])
orderSkuNew = append(orderSkuNew, v)
}
orderNew.CoinTotalAmount = coinTotal
if err = tx.Save(orderNew).Error; err != nil {
log.Println(err.Error())
return err
}
if err = tx.Create(orderSkuNew).Error; err != nil {
log.Println(err.Error())
return err
}
if len(parallelOrder) != 0 {
date, _ := strconv.Atoi(orderNew.PayTime.Time.Format("200601"))
//sub1 := tx.Table((&user.RecookUserTreeModel{}).TableName()).
// Select("root_id").Where("user_id = ?", orderNew.UserId)
//tx.Table((&user.RecookUserTeamExpectIncomeModel{}).TableName()).
// Where("user_id in (?) and date = ?", sub1, date).Updates(map[string]interface{}{
// "order_count": gorm.Expr("order_count + 1"),
//})
key1 := "purchase_count"
key2 := "not_recv_count"
userID := orderNew.UserId
if orderNew.UserId != orderNew.SharerId {
key1 = "guide_count"
key2 = "not_guide_count"
userID = orderNew.SharerId
}
//tx.Table((&user.RecookUserTeamExpectIncomeModel{}).TableName()).
// Where("user_id = ? and date = ?", userID, date).Updates(map[string]interface{}{
// key1: gorm.Expr(fmt.Sprintf("%s + 1", key1)),
//})
tx.Table((&user.RecookUserDayExpectIncomeModel{}).TableName()).
Where("user_id = ? and day = ?", userID, date).Updates(map[string]interface{}{
key1: gorm.Expr(fmt.Sprintf("%s + 1", key1)),
key2: gorm.Expr(fmt.Sprintf("%s + 1", key2)),
})
}
return nil
}); err != nil {
return err
}
}
return nil
})
}
func skuPrice() {
q := fmt.Sprintf("sku.price.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
client := jcook.GetClient()
var m model.SkuChange
err := json.Unmarshal(data, &m)
if err != nil {
log.Println(err.Error())
return err
}
rq := jcook.SkuPriceReq{
SkuIDSet: []uint{m.SkuID},
}
var res []jcook.SkuPriceResp
if err := client.Exec(rq, &res); err != nil {
log.Println(err.Error())
return err
}
if len(res) == 0 {
return nil
}
t := res[0]
var sku goods2.RecookGoodsSkuModel
mysql.Db.Table(sku.TableName()).Where("third_party_sku_id = ?", t.SkuID).Updates(map[string]interface{}{
"purchase_price": t.SupplyPrice,
})
return nil
})
}
func skuChange() {
q := fmt.Sprintf("sku.change.queue.%s", configs.ConfigJCookAppKey)
mq.Conn.ListenRetry(q, maxRetryNum, func(data []byte) error {
do := downloader.New(nil, "/jcook", 10)
do.Start()
client := jcook.GetClient()
var m model.SkuChange
_ = json.Unmarshal(data, &m)
rq := jcook.SkuDetailReq{
SkuIDSet: []uint{m.SkuID},
}
var gs goods2.RecookGoodsInfoModel
if err := mysql.Db.First(&gs, "third_party_id = ? AND third_party_type = 3 ", m.SkuID).Error; err != nil {
if err == gorm2.ErrRecordNotFound {
return nil
}
log.Println(err.Error())
return err
}
t := time.Unix(m.Timestamp, 0)
if t.Before(gs.UpdatedAt.Time) {
log.Println("时间不正常")
return nil
}
var res []jcook.SkuDetailResp
if err := client.Exec(rq, &res); err != nil {
return err
}
if len(res) == 0 {
return nil
}
if err := mysql.Db.Transaction(func(tx *gorm2.DB) error {
patch := res[0]
var pu uint
if patch.SkuDetailBase.Yn && patch.SkuDetailBase.Status {
pu = 1
} else {
pu = 0
}
p1 := map[string]interface{}{
"goods_name": patch.SkuDetailBase.SkuName,
"updated_at": formatime.NewSecondNow(),
"jcook_publish_status": pu,
}
if pu == 0 {
p1["publish_status"] = 0
}
if err := tx.Model(&gs).Updates(p1).Error; err != nil {
return err
}
do.AddTask(patch.SkuDetailBase.MainPhoto)
if err := tx.Table((&goods2.RecookGoodsSkuModel{}).TableName()).Where("goods_id = ?", gs.Id).Update("pic_url", do.GetPath(patch.SkuDetailBase.MainPhoto)).Error; err != nil {
return err
}
if err := tx.Delete(&goods2.RecookGoodsMainPhotoModel{}, "goods_id = ?", gs.Id).Error; err != nil {
return err
}
if err := tx.Delete(&goods2.RecookGoodsDetailPhotoModel{}, "goods_id = ?", gs.Id).Error; err != nil {
return err
}
mp := make([]goods2.RecookGoodsMainPhotoModel, 0)
dp := make([]goods2.RecookGoodsDetailPhotoModel, 0)
if len(patch.Images) == 1 {
mp = append(mp, goods2.RecookGoodsMainPhotoModel{
GoodsId: gs.Id,
Url: do.GetPath(patch.Images[0].Url),
IsMaster: 0,
Name: "api",
OrderNo: 1,
Width: 0,
Height: 0,
})
}
for ki, k := range patch.Images {
do.AddTask(k.Url)
master := 0
if k.IsPrimer {
master = 1
}
mp = append(mp, goods2.RecookGoodsMainPhotoModel{
GoodsId: gs.Id,
Url: do.GetPath(k.Url),
IsMaster: master,
Name: "api",
OrderNo: ki,
Width: 0,
Height: 0,
})
if k.IsPrimer {
mp = append(mp, goods2.RecookGoodsMainPhotoModel{
GoodsId: gs.Id,
Url: do.GetPath(k.Url),
IsMaster: 0,
Name: "api",
OrderNo: ki,
Width: 0,
Height: 0,
})
}
}
reg := regexp.MustCompile("background-image:url\\((?s:(.*?))\\)|src=\"(?s:(.*?))\"")
result := reg.FindAllStringSubmatch(patch.BigInfo.PcWDis, -1)
for ki, k := range result {
kt := k[1]
if len(strings.TrimSpace(kt)) == 0 {
if len(k) > 2 {
kt = k[2]
}
}
if len(strings.TrimSpace(kt)) == 0 {
continue
}
kt = strings.Trim(kt, "\\")
if !strings.HasPrefix(kt, "http") && !strings.HasPrefix(kt, "https") {
kt = "http:" + kt
}
do.AddTask(kt)
dp = append(dp, goods2.RecookGoodsDetailPhotoModel{
GoodsID: gs.Id,
Url: do.GetPath(kt),
Name: "api",
OrderNo: ki,
Width: 0,
Height: 0,
})
}
if len(mp) > 0 {
if err := tx.Create(&mp).Error; err != nil {
log.Println(err.Error())
return err
}
}
if len(dp) > 0 {
if err := tx.Create(&dp).Error; err != nil {
log.Println(err.Error())
return err
}
}
return nil
}); err != nil {
log.Println(err.Error())
return err
}
do.Wait()
return nil
})
}
func NoticeDelete() {
4 years ago
log.Println("清理商详")
var data []goods2.RecookGoodsNoticeModel
if err := dbc.DB.Table((&goods2.RecookGoodsNoticeModel{}).TableName()).Find(&data).Error; err != nil {
log.Println("task error: ", err.Error())
return
}
for _, v := range data {
if v.EndTime.Time.Before(time.Now()) {
var temp goods2.RecookGoodsNoticeGoodsModel
if err := dbc.DB.Delete(&temp, "notice_id = ?", v.Id).Error; err != nil {
4 years ago
log.Println("task error: ", err.Error())
continue
}
4 years ago
if err := dbc.DB.Delete(&v).Error; err != nil {
log.Println("task error: ", err.Error())
continue
4 years ago
}
}
}
}
4 years ago
//48小时自动关闭未填写单号的售后
var days1 time.Duration = 2
type updateReturnSuccessParam struct {
AsID uint `json:"asId" binding:"required"`
Opinion uint `json:"opinion"` // 1同意退款退货 2拒绝
RejectReason string `json:"rejectReason"`
}
// AfterNotToDoTwo 买家没有填写单号
func AfterNotToDoTwo() {
t := time.Now().Add(time.Hour * 72 * -days1)
var afterSales []after.RecookAfterSalesGoodsModel
dbc.DB.Find(&afterSales, "return_status = 3 AND ass_type=2 AND check_time <= ?", t.Format("2006-01-02 15:04:05"))
for _, v := range afterSales {
var p updateReturnSuccessParam
p = updateReturnSuccessParam{
v.Id,
2,
"72小时未填写单号系统自动关闭",
}
UpdateNotExp(p, v)
}
}
// UpdateNotExp 没有填写单号,退货被拒绝
func UpdateNotExp(p updateReturnSuccessParam, asGoods after.RecookAfterSalesGoodsModel) {
var orderInfo manage.RecookOrderInfoModel
if err := dbc.DB.First(&orderInfo, asGoods.OrderId).Error; err != nil {
return
}
if p.Opinion == 2 {
tx := mysql.Db.Begin()
{
if err := tx.Model(&asGoods).Updates(&after.RecookAfterSalesGoodsModel{
ReturnStatus: 6,
RejectReason: p.RejectReason,
FinishTime: formatime.NewSecondNow(),
IsClosed: 1,
}).Error; err != nil {
tx.Rollback()
return
}
if err := tx.Model(&manage.RecookOrderGoodsDetailModel{Id: asGoods.OrderGoodsId}).Updates(map[string]interface{}{
"ass_type": 0,
"is_closed": 1,
}).Error; err != nil {
tx.Rollback()
return
}
if err := hook.AfterHook.Close(&asGoods); err != nil {
tx.Rollback()
return
}
}
tx.Commit()
}
}