laravel-admin 按日期查询在不同数据库中根据日期选择数据表查询,适合大量数据
xuexi 2026-02-05 16:37:41 发表在:PHP 查看数:4

控制器中的筛选代码

<?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">&nbsp;&nbsp;视图</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
知识库:420条鸣谢:TAY  备案号:蜀ICP备2024090044号-1