| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354 |
- <?php
- namespace App\Module\Game\AdminControllers;
- use App\Module\Farm\Enums\DISASTER_TYPE;
- use App\Module\Farm\Enums\GROWTH_STAGE;
- use App\Module\Farm\Models\FarmCrop;
- use App\Module\Farm\Models\FarmGodBuff;
- use App\Module\Farm\Models\FarmHouseConfig;
- use App\Module\Farm\Models\FarmLand;
- use App\Module\Farm\Models\FarmUser;
- use App\Module\Farm\Services\DisasterService;
- use App\Module\Fund\Models\FundModel;
- use App\Module\Fund\Services\AccountService;
- use App\Module\GameItems\Enums\ITEM_TYPE;
- use App\Module\GameItems\Models\ItemUser;
- use App\Module\Pet\Models\PetUser;
- use App\Module\Pet\Models\PetActiveSkill;
- use App\Module\Pet\Models\PetSkillLog;
- use App\Module\Pet\Enums\PetStatus;
- use App\Module\User\Models\User;
- use Dcat\Admin\Grid;
- use Dcat\Admin\Layout\Content;
- use Dcat\Admin\Layout\Row;
- use Dcat\Admin\Widgets\Card;
- use Dcat\Admin\Widgets\Table;
- use Dcat\Admin\Widgets\Alert;
- use Spatie\RouteAttributes\Attributes\Get;
- use Spatie\RouteAttributes\Attributes\Resource;
- use UCore\DcatAdmin\AdminController;
- /**
- * 农场用户信息汇总控制器
- *
- * 用于展示用户的农场信息汇总,包括房屋、土地、作物、物品和代币信息
- */
- #[Resource('farm-user-summary', names: 'dcat.admin.farm-user-summary')]
- class FarmUserSummaryController extends AdminController
- {
- /**
- * 页面标题
- *
- * @var string
- */
- protected $title = '农场用户信息汇总';
- /**
- * 用户列表页面
- *
- * @param Content $content
- * @return Content
- */
- public function index(Content $content)
- {
- return $content
- ->title($this->title)
- ->description('查看用户的农场信息汇总')
- ->body($this->grid());
- }
- /**
- * 查看指定用户的农场信息汇总
- *
- * @param int $userId 用户ID
- * @param Content $content
- * @return Content
- */
- #[Get('farm-user-summary/{userId}', name: 'dcat.admin.farm-user-summary.show')]
- public function show($userId, Content $content)
- {
- // 记录请求信息
- \Illuminate\Support\Facades\Log::info('访问农场用户信息汇总', [
- 'user_id' => $userId,
- 'referer' => request()->header('referer'),
- 'user_agent' => request()->header('user-agent'),
- 'ip' => request()->ip(),
- 'url' => request()->fullUrl(),
- ]);
- // 查找用户
- $user = User::find($userId);
- if (!$user) {
- admin_error('错误', "用户 {$userId} 不存在");
- \Illuminate\Support\Facades\Log::warning('访问不存在的用户', [ 'user_id' => $userId ]);
- return redirect()->route('dcat.admin.farm-user-summary');
- }
- // 检查是否存在农场用户记录
- $farmUser = FarmUser::where('user_id', $userId)->first();
- if (!$farmUser) {
- admin_warning('提示', "用户 {$userId} 没有农场信息");
- \Illuminate\Support\Facades\Log::info('用户没有农场信息', [ 'user_id' => $userId ]);
- // 不重定向,继续显示用户信息,只是提示没有农场信息
- }
- return $content
- ->title($this->title)
- ->description("用户 {$user->username}(ID: {$user->id})的农场信息汇总")
- ->body(function (Row $row) use ($user) {
- // 第一行:用户基本信息和房屋信息
- $row->column(6, $this->userInfoCard($user));
- $row->column(6, $this->houseInfoCard($user->id));
- // 第二行:土地信息和作物信息
- $row->column(12, $this->landInfoCard($user->id));
- // 第三行:物品信息
- $row->column(12, $this->itemInfoCard($user->id));
- // 第四行:代币信息
- $row->column(12, $this->fundInfoCard($user->id));
- // 第五行:宠物信息
- $row->column(12, $this->petInfoCard($user->id));
- // 第六行:宠物生活技能情况
- $row->column(12, $this->petLifeSkillsCard($user->id));
- // 第七行:神像buff信息
- $row->column(12, $this->buffInfoCard($user->id));
- });
- }
- /**
- * 用户基本信息卡片
- *
- * @param User $user 用户对象
- * @return Card
- */
- protected function userInfoCard(User $user)
- {
- $userInfo = $user->info;
- $avatar = $userInfo ? $userInfo->avatar : '';
- $nickname = $userInfo ? $userInfo->nickname : '';
- $content = <<<HTML
- <div class="row">
- <div class="col-md-4">
- <img src="/saaa/{$avatar}" class="img-fluid rounded" style="max-width: 100px;" >
- </div>
- <div class="col-md-8">
- <p><strong>用户ID:</strong>{$user->id}</p>
- <p><strong>用户名:</strong>{$user->username}</p>
- <p><strong>昵称:</strong>{$nickname}</p>
- <p><strong>注册时间:</strong>{$user->created_at}</p>
- </div>
- </div>
- HTML;
- return new Card('用户基本信息', $content);
- }
- /**
- * 房屋信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function houseInfoCard($userId)
- {
- // 获取用户的农场信息
- $farmUser = FarmUser::where('user_id', $userId)->first();
- if (!$farmUser) {
- return new Card('房屋信息', new Alert('warning', '该用户没有农场信息'));
- }
- // 获取房屋配置信息
- $houseConfig = FarmHouseConfig::where('level', $farmUser->house_level)->first();
- if (!$houseConfig) {
- return new Card('房屋信息', new Alert('warning', "找不到房屋等级 {$farmUser->house_level} 的配置信息"));
- }
- $content = <<<HTML
- <div class="row">
- <div class="col-md-12">
- <p><strong>房屋等级:</strong>{$farmUser->house_level}</p>
- <p><strong>最后升级时间:</strong>{$farmUser->last_upgrade_time}</p>
- <p><strong>产出加成:</strong>{$houseConfig->output_bonus}</p>
- <p><strong>特殊土地上限:</strong>{$houseConfig->special_land_limit}</p>
- <p><strong>可用土地数量:</strong>{$houseConfig->available_lands}</p>
- HTML;
- // 如果有降级天数,显示降级信息
- if ($houseConfig->downgrade_days) {
- $content .= "<p><strong>降级天数:</strong>{$houseConfig->downgrade_days}</p>";
- }
- $content .= <<<HTML
- </div>
- </div>
- <div class="row mt-2">
- <div class="col-md-12">
- <a href="javascript:void(0);" class="btn btn-sm btn-primary" onclick="window.open('/admin/farm-house-configs', '_blank')">查看房屋配置</a>
- </div>
- </div>
- HTML;
- return new Card('房屋信息', $content);
- }
- /**
- * 土地信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function landInfoCard($userId)
- {
- // 获取用户的土地信息
- /**
- * @var FarmLand $land
- */
- $lands = FarmLand::with([ 'landType', 'crop.seed' ])
- ->where('user_id', $userId)
- ->get();
- if ($lands->isEmpty()) {
- return new Card('土地信息', new Alert('warning', '该用户没有土地信息'));
- }
- // 土地类型统计
- $landTypeStats = $lands->groupBy('land_type')->map->count();
- // 土地状态统计
- $landStatusStats = $lands->groupBy('status')->map->count();
- // 灾害统计
- $disasterStats = [
- 1 => 0, // 干旱
- 2 => 0, // 虫害
- 3 => 0, // 杂草
- ];
- // 统计活跃的灾害
- foreach ($lands as $land) {
- if ($land->crop && !empty($land->crop->disasters)) {
- foreach ($land->crop->disasters as $disaster) {
- if (($disaster['status'] ?? '') === 'active') {
- $type = $disaster['type'] ?? 0;
- if (isset($disasterStats[$type])) {
- $disasterStats[$type]++;
- }
- }
- }
- }
- }
- // 创建土地类型、状态和灾害的统计表格
- $statsContent = '<div class="row">';
- // 土地类型统计
- $statsContent .= '<div class="col-md-4">';
- $statsContent .= '<h5>土地类型统计</h5>';
- $statsContent .= '<table class="table table-sm table-bordered">';
- $statsContent .= '<thead><tr><th>土地类型</th><th>数量</th></tr></thead>';
- $statsContent .= '<tbody>';
- $landTypeNames = [
- 1 => '普通土地',
- 2 => '红土地',
- 3 => '黑土地',
- 4 => '金土地',
- 5 => '蓝土地',
- 6 => '紫土地',
- ];
- foreach ($landTypeStats as $typeId => $count) {
- $typeName = $landTypeNames[$typeId] ?? "类型{$typeId}";
- $statsContent .= "<tr><td>{$typeName}</td><td>{$count}</td></tr>";
- }
- $statsContent .= '</tbody></table></div>';
- // 土地状态统计
- $statsContent .= '<div class="col-md-4">';
- $statsContent .= '<h5>土地状态统计</h5>';
- $statsContent .= '<table class="table table-sm table-bordered">';
- $statsContent .= '<thead><tr><th>土地状态</th><th>数量</th></tr></thead>';
- $statsContent .= '<tbody>';
- $landStatusNames = [
- 0 => '空闲',
- 1 => '种植中',
- 2 => '灾害',
- 3 => '可收获',
- 4 => '枯萎',
- ];
- foreach ($landStatusStats as $statusId => $count) {
- $statusName = $landStatusNames[$statusId] ?? "状态{$statusId}";
- $statsContent .= "<tr><td>{$statusName}</td><td>{$count}</td></tr>";
- }
- $statsContent .= '</tbody></table></div>';
- // 灾害统计
- $statsContent .= '<div class="col-md-4">';
- $statsContent .= '<h5>灾害统计</h5>';
- $statsContent .= '<table class="table table-sm table-bordered">';
- $statsContent .= '<thead><tr><th>灾害类型</th><th>数量</th><th>减产比例</th></tr></thead>';
- $statsContent .= '<tbody>';
- $disasterNames = DISASTER_TYPE::getAll();
- $totalDisasters = 0;
- $d = DisasterService::getAllDisasters();
- foreach ($disasterStats as $typeId => $count) {
- $typeName = $disasterNames[$typeId] ?? "未知灾害{$typeId}";
- $penalty = $d[$typeId];
- $totalDisasters += $count;
- $statsContent .= "<tr><td>{$typeName}</td><td>{$count}</td><td>{$penalty}</td></tr>";
- }
- // 添加总计行
- $totalPenalty = $totalDisasters > 0 ? ($totalDisasters * 5) . '%' : '0%';
- $statsContent .= "<tr class='table-info'><td><strong>总计</strong></td><td>{$totalDisasters}</td><td>{$totalPenalty}</td></tr>";
- $statsContent .= '</tbody></table></div>';
- $statsContent .= '</div>';
- // 创建土地详情表格
- $headers = [
- 'ID', '位置', '土地类型', '状态', '种植作物', '种植时间', '生长阶段', '本阶段开始时间', '本阶段结束时间(剩余)',
- '果实信息', '灾害情况'
- ];
- $rows = [];
- foreach ($lands as $land) {
- $landType = $land->landType ? $land->landType->name : "类型{$land->land_type}";
- $status = $landStatusNames[$land->status] ?? "状态{$land->status}";
- $crop = $land->crop;
- $cropInfo = '无';
- $plantTime = '';
- $growthStage = '';
- $stageStartTime = '';
- $stageEndTime = '';
- $fruitInfo = '无';
- $disasterInfo = '无';
- if ($crop) {
- $seedName = $crop->seed ? $crop->seed->name : "种子{$crop->seed_id}";
- $cropInfo = $seedName;
- $plantTime = $crop->plant_time;
- $growthStage = $this->getGrowthStageName($crop->growth_stage);
- $stageStartTime = $crop->stage_start_time;
- $stageEndTime = $this->formatRelativeTime($crop->stage_end_time);
- // 处理果实信息
- if ($crop->final_output_item_id) {
- $fruitInfo = $this->getFruitInfo($crop->final_output_item_id);
- } else {
- // 如果还没有确定最终产出,显示种子的可能产出
- if ($crop->seed && $crop->seed->outputs) {
- $fruitInfo = $this->getSeedPossibleOutputs($crop->seed);
- }
- }
- // 处理灾害信息
- if (!empty($crop->disasters)) {
- $disasterInfo = $this->formatDisasterInfo($crop->disasters);
- }
- }
- $rows[] = [
- $land->id,
- $land->position,
- $landType,
- $status,
- $cropInfo,
- $plantTime,
- $growthStage,
- $stageStartTime,
- $stageEndTime,
- $fruitInfo,
- $disasterInfo,
- ];
- }
- $table = new Table($headers, $rows);
- $content = $statsContent . '<div class="mt-3">' . $table->render() . '</div>';
- $content .= <<<HTML
- <div class="row mt-2">
- <div class="col-md-12">
- <a href="javascript:void(0);" class="btn btn-sm btn-primary"
- onclick="window.open('/admin/farm-lands?user_id={$userId}', '_blank')">查看土地详情</a>
- <a href="javascript:void(0);" class="btn btn-sm btn-primary"
- onclick="window.open('/admin/farm-crops?user_id={$userId}', '_blank')">查看作物详情</a>
- </div>
- </div>
- HTML;
- return new Card('土地信息', $content);
- }
- /**
- * 获取生长阶段名称
- *
- * @param GROWTH_STAGE|int $stage 生长阶段枚举或整数值
- * @return string 生长阶段名称
- */
- protected function getGrowthStageName($stage)
- {
- // 如果传入的是整数,转换为枚举
- if (is_int($stage)) {
- try {
- $stage = GROWTH_STAGE::from($stage);
- } catch (\ValueError) {
- return "未知阶段{$stage}";
- }
- }
- // 如果是null,返回未知
- if ($stage === null) {
- return "未知阶段";
- }
- // 获取阶段名称
- try {
- $stageNames = GROWTH_STAGE::getAll();
- $stageValue = $stage->value;
- return $stageNames[$stageValue] ?? "阶段{$stageValue}";
- } catch (\Throwable) {
- return "错误阶段";
- }
- }
- /**
- * 格式化相对时间描述
- *
- * @param string|\Carbon\Carbon|null $dateTime 时间
- * @return string 相对时间描述
- */
- protected function formatRelativeTime($dateTime): string
- {
- if (!$dateTime) {
- return '无';
- }
- try {
- // 确保是Carbon实例
- if (is_string($dateTime)) {
- $dateTime = \Carbon\Carbon::parse($dateTime);
- }
- $now = now();
- // 如果时间已经过去
- if ($dateTime->isPast()) {
- return '<span class="text-danger">已过期</span>';
- }
- // 计算时间差
- $diffInSeconds = $now->diffInSeconds($dateTime);
- $diffInMinutes = $now->diffInMinutes($dateTime);
- $diffInHours = $now->diffInHours($dateTime);
- $diffInDays = $now->diffInDays($dateTime);
- // 根据时间差返回合适的描述
- if ($diffInSeconds < 60) {
- $seconds = round($diffInSeconds);
- return "<span class='text-warning'>{$seconds}秒后</span>";
- } elseif ($diffInMinutes < 60) {
- $minutes = round($diffInMinutes);
- return "<span class='text-info'>{$minutes}分钟后</span>";
- } elseif ($diffInHours < 24) {
- // 小时显示保留1位小数,但如果是整数则不显示小数
- $hours = round($diffInHours, 1);
- $hoursDisplay = $hours == intval($hours) ? intval($hours) : $hours;
- return "<span class='text-primary'>{$hoursDisplay}小时后</span>";
- } elseif ($diffInDays < 7) {
- $days = round($diffInDays);
- return "<span class='text-secondary'>{$days}天后</span>";
- } else {
- // 超过7天显示具体日期
- return $dateTime->format('Y-m-d H:i:s');
- }
- } catch (\Throwable $e) {
- return '<span class="text-muted">时间格式错误</span>';
- }
- }
- /**
- * 物品信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function itemInfoCard($userId)
- {
- // 获取用户的物品信息
- $items = ItemUser::with('item')
- ->where('user_id', $userId)
- ->orderBy('quantity', 'desc')
- ->limit(20)
- ->get();
- if ($items->isEmpty()) {
- return new Card('物品信息', new Alert('warning', '该用户没有物品信息'));
- }
- // 创建物品表格
- $headers = [ '物品ID', '物品名称', '数量', '物品类型', '过期时间' ];
- $rows = [];
- foreach ($items as $item) {
- $itemName = $item->item ? $item->item->name : "物品{$item->item_id}";
- $itemType = $item->item ? $this->getItemTypeName($item->item->type) : '';
- $rows[] = [
- $item->item_id,
- $itemName,
- $item->quantity,
- $itemType,
- $item->expire_at ?: '永久',
- ];
- }
- $table = new Table($headers, $rows);
- // 获取用户物品总数
- $totalCount = ItemUser::where('user_id', $userId)->count();
- $content = <<<HTML
- <div class="alert alert-info">
- 用户共有 {$totalCount} 种物品,下表显示数量最多的前20种物品
- </div>
- {$table->render()}
- <div class="row mt-2">
- <div class="col-md-12">
- <a href="javascript:void(0);" class="btn btn-sm btn-primary" onclick="window.open('/admin/game-items-user-items?user_id={$userId}', '_blank')">查看物品详情</a>
- </div>
- </div>
- HTML;
- return new Card('物品信息', $content);
- }
- /**
- * 获取物品类型名称
- *
- * @param ITEM_TYPE|int $type 物品类型枚举或整数值
- * @return string 物品类型名称
- */
- protected function getItemTypeName($type)
- {
- // 如果传入的是整数,转换为枚举
- if (is_int($type)) {
- try {
- $type = ITEM_TYPE::from($type);
- } catch (\ValueError) {
- return "未知类型{$type}";
- }
- }
- // 如果是null,返回未知
- if ($type === null) {
- return "未知类型";
- }
- // 获取类型名称
- try {
- $typeNames = ITEM_TYPE::getValueDescription();
- $typeValue = $type->value;
- return $typeNames[$typeValue] ?? "类型{$typeValue}";
- } catch (\Throwable) {
- return "错误类型";
- }
- }
- /**
- * 格式化灾害信息
- *
- * @param array $disasters 灾害数组
- * @return string 格式化后的灾害信息
- */
- protected function formatDisasterInfo(array $disasters): string
- {
- if (empty($disasters)) {
- return '无';
- }
- $result = [];
- foreach ($disasters as $disaster) {
- $type = $disaster['type'] ?? 0;
- $status = $disaster['status'] ?? 'active'; // 默认为活跃状态
- // 获取灾害类型名称
- $typeName = DISASTER_TYPE::getName($type);
- // 灾害状态
- $statusText = $status === 'active' ? '<span class="badge badge-danger">活跃</span>' : '<span class="badge badge-secondary">已处理</span>';
- // 灾害开始时间
- $startTime = isset($disaster['start_time']) ? date('Y-m-d H:i:s', $disaster['start_time']) : '未知';
- // 灾害结束时间(如果有)
- $endTime = '';
- if (isset($disaster['end_time']) && $disaster['end_time'] > 0) {
- $endTime = date('Y-m-d H:i:s', $disaster['end_time']);
- }
- // 减产比例 - 从DisasterService获取默认值
- $defaultPenalties = \App\Module\Farm\Services\DisasterService::getAllDisasters();
- $defaultPenalty = $defaultPenalties[$type] ?? 0.05;
- $penalty = isset($disaster['penalty']) ? ($disaster['penalty'] * 100) . '%' : ($defaultPenalty * 100) . '%';
- // 组合灾害信息
- $disasterInfo = "<div><strong>{$typeName}</strong>: {$statusText}</div>";
- $disasterInfo .= "<div>开始: {$startTime}</div>";
- if ($endTime) {
- $disasterInfo .= "<div>结束: {$endTime}</div>";
- }
- $disasterInfo .= "<div>减产: {$penalty}</div>";
- // 如果有额外的灾害信息,也显示出来
- if (isset($disaster['id'])) {
- $disasterInfo .= "<div>ID: {$disaster['id']}</div>";
- }
- $result[] = $disasterInfo;
- }
- return implode('<hr style="margin: 5px 0;">', $result);
- }
- /**
- * 代币信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function fundInfoCard($userId)
- {
- // 获取用户的代币信息
- $funds = FundModel::where('user_id', $userId)->get();
- if ($funds->isEmpty()) {
- return new Card('代币信息', new Alert('warning', '该用户没有代币信息'));
- }
- // 获取资金类型名称映射
- $fundNames = AccountService::getFundsDesc();
- // 创建代币表格
- $headers = [ '账户ID', '账户名称', '余额', '更新时间' ];
- $rows = [];
- foreach ($funds as $fund) {
- try {
- $fundIdValue = $fund->fund_id->value();
- $fundName = $fundNames[$fundIdValue] ?? "账户{$fundIdValue}";
- $balance = $fund->balance;
- $rows[] = [
- $fundIdValue,
- $fundName,
- $balance,
- $fund->update_time ? date('Y-m-d H:i:s', $fund->update_time) : '',
- ];
- } catch (\Throwable) {
- // 如果出现异常,添加一个错误行
- $rows[] = [
- $fund->id ?? '未知',
- '数据错误',
- $fund->balance ?? '未知',
- $fund->update_time ? date('Y-m-d H:i:s', $fund->update_time) : '',
- ];
- }
- }
- $table = new Table($headers, $rows);
- $content = <<<HTML
- {$table->render()}
- <div class="row mt-2">
- <div class="col-md-12">
- <a href="javascript:void(0);" class="btn btn-sm btn-primary" onclick="window.open('/admin/fund-accounts?user_id={$userId}', '_blank')">查看账户详情</a>
- </div>
- </div>
- HTML;
- return new Card('代币信息', $content);
- }
- /**
- * 宠物信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function petInfoCard($userId)
- {
- // 获取用户的宠物信息
- $pets = PetUser::where('user_id', $userId)
- ->orderBy('level', 'desc')
- ->orderBy('grade', 'desc')
- ->get();
- if ($pets->isEmpty()) {
- return new Card('宠物信息', new Alert('warning', '该用户没有宠物信息'));
- }
- // 宠物统计
- $totalPets = $pets->count();
- $gradeStats = $pets->groupBy('grade')->map->count();
- $statusStats = $pets->groupBy('status')->map->count();
- $levelStats = [
- 'max_level' => $pets->max('level'),
- 'avg_level' => round($pets->avg('level'), 1),
- 'total_exp' => $pets->sum('experience'),
- ];
- // 创建宠物表格
- $headers = ['宠物ID','宠物名称', '品阶', '等级', '经验', '体力', '状态', '创建时间'];
- $rows = [];
- $gradeNames = [
- 1 => '一品',
- 2 => '二品',
- 3 => '三品',
- 4 => '四品',
- ];
- $statusNames = [
- PetStatus::NONE->value => '未知',
- PetStatus::NORMAL->value => '正常',
- PetStatus::FIGHTING->value => '战斗中',
- PetStatus::DEAD->value => '死亡',
- PetStatus::FEEDING->value => '喂养中',
- PetStatus::TRAINING->value => '训练中',
- PetStatus::RESTING->value => '休息中',
- PetStatus::TRAVELING->value => '外出中',
- ];
- foreach ($pets as $pet) {
- try {
- $gradeName = $gradeNames[$pet->grade] ?? "品阶{$pet->grade}";
- $statusName = $statusNames[$pet->status->value] ?? "状态{$pet->status->value}";
- // 状态颜色
- $statusBadge = match($pet->status) {
- PetStatus::NORMAL => '<span class="badge badge-success">' . $statusName . '</span>',
- PetStatus::FIGHTING => '<span class="badge badge-warning">' . $statusName . '</span>',
- PetStatus::DEAD => '<span class="badge badge-danger">' . $statusName . '</span>',
- PetStatus::FEEDING, PetStatus::TRAINING => '<span class="badge badge-info">' . $statusName . '</span>',
- PetStatus::RESTING, PetStatus::TRAVELING => '<span class="badge badge-primary">' . $statusName . '</span>',
- default => '<span class="badge badge-secondary">' . $statusName . '</span>',
- };
- $rows[] = [
- $pet->id,
- $pet->name,
- $gradeName,
- $pet->level,
- $pet->experience,
- $pet->stamina,
- $statusBadge,
- $pet->created_at->format('Y-m-d H:i:s'),
- ];
- } catch (\Throwable) {
- // 如果出现异常,添加一个错误行
- $rows[] = [
- $pet->name ?? '未知',
- '数据错误',
- $pet->level ?? 0,
- $pet->experience ?? 0,
- $pet->stamina ?? 0,
- '<span class="badge badge-danger">数据错误</span>',
- $pet->created_at ? $pet->created_at->format('Y-m-d H:i:s') : '未知',
- ];
- }
- }
- $table = new Table($headers, $rows);
- // 统计信息
- $statsHtml = '<div class="row mb-3">';
- $statsHtml .= '<div class="col-md-3"><strong>宠物总数:</strong> ' . $totalPets . '</div>';
- $statsHtml .= '<div class="col-md-3"><strong>最高等级:</strong> ' . $levelStats['max_level'] . '</div>';
- $statsHtml .= '<div class="col-md-3"><strong>平均等级:</strong> ' . $levelStats['avg_level'] . '</div>';
- $statsHtml .= '<div class="col-md-3"><strong>总经验:</strong> ' . number_format($levelStats['total_exp']) . '</div>';
- $statsHtml .= '</div>';
- // 品阶统计
- if (!$gradeStats->isEmpty()) {
- $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>品阶分布:</strong> ';
- foreach ($gradeStats as $grade => $count) {
- $gradeName = $gradeNames[$grade] ?? "品阶{$grade}";
- $statsHtml .= "{$gradeName}: {$count}只 ";
- }
- $statsHtml .= '</div></div>';
- }
- // 状态统计
- if (!$statusStats->isEmpty()) {
- $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>状态分布:</strong> ';
- foreach ($statusStats as $status => $count) {
- $statusName = $statusNames[$status] ?? "状态{$status}";
- $statsHtml .= "{$statusName}: {$count}只 ";
- }
- $statsHtml .= '</div></div>';
- }
- $content = $statsHtml . $table->render();
- return new Card('宠物信息', $content);
- }
- /**
- * 宠物生活技能情况卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function petLifeSkillsCard($userId)
- {
- // 获取用户的宠物
- $pets = PetUser::where('user_id', $userId)->get();
- if ($pets->isEmpty()) {
- return new Card('宠物生活技能情况', new Alert('warning', '该用户没有宠物,无法使用生活技能'));
- }
- // 获取所有宠物的激活技能
- $petIds = $pets->pluck('id')->toArray();
- $activeSkills = PetActiveSkill::with(['pet', 'skill'])
- ->whereIn('pet_id', $petIds)
- ->orderBy('status', 'asc')
- ->orderBy('end_time', 'desc')
- ->get();
- // 统计信息
- $totalSkills = $activeSkills->count();
- $activeCount = $activeSkills->where('status', 'active')->count();
- $expiredCount = $activeSkills->where('status', 'expired')->count();
- $cancelledCount = $activeSkills->where('status', 'cancelled')->count();
- // 技能类型统计
- $skillTypeStats = $activeSkills->groupBy('skill_name')->map->count();
- // 创建统计信息
- $statsHtml = '<div class="row mb-3">';
- $statsHtml .= '<div class="col-md-3"><strong>总技能使用次数:</strong> ' . $totalSkills . '</div>';
- $statsHtml .= '<div class="col-md-3"><strong>当前激活:</strong> <span class="badge badge-success">' . $activeCount . '</span></div>';
- $statsHtml .= '<div class="col-md-3"><strong>已过期:</strong> <span class="badge badge-secondary">' . $expiredCount . '</span></div>';
- $statsHtml .= '<div class="col-md-3"><strong>已取消:</strong> <span class="badge badge-warning">' . $cancelledCount . '</span></div>';
- $statsHtml .= '</div>';
- // 技能类型统计
- if (!$skillTypeStats->isEmpty()) {
- $statsHtml .= '<div class="row mb-3"><div class="col-md-12"><strong>技能使用统计:</strong> ';
- foreach ($skillTypeStats as $skillName => $count) {
- $statsHtml .= "<span class='badge badge-info mr-2'>{$skillName}: {$count}次</span> ";
- }
- $statsHtml .= '</div></div>';
- }
- if ($activeSkills->isEmpty()) {
- $content = $statsHtml . '<div class="alert alert-info">该用户的宠物还没有使用过生活技能</div>';
- // 即使没有激活技能,也显示历史使用记录
- $content .= $this->getRecentSkillUsageSection($petIds);
- return new Card('宠物生活技能情况', $content);
- }
- // 创建技能详情表格
- $headers = ['宠物名称', '技能名称', '开始时间', '结束时间', '状态', '剩余时间', '技能配置'];
- $rows = [];
- foreach ($activeSkills as $activeSkill) {
- try {
- $petName = $activeSkill->pet ? $activeSkill->pet->name : "宠物{$activeSkill->pet_id}";
- $skillName = $activeSkill->skill_name;
- $startTime = $activeSkill->start_time->format('Y-m-d H:i:s');
- $endTime = $activeSkill->end_time->format('Y-m-d H:i:s');
- // 状态显示 - 实时检查过期状态
- $isReallyActive = $activeSkill->status === 'active' && $activeSkill->end_time > now();
- $statusBadge = match(true) {
- $activeSkill->status === 'cancelled' => '<span class="badge badge-warning">已取消</span>',
- $isReallyActive => '<span class="badge badge-success">生效中</span>',
- default => '<span class="badge badge-secondary">已过期</span>',
- };
- // 剩余时间 - 实时计算
- $remainingTime = '';
- if ($isReallyActive) {
- $remaining = $activeSkill->getRemainingSeconds();
- if ($remaining > 0) {
- $hours = floor($remaining / 3600);
- $minutes = floor(($remaining % 3600) / 60);
- $seconds = $remaining % 60;
- $remainingTime = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds);
- } else {
- $remainingTime = '<span class="text-danger">已过期</span>';
- }
- } else {
- $remainingTime = '-';
- }
- // 技能配置信息
- $configInfo = '';
- if (!empty($activeSkill->config)) {
- $config = $activeSkill->config;
- $configItems = [];
- // 显示关键配置信息
- if (isset($config['check_interval'])) {
- $configItems[] = "检查间隔: {$config['check_interval']}秒";
- }
- if (isset($config['preferred_seeds']) && !empty($config['preferred_seeds'])) {
- $seedCount = count($config['preferred_seeds']);
- $configItems[] = "优先种子: {$seedCount}种";
- }
- if (isset($config['protected_types']) && !empty($config['protected_types'])) {
- $protectedTypes = implode(', ', $config['protected_types']);
- $configItems[] = "防护类型: {$protectedTypes}";
- }
- if (isset($config['statistics']) && !empty($config['statistics'])) {
- $statsCount = count($config['statistics']);
- $configItems[] = "执行记录: {$statsCount}次";
- }
- $configInfo = implode('<br>', $configItems);
- }
- $rows[] = [
- $petName,
- $skillName,
- $startTime,
- $endTime,
- $statusBadge,
- $remainingTime,
- $configInfo ?: '-'
- ];
- } catch (\Throwable $e) {
- // 如果出现异常,添加一个错误行
- $rows[] = [
- $activeSkill->pet ? $activeSkill->pet->name : '未知',
- $activeSkill->skill_name ?? '未知',
- $activeSkill->start_time ? $activeSkill->start_time->format('Y-m-d H:i:s') : '未知',
- $activeSkill->end_time ? $activeSkill->end_time->format('Y-m-d H:i:s') : '未知',
- '<span class="badge badge-danger">数据错误</span>',
- '-',
- '数据解析错误'
- ];
- }
- }
- $table = new Table($headers, $rows);
- $content = $statsHtml . $table->render();
- // 添加最近10次技能使用记录
- $content .= $this->getRecentSkillUsageSection($petIds);
- // 添加说明信息
- $content .= '<div class="alert alert-info mt-3">';
- $content .= '<strong>说明:</strong><br>';
- $content .= '• <strong>自动收菜</strong>:每分钟自动收获成熟的作物<br>';
- $content .= '• <strong>自动播种</strong>:每分钟自动在空闲土地上播种<br>';
- $content .= '• <strong>灾害防护</strong>:每5分钟检查并自动清除作物灾害<br>';
- $content .= '• 所有自动操作仍需消耗相应的道具(种子、化肥等)';
- $content .= '</div>';
- return new Card('宠物生活技能情况', $content);
- }
- /**
- * 获取最近技能使用记录区域
- *
- * @param array $petIds 宠物ID数组
- * @return string HTML内容
- */
- protected function getRecentSkillUsageSection(array $petIds): string
- {
- if (empty($petIds)) {
- return '';
- }
- // 获取最近10次技能使用记录
- $recentLogs = PetSkillLog::with(['pet', 'skill'])
- ->whereIn('pet_id', $petIds)
- ->orderBy('used_at', 'desc')
- ->limit(10)
- ->get();
- if ($recentLogs->isEmpty()) {
- return '<div class="mt-4"><h5>最近技能使用记录</h5><div class="alert alert-info">暂无技能使用记录</div></div>';
- }
- // 创建技能使用记录表格
- $headers = ['使用时间', '宠物名称', '技能名称', '体力消耗', '技能效果', '状态'];
- $rows = [];
- foreach ($recentLogs as $log) {
- try {
- $petName = $log->pet ? $log->pet->name : "宠物{$log->pet_id}";
- $skillName = $log->skill ? $log->skill->skill_name : "技能{$log->skill_id}";
- // 安全处理时间格式
- $usedAt = '未知时间';
- if ($log->used_at) {
- try {
- if ($log->used_at instanceof \Carbon\Carbon) {
- $usedAt = $log->used_at->format('Y-m-d H:i:s');
- } else {
- // 尝试解析字符串时间
- $usedAt = date('Y-m-d H:i:s', strtotime($log->used_at));
- }
- } catch (\Exception $e) {
- $usedAt = (string) $log->used_at;
- }
- }
- $staminaCost = $log->skill ? $log->skill->stamina_cost : '-';
- // 解析技能效果结果
- $effectResult = '';
- $status = '<span class="badge badge-success">成功</span>';
- if (!empty($log->effect_result)) {
- try {
- $effectData = json_decode($log->effect_result, true);
- if (is_array($effectData)) {
- $effectItems = [];
- // 根据技能类型显示不同的效果信息
- if (isset($effectData['skill_type'])) {
- $effectItems[] = "类型: {$effectData['skill_type']}";
- }
- if (isset($effectData['duration'])) {
- $hours = round($effectData['duration'] / 3600, 1);
- $effectItems[] = "持续: {$hours}小时";
- }
- if (isset($effectData['end_time'])) {
- $endTime = date('H:i', strtotime($effectData['end_time']));
- $effectItems[] = "结束: {$endTime}";
- }
- if (isset($effectData['message'])) {
- $effectItems[] = $effectData['message'];
- }
- // 检查是否有错误
- if (isset($effectData['success']) && $effectData['success'] === false) {
- $status = '<span class="badge badge-danger">失败</span>';
- if (isset($effectData['message'])) {
- $effectItems = [$effectData['message']];
- }
- }
- $effectResult = implode('<br>', $effectItems);
- } else {
- $effectResult = $log->effect_result;
- }
- } catch (\Exception $e) {
- $effectResult = '数据解析错误';
- $status = '<span class="badge badge-warning">异常</span>';
- }
- } else {
- $effectResult = '无效果记录';
- }
- $rows[] = [
- $usedAt,
- $petName,
- $skillName,
- $staminaCost,
- $effectResult ?: '-',
- $status
- ];
- } catch (\Throwable $e) {
- // 如果出现异常,添加一个错误行
- $usedAtDisplay = '未知';
- if ($log->used_at) {
- try {
- if ($log->used_at instanceof \Carbon\Carbon) {
- $usedAtDisplay = $log->used_at->format('Y-m-d H:i:s');
- } else {
- $usedAtDisplay = (string) $log->used_at;
- }
- } catch (\Exception $e) {
- $usedAtDisplay = '时间格式错误';
- }
- }
- $rows[] = [
- $usedAtDisplay,
- $log->pet ? $log->pet->name : '未知',
- $log->skill ? $log->skill->skill_name : '未知',
- '-',
- '数据解析错误',
- '<span class="badge badge-danger">错误</span>'
- ];
- }
- }
- $table = new Table($headers, $rows);
- $html = '<div class="mt-4">';
- $html .= '<h5>最近技能使用记录 <small class="text-muted">(最近10次)</small></h5>';
- $html .= $table->render();
- $html .= '</div>';
- return $html;
- }
- /**
- * 神像buff信息卡片
- *
- * @param int $userId 用户ID
- * @return Card
- */
- protected function buffInfoCard($userId)
- {
- // 获取用户的神像buff信息
- $buffs = FarmGodBuff::where('user_id', $userId)
- ->orderBy('expire_time', 'desc')
- ->get();
- if ($buffs->isEmpty()) {
- return new Card('神像加持信息', new Alert('warning', '该用户没有神像加持信息'));
- }
- // 创建buff表格
- $headers = [ '加持类型', '过期时间', '状态' ];
- $rows = [];
- $buffTypeNames = [
- 1 => '丰收之神',
- 2 => '雨露之神',
- 3 => '屠草之神',
- 4 => '拭虫之神',
- ];
- foreach ($buffs as $buff) {
- try {
- $buffType = $buffTypeNames[$buff->buff_type] ?? "类型{$buff->buff_type}";
- $isActive = now()->lt($buff->expire_time);
- $status = $isActive ? '<span class="badge badge-success">生效中</span>' : '<span class="badge badge-secondary">已过期</span>';
- $rows[] = [
- $buffType,
- $buff->expire_time,
- $status,
- ];
- } catch (\Throwable) {
- // 如果出现异常,添加一个错误行
- $rows[] = [
- '数据错误',
- $buff->expire_time ?? '未知',
- '<span class="badge badge-danger">数据错误</span>',
- ];
- }
- }
- $table = new Table($headers, $rows);
- $content = $table->render();
- return new Card('神像加持信息', $content);
- }
- /**
- * 用户列表网格
- *
- * @return Grid
- */
- protected function grid()
- {
- return Grid::make(User::with([ 'info', 'farmUser' ]), function (Grid $grid) {
- $grid->column('id', 'ID')->sortable();
- // 用户基本信息
- $grid->column('username', '用户名');
- $grid->column('info.nickname', '昵称');
- // 农场信息
- $grid->column('farmUser.house_level', '房屋等级')->sortable();
- // 土地统计
- $grid->column('id', '土地统计')->display(function ($userId) {
- $lands = FarmLand::where('user_id', $userId)->get();
- if ($lands->isEmpty()) {
- return '<span class="text-muted">无土地</span>';
- }
- $landTypeStats = $lands->groupBy('land_type')->map->count();
- $totalLands = $lands->count();
- $html = "<div>总计: {$totalLands}块土地</div>";
- $landTypeNames = [
- 1 => '普通土地',
- 2 => '红土地',
- 3 => '黑土地',
- 4 => '金土地',
- 5 => '蓝土地',
- 6 => '紫土地',
- ];
- foreach ($landTypeStats as $typeId => $count) {
- $typeName = $landTypeNames[$typeId] ?? "类型{$typeId}";
- $html .= "<div>{$typeName}: {$count}块</div>";
- }
- return $html;
- });
- // 作物统计
- $grid->column('id', '作物统计')->display(function ($userId) {
- $crops = FarmCrop::where('user_id', $userId)->count();
- return $crops > 0 ? "{$crops}种作物" : '<span class="text-muted">无作物</span>';
- });
- // 物品统计
- $grid->column('id', '物品统计')->display(function ($userId) {
- $itemCount = ItemUser::where('user_id', $userId)->count();
- return $itemCount > 0 ? "{$itemCount}种物品" : '<span class="text-muted">无物品</span>';
- });
- // 代币统计
- $grid->column('id', '代币统计')->display(function ($userId) {
- $fundCount = FundModel::where('user_id', $userId)->count();
- return $fundCount > 0 ? "{$fundCount}个账户" : '<span class="text-muted">无账户</span>';
- });
- // 宠物统计
- $grid->column('id', '宠物统计')->display(function ($userId) {
- $pets = PetUser::where('user_id', $userId)->get();
- if ($pets->isEmpty()) {
- return '<span class="text-muted">无宠物</span>';
- }
- $totalPets = $pets->count();
- $maxLevel = $pets->max('level');
- $gradeStats = $pets->groupBy('grade')->map->count();
- $html = "<div>总计: {$totalPets}只宠物</div>";
- $html .= "<div>最高等级: {$maxLevel}</div>";
- // 显示品阶分布
- $gradeNames = [1 => '一品', 2 => '二品', 3 => '三品', 4 => '四品'];
- foreach ($gradeStats as $grade => $count) {
- $gradeName = $gradeNames[$grade] ?? "品阶{$grade}";
- $html .= "<div>{$gradeName}: {$count}只</div>";
- }
- return $html;
- });
- $grid->column('created_at', '创建时间')->sortable();
- // 添加查看详情操作
- $grid->actions(function (Grid\Displayers\Actions $actions) {
- // 禁用默认操作按钮
- $actions->disableDelete();
- $actions->disableEdit();
- $actions->disableQuickEdit();
- // 修改查看按钮,使其打开详情页
- $actions->append('<a href="' . admin_url('farm-user-summary/' . $actions->getKey()) . '" class="btn btn-sm btn-primary">查看详情</a>');
- });
- // 禁用创建按钮
- $grid->disableCreateButton();
- // 禁用批量操作
- $grid->disableBatchActions();
- // 添加搜索
- $grid->filter(function (Grid\Filter $filter) {
- $filter->equal('id', '用户ID');
- $filter->like('username', '用户名');
- $filter->like('info.nickname', '昵称');
- $filter->equal('farmUser.house_level', '房屋等级');
- });
- });
- }
- /**
- * 获取果实信息
- *
- * @param int $itemId 物品ID
- * @return string
- */
- protected function getFruitInfo($itemId)
- {
- try {
- // 查询物品信息
- $item = \App\Module\GameItems\Models\Item::find($itemId);
- if ($item) {
- return "<span class='text-success'><strong>{$item->name}</strong></span><br><small class='text-muted'>ID: {$itemId}</small>";
- } else {
- return "<span class='text-warning'>物品ID: {$itemId}</span><br><small class='text-muted'>物品不存在</small>";
- }
- } catch (\Exception $e) {
- return "<span class='text-danger'>获取失败</span><br><small class='text-muted'>ID: {$itemId}</small>";
- }
- }
- /**
- * 获取种子可能的产出信息
- *
- * @param \App\Module\Farm\Models\FarmSeed $seed 种子对象
- * @return string
- */
- protected function getSeedPossibleOutputs($seed)
- {
- try {
- $outputs = $seed->outputs;
- if ($outputs->isEmpty()) {
- return '<span class="text-muted">无产出配置</span>';
- }
- $outputInfo = [];
- foreach ($outputs as $output) {
- try {
- $item = \App\Module\GameItems\Models\Item::find($output->item_id);
- $itemName = $item ? $item->name : "物品{$output->item_id}";
- $probability = $output->probability ?? 0;
- $outputInfo[] = "<span class='text-info'>{$itemName}</span> <small class='text-muted'>({$probability}%)</small>";
- } catch (\Exception $e) {
- $outputInfo[] = "<span class='text-warning'>物品{$output->item_id}</span>";
- }
- }
- return '<small>可能产出:<br>' . implode('<br>', $outputInfo) . '</small>';
- } catch (\Exception $e) {
- return '<span class="text-muted">获取产出信息失败</span>';
- }
- }
- }
|