|
@@ -9,6 +9,7 @@ import (
|
|
|
"io"
|
|
"io"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+ "sync"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
"fohow.com/apps/models/balance_model"
|
|
"fohow.com/apps/models/balance_model"
|
|
@@ -24,6 +25,19 @@ type SyncData struct {
|
|
|
CreatedAt time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)"`
|
|
CreatedAt time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+var takeCashTimeoutLock sync.Mutex
|
|
|
|
|
+
|
|
|
|
|
+type TakeCashTimeoutResult struct {
|
|
|
|
|
+ StartedAt string `json:"started_at"`
|
|
|
|
|
+ FinishedAt string `json:"finished_at"`
|
|
|
|
|
+ Scanned int `json:"scanned"`
|
|
|
|
|
+ TimedOut int `json:"timed_out"`
|
|
|
|
|
+ Refunded int `json:"refunded"`
|
|
|
|
|
+ Notified int `json:"notified"`
|
|
|
|
|
+ Skipped int `json:"skipped"`
|
|
|
|
|
+ Failed int `json:"failed"`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (self *SyncData) Sign() string {
|
|
func (self *SyncData) Sign() string {
|
|
|
h := md5.New()
|
|
h := md5.New()
|
|
|
s := fmt.Sprintf("%s,%s,%d,!iGUESS", self.UnionId, self.Amount, self.CreatedAt.Unix())
|
|
s := fmt.Sprintf("%s,%s,%d,!iGUESS", self.UnionId, self.Amount, self.CreatedAt.Unix())
|
|
@@ -73,11 +87,16 @@ func autoTakeCash() {
|
|
|
}
|
|
}
|
|
|
//提现记录状态判断
|
|
//提现记录状态判断
|
|
|
cashRecord := balance_model.GetTakeCashOrderById(id, false)
|
|
cashRecord := balance_model.GetTakeCashOrderById(id, false)
|
|
|
- //已支付或记录不存在,剔除队列
|
|
|
|
|
- if cashRecord == nil || cashRecord.State == 1 {
|
|
|
|
|
|
|
+ //已处理、记录不存在,或超过24小时未处理,剔除队列
|
|
|
|
|
+ if cashRecord == nil || cashRecord.State != 0 {
|
|
|
lib_redis.ThrowOutRedisList(cashKey, takeId)
|
|
lib_redis.ThrowOutRedisList(cashKey, takeId)
|
|
|
continue
|
|
continue
|
|
|
}
|
|
}
|
|
|
|
|
+ if isTakeCashTimeout(cashRecord) {
|
|
|
|
|
+ lib_redis.ThrowOutRedisList(cashKey, takeId)
|
|
|
|
|
+ processTakeCashTimeoutOrder(cashRecord, nil)
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
//未审批或者不达处理时间不提现,则下次处理
|
|
//未审批或者不达处理时间不提现,则下次处理
|
|
|
if cashRecord.AuditState < 1 || cashRecord.ExpcPayAt.Unix() > time.Now().Unix() {
|
|
if cashRecord.AuditState < 1 || cashRecord.ExpcPayAt.Unix() > time.Now().Unix() {
|
|
|
continue
|
|
continue
|
|
@@ -96,6 +115,167 @@ func autoTakeCash() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func StartTakeCashTimeoutScheduler() {
|
|
|
|
|
+ if !beego.AppConfig.DefaultBool("TakeCashTimeoutCronEnabled", true) {
|
|
|
|
|
+ beego.BeeLogger.Warn("take cash timeout scheduler disabled")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ go func() {
|
|
|
|
|
+ time.Sleep(30 * time.Second)
|
|
|
|
|
+ for {
|
|
|
|
|
+ ProcessTakeCashTimeoutOrders()
|
|
|
|
|
+ time.Sleep(5 * time.Minute)
|
|
|
|
|
+ }
|
|
|
|
|
+ }()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func ProcessTakeCashTimeoutOrders() *TakeCashTimeoutResult {
|
|
|
|
|
+ if !takeCashTimeoutLock.TryLock() {
|
|
|
|
|
+ beego.BeeLogger.Warn("take cash timeout task already running")
|
|
|
|
|
+ return &TakeCashTimeoutResult{StartedAt: time.Now().Format("2006-01-02 15:04:05")}
|
|
|
|
|
+ }
|
|
|
|
|
+ defer takeCashTimeoutLock.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ ret := &TakeCashTimeoutResult{StartedAt: time.Now().Format("2006-01-02 15:04:05")}
|
|
|
|
|
+ orders := balance_model.GetTimeoutTakingCashOrders(200)
|
|
|
|
|
+ ret.Scanned = len(orders)
|
|
|
|
|
+ for _, order := range orders {
|
|
|
|
|
+ processTakeCashTimeoutOrder(order, ret)
|
|
|
|
|
+ }
|
|
|
|
|
+ ret.FinishedAt = time.Now().Format("2006-01-02 15:04:05")
|
|
|
|
|
+ beego.BeeLogger.Warn("take cash timeout task finished: %+v", ret)
|
|
|
|
|
+ return ret
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func processTakeCashTimeoutOrder(order *balance_model.TakeCashOrder, result *TakeCashTimeoutResult) {
|
|
|
|
|
+ if order == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if order.State != 0 || order.AuditState != 1 || !isTakeCashTimeout(order) {
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ wxUser := user_model.GetWxUserById(order.WxUId, false)
|
|
|
|
|
+ if wxUser == nil {
|
|
|
|
|
+ beego.BeeLogger.Error("take cash timeout wxUser[%d] is not exist, order=%s", order.WxUId, order.OrderId)
|
|
|
|
|
+ incTakeCashTimeoutFailed(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ canTimeout := false
|
|
|
|
|
+ if order.TradeNo == "" {
|
|
|
|
|
+ canTimeout = true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ payCode := sys_config.GetPayConfigByDepart(wxUser.Depart, true)
|
|
|
|
|
+ wechatState, ok := prepareTimeoutMerchantTransfer(order, payCode)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ switch wechatState {
|
|
|
|
|
+ case wx_mp.MerchantTransferSuccess:
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ case wx_mp.MerchantTransferFail, wx_mp.MerchantTransferCancelled:
|
|
|
|
|
+ canTimeout = true
|
|
|
|
|
+ default:
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !canTimeout {
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ marked, err := balance_model.MarkTakeCashOrderTimeout(order.OrderId)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ incTakeCashTimeoutFailed(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if !marked {
|
|
|
|
|
+ incTakeCashTimeoutSkipped(result)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if result != nil {
|
|
|
|
|
+ result.TimedOut++
|
|
|
|
|
+ }
|
|
|
|
|
+ if refundTakeCashTimeout(order) && result != nil {
|
|
|
|
|
+ result.Refunded++
|
|
|
|
|
+ }
|
|
|
|
|
+ if notifyErr := wx_mp.SendTakeCashTimeoutSubscribe(wxUser.Openid, order.Count+order.Fee); notifyErr != nil {
|
|
|
|
|
+ beego.BeeLogger.Error("take cash timeout subscribe order[%s] err[%s]", order.OrderId, notifyErr)
|
|
|
|
|
+ } else if result != nil {
|
|
|
|
|
+ result.Notified++
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func prepareTimeoutMerchantTransfer(order *balance_model.TakeCashOrder, payCode string) (string, bool) {
|
|
|
|
|
+ ret, err := wx_mp.QueryMerchantTransferByOutBillNo(order.OrderId, payCode)
|
|
|
|
|
+ if err != nil || ret == nil {
|
|
|
|
|
+ beego.BeeLogger.Error("take cash timeout query transfer order[%s] err[%v]", order.OrderId, err)
|
|
|
|
|
+ return "", false
|
|
|
|
|
+ }
|
|
|
|
|
+ switch ret.State {
|
|
|
|
|
+ case wx_mp.MerchantTransferSuccess:
|
|
|
|
|
+ applyMerchantTransferResult(order, ret)
|
|
|
|
|
+ return ret.State, true
|
|
|
|
|
+ case wx_mp.MerchantTransferFail, wx_mp.MerchantTransferCancelled:
|
|
|
|
|
+ return ret.State, true
|
|
|
|
|
+ case wx_mp.MerchantTransferCanceling:
|
|
|
|
|
+ applyMerchantTransferResult(order, ret)
|
|
|
|
|
+ return ret.State, true
|
|
|
|
|
+ }
|
|
|
|
|
+ if !wx_mp.MerchantTransferStatePending(ret.State) {
|
|
|
|
|
+ beego.BeeLogger.Warn("take cash timeout order[%s] skipped unexpected transfer state=%s", order.OrderId, ret.State)
|
|
|
|
|
+ return ret.State, false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cancelRet, err := wx_mp.CancelMerchantTransferByOutBillNo(order.OrderId, payCode)
|
|
|
|
|
+ if err != nil || cancelRet == nil {
|
|
|
|
|
+ beego.BeeLogger.Error("take cash timeout cancel transfer order[%s] err[%v]", order.OrderId, err)
|
|
|
|
|
+ return ret.State, false
|
|
|
|
|
+ }
|
|
|
|
|
+ if cancelRet.State == wx_mp.MerchantTransferSuccess {
|
|
|
|
|
+ applyMerchantTransferResult(order, cancelRet)
|
|
|
|
|
+ return cancelRet.State, true
|
|
|
|
|
+ }
|
|
|
|
|
+ if cancelRet.State == wx_mp.MerchantTransferCanceling {
|
|
|
|
|
+ applyMerchantTransferResult(order, cancelRet)
|
|
|
|
|
+ return cancelRet.State, true
|
|
|
|
|
+ }
|
|
|
|
|
+ return cancelRet.State, true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func refundTakeCashTimeout(order *balance_model.TakeCashOrder) bool {
|
|
|
|
|
+ amount := order.Count + order.Fee
|
|
|
|
|
+ if amount <= 0 {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if balance_model.GetCashBalanceBySourceAndRId(balance_model.CASH_SOURCE_TAKE_CASH_TIMEOUT, order.OrderId) != nil {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ item := new(balance_model.CashBalance).Create(order.WxUId, amount, balance_model.CASH_SOURCE_TAKE_CASH_TIMEOUT, order.OrderId, balance_model.CASH_SOURCE_TAKE_CASH_TIMEOUT_NAME)
|
|
|
|
|
+ return item != nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func isTakeCashTimeout(order *balance_model.TakeCashOrder) bool {
|
|
|
|
|
+ return order != nil && !order.CreatedAt.IsZero() && time.Now().After(order.CreatedAt.Add(24*time.Hour))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func incTakeCashTimeoutSkipped(result *TakeCashTimeoutResult) {
|
|
|
|
|
+ if result != nil {
|
|
|
|
|
+ result.Skipped++
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func incTakeCashTimeoutFailed(result *TakeCashTimeoutResult) {
|
|
|
|
|
+ if result != nil {
|
|
|
|
|
+ result.Failed++
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func updateTakeCashByMerchantTransfer(order *balance_model.TakeCashOrder, openid, payCode string) (*wx_mp.MerchantTransferResponse, error) {
|
|
func updateTakeCashByMerchantTransfer(order *balance_model.TakeCashOrder, openid, payCode string) (*wx_mp.MerchantTransferResponse, error) {
|
|
|
var (
|
|
var (
|
|
|
ret *wx_mp.MerchantTransferResponse
|
|
ret *wx_mp.MerchantTransferResponse
|