doge 2 дней назад
Родитель
Сommit
d6ce84070b

+ 11 - 0
app/Services/BackflowService.php

@@ -104,7 +104,18 @@ class BackflowService extends BaseService
 
         $paginator = $query->select("backflow.*", "users.first_name", "users.account", "users.admin_note as user_admin_note")->orderByDesc('date')->orderBy('status')
             ->paginate($search['limit']);
+        // 未结清的记录按最新注单和当前规则实时重算(规则修改后存储值可能过期),与点“发放”时的计算一致
+        $tiers = null;
         foreach ($paginator->items() as $item) {
+            if (in_array((int) $item->status, Backflow::OPEN_STATUSES, true)) {
+                $tiers ??= RewardRuleService::tiers('backflow');
+                $user = RewardBasisService::user((string) $item->member_id);
+                $live = RewardBasisService::backflowReward($user ? RewardBasisService::backflowBasis($user, (string) $item->date) : [], $tiers);
+                $item['amount'] = $live['amount'];
+                $item['loss_amount'] = $live['loss_amount'];
+                $item['need_flow'] = $live['need_flow'];
+                $item['details'] = $live['details'];
+            }
             foreach (self::grantFields($item->toArray()) as $field => $value) $item[$field] = $value;
         }
 

+ 1 - 1
app/Services/RewardGrantService.php

@@ -78,7 +78,7 @@ class RewardGrantService
                 $payFlow = self::positive(bcsub($needFlow, (string) $reward->granted_need_flow, 2));
                 $closed = (string) $reward->date < $cutoff;
                 if (!$closed && bccomp($pay, '0', 2) <= 0) {
-                    throw new RuntimeException('暂无可发放金额', HttpStatus::CUSTOM_ERROR);
+                    throw new RuntimeException("暂无可发放金额(按当前规则应发 {$amount},已发放 " . bcadd((string) $reward->granted_amount, '0', 2) . ')', HttpStatus::CUSTOM_ERROR);
                 }
                 $remark .= ";本次发放:{$pay};累计发放:" . bcadd((string) $reward->granted_amount, $pay, 2);
             }

+ 2 - 2
docs/前端交付/bot-28/后台/回水-已发放与继续发放-前端接入.md

@@ -22,7 +22,7 @@
 
 | 字段 | 类型 | 说明 |
 |---|---|---|
-| `amount` | string | **应发总额**:按当前亏损与规则算出的本月回水,两位小数。未结清的记录会随注单实时更新 |
+| `amount` | string | **应发总额**:按当前亏损与规则算出的本月回水,两位小数。未结清(`status` 0/2)的记录在每次查询列表时按最新注单和**当前规则**实时重算,与点“发放”时一致;`loss_amount`、`need_flow`、`details` 同样为实时值 |
 | `granted_amount` | string | **已发放**:累计已打入会员钱包的金额 |
 | `pending_amount` | string | **待发放**:`amount − granted_amount`,最小 `0.00`;已发放(`status=1`)的记录恒为 `0.00` |
 | `status` | int | `0` 未发放/`1` 已发放/`2` 部分发放(新增) |
@@ -86,7 +86,7 @@
 
 | msg | 情况 |
 |---|---|
-| `暂无可发放金额` | 当月记录没有差额(`can_grant=false` 时不应能点到) |
+| `暂无可发放金额(按当前规则应发 x,已发放 y)` | 当月记录没有差额(`can_grant=false` 时不应能点到)。若列表刚显示有待发放,刷新列表即可看到最新金额 |
 | `奖励已发放,无需再次发放` | `status=1` 的记录 |
 | `数据不存在` | id 错误 |
 

+ 2 - 2
docs/总后台/回水-已发放与继续发放-前端接入.md

@@ -22,7 +22,7 @@
 
 | 字段 | 类型 | 说明 |
 |---|---|---|
-| `amount` | string | **应发总额**:按当前亏损与规则算出的本月回水,两位小数。未结清的记录会随注单实时更新 |
+| `amount` | string | **应发总额**:按当前亏损与规则算出的本月回水,两位小数。未结清(`status` 0/2)的记录在每次查询列表时按最新注单和**当前规则**实时重算,与点“发放”时一致;`loss_amount`、`need_flow`、`details` 同样为实时值 |
 | `granted_amount` | string | **已发放**:累计已打入会员钱包的金额 |
 | `pending_amount` | string | **待发放**:`amount − granted_amount`,最小 `0.00`;已发放(`status=1`)的记录恒为 `0.00` |
 | `status` | int | `0` 未发放/`1` 已发放/`2` 部分发放(新增) |
@@ -86,7 +86,7 @@
 
 | msg | 情况 |
 |---|---|
-| `暂无可发放金额` | 当月记录没有差额(`can_grant=false` 时不应能点到) |
+| `暂无可发放金额(按当前规则应发 x,已发放 y)` | 当月记录没有差额(`can_grant=false` 时不应能点到)。若列表刚显示有待发放,刷新列表即可看到最新金额 |
 | `奖励已发放,无需再次发放` | `status=1` 的记录 |
 | `数据不存在` | id 错误 |
 

+ 30 - 12
tests/Integration/AdminListAccountTest.php

@@ -43,6 +43,15 @@ class AdminListAccountTest extends TestCase
             $t->id(); $t->string('member_id'); $t->string('date'); $t->integer('status')->default(0);
             $t->decimal('amount', 20, 2)->default(0); $t->decimal('granted_amount', 20, 2)->default(0); $t->timestamps();
         });
+        $schema->create('backflow_rules', function (Blueprint $t) {
+            $t->id(); $t->string('category'); $t->decimal('min_loss_amount', 20, 2); $t->decimal('reward_amount', 20, 2);
+            $t->decimal('turnover_multiple', 20, 2)->default(0);
+        });
+        $schema->create('share_bet_facts', function (Blueprint $t) {
+            $t->id(); $t->unsignedBigInteger('user_id'); $t->string('category'); $t->date('bet_date'); $t->dateTime('bet_at');
+            $t->decimal('bet_amount', 24, 10); $t->decimal('effective_amount', 24, 10); $t->decimal('net_profit', 24, 10);
+            $t->boolean('voided')->default(false); $t->string('currency')->default('CNY');
+        });
         $schema->create('recharges', function (Blueprint $t) {
             $t->id(); $t->string('member_id'); $t->string('coin')->default('CNY'); $t->decimal('amount', 20, 2)->default(0);
             $t->integer('status')->default(0); $t->timestamps();
@@ -100,29 +109,38 @@ class AdminListAccountTest extends TestCase
         $this->assertSame(['m2' => 'bob'], $this->accounts(BackflowService::paginate(['limit' => 10, 'account' => 'bo'])['data']));
     }
 
-    public function test_backflow_list_shows_granted_pending_and_whether_more_can_be_granted(): void
+    public function test_backflow_list_recalculates_unpaid_rows_with_current_rules_and_shows_granted_and_pending(): void
     {
         \Carbon\Carbon::setTestNow(\Carbon\Carbon::parse('2026-11-05 15:00:00', 'Asia/Shanghai'));
         try {
+            DB::table('backflow_rules')->insert([
+                ['category' => 'self:lottery', 'min_loss_amount' => 10, 'reward_amount' => 8],
+                ['category' => 'self:lottery', 'min_loss_amount' => 300, 'reward_amount' => 50],
+            ]);
+            foreach ([1 => '-50', 2 => '-350', 3 => '-50'] as $userId => $profit) {
+                DB::table('share_bet_facts')->insert(['user_id' => $userId, 'category' => 'self:lottery', 'bet_date' => '2026-11-03',
+                    'bet_at' => '2026-11-03 12:00:00', 'bet_amount' => 100, 'effective_amount' => 100, 'net_profit' => $profit]);
+            }
+            // 存储的 amount=20 是旧规则算出的过期值;列表应按当前规则显示
             DB::table('backflow')->insert([
-                ['member_id' => 'm1', 'date' => '2026-11', 'status' => 0, 'amount' => 8, 'granted_amount' => 0],   // 当月未发
-                ['member_id' => 'm2', 'date' => '2026-11', 'status' => 2, 'amount' => 50, 'granted_amount' => 8],  // 当月部分发放,可补发
-                ['member_id' => 'm3', 'date' => '2026-11', 'status' => 2, 'amount' => 8, 'granted_amount' => 8],   // 当月已发完,暂无差额
-                ['member_id' => 'm1', 'date' => '2026-10', 'status' => 0, 'amount' => 0, 'granted_amount' => 0],   // 往月未结清
-                ['member_id' => 'm2', 'date' => '2026-10', 'status' => 1, 'amount' => 35, 'granted_amount' => 35], // 已发放
+                ['member_id' => 'm1', 'date' => '2026-11', 'status' => 0, 'amount' => 20, 'granted_amount' => 0],   // 当月未发:应发 8
+                ['member_id' => 'm2', 'date' => '2026-11', 'status' => 2, 'amount' => 20, 'granted_amount' => 8],   // 部分发放:应发 50,待发 42
+                ['member_id' => 'm3', 'date' => '2026-11', 'status' => 2, 'amount' => 20, 'granted_amount' => 8],   // 已发完:旧值会误显示待发 12
+                ['member_id' => 'm1', 'date' => '2026-10', 'status' => 0, 'amount' => 5, 'granted_amount' => 0],    // 往月无注单:应发 0,可点发放结清
+                ['member_id' => 'm2', 'date' => '2026-10', 'status' => 1, 'amount' => 35, 'granted_amount' => 35],  // 已发放:保留存储值
                 ['member_id' => 'm1', 'date' => '2026-12', 'status' => 0, 'amount' => 0, 'granted_amount' => 0],   // 未来月份不展示
             ]);
             $rows = [];
             foreach (json_decode(json_encode(BackflowService::paginate(['limit' => 10])['data']), true) as $row) {
-                $rows[$row['member_id'] . '|' . $row['date']] = [$row['granted_amount'], $row['pending_amount'], $row['can_grant'], $row['status_text']];
+                $rows[$row['member_id'] . '|' . $row['date']] = [$row['amount'], $row['granted_amount'], $row['pending_amount'], $row['can_grant'], $row['status_text']];
             }
             ksort($rows);
             $this->assertSame([
-                'm1|2026-10' => ['0.00', '0.00', true, '未发放'],
-                'm1|2026-11' => ['0.00', '8.00', true, '未发放'],
-                'm2|2026-10' => ['35.00', '0.00', false, '已发放'],
-                'm2|2026-11' => ['8.00', '42.00', true, '部分发放'],
-                'm3|2026-11' => ['8.00', '0.00', false, '部分发放'],
+                'm1|2026-10' => ['0.00', '0.00', '0.00', true, '未发放'],
+                'm1|2026-11' => ['8.00', '0.00', '8.00', true, '未发放'],
+                'm2|2026-10' => ['35.00', '35.00', '0.00', false, '已发放'],
+                'm2|2026-11' => ['50.00', '8.00', '42.00', true, '部分发放'],
+                'm3|2026-11' => ['8.00', '8.00', '0.00', false, '部分发放'],
             ], $rows);
             $this->assertSame(2, BackflowService::paginate(['limit' => 10, 'status' => 2])['total']);
         } finally {

+ 1 - 1
tests/Integration/AutomaticRewardGrantTest.php

@@ -558,7 +558,7 @@ class AutomaticRewardGrantTest extends TestCase
 
         // 没有差额时当月不能再发
         try { RewardGrantService::grant('backflow', $id, 'operator-one'); $this->fail('expected exception'); }
-        catch (\RuntimeException $e) { $this->assertSame('暂无可发放金额', $e->getMessage()); }
+        catch (\RuntimeException $e) { $this->assertSame('暂无可发放金额(按当前规则应发 8.00,已发放 8.00)', $e->getMessage()); }
 
         // 继续亏到 350:同步后应发 50,已发 8,待发 42
         $this->fact('1001', 'self:lottery', '2026-10-20 12:00:00', '300', '-300');