删除不再使用的文件,包括开放 API 鉴权规范文档、数据库迁移脚本和旧版图标组件,优化项目结构和资源管理。更新小程序代码以支持代付功能,增加代付分享弹窗和支付逻辑,提升用户体验。

This commit is contained in:
Alex-larget
2026-03-18 20:33:50 +08:00
parent 0f3933fabd
commit d6cdd6fdba
57 changed files with 1672 additions and 2761 deletions

View File

@@ -203,3 +203,86 @@ func CronSyncOrders(c *gin.Context) {
func CronUnbindExpired(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true})
}
// RunSyncVipCkbPlans 扫描已到期 VIP 用户,自动停用其绑定 Person 的存客宝计划
// - 最佳努力:停用失败只记日志,不中断整体任务
// - 幂等:重复执行不会产生额外副作用(计划已停用则仍然 update
func RunSyncVipCkbPlans(ctx context.Context, limit int) (scanned, disabled int, err error) {
if limit < 1 {
limit = 200
}
if limit > 2000 {
limit = 2000
}
db := database.DB()
// 只处理“有过期日且已过期,并且绑定了 Person(user_id) 且有 planId”的用户
// 说明persons.user_id 为新增字段;历史未绑定的不在本任务处理范围内
type row struct {
UserID string `gorm:"column:user_id"`
PlanID int64 `gorm:"column:ckb_plan_id"`
Nickname string `gorm:"column:nickname"`
}
rows := make([]row, 0)
q := `
SELECT u.id as user_id, p.ckb_plan_id, COALESCE(u.nickname,'') as nickname
FROM users u
INNER JOIN persons p ON p.user_id = u.id
WHERE u.is_vip = 1
AND u.vip_expire_date IS NOT NULL
AND u.vip_expire_date <= NOW()
AND p.ckb_plan_id > 0
ORDER BY u.vip_expire_date ASC
LIMIT ?`
if err := db.Raw(q, limit).Scan(&rows).Error; err != nil {
return 0, 0, err
}
scanned = len(rows)
if scanned == 0 {
return scanned, 0, nil
}
openToken, tokErr := ckbOpenGetToken()
if tokErr != nil {
// 没 token 直接失败,让 cron 重试(避免把用户标记成非 VIP 但计划未停用)
return scanned, 0, tokErr
}
for _, r := range rows {
select {
case <-ctx.Done():
return scanned, disabled, ctx.Err()
default:
}
if r.PlanID <= 0 || r.UserID == "" {
continue
}
if err := setCkbPlanEnabled(openToken, r.PlanID, false); err != nil {
syncOrdersLogf("停用存客宝计划失败: userId=%s, planId=%d, nickname=%s, err=%v", r.UserID, r.PlanID, r.Nickname, err)
continue
}
disabled++
syncOrdersLogf("已停用存客宝计划: userId=%s, planId=%d, nickname=%s", r.UserID, r.PlanID, r.Nickname)
// 兜底清理脏标记:到期用户将 is_vip 置为 0vip_expire_date 保留)
_ = db.Model(&model.User{}).Where("id = ?", r.UserID).Update("is_vip", false).Error
}
return scanned, disabled, nil
}
// CronSyncVipCkbPlans GET/POST /api/cron/sync-vip-ckb-plans
// ?limit=200 每次最多处理 N 个到期用户
func CronSyncVipCkbPlans(c *gin.Context) {
limit := 200
if s := strings.TrimSpace(c.Query("limit")); s != "" {
if n, err := strconv.Atoi(s); err == nil && n > 0 && n <= 2000 {
limit = n
}
}
scanned, disabled, err := RunSyncVipCkbPlans(c.Request.Context(), limit)
if err != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error(), "scanned": scanned, "disabled": disabled})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "scanned": scanned, "disabled": disabled, "limit": limit})
}