|
|
<?php
|
|
|
|
|
|
namespace app\admin\controller;
|
|
|
|
|
|
use app\common\controller\Backend;
|
|
|
use addons\fastexport\library\ExportLib;
|
|
|
use think\Db;
|
|
|
use think\Exception;
|
|
|
use think\exception\PDOException;
|
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
|
|
|
|
|
/**
|
|
|
* 数据导出管理
|
|
|
*
|
|
|
* @icon fa fa-circle-o
|
|
|
*/
|
|
|
class Fastexport extends Backend
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
* FastExport模型对象
|
|
|
* @var \app\admin\model\FastExport
|
|
|
*/
|
|
|
protected $model = null;
|
|
|
protected $database = [];
|
|
|
public $save_dir = RUNTIME_PATH . 'fastexport' . DS;// 临时文件保存位置(每次开始任务时清空)
|
|
|
public $fastExportDir;// ZIP包保存位置(重新初始化/删除任务时删除ZIP包)
|
|
|
|
|
|
public function _initialize()
|
|
|
{
|
|
|
parent::_initialize();
|
|
|
$rootPath = str_replace('/', DS, ROOT_PATH);
|
|
|
$this->model = new \app\admin\model\FastExport;
|
|
|
$this->database = config('database');
|
|
|
$this->fastExportDir = $rootPath . 'public' . DS . 'fastexport';
|
|
|
|
|
|
$tableList = [];
|
|
|
$list = Db::query("SELECT TABLE_NAME,TABLE_COMMENT FROM information_schema.TABLES WHERE table_schema = ? ", [$this->database['database']]);
|
|
|
|
|
|
// 需要排除的数据表
|
|
|
$rule_out_table = [
|
|
|
$this->database['prefix'] . 'config',
|
|
|
$this->database['prefix'] . 'version',
|
|
|
// $this->database['prefix'] . 'admin',
|
|
|
$this->database['prefix'] . 'user_token',
|
|
|
$this->database['prefix'] . 'fast_auth_group_access',
|
|
|
];
|
|
|
|
|
|
foreach ($list as $key => $row) {
|
|
|
if (!in_array($row['TABLE_NAME'], $rule_out_table)) {
|
|
|
$tableList[] = [
|
|
|
'name' => $row['TABLE_NAME'],
|
|
|
'comment' => $row['TABLE_COMMENT'] ? str_replace('表', '', $row['TABLE_COMMENT']) : '',
|
|
|
];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$this->view->assign("tableList", $tableList);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 删除
|
|
|
*/
|
|
|
public function del($ids = "")
|
|
|
{
|
|
|
if ($ids) {
|
|
|
$pk = $this->model->getPk();
|
|
|
$adminIds = $this->getDataLimitAdminIds();
|
|
|
if (is_array($adminIds)) {
|
|
|
$this->model->where($this->dataLimitField, 'in', $adminIds);
|
|
|
}
|
|
|
$list = $this->model->where($pk, 'in', $ids)->select();
|
|
|
|
|
|
$count = 0;
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
foreach ($list as $k => $v) {
|
|
|
|
|
|
// 同时删除临时文件和zip包
|
|
|
if (is_dir($this->save_dir . $v->id)) {
|
|
|
$this->emptydir($this->save_dir . $v->id, true);
|
|
|
}
|
|
|
|
|
|
if ($v->file) {
|
|
|
$file_name = explode(DS, str_replace('/', DS, $v->file));
|
|
|
if (file_exists($this->fastExportDir . DS . end($file_name))) {
|
|
|
unlink($this->fastExportDir . DS . end($file_name));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$count += $v->delete();
|
|
|
}
|
|
|
|
|
|
Db::commit();
|
|
|
} catch (PDOException $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (Exception $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
}
|
|
|
if ($count) {
|
|
|
$this->success();
|
|
|
} else {
|
|
|
$this->error(__('No rows were deleted'));
|
|
|
}
|
|
|
}
|
|
|
$this->error(__('Parameter %s can not be empty', 'ids'));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 测试任务-导出前10条数据
|
|
|
* @param int $ids [description]
|
|
|
*/
|
|
|
public function testTask($ids = null)
|
|
|
{
|
|
|
$row = $this->model->get($ids);
|
|
|
if (!$row) {
|
|
|
$this->error(__('No Results were found'));
|
|
|
}
|
|
|
|
|
|
$ExportLib = new ExportLib($row);
|
|
|
|
|
|
try {
|
|
|
|
|
|
$export_number = $row['export_number'] ? $row['export_number'] : $ExportLib->buildSql('count');
|
|
|
$xls_max_number = ($export_number >= $row['xls_max_number']) ? $row['xls_max_number'] : $export_number;
|
|
|
if (!$export_number) {
|
|
|
$this->error('没有数据需要导出~');
|
|
|
}
|
|
|
|
|
|
$min = 0;
|
|
|
$max = 10;
|
|
|
|
|
|
$subtask = [
|
|
|
1 => [
|
|
|
'id' => 1,
|
|
|
'status' => 0,// 0准备好,1进行中,2完成,3失败
|
|
|
'min' => $min,
|
|
|
'max' => $max,
|
|
|
'sql' => $ExportLib->buildSql('limit', [$min, $max])
|
|
|
]
|
|
|
];
|
|
|
|
|
|
// 测试sql
|
|
|
Db::query($subtask[1]['sql']);
|
|
|
|
|
|
$row->subtask = $subtask;
|
|
|
$row->save();
|
|
|
|
|
|
} catch (PDOException $e) {
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (Exception $e) {
|
|
|
$this->error($e->getMessage());
|
|
|
}
|
|
|
|
|
|
$this->success('导出任务测试准备好~', null, [
|
|
|
'direct_export' => true,
|
|
|
'subtask' => $subtask,
|
|
|
'id' => $ids
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 开始任务->初始化任务配置
|
|
|
* @return bool
|
|
|
*/
|
|
|
public function performTask($ids = null)
|
|
|
{
|
|
|
$row = $this->model->get($ids);
|
|
|
if (!$row) {
|
|
|
$this->error(__('No Results were found'));
|
|
|
}
|
|
|
|
|
|
$ExportLib = new ExportLib($row);
|
|
|
|
|
|
try {
|
|
|
|
|
|
$export_number = $row['export_number'] ? $row['export_number'] : $ExportLib->buildSql('count');
|
|
|
$xls_max_number = ($export_number >= $row['xls_max_number']) ? $row['xls_max_number'] : $export_number;
|
|
|
if (!$export_number) {
|
|
|
$this->error('没有数据需要导出~');
|
|
|
}
|
|
|
|
|
|
$subtask = [];
|
|
|
$subtask_count = ceil($export_number / $xls_max_number);// 子任务数量
|
|
|
for ($i = 1; $i <= $subtask_count; $i++) {
|
|
|
|
|
|
if ($i == 1) {
|
|
|
$min = 0;
|
|
|
} else {
|
|
|
$min = ($i - 1) * $xls_max_number;
|
|
|
}
|
|
|
|
|
|
$subtask[$i] = [
|
|
|
'id' => $i,
|
|
|
'status' => 0,// 0准备好,1进行中,2完成,3失败
|
|
|
'min' => $min,
|
|
|
'max' => $xls_max_number,
|
|
|
'sql' => $ExportLib->buildSql('limit', [$min, $xls_max_number])
|
|
|
];
|
|
|
|
|
|
// 测试sql
|
|
|
if ($i == 1) {
|
|
|
Db::query($subtask[$i]['sql']);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ($subtask_count > 1) {
|
|
|
// 清理任务临时文件目录
|
|
|
if (!is_dir($this->save_dir . $ids)) {
|
|
|
mkdir($this->save_dir . $ids, 0777, true);
|
|
|
} else {
|
|
|
$this->emptydir($this->save_dir . $ids);
|
|
|
}
|
|
|
|
|
|
$row->progress = 5;
|
|
|
} else {
|
|
|
$row->progress = 0;
|
|
|
}
|
|
|
|
|
|
// 删除上次任务的zip包
|
|
|
if ($row->file) {
|
|
|
|
|
|
if ($row->file) {
|
|
|
$file_name = explode(DS, str_replace('/', DS, $row->file));
|
|
|
if (file_exists($this->fastExportDir . DS . end($file_name))) {
|
|
|
unlink($this->fastExportDir . DS . end($file_name));
|
|
|
}
|
|
|
}
|
|
|
$row->file = '';
|
|
|
}
|
|
|
|
|
|
$row->subtask = $subtask;
|
|
|
$row->save();
|
|
|
|
|
|
} catch (PDOException $e) {
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (Exception $e) {
|
|
|
$this->error($e->getMessage());
|
|
|
}
|
|
|
|
|
|
$this->success('导出任务初始化成功!', null, [
|
|
|
'direct_export' => ($subtask_count == 1) ? true : false,
|
|
|
'subtask' => $subtask,
|
|
|
'id' => $ids
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 子任务控制器
|
|
|
* @param [type] $task_id 任务ID
|
|
|
*/
|
|
|
public function taskControl($task_id = null)
|
|
|
{
|
|
|
$row = $this->model->get($task_id);
|
|
|
if (!$row) {
|
|
|
$this->error(__('任务找不到啦~'));
|
|
|
}
|
|
|
|
|
|
if (!isset($row['subtask']) || !is_array($row['subtask'])) {
|
|
|
$this->error('子任务找不到啦,请重新开始任务~');
|
|
|
}
|
|
|
$row = $row->toArray();
|
|
|
|
|
|
// 子任务状态:0准备好,1进行中,2完成,3失败
|
|
|
foreach ($row['subtask'] as $key => $value) {
|
|
|
switch ($value['status']) {
|
|
|
case '0':
|
|
|
$row['subtask'][$key]['status_text'] = '等待中';
|
|
|
break;
|
|
|
case '1':
|
|
|
$row['subtask'][$key]['status_text'] = '进行中';
|
|
|
break;
|
|
|
case '2':
|
|
|
$row['subtask'][$key]['status_text'] = '已完成';
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
$row['subtask'][$key]['status_text'] = '失败';
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$current_page = 1;
|
|
|
$subtask_page = [];
|
|
|
foreach ($row['subtask'] as $key => $value) {
|
|
|
$value['status'] = 0;// 用户可能会刷新任务控制页面,js将重新确定状态
|
|
|
$subtask_page[$current_page][] = $value;
|
|
|
if (count($subtask_page[$current_page]) >= $row['xls_create_concurrent']) {
|
|
|
$current_page++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$subtask_count = count($row['subtask']);
|
|
|
$row['item_subtask_progress'] = round(92 / $subtask_count, 2);
|
|
|
$row['progress'] = 5;
|
|
|
|
|
|
$this->assignconfig('subtask_page', $subtask_page);
|
|
|
$this->assignconfig('task', $row);
|
|
|
$this->view->assign("task", $row);
|
|
|
return $this->view->fetch();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 执行子任务
|
|
|
* @param int $task_id 任务ID
|
|
|
* @param int $subtask_id 子任务ID
|
|
|
* @param boolean $direct_export 是否直接反回文件
|
|
|
*/
|
|
|
public function performSubTask($task_id, $subtask_id, $direct_export = false)
|
|
|
{
|
|
|
\think\Config::set('default_return_type', 'json');
|
|
|
$row = $this->model->get($task_id);
|
|
|
if (!$row) {
|
|
|
$this->error(__('No Results were found'));
|
|
|
}
|
|
|
|
|
|
if (isset($row['subtask'][$subtask_id]) && is_array($row['subtask'][$subtask_id])) {
|
|
|
$subtask = $row['subtask'][$subtask_id];
|
|
|
$task_name = $row['name'];
|
|
|
} else {
|
|
|
$this->result(['subtask_id' => $subtask_id], 0, '子任务找不到啦~', 'json');
|
|
|
}
|
|
|
|
|
|
set_time_limit(0);// 脚本执行时间限制
|
|
|
ini_set('memory_limit', $row['memory_limit'] . 'M');// 脚本内存限制
|
|
|
$ExportLib = new ExportLib($row);
|
|
|
unset($row);
|
|
|
|
|
|
// 检查任务状态
|
|
|
if (!$direct_export) {
|
|
|
if ($subtask['status'] == 1) {
|
|
|
$this->result(['subtask_id' => $subtask_id], 0, '此子任务正在执行中~', 'json');
|
|
|
} else if ($subtask['status'] == 2) {
|
|
|
if (file_exists($this->save_dir . $task_id . DS . $subtask_id . '.xlsx')) {
|
|
|
$this->result(['subtask_id' => $subtask_id], 1, '此子任务已经处理过啦~', 'json');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$spreadsheet = new Spreadsheet();
|
|
|
$worksheet = $spreadsheet->getActiveSheet();
|
|
|
$worksheet->setTitle($task_name);
|
|
|
|
|
|
//设置表头
|
|
|
$head = $ExportLib->getXlsTitle();
|
|
|
$head_count = count($head);
|
|
|
for ($i = 0; $i < $head_count; $i++) {
|
|
|
$worksheet->setCellValueByColumnAndRow($i + 1, 1, $head[$i]);
|
|
|
}
|
|
|
|
|
|
// 写人数据
|
|
|
try {
|
|
|
$data = Db::query($subtask['sql']);
|
|
|
} catch (PDOException $e) {
|
|
|
$this->result([
|
|
|
'subtask_id' => $subtask_id,
|
|
|
'error_msg' => $e->getMessage(),
|
|
|
], 0, '任务失败!', 'json');
|
|
|
}
|
|
|
|
|
|
$y = 2;
|
|
|
foreach ($data as $row_key => $row) {
|
|
|
$i = 1;
|
|
|
foreach ($ExportLib->fields as $key => $value) {
|
|
|
|
|
|
$field = $value['field'];
|
|
|
|
|
|
if ($value['discerns'] == 0) {
|
|
|
// 文本
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field], DataType::TYPE_STRING);
|
|
|
} else if ($value['discerns'] == 1) {
|
|
|
// 数字
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field] . "\t");
|
|
|
} else if ($value['discerns'] == 2) {
|
|
|
// 日期时间
|
|
|
if ($row[$field]) {
|
|
|
|
|
|
$excelDateValue = date('Y-m-d H:i:s', $row[$field]);
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $excelDateValue, DataType::TYPE_STRING);
|
|
|
|
|
|
/*$excelDateValue = PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel( $row[$field] );
|
|
|
if ($excelDateValue) {
|
|
|
// 以日期日期格式写入,没区别,有需要的可以打开
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $excelDateValue);
|
|
|
$worksheet->getStyleByColumnAndRow($i,$y)
|
|
|
->getNumberFormat()
|
|
|
->setFormatCode('yyyy-mm-dd h:mm:ss');
|
|
|
$worksheet->getStyleByColumnAndRow($i,$y)->getFont()->setBold(true);
|
|
|
$worksheet->getStyleByColumnAndRow($i,$y)->getFont()->setBold(false);
|
|
|
}*/
|
|
|
} else {
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, '-', DataType::TYPE_STRING);
|
|
|
}
|
|
|
} else if ($value['discerns'] == 3) {
|
|
|
// 图片-太慢,太耗资源,弃用
|
|
|
/*$file_info = pathinfo($row[$field]);
|
|
|
$coorrow = $worksheet->getCellByColumnAndRow($i, $y)->getRow();
|
|
|
$coorcolumn = $worksheet->getCellByColumnAndRow($i, $y)->getColumn();
|
|
|
|
|
|
if ($y == 2) {
|
|
|
$worksheet->getColumnDimension($coorcolumn)->setWidth(8);
|
|
|
}
|
|
|
|
|
|
$worksheet->getRowDimension($coorrow)->setRowHeight(40);
|
|
|
|
|
|
if (!empty($file_info['basename'])) {
|
|
|
$basename = $file_info['basename'];
|
|
|
$drawing = new Drawing();
|
|
|
$drawing->setName('图片');
|
|
|
$drawing->setPath($remote_save_dir . $row[$field]);
|
|
|
$drawing->setWidth(40);
|
|
|
$drawing->setHeight(40);
|
|
|
$drawing->setCoordinates($coorcolumn . $coorrow);
|
|
|
$drawing->setWorksheet($worksheet);
|
|
|
} else {
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $row[$field], DataType::TYPE_STRING);
|
|
|
}*/
|
|
|
|
|
|
// 以链接导出图片
|
|
|
$field_value = cdnurl($row[$field], true);
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
|
|
|
$worksheet->getCellByColumnAndRow($i, $y)->getHyperlink()->setUrl($field_value);
|
|
|
|
|
|
} else if ($value['discerns'] == 4) {
|
|
|
// 文件
|
|
|
$field_value = cdnurl($row[$field], true);
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
|
|
|
$worksheet->getCellByColumnAndRow($i, $y)->getHyperlink()->setUrl($field_value);
|
|
|
} else if ($value['discerns'] == 5) {
|
|
|
// 赋值
|
|
|
$field_value = $ExportLib->assignment($row[$field], $value['scheme']);
|
|
|
$worksheet->setCellValueByColumnAndRow($i, $y, $field_value, DataType::TYPE_STRING);
|
|
|
}
|
|
|
|
|
|
$i++;
|
|
|
}
|
|
|
$y++;
|
|
|
unset($data[$row_key]); // 能节约一点内存
|
|
|
}
|
|
|
|
|
|
// xls文件处理
|
|
|
if ($direct_export) {
|
|
|
// 直接下载
|
|
|
ob_end_clean();
|
|
|
header("Pragma: public");
|
|
|
header("Expires: 0");
|
|
|
header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
|
|
|
header("Content-Type:application/force-download");
|
|
|
header("Content-Type:application/vnd.ms-execl");
|
|
|
header("Content-Type:application/octet-stream");
|
|
|
header("Content-Type:application/download");
|
|
|
|
|
|
$task_name = $task_id . '.' . $task_name . '.xlsx';
|
|
|
$encoded_filename = urlencode($task_name);
|
|
|
$ua = $_SERVER["HTTP_USER_AGENT"];
|
|
|
if (preg_match("/MSIE/", $ua)) {
|
|
|
header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
|
|
|
} else if (preg_match("/Firefox/", $ua)) {
|
|
|
header('Content-Disposition: attachment; filename*="utf8\'\'' . $task_name . '"');
|
|
|
} else {
|
|
|
header('Content-Disposition: attachment; filename="' . $task_name . '"');
|
|
|
}
|
|
|
header("Content-Transfer-Encoding:binary");
|
|
|
header('Cache-Control: max-age=0');// 禁止缓存
|
|
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
|
$writer->save('php://output');
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
|
unset($spreadsheet);
|
|
|
} else {
|
|
|
// 保存
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
|
$writer->save($this->save_dir . $task_id . DS . $subtask_id . '.xlsx');
|
|
|
|
|
|
// 设置任务状态
|
|
|
$result = false;
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
// 读取最新的 subtask 数据
|
|
|
$row = $this->model->get($task_id);
|
|
|
$progress = $row->progress + round(92 / count($row['subtask']), 2);
|
|
|
$progress = ($progress > 100) ? 100 : $progress;
|
|
|
|
|
|
if (isset($row['subtask'][$subtask_id]) && is_array($row['subtask'][$subtask_id])) {
|
|
|
$subtask = $row['subtask'];
|
|
|
$subtask[$subtask_id]['status'] = 2;
|
|
|
$row->subtask = $subtask;
|
|
|
$row->progress = $progress;
|
|
|
$row->save();
|
|
|
$result = true;
|
|
|
}
|
|
|
Db::commit();
|
|
|
} catch (PDOException $e) {
|
|
|
Db::rollback();
|
|
|
|
|
|
$this->result([
|
|
|
'subtask_id' => $subtask_id,
|
|
|
'error_msg' => $e->getMessage(),
|
|
|
], 0, '任务失败!', 'json');
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
Db::rollback();
|
|
|
|
|
|
$this->result([
|
|
|
'subtask_id' => $subtask_id,
|
|
|
'error_msg' => $e->getMessage(),
|
|
|
], 0, '任务失败!', 'json');
|
|
|
}
|
|
|
|
|
|
$spreadsheet->disconnectWorksheets();
|
|
|
unset($spreadsheet);
|
|
|
|
|
|
if ($result) {
|
|
|
$this->result(['subtask_id' => $subtask_id], 1, '子任务处理成功!', 'json');
|
|
|
} else {
|
|
|
$this->result(['subtask_id' => $subtask_id], 0, '子任务找不到啦~', 'json');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 打包ZIP
|
|
|
*/
|
|
|
public function taskFilePack($task_id)
|
|
|
{
|
|
|
$row = $this->model->get($task_id);
|
|
|
if (!$row) {
|
|
|
$this->error(__('No Results were found'));
|
|
|
}
|
|
|
|
|
|
if (!isset($row['subtask']) || !is_array($row['subtask'])) {
|
|
|
$this->result(null, 0, '打包失败,任务找不到啦~', 'json');
|
|
|
}
|
|
|
|
|
|
if ($row['file']) {
|
|
|
$this->result(['file_name' => $row['file']], 1, '此任务已经打过包了!', 'json');
|
|
|
}
|
|
|
|
|
|
is_dir($this->fastExportDir) OR mkdir($this->fastExportDir, 0755, true);
|
|
|
|
|
|
foreach ($row['subtask'] as $key => $subtask) {
|
|
|
if (!file_exists($this->save_dir . $task_id . DS . $subtask['id'] . '.xlsx')) {
|
|
|
$this->result(null, 0, '子任务未处理完毕!', 'json');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$task_dir = $this->save_dir . $task_id . DS;// 导出任务的临时文件目录
|
|
|
$zipname = $task_id . '.export_' . \fast\Random::alnum(6) . '.zip';
|
|
|
$zipurl = request()->domain() . DS . 'fastexport' . DS . $zipname;// 绝对地址,以便各处直接点击下载
|
|
|
$zipname = $this->fastExportDir . DS . $zipname;
|
|
|
|
|
|
$zip = new \ZipArchive();
|
|
|
$res = $zip->open($zipname, \ZipArchive::CREATE);
|
|
|
|
|
|
if ($res === TRUE) {
|
|
|
$dh = opendir($task_dir);
|
|
|
while ($file = readdir($dh)) {
|
|
|
if ($file != "." && $file != "..") {
|
|
|
$fullpath = $task_dir . $file;
|
|
|
if (!is_dir($fullpath)) {
|
|
|
$zip->addFile($fullpath, $file);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
closedir($dh);
|
|
|
}
|
|
|
|
|
|
$zip->close();
|
|
|
$row->file = $zipurl;
|
|
|
$row->progress = 100;
|
|
|
$row->save();
|
|
|
|
|
|
$this->result(['file_name' => $zipurl], 1, '文件打包完成!', 'json');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取字段列表、字段注释、表主键
|
|
|
* @internal
|
|
|
*/
|
|
|
public function getTableFields($table = null)
|
|
|
{
|
|
|
if ($this->request->isAjax()) {
|
|
|
$table = $this->request->request('table');
|
|
|
}
|
|
|
|
|
|
//从数据库中获取表字段信息
|
|
|
$sql = "SELECT * FROM `information_schema`.`columns` "
|
|
|
. "WHERE TABLE_SCHEMA = ? AND table_name = ? "
|
|
|
. "ORDER BY ORDINAL_POSITION";
|
|
|
|
|
|
//加载主表的列
|
|
|
$columnList = Db::query($sql, [$this->database['database'], $table]);
|
|
|
$fieldlist_comment = [];
|
|
|
|
|
|
foreach ($columnList as $index => $item) {
|
|
|
$fieldlist_comment[$item['COLUMN_NAME']] = $this->FieldComment($item['COLUMN_NAME'], $item['DATA_TYPE'], $item['COLUMN_COMMENT']);
|
|
|
}
|
|
|
|
|
|
if ($this->request->isAjax()) {
|
|
|
$this->success("", null, ['fieldlist_comment' => $fieldlist_comment]);
|
|
|
} else {
|
|
|
return $fieldlist_comment;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 字段分析
|
|
|
* @param String $field 字段名称
|
|
|
* @param String $data_type 字段数据类型
|
|
|
* @param String $comment 字段注释
|
|
|
* @return Array 可用的字段数据
|
|
|
*/
|
|
|
protected function FieldComment($field, $data_type, $comment)
|
|
|
{
|
|
|
// 字段类型识别
|
|
|
$data_type_int = [
|
|
|
'tinyint',
|
|
|
'int',
|
|
|
'smallint',
|
|
|
'mediumint',
|
|
|
'integer',
|
|
|
'bigint'
|
|
|
];
|
|
|
|
|
|
$discerns = 0;
|
|
|
|
|
|
if (in_array($data_type, $data_type_int)) {
|
|
|
$discerns = 1;// 数字
|
|
|
}
|
|
|
|
|
|
if (preg_match("/time$/i", $field)) {
|
|
|
$discerns = 2;// 日期时间
|
|
|
} else if (preg_match("/image$|avatar$/i", $field)) {
|
|
|
$discerns = 4;// 3为图片-导出慢弃用
|
|
|
} else if (preg_match("/file$/i", $field)) {
|
|
|
$discerns = 4;// 文件
|
|
|
}
|
|
|
|
|
|
/*else if (preg_match("/list$|data$|switch$/i", $field)) {
|
|
|
$discerns = 5;// 赋值
|
|
|
}*/
|
|
|
|
|
|
|
|
|
// 字段标题和赋值分析
|
|
|
if (!$comment) {
|
|
|
return [
|
|
|
'discerns' => $discerns,
|
|
|
'name' => $field,
|
|
|
'comment' => ''
|
|
|
];
|
|
|
}
|
|
|
|
|
|
$comment = str_replace(',', ',', $comment);
|
|
|
$comment = str_replace(['(多选)', '(单选)', '(多选)', '(单选)'], '', $comment);
|
|
|
|
|
|
if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) {
|
|
|
list($field_name, $field_comment) = explode(':', $comment);
|
|
|
return [
|
|
|
'discerns' => 5,
|
|
|
'name' => $field_name ? $field_name : $field,
|
|
|
'comment' => $field_comment
|
|
|
];
|
|
|
} else {
|
|
|
|
|
|
if (preg_match("/switch$/i", $field)) {
|
|
|
$discerns = 5;// 赋值
|
|
|
$comment_temp = '0=关闭,1=开启';
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
'discerns' => $discerns,
|
|
|
'name' => $comment ? $comment : $field,
|
|
|
'comment' => isset($comment_temp) ? $comment_temp : ''
|
|
|
];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查看
|
|
|
*/
|
|
|
public function index()
|
|
|
{
|
|
|
//当前是否为关联查询
|
|
|
$this->relationSearch = true;
|
|
|
//设置过滤方法
|
|
|
$this->request->filter(['strip_tags']);
|
|
|
if ($this->request->isAjax()) {
|
|
|
//如果发送的来源是Selectpage,则转发到Selectpage
|
|
|
if ($this->request->request('keyField')) {
|
|
|
return $this->selectpage();
|
|
|
}
|
|
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
|
|
$total = $this->model
|
|
|
->with(['admin'])
|
|
|
->where($where)
|
|
|
->order($sort, $order)
|
|
|
->count();
|
|
|
|
|
|
$list = $this->model
|
|
|
->with(['admin'])
|
|
|
->where($where)
|
|
|
->order($sort, $order)
|
|
|
->limit($offset, $limit)
|
|
|
->select();
|
|
|
|
|
|
foreach ($list as $row) {
|
|
|
|
|
|
$row->getRelation('admin')->visible(['username']);
|
|
|
}
|
|
|
$list = collection($list)->toArray();
|
|
|
$result = array("total" => $total, "rows" => $list);
|
|
|
|
|
|
return json($result);
|
|
|
}
|
|
|
return $this->view->fetch();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 添加
|
|
|
*/
|
|
|
public function add()
|
|
|
{
|
|
|
if ($this->request->isPost()) {
|
|
|
$params = $this->request->post("row/a");
|
|
|
|
|
|
if ($params) {
|
|
|
$params = method_exists($this, 'preExcludeFields') ? $this->preExcludeFields($params) : $params;
|
|
|
|
|
|
$params['admin_id'] = $this->auth->id;// 记录导出人
|
|
|
if (isset($params['where_field']['op']) && is_array($params['where_field']['op'])) {
|
|
|
foreach ($params['where_field']['op'] as $key => $value) {
|
|
|
$params['where_field']['op'][$key] = trim($value);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ($this->dataLimit && $this->dataLimitFieldAutoFill) {
|
|
|
$params[$this->dataLimitField] = $this->auth->id;
|
|
|
}
|
|
|
$result = false;
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
//是否采用模型验证
|
|
|
if ($this->modelValidate) {
|
|
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
|
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate;
|
|
|
$this->model->validateFailException(true)->validate($validate);
|
|
|
}
|
|
|
$result = $this->model->allowField(true)->save($params);
|
|
|
Db::commit();
|
|
|
} catch (ValidateException $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (PDOException $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (Exception $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
}
|
|
|
if ($result !== false) {
|
|
|
$this->success();
|
|
|
} else {
|
|
|
$this->error(__('No rows were inserted'));
|
|
|
}
|
|
|
}
|
|
|
$this->error(__('Parameter %s can not be empty', ''));
|
|
|
}
|
|
|
return $this->view->fetch();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 编辑
|
|
|
*/
|
|
|
public function edit($ids = null)
|
|
|
{
|
|
|
$row = $this->model->get($ids);
|
|
|
if (!$row) {
|
|
|
$this->error(__('No Results were found'));
|
|
|
}
|
|
|
|
|
|
$adminIds = $this->getDataLimitAdminIds();
|
|
|
if (is_array($adminIds)) {
|
|
|
if (!in_array($row[$this->dataLimitField], $adminIds)) {
|
|
|
$this->error(__('You have no permission'));
|
|
|
}
|
|
|
}
|
|
|
if ($this->request->isPost()) {
|
|
|
$params = $this->request->post("row/a");
|
|
|
if ($params) {
|
|
|
$params = method_exists($this, 'preExcludeFields') ? $this->preExcludeFields($params) : $params;
|
|
|
$params['join_table'] = isset($params['join_table']) ? $params['join_table'] : [];
|
|
|
$params['where_field'] = isset($params['where_field']) ? $params['where_field'] : [];
|
|
|
if (isset($params['where_field']['op']) && is_array($params['where_field']['op'])) {
|
|
|
foreach ($params['where_field']['op'] as $key => $value) {
|
|
|
$params['where_field']['op'][$key] = trim($value);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$result = false;
|
|
|
Db::startTrans();
|
|
|
try {
|
|
|
//是否采用模型验证
|
|
|
if ($this->modelValidate) {
|
|
|
$name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
|
|
$validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
|
|
|
$row->validateFailException(true)->validate($validate);
|
|
|
}
|
|
|
$result = $row->allowField(true)->save($params);
|
|
|
Db::commit();
|
|
|
} catch (ValidateException $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (PDOException $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
} catch (Exception $e) {
|
|
|
Db::rollback();
|
|
|
$this->error($e->getMessage());
|
|
|
}
|
|
|
if ($result !== false) {
|
|
|
$this->success();
|
|
|
} else {
|
|
|
$this->error(__('No rows were updated'));
|
|
|
}
|
|
|
}
|
|
|
$this->error(__('Parameter %s can not be empty', ''));
|
|
|
}
|
|
|
|
|
|
$row = $row->toArray();
|
|
|
$row['join_table_number'] = count($row['join_table']);
|
|
|
|
|
|
// 源表字段列表
|
|
|
$table_field = $this->getTableFields($row['main_table']);
|
|
|
|
|
|
// 准备源表和关联表的所有字段-带表名-供筛选和排序
|
|
|
$allField = [];
|
|
|
foreach ($table_field as $key => $value) {
|
|
|
$field_name = $row['main_table'] . '.' . $key;
|
|
|
$value['field_name'] = $field_name . ' - ' . $value['name'];
|
|
|
$allField[$field_name] = $value;
|
|
|
}
|
|
|
|
|
|
// 要导出的字段
|
|
|
$fields = [];
|
|
|
if ($row['field_config'] && isset($row['field_config']['title'])) {
|
|
|
foreach ($row['field_config']['title'] as $key => $value) {
|
|
|
$fields[$key]['title'] = $value;
|
|
|
$fields[$key]['discerns'] = $row['field_config']['discerns'][$key];
|
|
|
$fields[$key]['scheme'] = $row['field_config']['scheme'][$key];
|
|
|
}
|
|
|
}
|
|
|
$row['fields'] = $fields;
|
|
|
|
|
|
// 源表和关联表的字段列表透传数据准备
|
|
|
$join_field_count = 0;
|
|
|
if (isset($row['join_table'])) {
|
|
|
foreach ($row['join_table'] as $key => $value) {
|
|
|
$join_table_name = isset($value['table']) ? $value['table'] : false;
|
|
|
if ($join_table_name) {
|
|
|
|
|
|
$row['join_table'][$key]['field_list'] = $join_table_field[$join_table_name] = $this->getTableFields($join_table_name);
|
|
|
|
|
|
$join_table_name_as = $value['join_as'] ? $value['join_as'] : $join_table_name;
|
|
|
foreach ($join_table_field[$join_table_name] as $join_field_key => $join_field_value) {
|
|
|
$field_name = $join_table_name_as . '.' . $join_field_key;
|
|
|
$join_field_value['field_name'] = $field_name . ' - ' . $join_field_value['name'];
|
|
|
$allField[$field_name] = $join_field_value;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 关联表取值字段准备
|
|
|
$field_name_arr = [];
|
|
|
$field_name_arr_select = [];
|
|
|
if (isset($row['join_table'][$key]['fields']) && isset($row['join_table'][$key]['fields']['title'])) {
|
|
|
foreach ($row['join_table'][$key]['fields']['title'] as $fkey => $fvalue) {
|
|
|
$field_name_arr[$fkey]['title'] = $fvalue;
|
|
|
$field_name_arr[$fkey]['discerns'] = $row['join_table'][$key]['fields']['discerns'][$fkey];
|
|
|
$field_name_arr[$fkey]['scheme'] = $row['join_table'][$key]['fields']['scheme'][$fkey];
|
|
|
$field_name_arr_select[] = $fkey;
|
|
|
}
|
|
|
}
|
|
|
$row['join_table'][$key]['field_name_arr'] = $field_name_arr;
|
|
|
$row['join_table'][$key]['field_name_arr_select'] = $field_name_arr_select;// 供selectpicker初始化
|
|
|
$join_field_count += count($field_name_arr_select);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$row['where_field_arr_select'] = [];
|
|
|
$row['where_field_arr'] = [];
|
|
|
if ($row['where_field'] && isset($row['where_field']['op'])) {
|
|
|
foreach ($row['where_field']['op'] as $key => $value) {
|
|
|
$row['where_field_arr_select'][] = $key;
|
|
|
$row['where_field_arr'][$key] = [
|
|
|
'op' => $value,
|
|
|
'condition' => $row['where_field']['condition'][$key]
|
|
|
];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$this->assignconfig('fields', [
|
|
|
'table_field' => $table_field,
|
|
|
'join_table_field' => isset($join_table_field) ? $join_table_field : [],
|
|
|
'join_table_number' => $row['join_table_number'],
|
|
|
'field_count' => count($row['fields']) + $join_field_count,
|
|
|
]);
|
|
|
|
|
|
$this->view->assign("row", $row);
|
|
|
$this->view->assign("allField", $allField);
|
|
|
$this->view->assign("table_field", $table_field);
|
|
|
return $this->view->fetch();
|
|
|
}
|
|
|
|
|
|
public function emptydir($dir, $del_current_dir = false)
|
|
|
{
|
|
|
$dh = opendir($dir);
|
|
|
while ($file = readdir($dh)) {
|
|
|
if ($file != "." && $file != "..") {
|
|
|
$fullpath = $dir . DS . $file;
|
|
|
if (!is_dir($fullpath)) {
|
|
|
unlink($fullpath);
|
|
|
} else {
|
|
|
$this->emptydir($fullpath, true);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
closedir($dh);
|
|
|
|
|
|
// 删除当前文件夹
|
|
|
if ($del_current_dir) {
|
|
|
rmdir($dir);
|
|
|
}
|
|
|
}
|
|
|
} |
...
|
...
|
|