算力扣费及列表接口
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\controller;
|
||||
|
||||
use app\chukebao\model\TokensRecord;
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
|
||||
class TokensRecordController extends BaseController
|
||||
{
|
||||
|
||||
|
||||
public function getList(){
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$type = $this->request->param('type', '');
|
||||
$form = $this->request->param('form', '');
|
||||
$userId = $this->getUserInfo('id');
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
|
||||
|
||||
$where = [
|
||||
['companyId','=',$companyId],
|
||||
['userId' ,'=', $userId]
|
||||
];
|
||||
|
||||
if ($type != '') {
|
||||
$where[] = ['type','=',$type];
|
||||
}
|
||||
if ($form != '') {
|
||||
$where[] = ['form','=',$form];
|
||||
}
|
||||
|
||||
|
||||
$query = TokensRecord::where($where);
|
||||
|
||||
$list = $query->where($where)->page($page,$limit)->order('id desc')->select();
|
||||
$total = $query->count();
|
||||
|
||||
foreach ($list as &$item) {
|
||||
if (in_array($item['type'],[1])){
|
||||
$nickname = Db::table('s2_wechat_friend')->where(['id' => $item['friendIdOrGroupId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
}
|
||||
if (in_array($item['type'],[2,3])){
|
||||
$nickname = Db::table('s2_wechat_chatroom')->where(['id' => $item['friendIdOrGroupId']])->value('nickname');
|
||||
$item['nickname'] = !empty($nickname) ? $nickname : '-';
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return ResponseHelper::success(['list'=>$list,'total'=>$total]);
|
||||
}
|
||||
|
||||
|
||||
public function consumeTokens($data = [])
|
||||
{
|
||||
if (empty($data)){
|
||||
return ResponseHelper::error('数据缺失');
|
||||
}
|
||||
|
||||
$tokens = isset($data['tokens']) ? intval($data['tokens']) : 0;
|
||||
$type = isset($data['type']) ? intval($data['type']) : 0;
|
||||
$form = isset($data['form']) ? intval($data['form']) : 0;
|
||||
$wechatAccountId = isset($data['wechatAccountId']) ? intval($data['wechatAccountId']) : 0;
|
||||
$friendIdOrGroupId = isset($data['friendIdOrGroupId']) ? intval($data['friendIdOrGroupId']) : 0;
|
||||
$remarks = isset($data['remarks']) ? $data['remarks'] : '';
|
||||
$companyId = isset($data['companyId']) ? intval($data['companyId']) : $this->getUserInfo('companyId');
|
||||
$userId = isset($data['userId']) ? intval($data['userId']) : $this->getUserInfo('id');
|
||||
|
||||
// 验证必要参数
|
||||
if ($tokens <= 0) {
|
||||
return ResponseHelper::error('tokens数量必须大于0');
|
||||
}
|
||||
|
||||
if (!in_array($type, [0, 1])) {
|
||||
return ResponseHelper::error('类型参数错误,0为减少,1为增加');
|
||||
}
|
||||
|
||||
if (!in_array($form, [0, 1, 2, 3, 4, 5])) {
|
||||
return ResponseHelper::error('来源参数错误');
|
||||
}
|
||||
|
||||
// 重试机制,最多重试3次
|
||||
$maxRetries = 3;
|
||||
$retryCount = 0;
|
||||
while ($retryCount < $maxRetries) {
|
||||
try {
|
||||
return $this->doConsumeTokens($userId, $companyId, $tokens, $type, $form, $wechatAccountId, $friendIdOrGroupId, $remarks);
|
||||
} catch (\Exception $e) {
|
||||
$retryCount++;
|
||||
if ($retryCount >= $maxRetries) {
|
||||
return ResponseHelper::error('操作失败,请稍后重试:' . $e->getMessage());
|
||||
}
|
||||
// 短暂延迟后重试
|
||||
usleep(100000); // 100ms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行tokens消费的核心方法
|
||||
*/
|
||||
private function doConsumeTokens($userId, $companyId, $tokens, $type, $form, $wechatAccountId, $friendIdOrGroupId, $remarks)
|
||||
{
|
||||
// 开启数据库事务
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 使用悲观锁获取用户当前tokens余额,确保并发安全
|
||||
$userInfo = Db::name('users')
|
||||
->where('id', $userId)
|
||||
->where('companyId', $companyId)
|
||||
->lock(true) // 悲观锁,防止并发问题
|
||||
->find();
|
||||
|
||||
if (!$userInfo) {
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
|
||||
$currentTokens = intval($userInfo['tokens']);
|
||||
|
||||
// 计算新的余额
|
||||
$newBalance = $type == 1 ? ($currentTokens + $tokens) : ($currentTokens - $tokens);
|
||||
|
||||
// 使用原子更新操作,基于当前值进行更新,防止并发覆盖
|
||||
$updateResult = Db::name('users')
|
||||
->where('id', $userId)
|
||||
->where('companyId', $companyId)
|
||||
->where('tokens', $currentTokens) // 确保基于当前值更新
|
||||
->update([
|
||||
'tokens' => $newBalance,
|
||||
'updateTime' => time()
|
||||
]);
|
||||
|
||||
if (!$updateResult) {
|
||||
// 如果更新失败,说明tokens值已被其他事务修改,需要重新获取
|
||||
throw new \Exception('tokens余额已被其他操作修改,请重试');
|
||||
}
|
||||
|
||||
// 记录tokens变动
|
||||
$recordData = [
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatAccountId' => $wechatAccountId,
|
||||
'friendIdOrGroupId' => $friendIdOrGroupId,
|
||||
'form' => $form,
|
||||
'type' => $type,
|
||||
'tokens' => $tokens,
|
||||
'balanceTokens' => $newBalance,
|
||||
'remarks' => $remarks,
|
||||
'createTime' => time()
|
||||
];
|
||||
|
||||
$recordId = Db::name('tokens_record')->insertGetId($recordData);
|
||||
|
||||
if (!$recordId) {
|
||||
throw new \Exception('记录tokens变动失败');
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
Db::commit();
|
||||
|
||||
return ResponseHelper::success([
|
||||
'recordId' => $recordId,
|
||||
'oldBalance' => $currentTokens,
|
||||
'newBalance' => $newBalance,
|
||||
'changeAmount' => $type == 1 ? $tokens : -$tokens
|
||||
], 'tokens变动记录成功');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 回滚事务
|
||||
Db::rollback();
|
||||
throw $e; // 重新抛出异常,让重试机制处理
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
16
Server/application/chukebao/model/TokensRecord.php
Normal file
16
Server/application/chukebao/model/TokensRecord.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace app\chukebao\model;
|
||||
|
||||
use think\Model;
|
||||
class TokensRecord extends Model
|
||||
{
|
||||
protected $pk = 'id';
|
||||
protected $name = 'tokens_record';
|
||||
|
||||
// 自动写入时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $createTime = 'createTime';
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user