优化小程序推荐码处理逻辑,支持通过扫码场景解析推荐码和初始章节ID。新增获取用户邀请码的功能以便于分享。更新分享配置,确保分享时自动带上推荐码。调整部分页面逻辑以提升用户体验。
This commit is contained in:
@@ -1,14 +1,72 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"soul-api/internal/database"
|
||||
"soul-api/internal/model"
|
||||
"soul-api/internal/wechat"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CronSyncOrders GET/POST /api/cron/sync-orders
|
||||
// 对账:查询 status=created 的订单,向微信查询实际状态,若已支付则补齐更新(防漏单)
|
||||
func CronSyncOrders(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
db := database.DB()
|
||||
var createdOrders []model.Order
|
||||
// 只处理最近 24 小时内创建的未支付订单
|
||||
cutoff := time.Now().Add(-24 * time.Hour)
|
||||
if err := db.Where("status = ? AND created_at > ?", "created", cutoff).Find(&createdOrders).Error; err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
synced := 0
|
||||
ctx := context.Background()
|
||||
for _, o := range createdOrders {
|
||||
tradeState, transactionID, totalFee, err := wechat.QueryOrderByOutTradeNo(ctx, o.OrderSN)
|
||||
if err != nil {
|
||||
fmt.Printf("[SyncOrders] 查询订单 %s 失败: %v\n", o.OrderSN, err)
|
||||
continue
|
||||
}
|
||||
if tradeState != "SUCCESS" {
|
||||
continue
|
||||
}
|
||||
// 微信已支付,本地未更新 → 补齐
|
||||
totalAmount := float64(totalFee) / 100
|
||||
now := time.Now()
|
||||
if err := db.Model(&o).Updates(map[string]interface{}{
|
||||
"status": "paid",
|
||||
"transaction_id": transactionID,
|
||||
"pay_time": now,
|
||||
"updated_at": now,
|
||||
}).Error; err != nil {
|
||||
fmt.Printf("[SyncOrders] 更新订单 %s 失败: %v\n", o.OrderSN, err)
|
||||
continue
|
||||
}
|
||||
synced++
|
||||
fmt.Printf("[SyncOrders] 补齐漏单: %s, amount=%.2f\n", o.OrderSN, totalAmount)
|
||||
|
||||
// 同步后续逻辑(全书、分销等)
|
||||
pt := "fullbook"
|
||||
if o.ProductType != "" {
|
||||
pt = o.ProductType
|
||||
}
|
||||
if pt == "fullbook" {
|
||||
db.Model(&model.User{}).Where("id = ?", o.UserID).Update("has_full_book", true)
|
||||
}
|
||||
processReferralCommission(db, o.UserID, totalAmount, o.OrderSN)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"synced": synced,
|
||||
"total": len(createdOrders),
|
||||
})
|
||||
}
|
||||
|
||||
// CronUnbindExpired GET/POST /api/cron/unbind-expired
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -425,15 +426,21 @@ func MiniprogramPayNotify(c *gin.Context) {
|
||||
TransactionID: &transactionID,
|
||||
PayTime: &now,
|
||||
}
|
||||
db.Create(&order)
|
||||
if err := db.Create(&order).Error; err != nil {
|
||||
fmt.Printf("[PayNotify] 补记订单失败: %s, err=%v\n", orderSn, err)
|
||||
return fmt.Errorf("create order: %w", err)
|
||||
}
|
||||
} else if *order.Status != "paid" {
|
||||
status := "paid"
|
||||
now := time.Now()
|
||||
db.Model(&order).Updates(map[string]interface{}{
|
||||
if err := db.Model(&order).Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"transaction_id": transactionID,
|
||||
"pay_time": now,
|
||||
})
|
||||
}).Error; err != nil {
|
||||
fmt.Printf("[PayNotify] 更新订单状态失败: %s, err=%v\n", orderSn, err)
|
||||
return fmt.Errorf("update order: %w", err)
|
||||
}
|
||||
fmt.Printf("[PayNotify] 订单状态已更新为已支付: %s\n", orderSn)
|
||||
} else {
|
||||
fmt.Printf("[PayNotify] 订单已支付,跳过更新: %s\n", orderSn)
|
||||
@@ -666,6 +673,30 @@ func MiniprogramQrcode(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// MiniprogramQrcodeImage GET /api/miniprogram/qrcode/image?scene=xxx&page=xxx&width=280
|
||||
// 直接返回 image/png,供小程序 wx.downloadFile 使用,便于开发工具与真机统一用 tempFilePath 绘制
|
||||
func MiniprogramQrcodeImage(c *gin.Context) {
|
||||
scene := c.Query("scene")
|
||||
if scene == "" {
|
||||
scene = "soul"
|
||||
}
|
||||
page := c.DefaultQuery("page", "pages/read/read")
|
||||
width, _ := strconv.Atoi(c.DefaultQuery("width", "280"))
|
||||
if width <= 0 {
|
||||
width = 280
|
||||
}
|
||||
imageData, err := wechat.GenerateMiniProgramCode(scene, page, width)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"error": fmt.Sprintf("生成小程序码失败: %v", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.Header("Content-Type", "image/png")
|
||||
c.Data(http.StatusOK, "image/png", imageData)
|
||||
}
|
||||
|
||||
// base64 编码
|
||||
func base64Encode(data []byte) string {
|
||||
const base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
Reference in New Issue
Block a user