- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController - 新增 Service 层: TransactionService, ReportService, FollowPlanService - pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll) - 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan) - 后台新增跟单计划管理 + 用户输赢明细统计 - 封盘状态显示倒计时 (x:xx) - 音效仅在开奖弹窗打开时播放 - 路由按模块分组整理 - autoload 支持 App\Services 命名空间
246 lines
7.7 KiB
PHP
246 lines
7.7 KiB
PHP
<?php
|
|
namespace App\Services;
|
|
|
|
use Db\Database;
|
|
|
|
class FollowPlanService {
|
|
private $db;
|
|
|
|
public function __construct(Database $db) {
|
|
$this->db = $db;
|
|
}
|
|
|
|
/**
|
|
* 获取所有启用的跟单计划(前台展示)
|
|
*/
|
|
public function getActivePlans(int $gameId = 0): array
|
|
{
|
|
$where = ['status' => 1];
|
|
if ($gameId > 0) $where['game_id'] = $gameId;
|
|
$where['ORDER'] = ['id' => 'ASC'];
|
|
|
|
$plans = $this->db->select('follow_plans', '*', $where);
|
|
foreach ($plans as &$plan) {
|
|
$stats = $this->getPlanStats($plan['id']);
|
|
$plan['total_records'] = $stats['total'];
|
|
$plan['win_count'] = $stats['wins'];
|
|
$plan['win_rate'] = $stats['total'] > 0 ? round($stats['wins'] / $stats['total'] * 100, 1) : 0;
|
|
// 最近 10 期记录
|
|
$plan['recent'] = $this->db->select('follow_plan_records', '*', [
|
|
'plan_id' => $plan['id'],
|
|
'is_win[!]' => null,
|
|
'ORDER' => ['id' => 'DESC'],
|
|
'LIMIT' => 10
|
|
]);
|
|
}
|
|
unset($plan);
|
|
return $plans;
|
|
}
|
|
|
|
/**
|
|
* 获取计划统计
|
|
*/
|
|
public function getPlanStats(int $planId): array
|
|
{
|
|
$total = $this->db->count('follow_plan_records', [
|
|
'plan_id' => $planId,
|
|
'is_win[!]' => null
|
|
]);
|
|
$wins = $this->db->count('follow_plan_records', [
|
|
'plan_id' => $planId,
|
|
'is_win' => 1
|
|
]);
|
|
return ['total' => $total, 'wins' => $wins];
|
|
}
|
|
|
|
/**
|
|
* 获取用户的跟单状态
|
|
*/
|
|
public function getUserFollowStatus(int $userId): array
|
|
{
|
|
$rows = $this->db->select('user_follow_plans', '*', [
|
|
'user_id' => $userId,
|
|
'is_active' => 1
|
|
]);
|
|
$result = [];
|
|
foreach ($rows as $r) {
|
|
$result[$r['plan_id']] = $r;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 用户开始/停止跟单
|
|
*/
|
|
public function toggleFollow(int $userId, int $planId): array
|
|
{
|
|
$existing = $this->db->get('user_follow_plans', '*', [
|
|
'user_id' => $userId,
|
|
'plan_id' => $planId
|
|
]);
|
|
|
|
if ($existing) {
|
|
$newStatus = $existing['is_active'] ? 0 : 1;
|
|
$this->db->update('user_follow_plans', ['is_active' => $newStatus], [
|
|
'id' => $existing['id']
|
|
]);
|
|
return ['success' => true, 'active' => $newStatus];
|
|
}
|
|
|
|
// 新建
|
|
$plan = $this->db->get('follow_plans', '*', ['id' => $planId, 'status' => 1]);
|
|
if (!$plan) {
|
|
return ['success' => false, 'message' => '计划不存在或已禁用'];
|
|
}
|
|
|
|
$this->db->insert('user_follow_plans', [
|
|
'user_id' => $userId,
|
|
'plan_id' => $planId,
|
|
'is_active' => 1,
|
|
]);
|
|
return ['success' => true, 'active' => 1];
|
|
}
|
|
|
|
/**
|
|
* 获取用户跟单汇总(总胜率、总盈亏)
|
|
*/
|
|
public function getUserSummary(int $userId): array
|
|
{
|
|
$pdo = $this->db->medoo->pdo;
|
|
$totalWinRate = 0;
|
|
$totalProfit = 0;
|
|
|
|
try {
|
|
$stmt = $pdo->prepare("
|
|
SELECT SUM(total_bets) as bets, SUM(total_wins) as wins, SUM(total_profit) as profit
|
|
FROM user_follow_plans WHERE user_id = ?
|
|
");
|
|
$stmt->execute([$userId]);
|
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
|
$bets = intval($row['bets'] ?? 0);
|
|
$wins = intval($row['wins'] ?? 0);
|
|
$totalWinRate = $bets > 0 ? round($wins / $bets * 100, 1) : 0;
|
|
$totalProfit = floatval($row['profit'] ?? 0);
|
|
} catch (\Exception $e) {}
|
|
|
|
return ['win_rate' => $totalWinRate, 'profit' => $totalProfit];
|
|
}
|
|
|
|
// ===== 后台管理 =====
|
|
|
|
/**
|
|
* 获取所有计划(后台)
|
|
*/
|
|
public function getAllPlans(): array
|
|
{
|
|
$plans = $this->db->select('follow_plans', '*', ['ORDER' => ['id' => 'DESC']]);
|
|
foreach ($plans as &$plan) {
|
|
$stats = $this->getPlanStats($plan['id']);
|
|
$plan['total_records'] = $stats['total'];
|
|
$plan['win_count'] = $stats['wins'];
|
|
$plan['win_rate'] = $stats['total'] > 0 ? round($stats['wins'] / $stats['total'] * 100, 1) : 0;
|
|
$plan['follower_count'] = $this->db->count('user_follow_plans', [
|
|
'plan_id' => $plan['id'], 'is_active' => 1
|
|
]);
|
|
}
|
|
unset($plan);
|
|
return $plans;
|
|
}
|
|
|
|
/**
|
|
* 创建/更新计划
|
|
*/
|
|
public function savePlan(array $data): array
|
|
{
|
|
$fields = [
|
|
'game_id' => intval($data['game_id'] ?? 1),
|
|
'name' => trim($data['name'] ?? ''),
|
|
'plan_type' => trim($data['plan_type'] ?? 'bs'),
|
|
'target_rank' => intval($data['target_rank'] ?? 1),
|
|
'strategy' => trim($data['strategy'] ?? 'follow'),
|
|
'bet_amount' => floatval($data['bet_amount'] ?? 100),
|
|
'status' => intval($data['status'] ?? 1),
|
|
];
|
|
|
|
if (empty($fields['name'])) {
|
|
return ['success' => false, 'message' => '计划名称不能为空'];
|
|
}
|
|
|
|
$id = intval($data['id'] ?? 0);
|
|
if ($id > 0) {
|
|
$this->db->update('follow_plans', $fields, ['id' => $id]);
|
|
} else {
|
|
$fields['created_by'] = intval($data['created_by'] ?? 0);
|
|
$this->db->insert('follow_plans', $fields);
|
|
$id = intval($this->db->id());
|
|
}
|
|
|
|
return ['success' => true, 'id' => $id];
|
|
}
|
|
|
|
/**
|
|
* 删除计划
|
|
*/
|
|
public function deletePlan(int $planId): array
|
|
{
|
|
$this->db->delete('follow_plan_records', ['plan_id' => $planId]);
|
|
$this->db->delete('user_follow_plans', ['plan_id' => $planId]);
|
|
$this->db->delete('follow_plans', ['id' => $planId]);
|
|
return ['success' => true];
|
|
}
|
|
|
|
/**
|
|
* 添加计划推荐记录(由 cron 或后台手动触发)
|
|
*/
|
|
public function addRecord(int $planId, int $periodId, string $periodNumber, string $recommendValue): array
|
|
{
|
|
$exists = $this->db->get('follow_plan_records', 'id', [
|
|
'plan_id' => $planId, 'period_id' => $periodId
|
|
]);
|
|
if ($exists) {
|
|
return ['success' => false, 'message' => '该期已有推荐记录'];
|
|
}
|
|
|
|
$this->db->insert('follow_plan_records', [
|
|
'plan_id' => $planId,
|
|
'period_id' => $periodId,
|
|
'period_number' => $periodNumber,
|
|
'recommend_value' => $recommendValue,
|
|
]);
|
|
return ['success' => true, 'id' => intval($this->db->id())];
|
|
}
|
|
|
|
/**
|
|
* 结算推荐记录(开奖后调用)
|
|
*/
|
|
public function settleRecord(int $recordId, string $resultValue, bool $isWin): void
|
|
{
|
|
$this->db->update('follow_plan_records', [
|
|
'result_value' => $resultValue,
|
|
'is_win' => $isWin ? 1 : 0,
|
|
], ['id' => $recordId]);
|
|
|
|
// 更新所有跟了这个计划的用户统计
|
|
$record = $this->db->get('follow_plan_records', '*', ['id' => $recordId]);
|
|
if (!$record) return;
|
|
|
|
$plan = $this->db->get('follow_plans', '*', ['id' => $record['plan_id']]);
|
|
if (!$plan) return;
|
|
|
|
$betAmount = floatval($plan['bet_amount']);
|
|
$profit = $isWin ? $betAmount * 0.95 : -$betAmount; // 简化盈亏计算
|
|
|
|
$followers = $this->db->select('user_follow_plans', '*', [
|
|
'plan_id' => $record['plan_id'], 'is_active' => 1
|
|
]);
|
|
|
|
foreach ($followers as $f) {
|
|
$this->db->update('user_follow_plans', [
|
|
'total_bets[+]' => 1,
|
|
'total_wins[+]' => $isWin ? 1 : 0,
|
|
'total_profit[+]' => $profit,
|
|
], ['id' => $f['id']]);
|
|
}
|
|
}
|
|
}
|