From a038772795b4395ef55ba7814aaa205782c05ef6 Mon Sep 17 00:00:00 2001 From: wxxwjef <505322415@qq.com> Date: Tue, 14 Nov 2023 10:16:19 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=AF=BC=E5=85=A5=EF=BC=8C?= =?UTF-8?q?=E7=89=A9=E5=93=81=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/controller/GorseController.java | 13 +- .../wx/application/gorse4j/GorseService.java | 54 +++ web/package-lock.json | 167 ++++----- web/package.json | 1 + web/src/components/menus/EntrysManage.vue | 6 + web/src/components/menus/UserManage.vue | 8 + web/src/components/menus/item/ImportItem.vue | 322 ++++++++++++++++++ web/src/components/menus/user/ImportUser.vue | 97 ++++-- web/src/main.js | 3 + web/src/router/index.js | 4 + web/src/utils/request.js | 4 +- 11 files changed, 563 insertions(+), 116 deletions(-) create mode 100644 web/src/components/menus/item/ImportItem.vue diff --git a/api/src/main/java/com/wx/application/adapter/controller/GorseController.java b/api/src/main/java/com/wx/application/adapter/controller/GorseController.java index fc0e56a..ec65437 100644 --- a/api/src/main/java/com/wx/application/adapter/controller/GorseController.java +++ b/api/src/main/java/com/wx/application/adapter/controller/GorseController.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.alibaba.fastjson.JSONObject; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -21,6 +22,7 @@ import com.wx.application.nebula.graph.service.ImportGraphService; import com.wx.application.nebula.graph.service.NebulaOperateService; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; @Slf4j @RequestMapping("/gorse") @@ -116,9 +118,14 @@ public class GorseController extends BaseController { return success(gorseService.getRecommendByUser(gQo.getUserId(), gQo.getRecommendation(), gQo.getCategory(), gQo.getN())); } - @PostMapping(value = "/get_similar_item") - public ResponseData getSimilarItem(@RequestBody GorseQ gQo) throws Exception { - return success(gorseService.getSimilarItem(gQo.getItemId(), gQo.getCategory(), gQo.getN())); + @PutMapping(value = "/bulk/{type}") + public ResponseData bulkUser(@RequestParam Map data, @RequestParam("file") MultipartFile file, @PathVariable("type") String type) throws Exception { + return success(gorseService.bulkUserOrItem(data, file, type)); + } + + @GetMapping(value = "/get_bulk_user") + public ResponseData getBulkUser() throws Exception { + return success(gorseService.getBulkUser()); } diff --git a/api/src/main/java/com/wx/application/gorse4j/GorseService.java b/api/src/main/java/com/wx/application/gorse4j/GorseService.java index fe7b7c2..5aa2bf9 100644 --- a/api/src/main/java/com/wx/application/gorse4j/GorseService.java +++ b/api/src/main/java/com/wx/application/gorse4j/GorseService.java @@ -1,17 +1,32 @@ package com.wx.application.gorse4j; +import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.ObjectMapper; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; + import java.io.DataOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; import java.util.List; +import java.util.Map; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; @Service public class GorseService { @@ -95,6 +110,45 @@ public class GorseService { return Arrays.asList(this.request("GET", url, null, Item[].class)); } + /** + * 文件方式导入用户或者物品users\items + */ + public JSONObject bulkUserOrItem(Map data, MultipartFile multipartFile, String type) throws IOException { + File file = new File("path/to/file"); + FileUtils.writeByteArrayToFile(file, multipartFile.getBytes()); + + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + for (Map.Entry entry : data.entrySet()) { + builder.addTextBody(entry.getKey(), entry.getValue()); + } + + builder.addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, multipartFile.getOriginalFilename()); + return this.sendPostFormData(this.endpoint + "/api/bulk/" + type, builder); + } + + + public File getBulkUser() throws IOException { + return this.request("GET", this.endpoint + "/api/bulk/users", null, File.class); + } + + public JSONObject sendPostFormData(String url, MultipartEntityBuilder builder) throws IOException { + JSONObject resJson; + HttpClient httpClient = HttpClientBuilder.create().build(); + HttpPost httpPost = new HttpPost(url); + + HttpEntity entity = builder.build(); + httpPost.setEntity(entity); + + HttpResponse response = null; + try { + response = httpClient.execute(httpPost); + HttpEntity responseEntity = response.getEntity(); + resJson = JSONObject.parseObject(EntityUtils.toString(responseEntity)); + } catch (IOException e) { + throw e; + } + return resJson; + } private Response request(String method, String url, Request request, Class responseClass) throws IOException { HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); diff --git a/web/package-lock.json b/web/package-lock.json index 4bbc293..65b431e 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -2139,6 +2139,16 @@ "integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=", "dev": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "cacache": { "version": "13.0.1", "resolved": "https://registry.npm.taobao.org/cacache/download/cacache-13.0.1.tgz?cache=0&sync_timestamp=1616431241238&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-13.0.1.tgz", @@ -2165,6 +2175,34 @@ "unique-filename": "^1.1.1" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, "cssnano": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", @@ -2177,6 +2215,25 @@ "postcss": "^7.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", @@ -2193,6 +2250,16 @@ "minipass": "^3.1.1" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "terser-webpack-plugin": { "version": "2.3.8", "resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-2.3.8.tgz", @@ -2209,6 +2276,18 @@ "terser": "^4.6.12", "webpack-sources": "^1.4.3" } + }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.8.3", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz", + "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + } } } }, @@ -9917,6 +9996,11 @@ "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=", "dev": true }, + "papaparse": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/papaparse/-/papaparse-5.4.1.tgz", + "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" + }, "parallel-transform": { "version": "1.2.0", "resolved": "https://registry.npm.taobao.org/parallel-transform/download/parallel-transform-1.2.0.tgz", @@ -14521,85 +14605,12 @@ } } }, - "vue-loader-v16": { - "version": "npm:vue-loader@16.8.3", - "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz", - "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", - "dev": true, - "optional": true, + "vue-papa-parse": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/vue-papa-parse/-/vue-papa-parse-3.1.0.tgz", + "integrity": "sha512-5YdF3Dtf49EGfaz3+IgIpUw9yYuvV3HekZkob6jrK/Ffz1aCrWjevtcQByKxrNtK7RAL39B0ca93bogKuiQQKg==", "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "optional": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "optional": true - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "papaparse": "^5.3.0" } }, "vue-quill-editor": { diff --git a/web/package.json b/web/package.json index cd12fb9..95add16 100644 --- a/web/package.json +++ b/web/package.json @@ -32,6 +32,7 @@ "vue": "^2.6.11", "vue-axios": "^3.2.4", "vue-json-editor": "^1.4.3", + "vue-papa-parse": "^3.1.0", "vue-quill-editor": "^3.0.6", "vuex": "^3.1.0" }, diff --git a/web/src/components/menus/EntrysManage.vue b/web/src/components/menus/EntrysManage.vue index 402210f..f2aeccf 100644 --- a/web/src/components/menus/EntrysManage.vue +++ b/web/src/components/menus/EntrysManage.vue @@ -19,6 +19,7 @@
添 加 + 导入条目
@@ -217,6 +218,11 @@ export default { } }); }, + showImportItemPage() { + _this.$router.push({ + path: "importItem" + }); + }, removeRow(item) { _this.$confirm('此操作将永久删除记录, 是否继续?', '提示', { confirmButtonText: '确定', diff --git a/web/src/components/menus/UserManage.vue b/web/src/components/menus/UserManage.vue index c48ae43..ef59513 100644 --- a/web/src/components/menus/UserManage.vue +++ b/web/src/components/menus/UserManage.vue @@ -16,6 +16,7 @@
添 加 + 导出用户 导入用户
@@ -137,6 +138,13 @@ export default { path: "importUser" }); }, + downloadUser() { + request({ + url: '/gorse/get_bulk_user', + method: 'get' + }).then(res => { + }); + }, linkUser(item) { _this.$router.push({ path: "linkUser", query: { diff --git a/web/src/components/menus/item/ImportItem.vue b/web/src/components/menus/item/ImportItem.vue new file mode 100644 index 0000000..f00bb54 --- /dev/null +++ b/web/src/components/menus/item/ImportItem.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/web/src/components/menus/user/ImportUser.vue b/web/src/components/menus/user/ImportUser.vue index 2c73fe6..0394566 100644 --- a/web/src/components/menus/user/ImportUser.vue +++ b/web/src/components/menus/user/ImportUser.vue @@ -7,29 +7,31 @@
- + + :show-file-list="false"> 点击上传
只能上传csv文件
+
+ @change="handleFieldChange"> - - - + + + +
- + @@ -39,8 +41,7 @@ - + @@ -49,11 +50,12 @@ - +
+
- + @@ -63,7 +65,7 @@ - 确认提交 + 确认提交
@@ -83,7 +85,7 @@ export default { labels: '', ifFirstHead: true }, - fileList: [], + file: [], originList: [], // 原始的可能包含表头的数据,默认存储最多21行(如果数据不止21行的话) list: [], // 页面table显示的数据 columnList: [],// 上传csv的表头 @@ -105,18 +107,24 @@ export default { }, methods: { handleFileChange(file, fileList) { - const fileReader = new FileReader(); - fileReader.onload = function () { - const fileData = fileReader.result; - _this.originList = fileData.split('\n').slice(0, 21); - // 提前填充一个默认的值给对应的ID列和标签列 - _this.prepareFieldAndLabels(); - _this.refreshList(); - }; - fileReader.readAsText(file.raw); + _this.file = file.raw; + if(_this.cmd.field.length > 0) { + _this.handleFieldChange(); + } + }, + handleFieldChange() { + this.$papa.parse(_this.file, { + delimiter: _this.cmd.field, + complete: (results) => { + _this.originList = results.data.slice(0, 21); + // 提前填充一个默认的值给对应的ID列和标签列 + _this.prepareFieldAndLabels(); + _this.refreshList(); + } + }) }, prepareFieldAndLabels() { - _this.columnList = _this.originList[0].split(_this.cmd.field); + _this.columnList = _this.originList[0]; _this.cmd.id = 0; _this.cmd.labels = 1; }, @@ -131,7 +139,7 @@ export default { // 第一行是表头,跳过list塞入 continue; } - let row = _this.originList[i].split(_this.cmd.field); + let row = _this.originList[i]; let errorFlag = false; try { JSON.parse(row[_this.cmd.labels]); @@ -146,14 +154,37 @@ export default { } }, confirmSubmit() { // 提交确认 - // request({ - // url: '/risk-user/query_unique', - // method: 'post', - // data: { - // EQS_fid: _this.userId - // } - // }).then(res => { - // }); + this.$refs.cmd.validate((valid) => { + let formData = new FormData(); + formData.append("sep", _this.cmd.field); + formData.append("has-header", _this.cmd.ifFirstHead); + formData.append("label-sep", _this.cmd.categories); + formData.append("file", _this.file); + let idIndex = _this.cmd.id; + let labelIndex = _this.cmd.labels; + let _format = ''; + for (let i = 0; i <= Math.max(idIndex, labelIndex); i++) { + if (i == idIndex) { + _format += "u"; + } else if (i == labelIndex) { + _format += "l"; + } else { + _format += "_"; + } + } + formData.append("format", _format); + request({ + url: '/gorse/bulk/users', + method: 'put', + data: formData + }).then(res => { + if(res.data && res.data.RowAffected) { + _this.$message.success(`成功导入${res.data.RowAffected}条记录`); + } else { + _this.$message.warning("导入异常"); + } + }); + }); }, backBtn() { this.$router.push({ diff --git a/web/src/main.js b/web/src/main.js index b972bec..bc8f43c 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -21,11 +21,14 @@ import {getToken, setToken, removeToken} from '@/utils/auth' import '@/permission' import '@antv/x6-vue-shape' +import VuePapaParse from 'vue-papa-parse' + Vue.prototype.$echarts = echarts; Vue.use(ElementUI); Vue.use(VueQuillEditor); +Vue.use(VuePapaParse); Vue.config.productionTip = false; diff --git a/web/src/router/index.js b/web/src/router/index.js index 586f63b..b1a1869 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -55,6 +55,10 @@ export default new Router({ path: 'entrysManage', component: () => import('@/components/menus/EntrysManage') }, + { + path: 'importItem', + component: () => import('@/components/menus/item/ImportItem') + }, { path: 'feedbackTypeManage', component: () => import('@/components/menus/FeedbackTypeManage') diff --git a/web/src/utils/request.js b/web/src/utils/request.js index dfc16de..0b839bb 100644 --- a/web/src/utils/request.js +++ b/web/src/utils/request.js @@ -3,8 +3,8 @@ import {MessageBox, Message} from 'element-ui' import store from '@/store' import {getToken} from '@/utils/auth' -// var _baseURL = "http://localhost:4026"; -var _baseURL = "http://139.9.106.207:4026"; +var _baseURL = "http://localhost:4026"; +// var _baseURL = "http://139.9.106.207:4026"; var _fileURL = "http://139.9.106.207:4026/web/"; // create an axios instance const service = axios.create({