新增用户确认收款功能,更新提现记录模型以支持用户确认时间,并在提现管理页面展示确认状态。同时,优化提现处理逻辑,确保在用户确认后记录相关信息,提升系统稳定性和用户体验。
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -69,10 +70,15 @@ func AdminWithdrawalsList(c *gin.Context) {
|
||||
st = "pending_confirm"
|
||||
}
|
||||
}
|
||||
userConfirmedAt := interface{}(nil)
|
||||
if w.UserConfirmedAt != nil && !w.UserConfirmedAt.IsZero() {
|
||||
userConfirmedAt = w.UserConfirmedAt.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
withdrawals = append(withdrawals, gin.H{
|
||||
"id": w.ID, "userId": w.UserID, "userName": userName, "userAvatar": userAvatar,
|
||||
"amount": w.Amount, "status": st, "createdAt": w.CreatedAt,
|
||||
"method": "wechat", "account": account,
|
||||
"userConfirmedAt": userConfirmedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "withdrawals": withdrawals, "stats": gin.H{"total": len(withdrawals)}})
|
||||
@@ -218,20 +224,36 @@ func AdminWithdrawalsAction(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 打款已受理(FundApp 单笔),更新为处理中并保存商户单号、微信转账单号
|
||||
fmt.Printf("[AdminWithdrawals] 微信已受理 id=%s out_bill_no=%s transfer_bill_no=%s\n", body.ID, result.OutBillNo, result.TransferBillNo)
|
||||
processingStatus := "processing"
|
||||
if err := db.Model(&w).Updates(map[string]interface{}{
|
||||
"status": processingStatus,
|
||||
"detail_no": result.OutBillNo, // 回调用 out_bill_no 匹配此字段
|
||||
"batch_no": result.OutBillNo, // 单笔无批次,存同一单号便于查询
|
||||
// 打款已受理(微信同步返回),立即落库:商户单号、微信单号、package_info、按 state 设 status(不依赖回调)
|
||||
fmt.Printf("[AdminWithdrawals] 微信已受理 id=%s out_bill_no=%s transfer_bill_no=%s state=%s\n", body.ID, result.OutBillNo, result.TransferBillNo, result.State)
|
||||
rowStatus := "processing"
|
||||
if result.State == "WAIT_USER_CONFIRM" {
|
||||
rowStatus = "pending_confirm" // 待用户在小程序点击确认收款,回调在用户确认后才触发
|
||||
}
|
||||
upd := map[string]interface{}{
|
||||
"status": rowStatus,
|
||||
"detail_no": result.OutBillNo,
|
||||
"batch_no": result.OutBillNo,
|
||||
"batch_id": result.TransferBillNo,
|
||||
"processed_at": now,
|
||||
}).Error; err != nil {
|
||||
}
|
||||
if result.PackageInfo != "" {
|
||||
upd["package_info"] = result.PackageInfo
|
||||
}
|
||||
if err := db.Model(&w).Updates(upd).Error; err != nil {
|
||||
fmt.Printf("[AdminWithdrawals] 更新提现状态失败 id=%s: %v\n", body.ID, err)
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "更新状态失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
// 发起转账成功后发订阅消息(异步,失败不影响接口返回)
|
||||
if openID != "" {
|
||||
go func() {
|
||||
ctx := context.Background()
|
||||
if err := wechat.SendWithdrawSubscribeMessage(ctx, openID, w.Amount, true); err != nil {
|
||||
fmt.Printf("[AdminWithdrawals] 订阅消息发送失败 id=%s: %v\n", body.ID, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "已发起打款,微信处理中",
|
||||
|
||||
@@ -258,8 +258,10 @@ func WithdrawConfirmInfo(c *gin.Context) {
|
||||
if appId == "" {
|
||||
appId = "wxb8bbb2b10dec74aa"
|
||||
}
|
||||
// package 需由「用户确认模式」转账接口返回并落库,当前批量转账无 package,返回空;有值时可调 wx.requestMerchantTransfer
|
||||
packageInfo := ""
|
||||
if w.PackageInfo != nil && *w.PackageInfo != "" {
|
||||
packageInfo = *w.PackageInfo
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"data": gin.H{
|
||||
@@ -293,8 +295,16 @@ func WithdrawPendingConfirm(c *gin.Context) {
|
||||
"amount": w.Amount,
|
||||
"createdAt": w.CreatedAt,
|
||||
}
|
||||
// 若有 package 信息(requestMerchantTransfer 用),一并返回;当前直接打款无 package,给空字符串
|
||||
item["package"] = ""
|
||||
if w.PackageInfo != nil && *w.PackageInfo != "" {
|
||||
item["package"] = *w.PackageInfo
|
||||
} else {
|
||||
item["package"] = ""
|
||||
}
|
||||
if w.UserConfirmedAt != nil && !w.UserConfirmedAt.IsZero() {
|
||||
item["userConfirmedAt"] = w.UserConfirmedAt.Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
item["userConfirmedAt"] = nil
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
mchId := os.Getenv("WECHAT_MCH_ID")
|
||||
@@ -314,3 +324,41 @@ func WithdrawPendingConfirm(c *gin.Context) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// WithdrawConfirmReceived POST /api/miniprogram/withdraw/confirm-received 用户确认收款(记录已点击确认)
|
||||
// body: { "withdrawalId": "xxx", "userId": "xxx" },仅本人可操作,更新 user_confirmed_at
|
||||
func WithdrawConfirmReceived(c *gin.Context) {
|
||||
var req struct {
|
||||
WithdrawalID string `json:"withdrawalId" binding:"required"`
|
||||
UserID string `json:"userId" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "缺少 withdrawalId 或 userId"})
|
||||
return
|
||||
}
|
||||
db := database.DB()
|
||||
var w model.Withdrawal
|
||||
if err := db.Where("id = ? AND user_id = ?", req.WithdrawalID, req.UserID).First(&w).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "提现记录不存在或无权操作"})
|
||||
return
|
||||
}
|
||||
st := ""
|
||||
if w.Status != nil {
|
||||
st = *w.Status
|
||||
}
|
||||
// 仅处理中或已成功的可标记「用户已确认收款」
|
||||
if st != "processing" && st != "pending_confirm" && st != "success" {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "当前状态不可确认收款"})
|
||||
return
|
||||
}
|
||||
if w.UserConfirmedAt != nil && !w.UserConfirmedAt.IsZero() {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "已确认过"})
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
if err := db.Model(&w).Update("user_confirmed_at", now).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "message": "更新失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "message": "已记录确认收款"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user