123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602 |
- <?php
- namespace app\admin\controller\routine;
- use app\common\controller\Backend;
- use ba\Random;
- use PhpZip\Exception\ZipException;
- use PhpZip\ZipFile;
- use think\db\exception\PDOException;
- use think\exception\ValidateException;
- use modules\dataexport\library\ExportLib;
- use think\facade\Db;
- use think\Exception;
- use PhpOffice\PhpSpreadsheet\Spreadsheet;
- use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
- use PhpOffice\PhpSpreadsheet\Cell\DataType;
- /**
- * 导出任务管理
- *
- */
- class Dataexport extends Backend
- {
- /**
- * Dataexport模型对象
- * @var \app\admin\model\routine\Dataexport
- */
- protected $model = null;
- protected $quickSearchField = ['name'];
- protected $defaultSortField = 'id,desc';
- protected $withJoinTable = ['admin'];
- protected $preExcludeFields = ['createtime'];
- protected $noNeedPermission = ['getFieldList', 'test', 'task', 'taskControl', 'taskZip'];
- protected $saveDir = null;
- protected $exportZipDir = null;
- public function initialize()
- {
- parent::initialize();
- $this->model = new \app\admin\model\routine\Dataexport;
- $this->saveDir = runtime_path() . 'export' . DIRECTORY_SEPARATOR;// 临时文件保存位置(每次开始任务时清空)
- $this->exportZipDir = root_path() . 'public' . DIRECTORY_SEPARATOR . 'export' . DIRECTORY_SEPARATOR;
- }
- public function test()
- {
- if (!$this->auth->check('routine/dataexport/start')) {
- $this->error(__('You have no permission'), ['routePath' => '/admin'], 302);
- }
- $id = $this->request->param($this->model->getPk());
- $row = $this->model->find($id);
- $export = new ExportLib($id);
- try {
- $exportNumber = $row->export_number ? $row->export_number : $export->getCount();
- if (!$exportNumber) {
- $this->error('没有数据需要导出~');
- }
- $subtask = [
- [
- 'id' => 1,
- 'status' => 0,// 状态:0=准备好,1=进行中,2=完成,3=失败
- 'min' => 0,
- 'max' => 10,
- 'sql' => $export->getSql('test')
- ]
- ];
- // 测试sql
- Db::query($subtask[0]['sql']);
- $row->subtask = $subtask;
- $row->save();
- } catch (PDOException|Exception $e) {
- $this->error($e->getMessage());
- }
- $this->success('', [
- 'taskId' => $id
- ]);
- }
- public function start()
- {
- $id = $this->request->param($this->model->getPk());
- $row = $this->model->find($id);
- $export = new ExportLib($id);
- try {
- $exportNumber = $row->export_number ? $row->export_number : $export->getCount();
- $xlsMaxNumber = ($exportNumber >= $row->xls_max_number) ? $row->xls_max_number : $exportNumber;
- if (!$exportNumber) {
- $this->error('没有数据需要导出~');
- }
- $subtask = [];
- $subtaskCount = ceil($exportNumber / $xlsMaxNumber);// 子任务数量
- for ($i = 0; $i < $subtaskCount; $i++) {
- $min = $i * $xlsMaxNumber;
- $subtask[$i] = [
- 'id' => $i,
- 'status' => 0,// 状态:0=准备好,1=进行中,2=完成,3=失败
- 'min' => $min,
- 'max' => $xlsMaxNumber,
- 'sql' => $export->getSql('limit', [$min, $xlsMaxNumber])
- ];
- }
- // 测试sql
- Db::query($subtask[0]['sql']);
- if (!is_dir($this->exportZipDir)) {
- mkdir($this->exportZipDir, 0777, true);
- }
- if ($subtaskCount > 1) {
- // 清理任务临时文件目录
- if (!is_dir($this->saveDir . $id)) {
- mkdir($this->saveDir . $id . DIRECTORY_SEPARATOR, 0777, true);
- } else {
- deldir($this->saveDir . $id, false);
- }
- $row->lastprogress = 5;
- } else {
- $row->lastprogress = 0;
- }
- // 删除上次任务的zip包
- if ($row->lastfile) {
- $fileName = explode(DIRECTORY_SEPARATOR, path_transform($row->lastfile));
- if (file_exists($this->exportZipDir . end($fileName))) {
- unlink($this->exportZipDir . end($fileName));
- }
- $row->lastfile = '';
- }
- $row->subtask = $subtask;
- $row->lastexporttime = time();
- $row->save();
- } catch (PDOException|Exception $e) {
- $this->error($e->getMessage());
- }
- $this->success('导出任务初始化成功!', [
- 'download' => ($subtaskCount == 1) ? true : false,
- 'subtask' => $subtask,
- 'id' => $id
- ]);
- }
- public function task(int $id, int $subId, bool $download)
- {
- if (!$this->auth->check('routine/dataexport/start')) {
- $this->error(__('You have no permission'), ['routePath' => '/admin'], 302);
- }
- $row = $this->model->find($id);
- $export = new ExportLib($id);
- if (isset($row->subtask[$subId]) && is_array($row->subtask[$subId])) {
- $subTask = $row['subtask'][$subId];
- $taskName = $row['name'];
- } else {
- $this->error('导出子任务未找到,请重新开始!', ['subId' => $subId]);
- }
- set_time_limit(0);// 脚本执行时间限制
- ini_set('memory_limit', $row->memory_limit . 'M');// 脚本内存限制
- // 检查任务状态
- if (!$download) {
- if ($subTask['status'] == 1) {
- $this->error('此子任务正在执行中~', ['subId' => $subId]);
- } else if ($subTask['status'] == 2) {
- if (file_exists($this->saveDir . $id . DIRECTORY_SEPARATOR . $subId . '.xlsx')) {
- $this->error('此子任务已经处理过啦~', ['subId' => $subId]);
- }
- }
- }
- $spreadsheet = new Spreadsheet();
- $worksheet = $spreadsheet->getActiveSheet();
- $worksheet->setTitle($taskName);
- // 设置表头
- $head = $export->getXlsTitle();
- $headCount = count($head);
- for ($i = 0; $i < $headCount; $i++) {
- $worksheet->setCellValueByColumnAndRow($i + 1, 1, $head[$i]);
- }
- // 写入数据
- try {
- $data = Db::query($subTask['sql']);
- } catch (PDOException $e) {
- $this->error('任务失败!', ['subId' => $subId, 'msg' => $e->getMessage()]);
- }
- $y = 2;
- $fields = $export->getFields();
- foreach ($data as $rowKey => $row) {
- $i = 1;
- foreach ($fields as $key => $value) {
- $field = $value['field'];
- if ($value['discern'] == 'text') {
- // 文本
- $worksheet->setCellValueExplicitByColumnAndRow($i, $y, $row[$field], DataType::TYPE_STRING);
- } else if ($value['discern'] == 'int') {
- // 数字
- $worksheet->setCellValueExplicitByColumnAndRow($i, $y, (int)$row[$field], DataType::TYPE_NUMERIC);
- } else if ($value['discern'] == 'time') {
- // 日期时间
- if ($row[$field]) {
- if (is_numeric($row[$field])) {
- $excelDateValue = date('Y-m-d H:i:s', $row[$field]);
- } else {
- $excelDateValue = $row[$field];
- }
- $worksheet->setCellValueByColumnAndRow($i, $y, $excelDateValue);
- } else {
- $worksheet->setCellValueByColumnAndRow($i, $y, '-');
- }
- } else if ($value['discern'] == 'valuation') {
- // 赋值
- $fieldValue = $export->assignment($row[$field], $value['comment']);
- $worksheet->setCellValueByColumnAndRow($i, $y, $fieldValue);
- }
- $i++;
- }
- $y++;
- unset($data[$rowKey]); // 能节约一点内存
- }
- // xls文件处理
- if ($download) {
- // 直接下载
- 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");
- $taskName = $id . '.' . $taskName . '.xlsx';
- $encodedFilename = urlencode($taskName);
- $ua = $_SERVER["HTTP_USER_AGENT"];
- if (preg_match("/MSIE/", $ua)) {
- header('Content-Disposition: attachment; filename="' . $encodedFilename . '"');
- } else if (preg_match("/Firefox/", $ua)) {
- header('Content-Disposition: attachment; filename*="utf8\'\'' . $taskName . '"');
- } else {
- header('Content-Disposition: attachment; filename="' . $taskName . '"');
- }
- 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->saveDir . $id . DIRECTORY_SEPARATOR . $subId . '.xlsx');
- $result = false;
- Db::startTrans();
- try {
- // 获取最新状态
- $row = $this->model->find($id);
- $lastprogress = $row->lastprogress + round(92 / count($row->subtask), 2);
- $lastprogress = ($lastprogress > 100) ? 100 : $lastprogress;
- if (isset($row->subtask[$subId]) && is_array($row->subtask[$subId])) {
- $subTaskTemp = $row->subtask;
- $subTaskTemp[$subId]['status'] = 2;
- $row->subtask = $subTaskTemp;
- $row->lastprogress = $lastprogress;
- $row->save();
- $result = true;
- }
- Db::commit();
- } catch (PDOException|Exception $e) {
- Db::rollback();
- $this->error('任务失败!', ['subId' => $subId, 'msg' => $e->getMessage()]);
- }
- $spreadsheet->disconnectWorksheets();
- unset($spreadsheet);
- if ($result) {
- $this->success('', [
- 'subId' => $subId,
- ]);
- } else {
- $this->error('', [
- 'subId' => $subId,
- ]);
- }
- }
- }
- public function taskControl()
- {
- if (!$this->auth->check('routine/dataexport/start')) {
- $this->error(__('You have no permission'), ['routePath' => '/admin'], 302);
- }
- $id = $this->request->param($this->model->getPk());
- $row = $this->model->find($id);
- if (!$row) {
- $this->error('任务找不到啦~');
- }
- if (!isset($row->subtask) || !is_array($row->subtask)) {
- $this->error('子任务找不到啦,请重新开始任务~');
- }
- $row = $row->toArray();
- $currentPage = 0;
- $subtaskPage = [];
- foreach ($row['subtask'] as $value) {
- $value['status'] = 0;// 用户可能会刷新任务控制页面,js将重新确定状态
- $subtaskPage[$currentPage][] = $value;
- if (count($subtaskPage[$currentPage]) >= $row['concurrent_create_xls']) {
- $currentPage++;
- }
- }
- $subtaskCount = is_array($row['subtask']) ? count($row['subtask']) : 0;
- $row['subtask_progress'] = round(92 / $subtaskCount, 2);
- $row['lastprogress'] = 5;
- $this->success('', [
- 'task' => $row,
- 'subtaskPage' => $subtaskPage,
- ]);
- }
- public function taskZip()
- {
- if (!$this->auth->check('routine/dataexport/start')) {
- $this->error(__('You have no permission'), ['routePath' => '/admin'], 302);
- }
- $id = $this->request->param($this->model->getPk());
- $row = $this->model->find($id);
- if (!$row) {
- $this->error(__('Record not found'));
- }
- if (!isset($row->subtask) || !is_array($row->subtask)) {
- $this->error('打包失败,任务找不到啦~');
- }
- if ($row->lastfile) {
- $this->success('', ['file' => $row->lastfile]);
- }
- foreach ($row->subtask as $subtask) {
- if (!file_exists($this->saveDir . $id . DIRECTORY_SEPARATOR . $subtask['id'] . '.xlsx')) {
- $this->error('子任务未处理完毕!');
- }
- }
- $taskDir = $this->saveDir . $id . DIRECTORY_SEPARATOR;// 导出任务的临时文件目录
- $zipName = $id . '.export_' . Random::build() . '.zip';
- $zipUrl = full_url(path_transform(DIRECTORY_SEPARATOR . 'export' . DIRECTORY_SEPARATOR . $zipName));// 绝对地址,以便各处直接点击下载
- $zipName = $this->exportZipDir . $zipName;
- $zip = new ZipFile();
- try {
- $dh = opendir($taskDir);
- while ($file = readdir($dh)) {
- if ($file != "." && $file != "..") {
- $fullPath = $taskDir . $file;
- if (!is_dir($fullPath)) {
- $zip->addFile($fullPath, $file);
- }
- }
- }
- closedir($dh);
- $zip->saveAsFile($zipName);
- deldir($taskDir);
- } catch (ZipException $e) {
- $this->error('打包失败:' . $e->getMessage());
- } finally {
- $zip->close();
- }
- $row->lastfile = $zipUrl;
- $row->lastprogress = 100;
- $row->save();
- $this->success('', ['url' => $zipUrl]);
- }
- public function add()
- {
- if ($this->request->isPost()) {
- $this->request->filter(['trim']);
- $data = $this->request->post();
- if (!$data) {
- $this->error(__('Parameter %s can not be empty', ['']));
- }
- $data = $this->excludeFields($data);
- $data['admin_id'] = $this->auth->id;
- $result = false;
- Db::startTrans();
- try {
- // 模型验证
- if ($this->modelValidate) {
- $validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
- if (class_exists($validate)) {
- $validate = new $validate;
- if ($this->modelSceneValidate) $validate->scene('add');
- $validate->check($data);
- }
- }
- $result = $this->model->save($data);
- Db::commit();
- } catch (ValidateException|PDOException|Exception $e) {
- Db::rollback();
- $this->error($e->getMessage());
- }
- if ($result !== false) {
- $this->success(__('Added successfully'));
- } else {
- $this->error(__('No rows were added'));
- }
- }
- $this->success('', [
- 'tables' => $this->getTableList(),
- ]);
- }
- /**
- * 编辑
- */
- public function edit()
- {
- $id = $this->request->param($this->model->getPk());
- $row = $this->model->find($id);
- if (!$row) {
- $this->error(__('Record not found'));
- }
- $dataLimitAdminIds = $this->getDataLimitAdminIds();
- if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
- $this->error(__('You have no permission'));
- }
- if ($this->request->isPost()) {
- $this->request->filter(['trim']);
- $data = $this->request->post();
- if (!$data) {
- $this->error(__('Parameter %s can not be empty', ['']));
- }
- $data = $this->excludeFields($data);
- $result = false;
- Db::startTrans();
- try {
- // 模型验证
- if ($this->modelValidate) {
- $validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
- if (class_exists($validate)) {
- $validate = new $validate;
- if ($this->modelSceneValidate) $validate->scene('edit');
- $validate->check($data);
- }
- }
- $result = $row->save($data);
- Db::commit();
- } catch (ValidateException|PDOException|Exception $e) {
- Db::rollback();
- $this->error($e->getMessage());
- }
- if ($result !== false) {
- $this->success(__('Update successful'));
- } else {
- $this->error(__('No rows updated'));
- }
- }
- $this->success('', [
- 'row' => $row,
- 'tables' => $this->getTableList(),
- ]);
- }
- /**
- * 查看
- */
- public function index()
- {
- // 设置过滤方法
- $this->request->filter(['strip_tags', 'trim']);
- // 如果是select则转发到select方法,若select未重写,其实还是继续执行index
- if ($this->request->param('select')) {
- $this->select();
- }
- list($where, $alias, $limit, $order) = $this->queryBuilder();
- $res = $this->model
- ->withJoin($this->withJoinTable, $this->withJoinType)
- ->alias($alias)
- ->where($where)
- ->order($order)
- ->paginate($limit);
- $res->visible(['admin' => ['nickname']]);
- $this->success('', [
- 'list' => $res->items(),
- 'total' => $res->total(),
- 'remark' => get_route_remark(),
- ]);
- }
- protected function getTableList()
- {
- $tablePrefix = config('database.connections.mysql.prefix');
- $outExcludeTable = [
- // 功能表
- 'area',
- 'config',
- 'token',
- 'captcha',
- 'admin_group_access',
- ];
- $outTables = [];
- $tables = get_table_list();
- $pattern = '/^' . $tablePrefix . '/i';
- foreach ($tables as $table => $tableComment) {
- $table = preg_replace($pattern, '', $table);
- if (!in_array($table, $outExcludeTable)) {
- $outTables[$table] = $tableComment;
- }
- }
- return $outTables;
- }
- public function getFieldList($table = null)
- {
- if (!$table) {
- $this->error(__('Parameter error'));
- }
- // 字段类型识别
- $dataTypeInt = [
- 'tinyint',
- 'int',
- 'smallint',
- 'mediumint',
- 'integer',
- 'bigint'
- ];
- $fieldList = get_table_fields($table);
- $fields = [];
- foreach ($fieldList as $key => $item) {
- $discern = 'text'; // 文本
- if (in_array($item['DATA_TYPE'], $dataTypeInt)) {
- $discern = 'int'; // 数字
- }
- if (preg_match("/time$|datetime$/i", $key)) {
- $discern = 'time'; // 日期时间
- }
- if (preg_match("/switch$|toggle$/i", $key)) {
- $discern = 'valuation';// 赋值
- $commentVal = '0=关闭,1=开启';
- }
- $comment = str_replace(',', ',', $item['COLUMN_COMMENT']);
- $comment = str_replace(['(多选)', '(单选)', '(多选)', '(单选)'], '', $comment);
- if (stripos($comment, ':') !== false && stripos($comment, ',') && stripos($comment, '=') !== false) {
- list($fieldName, $commentVal) = explode(':', $comment);
- $discern = 'valuation';// 赋值
- }
- $fields[$key] = [
- 'name' => $key,
- 'discern' => $discern,
- 'title' => $fieldName ?? ($item['COLUMN_COMMENT'] ? $item['COLUMN_COMMENT'] : $key),
- 'comment' => $commentVal ?? '',
- ];
- unset($commentVal, $fieldName);
- }
- $this->success('', [
- 'fields' => $fields,
- ]);
- }
- }
|