Dataimport.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <?php
  2. namespace app\admin\controller\routine;
  3. use Throwable;
  4. use ba\Filesystem;
  5. use think\facade\Db;
  6. use app\common\controller\Backend;
  7. use PhpOffice\PhpSpreadsheet\IOFactory;
  8. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  9. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  10. use app\admin\model\Asset;
  11. /**
  12. * 数据导入记录
  13. *
  14. */
  15. class Dataimport extends Backend
  16. {
  17. /**
  18. * Dataimport模型对象
  19. * @var object
  20. * @phpstan-var \app\admin\model\routine\Dataimport
  21. */
  22. protected object $model;
  23. protected string|array $preExcludeFields = ['id', 'create_time'];
  24. protected array $withJoinTable = ['admin'];
  25. protected string|array $quickSearchField = ['id', 'data_table', 'records', 'import_success_records'];
  26. protected array $noNeedPermission = ['handleXls'];
  27. public function initialize(): void
  28. {
  29. parent::initialize();
  30. $this->model = new \app\admin\model\routine\Dataimport;
  31. }
  32. public function downloadImportTemplate()
  33. {
  34. $table = $this->request->get('table', '');
  35. if (!$table) {
  36. $this->error(__('Parameter error'));
  37. }
  38. $fields = get_table_fields($table);
  39. $spreadsheet = new Spreadsheet();
  40. $worksheet = $spreadsheet->getActiveSheet();
  41. $worksheet->setTitle($table);
  42. // 设置表头
  43. $i = 0;
  44. foreach ($fields as $field) {
  45. $worksheet->setCellValue([$i + 1, 1], $field['COLUMN_NAME'] . ($field['COLUMN_COMMENT'] ? '(' . $field['COLUMN_COMMENT'] . ')' : ''));
  46. $i++;
  47. }
  48. // 直接下载
  49. ob_end_clean();
  50. header("Pragma: public");
  51. header("Expires: 0");
  52. header("Cache-Control:must-revalidate, post-check=0, pre-check=0");
  53. header("Content-Type:application/force-download");
  54. header("Content-Type:application/vnd.ms-execl");
  55. header("Content-Type:application/octet-stream");
  56. header("Content-Type:application/download");
  57. $taskName = $table . '-template.xlsx';
  58. $encodedFilename = urlencode($taskName);
  59. $ua = $_SERVER["HTTP_USER_AGENT"];
  60. if (str_contains($ua, "MSIE")) {
  61. header('Content-Disposition: attachment; filename="' . $encodedFilename . '"');
  62. } else if (str_contains($ua, "Firefox")) {
  63. header('Content-Disposition: attachment; filename*="utf8\'\'' . $taskName . '"');
  64. } else {
  65. header('Content-Disposition: attachment; filename="' . $taskName . '"');
  66. }
  67. header("Content-Transfer-Encoding:binary");
  68. header('Cache-Control: max-age=0');
  69. $writer = new Xlsx($spreadsheet);
  70. $writer->save('php://output');
  71. $spreadsheet->disconnectWorksheets();
  72. unset($spreadsheet);
  73. }
  74. public function handleXls()
  75. {
  76. if (!$this->auth->check('routine/dataimport/add')) {
  77. $this->error(__('You have no permission'), [], 401);
  78. }
  79. $file = $this->request->request('file', '');
  80. $table = $this->request->request('table', '');
  81. if (!$table) {
  82. $this->error('请选择数据表!');
  83. }
  84. if (!$file) {
  85. $this->error('请上传导入数据!');
  86. }
  87. // 读取xls文件内容
  88. $filePath = Filesystem::fsFit(public_path() . $file);
  89. $spreadsheet = IOFactory::load($filePath);
  90. $sheet = $spreadsheet->getSheet(0);
  91. $rowCount = $sheet->getHighestRow();
  92. $data = $sheet->toArray();
  93. $fields = get_table_fields($table);
  94. // 寻找字段对应数据所在的 key 值
  95. // 字段名、字段备注、字段名(字段备注)、字段名(字段备注+字段字典)
  96. $importKeys = [];
  97. foreach ($fields as $key => $field) {
  98. $title = $field['COLUMN_NAME'] . ($field['COLUMN_COMMENT'] ? '(' . $field['COLUMN_COMMENT'] . ')' : '');
  99. $titleKey = array_search($title, $data[0]);
  100. if ($titleKey !== false) {
  101. $importKeys[$field['COLUMN_NAME']] = $titleKey;
  102. continue;
  103. }
  104. $nameKey = array_search($field['COLUMN_NAME'], $data[0]);
  105. if ($nameKey !== false) {
  106. $importKeys[$field['COLUMN_NAME']] = $nameKey;
  107. continue;
  108. }
  109. if ($field['COLUMN_COMMENT']) {
  110. $commentKey = array_search($field['COLUMN_COMMENT'], $data[0]);
  111. if ($commentKey !== false) {
  112. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  113. continue;
  114. }
  115. if (strpos($field['COLUMN_COMMENT'], ':')) {
  116. $comment = explode(':', $field['COLUMN_COMMENT']);
  117. $cleanComment = $comment[0];
  118. $commentKey = array_search($cleanComment, $data[0]);
  119. if ($commentKey !== false) {
  120. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  121. continue;
  122. }
  123. $titleCleanComment = $field['COLUMN_NAME'] . '(' . $cleanComment . ')';
  124. $commentKey = array_search($titleCleanComment, $data[0]);
  125. if ($commentKey !== false) {
  126. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  127. }
  128. $fields[$key]['COLUMN_COMMENT'] = $cleanComment;
  129. }
  130. }
  131. }
  132. $importPre = [];
  133. foreach ($data as $key => $item) {
  134. if ($key == 0) continue;
  135. $importPreItem = [];
  136. foreach ($importKeys as $importKey => $importValueKey) {
  137. $importPreItem[$importKey] = $item[$importValueKey];
  138. }
  139. $importPre[] = $importPreItem;
  140. }
  141. if ($this->request->isPost()) {
  142. // 导入到表
  143. $nowTime = time();
  144. $nowYmdHis = date('Y-m-d H:i:s');
  145. $timeFields = ['createtime', 'create_time', 'updatetime', 'update_time'];
  146. foreach ($importPre as &$item) {
  147. foreach ($timeFields as $timeField) {
  148. if (array_key_exists($timeField, $fields) && (!isset($item[$timeField]) || !$item[$timeField])) {
  149. if ($fields[$timeField]['DATA_TYPE'] == 'int' || $fields[$timeField]['DATA_TYPE'] == 'bigint') {
  150. $item[$timeField] = $nowTime;
  151. } elseif ($fields[$timeField]['DATA_TYPE'] == 'datetime') {
  152. $item[$timeField] = $nowYmdHis;
  153. }
  154. }
  155. }
  156. }
  157. Db::startTrans();
  158. $res = 0;
  159. try {
  160. $res = Db::name($table)->strict(false)->limit(500)->insertAll($importPre);
  161. Db::name('dataimport')->insert([
  162. 'data_table' => $table,
  163. 'admin_id' => $this->auth->id,
  164. 'file' => $file,
  165. 'records' => $rowCount - 1,
  166. 'import_success_records' => $res,
  167. 'radio' => 'import',
  168. 'create_time' => $nowTime,
  169. ]);
  170. Db::commit();
  171. @unlink($filePath);
  172. } catch (Throwable $e) {
  173. Db::rollback();
  174. $this->error($e->getMessage());
  175. }
  176. $this->success('总计' . ($rowCount - 1) . '行数据,成功导入' . $res . '条!', [
  177. 'data' => $importPre,
  178. ]);
  179. }
  180. if ($rowCount > 101) {
  181. $importPre = array_merge(array_slice($importPre, 0, 50), array_slice($importPre, $rowCount - 51, $rowCount));
  182. }
  183. $this->success('', [
  184. 'fields' => $fields,
  185. 'rowCount' => ($rowCount - 1),
  186. 'data' => $importPre,
  187. ]);
  188. }
  189. public function assetHandleXls()
  190. {
  191. if (!$this->auth->check('routine/dataimport/add')) {
  192. $this->error(__('You have no permission'), [], 401);
  193. }
  194. $file = $this->request->request('file', '');
  195. $table = $this->request->request('table', '');
  196. if (!$table) {
  197. $this->error('请选择数据表!');
  198. }
  199. if (!$file) {
  200. $this->error('请上传导入数据!');
  201. }
  202. // 读取xls文件内容
  203. $filePath = Filesystem::fsFit(public_path() . $file);
  204. $spreadsheet = IOFactory::load($filePath);
  205. $sheet = $spreadsheet->getSheet(0);
  206. $rowCount = 0;
  207. $data = $sheet->toArray();
  208. $fields = get_table_fields($table);
  209. // 寻找字段对应数据所在的 key 值
  210. // 字段名、字段备注、字段名(字段备注)、字段名(字段备注+字段字典)
  211. $importKeys = [];
  212. $assetKey = '';
  213. foreach ($fields as $key => $field) {
  214. $title = $field['COLUMN_NAME'] . ($field['COLUMN_COMMENT'] ? '(' . $field['COLUMN_COMMENT'] . ')' : '');
  215. $titleKey = array_search($title, $data[0]);
  216. if($title == "asset_id(资产编号)"){
  217. $assetKey = $titleKey;
  218. }
  219. if ($titleKey !== false) {
  220. $importKeys[$field['COLUMN_NAME']] = $titleKey;
  221. continue;
  222. }
  223. $nameKey = array_search($field['COLUMN_NAME'], $data[0]);
  224. if($field['COLUMN_NAME'] == 'asset_id'){
  225. $assetKey = $nameKey;
  226. }
  227. if ($nameKey !== false) {
  228. $importKeys[$field['COLUMN_NAME']] = $nameKey;
  229. continue;
  230. }
  231. if ($field['COLUMN_COMMENT']) {
  232. $commentKey = array_search($field['COLUMN_COMMENT'], $data[0]);
  233. if($field['COLUMN_COMMENT'] == '资产编号'){
  234. $assetKey = $commentKey;
  235. }
  236. if ($commentKey !== false) {
  237. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  238. continue;
  239. }
  240. if (strpos($field['COLUMN_COMMENT'], ':')) {
  241. $comment = explode(':', $field['COLUMN_COMMENT']);
  242. $cleanComment = $comment[0];
  243. $commentKey = array_search($cleanComment, $data[0]);
  244. if ($commentKey !== false) {
  245. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  246. continue;
  247. }
  248. $titleCleanComment = $field['COLUMN_NAME'] . '(' . $cleanComment . ')';
  249. $commentKey = array_search($titleCleanComment, $data[0]);
  250. if ($commentKey !== false) {
  251. $importKeys[$field['COLUMN_NAME']] = $commentKey;
  252. }
  253. $fields[$key]['COLUMN_COMMENT'] = $cleanComment;
  254. }
  255. }
  256. }
  257. $importPre = [];
  258. $newAssetIds = [];
  259. foreach ($data as $key => $item) {
  260. if ($key == 0) continue;
  261. if(isset($item[$assetKey]) && $item[$assetKey] !== 0){
  262. $importPreItem = [];
  263. foreach ($importKeys as $importKey => $importValueKey) {
  264. $importPreItem[$importKey] = $item[$importValueKey];
  265. }
  266. $importPre[] = $importPreItem;
  267. $newAssetIds[] = $item[$assetKey];
  268. }
  269. }
  270. $rowCount = count($importPre);
  271. $AssetModel = new Asset();
  272. $oldRow = $AssetModel->where('asset_id', 'in', $newAssetIds)->select();
  273. if ($this->request->isPost()) {
  274. // 导入到表
  275. $nowTime = time();
  276. $nowYmdHis = date('Y-m-d H:i:s');
  277. $timeFields = ['createtime', 'create_time', 'updatetime', 'update_time'];
  278. foreach ($importPre as &$item) {
  279. foreach ($timeFields as $timeField) {
  280. if (array_key_exists($timeField, $fields) && (!isset($item[$timeField]) || !$item[$timeField])) {
  281. if ($fields[$timeField]['DATA_TYPE'] == 'int' || $fields[$timeField]['DATA_TYPE'] == 'bigint') {
  282. $item[$timeField] = $nowTime;
  283. } elseif ($fields[$timeField]['DATA_TYPE'] == 'datetime') {
  284. $item[$timeField] = $nowYmdHis;
  285. }
  286. }
  287. }
  288. }
  289. Db::startTrans();
  290. $res = 0;
  291. try {
  292. $oldRow->delete();
  293. $res = Db::name($table)->strict(false)->limit(500)->insertAll($importPre);
  294. Db::name('dataimport')->insert([
  295. 'data_table' => $table,
  296. 'admin_id' => $this->auth->id,
  297. 'file' => $file,
  298. 'records' => $rowCount,
  299. 'import_success_records' => $res,
  300. 'radio' => 'import',
  301. 'create_time' => $nowTime,
  302. ]);
  303. Db::commit();
  304. @unlink($filePath);
  305. } catch (Throwable $e) {
  306. Db::rollback();
  307. $this->error($e->getMessage());
  308. }
  309. $this->success('总计' . ($rowCount) . '行数据,成功导入' . $res . '条!', [
  310. 'data' => $importPre,
  311. ]);
  312. }
  313. if ($rowCount > 101) {
  314. $importPre = array_merge(array_slice($importPre, 0, 50), array_slice($importPre, $rowCount - 51, $rowCount));
  315. }
  316. $oldCount = count($oldRow);
  317. if ($oldCount > 101 && $oldCount !== 0 ) {
  318. $oldRow = array_merge(array_slice($oldRow->toArray(), 0, 50), array_slice($oldRow->toArray(), $oldCount - 51, $oldCount));
  319. }
  320. $this->success('', [
  321. 'fields' => $fields,
  322. 'rowCount' => ($rowCount),
  323. 'data' => $importPre,
  324. 'old' => $oldRow,
  325. 'oldCount' => $oldCount
  326. ]);
  327. }
  328. /**
  329. * 查看
  330. * @throws Throwable
  331. */
  332. public function index(): void
  333. {
  334. $this->request->filter(['strip_tags', 'trim']);
  335. // 如果是select则转发到select方法,若select未重写,其实还是继续执行index
  336. if ($this->request->param('select')) {
  337. $this->select();
  338. }
  339. list($where, $alias, $limit, $order) = $this->queryBuilder();
  340. $res = $this->model
  341. ->withJoin($this->withJoinTable, $this->withJoinType)
  342. ->alias($alias)
  343. ->where($where)
  344. ->order($order)
  345. ->paginate($limit);
  346. $res->visible(['admin' => ['username']]);
  347. $this->success('', [
  348. 'list' => $res->items(),
  349. 'total' => $res->total(),
  350. 'remark' => get_route_remark(),
  351. ]);
  352. }
  353. /**
  354. * 添加
  355. * @throws Throwable
  356. */
  357. public function add(): void
  358. {
  359. $this->success('', [
  360. 'tables' => $this->getTableList(),
  361. ]);
  362. }
  363. protected function getTableList(): array
  364. {
  365. $tablePrefix = config('database.connections.mysql.prefix');
  366. $outExcludeTable = [
  367. // 功能表
  368. 'admin',
  369. 'admin_group',
  370. 'area',
  371. 'token',
  372. 'captcha',
  373. 'attachment',
  374. 'admin_log',
  375. 'admin_group_access',
  376. 'user_money_log',
  377. 'user_score_log',
  378. 'dataimport',
  379. 'dataexport',
  380. 'crud_log',
  381. 'security_data_recycle_log',
  382. 'security_sensitive_data_log',
  383. ];
  384. $outTables = [];
  385. $tables = get_table_list();
  386. $pattern = '/^' . $tablePrefix . '/i';
  387. foreach ($tables as $table => $tableComment) {
  388. $table = preg_replace($pattern, '', $table);
  389. if (!in_array($table, $outExcludeTable)) {
  390. $outTables[$table] = $tableComment;
  391. }
  392. }
  393. return $outTables;
  394. }
  395. }