From aa1095b99ef8a8b457381e2f18b4ffc994a09c0c Mon Sep 17 00:00:00 2001 From: tommy <3405129587@qq.com> Date: Fri, 17 Apr 2020 15:15:50 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=AE=BE=E7=BD=AE=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/client/bully_screen.go | 2 +- controllers/client/live.go | 190 ++++++++++++++----------------- controllers/client/reward.go | 8 +- controllers/common/wechat_oauth.go | 27 +++-- controllers/pc/bully_screen.go | 27 +---- controllers/pc/reward.go | 34 ++---- go.mod | 1 + libs/im/im.go | 9 +- log/hdzj.log | 2 + models/bully_screen_history.go | 26 ++--- models/init_models.go | 1 + models/live_red_pack.go | 2 +- models/live_red_pack_info.go | 3 +- models/reward_history.go | 16 ++- models/user_order.go | 24 ++-- models/user_transfer.go | 53 +++++++++ services/bully_reward/dao.go | 140 +++++++++++++++++++++++ services/im/im.go | 19 ++++ services/pay/const.go | 4 +- services/pay/order.go | 224 ++++++++++++++++++++++--------------- services/pay/refund.go | 32 ------ services/pay/transfer.go | 163 ++++++++++++++++++++++++++- test/pay_test.go | 26 +++++ 23 files changed, 702 insertions(+), 331 deletions(-) create mode 100644 models/user_transfer.go create mode 100644 services/im/im.go delete mode 100644 services/pay/refund.go diff --git a/controllers/client/bully_screen.go b/controllers/client/bully_screen.go index fe8dede..7f01faf 100644 --- a/controllers/client/bully_screen.go +++ b/controllers/client/bully_screen.go @@ -54,7 +54,7 @@ func (t *BullyScreenCtl) PaScreen() { // 调用微信统一下单接口 amount := bullyScreenServer.Price * second ip := strings.Split(t.Request.OriginRequest.RemoteAddr, ":") - res, err := pay_service.Order("欧轩互动-霸屏支付", ip[0], user.Openid, int64(amount*100), 1, user.Id, activityId) + res, err := pay_service.UnifiedOrder("欧轩互动-霸屏支付", ip[0], user.Openid, int64(amount*100), 1, user.Id, activityId) t.CheckErr(err) history := &models.BullyScreenHistory{ diff --git a/controllers/client/live.go b/controllers/client/live.go index 6cd4f90..71c74b9 100644 --- a/controllers/client/live.go +++ b/controllers/client/live.go @@ -6,102 +6,50 @@ import ( "hudongzhuanjia/libs/filter" "hudongzhuanjia/logger" "hudongzhuanjia/models" + im_service "hudongzhuanjia/services/im" pay_service "hudongzhuanjia/services/pay" red_envelope_service "hudongzhuanjia/services/red_envelope" "hudongzhuanjia/utils" "hudongzhuanjia/utils/code" + "math" "strings" "time" ) -var MaxQueueSize = 10000 -var RequestLimit = 60 * time.Second -var GetRedPackQueue = make(chan string, MaxQueueSize) - -func loopGetRedPack() { - redPacks, err := models.GetRedPacksByStatus(1) - if err != nil { - panic(err) - } - - // 保证容量足够 - GetRedPackQueue = make(chan string, MaxQueueSize+len(redPacks)) - - // 初始化 - for _, redPack := range redPacks { - GetRedPackQueue <- redPack.TransferNo - } - - defer func() { - if errRec := recover(); errRec != nil { - logger.Error("用户转账轮询发生错误", - zap.String("函数", "loopGetRedPack"), zap.Any("错误原因", errRec)) - } - time.Sleep(5 * time.Second) - loopGetRedPack() - }() - for { - select { - case transferNo, ok := <-GetRedPackQueue: - if !ok { - panic("GetRedPackQueue通道关闭") - } - time.Sleep(RequestLimit) // 请求频率 +type NoticeRedPackEvent struct { + OutTradeNo string `json:"out_trade_no"` + Prompt string `json:"prompt"` + RedPackInfoId int64 `json:"red_pack_info_id"` + ActivityId int64 `json:"activity_id"` + Status int `json:"status"` +} - redPack := new(models.LiveRedPack) - exist, err := redPack.GetByTransferNo(transferNo) - if err != nil || !exist { - logger.Error("获取LiveRedPack表数据出现错误", zap.String("错误原因", err.Error()), - zap.Bool("是否存在", exist), zap.Int64("LiveRedPack表的id", redPack.Id)) - continue - } +var SendRedPackQueue = make(chan *NoticeRedPackEvent, math.MaxInt8) - // todo: 增加一个查询机制 --查询此次付款是否成功 - // todo: 是否需要发放系统通知 - resp, err := pay_service.TransferInfo(redPack.TransferNo) - if err != nil { - GetRedPackQueue <- transferNo - logger.Error("微信企业转账查询API", - zap.Any("错误原因", err), zap.Int64("LiveRedPack表的id", redPack.Id)) - continue - } - if resp.Status == pay_service.CODE_FAIL { // 转账失败 - _, err = pay_service.Transfer("欧轩互动-红包活动", redPack.OpenId, redPack.TransferNo, redPack.Amount) - if err != nil { - GetRedPackQueue <- transferNo - logger.Error("微信企业转账API", - zap.String("错误原因", err.Error()), zap.String("转账单号", transferNo)) - continue - } - _, err = redPack.UpdateStatusById(redPack.Id, 2) - if err != nil { - GetRedPackQueue <- transferNo - logger.Error("微信企业转账API,LiveRedPack表更新错误", - zap.String("错误原因", err.Error()), zap.String("转账单号", transferNo)) - continue - } - } else if resp.Status == pay_service.CODE_SUCCESS { - _, err = redPack.UpdateStatusById(redPack.Id, 2) - if err != nil { - logger.Error("微信企业查询,转账成功", - zap.String("错误原因", err.Error()), zap.String("转账单号", transferNo)) - continue - } - } - } +func PutSendRedPackQueue(outTradeNo, prompt string, redPackInfoId, activityId int64, status int) { + SendRedPackQueue <- &NoticeRedPackEvent{ + OutTradeNo: outTradeNo, + Prompt: prompt, + RedPackInfoId: redPackInfoId, + ActivityId: activityId, + Status: status, } } -var SendRedPackQueue = make(chan string) - func loopSendRedPack() { redPackInfos, err := models.GetLiveRedPackInfos(0) if err != nil { panic(err) } - SendRedPackQueue = make(chan string, MaxQueueSize+len(redPackInfos)) + SendRedPackQueue = make(chan *NoticeRedPackEvent, math.MaxInt8) for _, redPackInfo := range redPackInfos { - SendRedPackQueue <- redPackInfo.OutTradeNo + SendRedPackQueue <- &NoticeRedPackEvent{ + OutTradeNo: redPackInfo.OutTradeNo, + Prompt: redPackInfo.Prompt, + RedPackInfoId: redPackInfo.Id, + ActivityId: redPackInfo.ActivityId, + Status: redPackInfo.Status, + } } defer func() { @@ -112,47 +60,56 @@ func loopSendRedPack() { time.Sleep(5 * time.Second) loopSendRedPack() }() + for { select { - case outTradeNo, ok := <-SendRedPackQueue: + case param, ok := <-SendRedPackQueue: if !ok { panic("SendRedPackQueue通道异常关闭") } - redPackInfo := new(models.LiveRedPackInfo) - exist, err := redPackInfo.GetByOutTradeNo(outTradeNo) + + if param.Status != 0 && param.Status != 1 { // 已推送和已作废过滤掉 + continue + } + + userOrder := new(models.UserOrder) + exist, err := userOrder.GetByOutTradeNo(param.OutTradeNo) if err != nil || !exist { - logger.Error("通过out_trade_no获取red_pack_info", zap.String("错误原因", err.Error()), - zap.Bool("是否存在", exist), zap.String("交易单号", outTradeNo)) + logger.Error("通过out_trade_no获取user_order订单信息", zap.String("错误原因", err.Error()), + zap.Bool("是否存在", exist), zap.String("交易单号", param.OutTradeNo)) continue } - res, err := pay_service.OrderQuery(outTradeNo) - if err != nil { - SendRedPackQueue <- outTradeNo - logger.Error("微信查询订单API", zap.String("错误原因", err.Error()), - zap.String("交易单号", outTradeNo)) + if userOrder.Status == 0 { // NOPAY + SendRedPackQueue <- param + continue + } else if userOrder.Status == 1 { + param.Status = 1 + } else { + param.Status = 2 // 作废订单 } - if res.TradeState == pay_service.CODE_TRADE_SUCCESS { // 交易成功 - redPackInfo.Status = 1 - } else if res.TradeState == pay_service.CODE_TRADE_CLOSED { - redPackInfo.Status = 3 - } else if res.TradeState == pay_service.CODE_TRADE_PAYERROR { // 支付失败 - redPackInfo.Status = 4 - } else if res.TradeState == pay_service.CODE_TRADE_NOTPAY { - time.Sleep(5 * time.Second) - SendRedPackQueue <- outTradeNo + + err = im_service.SendNoticeByActivityId(param.ActivityId, map[string]interface{}{ + "prompt": param.Prompt, + "timestamp": time.Now().Unix(), + "red_pack_info_id": param.RedPackInfoId, + }) + if err != nil { + logger.Error("red_pack_info推送通知出现错误", zap.String("错误原因", err.Error()), + zap.String("交易单号", param.Prompt)) continue } - _, err = redPackInfo.UpdateStatusById(redPackInfo.Id, redPackInfo.Status) + + _, err = new(models.LiveRedPackInfo).UpdateStatusByOutTradeNo(param.OutTradeNo, param.Status) if err != nil { logger.Error("red_pack_info状态更新出现错误", zap.String("错误原因", err.Error()), - zap.String("交易单号", outTradeNo)) + zap.String("交易单号", param.OutTradeNo)) + continue } } } } func init() { - go loopGetRedPack() go loopSendRedPack() } @@ -215,7 +172,7 @@ func (t *LiveCtl) LoopQuery() { }) } -// 发送红包 +// 下单发送红包 // 维护一个队列进行循环, 遍历是否付款成功 func (t *LiveCtl) SendLiveRedPack() { userId := t.MustGetUID() // 用户 uid @@ -230,7 +187,7 @@ func (t *LiveCtl) SendLiveRedPack() { t.Assert(exist, code.MSG_USER_NOT_EXIST, "用户不存在") ip := strings.Split(t.Request.OriginRequest.RemoteAddr, ":") - res, err := pay_service.Order("欧轩互动-直播红包", ip[0], user.Openid, amount, 3, activityId, userId) + res, err := pay_service.UnifiedOrder("欧轩互动-直播红包", ip[0], user.Openid, amount, 3, activityId, userId) t.CheckErr(err) info := models.LiveRedPackInfo{} @@ -259,11 +216,34 @@ func (t *LiveCtl) SendLiveRedPack() { } res["red_pack_info_id"] = info.Id - pay_service.PutOrderChan(info.OutTradeNo) // 加入查询队列 - + go PutSendRedPackQueue(info.OutTradeNo, info.Prompt, info.Id, info.ActivityId, info.Status) // 加入发送队列 t.JSON(res) } +// 支付之后可以遍历查询是否成功 -- 前端发送消息 +func (t *LiveCtl) QueryRedPack() { + outTradeNo := t.MustGet("out_trade_no") + res, err := pay_service.OrderQuery(outTradeNo) + t.CheckErr(err) + info := new(models.LiveRedPackInfo) + exist, err := info.GetByOutTradeNo(outTradeNo) + t.CheckErr(err) + t.Assert(exist, code.MSG_LIVE_RED_PACK_NOT_EXIST, "直播红包信息不存在") + if res.TradeState == pay_service.CODE_TRADE_SUCCESS { + info.Status = 1 + info.UpdateStatusById(info.Id, info.Status) + t.JSON(map[string]interface{}{ + "red_pack_info_id": info.Id, + "status": info.Status, + }) + } else { + t.JSON(map[string]interface{}{ + "red_pack_info_id": info.Id, + "status": info.Status, + }) + } +} + // 领取红包 func (t *LiveCtl) GetRedPack() { liveRedPackInfoId := t.MustGetInt64("live_red_pack_info_id") @@ -287,14 +267,14 @@ func (t *LiveCtl) GetRedPack() { redPack.OpenId = user.Openid redPack.Receiver = user.Id redPack.TransferType = 1 - redPack.TransferNo = utils.RandomStr(32) + redPack.PartnerTradeNo = utils.RandomStr(32) row, err := redPack.UpdateStatusById(redPack.Id, 1) t.CheckErr(err) if row != 1 { t.ERROR("红包被领完了", code.MSG_LIVE_RED_PACK_NOT_EXIST) return } - GetRedPackQueue <- redPack.TransferNo // 进入队列 + pay_service.PutTransferDelayQueue("欧轩互动-红包活动", user.Openid, redPack.PartnerTradeNo, redPack.Amount, 5, 5*60) t.JSON(redPack) } diff --git a/controllers/client/reward.go b/controllers/client/reward.go index d44de81..3d1d992 100644 --- a/controllers/client/reward.go +++ b/controllers/client/reward.go @@ -27,10 +27,12 @@ func (t *RewardCtl) Reward() { if amount <= 0 { t.ERROR("打赏金额不能小于0", code.MSG_ERR_Param) + return } //检查内容是否包含敏感 if ok, _ := filter.Validate(content); !ok { t.ERROR("内容包含敏感字", code.MSG_ERR) + return } activity := new(models.Activity) @@ -52,7 +54,7 @@ func (t *RewardCtl) Reward() { t.Assert(exist, code.MSG_USER_NOT_EXIST, "用户不存在") ip := strings.Split(t.Request.OriginRequest.RemoteAddr, ":") - res, err := pay_service.Order("欧轩互动-打赏支付", ip[0], user.Openid, int64(amount*100), 2, user.Id, activityId) + res, err := pay_service.UnifiedOrder("欧轩互动-打赏支付", ip[0], user.Openid, int64(amount*100), 2, user.Id, activityId) t.CheckErr(err) _, err = core.GetXormAuto().InsertOne(&models.RewardHistory{ @@ -70,6 +72,7 @@ func (t *RewardCtl) Reward() { UpdatedAt: time.Now(), }) t.CheckErr(err) + t.JSON(res) } @@ -82,6 +85,9 @@ func (t *RewardCtl) List() { t.CheckErr(err) t.Assert(exist, code.MSG_REWARD_NOT_EXIST, "打赏不存在") + // todo: 检查订单 + t.CheckErr(bully_reward_service.CheckRewardStatus(rs.Id)) + list, err := bully_reward_service.GetRewardList(uid, rs.Id) t.CheckErr(err) t.JSON(map[string]interface{}{ diff --git a/controllers/common/wechat_oauth.go b/controllers/common/wechat_oauth.go index 208fe9b..63ecbf5 100644 --- a/controllers/common/wechat_oauth.go +++ b/controllers/common/wechat_oauth.go @@ -4,10 +4,9 @@ import ( "crypto/sha1" "encoding/xml" "fmt" + "go.uber.org/atomic" "hudongzhuanjia/controllers" "hudongzhuanjia/libs/filter" - "hudongzhuanjia/logger" - "hudongzhuanjia/services/pay" "os" "time" ) @@ -57,20 +56,32 @@ func (t *WeChatOauthCtl) Checkin() { // //} +type CDATA struct { + Text string `xml:",cdata"` +} + type CallbackParam struct { XMLName xml.Name `xml:"xml"` - ReturnCode string `xml:"return_code"` - ReturnMsg string `xml:"return_msg"` + ReturnCode CDATA `xml:"return_code"` + ReturnMsg CDATA `xml:"return_msg"` } +var counter = new(atomic.Int64) + func (t *WeChatOauthCtl) CallbackOrder() { + counter.Add(1) // 搜索支付的order表, 查找到某条记录 - logger.Sugar.Infof("%s", t.Request.OriginRequest.RemoteAddr) - t.CheckErr(pay_service.NotifyOrder(t.Request.OriginRequest)) + //t.CheckErr(pay_service.NotifyOrder(t.Request.OriginRequest)) param := new(CallbackParam) - param.ReturnCode = "SUCCESS" - param.ReturnMsg = "OK" + param.ReturnCode = CDATA{Text: "SUCCESS"} + param.ReturnMsg = CDATA{Text: "OK"} xmlRes, _ := xml.Marshal(param) + if t.Request.OriginRequest.Method == "GET" { + t.JSON(map[string]interface{}{ + "counter": counter.Load(), + "time": time.Now(), + }) + } t.XML(xmlRes) } diff --git a/controllers/pc/bully_screen.go b/controllers/pc/bully_screen.go index 2cd019b..a650953 100644 --- a/controllers/pc/bully_screen.go +++ b/controllers/pc/bully_screen.go @@ -3,6 +3,7 @@ package pc import ( "hudongzhuanjia/controllers" "hudongzhuanjia/models" + bully_reward_service "hudongzhuanjia/services/bully_reward" "hudongzhuanjia/services/pay" "hudongzhuanjia/utils" "hudongzhuanjia/utils/code" @@ -17,11 +18,6 @@ type BullyScreenCtl struct { controllers.AuthorCtl } -type BullyScreenBaseResult struct { - models.BullyScreenHistory `xorm:"extends"` - User *models.User `json:"user" xorm:"extends"` -} - //获取待审核列表 func (t *BullyScreenCtl) WaitReview() { activityId := t.MustGetInt64("activity_id") @@ -38,11 +34,8 @@ func (t *BullyScreenCtl) WaitReview() { t.Assert(exist, code.MSG_BULLY_SCREEN_SERVER_NOT_EXIST, "霸屏不存在") //根据霸屏服务得id获取待审核得霸屏列表 - result := make([]*BullyScreenBaseResult, 0) - err = core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). - Join("LEFT", "ox_user as u", "h.user_id=u.id and u.is_delete=0"). - Where("h.bully_screen_server_id=? and h.is_delete=0 and h.status=0 and h.rehearsal_id=?", - bullyScreenServer.Id, activity.RehearsalId).Desc("h.created_at").Find(&result) + t.CheckErr(bully_reward_service.CheckBullyScreenStatus(bullyScreenServer.Id)) + result, err := bully_reward_service.GetBullyScreenReview(bullyScreenServer.Id, activity.RehearsalId) t.CheckErr(err) t.JSON(map[string]interface{}{ @@ -132,11 +125,7 @@ func (t *BullyScreenCtl) Blacklist() { t.Assert(exist, code.MSG_BULLY_SCREEN_SERVER_NOT_EXIST, "霸屏活动不存在") //根据霸屏服务得id获取待审核得霸屏列表 - result := make([]*BullyScreenBaseResult, 0) - err = core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). - Join("LEFT", new(models.User).Alias("u"), "u.id=h.user_id and u.is_delete=0"). - Where("h.is_delete=0 and h.bully_screen_server_id=? and h.status=1 and h.rehearsal_id=?", - bullyScreenServer.Id, activity.RehearsalId).Desc("review_time").Find(&result) + result, err := bully_reward_service.GetBullyScreenBlacklist(bullyScreenServer.Id, activity.RehearsalId) t.CheckErr(err) t.DisplayByData(map[string]interface{}{ @@ -186,13 +175,9 @@ func (t *BullyScreenCtl) Latest() { t.CheckErr(err) t.Assert(exist, code.MSG_BULLY_SCREEN_SERVER_NOT_EXIST, "霸屏不存在") - result := new(BullyScreenBaseResult) - exist, err = core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). - Join("LEFT", new(models.User).Alias("u"), "u.id=h.user_id and u.is_delete=0"). - Where("h.bully_screen_server_id=? and h.status=2 and h.rehearsal_id=? and h.is_delete=0", - bullyScreenServer.Id, activity.RehearsalId).Desc("review_time").Get(result) + result, err := bully_reward_service.GetBullyScreenLatest(bullyScreenServer.Id, activity.RehearsalId) t.CheckErr(err) - if !exist { + if result == nil || result.Id == 0 { t.RAW(result) } diff --git a/controllers/pc/reward.go b/controllers/pc/reward.go index 16e0055..4bbb233 100644 --- a/controllers/pc/reward.go +++ b/controllers/pc/reward.go @@ -4,6 +4,7 @@ import ( "fmt" "hudongzhuanjia/controllers" "hudongzhuanjia/models" + bully_reward_service "hudongzhuanjia/services/bully_reward" "hudongzhuanjia/services/pay" "hudongzhuanjia/utils" "hudongzhuanjia/utils/code" @@ -16,10 +17,6 @@ import ( type RewardCtl struct { controllers.AuthorCtl } -type RewardResult struct { - models.RewardHistory `xorm:"extends"` - User *models.User `json:"user" xorm:"extends"` -} //获取最新的打赏记录 func (t *RewardCtl) Latest() { @@ -32,13 +29,9 @@ func (t *RewardCtl) Latest() { t.CheckErr(err) t.Assert(exist, code.MSG_REWARD_NOT_EXIST, "打赏不存在") - result := new(RewardResult) - exist, err = core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). - Join("LEFT", new(models.User).Alias("u"), "h.user_id=u.id and u.is_delete=0"). - Where("h.is_delete=0 and h.status=2 and h.reward_server_id=? and h.rehearsal_id=?", server.Id, rehearsalId). - OrderBy("review_time desc").Get(result) + result, err := bully_reward_service.GetRewardLatest(server.Id, rehearsalId) t.CheckErr(err) - if !exist { + if result == nil || result.Id == 0 { t.RAW(result) } @@ -65,14 +58,10 @@ func (t *RewardCtl) WaitReview() { t.CheckErr(err) t.Assert(exist, code.MSG_REWARD_NOT_EXIST, "打赏不存在") - t.CheckErr(pay_service.BatchQueryByActivityId(activityId)) - t.CheckErr(pay_service.BatchQueryRefundByActivityId(activityId)) + // todo: 检查订单 + t.CheckErr(bully_reward_service.CheckRewardStatus(server.Id)) - result := make([]*RewardResult, 0) - err = core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). - Join("LEFT", new(models.User).Alias("u"), "h.user_id=u.id and u.is_delete=0"). - Where("h.is_delete=0 and h.status=0 and h.reward_server_id=? and h.rehearsal_id=?", - server.Id, rehearsalId).OrderBy("h.created_at desc").Find(&result) + result, err := bully_reward_service.GetRewardReview(server.Id, rehearsalId) t.CheckErr(err) //根据打赏服务得id获取待审核得打赏列表 @@ -94,11 +83,6 @@ func (t *RewardCtl) Review() { err := core.GetXormAuto().Where("is_delete=0").In("id", ids).Find(&result) t.CheckErr(err) - //if rehearsalId != 0 { // 彩排不需要金额 - // t.RAW("审核成功") - // return - //} - // 审核 for _, v := range result { if v.Status != 0 { @@ -189,11 +173,7 @@ func (t *RewardCtl) Blacklist() { // 根据打赏服务id获取待审核的名单 // status=true 代表审核不通过还是通过 // 目前表示不通过,bully_screen也一样 - result := make([]*RewardResult, 0) - err = core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). - Join("LEFT", new(models.User).Alias("u"), "h.user_id=u.id and u.is_delete=0"). - Where("h.is_delete=0 and h.status=1 and h.reward_server_id=? and h.rehearsal_id=?", - server.Id, rehearsalId).OrderBy("review_time desc").Find(&result) + result, err := bully_reward_service.GetRewardBlacklist(server.Id, rehearsalId) t.CheckErr(err) t.JSON(map[string]interface{}{ diff --git a/go.mod b/go.mod index d437dab..a7132aa 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/xormplus/builder v0.0.0-20200331055651-240ff40009be // indirect github.com/xormplus/core v0.0.0-20200308074340-f3bce19d5f31 // indirect github.com/xormplus/xorm v0.0.0-20200410045938-f6b4c1cd3b8b + go.uber.org/atomic v1.6.0 go.uber.org/zap v1.14.1 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a // indirect golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect diff --git a/libs/im/im.go b/libs/im/im.go index 632a7b2..8fc3385 100644 --- a/libs/im/im.go +++ b/libs/im/im.go @@ -22,8 +22,9 @@ type NoticeType int const NoticeLiveRedPackStart NoticeType = 256 // 通知直播用户红包开始了 const NoticeLiveRedPackEnd NoticeType = 257 // 通知直播用户红包结束了 -const NoticeShakeRedPackStart NoticeType = 258 // 通知摇红包开始了 -const NoticeShakeRedPackEnd NoticeType = 259 // 通知摇红包结束了 +const NoticeLiveRedPackGet NoticeType = 258 // 某人摇中红包 +const NoticeShakeRedPackStart NoticeType = 259 // 通知摇红包开始了 +const NoticeShakeRedPackEnd NoticeType = 260 // 通知摇红包结束了 type CommonResult struct { ActionStatus string `json:"ActionStatus"` @@ -79,7 +80,7 @@ type SendGroupSystemNotificationParam struct { ToMembersAccount []string `json:"ToMembers_Account,omitempty"` } -func SendGroupSystemNotification(groupId string, noticeType NoticeType, notice string, members ...string) error { +func SendGroupSystemNotification(groupId string, noticeType NoticeType, data map[string]interface{}, members ...string) error { sig, err := GenSig("admin") if err != nil { return err @@ -90,7 +91,7 @@ func SendGroupSystemNotification(groupId string, noticeType NoticeType, notice s var m = make(map[string]interface{}) m["type"] = noticeType - m["notice"] = notice + m["data"] = data content, err := json.Marshal(&m) if err != nil { return err diff --git a/log/hdzj.log b/log/hdzj.log index 1bc6a2d..c5dbab7 100644 --- a/log/hdzj.log +++ b/log/hdzj.log @@ -15,3 +15,5 @@ 2020-04-07 14:31:17.846 ERROR logger/logger.go:92 check err {"error": "Error 1146: Table 'hudongzhuanjia.ox_live_viewer' doesn't exist"} 2020-04-07 14:33:15.037 ERROR logger/logger.go:92 check err {"error": "Error 1292: Incorrect datetime value: '' for column 'created_at' at row 1"} 2020-04-07 14:33:46.303 ERROR logger/logger.go:92 check err {"error": "Error 1292: Incorrect datetime value: '' for column 'created_at' at row 1"} +2020-04-16 14:25:59.479 INFO common/wechat_oauth.go:68 127.0.0.1:5000 +2020-04-16 14:25:59.490 ERROR logger/logger.go:92 check err {"error": "xml.NewDecoder.Decode:EOF"} diff --git a/models/bully_screen_history.go b/models/bully_screen_history.go index 9a2c17b..d7caac0 100644 --- a/models/bully_screen_history.go +++ b/models/bully_screen_history.go @@ -20,7 +20,7 @@ type BullyScreenHistory struct { Style int `json:"style" xorm:"not null comment('服务样式') INT(11)"` Second int `json:"second" xorm:"not null comment('霸屏时间(秒)') INT(11)"` Content string `json:"content" xorm:"not null comment('内容') TEXT"` - Status int `json:"status" xorm:"not null default(0) comment('[-1未支付,0未审核,1未通过,2已通过,3已推送,4已退款,5被取消]') INT(11)"` + Status int `json:"status" xorm:"not null default(0) comment('[-1未支付,0未审核,1未通过,2已通过,3已推送,4已作废]') INT(11)"` Amount float64 `json:"amount" xorm:"not null default 0.00 comment('霸屏金额') DECIMAL(10)"` ReviewTime int64 `json:"review_time" xorm:"not null comment('审核的时间') INT(11)"` Version int64 `json:"version" xorm:"not null version comment('乐观锁') INT(11)"` @@ -38,22 +38,20 @@ func (t *BullyScreenHistory) UpdateStatus(id int64, status int) (int64, error) { return core.GetXormAuto().ID(id).Cols("status").Update(t) } -// -//func (t *BullyScreenHistory) UpdateStatusByUserOrderId(userOrderId interface{}, status int) (int64, error) { -// t.Status = status -// return core.GetXormAuto().Where("is_delete=0 and user_order_id=?", userOrderId). -// Cols("status").Update(t) -//} - -func GetBullyScreenHistoriesByStatus(status int) ([]*BullyScreenHistory, error) { - histories := make([]*BullyScreenHistory, 0) - err := core.GetXormAuto().Where("is_delete and status=?", status).Find(&histories) - return histories, err -} - // 更改未支付状态 func (t *BullyScreenHistory) UpdateStatusByOutTradeNo(outTradeNo string, status int) (int64, error) { t.Status = status return core.GetXormAuto().Where("is_delete=0 and status=-1 and out_trade_no=?", outTradeNo). Cols("status").Update(t) } + +func (t *BullyScreenHistory) UpdateStatusByIds(ids []int64, status int) error { + if len(ids) > 0 { + + t.Status = status + _, err := core.GetXormAuto().In("id", ids).Cols("status"). + Update(&BullyScreenHistory{Status: status}) + return err + } + return nil +} diff --git a/models/init_models.go b/models/init_models.go index 50bcfdb..9a6c08b 100644 --- a/models/init_models.go +++ b/models/init_models.go @@ -88,6 +88,7 @@ func init() { new(LiveConfig), new(LiveRedPackInfo), new(LiveRedPack), + new(UserTransfer), ) fmt.Printf("error=======>%v\n\n", err) } diff --git a/models/live_red_pack.go b/models/live_red_pack.go index ffc222e..7860929 100644 --- a/models/live_red_pack.go +++ b/models/live_red_pack.go @@ -19,7 +19,7 @@ type LiveRedPack struct { OpenId string `json:"open_id" xorm:"not null default '' comment('用户openid') VARCHAR(128)"` Amount int `json:"amount" xorm:"not null default 0 comment('红包金额, 分') INT(18)"` TransferType int `json:"transfer_type" xorm:"not null default 0 comment('转账方式[0微信红包1微信零钱]') TINYINT(1)"` - TransferNo string `json:"transfer_no" xorm:"not null default '' comment('转账账号') VARCHAR(128) "` + PartnerTradeNo string `json:"partner_trade_no" xorm:"not null default '' comment('转账单号') VARCHAR(128) "` Status int `json:"status" xorm:"not null default 0 comment('0 未被领取 1 已被领取 2 已发送 3 出现错误') TINYINT(1)"` Version int `json:"version" xorm:"not null version comment('乐观锁') INT(11)"` } diff --git a/models/live_red_pack_info.go b/models/live_red_pack_info.go index 9cffcc4..b3087f2 100644 --- a/models/live_red_pack_info.go +++ b/models/live_red_pack_info.go @@ -15,11 +15,12 @@ type LiveRedPackInfo struct { UserId int64 `json:"user_id" xorm:"not null default 0 comment('用户id') INT(11)"` ActivityId int64 `json:"activity_id" xorm:"not null default 0 comment('互动id') INT(11)"` + GroupId string `json:"group_id" xorm:"not null default '' comment('聊天室地址') VARCHAR(128)"` Prompt string `json:"prompt" xorm:"not null default 0 comment('祝福语') VARCHAR(255)"` Amount int64 `json:"amount" xorm:"not null default 0 comment('红包金额, 分') INT(18)"` OutTradeNo string `json:"out_trade_no" xorm:"not null default '' comment('订单号') VARCHAR(128)"` Error string `json:"error" xorm:"not null default '' comment('出现错误') VARCHAR(255)"` - Status int `json:"status" xorm:"not null default 0 comment('-1尚未支付0支付成功1已推送')"` + Status int `json:"status" xorm:"not null default 0 comment('-1尚未支付0支付成功1已推送2已作废')"` } func (t *LiveRedPackInfo) TableName() string { diff --git a/models/reward_history.go b/models/reward_history.go index abb838a..409e542 100644 --- a/models/reward_history.go +++ b/models/reward_history.go @@ -19,7 +19,7 @@ type RewardHistory struct { Content string `json:"content" xorm:"not null comment('内容') text"` Amount float64 `json:"amount" xorm:"not null default(0.0) comment('金额') DECIMAL"` RewardAmount string `json:"reward_amount" xorm:"-" description:"同上, 字符串"` - Status int `json:"status" xorm:"not null default(0) comment('-1未支付,0未审核,1未通过,2已通过,3已推送,4已退款,5被取消') INT(11)"` + Status int `json:"status" xorm:"not null default(0) comment('-1未支付,0未审核,1未通过,2已通过,3已推送,4已作废') INT(11)"` ReviewTime int64 `json:"review_time" xorm:"not null default(0) comment('审核时间') INT(11)"` Version int64 `json:"version" xorm:"not null version comment('乐观锁') INT(11)"` IsDelete bool `json:"is_delete" xorm:"not null default(0)"` @@ -40,12 +40,16 @@ func (t *RewardHistory) UpdateStatus(id int64, status int) (int64, error) { return core.GetXormAuto().Where("id=?", id).Cols("status").Update(t) } -//func (t *RewardHistory) UpdateStatusByUserOrderId(userOrderId interface{}, status int) (int64, error) { -// t.Status = status -// return core.GetXormAuto().Where("is_delete=0 and user_order_id=?", userOrderId).Cols("status").Update(t) -//} - func (t *RewardHistory) UpdateStatusByOutTradeNo(outTradeNo string, status int) (int64, error) { t.Status = status return core.GetXormAuto().Where("is_delete=0 and status=-1 and out_trade_no=?", outTradeNo).Update(status) } + +func (t *RewardHistory) UpdateStatusByIds(ids []int64, status int) error { + if len(ids) > 0 { + _, err := core.GetXormAuto().In("id", ids).Cols("status"). + Update(&RewardHistory{Status: status}) + return err + } + return nil +} diff --git a/models/user_order.go b/models/user_order.go index e510d34..2afb5e2 100644 --- a/models/user_order.go +++ b/models/user_order.go @@ -16,7 +16,7 @@ type UserOrder struct { // 订单信息 DeviceInfo string `json:"device_info" xorm:"not null default('') comment('设备号') VARCHAR(32)"` GoodType int64 `json:"good_type" xorm:"not null default(0) comment('1霸屏2打赏3直播红包')"` - Desc string `json:"desc" xorm:"not null default('') comment('') VARCHAR(128)"` + Body string `json:"body" xorm:"not null default('') comment('') VARCHAR(128)"` OutTradeNo string `json:"out_trade_no" xorm:"not null default('') comment('商户订单号') VARCHAR(32)"` FeeType string `json:"fee_type" xorm:"not null default('CNY') comment('货币种类') VARCHAR(16)"` TotalFee int64 `json:"total_fee" xorm:"not null default(0) comment('订单总金额,单位是分') INT(88)"` @@ -30,7 +30,7 @@ type UserOrder struct { TimeEnd string `json:"time_end" xorm:"not null default('') comment('交易结算时间') VARCHAR(14)"` PrepayId string `json:"prepay_id" xorm:"not null default('') comment('预支付交易会话标识') VARCHAR(64)"` Status int `json:"status" xorm:"not null default(0) comment('0尚未支付/支付中1支付成功2已撤销3转入退款4退款成功5支付失败6订单关闭') TINYINT(1)"` - ErrMsg string `json:"err_msg" xorm:"not null default 0 comment('出现错误') VARCHAR(255)"` + ErrMsg string `json:"err_msg" xorm:"not null default '' comment('出现错误') VARCHAR(255)"` // 退款 SuccessTime time.Time `json:"success_time" xorm:"not null default '' comment('退款成功时间') VARCHAR(20)"` @@ -42,6 +42,10 @@ func (t *UserOrder) TableName() string { return UserOrderTableName } +func (t *UserOrder) AliasName(alias string) string { + return AliasTableName(t, alias) +} + func (t *UserOrder) AddUserOrder() (int64, error) { return core.GetXormAuto().InsertOne(t) } @@ -69,20 +73,8 @@ func (t *UserOrder) UpdateRefundByOutTradeNo(outTradeNo interface{}) (int64, err return core.GetXormAuto().Where("out_trade_no=?", outTradeNo).Cols("success_time", "refund_recv_account", "refund_account").Update(t) } -func GetUserOrdersByStatusAndUserId(userId interface{}, status int) ([]*UserOrder, error) { - orders := make([]*UserOrder, 0) - err := core.GetXormAuto().Where("is_delete = 0 and status = ? and user_id = ?", status, userId).Find(&orders) - return orders, err -} - -func GetUserOrdersByStatusAndActivityId(activityId interface{}, status int) ([]*UserOrder, error) { - orders := make([]*UserOrder, 0) - err := core.GetXormAuto().Where("is_delete = 0 and status = ? and activity_id = ?", status, activityId).Find(&orders) - return orders, err -} - -func GetValidUserOrders() ([]*UserOrder, error) { +func GetUserOrdersByStatus(status ...int) ([]*UserOrder, error) { orders := make([]*UserOrder, 0) - err := core.GetXormAuto().Where("is_delete=0 and (status=? or status=?)", 0, 3).Find(&orders) + err := core.GetXormAuto().Where("is_delete=0").In("status", status).Find(&orders) return orders, err } diff --git a/models/user_transfer.go b/models/user_transfer.go new file mode 100644 index 0000000..26d0af1 --- /dev/null +++ b/models/user_transfer.go @@ -0,0 +1,53 @@ +package models + +import ( + "github.com/ouxuanserver/osmanthuswine/src/core" + "time" +) + +const UserTransferTN = TableNamePrefix + "user_transfer" + +type UserTransfer struct { + Id int64 `json:"id" xorm:"not null pk autoincr INT(11)"` + CreatedAt time.Time `json:"created_at" xorm:"created comment('创建时间') TIMESTAMP"` + UpdatedAt time.Time `json:"updated_at" xorm:"updated comment('更新时间') TIMESTAMP"` + IsDelete int `json:"is_delete" xorm:"not null default 0 comment('是否删除') TINYINT(1)"` + DeviceInfo string `json:"device_info" xorm:"not null default '' comment('设备信息') VARCHAR(32)"` + NonceStr string `json:"nonce_str" xorm:"not null default '' comment('随机字符串') VARCHAR(32)"` + PartnerTradeNo string `json:"partner_trade_no" xorm:"not null default '' comment('商户订单号') VARCHAR(32)"` + PaymentNo string `json:"payment_no" xorm:"not null default '' comment('微信付款单号') VARCHAR(64)"` + PaymentTime string `json:"payment_time" xorm:"not null default '' comment('企业付款成功时间') VARCHAR(32)"` + Status int `json:"status" xorm:"not null default 0 comment('0未处理1处理中2转账成功3转账失败4查询尝试失败') TINYINT(1)"` + Reason string `json:"reason" xorm:"not null default '' comment('转账失败原因') VARCHAR(255)"` + OpenId string `json:"open_id" xorm:"not null default '' comment('用户open_id') VARCHAR(64)"` + PaymentAmount int `json:"payment_amount" xorm:"not null default 0 comment('转账金额,单位分') INT(18)"` + TransferTime string `json:"transfer_time" xorm:"not null default '' comment('转账时间') VARCHAR(32)"` + Desc string `json:"desc" xorm:"not null default '' comment('备注') VARCHAR(128)"` + ErrMsg string `json:"err_msg" xorm:"not null default '' comment('错误信息') VARCHAR(255)"` +} + +func (t *UserTransfer) TableName() string { + return UserTransferTN +} + +func GetUserTransferByStatus(status ...int) ([]*UserTransfer, error) { + transfers := make([]*UserTransfer, 0) + err := core.GetXormAuto().Where("is_delete=0").In("status", status).Find(&transfers) + return transfers, err +} + +func (t *UserTransfer) Add() (int64, error) { + return core.GetXormAuto().InsertOne(t) +} + +func (t *UserTransfer) GetByPartnerTradeNo(partnerTradeNo string) (bool, error) { + return core.GetXormAuto().Where("is_delete=0 and partner_trade_no=?", partnerTradeNo).Get(t) +} + +func (t *UserTransfer) UpdateByPartnerTradeNo(partnerTradeNo string) (int64, error) { + return core.GetXormAuto().Where("is_delete=0 and partner_trade_no=?", partnerTradeNo).Update(t) +} + +func (t *UserTransfer) UpdateErrMsg(partnerTradeNo, msg string) (int64, error) { + return core.GetXormAuto().Where("is_delete=0 and partner_trade_no=?", partnerTradeNo).Update(&UserTransfer{ErrMsg: msg}) +} diff --git a/services/bully_reward/dao.go b/services/bully_reward/dao.go index a28c99c..1cf4d40 100644 --- a/services/bully_reward/dao.go +++ b/services/bully_reward/dao.go @@ -34,3 +34,143 @@ func GetRewardList(userId, rewardId int64) ([]*RewardListResult, error) { Desc("created_at").Find(&list) return list, err } + +type RewardResult struct { + models.RewardHistory `xorm:"extends"` + User *models.User `json:"user" xorm:"extends"` +} + +func GetRewardLatest(serverId, rehearsalId int64) (*RewardResult, error) { + result := new(RewardResult) + _, err := core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), + "h.user_id=u.id and u.is_delete=0"). + Where("h.is_delete=0 and h.status=2 and h.reward_server_id=? and h.rehearsal_id=?", + serverId, rehearsalId).Desc("h.review_time").Get(result) + return result, err +} + +func GetRewardReview(serverId, rehearsalId int64) ([]*RewardResult, error) { + result := make([]*RewardResult, 0) + err := core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), + "h.user_id=u.id and u.is_delete=0"). + Where("h.is_delete=0 and h.status=0 and h.reward_server_id=? and h.rehearsal_id=?", + serverId, rehearsalId).Desc("h.created_at").Find(&result) + return result, err +} + +func GetRewardBlacklist(serverId, rehearsalId int64) ([]*RewardResult, error) { + result := make([]*RewardResult, 0) + err := core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), + "h.user_id=u.id and u.is_delete=0"). + Where("h.is_delete=0 and h.status=1 and h.reward_server_id=? and h.rehearsal_id=?", + serverId, rehearsalId).Desc("h.review_time").Find(&result) + return result, err +} + +type RewardStatus struct { + History *models.RewardHistory `json:"history" xorm:"extends"` + Order *models.UserOrder `json:"order" xorm:"extends"` +} + +func CheckRewardStatus(serverId int64) error { + result := make([]*RewardStatus, 0) + err := core.GetXormAuto().Table(new(models.RewardHistory)).Alias("h"). + Join("LEFT", new(models.UserOrder).AliasName("u"), + "h.out_trade_no=u.out_trade_no and u.is_delete=0"). + Where("h.is_delete=0 and h.status=-1 and h.reward_server_id=?", + serverId).Find(&result) + if err != nil { + return err + } + success := make([]int64, 0) + destroy := make([]int64, 0) + for _, v := range result { + if v.Order.Status == 1 { + success = append(success, v.History.Id) + } else if v.Order.Status != 0 { + destroy = append(destroy, v.History.Id) + } + } + err = new(models.RewardHistory).UpdateStatusByIds(success, 0) + if err != nil { + return err + } + + err = new(models.RewardHistory).UpdateStatusByIds(success, 4) + if err != nil { + return err + } + return nil +} + +type BullyScreenResult struct { + models.BullyScreenHistory `xorm:"extends"` + User *models.User `json:"user" xorm:"extends"` +} + +func GetBullyScreenReview(bullyScreenServerId, rehearsalId int64) ([]*BullyScreenResult, error) { + result := make([]*BullyScreenResult, 0) + err := core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), + "h.user_id=u.id and u.is_delete=0"). + Where("h.bully_screen_server_id=? and h.is_delete=0 and h.status=0 and h.rehearsal_id=?", + bullyScreenServerId, rehearsalId).Desc("h.created_at").Find(&result) + return result, err +} + +func GetBullyScreenBlacklist(bullyScreenServerId, rehearsalId int64) ([]*BullyScreenResult, error) { + result := make([]*BullyScreenResult, 0) + err := core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), "u.id=h.user_id and u.is_delete=0"). + Where("h.is_delete=0 and h.bully_screen_server_id=? and h.status=1 and h.rehearsal_id=?", + bullyScreenServerId, rehearsalId).Desc("review_time").Find(&result) + return result, err +} + +func GetBullyScreenLatest(bullyScreenServerId, rehearsalId int64) (*BullyScreenResult, error) { + result := new(BullyScreenResult) + _, err := core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). + Join("LEFT", new(models.User).Alias("u"), "u.id=h.user_id and u.is_delete=0"). + Where("h.bully_screen_server_id=? and h.status=2 and h.rehearsal_id=? and h.is_delete=0", + bullyScreenServerId, rehearsalId).Desc("review_time").Get(result) + return result, err +} + +type BullyScreenStatus struct { + History *models.BullyScreenHistory `json:"history" xorm:"extends"` + Order *models.UserOrder `json:"order" xorm:"extends"` +} + +func CheckBullyScreenStatus(bullyScreenServerId int64) error { + result := make([]*BullyScreenStatus, 0) + err := core.GetXormAuto().Table(new(models.BullyScreenHistory)).Alias("h"). + Join("LEFT", new(models.UserOrder).AliasName("u"), + "u.out_trade_no=h.out_trade_no and u.is_delete=0"). + Where("h.bully_screen_server_id=? and h.status=-1 and h.is_delete=0", + bullyScreenServerId).Find(&result) + if err != nil { + return err + } + success := make([]int64, 0) + destroy := make([]int64, 0) + for _, v := range result { + if v.Order.Status == 1 { + success = append(success, v.History.Id) + } else if v.Order.Status != 0 { + destroy = append(destroy, v.History.Id) + } + } + err = new(models.BullyScreenHistory).UpdateStatusByIds(success, 0) + if err != nil { + return err + } + + err = new(models.BullyScreenHistory).UpdateStatusByIds(success, 4) + if err != nil { + return err + } + return nil +} diff --git a/services/im/im.go b/services/im/im.go new file mode 100644 index 0000000..0b91fe8 --- /dev/null +++ b/services/im/im.go @@ -0,0 +1,19 @@ +package im_service + +import ( + "errors" + "hudongzhuanjia/libs/im" + "hudongzhuanjia/models" +) + +func SendNoticeByActivityId(activityId int64, data map[string]interface{}, members ...string) error { + live := new(models.LiveConfig) + exist, err := live.GetByActivityId(activityId) + if err != nil { + return err + } + if !exist { + return errors.New("直播信息不存在") + } + return im.SendGroupSystemNotification(live.ImGroupId, im.NoticeLiveRedPackStart, data, members...) +} diff --git a/services/pay/const.go b/services/pay/const.go index bbdac5d..918c66a 100644 --- a/services/pay/const.go +++ b/services/pay/const.go @@ -64,11 +64,13 @@ const ( CODE_FREQUENCY_LIMITED = "FREQUENCY_LIMITED" // 请求频率限制 CODE_TRADE_SUCCESS = "SUCCESS" // 交易成功 CODE_TRADE_REFUND = "REFUND" // 转入退款 - CODE_TRADE_NOTPAY = "NOTPAY" // 尚未退款 + CODE_TRADE_NOTPAY = "NOTPAY" // 尚未支付 CODE_TRADE_CLOSED = "CLOSED" // 已关闭 CODE_TRADE_REVOKED = "REVOKED" // 已撤销 CODE_TRADE_USERPAYING = "USERPAYING" // 用户支付中 CODE_TRADE_PAYERROR = "PAYERROR" // 支付失败 + CODE_TRANSFER_PROCESSING = "PROCESSING" // 转账处理中 + CODE_TRANSFER_NOT_FOUND = "NOT_FOUND" // 账单消息 ) type CommonReturn struct { diff --git a/services/pay/order.go b/services/pay/order.go index c1316fc..dd20416 100644 --- a/services/pay/order.go +++ b/services/pay/order.go @@ -1,8 +1,6 @@ package pay_service import ( - "encoding/xml" - "fmt" core2 "github.com/chanxuehong/wechat/mch/core" "github.com/chanxuehong/wechat/mch/pay" "github.com/pkg/errors" @@ -10,33 +8,53 @@ import ( "hudongzhuanjia/logger" "hudongzhuanjia/models" "hudongzhuanjia/utils" - "net/http" + "math" "strconv" "time" ) func init() { - go loop() + go loopUnifiedOrder() } -var orderChanSize = 10000 -var orderChanDelay = 1 -var orderChan chan string +var orderDelayQueue = make(chan *orderDelayQueueParam, math.MaxInt8) + +type orderDelayQueueParam struct { + First bool `json:"first"` + Expires int64 `json:"expires"` + Delay int `json:"delay"` + OutTradeNo string `json:"out_trade_no"` + Body string `json:"body"` + Amount int `json:"amount"` // 金额 + Status int `json:"status"` // 0 订单 3 退款 + OpenId string `json:"open_id"` // 被操作人 +} + +func PutOrderDelayQueue(body, outTradeNo, openId string, amount, status int, expires int64, delay int) { + if expires == 0 { + expires = time.Now().Add(2 * time.Hour).Unix() // 2 个小时 + } -func PutOrderChan(value string) { - time.Sleep(time.Duration(orderChanDelay) * time.Second) - orderChan <- value + orderDelayQueue <- &orderDelayQueueParam{ + First: true, + Expires: expires, + Delay: delay, + OutTradeNo: outTradeNo, + Body: body, + Amount: amount, + Status: status, + OpenId: openId, + } } -func loop() { - orders, err := models.GetValidUserOrders() +func loopUnifiedOrder() { + orders, err := models.GetUserOrdersByStatus(0, 3) if err != nil { panic(err) } - orderChan = make(chan string, orderChanSize+len(orders)) for _, order := range orders { - orderChan <- order.OutTradeNo + PutOrderDelayQueue(order.Body, order.OutTradeNo, order.OpenId, int(order.TotalFee), order.Status, 0, 0) } defer func() { @@ -45,37 +63,57 @@ func loop() { } // 重启 time.Sleep(5 * time.Second) - loop() + loopUnifiedOrder() }() for { select { - case outTradeNo, ok := <-orderChan: + case param, ok := <-orderDelayQueue: if !ok { panic("通道异常关闭") } - userOrder := new(models.UserOrder) - if userOrder.Status == 0 { - res, err := OrderQuery(outTradeNo) + //userOrder := new(models.UserOrder) + //exist, err := userOrder.GetByOutTradeNo(outTradeNo) + //if err != nil || !exist { + // logger.Error("通过订单查询UserOrder", zap.String("错误原因", err.Error()), + // zap.Bool("是否存在", exist), zap.String("交易订单号", outTradeNo)) + // continue + //} + if param.Expires <= time.Now().Unix() { + Close(param.OutTradeNo) // 超时关闭订单 + continue // 超时 + } + + // 首次进入不延迟 + if !param.First { + time.Sleep(time.Duration(param.Delay) * time.Second) + } + param.First = false + + if param.Status == 0 { + res, err := OrderQuery(param.OutTradeNo) // 出现错误 if err != nil { logger.Error("查询订单出现错误", zap.String("错误原因", err.Error()), - zap.String("交易订单号", outTradeNo)) - PutOrderChan(outTradeNo) + zap.String("交易订单号", param.OutTradeNo)) + orderDelayQueue <- param // 重新进入队列 continue } - if res.TradeState == CODE_TRADE_SUCCESS { + if res.TradeState == CODE_TRADE_REFUND { + param.Status = 3 + orderDelayQueue <- param continue - } else if res.TradeState == CODE_TRADE_REFUND { - PutOrderChan(outTradeNo) + } else if res.TradeState == CODE_TRADE_NOTPAY || res.TradeState == CODE_TRADE_USERPAYING { + orderDelayQueue <- param continue } - } else if userOrder.Status == 3 { - _, err = QueryRefund(outTradeNo) + } else if param.Status == 3 { + _, err = QueryRefund(param.OutTradeNo) if err != nil { logger.Error("退款订单查询错误", zap.String("错误原因", err.Error()), - zap.String("交易订单号", outTradeNo)) + zap.String("交易订单号", param.OutTradeNo)) + continue } } else { continue @@ -84,27 +122,23 @@ func loop() { } } -func init() { - go loop() -} - const CallbackOrderUrl = "https://api.ouxuanhudong.com/PcClient/common/WeChatOauthCtl/callbackOrder" -func Order(content, ip, openid string, fee, goodType, userId, activityId int64) (map[string]interface{}, error) { +func UnifiedOrder(body, ip, openid string, fee, goodType, userId, activityId int64) (map[string]interface{}, error) { client, err := Client() if err != nil { return nil, err } now := time.Now() - timeStart := now.Format("20060101150405") - timeExpire := now.Add(2 * time.Hour).Format("20060101150405") + timeStart := core2.FormatTime(now) + timeExpire := core2.FormatTime(now.Add(2 * time.Hour)) outTradeNo := utils.RandomStr(32) nonceStr := utils.RandomStr(32) resp, err := pay.UnifiedOrder2(client, &pay.UnifiedOrderRequest{ - Body: content, + Body: body, OutTradeNo: outTradeNo, - TotalFee: int64(fee), + TotalFee: fee, SpbillCreateIP: ip, NotifyURL: CallbackOrderUrl, TradeType: "JSAPI", @@ -121,7 +155,7 @@ func Order(content, ip, openid string, fee, goodType, userId, activityId int64) // 记录这次订单 userOrder := new(models.UserOrder) userOrder.DeviceInfo = "WEB" - userOrder.Desc = content + userOrder.Body = body userOrder.UserId = userId userOrder.ActivityId = activityId userOrder.FeeType = "CNY" @@ -146,7 +180,7 @@ func Order(content, ip, openid string, fee, goodType, userId, activityId int64) pac := "prepay_id=" + resp.PrepayId paySign := core2.JsapiSign(client.AppId(), nonceStr, pac, core2.SignType_MD5, timestamp, ApiKey) - PutOrderChan(outTradeNo) + go PutOrderDelayQueue(userOrder.Body, userOrder.OutTradeNo, userOrder.OpenId, int(userOrder.TotalFee), userOrder.Status, 5, 0) return map[string]interface{}{ "appid": Appid, "timestamp": timestamp, @@ -195,48 +229,47 @@ type NotifyRequest struct { TimeEnd string `xml:"time_end,omitempty" json:"time_end,omitempty"` } -func NotifyOrder(req *http.Request) error { - res := new(NotifyRequest) - if err := xml.NewDecoder(req.Body).Decode(res); err != nil { - return fmt.Errorf("xml.NewDecoder.Decode:%w", err) - } - - if res.ReturnCode != CODE_SUCCESS { - return fmt.Errorf("network error, retrun_code: %v and return_msg: %v", res.ReturnCode, res.ReturnMsg) - } - - if res.ResultCode != CODE_SUCCESS { - return fmt.Errorf("trade error, err_code: %v and err_code_des: %v", res.ErrCode, res.ErrCodeDes) - } - - userOrder := new(models.UserOrder) - exist, err := userOrder.GetByOutTradeNo(res.OutTradeNo) - if err != nil { - return err - } - if !exist { - return fmt.Errorf("user order not exist") - } - userOrder.TimeEnd = res.TimeEnd - userOrder.TransactionId = res.TransactionId - userOrder.Status = 1 - if _, err = userOrder.UpdateStatusById(userOrder.Id); err != nil { - return err - } - //// 设置一下 - //if userOrder.GoodType == 1 { // 霸屏 - // _, err = new(models.BullyScreenHistory).UpdateStatusByUserOrderId(userOrder.Id, 0) - // if err != nil { - // return err - // } - //} else if userOrder.GoodType == 2 { - // _, err = new(models.RewardHistory).UpdateStatusByUserOrderId(userOrder.Id, 0) - // if err != nil { - // return err - // } - //} - return nil -} +//func NotifyOrder(req *http.Request) error { +// res := new(NotifyRequest) +// if err := xml.NewDecoder(req.Body).Decode(res); err != nil { +// return fmt.Errorf("xml.NewDecoder.Decode:%w", err) +// } + +//if res.ReturnCode != CODE_SUCCESS { +// return fmt.Errorf("network error, retrun_code: %v and return_msg: %v", res.ReturnCode, res.ReturnMsg) +//} +// +//if res.ResultCode != CODE_FAIL && res.ErrCode == CODE_SYSTEMERROR { +// return fmt.Errorf("trade error, err_code: %v and err_code_des: %v", res.ErrCode, res.ErrCodeDes) +//} +// +//if res.ResultCode == CODE_SUCCESS { +// userOrder := new(models.UserOrder) +// userOrder.TimeEnd = res.TimeEnd +// userOrder.TransactionId = res.TransactionId +// if res.TradeType == CODE_TRADE_SUCCESS { +// userOrder.Status = 1 +// } else if _, err := userOrder.UpdateStatusByOutTradeNo(res.OutTradeNo, userOrder.TradeType); err != nil { +// return err +// } +// +//} else { +//} + +//// 设置一下 +//if userOrder.GoodType == 1 { // 霸屏 +// _, err = new(models.BullyScreenHistory).UpdateStatusByUserOrderId(userOrder.Id, 0) +// if err != nil { +// return err +// } +//} else if userOrder.GoodType == 2 { +// _, err = new(models.RewardHistory).UpdateStatusByUserOrderId(userOrder.Id, 0) +// if err != nil { +// return err +// } +//} +// return nil +//} func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { client, err := Client() @@ -245,11 +278,20 @@ func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { } // 请求订单查询,成功后得到结果 userOrder := new(models.UserOrder) + exist, err := userOrder.GetByOutTradeNo(outTradeNo) + if err != nil { + return nil, err + } + if !exist { + return nil, errors.New("订单不存在") + } + res, err := pay.OrderQuery2(client, &pay.OrderQueryRequest{ OutTradeNo: outTradeNo, NonceStr: utils.RandomStr(32), SignType: core2.SignType_MD5, }) + if err != nil { userOrder.ErrMsg = err.Error() userOrder.UpdateErrByOutTradeNo(outTradeNo) @@ -259,7 +301,6 @@ func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { userOrder.TransactionId = res.TransactionId userOrder.TimeEnd = core2.FormatTime(res.TimeEnd) switch res.TradeState { - case CODE_TRADE_SUCCESS: userOrder.Status = 1 case CODE_TRADE_REVOKED: @@ -270,7 +311,7 @@ func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { userOrder.Status = 5 case CODE_TRADE_CLOSED: userOrder.Status = 6 - default: + case CODE_TRADE_NOTPAY: // 超过期限,就关闭 userOrder.Status = 0 } @@ -281,7 +322,6 @@ func OrderQuery(outTradeNo string) (*pay.OrderQueryResponse, error) { } func Close(outTradeNo string) error { - //client := wechat.NewClient(Appid, Mchid, ApiKey, true) client, err := Client() if err != nil { return err @@ -336,19 +376,19 @@ func Refund(reason, outTradeNo string) (*pay.RefundResponse, error) { return nil, err } - PutOrderChan(outTradeNo) // 退款查询 + PutOrderDelayQueue(userOrder.Body, userOrder.OutTradeNo, userOrder.OpenId, int(userOrder.TotalFee), userOrder.Status, 5, 0) // 退款查询 return res, nil } func QueryRefund(outTradeNo string) (*pay.RefundQueryResponse, error) { userOrder := new(models.UserOrder) - exist, err := userOrder.GetByOutTradeNo(outTradeNo) - if err != nil { - return nil, err - } - if !exist { - return nil, errors.New("不存在改笔退款") - } + //exist, err := userOrder.GetByOutTradeNo(outTradeNo) + //if err != nil { + // return nil, err + //} + //if !exist { + // return nil, errors.New("不存在改笔退款") + //} client, err := Client() res, err := pay.RefundQuery2(client, &pay.RefundQueryRequest{ @@ -358,6 +398,8 @@ func QueryRefund(outTradeNo string) (*pay.RefundQueryResponse, error) { }) //请求申请退款 if err != nil { + userOrder.ErrMsg = err.Error() + userOrder.UpdateErrByOutTradeNo(outTradeNo) return nil, err } diff --git a/services/pay/refund.go b/services/pay/refund.go deleted file mode 100644 index c2fbae7..0000000 --- a/services/pay/refund.go +++ /dev/null @@ -1,32 +0,0 @@ -package pay_service - -//func BatchQueryRefundByUserId(userId int64) error { -// refunds := make([]*models.UserRefund, 0) -// if err := core.GetXormAuto().Where("status = 0 and user_id=?", userId).Find(&refunds); err != nil { -// return err -// } -// if err := batchQueryRefund(refunds); err != nil { -// return err -// } -// return nil -//} -//func BatchQueryRefundByActivityId(activityId int64) error { -// refunds := make([]*models.UserRefund, 0) -// if err := core.GetXormAuto().Where("status = 0 and user_id=?", activityId).Find(&refunds); err != nil { -// return err -// } -// if err := batchQueryRefund(refunds); err != nil { -// return err -// } -// return nil -//} -// -//func batchQueryRefund(refunds []*models.UserRefund) error { -// for _, refund := range refunds { -// _, err := QueryRefund(refund.OutTradeNo) -// if err != nil { -// return err -// } -// } -// return nil -//} diff --git a/services/pay/transfer.go b/services/pay/transfer.go index 2ad36a0..a3631fe 100644 --- a/services/pay/transfer.go +++ b/services/pay/transfer.go @@ -4,12 +4,106 @@ import ( "fmt" "github.com/chanxuehong/wechat/mch/mmpaymkttransfers" "github.com/chanxuehong/wechat/mch/mmpaymkttransfers/promotion" + "go.uber.org/zap" + "hudongzhuanjia/logger" "hudongzhuanjia/models" "hudongzhuanjia/utils" + "math" "strconv" "time" ) +func init() { + go loopTransfer() +} + +var transferDelayQueue = make(chan *transferDelayQueueParam, math.MaxInt8) + +type transferDelayQueueParam struct { + Retries int `json:"retries"` // 尝试次数 + Delay int `json:"delay"` // 延迟时间, 单位second + Amount int `json:"amount"` // 转账金额 + Desc string `json:"desc"` // 转账描述 + OpenId string `json:"open_id"` // 被转账人 + PartnerTradeNo string `json:"partner_trade_no"` // 转账账单单号 +} + +func loopTransfer() { + //初始化 + transfers, err := models.GetUserTransferByStatus(0, 1, 3) + if err != nil { + panic(err) + } + + transferDelayQueue = make(chan *transferDelayQueueParam, math.MaxInt8) + for _, transfer := range transfers { + transferDelayQueue <- &transferDelayQueueParam{ + Retries: 5, + Delay: 2 * 60, + Amount: transfer.PaymentAmount, + Desc: transfer.Desc, + OpenId: transfer.OpenId, + PartnerTradeNo: transfer.PartnerTradeNo, + } + } + + defer func() { + if errRec := recover(); errRec != nil { + logger.Error("转账轮询 loop transfer panic", zap.Any("错误原因", errRec)) + } + loopTransfer() + }() + for { + select { + case param, ok := <-transferDelayQueue: + if !ok { + panic("转账延迟通道异常关闭") + } + + // 尝试次数 + if param.Retries <= 0 { + logger.Info("微信转账尝试3次失败", zap.String("转账账单单号", param.PartnerTradeNo)) + userTransfer := new(models.UserTransfer) + userTransfer.Status = 4 + _, err = userTransfer.UpdateByPartnerTradeNo(param.PartnerTradeNo) + if err != nil { + logger.Info("微信转账更新状态失败", zap.String("失败原因", err.Error()), + zap.String("转账账单单号", param.PartnerTradeNo)) + } + continue + } else { + param.Retries-- + } + + time.Sleep(time.Duration(param.Delay) * time.Second) + + res, err := TransferInfo(param.PartnerTradeNo) + if err != nil { + logger.Error("微信转账查询出现的错误", zap.String("错误原因", err.Error()), + zap.String("转账账单", param.PartnerTradeNo)) + transferDelayQueue <- param + continue + } + + if res.Status == CODE_SUCCESS { + continue + } else if res.Status == CODE_TRANSFER_PROCESSING { + transferDelayQueue <- param + continue + } else { + //失败 --> 重新转账 + _, err = Transfer(param.Desc, param.OpenId, param.PartnerTradeNo, param.Amount) + if err != nil { + logger.Error("微信转账出现的错误", zap.String("错误原因", err.Error()), + zap.String("转账账单", param.PartnerTradeNo)) + } + transferDelayQueue <- param // 重新确认 + continue + } + } + } +} + // 企业向微信用户个人付款(不支持沙箱环境) type TransferResponse struct { DeviceInfo string `xml:"device_info,omitempty" json:"device_info,omitempty"` @@ -19,6 +113,23 @@ type TransferResponse struct { PaymentTime string `xml:"payment_time,omitempty" json:"payment_time,omitempty"` } +func PutTransferDelayQueue(desc, openId, partnerTradeNo string, amount, retries, delay int) { + if retries <= 0 { + retries = 3 + } + if delay == 0 { + delay = 30 + } + transferDelayQueue <- &transferDelayQueueParam{ + Retries: retries, + Delay: delay, + Amount: amount, + Desc: desc, + OpenId: openId, + PartnerTradeNo: partnerTradeNo, + } +} + func Transfer(desc, openId, partnerTradeNo string, amount int) (*TransferResponse, error) { nonceStr := utils.RandomStr(32) @@ -39,9 +150,13 @@ func Transfer(desc, openId, partnerTradeNo string, amount int) (*TransferRespons client, err := Client() m, err := promotion.Transfers(client, body) + // 记录错误信息 if err != nil { + transfer := new(models.UserTransfer) + transfer.UpdateErrMsg(partnerTradeNo, err.Error()) return nil, err } + res := &TransferResponse{ DeviceInfo: m["device_info"], NonceStr: m["nonce_str"], @@ -49,10 +164,37 @@ func Transfer(desc, openId, partnerTradeNo string, amount int) (*TransferRespons PaymentNo: m["payment_no"], PaymentTime: m["payment_time"], } + + // 获取某些信息 + transfer := new(models.UserTransfer) + exist, err := transfer.GetByPartnerTradeNo(partnerTradeNo) + if err != nil { + return nil, err + } + transfer.Desc = desc + transfer.OpenId = openId + transfer.PartnerTradeNo = partnerTradeNo + transfer.PaymentAmount = amount + transfer.PaymentNo = res.PaymentNo + transfer.DeviceInfo = res.DeviceInfo + transfer.PaymentTime = res.PaymentTime + if !exist { + _, err = transfer.Add() + if err != nil { + return nil, err + } + } else { + _, err = transfer.UpdateByPartnerTradeNo(partnerTradeNo) + if err != nil { + return nil, err + } + } + return res, nil } type TransferInfoResponse struct { + ErrCode string `json:"err_code"` Status string `json:"status"` Reason string `json:"reason"` Openid string `json:"openid"` @@ -76,11 +218,13 @@ func TransferInfo(partnerTradeNo string) (*TransferInfoResponse, error) { m, err := mmpaymkttransfers.GetTransferInfo(client, body) if err != nil { + transfer := new(models.UserTransfer) + transfer.UpdateErrMsg(partnerTradeNo, err.Error()) return nil, err } amount, _ := strconv.Atoi(m["payment_amount"]) - resp := &TransferInfoResponse{ + res := &TransferInfoResponse{ Status: m["status"], Reason: m["reason"], Openid: m["openid"], @@ -88,8 +232,23 @@ func TransferInfo(partnerTradeNo string) (*TransferInfoResponse, error) { PaymentTime: m["payment_time"], TransferTime: m["transfer_time"], Desc: m["desc"], + ErrCode: m["err_code"], + } + transfer := new(models.UserTransfer) + if res.Status == CODE_SUCCESS { + transfer.Status = 2 + } else if res.Status == CODE_TRANSFER_PROCESSING { + transfer.Status = 1 + } else if res.Status == CODE_FAIL { + transfer.Status = 3 } - return resp, nil + transfer.Reason = res.Reason + transfer.TransferTime = res.TransferTime + _, err = transfer.UpdateByPartnerTradeNo(partnerTradeNo) + if err != nil { + return nil, err + } + return res, nil } diff --git a/test/pay_test.go b/test/pay_test.go index aec3d7b..f41b633 100644 --- a/test/pay_test.go +++ b/test/pay_test.go @@ -2,8 +2,10 @@ package test import ( "fmt" + "github.com/chanxuehong/wechat/mch/core" pay_service "hudongzhuanjia/services/pay" "testing" + "time" ) var openId = "o9XM41s_NN8Y0QK6_MbM-aYMV3TE" @@ -20,3 +22,27 @@ func TestSendRedPack(t *testing.T) { fmt.Println(err) fmt.Printf("%+v\n", res) } + +func TestQueryOrder(t *testing.T) { + //outTradeNo := "Dn13Gl6A6dB6aOae7syqtXSwUvCZ3mta" + outTradeNo := "jGrqipGJdcxQz4uwSTQqrtnJ4rJE6Mx9" + res, err := pay_service.OrderQuery(outTradeNo) + fmt.Println(err) + fmt.Printf("%+v\n", res) +} + +func TestClose(t *testing.T) { + pay_service.Close("jGrqipGJdcxQz4uwSTQqrtnJ4rJE6Mx9") +} + +func TestTimeExpire(t *testing.T) { + now := time.Now() + t1 := "202004174430" + t2 := core.FormatTime(time.Now()) + t3, _ := core.ParseTime(t1) + fmt.Println(t1, t2) + fmt.Println(t1 > t2) + fmt.Println(t3.Unix() > now.Unix()) + fmt.Println(t1, t2, core.FormatTime(t3)) + fmt.Println(t3.Unix(), now.Unix()) +}