事关破产, 求助如何写用户扣费逻辑

importmeta · 2024-9-24 18:53:35 · 43 次点击
我在写个功能:

1.调用第三方企业收费 API 转卖给个人,用户充值获得积分之后才能使用 API,自己先垫付钱.

2.第三方 API 调用失败不扣费.

3.我搞了多个队列来限流用户的请求,大概 20 多个.

4.数据库用的 MongoDB

这是精简版数据模型

```
数据表

id
userId // 用户 id
status // pending progress success fail
requestData // 用户的数据
resultData // 调用第三方 api 的结果

积分表

point
```



先保存用户的 requestData 请求数据, status 默认为 pending, 然后把这条数据给到 queue 队列, 队列里来处理逻辑.


下面是我的逻辑:

```ts
// 在开始事务
startTransaction();

try{

// 1.拿着 userId 查积分表 不足就 throw
if(point < 0) throw error NotEnoughPointError

// 2.如果处理成功过, 又因为某些原因又加入到 queue 里了
if(status == success) throw error AlreadyInQueueError

// 3.状态改为 progress
await setProgress()

// 4.转换一下用户格式 不修改表里的数据
await transformRequestData()

// 5.调用第三方 API 失败会抛出 ThirdAPIRrror
const result = await thirdAPI()

// 6.扣用户的积分
await minusUserPoint()

// 7.状态改为 success
await setSuccess()

// 8.提交事务
commitTransaction()

// 9.从队列里返回数据
return result

}catch(error){

// 1.终止事务
abortTransaction()

// 2.设置成错误状态
await setFail()

} finally {

// 1.结束事务
endSession();

}
```

问题来了...

1.我 20 多个队列同时大量这种请求, 队列是 Redis, 我加上这个事务是不是能解决并发扣费问题, 会不会多个操作同时扣费.

2.如果从 try 里面的调用第三方 API 之后的 `6.扣用户的积分` `7.状态改为 success` `8.提交事务` 这三个操作内部 throw 出错了,我钱就白花了.

3.我这逻辑有没有其他问题啊,实在没写过扣费,谢谢大家了.
举报· 43 次点击
登录 注册 站外分享
6 条回复  
rootx 初学 2024-9-24 19:01:33
先扣用户的 成功了 再调第三方的 出错的话 再返还积分给用户
lizon 小成 2024-9-24 20:12:08
把日志记好,出问题好追溯
zpfhbyx 小成 2024-9-24 20:50:47
api 无脑扣就好了  然后单写脚本来恢复积分.
drymonfidelia 小成 2024-9-24 21:53:01
招个合格的程序员来写,这都搞不定还是别自己写了
yhx77 初学 2024-9-25 10:29:51
搞个分布式锁啊 放并发
zizon 小成 2024-9-25 12:19:09
你如果关心 api 白花钱的话,把 api 响应持久化关联用户的请求特征.
这样即使你提到的 2 的问题发生也能从持久化/缓存里取回来,避免额外 api 花费.
返回顶部