refactor: update match configuration and user matching logic

- Changed the daily free match limit to a lifetime limit, aligning with backend configurations.
- Updated the match price display to include original pricing for better user clarity.
- Refactored match quota handling to sync with the server, ensuring accurate match counts and purchase statuses.
- Enhanced UI messages to reflect changes in match availability and pricing.
- Removed deprecated logic related to daily match counts, streamlining the matching process.
This commit is contained in:
Alex-larget
2026-04-01 17:09:45 +08:00
parent bdd0795434
commit 360bba697b
26 changed files with 374 additions and 781 deletions

View File

@@ -14,18 +14,30 @@ import (
"gorm.io/gorm"
)
const defaultFreeMatchLimit = 3
const defaultFreeMatchLimit = 1
const completeProfileSQL = "((phone IS NOT NULL AND phone != '') AND (nickname IS NOT NULL AND nickname != '' AND nickname != '微信用户') AND (avatar IS NOT NULL AND avatar != ''))"
// MatchQuota 匹配次数配额(纯计算:订单 + match_records
// 免费次数为「终身」额度不按自然日重置RemainToday JSON 字段名历史遗留,语义为「当前剩余可匹配次数」。
type MatchQuota struct {
PurchasedTotal int64 `json:"purchasedTotal"`
PurchasedUsed int64 `json:"purchasedUsed"`
MatchesUsedToday int64 `json:"matchesUsedToday"`
FreeRemainToday int64 `json:"freeRemainToday"`
MatchesUsedToday int64 `json:"matchesUsedToday"` // 今日已匹配次数(统计用)
FreeRemainToday int64 `json:"freeRemainToday"` // 终身免费剩余次数(字段名保留)
PurchasedRemain int64 `json:"purchasedRemain"`
RemainToday int64 `json:"remainToday"` // 今日剩余可匹配次数
RemainToday int64 `json:"remainToday"` // 当前剩余可匹配次数(免费剩余 + 已购剩余)
}
// normalizeFreeMatchLimit 产品规则:终身免费匹配仅 1 次(不按日重置);配置大于 1 时按 1 生效
func normalizeFreeMatchLimit(n int) int {
if n <= 0 {
return defaultFreeMatchLimit
}
if n > 1 {
return 1
}
return n
}
func getFreeMatchLimit(db *gorm.DB) int {
@@ -38,35 +50,31 @@ func getFreeMatchLimit(db *gorm.DB) int {
return defaultFreeMatchLimit
}
if v, ok := config["freeMatchLimit"].(float64); ok && v > 0 {
return int(v)
return normalizeFreeMatchLimit(int(v))
}
return defaultFreeMatchLimit
}
// GetMatchQuota 根据订单和 match_records 纯计算用户匹配配额
// GetMatchQuota 根据订单和 match_records 纯计算用户匹配配额(免费次数为终身额度,不按日重置)
func GetMatchQuota(db *gorm.DB, userID string, freeLimit int) MatchQuota {
if freeLimit <= 0 {
freeLimit = defaultFreeMatchLimit
}
freeLimit = normalizeFreeMatchLimit(freeLimit)
var purchasedTotal int64
db.Model(&model.Order{}).Where("user_id = ? AND product_type = ? AND status = ?", userID, "match", "paid").Count(&purchasedTotal)
var matchesToday int64
db.Model(&model.MatchRecord{}).Where("user_id = ? AND created_at >= CURDATE()", userID).Count(&matchesToday)
// 历史每日超出免费部分之和 = 已消耗的购买次数
var purchasedUsed int64
db.Raw(`
SELECT COALESCE(SUM(cnt - ?), 0) FROM (
SELECT DATE(created_at) AS d, COUNT(*) AS cnt
FROM match_records WHERE user_id = ?
GROUP BY DATE(created_at)
HAVING cnt > ?
) t
`, freeLimit, userID, freeLimit).Scan(&purchasedUsed)
freeUsed := matchesToday
if freeUsed > int64(freeLimit) {
freeUsed = int64(freeLimit)
var lifetimeMatches int64
db.Model(&model.MatchRecord{}).Where("user_id = ?", userID).Count(&lifetimeMatches)
fl := int64(freeLimit)
beyondFree := lifetimeMatches - fl
if beyondFree < 0 {
beyondFree = 0
}
freeRemain := int64(freeLimit) - freeUsed
purchasedUsed := beyondFree
if purchasedUsed > purchasedTotal {
purchasedUsed = purchasedTotal
}
freeRemain := fl - lifetimeMatches
if freeRemain < 0 {
freeRemain = 0
}
@@ -74,14 +82,17 @@ func GetMatchQuota(db *gorm.DB, userID string, freeLimit int) MatchQuota {
if purchasedRemain < 0 {
purchasedRemain = 0
}
remainToday := freeRemain + purchasedRemain
remainTotal := freeRemain + purchasedRemain
if remainTotal < 0 {
remainTotal = 0
}
return MatchQuota{
PurchasedTotal: purchasedTotal,
PurchasedUsed: purchasedUsed,
MatchesUsedToday: matchesToday,
FreeRemainToday: freeRemain,
PurchasedRemain: purchasedRemain,
RemainToday: remainToday,
RemainToday: remainTotal,
}
}
@@ -100,10 +111,11 @@ func MatchConfigGet(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{
"matchTypes": defaultMatchTypes,
"freeMatchLimit": 3,
"matchPrice": 1,
"settings": gin.H{"enableFreeMatches": true, "enablePaidMatches": true, "maxMatchesPerDay": 10},
"matchTypes": defaultMatchTypes,
"freeMatchLimit": 1,
"matchPrice": 1,
"matchPriceOriginal": 9.9,
"settings": gin.H{"enableFreeMatches": true, "enablePaidMatches": true, "maxMatchesPerDay": 10},
},
"source": "default",
})
@@ -129,14 +141,18 @@ func MatchConfigGet(c *gin.Context) {
matchTypes = defaultMatchTypes
}
}
freeMatchLimit := 3
if v, ok := config["freeMatchLimit"].(float64); ok {
freeMatchLimit = int(v)
freeMatchLimit := defaultFreeMatchLimit
if v, ok := config["freeMatchLimit"].(float64); ok && int(v) > 0 {
freeMatchLimit = normalizeFreeMatchLimit(int(v))
}
matchPrice := 1
if v, ok := config["matchPrice"].(float64); ok {
matchPrice = int(v)
}
matchPriceOriginal := 9.9
if v, ok := config["matchPriceOriginal"].(float64); ok && v > 0 {
matchPriceOriginal = v
}
settings := gin.H{"enableFreeMatches": true, "enablePaidMatches": true, "maxMatchesPerDay": 10}
if s, ok := config["settings"].(map[string]interface{}); ok {
for k, v := range s {
@@ -144,7 +160,11 @@ func MatchConfigGet(c *gin.Context) {
}
}
c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{
"matchTypes": matchTypes, "freeMatchLimit": freeMatchLimit, "matchPrice": matchPrice, "settings": settings,
"matchTypes": matchTypes,
"freeMatchLimit": freeMatchLimit,
"matchPrice": matchPrice,
"matchPriceOriginal": matchPriceOriginal,
"settings": settings,
}, "source": "database"})
}
@@ -188,7 +208,7 @@ func MatchUsers(c *gin.Context) {
if quota.RemainToday <= 0 {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "今日匹配次数已用完,请购买更多次数",
"message": "免费次数已用完,请购买匹配次数后再试",
"code": "QUOTA_EXCEEDED",
})
return