From 7cf7784a897d91d7d5e4f6278f80698ff2789428 Mon Sep 17 00:00:00 2001 From: liwq <122639653@qq.com> Date: Thu, 22 Jan 2026 10:04:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E6=89=A7?= =?UTF-8?q?=E8=A1=8Cpython=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - frontend/package-lock.json | 32 + frontend/package.json | 1 + frontend/src/components/index.vue | 706 ++++++++++++++++++ .../java/com/tem/bocai/BocaiApplication.java | 2 + .../tem/bocai/controller/ChartController.java | 94 +++ .../schedules/GetPredictResultSchedule.java | 112 +++ src/main/resources/a.jpg | Bin 1567 -> 0 bytes src/main/resources/b.jpg | Bin 5900 -> 0 bytes 9 files changed, 947 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/index.vue create mode 100644 src/main/java/com/tem/bocai/controller/ChartController.java create mode 100644 src/main/java/com/tem/bocai/schedules/GetPredictResultSchedule.java delete mode 100644 src/main/resources/a.jpg delete mode 100644 src/main/resources/b.jpg diff --git a/.gitignore b/.gitignore index 09ba7d3..dbe2def 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,6 @@ jre/ jdk/ # 数据库文件 -*.db *.db-shm *.db-wal *.sqlite diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c01c7c5..61cbb9d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "axios": "^1.13.2", + "echarts": "^5.6.0", "vue": "^3.5.24" }, "devDependencies": { @@ -593,6 +594,22 @@ "node": ">= 0.4" } }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, "node_modules/entities": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", @@ -1364,6 +1381,21 @@ "optional": true } } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" } } } diff --git a/frontend/package.json b/frontend/package.json index 12c11ad..5dfdb7f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "axios": "^1.13.2", + "echarts": "^5.6.0", "vue": "^3.5.24" }, "devDependencies": { diff --git a/frontend/src/components/index.vue b/frontend/src/components/index.vue new file mode 100644 index 0000000..6daeefe --- /dev/null +++ b/frontend/src/components/index.vue @@ -0,0 +1,706 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/com/tem/bocai/BocaiApplication.java b/src/main/java/com/tem/bocai/BocaiApplication.java index 82fcf8b..0ece728 100644 --- a/src/main/java/com/tem/bocai/BocaiApplication.java +++ b/src/main/java/com/tem/bocai/BocaiApplication.java @@ -3,8 +3,10 @@ package com.tem.bocai; import com.tem.bocai.util.SQLiteUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class BocaiApplication { public static void main(String[] args) { diff --git a/src/main/java/com/tem/bocai/controller/ChartController.java b/src/main/java/com/tem/bocai/controller/ChartController.java new file mode 100644 index 0000000..9525127 --- /dev/null +++ b/src/main/java/com/tem/bocai/controller/ChartController.java @@ -0,0 +1,94 @@ +package com.tem.bocai.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.*; + +@RestController +@RequestMapping("/api") +public class ChartController { + + // 获取折线图1数据 + @GetMapping("/charts/line1") + public Map getLineChartData1() { + Map response = new HashMap<>(); + + // 模拟数据 - 实际项目中应该从数据库获取 + List data = Arrays.asList(65, 59, 80, 81, 56, 55, 40); + List labels = Arrays.asList("1月", "2月", "3月", "4月", "5月", "6月", "7月"); + + response.put("data", data); + response.put("labels", labels); + response.put("title", "折线图1数据"); + + return response; + } + + // 获取折线图2数据 + @GetMapping("/charts/line2") + public Map getLineChartData2() { + Map response = new HashMap<>(); + + // 模拟数据 - 实际项目中应该从数据库获取 + List data = Arrays.asList(28, 48, 40, 19, 86, 27, 90); + List labels = Arrays.asList("1月", "2月", "3月", "4月", "5月", "6月", "7月"); + + response.put("data", data); + response.put("labels", labels); + response.put("title", "折线图2数据"); + + return response; + } + + // 获取表格数据 + @GetMapping("/table") + public List> getTableData() { + List> tableData = new ArrayList<>(); + + // 模拟数据 - 实际项目中应该从数据库获取 + Map item1 = new HashMap<>(); + item1.put("id", 1); + item1.put("name", "项目1"); + item1.put("value", 120); + item1.put("status", "正常"); + tableData.add(item1); + + Map item2 = new HashMap<>(); + item2.put("id", 2); + item2.put("name", "项目2"); + item2.put("value", 230); + item2.put("status", "警告"); + tableData.add(item2); + + Map item3 = new HashMap<>(); + item3.put("id", 3); + item3.put("name", "项目3"); + item3.put("value", 180); + item3.put("status", "正常"); + tableData.add(item3); + + Map item4 = new HashMap<>(); + item4.put("id", 4); + item4.put("name", "项目4"); + item4.put("value", 90); + item4.put("status", "异常"); + tableData.add(item4); + + Map item5 = new HashMap<>(); + item5.put("id", 5); + item5.put("name", "项目5"); + item5.put("value", 320); + item5.put("status", "正常"); + tableData.add(item5); + + Map item6 = new HashMap<>(); + item6.put("id", 6); + item6.put("name", "项目6"); + item6.put("value", 270); + item6.put("status", "警告"); + tableData.add(item6); + + return tableData; + } +} \ No newline at end of file diff --git a/src/main/java/com/tem/bocai/schedules/GetPredictResultSchedule.java b/src/main/java/com/tem/bocai/schedules/GetPredictResultSchedule.java new file mode 100644 index 0000000..8cf4887 --- /dev/null +++ b/src/main/java/com/tem/bocai/schedules/GetPredictResultSchedule.java @@ -0,0 +1,112 @@ +package com.tem.bocai.schedules; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +@Component +public class GetPredictResultSchedule { + + /** + * 处理文件路径,确保路径正确 + * @param filePath 文件路径 + * @return 处理后的文件路径 + */ + private String handleFilePath(String filePath) { + // 处理路径分隔符,统一使用系统默认分隔符 + filePath = filePath.replace("/", System.getProperty("file.separator")); + filePath = filePath.replace("\\", System.getProperty("file.separator")); + + // 如果路径包含空格,确保路径被正确处理 + // Runtime.exec会自动处理带空格的路径,不需要手动添加引号 + + return filePath; + } + + // 每天凌晨2点执行 + @Scheduled(cron = "0 0 2 * * ?") + public void executePythonScript() { + System.out.println("开始执行Python脚本..."); + + // 示例1:基本参数 + // String[] params = {"param1", "param2", "param3"}; + // executePythonScriptWithParams("your_script.py", params); + + // 示例2:传递文件路径作为参数 + String[] paramsWithFiles = { + "--input", "data/input.csv", + "--output", "results/output.json", + "--config", "config/settings.ini" + }; + executePythonScriptWithParams("scripts/process_data.py", paramsWithFiles); + } + + /** + * 执行带参数的Python脚本 + * @param scriptPath Python脚本路径 + * @param params 脚本参数数组(可以包含文件路径) + */ + public void executePythonScriptWithParams(String scriptPath, String[] params) { + try { + // 处理脚本路径,确保路径正确 + scriptPath = handleFilePath(scriptPath); + + // 构建命令数组 + String[] command = new String[params.length + 2]; + command[0] = "python"; + command[1] = scriptPath; + + // 添加参数,处理文件路径参数 + for (int i = 0; i < params.length; i++) { + // 检查参数是否为文件路径(包含路径分隔符) + if (params[i].contains("\\") || params[i].contains("/")) { + command[i + 2] = handleFilePath(params[i]); + } else { + command[i + 2] = params[i]; + } + } + + System.out.println("执行命令: " + String.join(" ", command)); + + // 执行Python脚本 + Process process = Runtime.getRuntime().exec(command); + + // 读取脚本输出 + BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream(), "UTF-8") + ); + + String line; + StringBuilder output = new StringBuilder(); + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + + // 读取错误输出 + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(process.getErrorStream(), "UTF-8") + ); + + StringBuilder errorOutput = new StringBuilder(); + while ((line = errorReader.readLine()) != null) { + errorOutput.append(line).append("\n"); + } + + // 等待脚本执行完成 + int exitCode = process.waitFor(); + + System.out.println("Python脚本执行完成,退出码: " + exitCode); + System.out.println("脚本输出:\n" + output.toString()); + + if (exitCode != 0) { + System.err.println("脚本执行错误:\n" + errorOutput.toString()); + } + + } catch (IOException | InterruptedException e) { + System.err.println("执行Python脚本时发生错误:"); + e.printStackTrace(); + } + } +} diff --git a/src/main/resources/a.jpg b/src/main/resources/a.jpg deleted file mode 100644 index 278c8ca6a9a26a93e0f54ba15f14930e887012d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1567 zcmbW!c{mhk90%}sZo@E!AvAK#6qaT)Ml*_B8P`%n=#V1iTE`fSk|ix-l}O1EPdT^5 zItH^m5Gb`|Li?{=V8P(l!ih27o{T zfQSkR2Y@*cLn2W~gcu5iLZijRF?dOggak%WMiz@#R#MratVAHFlE@mW>N-RML6fGX zvqzUorK)Hc7||()WIZZnYX}65Mq?x}3X+lv6g7exTzC59H45H)b60Tcp*LE$h20uC2-M~dD79EXrm(=|oPGTl+? zEIj2>%3U#{S$UJ3L+2bx&m%YnEiS)JK~ZVvE)7ksZ>aivX>@~q<_9epmR8m_jz^rF zU5*|*e#Z0cIWKP?Uv@}n7$^Mvh08z2#>HPrNKCzvcJo&H?TpNOdHDr}Ma4hguc)lz zR@c<>er;}PecZ-xfAX8)Wmk7kZ(skbcf%v2W8)LQPfpE$Tv%LMURhmR-*Q0!>?=!j ze}(L#O<<62-r(6=lo2A?>Z$cCG9OmRaf;+|K zNz`{c=eKBIWd9Bp^S@+&!Txr=0vH%X^m#BGFa}HAs%yEk*mp!OnX$AU6_#T5c|mbw zsG<8scowt5@99aYh}sW=5t%{5iu1m^o5suo++45Ld&Thaw6dj9>C0)U`c-$__{Yj= z^0I54I>VF!#h?}x;Zk3s;HR0gjn6qt{BWgl{?qOc{)IIi%Ow>Sc>Bhwis(JC)d(oP zG=Z9RewM_mQ}KEI$db@VIY@s286hRz_-vC_;4jB_5ZBi^6xd_jQqz;rK9uwP(;L=W z@3zvaeQtSy&Xe|yc! zC|bcNUjLcT0mLbMzr>)IV;2TYSH&4H`noWCX#s6kZ9s+7V8Pi?@;6xUE~(z+3W4fX zdfni_=!L^XOeBA9Bh#7K&YGQEDx(QJcyr0E+QyQv-gL0@hfF+iu7um=<0@%MAUJ;x z?B~Iw{RG>0n6FRtR6LNfei)F+bMpCUX&J)ThK&dRuu{Z5QYTm(*oaM?Y3>;|sWZ2O z^7yWv$!uqtvq7T)8!e^#=WTX6swZcSE2<<5!Ts_GA#h{ZOg>Ol!p8ek%{e+g61J=` zfp&=+hE)z{U{@GM@mzn)F}v-I0cFl~D4m$o8i{>x=M2>+EU3yczb{p*UjjccRXi?jCK zR62PVmsgc@!Zyo9J<(z<+Q|@#WyVA-7&JXV)QIfdezJ3 zm$~U0`FX3#W?3oMJ8Nq8rXvmEk9~djMGC={P2Hl04O~{?>zj!_WL3voYf>&_+D*H2 zo6|SYWj2z2%*=JtQLTLnaPL@OkjTpU;7F?7qK2&BgjKxpIEKebw<1~?YxiR~oS`fG2q=`<- hz8*0Vq!v0s5`yGa=cDP{n0_38+tw;UuUnC@{|};IrZE5j diff --git a/src/main/resources/b.jpg b/src/main/resources/b.jpg deleted file mode 100644 index 3bd1871414bc7d946d55f53933451d908cde8852..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5900 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 ihq@Z%nbOfx1O9&zzmo|V=4nm<0000