feat: implement concurrent top-up locking mechanism to prevent race conditions
This commit is contained in:
@@ -844,18 +844,64 @@ type topUpRequest struct {
|
|||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var topUpLock = sync.Mutex{}
|
var topUpLocks sync.Map
|
||||||
|
var topUpCreateLock sync.Mutex
|
||||||
|
|
||||||
|
type topUpTryLock struct {
|
||||||
|
ch chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTopUpTryLock() *topUpTryLock {
|
||||||
|
return &topUpTryLock{ch: make(chan struct{}, 1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *topUpTryLock) TryLock() bool {
|
||||||
|
select {
|
||||||
|
case l.ch <- struct{}{}:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *topUpTryLock) Unlock() {
|
||||||
|
select {
|
||||||
|
case <-l.ch:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTopUpLock(userID int) *topUpTryLock {
|
||||||
|
if v, ok := topUpLocks.Load(userID); ok {
|
||||||
|
return v.(*topUpTryLock)
|
||||||
|
}
|
||||||
|
topUpCreateLock.Lock()
|
||||||
|
defer topUpCreateLock.Unlock()
|
||||||
|
if v, ok := topUpLocks.Load(userID); ok {
|
||||||
|
return v.(*topUpTryLock)
|
||||||
|
}
|
||||||
|
l := newTopUpTryLock()
|
||||||
|
topUpLocks.Store(userID, l)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
func TopUp(c *gin.Context) {
|
func TopUp(c *gin.Context) {
|
||||||
topUpLock.Lock()
|
id := c.GetInt("id")
|
||||||
defer topUpLock.Unlock()
|
lock := getTopUpLock(id)
|
||||||
|
if !lock.TryLock() {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": "充值处理中,请稍后重试",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer lock.Unlock()
|
||||||
req := topUpRequest{}
|
req := topUpRequest{}
|
||||||
err := c.ShouldBindJSON(&req)
|
err := c.ShouldBindJSON(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ApiError(c, err)
|
common.ApiError(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := c.GetInt("id")
|
|
||||||
quota, err := model.Redeem(req.Key, id)
|
quota, err := model.Redeem(req.Key, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ApiError(c, err)
|
common.ApiError(c, err)
|
||||||
@@ -866,7 +912,6 @@ func TopUp(c *gin.Context) {
|
|||||||
"message": "",
|
"message": "",
|
||||||
"data": quota,
|
"data": quota,
|
||||||
})
|
})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserSettingRequest struct {
|
type UpdateUserSettingRequest struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user