laravel-admin 按日期查询在不同数据库中根据日期选择数据表查询,适合大量数据
控制器中的筛选代码
<?php
namespace App\Admin\Controllers\Aijieyun;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Show;
use DB;
use Encore\Admin\Admin;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Carbon\Carbon;
use App\Models\Aijieyun\AijieyunKehuziyuan;
use App\Models\Aijieyun\Aijieyuninsert;
use App\Models\Aijieyunuse;
use Encore\Admin\Layout\Content;
class ZhangdanController extends AdminController
{
/**
* Title for current resource.
*
* @var string
*/
protected $title = '账单信息';
/**
* Make a grid builder.
*
* @return Grid
*/
private $a; // 定义类属性
private $b;
private $c;
private $khname;
protected function grid()
{
$priceData = Aijieyuninsert::where('name', request('name'))->get();
$grid = new Grid(new Aijieyunuse);
$grid->model()->paginate(300);
//按钮
$curl = "/".env('ADMIN_ROUTE_PREFIX')."/aijieyun/zhangdanchats?kehu_id=".request('kehu_id');
$grid->tools(function (Grid\Tools $tools) use($curl){
$tools->append('<div class="btn-group grid-create-btn" style="margin-right:10px">
<a href="'.$curl.'" class="btn btn-sm btn-info" title="视图">
<i class="fa fa-bar-chart"></i><span class="hidden-xs"> 视图</span>
</a>
</div>');
});
// 添加分页选项
$grid->perPages([10, 20, 50, 100, 1000]);
$total = 0;
// 2. 定义闭包外部的引用变量(关键:用引用传递)
$sum = &$total;
//筛选
$grid->filter(function(Grid\Filter $filter){
//连接外部数据库查询
$filter->where(function ($query) {
// 1. 获取手动输入的日期范围(格式:2026-01-01/2026-01-10)
$dateInput = trim($this->input ?? '');
// 未输入内容,返回空结果(需显示全部则注释此行)
if (empty($dateInput)) {
$query->whereRaw('1=0');
return;
}
try {
// 2. 按/分割开始/结束日期,做基础格式校验
$dateArr = explode('/', $dateInput);
// 校验:必须是/分隔的2个日期值
if (count($dateArr) !== 2) {
admin_toastr("日期输入格式错误!请按【2026-01-01/2026-01-10】格式输入", 'error');
$query->whereRaw('1=0');
return;
}
// 提取并清理开始/结束日期
$startDateStr = trim($dateArr[0]);
$endDateStr = trim($dateArr[1]);
// 校验:开始/结束日期均不能为空
if (empty($startDateStr) || empty($endDateStr)) {
admin_toastr("开始/结束日期均不能为空!请按【2026-01-01/2026-01-10】格式输入", 'error');
$query->whereRaw('1=0');
return;
}
// 3. 解析为Carbon实例,校验日期合法性(如2026-02-30这种无效日期)
$startDate = Carbon::parse($startDateStr);
$endDate = Carbon::parse($endDateStr);
// 校验:结束日期不能早于开始日期
if ($endDate->isBefore($startDate)) {
admin_toastr("结束日期不能早于开始日期!", 'error');
$query->whereRaw('1=0');
return;
}
// 关键校验:因表是按月分表(aijieyun202601),禁止跨月查询
if ($startDate->format('Ym') !== $endDate->format('Ym')) {
admin_toastr("暂不支持跨月查询!请选择同一个月的开始/结束日期", 'error');
$query->whereRaw('1=0');
return;
}
// 4. 基于日期的所属月份,拼接动态表名(和原逻辑一致,保证分表匹配)
$tableSuffix = $startDate->format('Ym'); // 202601
$dynamicTable = 'aijieyun' . $tableSuffix;
Storage::put('e.txt', $dynamicTable);
// 5. 检查跨库的动态表是否存在(原逻辑保留)
$secondDb = DB::connection('aijieyunuse');
$tableExists = $secondDb->getSchemaBuilder()->hasTable($dynamicTable);
if (!$tableExists) {
admin_toastr("该日期所属月份的表【{$dynamicTable}】不存在!", 'error');
$query->whereRaw('1=0');
return;
}
// 6. 跨库动态表查询 + 核心:叠加chargeStartTime的时间范围筛选
$secondDbQuery = $secondDb->table($dynamicTable);
// 补全结束日期的时间为23:59:59,避免只查到结束日期0点前的数据
$endDateFull = $endDate->format('Y-m-d 23:59:59');
// 按chargeStartTime做时间范围查询(核心业务逻辑)
$secondDbQuery->whereBetween('chargeStartTime', [$startDateStr, $endDateFull]);
// 【可选】叠加其他业务过滤条件
// $secondDbQuery->where('status', 1);
// 7. 替换原查询为跨库动态表查询(原修复逻辑保留,无getQuery())
$query->setQuery($secondDbQuery);
$query->from($dynamicTable);
$query->getQuery()->connection = $secondDb;
} catch (\Exception $e) {
// 捕获所有异常:格式错误、日期非法、数据库异常等
admin_toastr("日期查询异常:{$e->getMessage()},请按【2026-01-01/2026-01-10】格式输入", 'error');
$query->whereRaw('1=0');
}
}, '大数据方案需要手动输入日期范围如:2026-01-01/2026-01-10'); // 筛选器显示名,匹配新的查询方式
$filter->like('zylx', __('资源类型'));
$filter->like('zymc', __('资源名称'));
// $filter->date('riqi', __('日期')); // 修正日期筛选器顺序
$filter->between('chargeStartTime', '创建时间')->datetime();
});
// 筛选结束
if( request('name')){
// 优化查询构建方式,避免直接设置子查询
$grid->model()->where('customName', request('name'))->orderBy('chargeStartTime', 'desc');
$kehu = Aijieyunuse::where('customName', request('name') )->first();
$this->khname = $kehu ? $kehu->kehuname : '未知客户';
} else {
$grid->model()->orderBy('id', 'desc');
}
$grid->column('id', __('ID'))->sortable();
$grid->column('chargeStartTime', __('日期'));
$grid->column('customName', __('客户名称'))->display(function(){
$this->customName = $this->customName;
$data['id'] =$this->id;
return $this->customName;
});
$grid->column('settleValue', __('计费量'));
$grid->column('offerName', __('资源类型'));
$grid->column('chargeName', __('资源名称'));
//$grid->column('b', __('总资源量'));
// $grid->column('chargeStartTime', __('总资源时'))->display(function(){
// return number_format($this->chargeStartTime, 2);
// });
// $grid->column('chargeStartTime', __('使用资源时'))->display(function(){
// $shiyongshi = number_format($this->chargeStartTime,2);
// return $shiyongshi;
// });
$grid->column('rateCycleName', __('单位'))->display(function(){
// 合并为一次JOIN查询,同时处理可能的空值
// $dw = DB::table('cb_aijieyun_chanpin_ziyuan as ziyuan')
// ->join('cb_aijieyun_insert as insert', 'ziyuan.id', '=', 'insert.ziyuan_id')
// ->where('ziyuan.name', $this->zymc)
// ->select('insert.danwei')
// ->first();
// $this->danwei=$dw?->danwei ?? 0;
return $this->rateCycleName; // 若不存在匹配数据,返回默认值(0或其他业务需要的值)
});
$grid->column('dj', __('单价'))->display(function() use ($priceData){
$dj = DB::table('cb_aijieyun_chanpin as chanpin')
->join('cb_aijieyun_insert as insert', 'chanpin.id', '=', 'insert.chanpin_id')
->where('chanpin.name', $this->offerName)
->select('insert.dj')
->first();
$this->a = $dj?->dj ?? '未获取到单价';
return $this->a; // 给类属性赋值(注意这里的 $this 是当前控制器实例)
// 计算小计
});
$grid->column('xiaoji', __('小计金额'))->display(function() use (&$sum) {
// 计算当前行小计
$subtotal = (float)$this->a * (float)$this->settleValue;
// 累加当前行的值到总和(使用引用变量)
$sum += $subtotal;
/*写入本地文件*/
// 生成唯一文件名(避免冲突)
$fileName = '客户名称/' . request('name') . '.json';
// 要存储的数据
$newData = [
'name' => request('name'),
'chargeStartTime' => $this->chargeStartTime,
'chanpin_id' => $this->zylx,
'zymc' => $this->zymc,
'zzys' => $this->zzys,
'syzys' => $this->syzys,
'jifei_danwei' => $this->danwei ?? 0,
'danjia' => $this->a ?? 0,
'feiyong_xiaoji' => $subtotal,
//'details' => json_encode(['product_id' => 123, 'price' => 99.99])
];
// 写入文件(存储到 storage/app目录)
// 1. 读取已有数据(如果文件存在)
$existingData = [];
if (Storage::exists($fileName)) {
// 读取文件内容并解析为数组
$fileContent = Storage::get($fileName);
$existingData = json_decode($fileContent, true);
// 验证JSON格式是否正确(防止文件损坏)
if (json_last_error() !== JSON_ERROR_NONE) {
$existingData = []; // 格式错误则重置为空数组
}
}
// 2. 确保原有数据是数组(避免非数组格式导致追加失败)
if (!is_array($existingData)) {
$existingData = [];
}
// 3. 将新数据追加到数组中
$existingData[] = $newData;
// 4. 重新写入文件(覆盖原有内容)
Storage::put($fileName, json_encode($existingData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
/*写入本地文件结束*/
return number_format($subtotal, 3);
});
// 方案一:添加底部合计行
$grid->footer(function ($query) use (&$total) {
$totalAmount = $query->sum('settleValue');
// 重新计算总费用,避免直接对显示值求和
$totalQuantity=bcmul(1, $total, 3); // 精确小数计算
session(['fyxj' => number_format($totalQuantity, 3)]);
return '<tr class="table-footer-summary">
<td colspan="5" style="padding:0;">
<div style="display:flex; justify-content:flex-end; background-color:#f8f9fa; border-top:2px solid #dee2e6; padding:12px 15px;">
<div style="font-weight:bold; margin-right:20px;">总计:</div>
<div style="font-weight:bold; color:#2c3e50; margin-right:30px;">
当页总使用资源时:<span class="summary-value">'. number_format($totalAmount, 3) .'</span>
</div>
<div style="font-weight:bold; color:#e74c3c;">
当页总费用:<span class="summary-value">¥'. number_format($totalQuantity, 3) .'</span>
</div>
</div>
</td>
</tr>';
});
//方案二
// 统计数据
$totalOrders = Aijieyunuse::where('customName', request('name'))->count(); //订单总数
$totalAmount = Aijieyunuse::sum('settleValue'); //使用资源时
$avgAmount = session('fyxj'); //费用小计
$zzyl = Aijieyunuse::sum('settleValue'); //使用资源时
// 在表格上方添加自定义视图
$grid->tools(function ($tools) use ($totalOrders, $totalAmount, $avgAmount,$zzyl) {
$tools->append(view('admin.tongji.zhangdan', compact('totalOrders', 'totalAmount', 'avgAmount','zzyl')));
});
$grid->export(function ($export) {
// 设置导出的文件名
$export->filename($this->khname.'-费用账单-导出时间' . date('YmdHis'));
// 可以在这里添加其他导出配置
});
return $grid;
}
protected function detail($id)
{
$show = new Show(Aijieyunchanpin::findOrFail($id));
$show->field('id', __('ID'));
$show->field('created_at', __('Created at'));
$show->field('updated_at', __('Updated at'));
return $show;
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
$form = new Form(new Aijieyunchanpin);
$form->display('id', __('ID'));
// $form->date('name', __('客户名称'));
$form->text('name', __('产品名称'));
$form->display('created_at', __('Created At'));
$form->display('updated_at', __('Updated At'));
return $form;
}
/**
* 重写列表页渲染(可选,解决筛选后面包屑/标题显示当前月份)
*/
public function index(Content $content)
{
$month = request()->input('filter.month', 1);
// 动态修改页面标题,显示当前查询的月份
$this->title = "账单数据";
return parent::index($content);
}
}
模型
<?php
//第二个数据库
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon; // 必须引入Carbon,否则报类不存在
class Aijieyunuse extends Model
{
// 可选:指定数据库连接(如果用第二个数据库)
protected $connection = 'aijieyunuse';
// 可选:指定表名(如果表名和模型名复数不一致,比如表名是aijieyunkehu)
protected $table = 'aijieyun202601';
// 可选:指定主键(如果不是id)
protected $primaryKey = 'id';
// 时间戳:分表有created_at/updated_at则为true,无则改为false
public $timestamps = false;
// 批量赋值:补充你的实际业务字段(非必须,仅作兼容)
protected $fillable = ['id', 'created_at', 'updated_at'];
}
最近访问时间:2026-02-05 20:14:49