25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

160 satır
5.0 KiB

  1. package model
  2. import (
  3. "time"
  4. "gorm.io/gorm"
  5. )
  6. const (
  7. PendingSyncStatusPending = "pending"
  8. PendingSyncStatusSettled = "settled"
  9. PendingSyncStatusSynced = "synced"
  10. PendingSyncStatusFailed = "failed"
  11. PendingSyncStatusArchived = "archived"
  12. )
  13. // PendingSyncRecord 待同步消费记录
  14. type PendingSyncRecord struct {
  15. Id int `json:"id" gorm:"primaryKey"`
  16. UserId int `json:"user_id" gorm:"index:idx_user_status"`
  17. RemoteUserId int `json:"remote_user_id" gorm:"index"`
  18. RequestId string `json:"request_id" gorm:"type:varchar(128);uniqueIndex"`
  19. Quota int `json:"quota"`
  20. EstimatedQuota int `json:"estimated_quota"`
  21. Status string `json:"status" gorm:"default:'pending';index:idx_user_status;index:idx_status_created"`
  22. CreatedAt int64 `json:"created_at" gorm:"index:idx_status_created"`
  23. SyncedAt int64 `json:"synced_at"`
  24. RetryCount int `json:"retry_count" gorm:"default:0"`
  25. ErrorMsg string `json:"error_msg"`
  26. }
  27. func (PendingSyncRecord) TableName() string {
  28. return "pending_sync_records"
  29. }
  30. // GetPendingSyncQuota 获取用户待同步金额总和
  31. func GetPendingSyncQuota(userId int) int {
  32. var total int64
  33. DB.Model(&PendingSyncRecord{}).
  34. Where("user_id = ? AND status IN ?", userId, []string{PendingSyncStatusPending, PendingSyncStatusSettled, PendingSyncStatusFailed}).
  35. Select("COALESCE(SUM(quota), 0)").
  36. Scan(&total)
  37. return int(total)
  38. }
  39. // CreatePendingSyncRecord 创建待同步记录(status=pending)
  40. func CreatePendingSyncRecord(userId, remoteUserId int, requestId string, quota, estimatedQuota int) error {
  41. record := PendingSyncRecord{
  42. UserId: userId,
  43. RemoteUserId: remoteUserId,
  44. RequestId: requestId,
  45. Quota: quota,
  46. EstimatedQuota: estimatedQuota,
  47. Status: PendingSyncStatusPending,
  48. CreatedAt: time.Now().Unix(),
  49. }
  50. return DB.Create(&record).Error
  51. }
  52. // CreateSettledSyncRecord 创建已结算的同步记录(PreConsume 跳过时使用)
  53. func CreateSettledSyncRecord(userId, remoteUserId int, requestId string, quota int) error {
  54. record := PendingSyncRecord{
  55. UserId: userId,
  56. RemoteUserId: remoteUserId,
  57. RequestId: requestId,
  58. Quota: quota,
  59. EstimatedQuota: quota,
  60. Status: PendingSyncStatusSettled,
  61. CreatedAt: time.Now().Unix(),
  62. }
  63. return DB.Create(&record).Error
  64. }
  65. // SettlePendingSyncRecord 将 pending 记录结算为 settled,同时更新 quota
  66. // 返回 (是否找到并更新, 错误)
  67. func SettlePendingSyncRecord(requestId string, actualQuota int) (bool, error) {
  68. result := DB.Model(&PendingSyncRecord{}).
  69. Where("request_id = ? AND status = ?", requestId, PendingSyncStatusPending).
  70. Updates(map[string]interface{}{
  71. "quota": actualQuota,
  72. "status": PendingSyncStatusSettled,
  73. })
  74. if result.Error != nil {
  75. return false, result.Error
  76. }
  77. return result.RowsAffected > 0, nil
  78. }
  79. // GetPendingRecordsForSync 获取待同步记录(只同步 settled 和 failed)
  80. func GetPendingRecordsForSync(limit, maxRetry int) []PendingSyncRecord {
  81. var records []PendingSyncRecord
  82. DB.Where("status IN ? AND retry_count < ?",
  83. []string{PendingSyncStatusSettled, PendingSyncStatusFailed},
  84. maxRetry).
  85. Limit(limit).
  86. Order("created_at asc").
  87. Find(&records)
  88. return records
  89. }
  90. // MarkRecordSynced 标记记录为已同步
  91. func MarkRecordSynced(recordId int) error {
  92. return DB.Model(&PendingSyncRecord{}).Where("id = ?", recordId).Updates(map[string]interface{}{
  93. "status": PendingSyncStatusSynced,
  94. "synced_at": time.Now().Unix(),
  95. }).Error
  96. }
  97. // MarkRecordFailed 标记记录为失败
  98. func MarkRecordFailed(recordId int, errMsg string) error {
  99. return DB.Model(&PendingSyncRecord{}).Where("id = ?", recordId).Updates(map[string]interface{}{
  100. "status": PendingSyncStatusFailed,
  101. "retry_count": gorm.Expr("retry_count + 1"),
  102. "error_msg": errMsg,
  103. }).Error
  104. }
  105. // ArchiveStalePendingRecords 将超时的 pending 记录标记为 archived(请求崩溃的情况)
  106. func ArchiveStalePendingRecords(timeoutSeconds int64) int64 {
  107. cutoff := time.Now().Unix() - timeoutSeconds
  108. result := DB.Model(&PendingSyncRecord{}).
  109. Where("status = ? AND created_at < ?", PendingSyncStatusPending, cutoff).
  110. Update("status", PendingSyncStatusArchived)
  111. if result.Error != nil {
  112. return 0
  113. }
  114. return result.RowsAffected
  115. }
  116. // CleanupOldSyncedRecords 清理超过指定天数的已同步记录
  117. func CleanupOldSyncedRecords(days int) int64 {
  118. result := DB.Where("status = ? AND synced_at < ?",
  119. PendingSyncStatusSynced,
  120. time.Now().Unix()-int64(days*86400)).
  121. Delete(&PendingSyncRecord{})
  122. return result.RowsAffected
  123. }
  124. // GetPendingQuotaByUser 获取每个用户的待同步金额
  125. func GetPendingQuotaByUser() map[int]int {
  126. type result struct {
  127. UserId int
  128. Total int
  129. }
  130. var results []result
  131. DB.Model(&PendingSyncRecord{}).
  132. Select("user_id, COALESCE(SUM(quota), 0) as total").
  133. Where("status IN ?", []string{PendingSyncStatusPending, PendingSyncStatusSettled, PendingSyncStatusFailed}).
  134. Group("user_id").
  135. Scan(&results)
  136. m := make(map[int]int)
  137. for _, r := range results {
  138. m[r.UserId] = r.Total
  139. }
  140. return m
  141. }