|
|
@@ -315,6 +315,11 @@ class PointsService
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 测试用户(TEST_CPID / TEST_UID 白名单)跳过余额预检:只记录明细,不实际扣费
|
|
|
+ if ($this->isTestUser($uid)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
$balance = $this->getUserPointsBalance($uid);
|
|
|
if ($balance < $pointsNeeded) {
|
|
|
Utils::throwError(ErrorConst::POINTS_NOT_ENOUGH);
|
|
|
@@ -322,6 +327,59 @@ class PointsService
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 判断是否为测试用户(TEST_CPID / TEST_UID 白名单)
|
|
|
+ *
|
|
|
+ * TEST_CPID / TEST_UID 在 .env 中为英文逗号分隔的纯字符串(如 "1"、"142857,142858"),
|
|
|
+ * 读取后先转换为数组再判断。uid 命中 TEST_UID 或 cpid 命中 TEST_CPID(任一命中)即视为测试用户:
|
|
|
+ * 预检不再校验余额,扣费只记录明细、不实际扣减积分。
|
|
|
+ *
|
|
|
+ * @param int $uid 用户ID,缺省取当前登录用户
|
|
|
+ * @param int $cpid 公司ID,缺省取当前上下文公司ID
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function isTestUser(int $uid = 0, int $cpid = 0): bool
|
|
|
+ {
|
|
|
+ if (!$uid) {
|
|
|
+ $uid = (int)Site::getUid();
|
|
|
+ }
|
|
|
+ if (!$cpid) {
|
|
|
+ $cpid = (int)Site::getCpid();
|
|
|
+ }
|
|
|
+
|
|
|
+ $testUids = $this->parseEnvList(env('TEST_UID'));
|
|
|
+ $testCpids = $this->parseEnvList(env('TEST_CPID'));
|
|
|
+
|
|
|
+ if (!empty($testUids) && in_array((string)$uid, $testUids, true)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($testCpids) && in_array((string)$cpid, $testCpids, true)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 env 中英文逗号分隔的字符串转换为数组
|
|
|
+ *
|
|
|
+ * @param mixed $value
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function parseEnvList($value): array
|
|
|
+ {
|
|
|
+ if ($value === null || $value === '') {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ $items = array_map('trim', explode(',', (string)$value));
|
|
|
+
|
|
|
+ return array_values(array_filter($items, function ($item) {
|
|
|
+ return $item !== '';
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 视频生成成功后记录计费明细并扣减用户积分
|
|
|
*
|
|
|
* 幂等处理:同一任务只允许计费一次(type + task_id 唯一索引兜底)。
|
|
|
@@ -601,9 +659,12 @@ class PointsService
|
|
|
}
|
|
|
|
|
|
$pointsBefore = (float)getProp($user, 'points', 0);
|
|
|
- $pointsAfter = $pointsBefore - $points;
|
|
|
|
|
|
- if ($pointsBefore < $points) {
|
|
|
+ // 测试用户(TEST_CPID / TEST_UID 白名单):只记录明细,不实际扣减积分余额
|
|
|
+ $isTestUser = $this->isTestUser($uid, (int)getProp($user, 'cpid', 0));
|
|
|
+ $pointsAfter = $isTestUser ? $pointsBefore : $pointsBefore - $points;
|
|
|
+
|
|
|
+ if (!$isTestUser && $pointsBefore < $points) {
|
|
|
dLog('points')->warning('用户积分不足,扣费后积分为负数', [
|
|
|
'task_id' => $taskId,
|
|
|
'uid' => $uid,
|
|
|
@@ -612,11 +673,13 @@ class PointsService
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
- // 更新用户积分总额
|
|
|
- DB::table('mp_manage_users')->where('id', $uid)->update([
|
|
|
- 'points' => $pointsAfter,
|
|
|
- 'updated_at' => date('Y-m-d H:i:s')
|
|
|
- ]);
|
|
|
+ if (!$isTestUser) {
|
|
|
+ // 更新用户积分总额
|
|
|
+ DB::table('mp_manage_users')->where('id', $uid)->update([
|
|
|
+ 'points' => $pointsAfter,
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s')
|
|
|
+ ]);
|
|
|
+ }
|
|
|
|
|
|
// 记录积分使用明细
|
|
|
DB::table('mp_user_points_details')->insert([
|
|
|
@@ -641,7 +704,8 @@ class PointsService
|
|
|
'uid' => $uid,
|
|
|
'points_consumed' => $points,
|
|
|
'points_after' => $pointsAfter,
|
|
|
- 'tokens_consumed' => $tokens
|
|
|
+ 'tokens_consumed' => $tokens,
|
|
|
+ 'test_mode' => $isTestUser
|
|
|
]);
|
|
|
|
|
|
return [
|
|
|
@@ -651,7 +715,8 @@ class PointsService
|
|
|
'points_consumed' => $points,
|
|
|
'points_before' => $pointsBefore,
|
|
|
'points_after' => $pointsAfter,
|
|
|
- 'tokens_consumed' => $tokens
|
|
|
+ 'tokens_consumed' => $tokens,
|
|
|
+ 'test_mode' => $isTestUser
|
|
|
];
|
|
|
|
|
|
} catch (\Exception $e) {
|