更新小程序和管理后台配置,将 API 地址切换为生产环境。新增撤回打款功能,允许用户在特定状态下撤回打款请求,并优化相关页面交互。更新文档以反映新的流程图,确保用户体验一致性。

This commit is contained in:
Alex-larget
2026-03-20 15:40:55 +08:00
parent d34d209b37
commit 0eee5a5fb7
10 changed files with 218 additions and 86 deletions

View File

@@ -351,6 +351,45 @@ func AdminWithdrawalsAction(c *gin.Context) {
}
}
// AdminWithdrawalsCancel POST /api/admin/withdrawals/cancel 撤回打款(仅 processing/pending_confirm 可撤回)
func AdminWithdrawalsCancel(c *gin.Context) {
var body struct {
ID string `json:"id"`
}
if err := c.ShouldBindJSON(&body); err != nil || body.ID == "" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 id"})
return
}
db := database.DB()
var w model.Withdrawal
if err := db.Where("id = ?", body.ID).First(&w).Error; err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "提现记录不存在"})
return
}
st := ""
if w.Status != nil {
st = *w.Status
}
if st != "processing" && st != "pending_confirm" {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "当前状态不允许撤回,仅待确认收款时可撤回"})
return
}
outBillNo := body.ID
if w.DetailNo != nil && *w.DetailNo != "" {
outBillNo = *w.DetailNo
}
if err := wechat.CancelTransferByOutBill(outBillNo); err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": "撤回失败: " + err.Error()})
return
}
now := time.Now()
reason := "商户已撤回"
_ = db.Model(&w).Updates(map[string]interface{}{
"status": "failed", "fail_reason": reason, "error_message": reason, "processed_at": now,
}).Error
c.JSON(http.StatusOK, gin.H{"success": true, "message": "已撤回打款"})
}
// AdminWithdrawalsSync POST /api/admin/withdrawals/sync 主动向微信查询转账结果并更新状态(无回调时的备选)
// body: { "id": "提现记录id" } 同步单条;不传 id 或 id 为空则同步所有 processing/pending_confirm
func AdminWithdrawalsSync(c *gin.Context) {

View File

@@ -118,7 +118,14 @@ func WithdrawPost(c *gin.Context) {
if err := db.Where("config_key = ?", "referral_config").First(&refCfg).Error; err == nil {
var config map[string]interface{}
if _ = json.Unmarshal(refCfg.ConfigValue, &config); config != nil {
if enabled, ok := config["enableAutoWithdraw"].(bool); ok && enabled {
enabled := false
switch v := config["enableAutoWithdraw"].(type) {
case bool:
enabled = v
case float64:
enabled = v != 0
}
if enabled {
go func(id string) {
if _, e := doApproveWithdrawal(db, id); e != nil {
fmt.Printf("[WithdrawPost] 自动审批失败 id=%s: %v\n", id, e)

View File

@@ -77,6 +77,7 @@ func Setup(cfg *config.Config) *gin.Engine {
admin.DELETE("/referral", handler.AdminReferral)
admin.GET("/withdrawals", handler.AdminWithdrawalsList)
admin.PUT("/withdrawals", handler.AdminWithdrawalsAction)
admin.POST("/withdrawals/cancel", handler.AdminWithdrawalsCancel)
admin.POST("/withdrawals/sync", handler.AdminWithdrawalsSync)
admin.GET("/withdrawals/auto-approve", handler.AdminWithdrawalsAutoApproveGet)
admin.PUT("/withdrawals/auto-approve", handler.AdminWithdrawalsAutoApprovePut)

View File

@@ -213,6 +213,17 @@ func InitiateTransferByFundApp(params FundAppTransferParams) (*FundAppTransferRe
return result, nil
}
// CancelTransferByOutBill 撤销转账(用户确认收款前,商户可主动撤回)
// 接口POST /v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{out_bill_no}/cancel
func CancelTransferByOutBill(outBillNo string) error {
if paymentApp == nil || paymentApp.FundApp == nil {
return fmt.Errorf("支付/转账未初始化")
}
ctx := context.Background()
_, err := paymentApp.FundApp.Cancel(ctx, outBillNo)
return err
}
// QueryTransferByOutBill 按商户单号查询单笔转账结果FundApp 接口,用于 sync
func QueryTransferByOutBill(outBillNo string) (state, transferBillNo, failReason string, err error) {
if paymentApp == nil || paymentApp.FundApp == nil {