Compare commits
93 Commits
cxversion
...
38f22ef8bd
| Author | SHA1 | Date | |
|---|---|---|---|
| 38f22ef8bd | |||
| a08ca8985a | |||
|
|
e92cab555f | ||
|
|
fef7d25e3c | ||
| b8b06a881a | |||
| 3f2c486f04 | |||
|
|
37239e1b0a | ||
|
|
3586f86ea6 | ||
|
|
bfb00804e6 | ||
|
|
767214b7db | ||
|
|
0ff4a201b0 | ||
| 71f9dab1be | |||
|
|
aefa44b8a9 | ||
| 4aa9952b26 | |||
| c8ce7a6fa3 | |||
|
|
94132ea8da | ||
|
|
e281f7fd92 | ||
|
|
22814cb1a7 | ||
|
|
44a5868349 | ||
| 71f6cfed3d | |||
| 84821955c9 | |||
|
|
a63cd6ad1a | ||
|
|
3539eab924 | ||
|
|
99e574f600 | ||
|
|
f3e0ffca4c | ||
|
|
d2c1d4443b | ||
|
|
9e36435a72 | ||
|
|
c70f7b2b90 | ||
|
|
7786369a63 | ||
|
|
e6241c16c7 | ||
|
|
51cac2c17a | ||
|
|
a579f0e15c | ||
|
|
41f8cef462 | ||
|
|
72aeee0f10 | ||
|
|
441c19e87a | ||
|
|
dc3f305303 | ||
| 589961397c | |||
| 837a85f7ba | |||
| a6579f019a | |||
| af8bf14b5e | |||
|
|
c85657d199 | ||
|
|
f3e3a99ebc | ||
|
|
d7fd9c6037 | ||
|
|
680eb6c54c | ||
| b56bf74bb8 | |||
| 467c49f432 | |||
| fa1c5c9b42 | |||
|
|
442b31ad37 | ||
|
|
89407d1f1d | ||
| 2496d05349 | |||
| 874e513c90 | |||
| e678276aba | |||
|
|
a10aae420a | ||
|
|
9f37292eea | ||
|
|
1a4027086c | ||
|
|
031725de7e | ||
| 09c58f80eb | |||
|
|
cd3194d1a6 | ||
| c09ca584c3 | |||
| 0e6eba8cf4 | |||
| 34b6ed4478 | |||
| 1b45d6124d | |||
|
|
27c2ed898c | ||
|
|
e5e8341b3e | ||
|
|
39c990b2ae | ||
| b86c94add9 | |||
| 84286a6769 | |||
| 9fe1da209d | |||
|
|
ddc10cf7ea | ||
| d57cb6cb8c | |||
|
|
be169ed131 | ||
|
|
500de8961d | ||
| e0833d3cbd | |||
| 5fd499e095 | |||
|
|
3eed489df6 | ||
|
|
feb857f7a0 | ||
|
|
ba80c96294 | ||
|
|
140f4a816e | ||
|
|
67ca5287e2 | ||
|
|
50b74f484f | ||
|
|
a102ed46f2 | ||
|
|
2ed796c1a1 | ||
| 4a4bcb3fab | |||
| a1181e49fc | |||
| 34009b8900 | |||
|
|
c11f3104cb | ||
| 5800b6b61c | |||
|
|
878961649d | ||
|
|
9461ba39e0 | ||
| 36015f26d9 | |||
| 6733f5f641 | |||
| 23a6b717d7 | |||
| a22a573c0d |
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,10 +1,20 @@
|
|||||||
## ide
|
## ide
|
||||||
**/.idea
|
**/.idea
|
||||||
|
**/.project
|
||||||
|
**/.classpath
|
||||||
|
**/.factorypath
|
||||||
|
**/.settings/
|
||||||
*.iml
|
*.iml
|
||||||
rebel.xml
|
rebel.xml
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.factorypath
|
||||||
|
**/.settings/
|
||||||
|
|
||||||
## backend
|
## backend
|
||||||
**/target
|
**/target
|
||||||
|
**/bin/
|
||||||
**/logs
|
**/logs
|
||||||
|
|
||||||
## front
|
## front
|
||||||
|
|||||||
41
.vscode/launch.json
vendored
41
.vscode/launch.json
vendored
@@ -1,11 +1,32 @@
|
|||||||
{
|
{
|
||||||
"configurations": [
|
"version": "0.2.0",
|
||||||
{
|
"configurations": [
|
||||||
"type": "java",
|
{
|
||||||
"name": "JeecgSystemApplication",
|
"type": "chrome",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "org.jeecg.JeecgSystemApplication",
|
"name": "jeecgboot-vue3: 调试前端 (Chrome)",
|
||||||
"projectName": "jeecg-system-start"
|
"url": "http://localhost:3100",
|
||||||
}
|
"webRoot": "${workspaceFolder}/jeecgboot-vue3",
|
||||||
]
|
"preLaunchTask": "jeecgboot-vue3: dev"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "jeecgboot-vue3: dev",
|
||||||
|
"runtimeExecutable": "pnpm",
|
||||||
|
"runtimeArgs": ["run", "dev"],
|
||||||
|
"cwd": "${workspaceFolder}/jeecgboot-vue3",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"skipFiles": ["<node_internals>/**", "**/node_modules/**"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "java",
|
||||||
|
"name": "JeecgSystemApplication (单体)",
|
||||||
|
"request": "launch",
|
||||||
|
"mainClass": "org.jeecg.JeecgSystemApplication",
|
||||||
|
"projectName": "jeecg-system-start",
|
||||||
|
"cwd": "${workspaceFolder}/jeecg-boot/jeecg-module-system/jeecg-system-start",
|
||||||
|
"vmArgs": "-Dfile.encoding=UTF-8 -Dspring.main.banner-mode=log -Dspring.banner.charset=UTF-8 -Dlogging.charset.console=GBK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
34
.vscode/settings.json
vendored
34
.vscode/settings.json
vendored
@@ -2,7 +2,10 @@
|
|||||||
"java.compile.nullAnalysis.mode": "automatic",
|
"java.compile.nullAnalysis.mode": "automatic",
|
||||||
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable",
|
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable",
|
||||||
"java.import.maven.enabled": true,
|
"java.import.maven.enabled": true,
|
||||||
"java.configuration.updateBuildConfiguration": "interactive",
|
"java.configuration.updateBuildConfiguration": "automatic",
|
||||||
|
"java.autobuild.enabled": true,
|
||||||
|
"java.import.maven.offline.enabled": false,
|
||||||
|
"java.configuration.maven.notCoveredPluginExecutionSeverity": "ignore",
|
||||||
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
||||||
"java.configuration.runtimes": [
|
"java.configuration.runtimes": [
|
||||||
{
|
{
|
||||||
@@ -10,5 +13,32 @@
|
|||||||
"path": "C:\\Program Files\\Java\\jdk-17",
|
"path": "C:\\Program Files\\Java\\jdk-17",
|
||||||
"default": true
|
"default": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"java.maven.downloadSources": true,
|
||||||
|
"java.eclipse.downloadSources": true,
|
||||||
|
"java.project.importOnFirstTimeStartup": "automatic",
|
||||||
|
"java.configuration.checkProjectSettingsExclusions": false,
|
||||||
|
"java.import.exclusions": [
|
||||||
|
"**/jeecg-server-cloud/**",
|
||||||
|
"**/jeecg-boot-platform/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/.metadata/**",
|
||||||
|
"**/archetype-resources/**",
|
||||||
|
"**/META-INF/maven/**"
|
||||||
|
],
|
||||||
|
"java.project.resourceFilters": [
|
||||||
|
"node_modules",
|
||||||
|
".git",
|
||||||
|
"jeecg-server-cloud",
|
||||||
|
"jeecg-boot-platform"
|
||||||
|
],
|
||||||
|
"java.debug.settings.console": "integratedTerminal",
|
||||||
|
"java.debug.settings.vmArgs": "-Dfile.encoding=UTF-8 -Dspring.main.banner-mode=log -Dspring.banner.charset=UTF-8 -Dlogging.charset.console=GBK",
|
||||||
|
"terminal.integrated.defaultProfile.windows": "PowerShell",
|
||||||
|
"terminal.integrated.profiles.windows": {
|
||||||
|
"PowerShell": {
|
||||||
|
"source": "PowerShell",
|
||||||
|
"args": ["-NoExit", "-Command", "chcp 936 | Out-Null"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
.vscode/tasks.json
vendored
39
.vscode/tasks.json
vendored
@@ -1,6 +1,27 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "JeecgBoot: compile",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "mvn",
|
||||||
|
"args": [
|
||||||
|
"compile",
|
||||||
|
"-pl",
|
||||||
|
"jeecg-module-system/jeecg-system-start",
|
||||||
|
"-am",
|
||||||
|
"-DskipTests",
|
||||||
|
"-q"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/jeecg-boot",
|
||||||
|
"env": {
|
||||||
|
"JAVA_HOME": "C:\\Program Files\\Java\\jdk-17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": "$javac",
|
||||||
|
"group": "build"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "YY.Admin: restore",
|
"label": "YY.Admin: restore",
|
||||||
"type": "process",
|
"type": "process",
|
||||||
@@ -33,6 +54,24 @@
|
|||||||
"dependsOn": "YY.Admin: build",
|
"dependsOn": "YY.Admin: build",
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "jeecgboot-vue3: dev",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "pnpm run dev",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/jeecgboot-vue3"
|
||||||
|
},
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "vite",
|
||||||
|
"pattern": { "regexp": "^$" },
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": ".",
|
||||||
|
"endsPattern": "(Local:|ready in|http://localhost)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "YY.Admin: run (script)",
|
"label": "YY.Admin: run (script)",
|
||||||
"type": "process",
|
"type": "process",
|
||||||
|
|||||||
@@ -324,17 +324,26 @@ func waitForWindowsPrintCompletion(printerName string, existingIDs map[int]bool,
|
|||||||
|
|
||||||
queued := false
|
queued := false
|
||||||
jobID := 0
|
jobID := 0
|
||||||
|
sumatraDone := false
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <-cmdDone:
|
case err := <-cmdDone:
|
||||||
|
sumatraDone = true
|
||||||
if err != nil && !queued {
|
if err != nil && !queued {
|
||||||
return fmt.Errorf("sumatra print failed: %v", err)
|
return fmt.Errorf("sumatra print failed: %v", err)
|
||||||
}
|
}
|
||||||
|
// Sumatra 已正常退出且 spooler 未出现新任务:部分驱动/打印机直接出纸,不经过队列
|
||||||
|
if err == nil && !queued {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
if !queued && sumatraDone {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if !queued && now.After(appearDeadline) {
|
if !queued && now.After(appearDeadline) {
|
||||||
return fmt.Errorf("print job not queued within %s", printQueueAppearTimeout)
|
return fmt.Errorf("print job not queued within %s", printQueueAppearTimeout)
|
||||||
}
|
}
|
||||||
|
|||||||
57
jeecg-boot/.claudeignore
Normal file
57
jeecg-boot/.claudeignore
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitmodules
|
||||||
|
|
||||||
|
# SVN
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ IDEA
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Maven / Gradle build output
|
||||||
|
target/
|
||||||
|
build/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Node (frontend artifacts if any)
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Docker volumes / data
|
||||||
|
docker/data/
|
||||||
|
|
||||||
|
# Compiled classes
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Custom
|
||||||
|
*.qqy
|
||||||
|
代码修改.log
|
||||||
|
代码修改日志
|
||||||
|
*.zip
|
||||||
|
backup/
|
||||||
|
.history/
|
||||||
|
.cursor/
|
||||||
|
doc/
|
||||||
|
docs/
|
||||||
6
jeecg-boot/.gitignore
vendored
6
jeecg-boot/.gitignore
vendored
@@ -2,6 +2,12 @@
|
|||||||
**/.idea
|
**/.idea
|
||||||
*.iml
|
*.iml
|
||||||
rebel.xml
|
rebel.xml
|
||||||
|
# VS Code/Cursor Java 扩展(Eclipse JDT)导入 Maven 时自动生成,勿提交
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.factorypath
|
||||||
|
**/.settings/
|
||||||
|
|
||||||
## backend
|
## backend
|
||||||
**/target
|
**/target
|
||||||
|
|||||||
14
jeecg-boot/.vscode/launch.json
vendored
Normal file
14
jeecg-boot/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "java",
|
||||||
|
"name": "JeecgSystemApplication (单体)",
|
||||||
|
"request": "launch",
|
||||||
|
"mainClass": "org.jeecg.JeecgSystemApplication",
|
||||||
|
"projectName": "jeecg-system-start",
|
||||||
|
"cwd": "${workspaceFolder}/jeecg-module-system/jeecg-system-start",
|
||||||
|
"vmArgs": "-Dspring.main.banner-mode=log -Dspring.banner.charset=UTF-8 -Dlogging.charset.console=GBK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
37
jeecg-boot/.vscode/settings.json
vendored
Normal file
37
jeecg-boot/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"java.compile.nullAnalysis.mode": "automatic",
|
||||||
|
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable",
|
||||||
|
"java.import.maven.enabled": true,
|
||||||
|
"java.configuration.updateBuildConfiguration": "automatic",
|
||||||
|
"java.autobuild.enabled": true,
|
||||||
|
"java.import.maven.offline.enabled": false,
|
||||||
|
"java.configuration.maven.notCoveredPluginExecutionSeverity": "ignore",
|
||||||
|
"java.jdt.ls.java.home": "C:\\Program Files\\Java\\jdk-17",
|
||||||
|
"java.configuration.runtimes": [
|
||||||
|
{
|
||||||
|
"name": "JavaSE-17",
|
||||||
|
"path": "C:\\Program Files\\Java\\jdk-17",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"java.maven.downloadSources": true,
|
||||||
|
"java.eclipse.downloadSources": true,
|
||||||
|
"java.project.importOnFirstTimeStartup": "automatic",
|
||||||
|
"java.configuration.checkProjectSettingsExclusions": false,
|
||||||
|
"java.import.exclusions": [
|
||||||
|
"**/jeecg-server-cloud/**",
|
||||||
|
"**/jeecg-boot-platform/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/.metadata/**",
|
||||||
|
"**/archetype-resources/**",
|
||||||
|
"**/META-INF/maven/**"
|
||||||
|
],
|
||||||
|
"java.project.resourceFilters": [
|
||||||
|
"node_modules",
|
||||||
|
".git",
|
||||||
|
"jeecg-server-cloud",
|
||||||
|
"jeecg-boot-platform"
|
||||||
|
],
|
||||||
|
"java.debug.settings.console": "integratedTerminal",
|
||||||
|
"java.debug.settings.vmArgs": "-Dspring.main.banner-mode=log -Dspring.banner.charset=UTF-8 -Dlogging.charset.console=GBK"
|
||||||
|
}
|
||||||
18
jeecg-boot/.vscode/tasks.json
vendored
Normal file
18
jeecg-boot/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Maven: 修复 Java Classpath",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "mvn clean install -DskipTests -pl jeecg-boot-base-core,jeecg-boot-module/jeecg-module-print,jeecg-boot-module/jeecg-module-xslmes -am",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": false
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
142
jeecg-boot/CLAUDE.md
Normal file
142
jeecg-boot/CLAUDE.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
> You should always answer questions in Simplified Chinese first, unless the user explicitly requests another language.
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
JeecgBoot 3.9.2 — a Java low-code development platform built on **Spring Boot 3.5.5**, **Java 17** (also supports 21, 24). It runs as a monolithic app by default, with an optional Spring Cloud microservices mode. Uses `jakarta` namespace (not `javax`) throughout.
|
||||||
|
|
||||||
|
## Build & Run Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Full build (tests are skipped by default via surefire config)
|
||||||
|
mvn clean package
|
||||||
|
|
||||||
|
# Build with tests
|
||||||
|
mvn clean package -DskipTests=false
|
||||||
|
|
||||||
|
# Run the standalone application (port 8080, context-path: /jeecg-boot)
|
||||||
|
cd jeecg-module-system/jeecg-system-start
|
||||||
|
mvn spring-boot:run
|
||||||
|
|
||||||
|
# Build a specific module (with dependencies)
|
||||||
|
mvn clean package -pl jeecg-boot-base-core -am
|
||||||
|
|
||||||
|
# Run a single test class
|
||||||
|
mvn test -DskipTests=false -pl <module> -Dtest=<TestClassName>
|
||||||
|
|
||||||
|
# Build with microservices modules included
|
||||||
|
mvn clean package -P SpringCloud
|
||||||
|
|
||||||
|
# Docker startup
|
||||||
|
./start-docker-compose.sh # or start-docker-compose.bat on Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
## Module Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
jeecg-boot-parent (root pom)
|
||||||
|
├── jeecg-boot-base-core # Core framework: Shiro/JWT auth, MyBatis-Plus config,
|
||||||
|
│ # common utilities, AOP aspects, base controllers
|
||||||
|
├── jeecg-module-system # System management (users, roles, permissions, dicts, menus)
|
||||||
|
│ ├── jeecg-system-api # API interfaces (local-api vs cloud-api for mono/micro switch)
|
||||||
|
│ │ ├── jeecg-system-local-api # Direct method calls (monolithic)
|
||||||
|
│ │ └── jeecg-system-cloud-api # Feign clients (microservices)
|
||||||
|
│ ├── jeecg-system-biz # Business logic, entities, mappers, services
|
||||||
|
│ └── jeecg-system-start # Main entry point (JeecgSystemApplication), all configs
|
||||||
|
├── jeecg-boot-module # Business feature modules
|
||||||
|
│ ├── jeecg-module-demo # Demo/example code
|
||||||
|
│ ├── jeecg-boot-module-airag # AI/RAG integration
|
||||||
|
│ ├── jeecg-boot-module-easyoa # Simple OA module
|
||||||
|
│ ├── jeecg-boot-module-joa-flowable # OA + Flowable workflow
|
||||||
|
│ ├── jeecg-boot-module-pay # Payment module
|
||||||
|
│ └── jeecg-boot-module-wps # WPS document integration
|
||||||
|
└── jeecg-boot-platform # Low-code platform modules
|
||||||
|
├── jeecg-boot-module-bpm-flowable # BPM workflow engine
|
||||||
|
├── jeecg-boot-module-airag-flow # AI RAG flow
|
||||||
|
├── jeecg-boot-module-bigscreen # Big screen/dashboard designer
|
||||||
|
├── jeecg-boot-module-desform # Form designer
|
||||||
|
├── jeecg-boot-module-drag # Drag-and-drop report designer
|
||||||
|
├── jeecg-boot-module-lowapp # Low-code application engine
|
||||||
|
├── jeecg-boot-module-mindesflow-flowable # Simple flow designer
|
||||||
|
└── jeecg-boot-module-online # Online code generator & forms
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional microservices modules (activated via `-P SpringCloud`):
|
||||||
|
- `jeecg-server-cloud/` — Gateway (port 9999), Nacos (8848), cloud service starters, monitoring (9111), XXL-Job (9080), Sentinel (9000)
|
||||||
|
|
||||||
|
## Key Technology Stack
|
||||||
|
|
||||||
|
| Layer | Technology |
|
||||||
|
|-------|-----------|
|
||||||
|
| ORM | MyBatis-Plus 3.5.12 (`BaseMapper<T>`, `ServiceImpl<M,T>`) |
|
||||||
|
| Auth | Apache Shiro 2.0.5 + JWT 4.5.0, Redis-backed sessions |
|
||||||
|
| DB Pool | Druid 1.2.24 with dynamic datasource support |
|
||||||
|
| DB Migration | Flyway (scripts in `jeecg-system-start/src/main/resources/flyway/sql/mysql/`) |
|
||||||
|
| JSON | FastJSON 2 |
|
||||||
|
| Excel | AutoPoi (`autopoi-spring-boot-3-starter`) |
|
||||||
|
| API Docs | Knife4j 4.5.0 (OpenAPI v3, `@Schema` annotations) |
|
||||||
|
| Scheduled Jobs | Quartz (JDBC store, clustered) |
|
||||||
|
| File Storage | MinIO / Aliyun OSS / Qiniu (controlled by `jeecg.uploadType` config) |
|
||||||
|
| Microservices | Spring Cloud 2025.0.0 + Alibaba (Nacos, Gateway, Sentinel) |
|
||||||
|
|
||||||
|
## Code Conventions & Patterns
|
||||||
|
|
||||||
|
**Package structure:** `org.jeecg.modules.<module-name>.{controller,entity,mapper,mapper.xml,service,service.impl,vo}`
|
||||||
|
|
||||||
|
**Naming conventions:**
|
||||||
|
- Entities: `Sys` prefix for system entities (e.g., `SysUser`, `SysRole`). Use `@TableName`, `@TableId(type = IdType.ASSIGN_ID)`
|
||||||
|
- Controllers: `<Entity>Controller extends JeecgController<Entity, IService>` — base class provides standard CRUD + Excel import/export
|
||||||
|
- Services: Interface `I<Entity>Service extends IService<Entity>`, impl `<Entity>ServiceImpl extends ServiceImpl<Mapper, Entity>`
|
||||||
|
- Mappers: `<Entity>Mapper extends BaseMapper<Entity>`, with XML in `mapper/xml/`
|
||||||
|
|
||||||
|
**Common annotations on entities:** `@Data`, `@EqualsAndHashCode(callSuper = false)`, `@Accessors(chain = true)`, `@TableName`
|
||||||
|
|
||||||
|
**API response wrapper:** `Result<T>` (from `org.jeecg.common.api.vo.Result`) — use `Result.OK(data)`, `Result.OK(msg, data)`, `Result.error(msg)`. The `result` field holds data, `success`/`code`/`message` hold status.
|
||||||
|
|
||||||
|
**Auto query building:** `QueryGenerator.initQueryWrapper(entity, request.getParameterMap())` auto-builds `QueryWrapper` from HTTP request params, supporting fuzzy match, range queries, etc.
|
||||||
|
|
||||||
|
**Monolithic ↔ Microservices switch:** The `jeecg-system-api` module has two implementations (`local-api` for direct calls, `cloud-api` for Feign). Switching is done by changing the dependency in the startup module, not by modifying business code.
|
||||||
|
|
||||||
|
**代码修改痕迹日志:** 所有新增或修改的代码块必须用 `update-begin` / `update-end` 注释包裹,格式如下:
|
||||||
|
|
||||||
|
```java
|
||||||
|
//update-begin---author:作者 ---date:YYYY-MM-DD for:【bug号/需求号】修改说明-----------
|
||||||
|
// 新增或修改的代码
|
||||||
|
//update-end---author:作者 ---date:YYYY-MM-DD for:【bug号/需求号】修改说明-----------
|
||||||
|
```
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- `author` 填实际修改人,`date` 填修改日期(格式 `YYYY-MM-DD`),`for` 填 bug 号或需求号 + 简要说明
|
||||||
|
- 新增方法:`update-begin` 放在方法声明前,`update-end` 放在方法结束 `}` 后
|
||||||
|
- 修改已有方法中的代码:`update-begin` / `update-end` 只包裹被修改的代码段,不包裹整个方法
|
||||||
|
- 用户未提供 bug 号时,需要主动询问
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
**Supported:** MySQL 8.0+ (default), PostgreSQL, Oracle 11g+, SQL Server 2017+, MariaDB, DM8 (达梦), KingBase ES. Database-specific configs are in `application-{dbtype}.yml` profiles.
|
||||||
|
|
||||||
|
**Initial setup:** Import `db/jeecgboot-mysql-5.7.sql` for the base schema. Flyway handles incremental migrations (scripts organized by date folders like `202512/`).
|
||||||
|
|
||||||
|
**Flyway note:** In dev mode, `spring.main.lazy-initialization=true` is enabled for startup speed, which can interfere with Flyway auto-config. Flyway auto-config is explicitly excluded and managed separately.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Main config files are in `jeecg-module-system/jeecg-system-start/src/main/resources/`:
|
||||||
|
- `application.yml` — profile selector (active profile set by Maven: dev/test/prod/docker)
|
||||||
|
- `application-dev.yml` — development config (port 8080, lazy-init enabled)
|
||||||
|
- Dev environment requires: MySQL, Redis. Optional: MongoDB, RabbitMQ
|
||||||
|
|
||||||
|
Key config namespace: `jeecg.*` in YAML controls platform features (upload type, firewall settings, AI config, MinIO, shiro excludes, etc.).
|
||||||
|
|
||||||
|
## Docker Services (docker-compose.yml)
|
||||||
|
|
||||||
|
MySQL (port 13306), Redis, PostgreSQL+pgvector, MongoDB, and the application container (port 8080).
|
||||||
|
|
||||||
|
## Online 低代码模块 (jeecg-boot-module-online)
|
||||||
|
|
||||||
|
Online 模块采用**元数据驱动**架构,通过数据库配置表(`onl_cgform_*`)实现运行时 CRUD,无需生成代码。配置存在数据库中而非文件系统,Claude Code 无法直接读取具体表单配置,需用户提供 JSON 导出或截图。
|
||||||
|
|
||||||
|
**完整的配置 Schema、控件类型、默认值语法、增强机制等详见: [online-form-schema.md](online-form-schema.md)**
|
||||||
@@ -11,9 +11,9 @@ menu_type=VALUES(menu_type), perms=VALUES(perms), perms_type=VALUES(perms_type),
|
|||||||
is_route=VALUES(is_route), is_leaf=VALUES(is_leaf), hidden=VALUES(hidden), status=VALUES(status), del_flag=VALUES(del_flag),
|
is_route=VALUES(is_route), is_leaf=VALUES(is_leaf), hidden=VALUES(hidden), status=VALUES(status), del_flag=VALUES(del_flag),
|
||||||
always_show=VALUES(always_show), keep_alive=VALUES(keep_alive), internal_or_external=VALUES(internal_or_external);
|
always_show=VALUES(always_show), keep_alive=VALUES(keep_alive), internal_or_external=VALUES(internal_or_external);
|
||||||
|
|
||||||
-- 二级菜单:物料信息
|
-- 二级菜单:胶料信息
|
||||||
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no, is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time)
|
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no, is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time)
|
||||||
VALUES ('1860000000000000011', '1860000000000000001', '物料信息', '/mes/materialinfo', 'mes/materialinfo/index', 'MesMaterialList', 1, NULL, '1', 1, 1, 1, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000011', '1860000000000000001', '胶料信息', '/mes/materialinfo', 'mes/materialinfo/index', 'MesMaterialList', 1, NULL, '1', 1, 1, 1, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
parent_id=VALUES(parent_id), name=VALUES(name), url=VALUES(url), component=VALUES(component), component_name=VALUES(component_name),
|
parent_id=VALUES(parent_id), name=VALUES(name), url=VALUES(url), component=VALUES(component), component_name=VALUES(component_name),
|
||||||
menu_type=VALUES(menu_type), perms=VALUES(perms), perms_type=VALUES(perms_type), sort_no=VALUES(sort_no),
|
menu_type=VALUES(menu_type), perms=VALUES(perms), perms_type=VALUES(perms_type), sort_no=VALUES(sort_no),
|
||||||
|
|||||||
@@ -32,19 +32,19 @@ VALUES ('1860000000000000060', @mes_base_pid, '工序管理', '/xslmes/mesXslPro
|
|||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000061', '1860000000000000060', '新增', 2, 'mes:mes_process_operation:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000061', '1860000000000000060', '新增', 2, 'mes:mes_process_operation:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000062', '1860000000000000060', '编辑', 2, 'mes:mes_process_operation:edit', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000062', '1860000000000000060', '编辑', 2, 'mes:mes_process_operation:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000063', '1860000000000000060', '删除', 2, 'mes:mes_process_operation:delete', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000063', '1860000000000000060', '删除', 2, 'mes:mes_process_operation:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000064', '1860000000000000060', '批量删除', 2, 'mes:mes_process_operation:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000064', '1860000000000000060', '批量删除', 2, 'mes:mes_process_operation:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000065', '1860000000000000060', '导出', 2, 'mes:mes_process_operation:exportXls', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000065', '1860000000000000060', '导出', 2, 'mes:mes_process_operation:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000066', '1860000000000000060', '导入', 2, 'mes:mes_process_operation:importExcel', '1', '1', 0, 'admin', NOW())
|
('1860000000000000066', '1860000000000000060', '导入', 2, 'mes:mes_process_operation:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
|||||||
92
jeecg-boot/db/mes-xsl-day-tank-material-map-menu.sql
Normal file
92
jeecg-boot/db/mes-xsl-day-tank-material-map-menu.sql
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
-- 日罐物料对应信息菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099311', @mixer_parent_id, '日罐物料对应信息',
|
||||||
|
'/mes/daytankmaterialmapinfo',
|
||||||
|
'mes/daytankmaterialmapinfo/index',
|
||||||
|
'MesXslDayTankMaterialMapList', 1, NULL, '1', 31,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099312', '1860000000000099311', '新增', 2, 'xslmes:mes_xsl_day_tank_material_map:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099313', '1860000000000099311', '编辑', 2, 'xslmes:mes_xsl_day_tank_material_map:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099314', '1860000000000099311', '删除', 2, 'xslmes:mes_xsl_day_tank_material_map:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099315', '1860000000000099311', '批量删除', 2, 'xslmes:mes_xsl_day_tank_material_map:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099316', '1860000000000099311', '导出', 2, 'xslmes:mes_xsl_day_tank_material_map:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099311',
|
||||||
|
'1860000000000099312', '1860000000000099313', '1860000000000099314', '1860000000000099315', '1860000000000099316'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确(避免历史错误路径导致“查看组件引用是否正确”)
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/daytankmaterialmapinfo',
|
||||||
|
component = 'mes/daytankmaterialmapinfo/index',
|
||||||
|
component_name = 'MesXslDayTankMaterialMapList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099311';
|
||||||
82
jeecg-boot/db/mes-xsl-downtime-main-type-menu-permission.sql
Normal file
82
jeecg-boot/db/mes-xsl-downtime-main-type-menu-permission.sql
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
-- MES 停机主类型:字典 + 建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀与 Controller、前端 v-auth 一致:mes:mes_xsl_downtime_main_type:*
|
||||||
|
-- 父菜单:MES基础资料 / MES资料;修改租户改 SET @mes_tenant_id
|
||||||
|
-- 新环境也可依赖 Flyway:V3.9.2_64__mes_xsl_downtime_main_type.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES停机主类型是否启用', 'xslmes_downtime_main_type_status', '0启用1停用', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_downtime_main_type_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '启用', '0', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_downtime_main_type_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '停用', '1', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_downtime_main_type_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_downtime_main_type` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序 mes_xsl_process_operation.id',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`downtime_type` varchar(500) NOT NULL COMMENT '停机类型(同租户未删除数据中唯一)',
|
||||||
|
`display_order` int NOT NULL DEFAULT '0' COMMENT '显示顺序(升序)',
|
||||||
|
`distinguish_color` varchar(500) DEFAULT NULL COMMENT '区分颜色(十六进制色值,如#1890ff)',
|
||||||
|
`status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否启用(字典xslmes_downtime_main_type_status:0启用1停用)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mdmt_tenant_type` (`tenant_id`, `downtime_type`),
|
||||||
|
KEY `idx_mdmt_process` (`process_operation_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES停机主类型';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000119', @mes_equip_pid, '停机主类型', '/xslmes/mesXslDowntimeMainType', 'xslmes/mesXslDowntimeMainType/MesXslDowntimeMainTypeList', NULL, 1, NULL, '1', 9, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:partition-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:partition-outlined' WHERE `id` = '1860000000000000119' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000120', '1860000000000000119', '新增', 2, 'mes:mes_xsl_downtime_main_type:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000121', '1860000000000000119', '编辑', 2, 'mes:mes_xsl_downtime_main_type:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000122', '1860000000000000119', '删除', 2, 'mes:mes_xsl_downtime_main_type:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000123', '1860000000000000119', '批量删除', 2, 'mes:mes_xsl_downtime_main_type:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000124', '1860000000000000119', '导出', 2, 'mes:mes_xsl_downtime_main_type:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000125', '1860000000000000119', '导入', 2, 'mes:mes_xsl_downtime_main_type:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000119',
|
||||||
|
'1860000000000000120', '1860000000000000121', '1860000000000000122', '1860000000000000123',
|
||||||
|
'1860000000000000124', '1860000000000000125'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
22
jeecg-boot/db/mes-xsl-downtime-main-type.sql
Normal file
22
jeecg-boot/db/mes-xsl-downtime-main-type.sql
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-- 仅建表 mes_xsl_downtime_main_type。完整初始化请执行 mes-xsl-downtime-main-type-menu-permission.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_downtime_main_type` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序 mes_xsl_process_operation.id',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`downtime_type` varchar(500) NOT NULL COMMENT '停机类型(同租户未删除数据中唯一)',
|
||||||
|
`display_order` int NOT NULL DEFAULT '0' COMMENT '显示顺序(升序)',
|
||||||
|
`distinguish_color` varchar(500) DEFAULT NULL COMMENT '区分颜色(十六进制色值,如#1890ff)',
|
||||||
|
`status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否启用(字典xslmes_downtime_main_type_status:0启用1停用)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mdmt_tenant_type` (`tenant_id`, `downtime_type`),
|
||||||
|
KEY `idx_mdmt_process` (`process_operation_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES停机主类型';
|
||||||
82
jeecg-boot/db/mes-xsl-downtime-record-menu-permission.sql
Normal file
82
jeecg-boot/db/mes-xsl-downtime-record-menu-permission.sql
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
-- MES 停机记录:建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀:mes:mes_xsl_downtime_record:*
|
||||||
|
-- 依赖:mes_xsl_equipment_ledger、mes_xsl_downtime_type;父菜单 设备管理
|
||||||
|
-- Flyway:V3.9.2_117__mes_xsl_downtime_record.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_downtime_record` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`equipment_ledger_id` varchar(32) NOT NULL COMMENT '设备台账主键 mes_xsl_equipment_ledger.id',
|
||||||
|
`equipment_code` varchar(500) DEFAULT NULL COMMENT '设备编号冗余',
|
||||||
|
`equipment_name` varchar(500) DEFAULT NULL COMMENT '设备名称冗余',
|
||||||
|
`downtime_type_id` varchar(32) NOT NULL COMMENT '停机类型主键 mes_xsl_downtime_type.id',
|
||||||
|
`downtime_type_name` varchar(500) DEFAULT NULL COMMENT '停机类型名称冗余',
|
||||||
|
`start_time` datetime NOT NULL COMMENT '开始时间',
|
||||||
|
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||||||
|
`equipment_part_id` varchar(32) DEFAULT NULL COMMENT '设备部位主键 mes_xsl_equipment_part.id',
|
||||||
|
`equipment_part_name` varchar(500) DEFAULT NULL COMMENT '设备部位名称冗余',
|
||||||
|
`inspect_maintain_item_id` varchar(32) DEFAULT NULL COMMENT '点检及保养项目主键 mes_xsl_inspect_maintain_item.id',
|
||||||
|
`inspect_maintain_item_name` varchar(500) DEFAULT NULL COMMENT '点检项目名称冗余',
|
||||||
|
`maintenance_result` varchar(500) DEFAULT NULL COMMENT '维修结果',
|
||||||
|
`maintainer_user_id` varchar(32) DEFAULT NULL COMMENT '维修人用户ID',
|
||||||
|
`maintainer_username` varchar(500) DEFAULT NULL COMMENT '维修人账号',
|
||||||
|
`maintainer_realname` varchar(500) DEFAULT NULL COMMENT '维修人姓名',
|
||||||
|
`maintenance_filled_flag` varchar(1) DEFAULT '0' COMMENT '是否已录入维修结果(字典yn:1是0否)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mdr_equipment` (`equipment_ledger_id`),
|
||||||
|
KEY `idx_mdr_downtime_type` (`downtime_type_id`),
|
||||||
|
KEY `idx_mdr_start_time` (`start_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES停机记录';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000201', @mes_equip_pid, '停机记录', '/xslmes/mesXslDowntimeRecord', 'xslmes/mesXslDowntimeRecord/MesXslDowntimeRecordList', 'MesXslDowntimeRecordList', 1, NULL, '1', 12, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:history-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:history-outlined' WHERE `id` = '1860000000000000201' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000202', '1860000000000000201', '新增', 2, 'mes:mes_xsl_downtime_record:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000203', '1860000000000000201', '编辑', 2, 'mes:mes_xsl_downtime_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000204', '1860000000000000201', '删除', 2, 'mes:mes_xsl_downtime_record:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000205', '1860000000000000201', '批量删除', 2, 'mes:mes_xsl_downtime_record:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000206', '1860000000000000201', '导出', 2, 'mes:mes_xsl_downtime_record:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000207', '1860000000000000201', '导入', 2, 'mes:mes_xsl_downtime_record:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000201',
|
||||||
|
'1860000000000000202', '1860000000000000203', '1860000000000000204', '1860000000000000205',
|
||||||
|
'1860000000000000206', '1860000000000000207'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
90
jeecg-boot/db/mes-xsl-downtime-type-menu-permission.sql
Normal file
90
jeecg-boot/db/mes-xsl-downtime-type-menu-permission.sql
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
-- MES 停机类型:字典 + 建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀:mes:mes_xsl_downtime_type:*
|
||||||
|
-- 依赖:mes_xsl_process_operation、mes_xsl_downtime_main_type;父菜单 MES基础资料
|
||||||
|
-- Flyway:V3.9.2_65__mes_xsl_downtime_type.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
-- 责任区分、故障等级为手填,不初始化字典;已上线库请执行 mes-xsl-downtime-type-remove-unused-dict.sql
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES停机类型是否启用', 'xslmes_downtime_type_status', '0启用1停用', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_downtime_type_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '启用', '0', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_downtime_type_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '停用', '1', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_downtime_type_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_downtime_type` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序 mes_xsl_process_operation.id',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`downtime_main_type_id` varchar(32) NOT NULL COMMENT '所属主类型 mes_xsl_downtime_main_type.id',
|
||||||
|
`downtime_main_type_name` varchar(500) DEFAULT NULL COMMENT '主类型名称冗余(停机主类型.downtime_type)',
|
||||||
|
`downtime_type` varchar(500) NOT NULL COMMENT '停机类型(同租户未删除数据中唯一)',
|
||||||
|
`display_order` int NOT NULL DEFAULT '0' COMMENT '显示顺序(升序,必填)',
|
||||||
|
`distinguish_color` varchar(500) DEFAULT NULL COMMENT '区分颜色(十六进制色值)',
|
||||||
|
`responsibility_distinct` varchar(500) DEFAULT NULL COMMENT '责任区分(手填)',
|
||||||
|
`fault_level` varchar(500) DEFAULT NULL COMMENT '故障等级(手填)',
|
||||||
|
`downtime_maintenance` varchar(1) DEFAULT '0' COMMENT '是否停机维修(字典yn:1是0否)',
|
||||||
|
`status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否启用(字典xslmes_downtime_type_status:0启用1停用)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mdt_tenant_type` (`tenant_id`, `downtime_type`),
|
||||||
|
KEY `idx_mdt_process` (`process_operation_id`),
|
||||||
|
KEY `idx_mdt_main_type` (`downtime_main_type_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES停机类型';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000126', @mes_equip_pid, '停机类型', '/xslmes/mesXslDowntimeType', 'xslmes/mesXslDowntimeType/MesXslDowntimeTypeList', NULL, 1, NULL, '1', 10, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:pause-circle-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:pause-circle-outlined' WHERE `id` = '1860000000000000126' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000127', '1860000000000000126', '新增', 2, 'mes:mes_xsl_downtime_type:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000128', '1860000000000000126', '编辑', 2, 'mes:mes_xsl_downtime_type:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000129', '1860000000000000126', '删除', 2, 'mes:mes_xsl_downtime_type:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000130', '1860000000000000126', '批量删除', 2, 'mes:mes_xsl_downtime_type:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000131', '1860000000000000126', '导出', 2, 'mes:mes_xsl_downtime_type:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000132', '1860000000000000126', '导入', 2, 'mes:mes_xsl_downtime_type:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000126',
|
||||||
|
'1860000000000000127', '1860000000000000128', '1860000000000000129', '1860000000000000130',
|
||||||
|
'1860000000000000131', '1860000000000000132'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
11
jeecg-boot/db/mes-xsl-downtime-type-remove-unused-dict.sql
Normal file
11
jeecg-boot/db/mes-xsl-downtime-type-remove-unused-dict.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- 删除停机类型模块中已废弃的字典(责任区分、故障等级已改为手填,不再使用下拉字典)
|
||||||
|
-- 保留:xslmes_downtime_type_status(是否启用)、xslmes_downtime_main_type_status(停机主类型是否启用)
|
||||||
|
-- 执行后请在系统管理刷新字典缓存,或重新登录
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
DELETE i FROM `sys_dict_item` i
|
||||||
|
INNER JOIN `sys_dict` d ON i.`dict_id` = d.`id`
|
||||||
|
WHERE d.`dict_code` IN ('xslmes_downtime_responsibility', 'xslmes_downtime_fault_level');
|
||||||
|
|
||||||
|
DELETE FROM `sys_dict`
|
||||||
|
WHERE `dict_code` IN ('xslmes_downtime_responsibility', 'xslmes_downtime_fault_level');
|
||||||
28
jeecg-boot/db/mes-xsl-downtime-type.sql
Normal file
28
jeecg-boot/db/mes-xsl-downtime-type.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-- 仅建表 mes_xsl_downtime_type。完整初始化请执行 mes-xsl-downtime-type-menu-permission.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_downtime_type` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序 mes_xsl_process_operation.id',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`downtime_main_type_id` varchar(32) NOT NULL COMMENT '所属主类型 mes_xsl_downtime_main_type.id',
|
||||||
|
`downtime_main_type_name` varchar(500) DEFAULT NULL COMMENT '主类型名称冗余(停机主类型.downtime_type)',
|
||||||
|
`downtime_type` varchar(500) NOT NULL COMMENT '停机类型(同租户未删除数据中唯一)',
|
||||||
|
`display_order` int NOT NULL DEFAULT '0' COMMENT '显示顺序(升序,必填)',
|
||||||
|
`distinguish_color` varchar(500) DEFAULT NULL COMMENT '区分颜色(十六进制色值)',
|
||||||
|
`responsibility_distinct` varchar(500) DEFAULT NULL COMMENT '责任区分(手填)',
|
||||||
|
`fault_level` varchar(500) DEFAULT NULL COMMENT '故障等级(手填)',
|
||||||
|
`downtime_maintenance` varchar(1) DEFAULT '0' COMMENT '是否停机维修(字典yn:1是0否)',
|
||||||
|
`status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否启用(字典xslmes_downtime_type_status:0启用1停用)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mdt_tenant_type` (`tenant_id`, `downtime_type`),
|
||||||
|
KEY `idx_mdt_process` (`process_operation_id`),
|
||||||
|
KEY `idx_mdt_main_type` (`downtime_main_type_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES停机类型';
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
-- MES 设备点检配置(主子表):建表 + 菜单 + 按钮 + 租户 admin 授权
|
||||||
|
-- 权限前缀:mes:mes_xsl_equip_inspect_config:*
|
||||||
|
-- 父菜单:设备管理;类型字典复用 xslmes_im_item_category(须先有点检及保养项目功能)
|
||||||
|
-- Flyway:V3.9.2_78__mes_xsl_equip_inspect_config.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equip_inspect_config` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`equipment_ledger_id` varchar(32) NOT NULL COMMENT '设备台账主键 mes_xsl_equipment_ledger.id',
|
||||||
|
`equipment_name` varchar(500) DEFAULT NULL COMMENT '设备名称冗余',
|
||||||
|
`equipment_code` varchar(500) DEFAULT NULL COMMENT '设备编号冗余',
|
||||||
|
`config_type` varchar(500) NOT NULL COMMENT '配置类型(字典xslmes_im_item_category:inspect点检/maintain保养;同设备同类型唯一)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_meic_tenant_equip_type` (`tenant_id`, `equipment_ledger_id`, `config_type`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES设备点检配置';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equip_inspect_config_line` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`config_id` varchar(32) NOT NULL COMMENT '主表主键 mes_xsl_equip_inspect_config.id',
|
||||||
|
`inspect_maintain_item_id` varchar(32) NOT NULL COMMENT '点检及保养项目主键 mes_xsl_inspect_maintain_item.id',
|
||||||
|
`item_code` varchar(500) DEFAULT NULL COMMENT '点检项目编号冗余',
|
||||||
|
`item_name` varchar(500) DEFAULT NULL COMMENT '项目名称冗余',
|
||||||
|
`item_category` varchar(500) DEFAULT NULL COMMENT '项目类别冗余',
|
||||||
|
`item_type` varchar(500) DEFAULT NULL COMMENT '项目类型冗余',
|
||||||
|
`equipment_part_name` varchar(500) DEFAULT NULL COMMENT '设备部位名称冗余',
|
||||||
|
`equipment_sub_part_name` varchar(500) DEFAULT NULL COMMENT '设备小部位名称冗余',
|
||||||
|
`inspect_method` varchar(500) DEFAULT NULL COMMENT '点检方式冗余',
|
||||||
|
`judgment_criteria` varchar(500) DEFAULT NULL COMMENT '判断基准冗余',
|
||||||
|
`sort_no` int DEFAULT '0' COMMENT '排序号',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_meicl_config` (`config_id`),
|
||||||
|
KEY `idx_meicl_item` (`inspect_maintain_item_id`),
|
||||||
|
UNIQUE KEY `uk_meicl_config_item` (`config_id`, `inspect_maintain_item_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES设备点检配置明细';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000148', @mes_equip_pid, '设备点检配置', '/xslmes/mesXslEquipInspectConfig', 'xslmes/mesXslEquipInspectConfig/MesXslEquipInspectConfigList', NULL, 1, NULL, '1', 12, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:control-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:control-outlined' WHERE `id` = '1860000000000000148' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000149', '1860000000000000148', '新增', 2, 'mes:mes_xsl_equip_inspect_config:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000150', '1860000000000000148', '编辑', 2, 'mes:mes_xsl_equip_inspect_config:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000151', '1860000000000000148', '删除', 2, 'mes:mes_xsl_equip_inspect_config:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000152', '1860000000000000148', '批量删除', 2, 'mes:mes_xsl_equip_inspect_config:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000153', '1860000000000000148', '导出', 2, 'mes:mes_xsl_equip_inspect_config:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000154', '1860000000000000148', '导入', 2, 'mes:mes_xsl_equip_inspect_config:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000148',
|
||||||
|
'1860000000000000149', '1860000000000000150', '1860000000000000151', '1860000000000000152',
|
||||||
|
'1860000000000000153', '1860000000000000154'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
138
jeecg-boot/db/mes-xsl-equip-inspect-record-menu-permission.sql
Normal file
138
jeecg-boot/db/mes-xsl-equip-inspect-record-menu-permission.sql
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
-- MES 点检/保养记录(主子表):建表 + 字典 + 菜单 + 按钮 + 租户 admin 授权
|
||||||
|
-- 权限前缀:mes:mes_xsl_equip_inspect_record:*
|
||||||
|
-- 父菜单:设备管理;依赖设备点检配置、设备台账
|
||||||
|
-- Flyway:V3.9.2_79__mes_xsl_equip_inspect_record.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict`(`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES点检记录结果', 'xslmes_im_inspect_result', '合格/不合格', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_im_inspect_result' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.`id`, '合格', 'pass', '', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_inspect_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.`id` AND i.`item_value` = 'pass');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.`id`, '不合格', 'fail', '', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_inspect_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.`id` AND i.`item_value` = 'fail');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict`(`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES点检记录状态', 'xslmes_im_record_status', '待点检/已点检', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_im_record_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.`id`, '待点检', 'pending', '', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_record_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.`id` AND i.`item_value` = 'pending');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.`id`, '已点检', 'done', '', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_record_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.`id` AND i.`item_value` = 'done');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equip_inspect_record` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`record_no` varchar(32) NOT NULL COMMENT '记录编号(EC+yyyyMMdd+4位流水,租户内按日递增)',
|
||||||
|
`plan_no` varchar(500) DEFAULT NULL COMMENT '计划单号',
|
||||||
|
`plan_id` varchar(32) DEFAULT NULL COMMENT '计划主键(隐藏)',
|
||||||
|
`equipment_ledger_id` varchar(32) NOT NULL COMMENT '设备台账主键 mes_xsl_equipment_ledger.id',
|
||||||
|
`equipment_code` varchar(500) DEFAULT NULL COMMENT '设备编码冗余',
|
||||||
|
`equipment_name` varchar(500) DEFAULT NULL COMMENT '设备名称冗余',
|
||||||
|
`equip_inspect_config_id` varchar(32) DEFAULT NULL COMMENT '设备点检配置主键 mes_xsl_equip_inspect_config.id',
|
||||||
|
`record_type` varchar(500) NOT NULL COMMENT '类型(字典xslmes_im_item_category:inspect点检/maintain保养)',
|
||||||
|
`inspect_date` date DEFAULT NULL COMMENT '点检日期',
|
||||||
|
`inspector_user_id` varchar(32) DEFAULT NULL COMMENT '点检人用户ID',
|
||||||
|
`inspector_username` varchar(500) DEFAULT NULL COMMENT '点检人账号',
|
||||||
|
`inspector_realname` varchar(500) DEFAULT NULL COMMENT '点检人姓名',
|
||||||
|
`inspect_result` varchar(500) NOT NULL COMMENT '点检结果(字典xslmes_im_inspect_result:pass合格/fail不合格)',
|
||||||
|
`record_status` varchar(500) NOT NULL COMMENT '状态(字典xslmes_im_record_status:pending待点检/done已点检)',
|
||||||
|
`handled_flag` varchar(1) DEFAULT NULL COMMENT '是否已处理(字典yn:1是0否,仅不合格记录使用)',
|
||||||
|
`handler_user_id` varchar(32) DEFAULT NULL COMMENT '处理人用户ID',
|
||||||
|
`handler_username` varchar(500) DEFAULT NULL COMMENT '处理人账号',
|
||||||
|
`handler_realname` varchar(500) DEFAULT NULL COMMENT '处理人姓名',
|
||||||
|
`handle_time` datetime DEFAULT NULL COMMENT '处理时间',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_meir_tenant_record_no` (`tenant_id`, `record_no`),
|
||||||
|
KEY `idx_meir_equip_type` (`equipment_ledger_id`, `record_type`),
|
||||||
|
KEY `idx_meir_inspect_date` (`inspect_date`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES点检保养记录';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equip_inspect_record_line` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`record_id` varchar(32) NOT NULL COMMENT '主表主键 mes_xsl_equip_inspect_record.id',
|
||||||
|
`equip_inspect_config_line_id` varchar(32) NOT NULL COMMENT '设备点检配置明细主键 mes_xsl_equip_inspect_config_line.id',
|
||||||
|
`inspect_maintain_item_id` varchar(32) DEFAULT NULL COMMENT '点检及保养项目主键冗余',
|
||||||
|
`item_code` varchar(500) DEFAULT NULL COMMENT '点检项目编号冗余',
|
||||||
|
`item_name` varchar(500) DEFAULT NULL COMMENT '项目名称冗余',
|
||||||
|
`item_category` varchar(500) DEFAULT NULL COMMENT '项目类别冗余',
|
||||||
|
`item_type` varchar(500) DEFAULT NULL COMMENT '项目类型冗余',
|
||||||
|
`equipment_part_name` varchar(500) DEFAULT NULL COMMENT '设备部位冗余',
|
||||||
|
`equipment_sub_part_name` varchar(500) DEFAULT NULL COMMENT '设备小部位冗余',
|
||||||
|
`inspect_method` varchar(500) DEFAULT NULL COMMENT '点检方式冗余',
|
||||||
|
`judgment_criteria` varchar(500) DEFAULT NULL COMMENT '判断基准冗余',
|
||||||
|
`line_inspect_result` varchar(500) DEFAULT NULL COMMENT '明细点检结果(文本)',
|
||||||
|
`picture_files` varchar(2000) DEFAULT NULL COMMENT '图片(上传路径,逗号分隔)',
|
||||||
|
`sort_no` int DEFAULT '0' COMMENT '排序号',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_meirl_record` (`record_id`),
|
||||||
|
KEY `idx_meirl_config_line` (`equip_inspect_config_line_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES点检保养记录明细';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000155', @mes_equip_pid, '点检保养记录', '/xslmes/mesXslEquipInspectRecord', 'xslmes/mesXslEquipInspectRecord/MesXslEquipInspectRecordList', 'MesXslEquipInspectRecordList', 1, NULL, '1', 13, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:file-done-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:file-done-outlined' WHERE `id` = '1860000000000000155' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000156', '1860000000000000155', '新增', 2, 'mes:mes_xsl_equip_inspect_record:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000157', '1860000000000000155', '编辑', 2, 'mes:mes_xsl_equip_inspect_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000158', '1860000000000000155', '删除', 2, 'mes:mes_xsl_equip_inspect_record:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000159', '1860000000000000155', '批量删除', 2, 'mes:mes_xsl_equip_inspect_record:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000160', '1860000000000000155', '导出', 2, 'mes:mes_xsl_equip_inspect_record:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000161', '1860000000000000155', '导入', 2, 'mes:mes_xsl_equip_inspect_record:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000155',
|
||||||
|
'1860000000000000156', '1860000000000000157', '1860000000000000158', '1860000000000000159',
|
||||||
|
'1860000000000000160', '1860000000000000161'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -56,30 +56,31 @@ WHERE d.`dict_code` = 'xslmes_equipment_maintain_distinct'
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000070', @mes_base_pid, '设备类别', '/xslmes/mesXslEquipmentCategory', 'xslmes/mesXslEquipmentCategory/MesXslEquipmentCategoryList', NULL, 1, NULL, '1', 10, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000070', @mes_equip_pid, '设备类别', '/xslmes/mesXslEquipmentCategory', 'xslmes/mesXslEquipmentCategory/MesXslEquipmentCategoryList', NULL, 1, NULL, '1', 2, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000071', '1860000000000000070', '新增', 2, 'mes:mes_xsl_equipment_category:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000071', '1860000000000000070', '新增', 2, 'mes:mes_xsl_equipment_category:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000072', '1860000000000000070', '编辑', 2, 'mes:mes_xsl_equipment_category:edit', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000072', '1860000000000000070', '编辑', 2, 'mes:mes_xsl_equipment_category:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000073', '1860000000000000070', '删除', 2, 'mes:mes_xsl_equipment_category:delete', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000073', '1860000000000000070', '删除', 2, 'mes:mes_xsl_equipment_category:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000074', '1860000000000000070', '批量删除', 2, 'mes:mes_xsl_equipment_category:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000074', '1860000000000000070', '批量删除', 2, 'mes:mes_xsl_equipment_category:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000075', '1860000000000000070', '导出', 2, 'mes:mes_xsl_equipment_category:exportXls', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000075', '1860000000000000070', '导出', 2, 'mes:mes_xsl_equipment_category:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
('1860000000000000076', '1860000000000000070', '导入', 2, 'mes:mes_xsl_equipment_category:importExcel', '1', '1', 0, 'admin', NOW())
|
('1860000000000000076', '1860000000000000070', '导入', 2, 'mes:mes_xsl_equipment_category:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
|||||||
31
jeecg-boot/db/mes-xsl-equipment-ledger-menu-parent-fix.sql
Normal file
31
jeecg-boot/db/mes-xsl-equipment-ledger-menu-parent-fix.sql
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-- 设备管理目录:从 MES管理 下迁出,与 MES基础资料 同级(已执行旧版 mes-xsl-equipment-ledger-menu-permission 时用)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_equip_root_parent = (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES基础资料'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_root_parent = IFNULL(@mes_equip_root_parent, (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES资料'
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
|
||||||
|
SET @mes_equip_root_sort = IFNULL((
|
||||||
|
SELECT `sort_no` + 1 FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
||||||
|
ORDER BY `sort_no` DESC
|
||||||
|
LIMIT 1
|
||||||
|
), 51);
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET
|
||||||
|
`parent_id` = @mes_equip_root_parent,
|
||||||
|
`sort_no` = @mes_equip_root_sort,
|
||||||
|
`menu_type` = 0,
|
||||||
|
`is_leaf` = 0,
|
||||||
|
`hidden` = 0,
|
||||||
|
`status` = '1',
|
||||||
|
`del_flag` = 0
|
||||||
|
WHERE `id` = '1860000000000000133';
|
||||||
131
jeecg-boot/db/mes-xsl-equipment-ledger-menu-permission.sql
Normal file
131
jeecg-boot/db/mes-xsl-equipment-ledger-menu-permission.sql
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
-- MES 设备台账:字典 + 建表 + 设备管理目录菜单 + 设备台账菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀:mes:mes_xsl_equipment_ledger:*
|
||||||
|
-- 父菜单:设备管理(目录,与 MES基础资料 同级,非 MES管理 子级);子菜单:设备台账
|
||||||
|
-- 修改租户:改 SET @mes_tenant_id;新环境也可依赖 Flyway V3.9.2_72__mes_xsl_equipment_ledger.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES设备台账状态', 'xslmes_equipment_ledger_status', '在用/停用/报废', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_equipment_ledger_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '在用', '0', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_equipment_ledger_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '停用', '1', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_equipment_ledger_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '报废', '2', 3, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_equipment_ledger_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '2');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equipment_ledger` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序 mes_xsl_process_operation.id',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`equipment_name` varchar(500) NOT NULL COMMENT '设备名称(同租户未删除唯一)',
|
||||||
|
`equipment_code` varchar(128) NOT NULL COMMENT '设备编号(同租户未删除唯一)',
|
||||||
|
`manufacturer_id` varchar(32) DEFAULT NULL COMMENT '所属设备厂家 mes_xsl_manufacturer.id',
|
||||||
|
`manufacturer_name` varchar(500) DEFAULT NULL COMMENT '设备厂家名称冗余',
|
||||||
|
`equipment_category_id` varchar(32) DEFAULT NULL COMMENT '设备类别 mes_xsl_equipment_category.id',
|
||||||
|
`equipment_category_name` varchar(500) DEFAULT NULL COMMENT '设备类别名称冗余',
|
||||||
|
`equipment_type_id` varchar(32) DEFAULT NULL COMMENT '设备类型 mes_xsl_equipment_type.id',
|
||||||
|
`equipment_type_name` varchar(500) DEFAULT NULL COMMENT '设备类型名称冗余',
|
||||||
|
`factory_id` varchar(32) DEFAULT NULL COMMENT '所属工厂(厂家信息)',
|
||||||
|
`factory_name` varchar(500) DEFAULT NULL COMMENT '所属工厂名称冗余',
|
||||||
|
`equipment_model` varchar(500) DEFAULT NULL COMMENT '设备型号',
|
||||||
|
`equipment_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '设备状态 字典xslmes_equipment_ledger_status:0在用1停用2报废',
|
||||||
|
`serial_no` varchar(500) DEFAULT NULL COMMENT '序列号',
|
||||||
|
`nameplate` varchar(500) DEFAULT NULL COMMENT '铭牌',
|
||||||
|
`maintain_dept_id` varchar(32) DEFAULT NULL COMMENT '维修部门 sys_depart.id',
|
||||||
|
`maintain_person` varchar(500) DEFAULT NULL COMMENT '维修人员',
|
||||||
|
`manage_dept_id` varchar(32) DEFAULT NULL COMMENT '主管部门 sys_depart.id',
|
||||||
|
`doc_file_no` varchar(500) DEFAULT NULL COMMENT '资料文件编号',
|
||||||
|
`production_date` date DEFAULT NULL COMMENT '生产日期',
|
||||||
|
`purchase_date` date DEFAULT NULL COMMENT '购买日期',
|
||||||
|
`use_date` date DEFAULT NULL COMMENT '使用日期',
|
||||||
|
`vendor_contact` varchar(500) DEFAULT NULL COMMENT '厂商联系人',
|
||||||
|
`asset_value` varchar(128) DEFAULT NULL COMMENT '价值',
|
||||||
|
`controlled_pda` varchar(500) DEFAULT NULL COMMENT '受控PDA',
|
||||||
|
`overproduction_ratio` varchar(128) DEFAULT NULL COMMENT '超产比率',
|
||||||
|
`effective_volume` varchar(128) DEFAULT NULL COMMENT '有效体积',
|
||||||
|
`enabled_flag` varchar(1) NOT NULL DEFAULT '1' COMMENT '是否启用 字典yn:1是0否',
|
||||||
|
`equipment_desc` varchar(1000) DEFAULT NULL COMMENT '设备描述',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mel_tenant_code` (`tenant_id`, `equipment_code`),
|
||||||
|
KEY `idx_mel_tenant_name` (`tenant_id`, `equipment_name`),
|
||||||
|
KEY `idx_mel_process` (`process_operation_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES设备台账';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
-- 与「MES基础资料」同级:取其 parent_id(通常为 NULL,与 MES管理 并列)
|
||||||
|
SET @mes_equip_root_parent = (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES基础资料'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_root_parent = IFNULL(@mes_equip_root_parent, (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES资料'
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
|
||||||
|
SET @mes_equip_root_sort = IFNULL((
|
||||||
|
SELECT `sort_no` + 1 FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
||||||
|
ORDER BY `sort_no` DESC
|
||||||
|
LIMIT 1
|
||||||
|
), 51);
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `id` = '1860000000000000133' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000133', @mes_equip_root_parent, '设备管理', '/xslmes/equipment', 'layouts/RouteView', 'MesEquipmentLayout', 0, NULL, '1', @mes_equip_root_sort, 1, 0, 0, '1', 0, 0, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `icon` = 'ant-design:tool-outlined', `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:tool-outlined' WHERE `id` = '1860000000000000133' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000134', '1860000000000000133', '设备台账', '/xslmes/mesXslEquipmentLedger', 'xslmes/mesXslEquipmentLedger/MesXslEquipmentLedgerList', 'MesXslEquipmentLedgerList', 1, NULL, '1', 1, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000135', '1860000000000000134', '新增', 2, 'mes:mes_xsl_equipment_ledger:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000136', '1860000000000000134', '编辑', 2, 'mes:mes_xsl_equipment_ledger:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000137', '1860000000000000134', '删除', 2, 'mes:mes_xsl_equipment_ledger:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000138', '1860000000000000134', '批量删除', 2, 'mes:mes_xsl_equipment_ledger:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000139', '1860000000000000134', '导出', 2, 'mes:mes_xsl_equipment_ledger:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000140', '1860000000000000134', '导入', 2, 'mes:mes_xsl_equipment_ledger:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000133',
|
||||||
|
'1860000000000000134',
|
||||||
|
'1860000000000000135', '1860000000000000136', '1860000000000000137', '1860000000000000138',
|
||||||
|
'1860000000000000139', '1860000000000000140'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
46
jeecg-boot/db/mes-xsl-equipment-ledger.sql
Normal file
46
jeecg-boot/db/mes-xsl-equipment-ledger.sql
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
-- MES 设备台账:仅建表(完整初始化请执行 mes-xsl-equipment-ledger-menu-permission.sql)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_equipment_ledger` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`process_operation_id` varchar(32) NOT NULL COMMENT '所属工序',
|
||||||
|
`process_operation_name` varchar(500) DEFAULT NULL COMMENT '工序名称冗余',
|
||||||
|
`equipment_name` varchar(500) NOT NULL COMMENT '设备名称',
|
||||||
|
`equipment_code` varchar(128) NOT NULL COMMENT '设备编号',
|
||||||
|
`manufacturer_id` varchar(32) DEFAULT NULL COMMENT '所属设备厂家',
|
||||||
|
`manufacturer_name` varchar(500) DEFAULT NULL COMMENT '设备厂家名称冗余',
|
||||||
|
`equipment_category_id` varchar(32) DEFAULT NULL COMMENT '设备类别',
|
||||||
|
`equipment_category_name` varchar(500) DEFAULT NULL COMMENT '设备类别名称冗余',
|
||||||
|
`equipment_type_id` varchar(32) DEFAULT NULL COMMENT '设备类型',
|
||||||
|
`equipment_type_name` varchar(500) DEFAULT NULL COMMENT '设备类型名称冗余',
|
||||||
|
`factory_id` varchar(32) DEFAULT NULL COMMENT '所属工厂',
|
||||||
|
`factory_name` varchar(500) DEFAULT NULL COMMENT '所属工厂名称冗余',
|
||||||
|
`equipment_model` varchar(500) DEFAULT NULL COMMENT '设备型号',
|
||||||
|
`equipment_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '设备状态',
|
||||||
|
`serial_no` varchar(500) DEFAULT NULL COMMENT '序列号',
|
||||||
|
`nameplate` varchar(500) DEFAULT NULL COMMENT '铭牌',
|
||||||
|
`maintain_dept_id` varchar(32) DEFAULT NULL COMMENT '维修部门',
|
||||||
|
`maintain_person` varchar(500) DEFAULT NULL COMMENT '维修人员',
|
||||||
|
`manage_dept_id` varchar(32) DEFAULT NULL COMMENT '主管部门',
|
||||||
|
`doc_file_no` varchar(500) DEFAULT NULL COMMENT '资料文件编号',
|
||||||
|
`production_date` date DEFAULT NULL COMMENT '生产日期',
|
||||||
|
`purchase_date` date DEFAULT NULL COMMENT '购买日期',
|
||||||
|
`use_date` date DEFAULT NULL COMMENT '使用日期',
|
||||||
|
`vendor_contact` varchar(500) DEFAULT NULL COMMENT '厂商联系人',
|
||||||
|
`asset_value` varchar(128) DEFAULT NULL COMMENT '价值',
|
||||||
|
`controlled_pda` varchar(500) DEFAULT NULL COMMENT '受控PDA',
|
||||||
|
`overproduction_ratio` varchar(128) DEFAULT NULL COMMENT '超产比率',
|
||||||
|
`effective_volume` varchar(128) DEFAULT NULL COMMENT '有效体积',
|
||||||
|
`enabled_flag` varchar(1) NOT NULL DEFAULT '1' COMMENT '是否启用',
|
||||||
|
`equipment_desc` varchar(1000) DEFAULT NULL COMMENT '设备描述',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mel_tenant_code` (`tenant_id`, `equipment_code`),
|
||||||
|
KEY `idx_mel_tenant_name` (`tenant_id`, `equipment_name`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES设备台账';
|
||||||
10
jeecg-boot/db/mes-xsl-equipment-menus-icon.sql
Normal file
10
jeecg-boot/db/mes-xsl-equipment-menus-icon.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- 设备管理目录及指定子菜单图标(可重复执行)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:tool-outlined' WHERE `id` = '1860000000000000133' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:apartment-outlined' WHERE `id` = '1860000000000000091' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:tags-outlined' WHERE `id` = '1860000000000000098' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:shopping-outlined' WHERE `id` = '1860000000000000105' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:shop-outlined' WHERE `id` = '1860000000000000112' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:partition-outlined' WHERE `id` = '1860000000000000119' AND `del_flag` = 0;
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:pause-circle-outlined' WHERE `id` = '1860000000000000126' AND `del_flag` = 0;
|
||||||
@@ -24,14 +24,15 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_equipment_part` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000084', @mes_base_pid, '设备部位', '/xslmes/mesXslEquipmentPart', 'xslmes/mesXslEquipmentPart/MesXslEquipmentPartList', NULL, 1, NULL, '1', 12, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000084', @mes_equip_pid, '设备部位', '/xslmes/mesXslEquipmentPart', 'xslmes/mesXslEquipmentPart/MesXslEquipmentPartList', NULL, 1, NULL, '1', 4, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
|||||||
@@ -27,19 +27,22 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_equipment_sub_part` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @xslmes_root_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES XSL', 'XSLMES管理', 'XSLMES')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @xslmes_root_pid = IFNULL(@xslmes_root_pid, '1900000000000000300');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000091', @xslmes_root_pid, '设备小部位', '/xslmes/mesXslEquipmentSubPart', 'xslmes/mesXslEquipmentSubPart/MesXslEquipmentSubPartList', NULL, 1, NULL, '1', 14, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000091', @mes_equip_pid, '设备小部位', '/xslmes/mesXslEquipmentSubPart', 'xslmes/mesXslEquipmentSubPart/MesXslEquipmentSubPartList', NULL, 1, NULL, '1', 5, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:apartment-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:apartment-outlined' WHERE `id` = '1860000000000000091' AND `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000092', '1860000000000000091', '新增', 2, 'mes:mes_xsl_equipment_sub_part:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000092', '1860000000000000091', '新增', 2, 'mes:mes_xsl_equipment_sub_part:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
|||||||
65
jeecg-boot/db/mes-xsl-equipment-type-menu-fix.sql
Normal file
65
jeecg-boot/db/mes-xsl-equipment-type-menu-fix.sql
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
-- 设备类型菜单/按钮补全并挂到「设备管理」(解决仅 UPDATE 时 id 077 不存在、菜单与权限均不显示)
|
||||||
|
-- 可重复执行;修改租户改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `id` = @mes_equip_pid;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000077', @mes_equip_pid, '设备类型', '/xslmes/mesXslEquipmentType', 'xslmes/mesXslEquipmentType/MesXslEquipmentTypeList', NULL, 1, NULL, '1', 3, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000078', '1860000000000000077', '新增', 2, 'mes:mes_xsl_equipment_type:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000079', '1860000000000000077', '编辑', 2, 'mes:mes_xsl_equipment_type:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000080', '1860000000000000077', '删除', 2, 'mes:mes_xsl_equipment_type:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000081', '1860000000000000077', '批量删除', 2, 'mes:mes_xsl_equipment_type:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000082', '1860000000000000077', '导出', 2, 'mes:mes_xsl_equipment_type:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000083', '1860000000000000077', '导入', 2, 'mes:mes_xsl_equipment_type:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`),
|
||||||
|
`perms_type` = VALUES(`perms_type`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` p
|
||||||
|
INNER JOIN (SELECT @mes_equip_pid AS `pid`) x ON 1 = 1
|
||||||
|
SET p.`parent_id` = x.`pid`,
|
||||||
|
p.`is_leaf` = 0,
|
||||||
|
p.`url` = '/xslmes/mesXslEquipmentType',
|
||||||
|
p.`component` = 'xslmes/mesXslEquipmentType/MesXslEquipmentTypeList',
|
||||||
|
p.`hidden` = 0,
|
||||||
|
p.`del_flag` = 0,
|
||||||
|
p.`status` = '1',
|
||||||
|
p.`update_time` = NOW()
|
||||||
|
WHERE p.`menu_type` = 1
|
||||||
|
AND p.`url` = '/xslmes/mesXslEquipmentType'
|
||||||
|
AND p.`id` <> '1860000000000000077';
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000077',
|
||||||
|
'1860000000000000078', '1860000000000000079', '1860000000000000080',
|
||||||
|
'1860000000000000081', '1860000000000000082', '1860000000000000083'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -23,14 +23,15 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_equipment_type` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000077', @mes_base_pid, '设备类型', '/xslmes/mesXslEquipmentType', 'xslmes/mesXslEquipmentType/MesXslEquipmentTypeList', NULL, 1, NULL, '1', 11, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000077', @mes_equip_pid, '设备类型', '/xslmes/mesXslEquipmentType', 'xslmes/mesXslEquipmentType/MesXslEquipmentTypeList', NULL, 1, NULL, '1', 3, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
|||||||
92
jeecg-boot/db/mes-xsl-final-batch-plan-menu.sql
Normal file
92
jeecg-boot/db/mes-xsl-final-batch-plan-menu.sql
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
-- 终胶计划菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099711', @mixer_parent_id, '终胶计划',
|
||||||
|
'/mes/finalbatchplaninfo',
|
||||||
|
'mes/finalbatchplaninfo/index',
|
||||||
|
'MesXslFinalBatchPlanList', 1, NULL, '1', 35,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099712', '1860000000000099711', '新增', 2, 'xslmes:mes_xsl_final_batch_plan:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099713', '1860000000000099711', '编辑', 2, 'xslmes:mes_xsl_final_batch_plan:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099714', '1860000000000099711', '删除', 2, 'xslmes:mes_xsl_final_batch_plan:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099715', '1860000000000099711', '批量删除', 2, 'xslmes:mes_xsl_final_batch_plan:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099716', '1860000000000099711', '导出', 2, 'xslmes:mes_xsl_final_batch_plan:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099711',
|
||||||
|
'1860000000000099712', '1860000000000099713', '1860000000000099714', '1860000000000099715', '1860000000000099716'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/finalbatchplaninfo',
|
||||||
|
component = 'mes/finalbatchplaninfo/index',
|
||||||
|
component_name = 'MesXslFinalBatchPlanList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099711';
|
||||||
30
jeecg-boot/db/mes-xsl-final-batch-plan-table.sql
Normal file
30
jeecg-boot/db/mes-xsl-final-batch-plan-table.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-- 终胶计划建表SQL
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_final_batch_plan` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`source_order_id` varchar(32) DEFAULT NULL COMMENT '来源生产订单ID',
|
||||||
|
`order_serial_no` varchar(500) DEFAULT NULL COMMENT '订单流水',
|
||||||
|
`order_no` varchar(500) DEFAULT NULL COMMENT '订单编号',
|
||||||
|
`production_segment_count` int DEFAULT NULL COMMENT '生产段数',
|
||||||
|
`order_date` date DEFAULT NULL COMMENT '订单日期',
|
||||||
|
`material_code` varchar(500) DEFAULT NULL COMMENT '物料编码',
|
||||||
|
`mes_material_name` varchar(500) DEFAULT NULL COMMENT 'MES胶料信息',
|
||||||
|
`plan_weight` decimal(18,4) DEFAULT NULL COMMENT '计划重量',
|
||||||
|
`per_car_weight` decimal(18,4) DEFAULT NULL COMMENT '每车重量',
|
||||||
|
`planned_car_count` int DEFAULT 0 COMMENT '计划车数',
|
||||||
|
`scheduled_car_count` int DEFAULT 0 COMMENT '已排产车数',
|
||||||
|
`finished_car_count` int DEFAULT 0 COMMENT '完成车数',
|
||||||
|
`status` int DEFAULT 0 COMMENT '状态:0未开始 1进行中 2已完成',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门编码',
|
||||||
|
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT 0 COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mxfb_source_order` (`source_order_id`),
|
||||||
|
KEY `idx_mxfb_material_code` (`material_code`),
|
||||||
|
UNIQUE KEY `uk_mxfb_source_order_del` (`source_order_id`, `del_flag`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES终胶计划';
|
||||||
118
jeecg-boot/db/mes-xsl-inspect-maintain-item-menu-permission.sql
Normal file
118
jeecg-boot/db/mes-xsl-inspect-maintain-item-menu-permission.sql
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
-- MES 点检及保养项目:字典 + 建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀与 Controller、前端 v-auth 一致:mes:mes_xsl_inspect_maintain_item:*
|
||||||
|
-- 父菜单:设备管理;修改租户改 SET @mes_tenant_id
|
||||||
|
-- 新环境也可依赖 Flyway:V3.9.2_77__mes_xsl_inspect_maintain_item.sql
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES点检保养项目类别', 'xslmes_im_item_category', 'inspect点检/maintain保养', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_im_item_category' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '点检', 'inspect', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_item_category' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'inspect');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '保养', 'maintain', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_item_category' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'maintain');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES点检保养项目类型', 'xslmes_im_item_type', 'mechanical机械类/electrical电气类', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_im_item_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '机械类', 'mechanical', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_item_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'mechanical');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '电气类', 'electrical', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_item_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'electrical');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES点检方式', 'xslmes_im_inspect_method', 'visual视觉/sight目测/hearing听觉', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_im_inspect_method' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '视觉', 'visual', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_inspect_method' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'visual');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '目测', 'sight', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_inspect_method' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'sight');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '听觉', 'hearing', 3, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_im_inspect_method' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'hearing');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_inspect_maintain_item` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`item_name` varchar(500) NOT NULL COMMENT '项目名称(同租户未删除数据中唯一)',
|
||||||
|
`item_code` varchar(500) NOT NULL COMMENT '项目编号(同租户未删除数据中唯一)',
|
||||||
|
`equipment_category_id` varchar(32) NOT NULL COMMENT '设备类别主键 mes_xsl_equipment_category.id',
|
||||||
|
`equipment_category_name` varchar(500) DEFAULT NULL COMMENT '设备类别名称冗余',
|
||||||
|
`equipment_type_id` varchar(32) NOT NULL COMMENT '设备类型主键 mes_xsl_equipment_type.id',
|
||||||
|
`equipment_type_name` varchar(500) DEFAULT NULL COMMENT '设备类型名称冗余',
|
||||||
|
`equipment_part_id` varchar(32) NOT NULL COMMENT '设备部位主键 mes_xsl_equipment_part.id',
|
||||||
|
`equipment_part_name` varchar(500) DEFAULT NULL COMMENT '设备部位名称冗余',
|
||||||
|
`equipment_sub_part_id` varchar(32) NOT NULL COMMENT '设备小部位主键 mes_xsl_equipment_sub_part.id',
|
||||||
|
`equipment_sub_part_name` varchar(500) DEFAULT NULL COMMENT '设备小部位名称冗余',
|
||||||
|
`item_category` varchar(500) NOT NULL COMMENT '项目类别(字典xslmes_im_item_category:inspect点检/maintain保养)',
|
||||||
|
`item_type` varchar(500) NOT NULL COMMENT '项目类型(字典xslmes_im_item_type:mechanical机械类/electrical电气类)',
|
||||||
|
`inspect_method` varchar(500) NOT NULL COMMENT '点检方式(字典xslmes_im_inspect_method:visual视觉/sight目测/hearing听觉)',
|
||||||
|
`judgment_criteria` varchar(500) NOT NULL COMMENT '判断基准',
|
||||||
|
`maintain_cycle_days` int DEFAULT NULL COMMENT '保养周期(天)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mimi_tenant_name` (`tenant_id`, `item_name`),
|
||||||
|
KEY `idx_mimi_tenant_code` (`tenant_id`, `item_code`),
|
||||||
|
KEY `idx_mimi_category` (`equipment_category_id`),
|
||||||
|
KEY `idx_mimi_type` (`equipment_type_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES点检及保养项目';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000141', @mes_equip_pid, '点检及保养项目', '/xslmes/mesXslInspectMaintainItem', 'xslmes/mesXslInspectMaintainItem/MesXslInspectMaintainItemList', NULL, 1, NULL, '1', 11, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:audit-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:audit-outlined' WHERE `id` = '1860000000000000141' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000142', '1860000000000000141', '新增', 2, 'mes:mes_xsl_inspect_maintain_item:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000143', '1860000000000000141', '编辑', 2, 'mes:mes_xsl_inspect_maintain_item:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000144', '1860000000000000141', '删除', 2, 'mes:mes_xsl_inspect_maintain_item:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000145', '1860000000000000141', '批量删除', 2, 'mes:mes_xsl_inspect_maintain_item:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000146', '1860000000000000141', '导出', 2, 'mes:mes_xsl_inspect_maintain_item:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000147', '1860000000000000141', '导入', 2, 'mes:mes_xsl_inspect_maintain_item:importExcel', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000141',
|
||||||
|
'1860000000000000142', '1860000000000000143', '1860000000000000144', '1860000000000000145',
|
||||||
|
'1860000000000000146', '1860000000000000147'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
-- MES 厂家信息:字典 + 建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
-- MES 厂家信息:字典 + 建表 + 菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
-- 权限前缀与 Controller、前端 v-auth 一致:mes:mes_xsl_manufacturer:*
|
-- 权限前缀与 Controller、前端 v-auth 一致:mes:mes_xsl_manufacturer:*
|
||||||
-- 父菜单:MES基础资料 / MES资料(与备品件等脚本相同 @mes_base_pid);修改租户改 SET @mes_tenant_id
|
-- 父菜单:设备管理(@mes_equip_pid);修改租户改 SET @mes_tenant_id
|
||||||
-- 新环境也可依赖 Flyway:V3.9.2_63__mes_xsl_manufacturer.sql(与本文内容一致,重复执行幂等)
|
-- 新环境也可依赖 Flyway:V3.9.2_63__mes_xsl_manufacturer.sql(与本文内容一致,重复执行幂等)
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
@@ -31,14 +31,14 @@ WHERE d.`dict_code` = 'xslmes_manufacturer_valid' AND NOT EXISTS (SELECT 1 FROM
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `mes_xsl_manufacturer` (
|
CREATE TABLE IF NOT EXISTS `mes_xsl_manufacturer` (
|
||||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
`manufacturer_category` varchar(32) NOT NULL COMMENT '厂家类别(字典xslmes_manufacturer_category:mold模具 capsule胶囊 equipment设备)',
|
`manufacturer_category` varchar(500) NOT NULL COMMENT '厂家类别(字典xslmes_manufacturer_category:mold模具 capsule胶囊 equipment设备)',
|
||||||
`manufacturer_name` varchar(128) NOT NULL COMMENT '厂家名称(同租户未删除数据中唯一)',
|
`manufacturer_name` varchar(500) NOT NULL COMMENT '厂家名称(同租户未删除数据中唯一)',
|
||||||
`valid_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否有效(字典xslmes_manufacturer_valid:0有效1无效)',
|
`valid_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否有效(字典xslmes_manufacturer_valid:0有效1无效)',
|
||||||
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
@@ -47,19 +47,22 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_manufacturer` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000112', @mes_base_pid, '厂家信息', '/xslmes/mesXslManufacturer', 'xslmes/mesXslManufacturer/MesXslManufacturerList', NULL, 1, NULL, '1', 17, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000112', @mes_equip_pid, '厂家信息', '/xslmes/mesXslManufacturer', 'xslmes/mesXslManufacturer/MesXslManufacturerList', NULL, 1, NULL, '1', 8, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:shop-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:shop-outlined' WHERE `id` = '1860000000000000112' AND `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000113', '1860000000000000112', '新增', 2, 'mes:mes_xsl_manufacturer:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000113', '1860000000000000112', '新增', 2, 'mes:mes_xsl_manufacturer:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ SET NAMES utf8mb4;
|
|||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `mes_xsl_manufacturer` (
|
CREATE TABLE IF NOT EXISTS `mes_xsl_manufacturer` (
|
||||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
`manufacturer_category` varchar(32) NOT NULL COMMENT '厂家类别(字典xslmes_manufacturer_category:mold模具 capsule胶囊 equipment设备)',
|
`manufacturer_category` varchar(500) NOT NULL COMMENT '厂家类别(字典xslmes_manufacturer_category:mold模具 capsule胶囊 equipment设备)',
|
||||||
`manufacturer_name` varchar(128) NOT NULL COMMENT '厂家名称(同租户未删除数据中唯一)',
|
`manufacturer_name` varchar(500) NOT NULL COMMENT '厂家名称(同租户未删除数据中唯一)',
|
||||||
`valid_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否有效(字典xslmes_manufacturer_valid:0有效1无效)',
|
`valid_status` varchar(1) NOT NULL DEFAULT '0' COMMENT '是否有效(字典xslmes_manufacturer_valid:0有效1无效)',
|
||||||
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
`sys_org_code` varchar(500) DEFAULT NULL COMMENT '部门',
|
||||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
`create_by` varchar(500) DEFAULT NULL COMMENT '创建人',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
`update_by` varchar(500) DEFAULT NULL COMMENT '更新人',
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
|
|||||||
92
jeecg-boot/db/mes-xsl-master-batch-plan-menu.sql
Normal file
92
jeecg-boot/db/mes-xsl-master-batch-plan-menu.sql
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
-- 母胶计划菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099611', @mixer_parent_id, '母胶计划',
|
||||||
|
'/mes/masterbatchplaninfo',
|
||||||
|
'mes/masterbatchplaninfo/index',
|
||||||
|
'MesXslMasterBatchPlanList', 1, NULL, '1', 34,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099612', '1860000000000099611', '新增', 2, 'xslmes:mes_xsl_master_batch_plan:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099613', '1860000000000099611', '编辑', 2, 'xslmes:mes_xsl_master_batch_plan:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099614', '1860000000000099611', '删除', 2, 'xslmes:mes_xsl_master_batch_plan:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099615', '1860000000000099611', '批量删除', 2, 'xslmes:mes_xsl_master_batch_plan:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099616', '1860000000000099611', '导出', 2, 'xslmes:mes_xsl_master_batch_plan:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099611',
|
||||||
|
'1860000000000099612', '1860000000000099613', '1860000000000099614', '1860000000000099615', '1860000000000099616'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/masterbatchplaninfo',
|
||||||
|
component = 'mes/masterbatchplaninfo/index',
|
||||||
|
component_name = 'MesXslMasterBatchPlanList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099611';
|
||||||
30
jeecg-boot/db/mes-xsl-master-batch-plan-table.sql
Normal file
30
jeecg-boot/db/mes-xsl-master-batch-plan-table.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-- 母胶计划建表SQL
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_master_batch_plan` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`source_order_id` varchar(32) DEFAULT NULL COMMENT '来源生产订单ID',
|
||||||
|
`order_serial_no` varchar(500) DEFAULT NULL COMMENT '订单流水号',
|
||||||
|
`order_no` varchar(500) DEFAULT NULL COMMENT '订单编号',
|
||||||
|
`production_segment_count` int DEFAULT NULL COMMENT '生产段数',
|
||||||
|
`order_date` date DEFAULT NULL COMMENT '订单日期',
|
||||||
|
`material_code` varchar(500) DEFAULT NULL COMMENT '物料编号',
|
||||||
|
`mes_material_name` varchar(500) DEFAULT NULL COMMENT 'MES胶料名称',
|
||||||
|
`plan_weight` decimal(18,4) DEFAULT NULL COMMENT '计划重量',
|
||||||
|
`per_car_weight` decimal(18,4) DEFAULT NULL COMMENT '每车重量',
|
||||||
|
`planned_car_count` int DEFAULT 0 COMMENT '计划车数',
|
||||||
|
`scheduled_car_count` int DEFAULT 0 COMMENT '已排产车数',
|
||||||
|
`finished_car_count` int DEFAULT 0 COMMENT '完成车数',
|
||||||
|
`status` int DEFAULT 0 COMMENT '状态:0未开始 1进行中 2已完成',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门编码',
|
||||||
|
`create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT 0 COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mxmbp_source_order` (`source_order_id`),
|
||||||
|
KEY `idx_mxmbp_material_code` (`material_code`),
|
||||||
|
UNIQUE KEY `uk_mxmbp_source_order_del` (`source_order_id`, `del_flag`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES母胶计划';
|
||||||
92
jeecg-boot/db/mes-xsl-mixer-action-menu.sql
Normal file
92
jeecg-boot/db/mes-xsl-mixer-action-menu.sql
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
-- 密炼机动作维护菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099211', @mixer_parent_id, '密炼机动作维护',
|
||||||
|
'/mes/mixeractioninfo',
|
||||||
|
'mes/mixeractioninfo/index',
|
||||||
|
'MesXslMixerActionList', 1, NULL, '1', 30,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099212', '1860000000000099211', '新增', 2, 'xslmes:mes_xsl_mixer_action:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099213', '1860000000000099211', '编辑', 2, 'xslmes:mes_xsl_mixer_action:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099214', '1860000000000099211', '删除', 2, 'xslmes:mes_xsl_mixer_action:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099215', '1860000000000099211', '批量删除', 2, 'xslmes:mes_xsl_mixer_action:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099216', '1860000000000099211', '导出', 2, 'xslmes:mes_xsl_mixer_action:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099211',
|
||||||
|
'1860000000000099212', '1860000000000099213', '1860000000000099214', '1860000000000099215', '1860000000000099216'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确(避免历史错误路径导致“查看组件引用是否正确”)
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/mixeractioninfo',
|
||||||
|
component = 'mes/mixeractioninfo/index',
|
||||||
|
component_name = 'MesXslMixerActionList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099211';
|
||||||
92
jeecg-boot/db/mes-xsl-mixer-condition-menu.sql
Normal file
92
jeecg-boot/db/mes-xsl-mixer-condition-menu.sql
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
-- 密炼机条件维护菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099411', @mixer_parent_id, '密炼机条件维护',
|
||||||
|
'/mes/mixerconditioninfo',
|
||||||
|
'mes/mixerconditioninfo/index',
|
||||||
|
'MesXslMixerConditionList', 1, NULL, '1', 32,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099412', '1860000000000099411', '新增', 2, 'xslmes:mes_xsl_mixer_condition:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099413', '1860000000000099411', '编辑', 2, 'xslmes:mes_xsl_mixer_condition:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099414', '1860000000000099411', '删除', 2, 'xslmes:mes_xsl_mixer_condition:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099415', '1860000000000099411', '批量删除', 2, 'xslmes:mes_xsl_mixer_condition:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099416', '1860000000000099411', '导出', 2, 'xslmes:mes_xsl_mixer_condition:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099411',
|
||||||
|
'1860000000000099412', '1860000000000099413', '1860000000000099414', '1860000000000099415', '1860000000000099416'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/mixerconditioninfo',
|
||||||
|
component = 'mes/mixerconditioninfo/index',
|
||||||
|
component_name = 'MesXslMixerConditionList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099411';
|
||||||
62
jeecg-boot/db/mes-xsl-move-menus-to-equipment-mgmt.sql
Normal file
62
jeecg-boot/db/mes-xsl-move-menus-to-equipment-mgmt.sql
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
-- 将设备类别/类型/部位/小部位、备品件、厂家、停机类菜单挂到「设备管理」目录下(工序管理仍留 MES基础资料)
|
||||||
|
-- 前置:已执行 mes-xsl-equipment-ledger-menu-permission.sql 或 Flyway V3.9.2_72 创建「设备管理」目录 id=1860000000000000133
|
||||||
|
-- 修改租户:改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0
|
||||||
|
WHERE `id` = @mes_equip_pid;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 1, `is_leaf` = 0 WHERE `id` = '1860000000000000134';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 2, `is_leaf` = 0 WHERE `id` = '1860000000000000070';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 3, `is_leaf` = 0 WHERE `id` = '1860000000000000077';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 4, `is_leaf` = 0 WHERE `id` = '1860000000000000084';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 5, `is_leaf` = 0 WHERE `id` = '1860000000000000091';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 6, `is_leaf` = 0 WHERE `id` = '1860000000000000098';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 7, `is_leaf` = 0 WHERE `id` = '1860000000000000105';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 8, `is_leaf` = 0 WHERE `id` = '1860000000000000112';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 9, `is_leaf` = 0 WHERE `id` = '1860000000000000119';
|
||||||
|
UPDATE `sys_permission` SET `parent_id` = @mes_equip_pid, `sort_no` = 10, `is_leaf` = 0 WHERE `id` = '1860000000000000126';
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
@mes_equip_pid,
|
||||||
|
'1860000000000000134',
|
||||||
|
'1860000000000000070',
|
||||||
|
'1860000000000000071', '1860000000000000072', '1860000000000000073', '1860000000000000074', '1860000000000000075', '1860000000000000076',
|
||||||
|
'1860000000000000077',
|
||||||
|
'1860000000000000078', '1860000000000000079', '1860000000000000080', '1860000000000000081', '1860000000000000082', '1860000000000000083',
|
||||||
|
'1860000000000000084',
|
||||||
|
'1860000000000000085', '1860000000000000086', '1860000000000000087', '1860000000000000088', '1860000000000000089', '1860000000000000090',
|
||||||
|
'1860000000000000091',
|
||||||
|
'1860000000000000092', '1860000000000000093', '1860000000000000094', '1860000000000000095', '1860000000000000096', '1860000000000000097',
|
||||||
|
'1860000000000000098',
|
||||||
|
'1860000000000000099', '1860000000000000100', '1860000000000000101', '1860000000000000102', '1860000000000000103', '1860000000000000104',
|
||||||
|
'1860000000000000105',
|
||||||
|
'1860000000000000106', '1860000000000000107', '1860000000000000108', '1860000000000000109', '1860000000000000110', '1860000000000000111',
|
||||||
|
'1860000000000000112',
|
||||||
|
'1860000000000000113', '1860000000000000114', '1860000000000000115', '1860000000000000116', '1860000000000000117', '1860000000000000118',
|
||||||
|
'1860000000000000119',
|
||||||
|
'1860000000000000120', '1860000000000000121', '1860000000000000122', '1860000000000000123', '1860000000000000124', '1860000000000000125',
|
||||||
|
'1860000000000000126',
|
||||||
|
'1860000000000000127', '1860000000000000128', '1860000000000000129', '1860000000000000130', '1860000000000000131', '1860000000000000132',
|
||||||
|
'1860000000000000135', '1860000000000000136', '1860000000000000137', '1860000000000000138', '1860000000000000139', '1860000000000000140'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
-- 工序管理挂「MES基础资料」;设备类别、设备类型挂「设备管理」;修复权限树(is_leaf)与租户 admin 授权
|
||||||
|
-- 适用:菜单曾在 MES XSL 根下或 is_leaf=1 导致角色授权页不显示按钮权限
|
||||||
|
-- 修改租户:改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_base_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES基础资料'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_base_pid = IFNULL(@mes_base_pid, (
|
||||||
|
SELECT MIN(`id`) FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES资料'
|
||||||
|
));
|
||||||
|
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `id` = @mes_equip_pid;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000060', @mes_base_pid, '工序管理', '/xslmes/mesXslProcessOperation', 'xslmes/mesXslProcessOperation/MesXslProcessOperationList', NULL, 1, NULL, '1', 9, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000061', '1860000000000000060', '新增', 2, 'mes:mes_process_operation:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000062', '1860000000000000060', '编辑', 2, 'mes:mes_process_operation:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000063', '1860000000000000060', '删除', 2, 'mes:mes_process_operation:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000064', '1860000000000000060', '批量删除', 2, 'mes:mes_process_operation:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000065', '1860000000000000060', '导出', 2, 'mes:mes_process_operation:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000066', '1860000000000000060', '导入', 2, 'mes:mes_process_operation:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `perms` = VALUES(`perms`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000070', @mes_equip_pid, '设备类别', '/xslmes/mesXslEquipmentCategory', 'xslmes/mesXslEquipmentCategory/MesXslEquipmentCategoryList', NULL, 1, NULL, '1', 2, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000071', '1860000000000000070', '新增', 2, 'mes:mes_xsl_equipment_category:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000072', '1860000000000000070', '编辑', 2, 'mes:mes_xsl_equipment_category:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000073', '1860000000000000070', '删除', 2, 'mes:mes_xsl_equipment_category:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000074', '1860000000000000070', '批量删除', 2, 'mes:mes_xsl_equipment_category:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000075', '1860000000000000070', '导出', 2, 'mes:mes_xsl_equipment_category:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000076', '1860000000000000070', '导入', 2, 'mes:mes_xsl_equipment_category:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `perms` = VALUES(`perms`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000077', @mes_equip_pid, '设备类型', '/xslmes/mesXslEquipmentType', 'xslmes/mesXslEquipmentType/MesXslEquipmentTypeList', NULL, 1, NULL, '1', 3, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000078', '1860000000000000077', '新增', 2, 'mes:mes_xsl_equipment_type:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000079', '1860000000000000077', '编辑', 2, 'mes:mes_xsl_equipment_type:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000080', '1860000000000000077', '删除', 2, 'mes:mes_xsl_equipment_type:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000081', '1860000000000000077', '批量删除', 2, 'mes:mes_xsl_equipment_type:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000082', '1860000000000000077', '导出', 2, 'mes:mes_xsl_equipment_type:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000083', '1860000000000000077', '导入', 2, 'mes:mes_xsl_equipment_type:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `perms` = VALUES(`perms`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `name` = '新增' WHERE `id` = '1860000000000000061' AND `name` = '添加';
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000060',
|
||||||
|
'1860000000000000061', '1860000000000000062', '1860000000000000063',
|
||||||
|
'1860000000000000064', '1860000000000000065', '1860000000000000066',
|
||||||
|
'1860000000000000070',
|
||||||
|
'1860000000000000071', '1860000000000000072', '1860000000000000073',
|
||||||
|
'1860000000000000074', '1860000000000000075', '1860000000000000076',
|
||||||
|
'1860000000000000077',
|
||||||
|
'1860000000000000078', '1860000000000000079', '1860000000000000080',
|
||||||
|
'1860000000000000081', '1860000000000000082', '1860000000000000083'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
90
jeecg-boot/db/mes-xsl-process-and-category-menu-fix.sql
Normal file
90
jeecg-boot/db/mes-xsl-process-and-category-menu-fix.sql
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
-- 工序管理(060)挂 MES基础资料;设备类别(070)挂设备管理;修复 is_leaf、租户 admin 授权
|
||||||
|
-- 可重复执行;修改租户改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_base_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES基础资料'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_base_pid = IFNULL(@mes_base_pid, (
|
||||||
|
SELECT MIN(`id`) FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES资料'
|
||||||
|
));
|
||||||
|
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
||||||
|
|
||||||
|
SET @mes_equip_pid = (
|
||||||
|
SELECT `id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料');
|
||||||
|
|
||||||
|
UPDATE `sys_permission`
|
||||||
|
SET `is_leaf` = 0
|
||||||
|
WHERE `id` = @mes_equip_pid;
|
||||||
|
|
||||||
|
-- 工序管理
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000060', @mes_base_pid, '工序管理', '/xslmes/mesXslProcessOperation', 'xslmes/mesXslProcessOperation/MesXslProcessOperationList', NULL, 1, NULL, '1', 9, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000061', '1860000000000000060', '新增', 2, 'mes:mes_process_operation:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000062', '1860000000000000060', '编辑', 2, 'mes:mes_process_operation:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000063', '1860000000000000060', '删除', 2, 'mes:mes_process_operation:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000064', '1860000000000000060', '批量删除', 2, 'mes:mes_process_operation:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000065', '1860000000000000060', '导出', 2, 'mes:mes_process_operation:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000066', '1860000000000000060', '导入', 2, 'mes:mes_process_operation:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`),
|
||||||
|
`perms_type` = VALUES(`perms_type`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `name` = '新增' WHERE `id` = '1860000000000000061' AND `name` = '添加';
|
||||||
|
|
||||||
|
-- 设备类别
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000070', @mes_equip_pid, '设备类别', '/xslmes/mesXslEquipmentCategory', 'xslmes/mesXslEquipmentCategory/MesXslEquipmentCategoryList', NULL, 1, NULL, '1', 2, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000071', '1860000000000000070', '新增', 2, 'mes:mes_xsl_equipment_category:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000072', '1860000000000000070', '编辑', 2, 'mes:mes_xsl_equipment_category:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000073', '1860000000000000070', '删除', 2, 'mes:mes_xsl_equipment_category:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000074', '1860000000000000070', '批量删除', 2, 'mes:mes_xsl_equipment_category:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000075', '1860000000000000070', '导出', 2, 'mes:mes_xsl_equipment_category:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000076', '1860000000000000070', '导入', 2, 'mes:mes_xsl_equipment_category:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`),
|
||||||
|
`perms_type` = VALUES(`perms_type`), `is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000060',
|
||||||
|
'1860000000000000061', '1860000000000000062', '1860000000000000063',
|
||||||
|
'1860000000000000064', '1860000000000000065', '1860000000000000066',
|
||||||
|
'1860000000000000070',
|
||||||
|
'1860000000000000071', '1860000000000000072', '1860000000000000073',
|
||||||
|
'1860000000000000074', '1860000000000000075', '1860000000000000076'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
93
jeecg-boot/db/mes-xsl-production-order-menu.sql
Normal file
93
jeecg-boot/db/mes-xsl-production-order-menu.sql
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
-- 生产订单菜单与权限(挂到「MES密炼工程」目录)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mixer_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = 'MES密炼工程' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0 AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mixer_parent_id = IFNULL(@mixer_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099511', @mixer_parent_id, '生产订单',
|
||||||
|
'/mes/productionorderinfo',
|
||||||
|
'mes/productionorderinfo/index',
|
||||||
|
'MesXslProductionOrderList', 1, NULL, '1', 33,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099512', '1860000000000099511', '新增', 2, 'xslmes:mes_xsl_production_order:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099513', '1860000000000099511', '编辑', 2, 'xslmes:mes_xsl_production_order:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099514', '1860000000000099511', '删除', 2, 'xslmes:mes_xsl_production_order:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099515', '1860000000000099511', '批量删除', 2, 'xslmes:mes_xsl_production_order:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099516', '1860000000000099511', '导出', 2, 'xslmes:mes_xsl_production_order:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099517', '1860000000000099511', '拆分', 2, 'xslmes:mes_xsl_production_order:split', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN (
|
||||||
|
'1860000000000099511',
|
||||||
|
'1860000000000099512', '1860000000000099513', '1860000000000099514', '1860000000000099515', '1860000000000099516', '1860000000000099517'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 强制修复:确保菜单路由与组件路径正确
|
||||||
|
UPDATE sys_permission
|
||||||
|
SET
|
||||||
|
parent_id = @mixer_parent_id,
|
||||||
|
url = '/mes/productionorderinfo',
|
||||||
|
component = 'mes/productionorderinfo/index',
|
||||||
|
component_name = 'MesXslProductionOrderList',
|
||||||
|
menu_type = 1,
|
||||||
|
is_route = 1,
|
||||||
|
is_leaf = 1,
|
||||||
|
hidden = 0,
|
||||||
|
status = '1',
|
||||||
|
del_flag = 0
|
||||||
|
WHERE id = '1860000000000099511';
|
||||||
64
jeecg-boot/db/mes-xsl-production-order-split-permission.sql
Normal file
64
jeecg-boot/db/mes-xsl-production-order-split-permission.sql
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
-- 生产订单「拆分」按钮权限补丁
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
-- 优先按组件路径定位“生产订单”菜单
|
||||||
|
SET @prod_menu_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE component = 'mes/productionorderinfo/index'
|
||||||
|
AND menu_type = 1
|
||||||
|
AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 兜底:按URL定位
|
||||||
|
SET @prod_menu_id = IFNULL(@prod_menu_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes/productionorderinfo'
|
||||||
|
AND menu_type = 1
|
||||||
|
AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
|
||||||
|
-- 再兜底:按名称定位
|
||||||
|
SET @prod_menu_id = IFNULL(@prod_menu_id, (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE name = '生产订单'
|
||||||
|
AND menu_type = 1
|
||||||
|
AND del_flag = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
|
||||||
|
-- 若找不到页面菜单,回退到约定ID(你现有脚本中使用)
|
||||||
|
SET @prod_menu_id = IFNULL(@prod_menu_id, '1860000000000099511');
|
||||||
|
|
||||||
|
-- 写入/修复“拆分”按钮权限
|
||||||
|
INSERT INTO sys_permission (
|
||||||
|
id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time
|
||||||
|
) VALUES (
|
||||||
|
'1860000000000099517', @prod_menu_id, '拆分', 2, 'xslmes:mes_xsl_production_order:split', '1', '1', 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- 给admin角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', '1860000000000099517', NOW(), '127.0.0.1'
|
||||||
|
FROM dual
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission
|
||||||
|
WHERE role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND permission_id = '1860000000000099517'
|
||||||
|
);
|
||||||
66
jeecg-boot/db/mes-xsl-raw-material-inspect-record-menu.sql
Normal file
66
jeecg-boot/db/mes-xsl-raw-material-inspect-record-menu.sql
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
-- 原材料送检记录菜单与权限(挂到 MES管理)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @mes_parent_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM sys_permission
|
||||||
|
WHERE url = '/mes' AND menu_type = 0
|
||||||
|
ORDER BY create_time ASC
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_parent_id = IFNULL(@mes_parent_id, '1860000000000000001');
|
||||||
|
|
||||||
|
-- 二级菜单:原材料送检记录
|
||||||
|
INSERT INTO sys_permission(
|
||||||
|
id, parent_id, name, url, component, component_name, menu_type, perms, perms_type, sort_no,
|
||||||
|
is_route, is_leaf, hidden, status, del_flag, keep_alive, internal_or_external, create_by, create_time
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
'1860000000000099111', @mes_parent_id, '原材料送检记录',
|
||||||
|
'/xslmes/mesXslRawMaterialInspectRecordList',
|
||||||
|
'xslmes/mesXslRawMaterialInspectRecord/MesXslRawMaterialInspectRecordList',
|
||||||
|
'MesXslRawMaterialInspectRecordList', 1, NULL, '1', 22,
|
||||||
|
1, 1, 0, '1', 0, 1, 0, 'admin', NOW()
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
url = VALUES(url),
|
||||||
|
component = VALUES(component),
|
||||||
|
component_name = VALUES(component_name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
sort_no = VALUES(sort_no),
|
||||||
|
is_route = VALUES(is_route),
|
||||||
|
is_leaf = VALUES(is_leaf),
|
||||||
|
hidden = VALUES(hidden),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag),
|
||||||
|
keep_alive = VALUES(keep_alive),
|
||||||
|
internal_or_external = VALUES(internal_or_external);
|
||||||
|
|
||||||
|
-- 按钮权限
|
||||||
|
INSERT INTO sys_permission(id, parent_id, name, menu_type, perms, perms_type, status, del_flag, create_by, create_time) VALUES
|
||||||
|
('1860000000000099112', '1860000000000099111', '编辑', 2, 'xslmes:mes_xsl_raw_material_inspect_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000099113', '1860000000000099111', '导出', 2, 'xslmes:mes_xsl_raw_material_inspect_record:exportXls', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
parent_id = VALUES(parent_id),
|
||||||
|
name = VALUES(name),
|
||||||
|
menu_type = VALUES(menu_type),
|
||||||
|
perms = VALUES(perms),
|
||||||
|
perms_type = VALUES(perms_type),
|
||||||
|
status = VALUES(status),
|
||||||
|
del_flag = VALUES(del_flag);
|
||||||
|
|
||||||
|
-- admin 角色授权
|
||||||
|
INSERT INTO sys_role_permission(id, role_id, permission_id, operate_date, operate_ip)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_permission p
|
||||||
|
WHERE p.id IN ('1860000000000099111', '1860000000000099112', '1860000000000099113')
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = 'f6817f48af4fb3af11b9e8bf182f618b'
|
||||||
|
AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
36
jeecg-boot/db/mes-xsl-raw-material-inspect-record-reset.sql
Normal file
36
jeecg-boot/db/mes-xsl-raw-material-inspect-record-reset.sql
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
-- 原材料送检记录测试数据重置脚本(稳妥版)
|
||||||
|
-- 作用:
|
||||||
|
-- 1) 将存在送检记录关联的原材料卡片检测结果重置为「未检(0)」
|
||||||
|
-- 2) 清空原材料送检记录主表/子表数据
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
-- 执行前计数(便于确认是否命中数据)
|
||||||
|
SELECT COUNT(1) AS before_record_count FROM mes_xsl_raw_material_inspect_record;
|
||||||
|
SELECT COUNT(1) AS before_record_line_count FROM mes_xsl_raw_material_inspect_record_line;
|
||||||
|
|
||||||
|
-- 回写原材料卡片检测结果为未检(0):只重置存在送检记录关联的卡片
|
||||||
|
UPDATE mes_xsl_raw_material_card c
|
||||||
|
JOIN (
|
||||||
|
SELECT DISTINCT r.raw_material_card_id AS card_id
|
||||||
|
FROM mes_xsl_raw_material_inspect_record r
|
||||||
|
WHERE r.raw_material_card_id IS NOT NULL
|
||||||
|
AND r.raw_material_card_id <> ''
|
||||||
|
) t ON t.card_id = c.id
|
||||||
|
SET c.test_result = '0',
|
||||||
|
c.update_time = NOW();
|
||||||
|
|
||||||
|
-- 先删子表,再删主表
|
||||||
|
DELETE FROM mes_xsl_raw_material_inspect_record_line
|
||||||
|
WHERE record_id IN (
|
||||||
|
SELECT id FROM mes_xsl_raw_material_inspect_record
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM mes_xsl_raw_material_inspect_record;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- 执行后计数
|
||||||
|
SELECT COUNT(1) AS remaining_record_count FROM mes_xsl_raw_material_inspect_record;
|
||||||
|
SELECT COUNT(1) AS remaining_record_line_count FROM mes_xsl_raw_material_inspect_record_line;
|
||||||
80
jeecg-boot/db/mes-xsl-raw-material-inspect-record.sql
Normal file
80
jeecg-boot/db/mes-xsl-raw-material-inspect-record.sql
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
-- 原材料送检记录(主表 + 子表)
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_raw_material_inspect_record` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`raw_material_card_id` varchar(32) DEFAULT NULL COMMENT '原材料卡片ID',
|
||||||
|
`barcode` varchar(128) DEFAULT NULL COMMENT '条码',
|
||||||
|
`batch_no` varchar(128) DEFAULT NULL COMMENT '批次号',
|
||||||
|
`material_id` varchar(32) DEFAULT NULL COMMENT '物料ID',
|
||||||
|
`material_name` varchar(200) DEFAULT NULL COMMENT '物料名称',
|
||||||
|
`inspect_status` varchar(10) DEFAULT '0' COMMENT '检验状态(字典 xslmes_inspect_status:0待检 1合格 2不合格)',
|
||||||
|
`inspect_time` datetime DEFAULT NULL COMMENT '送检时间',
|
||||||
|
`result_time` datetime DEFAULT NULL COMMENT '判定时间',
|
||||||
|
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_xsl_rm_ir_card` (`raw_material_card_id`),
|
||||||
|
KEY `idx_xsl_rm_ir_status` (`inspect_status`),
|
||||||
|
KEY `idx_xsl_rm_ir_time` (`inspect_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='原材料送检记录';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_raw_material_inspect_record_line` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`record_id` varchar(32) NOT NULL COMMENT '送检记录主表ID',
|
||||||
|
`inspect_std_id` varchar(32) DEFAULT NULL COMMENT '检验标准ID',
|
||||||
|
`inspect_item_id` varchar(32) DEFAULT NULL COMMENT '检验项目ID',
|
||||||
|
`inspect_item_name` varchar(200) DEFAULT NULL COMMENT '检验项目名称',
|
||||||
|
`allow_min` decimal(24,6) DEFAULT NULL COMMENT '容许最小值',
|
||||||
|
`include_min_flag` int NOT NULL DEFAULT '0' COMMENT '包含最小值:1是 0否',
|
||||||
|
`allow_max` decimal(24,6) DEFAULT NULL COMMENT '容许最大值',
|
||||||
|
`include_max_flag` int NOT NULL DEFAULT '0' COMMENT '包含最大值:1是 0否',
|
||||||
|
`inspect_value` decimal(24,6) DEFAULT NULL COMMENT '检验值',
|
||||||
|
`pass_flag` varchar(10) DEFAULT '0' COMMENT '判定状态(字典 xslmes_inspect_status:0待检 1合格 2不合格)',
|
||||||
|
`sort_no` int DEFAULT NULL COMMENT '排序',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_xsl_rm_irl_record` (`record_id`),
|
||||||
|
KEY `idx_xsl_rm_irl_item` (`inspect_item_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='原材料送检记录-检验明细';
|
||||||
|
|
||||||
|
-- 字典:送检状态
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), '送检状态', 'xslmes_inspect_status', '原材料送检状态:待检/合格/不合格', 0, 'admin', NOW(), 0
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_inspect_status' AND `del_flag` = 0
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '待检', '0', '待检', 1, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_inspect_status'
|
||||||
|
AND d.`del_flag` = 0
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict_item` i WHERE i.dict_id = d.id AND i.item_value = '0'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '合格', '1', '合格', 2, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_inspect_status'
|
||||||
|
AND d.`del_flag` = 0
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict_item` i WHERE i.dict_id = d.id AND i.item_value = '1'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '不合格', '2', '不合格', 3, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_inspect_status'
|
||||||
|
AND d.`del_flag` = 0
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_dict_item` i WHERE i.dict_id = d.id AND i.item_value = '2'
|
||||||
|
);
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
-- MES 胶料快检数据点:建表 + 菜单(质量管理下)+ 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_quick_test_data_point:*
|
||||||
|
-- 依赖:mes_xsl_rubber_quick_test_type;修改租户改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_data_point` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`point_name` varchar(128) NOT NULL COMMENT '数据点名称(同租户未删除唯一)',
|
||||||
|
`quick_test_type_id` varchar(32) NOT NULL COMMENT '实验类型 mes_xsl_rubber_quick_test_type.id',
|
||||||
|
`quick_test_type_name` varchar(128) DEFAULT NULL COMMENT '实验类型名称冗余',
|
||||||
|
`unit_type` varchar(64) DEFAULT NULL COMMENT '单位类型(手填)',
|
||||||
|
`point_desc` varchar(500) DEFAULT NULL COMMENT '描述',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrqtdp_tenant_type` (`tenant_id`, `quick_test_type_id`),
|
||||||
|
UNIQUE KEY `uk_mrqtdp_tenant_name_del` (`tenant_id`, `point_name`, `del_flag`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检数据点';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
'1860000000000000162'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000170', @mes_quality_pid, '胶料快检数据点', '/xslmes/mesXslRubberQuickTestDataPoint', 'xslmes/mesXslRubberQuickTestDataPoint/MesXslRubberQuickTestDataPointList', 'MesXslRubberQuickTestDataPointList', 1, NULL, '1', 2, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:dot-chart-outlined' WHERE `id` = '1860000000000000170' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000171', '1860000000000000170', '新增', 2, 'mes:mes_xsl_rubber_quick_test_data_point:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000172', '1860000000000000170', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_data_point:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000173', '1860000000000000170', '删除', 2, 'mes:mes_xsl_rubber_quick_test_data_point:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000174', '1860000000000000170', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_data_point:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000175', '1860000000000000170', '导出', 2, 'mes:mes_xsl_rubber_quick_test_data_point:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000176', '1860000000000000170', '导入', 2, 'mes:mes_xsl_rubber_quick_test_data_point:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000170',
|
||||||
|
'1860000000000000171', '1860000000000000172', '1860000000000000173', '1860000000000000174',
|
||||||
|
'1860000000000000175', '1860000000000000176'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
27
jeecg-boot/db/mes-xsl-rubber-quick-test-method-drop.sql
Normal file
27
jeecg-boot/db/mes-xsl-rubber-quick-test-method-drop.sql
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-- 撤销「胶料快检实验方法」:删除菜单权限、字典、业务表(可整文件一次执行)
|
||||||
|
-- 说明:若仅未执行过 V3.9.2_99 / mes-xsl-rubber-quick-test-method-menu-permission.sql,执行本脚本即可;
|
||||||
|
-- 已 Flyway 执行过 V3.9.2_99 的环境,请同时执行 flyway V3.9.2_100 或本脚本(内容一致)。
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
DELETE FROM `sys_role_permission`
|
||||||
|
WHERE `permission_id` IN (
|
||||||
|
'1860000000000000170',
|
||||||
|
'1860000000000000171', '1860000000000000172', '1860000000000000173',
|
||||||
|
'1860000000000000174', '1860000000000000175', '1860000000000000176'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM `sys_permission`
|
||||||
|
WHERE `id` IN (
|
||||||
|
'1860000000000000170',
|
||||||
|
'1860000000000000171', '1860000000000000172', '1860000000000000173',
|
||||||
|
'1860000000000000174', '1860000000000000175', '1860000000000000176'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE di FROM `sys_dict_item` di
|
||||||
|
INNER JOIN `sys_dict` d ON di.`dict_id` = d.`id`
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_rotor_type' AND d.`del_flag` = 0;
|
||||||
|
|
||||||
|
DELETE FROM `sys_dict`
|
||||||
|
WHERE `dict_code` = 'xslmes_rubber_quick_test_rotor_type';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `mes_xsl_rubber_quick_test_method`;
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
-- 胶料快检实验方法(主子表):字典 + 建表 + 菜单(质量管理下)+ 按钮 + 租户 admin 授权
|
||||||
|
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_quick_test_method:*
|
||||||
|
|
||||||
|
-- 菜单 ID 段 1860000000000000177(与数据点 170 段区分)
|
||||||
|
|
||||||
|
-- 可与 Flyway V3.9.2_102 重复执行(ON DUPLICATE / IF NOT EXISTS)
|
||||||
|
|
||||||
|
-- SET @mes_tenant_id:多租户 admin 授权目标租户
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检转子类型', 'xslmes_rubber_quick_test_rotor_type', '1大转子2小转子', 0, 'admin', NOW(), 0, 0
|
||||||
|
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_rotor_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '大转子', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_rotor_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '小转子', '2', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_rotor_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '2');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检扭矩单位', 'xslmes_rubber_quick_test_torque_unit', 'Ib.in、dNm、kg.cm、Nm、mdm', 0, 'admin', NOW(), 0, 0
|
||||||
|
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_torque_unit' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||||
|
|
||||||
|
FROM `sys_dict` d
|
||||||
|
|
||||||
|
CROSS JOIN (
|
||||||
|
|
||||||
|
SELECT 'Ib.in' AS txt, 'Ib.in' AS val, 1 AS ord UNION ALL
|
||||||
|
|
||||||
|
SELECT 'dNm', 'dNm', 2 UNION ALL
|
||||||
|
|
||||||
|
SELECT 'kg.cm', 'kg.cm', 3 UNION ALL
|
||||||
|
|
||||||
|
SELECT 'Nm', 'Nm', 4 UNION ALL
|
||||||
|
|
||||||
|
SELECT 'mdm', 'mdm', 5
|
||||||
|
|
||||||
|
) v
|
||||||
|
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_torque_unit'
|
||||||
|
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检时间单位', 'xslmes_rubber_quick_test_time_unit', 'sec、min、m:s', 0, 'admin', NOW(), 0, 0
|
||||||
|
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_time_unit' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||||
|
|
||||||
|
FROM `sys_dict` d
|
||||||
|
|
||||||
|
CROSS JOIN (
|
||||||
|
|
||||||
|
SELECT 'sec' AS txt, 'sec' AS val, 1 AS ord UNION ALL
|
||||||
|
|
||||||
|
SELECT 'min', 'min', 2 UNION ALL
|
||||||
|
|
||||||
|
SELECT 'm:s', 'm:s', 3
|
||||||
|
|
||||||
|
) v
|
||||||
|
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_time_unit'
|
||||||
|
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检门尼单位', 'xslmes_rubber_quick_test_mooney_unit', 'MU', 0, 'admin', NOW(), 0, 0
|
||||||
|
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_mooney_unit' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, 'MU', 'MU', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_mooney_unit'
|
||||||
|
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'MU');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_method` (
|
||||||
|
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
|
||||||
|
`method_code` varchar(16) NOT NULL COMMENT '方法编号(租户内从001递增自动生成)',
|
||||||
|
|
||||||
|
`method_name` varchar(128) NOT NULL COMMENT '实验方法名称(同租户未删除唯一)',
|
||||||
|
|
||||||
|
`quick_test_type_id` varchar(32) NOT NULL COMMENT '实验类型 mes_xsl_rubber_quick_test_type.id',
|
||||||
|
|
||||||
|
`quick_test_type_name` varchar(128) DEFAULT NULL COMMENT '实验类型名称冗余',
|
||||||
|
|
||||||
|
`test_temp_c` decimal(12,2) DEFAULT NULL COMMENT '实验温度°C',
|
||||||
|
|
||||||
|
`preheat_time_min` decimal(12,2) DEFAULT NULL COMMENT '预热时间min',
|
||||||
|
|
||||||
|
`test_time_min` decimal(12,2) DEFAULT NULL COMMENT '实验时间min',
|
||||||
|
|
||||||
|
`test_angle_deg` decimal(12,2) DEFAULT NULL COMMENT '实验角度Deg',
|
||||||
|
|
||||||
|
`test_freq_hz` decimal(12,2) DEFAULT NULL COMMENT '实验频率Hz',
|
||||||
|
|
||||||
|
`rotor_type` varchar(2) DEFAULT NULL COMMENT '转子类型(字典xslmes_rubber_quick_test_rotor_type:1大转子2小转子)',
|
||||||
|
|
||||||
|
`rotor_speed_rpm` decimal(12,2) DEFAULT NULL COMMENT '转子速度rpm',
|
||||||
|
|
||||||
|
`method_desc` varchar(500) DEFAULT NULL COMMENT '方法描述',
|
||||||
|
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
|
||||||
|
KEY `idx_mrqtm_tenant_code` (`tenant_id`, `method_code`),
|
||||||
|
|
||||||
|
KEY `idx_mrqtm_type` (`quick_test_type_id`),
|
||||||
|
|
||||||
|
UNIQUE KEY `uk_mrqtm_tenant_name_del` (`tenant_id`, `method_name`, `del_flag`)
|
||||||
|
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检实验方法';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_method_line` (
|
||||||
|
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
|
||||||
|
`method_id` varchar(32) NOT NULL COMMENT '主表 mes_xsl_rubber_quick_test_method.id',
|
||||||
|
|
||||||
|
`data_point_id` varchar(32) NOT NULL COMMENT '数据点 mes_xsl_rubber_quick_test_data_point.id',
|
||||||
|
|
||||||
|
`point_name` varchar(128) DEFAULT NULL COMMENT '数据点名称冗余(只读带出)',
|
||||||
|
|
||||||
|
`unit_type` varchar(64) DEFAULT NULL COMMENT '单位类型冗余(只读带出)',
|
||||||
|
|
||||||
|
`torque_unit_type` varchar(32) DEFAULT NULL COMMENT '扭矩单位类型(字典xslmes_rubber_quick_test_torque_unit)',
|
||||||
|
|
||||||
|
`time_unit_type` varchar(32) DEFAULT NULL COMMENT '时间单位类型(字典xslmes_rubber_quick_test_time_unit)',
|
||||||
|
|
||||||
|
`mooney_unit_type` varchar(32) DEFAULT NULL COMMENT '门尼单位类型(字典xslmes_rubber_quick_test_mooney_unit)',
|
||||||
|
|
||||||
|
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||||
|
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
|
||||||
|
KEY `idx_mrqtml_method` (`method_id`),
|
||||||
|
|
||||||
|
UNIQUE KEY `uk_mrqtml_method_point` (`method_id`, `data_point_id`)
|
||||||
|
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检实验方法明细';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
|
||||||
|
'1860000000000000162'
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
|
||||||
|
VALUES ('1860000000000000177', @mes_quality_pid, '胶料快检实验方法', '/xslmes/mesXslRubberQuickTestMethod', 'xslmes/mesXslRubberQuickTestMethod/MesXslRubberQuickTestMethodList', 'MesXslRubberQuickTestMethodList', 1, NULL, '1', 3, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
|
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:profile-outlined' WHERE `id` = '1860000000000000177' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
|
||||||
|
('1860000000000000178', '1860000000000000177', '新增', 2, 'mes:mes_xsl_rubber_quick_test_method:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
|
||||||
|
('1860000000000000179', '1860000000000000177', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_method:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
|
||||||
|
('1860000000000000180', '1860000000000000177', '删除', 2, 'mes:mes_xsl_rubber_quick_test_method:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
|
||||||
|
('1860000000000000181', '1860000000000000177', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_method:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
|
||||||
|
('1860000000000000182', '1860000000000000177', '导出', 2, 'mes:mes_xsl_rubber_quick_test_method:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
|
||||||
|
('1860000000000000183', '1860000000000000177', '导入', 2, 'mes:mes_xsl_rubber_quick_test_method:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
|
||||||
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
|
||||||
|
FROM `sys_role` r
|
||||||
|
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
|
||||||
|
AND p.`id` IN (
|
||||||
|
|
||||||
|
'1860000000000000177',
|
||||||
|
|
||||||
|
'1860000000000000178', '1860000000000000179', '1860000000000000180', '1860000000000000181',
|
||||||
|
|
||||||
|
'1860000000000000182', '1860000000000000183'
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
AND NOT EXISTS (
|
||||||
|
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
-- 胶料快检记录(主子表):字典 + 建表 + 菜单(质量管理下)+ 按钮 + 胶料信息「检验」按钮 + 租户 admin 授权
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_quick_test_record:*
|
||||||
|
-- 菜单 ID 段 1860000000000000192
|
||||||
|
-- SET @mes_tenant_id:多租户 admin 授权目标租户
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检记录检验结果', 'xslmes_rubber_quick_test_record_result', '1合格0不合格', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_record_result' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '合格', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '不合格', '0', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_record_result' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班次', 'xslmes_rubber_quick_test_work_shift', '班次', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_shift' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
CROSS JOIN (
|
||||||
|
SELECT '早班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||||
|
SELECT '中班', '2', 2 UNION ALL
|
||||||
|
SELECT '晚班', '3', 3
|
||||||
|
) v
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_shift'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检班组', 'xslmes_rubber_quick_test_work_team', '班组', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_work_team' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, v.txt, v.val, v.ord, 1, 'admin', NOW()
|
||||||
|
FROM `sys_dict` d
|
||||||
|
CROSS JOIN (
|
||||||
|
SELECT '甲班' AS txt, '1' AS val, 1 AS ord UNION ALL
|
||||||
|
SELECT '乙班', '2', 2 UNION ALL
|
||||||
|
SELECT '丙班', '3', 3
|
||||||
|
) v
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_work_team'
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = v.val);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`record_no` varchar(32) DEFAULT NULL COMMENT '单号(JL+日期+4位流水,如JL202605280001)',
|
||||||
|
`rubber_material_id` varchar(32) DEFAULT NULL COMMENT '胶料 mes_material.id',
|
||||||
|
`rubber_material_name` varchar(128) DEFAULT NULL COMMENT '胶料名称冗余',
|
||||||
|
`std_id` varchar(32) DEFAULT NULL COMMENT '引用的实验标准 mes_xsl_rubber_quick_test_std.id',
|
||||||
|
`prod_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '生产机台 mes_xsl_equipment_ledger.id',
|
||||||
|
`prod_equipment_name` varchar(128) DEFAULT NULL COMMENT '生产机台名称冗余',
|
||||||
|
`production_date` date DEFAULT NULL COMMENT '生产日期',
|
||||||
|
`train_no` varchar(64) DEFAULT NULL COMMENT '车次编号',
|
||||||
|
`work_shift` varchar(8) DEFAULT NULL COMMENT '班次(字典xslmes_rubber_quick_test_work_shift)',
|
||||||
|
`work_team` varchar(8) DEFAULT NULL COMMENT '班组(字典xslmes_rubber_quick_test_work_team)',
|
||||||
|
`inspect_times` int DEFAULT NULL COMMENT '检验次数',
|
||||||
|
`inspect_time` datetime DEFAULT NULL COMMENT '检验时间',
|
||||||
|
`inspector_user_id` varchar(32) DEFAULT NULL COMMENT '检验人用户ID',
|
||||||
|
`inspector_username` varchar(64) DEFAULT NULL COMMENT '检验人账号冗余',
|
||||||
|
`inspector_realname` varchar(64) DEFAULT NULL COMMENT '检验人姓名冗余',
|
||||||
|
`quick_test_type_id` varchar(32) DEFAULT NULL COMMENT '检验类型 mes_xsl_rubber_quick_test_type.id',
|
||||||
|
`quick_test_type_name` varchar(128) DEFAULT NULL COMMENT '检验类型名称冗余',
|
||||||
|
`inspect_result` varchar(2) DEFAULT NULL COMMENT '检验结果(字典xslmes_rubber_quick_test_record_result:1合格0不合格)',
|
||||||
|
`production_plan_no` varchar(100) DEFAULT NULL COMMENT '生产计划号',
|
||||||
|
`inspect_equipment_ledger_id` varchar(32) DEFAULT NULL COMMENT '检验机台 mes_xsl_equipment_ledger.id',
|
||||||
|
`inspect_equipment_name` varchar(128) DEFAULT NULL COMMENT '检验机台名称冗余',
|
||||||
|
`rubber_card_no` varchar(100) DEFAULT NULL COMMENT '胶料卡片号',
|
||||||
|
`rubber_batch_no` varchar(100) DEFAULT NULL COMMENT '胶料批次',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_mrqtr_record_no` (`record_no`),
|
||||||
|
KEY `idx_mrqtr_material` (`rubber_material_id`),
|
||||||
|
KEY `idx_mrqtr_std` (`std_id`),
|
||||||
|
KEY `idx_mrqtr_tenant` (`tenant_id`),
|
||||||
|
KEY `idx_mrqtr_inspect_time` (`inspect_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_record_line` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`record_id` varchar(32) NOT NULL COMMENT '主表 mes_xsl_rubber_quick_test_record.id',
|
||||||
|
`data_point_id` varchar(32) DEFAULT NULL COMMENT '数据点 mes_xsl_rubber_quick_test_data_point.id',
|
||||||
|
`inspect_item` varchar(128) DEFAULT NULL COMMENT '检验项目(数据点名称,只读带出)',
|
||||||
|
`lower_limit` decimal(18,6) DEFAULT NULL COMMENT '检验下限(只读带出)',
|
||||||
|
`inspect_value` decimal(18,6) DEFAULT NULL COMMENT '检验值',
|
||||||
|
`upper_limit` decimal(18,6) DEFAULT NULL COMMENT '检验上限(只读带出)',
|
||||||
|
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrqtrl_record` (`record_id`),
|
||||||
|
UNIQUE KEY `uk_mrqtrl_record_point` (`record_id`, `data_point_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检记录明细';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
'1860000000000000162'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000192', @mes_quality_pid, '胶料快检记录', '/xslmes/mesXslRubberQuickTestRecord', 'xslmes/mesXslRubberQuickTestRecord/MesXslRubberQuickTestRecordList', 'MesXslRubberQuickTestRecordList', 1, NULL, '1', 5, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`component_name` = VALUES(`component_name`), `sort_no` = VALUES(`sort_no`), `is_leaf` = VALUES(`is_leaf`), `keep_alive` = VALUES(`keep_alive`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:file-search-outlined' WHERE `id` = '1860000000000000192' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000193', '1860000000000000192', '新增', 2, 'mes:mes_xsl_rubber_quick_test_record:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000194', '1860000000000000192', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_record:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000195', '1860000000000000192', '删除', 2, 'mes:mes_xsl_rubber_quick_test_record:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000196', '1860000000000000192', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_record:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000197', '1860000000000000192', '导出', 2, 'mes:mes_xsl_rubber_quick_test_record:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000198', '1860000000000000192', '导入', 2, 'mes:mes_xsl_rubber_quick_test_record:importExcel', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000199', '1860000000000000192', '从胶料生成', 2, 'mes:mes_xsl_rubber_quick_test_record:batchFromMaterial', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000200', '1860000000000000011', '胶料快检', 2, 'mes:mes_material:rubberQuickTestInspect', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_permission p
|
||||||
|
WHERE r.tenant_id = @mes_tenant_id
|
||||||
|
AND r.role_code = 'admin'
|
||||||
|
AND p.id IN (
|
||||||
|
'1860000000000000192',
|
||||||
|
'1860000000000000193', '1860000000000000194', '1860000000000000195', '1860000000000000196',
|
||||||
|
'1860000000000000197', '1860000000000000198', '1860000000000000199',
|
||||||
|
'1860000000000000200'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
130
jeecg-boot/db/mes-xsl-rubber-quick-test-std-menu-permission.sql
Normal file
130
jeecg-boot/db/mes-xsl-rubber-quick-test-std-menu-permission.sql
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
-- 胶料快检实验标准(主子表):字典 + 建表 + 菜单(质量管理下)+ 按钮 + 租户 admin 授权
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_quick_test_std:*
|
||||||
|
-- 菜单 ID 段 1860000000000000184(实验方法占用 177-183)
|
||||||
|
-- 可与 Flyway V3.9.2_103 重复执行(ON DUPLICATE / IF NOT EXISTS)
|
||||||
|
-- SET @mes_tenant_id:多租户 admin 授权目标租户
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检密炼机类型', 'xslmes_rubber_quick_test_mixer_type', '1普通密炼机2串密炼机', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_mixer_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '普通密炼机', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_mixer_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '串密炼机', '2', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_mixer_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '2');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检标准启用状态', 'xslmes_rubber_quick_test_std_enable_status', '1使用中0已停用', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_std_enable_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '使用中', '1', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_std_enable_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已停用', '0', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_std_enable_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料快检标准审核状态', 'xslmes_rubber_quick_test_std_audit_status', '0草稿1已批准', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_quick_test_std_audit_status' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '草稿', '0', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_std_audit_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '0');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '已批准', '1', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_quick_test_std_audit_status' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = '1');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_std` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`std_name` varchar(128) NOT NULL COMMENT '实验标准名称(同租户未删除唯一)',
|
||||||
|
`test_method_id` varchar(32) NOT NULL COMMENT '实验方法 mes_xsl_rubber_quick_test_method.id',
|
||||||
|
`test_method_name` varchar(128) DEFAULT NULL COMMENT '实验方法名称冗余',
|
||||||
|
`mixer_type` varchar(2) DEFAULT NULL COMMENT '密炼机类型(字典xslmes_rubber_quick_test_mixer_type:1普通密炼机2串密炼机)',
|
||||||
|
`rubber_material_id` varchar(32) DEFAULT NULL COMMENT '胶料 mes_material.id',
|
||||||
|
`rubber_material_name` varchar(128) DEFAULT NULL COMMENT '胶料名称冗余',
|
||||||
|
`ps_compile_id` varchar(32) DEFAULT NULL COMMENT '密炼PS编制 mes_xsl_mixer_ps_compile.id',
|
||||||
|
`issue_number` varchar(100) DEFAULT NULL COMMENT '发行编号(密炼PS编码冗余)',
|
||||||
|
`issue_date` date DEFAULT NULL COMMENT '发行日期',
|
||||||
|
`issue_dept_id` varchar(32) DEFAULT NULL COMMENT '发行部门ID',
|
||||||
|
`issue_dept_name` varchar(200) DEFAULT NULL COMMENT '发行部门名称冗余',
|
||||||
|
`enable_status` varchar(2) DEFAULT '1' COMMENT '启用状态(字典xslmes_rubber_quick_test_std_enable_status:1使用中0已停用)',
|
||||||
|
`audit_status` varchar(2) DEFAULT '0' COMMENT '审核状态(字典xslmes_rubber_quick_test_std_audit_status:0草稿1已批准)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrqts_method` (`test_method_id`),
|
||||||
|
KEY `idx_mrqts_material` (`rubber_material_id`),
|
||||||
|
KEY `idx_mrqts_tenant` (`tenant_id`),
|
||||||
|
UNIQUE KEY `uk_mrqts_tenant_name_del` (`tenant_id`, `std_name`, `del_flag`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检实验标准';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_std_line` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`std_id` varchar(32) NOT NULL COMMENT '主表 mes_xsl_rubber_quick_test_std.id',
|
||||||
|
`data_point_id` varchar(32) NOT NULL COMMENT '数据点 mes_xsl_rubber_quick_test_data_point.id',
|
||||||
|
`point_name` varchar(128) DEFAULT NULL COMMENT '数据点名称冗余(只读带出)',
|
||||||
|
`lower_limit` decimal(18,6) DEFAULT NULL COMMENT '下限值',
|
||||||
|
`lower_warn` decimal(18,6) DEFAULT NULL COMMENT '下警告值',
|
||||||
|
`target_value` decimal(18,6) DEFAULT NULL COMMENT '目标值',
|
||||||
|
`upper_warn` decimal(18,6) DEFAULT NULL COMMENT '上警告值',
|
||||||
|
`upper_limit` decimal(18,6) DEFAULT NULL COMMENT '上限值',
|
||||||
|
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrqtsl_std` (`std_id`),
|
||||||
|
UNIQUE KEY `uk_mrqtsl_std_point` (`std_id`, `data_point_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检实验标准明细';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
'1860000000000000162'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000184', @mes_quality_pid, '胶料快检实验标准', '/xslmes/mesXslRubberQuickTestStd', 'xslmes/mesXslRubberQuickTestStd/MesXslRubberQuickTestStdList', 'MesXslRubberQuickTestStdList', 1, NULL, '1', 4, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`component_name` = VALUES(`component_name`), `sort_no` = VALUES(`sort_no`), `is_leaf` = VALUES(`is_leaf`), `keep_alive` = VALUES(`keep_alive`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:file-protect-outlined' WHERE `id` = '1860000000000000184' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000185', '1860000000000000184', '新增', 2, 'mes:mes_xsl_rubber_quick_test_std:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000186', '1860000000000000184', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_std:edit', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000187', '1860000000000000184', '删除', 2, 'mes:mes_xsl_rubber_quick_test_std:delete', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000188', '1860000000000000184', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_std:deleteBatch', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000189', '1860000000000000184', '导出', 2, 'mes:mes_xsl_rubber_quick_test_std:exportXls', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000190', '1860000000000000184', '导入', 2, 'mes:mes_xsl_rubber_quick_test_std:importExcel', '1', '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000191', '1860000000000000184', '启用停用', 2, 'mes:mes_xsl_rubber_quick_test_std:updateStatus', '1', '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE `perms` = VALUES(`perms`), `name` = VALUES(`name`);
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.id, p.id, NOW(), '127.0.0.1'
|
||||||
|
FROM sys_role r
|
||||||
|
CROSS JOIN sys_permission p
|
||||||
|
WHERE r.tenant_id = @mes_tenant_id
|
||||||
|
AND r.role_code = 'admin'
|
||||||
|
AND p.id IN (
|
||||||
|
'1860000000000000184',
|
||||||
|
'1860000000000000185', '1860000000000000186', '1860000000000000187', '1860000000000000188',
|
||||||
|
'1860000000000000189', '1860000000000000190', '1860000000000000191'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys_role_permission rp
|
||||||
|
WHERE rp.role_id = r.id AND rp.permission_id = p.id
|
||||||
|
);
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
-- MES 胶料快检实验类型:建表 + 质量管理目录 + 子菜单 + 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀与 Controller、前端 v-auth 一致:mes:mes_xsl_rubber_quick_test_type:*
|
||||||
|
-- 修改租户:改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_quick_test_type` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`type_code` varchar(16) NOT NULL COMMENT '实验类型编号(租户内从001递增自动生成)',
|
||||||
|
`type_name` varchar(128) NOT NULL COMMENT '实验类型名称',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrqtt_tenant_code` (`tenant_id`, `type_code`),
|
||||||
|
KEY `idx_mrqtt_tenant_name` (`tenant_id`, `type_name`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料快检实验类型';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_root_parent = (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES基础资料'
|
||||||
|
LIMIT 1
|
||||||
|
);
|
||||||
|
SET @mes_root_parent = IFNULL(@mes_root_parent, (
|
||||||
|
SELECT `parent_id` FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = 'MES资料'
|
||||||
|
LIMIT 1
|
||||||
|
));
|
||||||
|
SET @mes_root_parent = IFNULL(@mes_root_parent, '1860000000000000001');
|
||||||
|
|
||||||
|
SET @mes_quality_sort = IFNULL((
|
||||||
|
SELECT `sort_no` + 1 FROM `sys_permission`
|
||||||
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料', '设备管理')
|
||||||
|
ORDER BY `sort_no` DESC
|
||||||
|
LIMIT 1
|
||||||
|
), 52);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000162', @mes_root_parent, '质量管理', '/xslmes/quality', 'layouts/RouteView', 'MesQualityLayout', 0, NULL, '1', @mes_quality_sort, 1, 0, 0, '1', 0, 0, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:safety-certificate-outlined' WHERE `id` = '1860000000000000162' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000163', '1860000000000000162', '胶料快检实验类型', '/xslmes/mesXslRubberQuickTestType', 'xslmes/mesXslRubberQuickTestType/MesXslRubberQuickTestTypeList', 'MesXslRubberQuickTestTypeList', 1, NULL, '1', 1, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
|
`is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0,
|
||||||
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:experiment-outlined' WHERE `id` = '1860000000000000163' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000164', '1860000000000000163', '新增', 2, 'mes:mes_xsl_rubber_quick_test_type:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000165', '1860000000000000163', '编辑', 2, 'mes:mes_xsl_rubber_quick_test_type:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000166', '1860000000000000163', '删除', 2, 'mes:mes_xsl_rubber_quick_test_type:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000167', '1860000000000000163', '批量删除', 2, 'mes:mes_xsl_rubber_quick_test_type:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000168', '1860000000000000163', '导出', 2, 'mes:mes_xsl_rubber_quick_test_type:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000169', '1860000000000000163', '导入', 2, 'mes:mes_xsl_rubber_quick_test_type:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000162',
|
||||||
|
'1860000000000000163',
|
||||||
|
'1860000000000000164', '1860000000000000165', '1860000000000000166', '1860000000000000167',
|
||||||
|
'1860000000000000168', '1860000000000000169'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
-- MES 胶料小料锁定日志 + 锁定原因字段 reason_desc:可整文件一次执行
|
||||||
|
-- 若已执行 V3.9.2_119 Flyway 可只跑本脚本补菜单/字段(幂等)
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_small_lock_log:*
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @reason_desc_exists := (
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'mes_xsl_rubber_small_lock_reason'
|
||||||
|
AND COLUMN_NAME = 'reason_desc'
|
||||||
|
);
|
||||||
|
SET @ddl_reason_desc := IF(
|
||||||
|
@reason_desc_exists = 0,
|
||||||
|
'ALTER TABLE `mes_xsl_rubber_small_lock_reason` ADD COLUMN `reason_desc` varchar(500) NOT NULL DEFAULT '''' COMMENT ''原因(手动输入,必填)'' AFTER `barcode_type`',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt_reason_desc FROM @ddl_reason_desc;
|
||||||
|
EXECUTE stmt_reason_desc;
|
||||||
|
DEALLOCATE PREPARE stmt_reason_desc;
|
||||||
|
|
||||||
|
UPDATE `mes_xsl_rubber_small_lock_reason` SET `reason_desc` = CONCAT('原因', `reason_code`) WHERE `reason_desc` = '' OR `reason_desc` IS NULL;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_small_lock_log` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`lock_reason_id` varchar(32) NOT NULL COMMENT '锁定原因ID',
|
||||||
|
`barcode_type` varchar(16) NOT NULL COMMENT '条码类型',
|
||||||
|
`barcode` varchar(128) NOT NULL COMMENT '条码',
|
||||||
|
`lock_type` varchar(16) NOT NULL COMMENT '状态',
|
||||||
|
`reason_desc` varchar(500) NOT NULL COMMENT '原因',
|
||||||
|
`log_date` date NOT NULL COMMENT '日期',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrsl_log_tenant_date` (`tenant_id`, `log_date`),
|
||||||
|
KEY `idx_mrsl_log_barcode` (`tenant_id`, `barcode_type`, `barcode`),
|
||||||
|
KEY `idx_mrsl_log_reason` (`lock_reason_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料小料锁定日志';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
'1860000000000000162'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000215', @mes_quality_pid, '胶料小料锁定日志', '/xslmes/mesXslRubberSmallLockLog', 'xslmes/mesXslRubberSmallLockLog/MesXslRubberSmallLockLogList', 'MesXslRubberSmallLockLogList', 1, NULL, '1', 7, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`sort_no` = VALUES(`sort_no`), `is_leaf` = 0, `status` = '1', `del_flag` = 0, `keep_alive` = VALUES(`keep_alive`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:file-text-outlined' WHERE `id` = '1860000000000000215' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000216', '1860000000000000215', '新增', 2, 'mes:mes_xsl_rubber_small_lock_log:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000217', '1860000000000000215', '编辑', 2, 'mes:mes_xsl_rubber_small_lock_log:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000218', '1860000000000000215', '删除', 2, 'mes:mes_xsl_rubber_small_lock_log:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000219', '1860000000000000215', '批量删除', 2, 'mes:mes_xsl_rubber_small_lock_log:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000220', '1860000000000000215', '导出', 2, 'mes:mes_xsl_rubber_small_lock_log:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000221', '1860000000000000215', '导入', 2, 'mes:mes_xsl_rubber_small_lock_log:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000215',
|
||||||
|
'1860000000000000216', '1860000000000000217', '1860000000000000218', '1860000000000000219',
|
||||||
|
'1860000000000000220', '1860000000000000221'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
-- MES 胶料小料锁定原因:字典 + 建表 + 菜单(质量管理下)+ 按钮 + 租户 admin 授权(可整文件一次执行)
|
||||||
|
-- 权限前缀:mes:mes_xsl_rubber_small_lock_reason:*
|
||||||
|
-- 菜单 ID 段:1860000000000000208(按钮 209-214)
|
||||||
|
-- 修改租户:改 SET @mes_tenant_id
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料小料锁定类型', 'xslmes_rubber_small_lock_type', '锁定/解锁', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_small_lock_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '锁定', 'lock', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_small_lock_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'lock');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '解锁', 'unlock', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_small_lock_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'unlock');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `type`, `tenant_id`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), 'MES胶料小料锁定条码类型', 'xslmes_rubber_small_lock_barcode_type', '小料/胶料', 0, 'admin', NOW(), 0, 0
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `sys_dict` WHERE `dict_code` = 'xslmes_rubber_small_lock_barcode_type' AND `del_flag` = 0);
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '小料', 'small', 1, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_small_lock_barcode_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'small');
|
||||||
|
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `sort_order`, `status`, `create_by`, `create_time`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), d.id, '胶料', 'rubber', 2, 1, 'admin', NOW() FROM `sys_dict` d
|
||||||
|
WHERE d.`dict_code` = 'xslmes_rubber_small_lock_barcode_type' AND NOT EXISTS (SELECT 1 FROM `sys_dict_item` i WHERE i.`dict_id` = d.id AND i.`item_value` = 'rubber');
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `mes_xsl_rubber_small_lock_reason` (
|
||||||
|
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||||
|
`reason_code` varchar(16) NOT NULL COMMENT '编号(租户内从001递增自动生成,只读)',
|
||||||
|
`lock_type` varchar(16) NOT NULL COMMENT '类型(字典xslmes_rubber_small_lock_type:lock锁定unlock解锁)',
|
||||||
|
`barcode_type` varchar(16) NOT NULL COMMENT '条码类型(字典xslmes_rubber_small_lock_barcode_type:small小料rubber胶料)',
|
||||||
|
`reason_desc` varchar(500) NOT NULL COMMENT '原因(手动输入,必填)',
|
||||||
|
`tenant_id` int DEFAULT NULL COMMENT '租户',
|
||||||
|
`sys_org_code` varchar(64) DEFAULT NULL COMMENT '部门',
|
||||||
|
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
|
||||||
|
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`del_flag` int DEFAULT '0' COMMENT '删除标记(0正常1删除)',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_mrslr_tenant_code` (`tenant_id`, `reason_code`),
|
||||||
|
KEY `idx_mrslr_tenant_lock` (`tenant_id`, `lock_type`, `barcode_type`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES胶料小料锁定原因';
|
||||||
|
|
||||||
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
|
SET @mes_quality_pid = IFNULL(
|
||||||
|
(SELECT `id` FROM `sys_permission` WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '质量管理' LIMIT 1),
|
||||||
|
'1860000000000000162'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
|
VALUES ('1860000000000000208', @mes_quality_pid, '胶料小料锁定原因', '/xslmes/mesXslRubberSmallLockReason', 'xslmes/mesXslRubberSmallLockReason/MesXslRubberSmallLockReasonList', 'MesXslRubberSmallLockReasonList', 1, NULL, '1', 6, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
|
`menu_type` = VALUES(`menu_type`), `sort_no` = VALUES(`sort_no`), `is_route` = VALUES(`is_route`), `is_leaf` = 0, `hidden` = 0, `status` = '1', `del_flag` = 0, `keep_alive` = VALUES(`keep_alive`);
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:lock-outlined' WHERE `id` = '1860000000000000208' AND `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `is_leaf`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
|
('1860000000000000209', '1860000000000000208', '新增', 2, 'mes:mes_xsl_rubber_small_lock_reason:add', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000210', '1860000000000000208', '编辑', 2, 'mes:mes_xsl_rubber_small_lock_reason:edit', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000211', '1860000000000000208', '删除', 2, 'mes:mes_xsl_rubber_small_lock_reason:delete', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000212', '1860000000000000208', '批量删除', 2, 'mes:mes_xsl_rubber_small_lock_reason:deleteBatch', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000213', '1860000000000000208', '导出', 2, 'mes:mes_xsl_rubber_small_lock_reason:exportXls', '1', 1, '1', 0, 'admin', NOW()),
|
||||||
|
('1860000000000000214', '1860000000000000208', '导入', 2, 'mes:mes_xsl_rubber_small_lock_reason:importExcel', '1', 1, '1', 0, 'admin', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`),
|
||||||
|
`is_leaf` = 1, `status` = '1', `del_flag` = 0;
|
||||||
|
|
||||||
|
INSERT INTO `sys_role_permission`(`id`, `role_id`, `permission_id`, `operate_date`, `operate_ip`)
|
||||||
|
SELECT REPLACE(UUID(), '-', ''), r.`id`, p.`id`, NOW(), '127.0.0.1'
|
||||||
|
FROM `sys_role` r
|
||||||
|
CROSS JOIN `sys_permission` p
|
||||||
|
WHERE r.`tenant_id` = @mes_tenant_id
|
||||||
|
AND r.`role_code` = 'admin'
|
||||||
|
AND p.`id` IN (
|
||||||
|
'1860000000000000208',
|
||||||
|
'1860000000000000209', '1860000000000000210', '1860000000000000211', '1860000000000000212',
|
||||||
|
'1860000000000000213', '1860000000000000214'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM `sys_role_permission` rp
|
||||||
|
WHERE rp.`role_id` = r.`id` AND rp.`permission_id` = p.`id`
|
||||||
|
);
|
||||||
@@ -26,19 +26,22 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_spare_part` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000105', @mes_base_pid, '备品件信息', '/xslmes/mesXslSparePart', 'xslmes/mesXslSparePart/MesXslSparePartList', NULL, 1, NULL, '1', 16, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000105', @mes_equip_pid, '备品件信息', '/xslmes/mesXslSparePart', 'xslmes/mesXslSparePart/MesXslSparePartList', NULL, 1, NULL, '1', 7, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:shopping-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:shopping-outlined' WHERE `id` = '1860000000000000105' AND `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000106', '1860000000000000105', '新增', 2, 'mes:mes_xsl_spare_part:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000106', '1860000000000000105', '新增', 2, 'mes:mes_xsl_spare_part:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
|||||||
@@ -20,19 +20,22 @@ CREATE TABLE IF NOT EXISTS `mes_xsl_spare_parts_category` (
|
|||||||
|
|
||||||
SET @mes_tenant_id = 1002;
|
SET @mes_tenant_id = 1002;
|
||||||
|
|
||||||
SET @mes_base_pid = (
|
SET @mes_equip_pid = (
|
||||||
SELECT MIN(`id`) FROM `sys_permission`
|
SELECT `id` FROM `sys_permission`
|
||||||
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` IN ('MES基础资料', 'MES资料')
|
WHERE `del_flag` = 0 AND `menu_type` = 0 AND `name` = '设备管理'
|
||||||
|
LIMIT 1
|
||||||
);
|
);
|
||||||
SET @mes_base_pid = IFNULL(@mes_base_pid, '1860000000000000001');
|
SET @mes_equip_pid = IFNULL(@mes_equip_pid, '1860000000000000133');
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `url`, `component`, `component_name`, `menu_type`, `perms`, `perms_type`, `sort_no`, `is_route`, `is_leaf`, `hidden`, `status`, `del_flag`, `keep_alive`, `internal_or_external`, `create_by`, `create_time`)
|
||||||
VALUES ('1860000000000000098', @mes_base_pid, '备品件类别', '/xslmes/mesXslSparePartsCategory', 'xslmes/mesXslSparePartsCategory/MesXslSparePartsCategoryList', NULL, 1, NULL, '1', 15, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
VALUES ('1860000000000000098', @mes_equip_pid, '备品件类别', '/xslmes/mesXslSparePartsCategory', 'xslmes/mesXslSparePartsCategory/MesXslSparePartsCategoryList', NULL, 1, NULL, '1', 6, 1, 0, 0, '1', 0, 1, 0, 'admin', NOW())
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
`parent_id` = VALUES(`parent_id`), `name` = VALUES(`name`), `url` = VALUES(`url`), `component` = VALUES(`component`), `component_name` = VALUES(`component_name`),
|
||||||
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
`menu_type` = VALUES(`menu_type`), `perms` = VALUES(`perms`), `perms_type` = VALUES(`perms_type`), `sort_no` = VALUES(`sort_no`),
|
||||||
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
`is_route` = VALUES(`is_route`), `is_leaf` = VALUES(`is_leaf`), `hidden` = VALUES(`hidden`), `status` = VALUES(`status`), `del_flag` = VALUES(`del_flag`),
|
||||||
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`);
|
`keep_alive` = VALUES(`keep_alive`), `internal_or_external` = VALUES(`internal_or_external`), `icon` = 'ant-design:tags-outlined';
|
||||||
|
|
||||||
|
UPDATE `sys_permission` SET `icon` = 'ant-design:tags-outlined' WHERE `id` = '1860000000000000098' AND `del_flag` = 0;
|
||||||
|
|
||||||
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
INSERT INTO `sys_permission`(`id`, `parent_id`, `name`, `menu_type`, `perms`, `perms_type`, `status`, `del_flag`, `create_by`, `create_time`) VALUES
|
||||||
('1860000000000000099', '1860000000000000098', '新增', 2, 'mes:mes_xsl_spare_parts_category:add', '1', '1', 0, 'admin', NOW()),
|
('1860000000000000099', '1860000000000000098', '新增', 2, 'mes:mes_xsl_spare_parts_category:add', '1', '1', 0, 'admin', NOW()),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.jeecgframework.boot3</groupId>
|
<groupId>org.jeecgframework.boot3</groupId>
|
||||||
<artifactId>jeecg-boot-parent</artifactId>
|
<artifactId>jeecg-boot-parent</artifactId>
|
||||||
<version>3.9.1</version>
|
<version>3.9.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>jeecg-boot-base-core</artifactId>
|
<artifactId>jeecg-boot-base-core</artifactId>
|
||||||
@@ -377,7 +377,7 @@
|
|||||||
<!-- chatgpt -->
|
<!-- chatgpt -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.boot3</groupId>
|
<groupId>org.jeecgframework.boot3</groupId>
|
||||||
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
|
<artifactId>jeecg-boot-starter-ai</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 腾讯云 -->
|
<!-- 腾讯云 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package org.jeecg.common.aspect;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.alibaba.fastjson.parser.Feature;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@@ -23,7 +21,11 @@ import org.springframework.data.redis.core.RedisTemplate;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -44,9 +46,6 @@ public class DictAspect {
|
|||||||
@Autowired
|
@Autowired
|
||||||
public RedisTemplate redisTemplate;
|
public RedisTemplate redisTemplate;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
private static final String JAVA_UTIL_DATE = "java.util.Date";
|
private static final String JAVA_UTIL_DATE = "java.util.Date";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,19 +112,25 @@ public class DictAspect {
|
|||||||
|
|
||||||
log.debug(" __ 进入字典翻译切面 DictAspect —— " );
|
log.debug(" __ 进入字典翻译切面 DictAspect —— " );
|
||||||
for (Object record : records) {
|
for (Object record : records) {
|
||||||
String json="{}";
|
//update-begin---author:scott ---date:2026-04-15 for:【issues/9543】改用反射直接读取字段构建 JSONObject,避免 ObjectMapper 对循环引用实体进行全量序列化导致 OOM;合并字典字段收集逻辑为同一次循环,避免对 getAllFields 遍历两遍;保留 【issues/#3629】@JsonFormat 的 Date 格式化兼容;保留 【issues/3303】字段顺序(LinkedHashMap)-----------
|
||||||
try {
|
JSONObject item = new JSONObject(true);
|
||||||
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
|
|
||||||
json = objectMapper.writeValueAsString(record);
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
log.error("json解析失败"+e.getMessage(),e);
|
|
||||||
}
|
|
||||||
// 代码逻辑说明: 【issues/3303】restcontroller返回json数据后key顺序错乱 -----
|
|
||||||
JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
|
|
||||||
|
|
||||||
//for (Field field : record.getClass().getDeclaredFields()) {
|
|
||||||
// 遍历所有字段,把字典Code取出来,放到 map 里
|
|
||||||
for (Field field : oConvertUtils.getAllFields(record)) {
|
for (Field field : oConvertUtils.getAllFields(record)) {
|
||||||
|
if (Modifier.isStatic(field.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//update-begin---author:scott ---date:2026-04-16 for:【issues/9543】优先通过 getter 方法读取字段值(兼容实体重写 getter 的场景),getter 不存在时 fallback 到直接读字段-----------
|
||||||
|
Object fieldValue = getFieldValue(record, field);
|
||||||
|
//update-end---author:scott ---date:2026-04-16 for:【issues/9543】优先通过 getter 方法读取字段值(兼容实体重写 getter 的场景),getter 不存在时 fallback 到直接读字段-----------
|
||||||
|
// 解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
|
||||||
|
if (fieldValue instanceof Date) {
|
||||||
|
JsonFormat jsonFormat = field.getAnnotation(JsonFormat.class);
|
||||||
|
if (jsonFormat != null && oConvertUtils.isNotEmpty(jsonFormat.pattern())) {
|
||||||
|
fieldValue = new SimpleDateFormat(jsonFormat.pattern()).format((Date) fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.put(field.getName(), fieldValue);
|
||||||
|
|
||||||
|
// 遍历所有字段,把字典Code取出来,放到 map 里
|
||||||
String value = item.getString(field.getName());
|
String value = item.getString(field.getName());
|
||||||
if (oConvertUtils.isEmpty(value)) {
|
if (oConvertUtils.isEmpty(value)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -154,6 +159,7 @@ public class DictAspect {
|
|||||||
// item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
|
// item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
//update-end---author:scott ---date:2026-04-15 for:【issues/9543】改用反射直接读取字段构建 JSONObject,避免 ObjectMapper 对循环引用实体进行全量序列化导致 OOM;合并字典字段收集逻辑为同一次循环,避免对 getAllFields 遍历两遍;保留 【issues/#3629】@JsonFormat 的 Date 格式化兼容;保留 【issues/3303】字段顺序(LinkedHashMap)-----------
|
||||||
items.add(item);
|
items.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +423,30 @@ public class DictAspect {
|
|||||||
return textValue.toString();
|
return textValue.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update-begin---author:scott ---date:2026-04-16 for:【issues/9543】优先通过 getter 方法读取字段值(兼容实体重写 getter 的场景),getter 不存在时 fallback 到直接读字段-----------
|
||||||
|
/**
|
||||||
|
* 优先通过 PropertyDescriptor 获取 getter 方法读取字段值,兼容实体重写 getter 的场景;
|
||||||
|
* getter 不存在或调用异常时 fallback 到直接反射读字段。
|
||||||
|
*/
|
||||||
|
private Object getFieldValue(Object record, Field field) {
|
||||||
|
try {
|
||||||
|
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), record.getClass());
|
||||||
|
Method readMethod = pd.getReadMethod();
|
||||||
|
if (readMethod != null) {
|
||||||
|
return readMethod.invoke(record);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
return field.get(record);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
log.error("反射读取字段失败: " + field.getName(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-end---author:scott ---date:2026-04-16 for:【issues/9543】优先通过 getter 方法读取字段值(兼容实体重写 getter 的场景),getter 不存在时 fallback 到直接读字段-----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测返回结果集中是否包含Dict注解
|
* 检测返回结果集中是否包含Dict注解
|
||||||
* @param records
|
* @param records
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ public enum FileTypeEnum {
|
|||||||
mp4(".mp4","video","视频"),
|
mp4(".mp4","video","视频"),
|
||||||
zip(".zip","zip","压缩包"),
|
zip(".zip","zip","压缩包"),
|
||||||
pdf(".pdf","pdf","pdf"),
|
pdf(".pdf","pdf","pdf"),
|
||||||
mp3(".mp3","mp3","语音");
|
mp3(".mp3","mp3","语音"),
|
||||||
|
wav(".wav","wav","语音");
|
||||||
|
|
||||||
private String type;
|
private String type;
|
||||||
private String value;
|
private String value;
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ public enum UniPushTypeEnum {
|
|||||||
/**
|
/**
|
||||||
* 系统消息
|
* 系统消息
|
||||||
*/
|
*/
|
||||||
SYS_MSG("system", "系统消息", "收到一条系统通告");
|
SYS_MSG("system", "系统消息", "收到一条系统通告"),
|
||||||
|
/**
|
||||||
|
* 协同工作
|
||||||
|
*/
|
||||||
|
COLLABORATION_MSG("collaboration", "系统消息", "收到一条协同工作消息");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务类型(chat:聊天 bpm_task:流程 bpm_cc:流程抄送)
|
* 业务类型(chat:聊天 bpm_task:流程 bpm_cc:流程抄送)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
|||||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||||
|
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -105,6 +106,23 @@ public class JeecgBootExceptionHandler {
|
|||||||
return Result.error(404, "路径不存在,请检查路径是否正确");
|
return Result.error(404, "路径不存在,请检查路径是否正确");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理静态资源不存在异常(Spring Boot 3.2+)
|
||||||
|
* WebSocket路径被当作静态资源请求时会触发此异常,降级为debug日志避免刷屏
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NoResourceFoundException.class)
|
||||||
|
public Result<?> handleNoResourceFoundException(NoResourceFoundException e, HttpServletRequest request) {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
// WebSocket路径的非upgrade请求,降级为debug日志
|
||||||
|
if (uri.contains("Socket/") || uri.contains("websocket/") || uri.contains("Websocket/")) {
|
||||||
|
log.debug("WebSocket路径被当作静态资源请求: {}", uri);
|
||||||
|
} else {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
addSysLog(e);
|
||||||
|
}
|
||||||
|
return Result.error(404, "路径不存在,请检查路径是否正确");
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(DuplicateKeyException.class)
|
@ExceptionHandler(DuplicateKeyException.class)
|
||||||
public Result<?> handleDuplicateKeyException(DuplicateKeyException e){
|
public Result<?> handleDuplicateKeyException(DuplicateKeyException e){
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
|
|||||||
@@ -135,7 +135,11 @@ public class QueryGenerator {
|
|||||||
//权限规则自定义SQL表达式
|
//权限规则自定义SQL表达式
|
||||||
for (String c : ruleMap.keySet()) {
|
for (String c : ruleMap.keySet()) {
|
||||||
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
|
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
|
||||||
queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
|
// update-begin---author:sunjianlei ---date:20260331 for:【#9434】修复 QueryGenerator 自定义权限规则逻辑存在 SQL 注入漏洞
|
||||||
|
String sqlRule = getSqlRuleValue(ruleMap.get(c).getRuleValue());
|
||||||
|
SqlInjectionUtil.filterContent(sqlRule, null);
|
||||||
|
queryWrapper.and(i ->i.apply(sqlRule));
|
||||||
|
// update-end-----author:sunjianlei ---date:20260331 for:【#9434】修复 QueryGenerator 自定义权限规则逻辑存在 SQL 注入漏洞
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,26 +169,23 @@ public class QueryGenerator {
|
|||||||
//区间查询
|
//区间查询
|
||||||
doIntervalQuery(queryWrapper, parameterMap, type, name, column);
|
doIntervalQuery(queryWrapper, parameterMap, type, name, column);
|
||||||
//判断单值 参数带不同标识字符串 走不同的查询
|
//判断单值 参数带不同标识字符串 走不同的查询
|
||||||
//TODO 这种前后带逗号的支持分割后模糊查询(多选字段查询生效) 示例:,1,3,
|
// update-begin--author:claude--date:20260330--for:【issues/9265】多选字段查询精确匹配,避免值1匹配到值10
|
||||||
|
//多选字段查询生效 示例:,1,3, 使用精确边界匹配(兼容所有数据库)
|
||||||
if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
|
if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
|
||||||
String multiLikeval = value.toString().replace(",,", COMMA);
|
String multiLikeval = value.toString().replace(",,", COMMA);
|
||||||
String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
|
String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
|
||||||
final String field = oConvertUtils.camelToUnderline(column);
|
final String field = oConvertUtils.camelToUnderline(column);
|
||||||
if(vals.length>1) {
|
queryWrapper.and(j -> {
|
||||||
queryWrapper.and(j -> {
|
log.info("---查询过滤器,Query规则(多选精确匹配)---field:{}, rule:{}, value:{}", field, "multi_select", vals[0]);
|
||||||
log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
|
j = j.eq(field, vals[0]).or().likeRight(field, vals[0] + ",").or().like(field, "," + vals[0] + ",").or().likeLeft(field, "," + vals[0]);
|
||||||
j = j.like(field,vals[0]);
|
for (int k = 1; k < vals.length; k++) {
|
||||||
for (int k=1;k<vals.length;k++) {
|
log.info("---查询过滤器,Query规则(多选精确匹配) .or()---field:{}, rule:{}, value:{}", field, "multi_select", vals[k]);
|
||||||
j = j.or().like(field,vals[k]);
|
j = j.or().eq(field, vals[k]).or().likeRight(field, vals[k] + ",").or().like(field, "," + vals[k] + ",").or().likeLeft(field, "," + vals[k]);
|
||||||
log.info("---查询过滤器,Query规则 .or()---field:{}, rule:{}, value:{}", field, "like", vals[k]);
|
}
|
||||||
}
|
});
|
||||||
//return j;
|
}
|
||||||
});
|
// update-end--author:claude--date:20260330--for:【issues/9265】多选字段查询精确匹配,避免值1匹配到值10
|
||||||
}else {
|
else {
|
||||||
log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
|
|
||||||
queryWrapper.and(j -> j.like(field,vals[0]));
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
// 代码逻辑说明: [TV360X-378]增加自定义字段查询规则功能------------
|
// 代码逻辑说明: [TV360X-378]增加自定义字段查询规则功能------------
|
||||||
QueryRuleEnum rule;
|
QueryRuleEnum rule;
|
||||||
if(null != customRuleMap && customRuleMap.containsKey(name)) {
|
if(null != customRuleMap && customRuleMap.containsKey(name)) {
|
||||||
@@ -576,10 +577,16 @@ public class QueryGenerator {
|
|||||||
value = val.substring(1, val.length() - 1);
|
value = val.substring(1, val.length() - 1);
|
||||||
//mysql 模糊查询之特殊字符下划线 (_、\)
|
//mysql 模糊查询之特殊字符下划线 (_、\)
|
||||||
value = specialStrConvert(value.toString());
|
value = specialStrConvert(value.toString());
|
||||||
} else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) {
|
} else if (rule == QueryRuleEnum.LEFT_LIKE) {
|
||||||
value = val.substring(1);
|
value = val.substring(1);
|
||||||
//mysql 模糊查询之特殊字符下划线 (_、\)
|
//mysql 模糊查询之特殊字符下划线 (_、\)
|
||||||
value = specialStrConvert(value.toString());
|
value = specialStrConvert(value.toString());
|
||||||
|
//update-begin---author:scott ---date:20260416 for:【PR#9322】修复NE规则与LEFT_LIKE共用substring(1)导致ID首位字符丢失-----------
|
||||||
|
} else if (rule == QueryRuleEnum.NE) {
|
||||||
|
if (val.startsWith(QueryRuleEnum.NE.getValue())) {
|
||||||
|
value = val.substring(1);
|
||||||
|
}
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9322】修复NE规则与LEFT_LIKE共用substring(1)导致ID首位字符丢失-----------
|
||||||
} else if (rule == QueryRuleEnum.RIGHT_LIKE) {
|
} else if (rule == QueryRuleEnum.RIGHT_LIKE) {
|
||||||
value = val.substring(0, val.length() - 1);
|
value = val.substring(0, val.length() - 1);
|
||||||
//mysql 模糊查询之特殊字符下划线 (_、\)
|
//mysql 模糊查询之特殊字符下划线 (_、\)
|
||||||
@@ -754,6 +761,7 @@ public class QueryGenerator {
|
|||||||
queryWrapper.notLikeRight(name, value);
|
queryWrapper.notLikeRight(name, value);
|
||||||
break;
|
break;
|
||||||
// 代码逻辑说明: [TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
|
// 代码逻辑说明: [TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
|
||||||
|
// update-begin--author:claude--date:20260330--for:【issues/9265】LIKE_WITH_OR多选查询精确匹配,避免值1匹配到值10
|
||||||
case LIKE_WITH_OR:
|
case LIKE_WITH_OR:
|
||||||
final String nameFinal = name;
|
final String nameFinal = name;
|
||||||
Object[] vals;
|
Object[] vals;
|
||||||
@@ -769,14 +777,15 @@ public class QueryGenerator {
|
|||||||
vals = new Object[]{value};
|
vals = new Object[]{value};
|
||||||
}
|
}
|
||||||
queryWrapper.and(j -> {
|
queryWrapper.and(j -> {
|
||||||
log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", nameFinal, "like", vals[0]);
|
log.info("---查询过滤器,Query规则(多选精确匹配)---field:{}, rule:{}, value:{}", nameFinal, "multi_select", vals[0]);
|
||||||
j = j.like(nameFinal, vals[0]);
|
j = j.eq(nameFinal, vals[0]).or().likeRight(nameFinal, vals[0] + ",").or().like(nameFinal, "," + vals[0] + ",").or().likeLeft(nameFinal, "," + vals[0]);
|
||||||
for (int k = 1; k < vals.length; k++) {
|
for (int k = 1; k < vals.length; k++) {
|
||||||
j = j.or().like(nameFinal, vals[k]);
|
log.info("---查询过滤器,Query规则(多选精确匹配) .or()---field:{}, rule:{}, value:{}", nameFinal, "multi_select", vals[k]);
|
||||||
log.info("---查询过滤器,Query规则 .or()---field:{}, rule:{}, value:{}", nameFinal, "like", vals[k]);
|
j = j.or().eq(nameFinal, vals[k]).or().likeRight(nameFinal, vals[k] + ",").or().like(nameFinal, "," + vals[k] + ",").or().likeLeft(nameFinal, "," + vals[k]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
// update-end--author:claude--date:20260330--for:【issues/9265】LIKE_WITH_OR多选查询精确匹配,避免值1匹配到值10
|
||||||
default:
|
default:
|
||||||
log.info("--查询规则未匹配到---");
|
log.info("--查询规则未匹配到---");
|
||||||
break;
|
break;
|
||||||
@@ -984,7 +993,11 @@ public class QueryGenerator {
|
|||||||
PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
|
PropertyDescriptor[] origDescriptors = PropertyUtils.getPropertyDescriptors(clazz);
|
||||||
for (String c : ruleMap.keySet()) {
|
for (String c : ruleMap.keySet()) {
|
||||||
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
|
if(oConvertUtils.isNotEmpty(c) && c.startsWith(SQL_RULES_COLUMN)){
|
||||||
queryWrapper.and(i ->i.apply(getSqlRuleValue(ruleMap.get(c).getRuleValue())));
|
// update-begin---author:sunjianlei ---date:20260331 for:【#9434】修复 QueryGenerator 自定义权限规则逻辑存在 SQL 注入漏洞
|
||||||
|
String sqlRule = getSqlRuleValue(ruleMap.get(c).getRuleValue());
|
||||||
|
SqlInjectionUtil.filterContent(sqlRule, null);
|
||||||
|
queryWrapper.and(i ->i.apply(sqlRule));
|
||||||
|
// update-end-----author:sunjianlei ---date:20260331 for:【#9434】修复 QueryGenerator 自定义权限规则逻辑存在 SQL 注入漏洞
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String name, column;
|
String name, column;
|
||||||
|
|||||||
@@ -144,7 +144,9 @@ public class ResourceUtil {
|
|||||||
*/
|
*/
|
||||||
private static void processEnumClass(String classname) {
|
private static void processEnumClass(String classname) {
|
||||||
try {
|
try {
|
||||||
Class<?> clazz = Class.forName(classname);
|
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
|
Class<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
|
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
|
||||||
|
|
||||||
if (enumDict != null) {
|
if (enumDict != null) {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public class SysDepartModel {
|
|||||||
private String departNameEn;
|
private String departNameEn;
|
||||||
/**缩写*/
|
/**缩写*/
|
||||||
private String departNameAbbr;
|
private String departNameAbbr;
|
||||||
|
/**机构/部门路径名称*/
|
||||||
|
private String departPathName;
|
||||||
/**排序*/
|
/**排序*/
|
||||||
private Integer departOrder;
|
private Integer departOrder;
|
||||||
/**描述*/
|
/**描述*/
|
||||||
@@ -74,6 +76,14 @@ public class SysDepartModel {
|
|||||||
this.departNameAbbr = departNameAbbr;
|
this.departNameAbbr = departNameAbbr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDepartPathName() {
|
||||||
|
return departPathName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDepartPathName(String departPathName) {
|
||||||
|
this.departPathName = departPathName;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getDepartOrder() {
|
public Integer getDepartOrder() {
|
||||||
return departOrder;
|
return departOrder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -61,7 +63,18 @@ public class CommonUtils {
|
|||||||
//update-end---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的,导致不展示---
|
//update-end---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的,导致不展示---
|
||||||
try {
|
try {
|
||||||
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
|
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
|
||||||
File file = new File(basePath + File.separator + bizPath + File.separator );
|
//update-begin---author:wangshuai---date:2026-03-30---for:【issues/9435】uploadOnlineImage路径遍历漏洞修复---
|
||||||
|
// 1. 使用已有的路径遍历检查
|
||||||
|
SsrfFileTypeFilter.checkPathTraversal(bizPath);
|
||||||
|
// 2. 标准化路径并校验是否在basePath范围内
|
||||||
|
Path root = Paths.get(basePath).toAbsolutePath().normalize();
|
||||||
|
Path targetDir = root.resolve(bizPath).toAbsolutePath().normalize();
|
||||||
|
if (!targetDir.startsWith(root)) {
|
||||||
|
log.error("检测到路径遍历攻击!非法 bizPath: {}", bizPath);
|
||||||
|
throw new SecurityException("Illegal access to path outside of base directory.");
|
||||||
|
}
|
||||||
|
File file = targetDir.toFile();
|
||||||
|
//update-end---author:wangshuai---date:2026-03-30---for:【issues/9435】uploadOnlineImage路径遍历漏洞修复---
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
file.mkdirs();// 创建文件根目录
|
file.mkdirs();// 创建文件根目录
|
||||||
}
|
}
|
||||||
@@ -159,7 +172,14 @@ public class CommonUtils {
|
|||||||
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
|
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
|
||||||
|
|
||||||
String fileName = null;
|
String fileName = null;
|
||||||
File file = new File(uploadpath + File.separator + bizPath + File.separator );
|
//update-begin---author:liusq ---date:2026-03-30 for:【issues/9428】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------
|
||||||
|
// 路径遍历校验:规范化后确保目标目录在uploadpath内
|
||||||
|
File uploadDir = new File(uploadpath).getCanonicalFile();
|
||||||
|
File file = new File(uploadpath + File.separator + bizPath + File.separator).getCanonicalFile();
|
||||||
|
if (!file.toPath().startsWith(uploadDir.toPath())) {
|
||||||
|
throw new JeecgBootException("非法业务路径,禁止访问上传目录之外的路径: " + bizPath);
|
||||||
|
}
|
||||||
|
//update-end---author:liusq ---date:2026-03-30 for:【issues/9428】修复uploadLocal bizPath路径遍历漏洞(CWE-22)-----------
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
// 创建文件根目录
|
// 创建文件根目录
|
||||||
file.mkdirs();
|
file.mkdirs();
|
||||||
@@ -198,8 +218,14 @@ public class CommonUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一全局上传 带桶
|
* 统一全局上传(支持自定义桶)
|
||||||
* @Return: java.lang.String
|
* 根据 uploadType 自动选择 MinIO 或 阿里云OSS 进行文件上传
|
||||||
|
*
|
||||||
|
* @param file 待上传的文件
|
||||||
|
* @param bizPath 业务路径,作为文件存储的目录前缀(如 "upload/images")
|
||||||
|
* @param uploadType 上传方式:{@link CommonConstant#UPLOAD_TYPE_MINIO} 使用MinIO,其他使用阿里云OSS
|
||||||
|
* @param customBucket 自定义桶名称,为空则使用各存储服务的默认桶
|
||||||
|
* @return 文件访问URL,上传失败返回空字符串
|
||||||
*/
|
*/
|
||||||
public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
|
public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
|
||||||
String url = "";
|
String url = "";
|
||||||
@@ -368,7 +394,7 @@ public class CommonUtils {
|
|||||||
}else{
|
}else{
|
||||||
baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
|
baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
|
||||||
}
|
}
|
||||||
log.info("-----Common getBaseUrl----- : " + baseDomainPath);
|
log.debug("-----获取当前服务 BaseUrl----- : " + baseDomainPath);
|
||||||
return baseDomainPath;
|
return baseDomainPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,22 @@ public class FileDownloadUtils {
|
|||||||
* @date 2024/1/19 10:09
|
* @date 2024/1/19 10:09
|
||||||
*/
|
*/
|
||||||
public static String download2DiskFromNet(String fileUrl, String storePath) {
|
public static String download2DiskFromNet(String fileUrl, String storePath) {
|
||||||
|
//update-begin---author:liusq ---date:2026-03-30 for:【issues/9437】修复download2DiskFromNet storePath路径遍历漏洞(CWE-22)-----------
|
||||||
|
// 路径遍历校验:拦截 ../ 等遍历字符,并确保规范化路径与原始路径一致
|
||||||
|
SsrfFileTypeFilter.checkPathTraversal(storePath);
|
||||||
|
try {
|
||||||
|
String canonicalPath = new File(storePath).getCanonicalPath();
|
||||||
|
String absolutePath = new File(storePath).getAbsolutePath();
|
||||||
|
if (!canonicalPath.equals(absolutePath)) {
|
||||||
|
throw new JeecgBootException("非法存储路径,路径包含遍历字符: " + storePath);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new JeecgBootException("存储路径校验失败: " + storePath, e);
|
||||||
|
}
|
||||||
|
//update-end---author:liusq ---date:2026-03-30 for:【issues/9437】修复download2DiskFromNet storePath路径遍历漏洞(CWE-22)-----------
|
||||||
|
//update-begin---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】下载网络资源前增加SSRF校验-----------
|
||||||
|
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
|
||||||
|
//update-end---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】下载网络资源前增加SSRF校验-----------
|
||||||
try {
|
try {
|
||||||
URL url = new URL(fileUrl);
|
URL url = new URL(fileUrl);
|
||||||
URLConnection conn = url.openConnection();
|
URLConnection conn = url.openConnection();
|
||||||
@@ -260,6 +276,9 @@ public class FileDownloadUtils {
|
|||||||
try {
|
try {
|
||||||
// 处理HTTP URL:通过网络下载
|
// 处理HTTP URL:通过网络下载
|
||||||
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||||
|
//update-begin---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】修复二次SSRF漏洞,对HTTP下载URL进行安全校验-----------
|
||||||
|
SsrfFileTypeFilter.checkSsrfHttpUrl(fileUrl);
|
||||||
|
//update-end---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】修复二次SSRF漏洞,对HTTP下载URL进行安全校验-----------
|
||||||
URL url = new URL(fileUrl);
|
URL url = new URL(fileUrl);
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setConnectTimeout(5000); // 连接超时5秒
|
connection.setConnectTimeout(5000); // 连接超时5秒
|
||||||
|
|||||||
@@ -73,8 +73,20 @@ public class FillRuleUtil {
|
|||||||
if (formData == null) {
|
if (formData == null) {
|
||||||
formData = new JSONObject();
|
formData = new JSONObject();
|
||||||
}
|
}
|
||||||
// 通过反射执行配置的类里的方法
|
// 包路径白名单校验,防止任意类加载漏洞
|
||||||
IFillRuleHandler ruleHandler = (IFillRuleHandler) Class.forName(ruleClass).newInstance();
|
if (!ruleClass.startsWith("org.jeecg.")) {
|
||||||
|
log.error("检测到非法填值规则类加载尝试: {}", ruleClass);
|
||||||
|
throw new SecurityException("不允许加载非 org.jeecg 包路径下的填值规则类: " + ruleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过反射执行配置的类里的方法(先加载类并校验接口,再实例化)
|
||||||
|
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
|
Class<?> clazz = Class.forName(ruleClass, true, Thread.currentThread().getContextClassLoader());
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
|
if (!IFillRuleHandler.class.isAssignableFrom(clazz)) {
|
||||||
|
throw new IllegalArgumentException("类 " + ruleClass + " 未实现 IFillRuleHandler 接口");
|
||||||
|
}
|
||||||
|
IFillRuleHandler ruleHandler = (IFillRuleHandler) clazz.getDeclaredConstructor().newInstance();
|
||||||
return ruleHandler.execute(params, formData);
|
return ruleHandler.execute(params, formData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -152,11 +152,13 @@ public class MinioUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件外链
|
* 获取私有桶文件的预签名访问URL(带过期时间)
|
||||||
* @param bucketName
|
* 通过MinIO预签名机制生成临时GET链接,无需公开桶即可让外部访问文件
|
||||||
* @param objectName
|
*
|
||||||
* @param expires
|
* @param bucketName 桶名称
|
||||||
* @return
|
* @param objectName 文件对象路径(如 "eoafile/2026/04/test.pdf")
|
||||||
|
* @param expires 链接有效期,单位:秒(注意不是天)
|
||||||
|
* @return 预签名URL,失败返回null
|
||||||
*/
|
*/
|
||||||
public static String getObjectUrl(String bucketName, String objectName, Integer expires) {
|
public static String getObjectUrl(String bucketName, String objectName, Integer expires) {
|
||||||
initMinio(minioUrl, minioName,minioPass);
|
initMinio(minioUrl, minioName,minioPass);
|
||||||
@@ -195,10 +197,13 @@ public class MinioUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件到minio
|
* 通过输入流上传文件到MinIO默认桶
|
||||||
* @param stream
|
* 若桶不存在会自动创建,上传成功后关闭输入流
|
||||||
* @param relativePath
|
*
|
||||||
* @return
|
* @param stream 文件输入流
|
||||||
|
* @param relativePath 文件在桶中的相对路径(如 "upload/2026/04/test.pdf")
|
||||||
|
* @return 文件完整访问URL(格式:minioUrl + bucketName + "/" + relativePath)
|
||||||
|
* @throws Exception 桶操作或上传过程中的异常
|
||||||
*/
|
*/
|
||||||
public static String upload(InputStream stream,String relativePath) throws Exception {
|
public static String upload(InputStream stream,String relativePath) throws Exception {
|
||||||
initMinio(minioUrl, minioName,minioPass);
|
initMinio(minioUrl, minioName,minioPass);
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ public class MyClassLoader extends ClassLoader {
|
|||||||
public static Class getClassByScn(String className) {
|
public static Class getClassByScn(String className) {
|
||||||
Class myclass = null;
|
Class myclass = null;
|
||||||
try {
|
try {
|
||||||
myclass = Class.forName(className);
|
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
|
myclass = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(className+" not found!");
|
throw new RuntimeException(className+" not found!");
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
|||||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -235,7 +237,7 @@ public class RestUtil {
|
|||||||
}
|
}
|
||||||
// 发送请求
|
// 发送请求
|
||||||
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
||||||
return RT.exchange(url, method, request, responseType);
|
return RT.exchange(URI.create(url), method, request, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,7 +310,7 @@ public class RestUtil {
|
|||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
||||||
return restTemplate.exchange(url, method, request, responseType);
|
return restTemplate.exchange(URI.create(url), method, request, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,7 +343,10 @@ public class RestUtil {
|
|||||||
Object object = source.get(key);
|
Object object = source.get(key);
|
||||||
if (object != null) {
|
if (object != null) {
|
||||||
if (!StringUtils.isEmpty(object.toString())) {
|
if (!StringUtils.isEmpty(object.toString())) {
|
||||||
value = object.toString();
|
//update-begin---author:sjlei---date:20260414 for:【jeecg-ai#17】修复工具节点参数值含{}时URI模板展开报错-----------
|
||||||
|
// URL 编码参数值,防止值中含 {}、空格等特殊字符导致 URI 解析异常
|
||||||
|
value = URLEncoder.encode(object.toString(), StandardCharsets.UTF_8);
|
||||||
|
//update-end-----author:sjlei---date:20260414 for:【jeecg-ai#17】修复工具节点参数值含{}时URI模板展开报错-----------
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
urlVariables.append("&").append(key).append("=").append(value);
|
urlVariables.append("&").append(key).append("=").append(value);
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ public class SqlInjectionUtil {
|
|||||||
private static String specialReportXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--";
|
private static String specialReportXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--";
|
||||||
/**
|
/**
|
||||||
* 字典专用—sql注入关键词
|
* 字典专用—sql注入关键词
|
||||||
|
*
|
||||||
|
* @updateBy: sunjianlei 20260331 加上 substring 注入检测
|
||||||
*/
|
*/
|
||||||
private static String specialDictSqlXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|--";
|
private static String specialDictSqlXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|--|substring |substring(";
|
||||||
/**
|
/**
|
||||||
* 完整匹配的key,不需要考虑前空格
|
* 完整匹配的key,不需要考虑前空格
|
||||||
*/
|
*/
|
||||||
@@ -62,6 +64,27 @@ public class SqlInjectionUtil {
|
|||||||
"show\\s+databases",
|
"show\\s+databases",
|
||||||
"sleep\\(\\d*\\)",
|
"sleep\\(\\d*\\)",
|
||||||
"sleep\\(.*\\)",
|
"sleep\\(.*\\)",
|
||||||
|
// update-begin---author:sjlei---date:20260413 for:【#9523】修复 SQL 注入漏洞
|
||||||
|
// 时间盲注函数(#9523):MySQL BENCHMARK、PostgreSQL pg_sleep、SQL Server WAITFOR DELAY
|
||||||
|
"benchmark\\s*\\(",
|
||||||
|
"pg_sleep\\s*\\(",
|
||||||
|
"waitfor\\s+delay",
|
||||||
|
// update-end-----author:sjlei---date:20260413 for:【#9523】修复 SQL 注入漏洞
|
||||||
|
// update-begin---author:zhangdaihao---date:20260427 for:【issue/9571】修复字典/Online报表 boolean-blind 信息泄露
|
||||||
|
// 通过 case-when + database()/version() 等函数 + LIKE 前缀枚举进行字符级数据提取(绕过 select/union 黑名单),
|
||||||
|
"database\\s*\\(",
|
||||||
|
"version\\s*\\(",
|
||||||
|
"current_user\\s*\\(",
|
||||||
|
"current_database\\s*\\(",
|
||||||
|
"current_schema\\s*\\(",
|
||||||
|
"session_user\\s*\\(",
|
||||||
|
"system_user\\s*\\(",
|
||||||
|
"ascii\\s*\\(",
|
||||||
|
"unhex\\s*\\(",
|
||||||
|
"load_file\\s*\\(",
|
||||||
|
"into\\s+outfile",
|
||||||
|
"into\\s+dumpfile",
|
||||||
|
// update-end-----author:zhangdaihao---date:20260427 for:【issue/9571】修复字典/Online报表 boolean-blind 信息泄露
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* sql注释的正则
|
* sql注释的正则
|
||||||
@@ -146,7 +169,20 @@ public class SqlInjectionUtil {
|
|||||||
private static boolean isExistSqlInjectKeyword(String sql, String keyword) {
|
private static boolean isExistSqlInjectKeyword(String sql, String keyword) {
|
||||||
if (sql.startsWith(keyword.trim())) {
|
if (sql.startsWith(keyword.trim())) {
|
||||||
return true;
|
return true;
|
||||||
} else if (sql.contains(keyword)) {
|
}
|
||||||
|
// update-begin---author:zhangdaihao---date:20260427 for:【issue/9572】修复 SQL 黑名单 keyword( 紧贴形式绕过
|
||||||
|
// 原来对带 trailing space 的关键字(如 "select ")只能匹配 "select " 形式,
|
||||||
|
// 导致 id=(select(id)from(sys_user)where(...)) 的 select( 形式绕过检测。
|
||||||
|
// 这里补充:对带 trailing space 的关键字,额外检测 trimmedKeyword + "(" 形式。
|
||||||
|
// FULL_MATCHING_KEYWRODS(;、+、--)保持原匹配逻辑不变。
|
||||||
|
if (keyword.endsWith(" ") && !FULL_MATCHING_KEYWRODS.contains(keyword)) {
|
||||||
|
String trimmedKeyword = keyword.trim();
|
||||||
|
if (sql.contains(trimmedKeyword + "(")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update-end-----author:zhangdaihao---date:20260427 for:【issue/9572】修复 SQL 黑名单 keyword( 紧贴形式绕过
|
||||||
|
if (sql.contains(keyword)) {
|
||||||
// 需要匹配的,sql注入关键词
|
// 需要匹配的,sql注入关键词
|
||||||
String matchingText = " " + keyword;
|
String matchingText = " " + keyword;
|
||||||
if(FULL_MATCHING_KEYWRODS.contains(keyword)){
|
if(FULL_MATCHING_KEYWRODS.contains(keyword)){
|
||||||
@@ -156,6 +192,18 @@ public class SqlInjectionUtil {
|
|||||||
if (sql.contains(matchingText)) {
|
if (sql.contains(matchingText)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
// update-begin---author:sjlei---date:20260413 for:【#9524】修复 SQL 注入漏洞
|
||||||
|
// 检测关键词前紧跟非字母分隔符的情况,原来只检测前置空格,
|
||||||
|
// 导致 (updatexml(、(extractvalue( 等写法绕过检测(#9524)
|
||||||
|
String[] sqlTokenPrefixes = {"(", ",", "=", "!", "<", ">"};
|
||||||
|
for (String prefix : sqlTokenPrefixes) {
|
||||||
|
if (sql.contains(prefix + keyword)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update-end-----author:sjlei---date:20260413 for:【#9524】修复 SQL 注入漏洞
|
||||||
|
|
||||||
|
// 检测编码空格绕过(%09 %0A %0D 等可替代空格的字符)
|
||||||
String regularStr = "\\s+\\S+" + keyword;
|
String regularStr = "\\s+\\S+" + keyword;
|
||||||
List<String> resultFindAll = ReUtil.findAll(regularStr, sql, 0, new ArrayList<String>());
|
List<String> resultFindAll = ReUtil.findAll(regularStr, sql, 0, new ArrayList<String>());
|
||||||
for (String res : resultFindAll) {
|
for (String res : resultFindAll) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.jeecg.common.exception.JeecgBootException;
|
|||||||
import org.jeecg.common.system.vo.DynamicDataSourceModel;
|
import org.jeecg.common.system.vo.DynamicDataSourceModel;
|
||||||
import org.jeecg.common.util.ReflectHelper;
|
import org.jeecg.common.util.ReflectHelper;
|
||||||
import org.jeecg.common.util.oConvertUtils;
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
|
import org.jeecg.common.util.security.JdbcSecurityUtil;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||||
|
|
||||||
@@ -42,7 +43,10 @@ public class DynamicDBUtil {
|
|||||||
if (oConvertUtils.isEmpty(url) || !url.toLowerCase().startsWith("jdbc:")) {
|
if (oConvertUtils.isEmpty(url) || !url.toLowerCase().startsWith("jdbc:")) {
|
||||||
throw new JeecgBootException("数据源URL配置格式不正确!");
|
throw new JeecgBootException("数据源URL配置格式不正确!");
|
||||||
}
|
}
|
||||||
|
// 纵深防御: 连接建立时二次校验 URL 和驱动安全性
|
||||||
|
JdbcSecurityUtil.validate(url);
|
||||||
|
JdbcSecurityUtil.validateDriver(driverClassName);
|
||||||
|
|
||||||
String dbUser = dbSource.getDbUsername();
|
String dbUser = dbSource.getDbUsername();
|
||||||
String dbPassword = dbSource.getDbPassword();
|
String dbPassword = dbSource.getDbPassword();
|
||||||
dataSource.setDriverClassName(driverClassName);
|
dataSource.setDriverClassName(driverClassName);
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -305,6 +309,50 @@ public class SsrfFileTypeFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update-begin---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】修复二次SSRF漏洞,对HTTP下载URL进行安全校验-----------
|
||||||
|
/**
|
||||||
|
* 校验HTTP(S) URL,防止SSRF攻击(最小化拦截,只挡真正危险的目标)。
|
||||||
|
* 规则:
|
||||||
|
* 1. 仅允许 http / https 协议;
|
||||||
|
* 2. 解析主机IP,拒绝 loopback(127.x / ::1)和 link-local(169.254.x,含云元数据 169.254.169.254 / fe80:);
|
||||||
|
* 注意:RFC1918 私网段(10/172.16/192.168)允许通过,兼容企业内网 MinIO/OSS/文件服务等合法用途。
|
||||||
|
*
|
||||||
|
* @param fileUrl HTTP(S) URL
|
||||||
|
*/
|
||||||
|
public static void checkSsrfHttpUrl(String fileUrl) {
|
||||||
|
if (StringUtils.isBlank(fileUrl)) {
|
||||||
|
throw new JeecgBootException("非法URL:地址为空");
|
||||||
|
}
|
||||||
|
URI uri;
|
||||||
|
try {
|
||||||
|
uri = new URI(fileUrl);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new JeecgBootException("非法URL:格式错误");
|
||||||
|
}
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
if (scheme == null || !(scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https"))) {
|
||||||
|
throw new JeecgBootException("非法URL:仅允许 http / https 协议");
|
||||||
|
}
|
||||||
|
String host = uri.getHost();
|
||||||
|
if (StringUtils.isBlank(host)) {
|
||||||
|
throw new JeecgBootException("非法URL:主机名为空");
|
||||||
|
}
|
||||||
|
// 去掉 IPv6 的中括号
|
||||||
|
if (host.startsWith("[") && host.endsWith("]")) {
|
||||||
|
host = host.substring(1, host.length() - 1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (InetAddress addr : InetAddress.getAllByName(host)) {
|
||||||
|
if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()) {
|
||||||
|
throw new JeecgBootException("非法URL:禁止访问本机或链路本地地址 " + addr.getHostAddress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
throw new JeecgBootException("非法URL:主机名无法解析");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-end---author:zhangdaihao ---date:2026-04-15 for:【issues/9553】修复二次SSRF漏洞,对HTTP下载URL进行安全校验-----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量校验文件路径安全性(逗号分隔的多个文件路径)
|
* 批量校验文件路径安全性(逗号分隔的多个文件路径)
|
||||||
* @param files 逗号分隔的文件路径
|
* @param files 逗号分隔的文件路径
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.lang.reflect.Array;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -1046,7 +1047,7 @@ public class oConvertUtils {
|
|||||||
BigDecimal bigDecimal = new BigDecimal(uploadCount);
|
BigDecimal bigDecimal = new BigDecimal(uploadCount);
|
||||||
//换算成MB
|
//换算成MB
|
||||||
BigDecimal divide = bigDecimal.divide(new BigDecimal(1048576));
|
BigDecimal divide = bigDecimal.divide(new BigDecimal(1048576));
|
||||||
count = divide.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
count = divide.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
|
|||||||
@@ -249,10 +249,12 @@ public class OssBootUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件流
|
* 获取指定桶(私有桶)中的文件流
|
||||||
* @param objectName
|
* 通过OSS SDK直接读取文件内容,支持指定自定义桶名(如 "eoafile"),为空则使用默认桶
|
||||||
* @param bucket
|
*
|
||||||
* @return
|
* @param objectName 文件对象路径(如 "eoafile/2026/04/test.pdf",会自动替换前缀)
|
||||||
|
* @param bucket 自定义桶名称,为空则使用默认桶
|
||||||
|
* @return 文件输入流,失败返回null
|
||||||
*/
|
*/
|
||||||
public static InputStream getOssFile(String objectName,String bucket){
|
public static InputStream getOssFile(String objectName,String bucket){
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
@@ -282,11 +284,13 @@ public class OssBootUtil {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文件外链
|
* 获取私有桶文件的预签名访问URL(带过期时间)
|
||||||
* @param bucketName
|
* 通过OSS预签名机制生成临时访问链接,无需公开桶即可让外部下载/预览文件
|
||||||
* @param objectName
|
*
|
||||||
* @param expires
|
* @param bucketName 桶名称(如 "eoafile")
|
||||||
* @return
|
* @param objectName 文件对象路径(会自动替换前缀)
|
||||||
|
* @param expires 链接过期时间点(Date类型,如1天后过期)
|
||||||
|
* @return 预签名URL字符串,文件不存在或失败返回null
|
||||||
*/
|
*/
|
||||||
public static String getObjectUrl(String bucketName, String objectName, Date expires) {
|
public static String getObjectUrl(String bucketName, String objectName, Date expires) {
|
||||||
initOss(endPoint, accessKeyId, accessKeySecret);
|
initOss(endPoint, accessKeyId, accessKeySecret);
|
||||||
@@ -322,10 +326,12 @@ public class OssBootUtil {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件到oss
|
* 通过输入流上传文件到阿里云OSS默认桶
|
||||||
* @param stream
|
* 上传后设置桶为公开读权限,返回文件完整访问URL
|
||||||
* @param relativePath
|
*
|
||||||
* @return
|
* @param stream 文件输入流
|
||||||
|
* @param relativePath 文件在桶中的相对路径(如 "upload/2026/04/test.pdf")
|
||||||
|
* @return 文件完整访问URL(优先使用staticDomain,否则拼接 bucketName.endPoint)
|
||||||
*/
|
*/
|
||||||
public static String upload(InputStream stream, String relativePath) {
|
public static String upload(InputStream stream, String relativePath) {
|
||||||
String filePath = null;
|
String filePath = null;
|
||||||
|
|||||||
@@ -4,43 +4,132 @@ import org.jeecg.common.exception.JeecgBootException;
|
|||||||
import org.jeecg.common.util.oConvertUtils;
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jdbc连接校验
|
* JDBC连接安全校验工具类
|
||||||
|
*
|
||||||
|
* 修复说明:
|
||||||
|
* 原实现仅检查 URL 中 '?' 之后的参数,且黑名单仅包含 5 个 PostgreSQL 参数。
|
||||||
|
* 存在以下安全隐患:
|
||||||
|
* 1. MySQL 危险参数 (allowLoadLocalInfile, autoDeserialize 等) 未覆盖
|
||||||
|
* 2. H2 使用 ';' 分隔参数,完全绕过 '?' 检查
|
||||||
|
* 3. MySQL multi-host 语法 '(host,param=val)' 和 address-block 语法不使用 '?'
|
||||||
|
*
|
||||||
|
* 修复方案: 对 URL 全文做 toLowerCase() + contains() 匹配,
|
||||||
|
* 覆盖所有参数分隔符格式 (?, ;, (), address=),并扩展黑名单覆盖全部主流驱动。
|
||||||
|
*
|
||||||
* @Author taoYan
|
* @Author taoYan
|
||||||
* @Date 2022/8/10 18:15
|
* @Date 2022/8/10 18:15
|
||||||
**/
|
*/
|
||||||
public class JdbcSecurityUtil {
|
public class JdbcSecurityUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接驱动漏洞 最新版本修复后,可删除相应的key
|
* 全驱动危险参数黑名单 (全小写,用于 contains 匹配)
|
||||||
* postgre:authenticationPluginClassName, sslhostnameverifier, socketFactory, sslfactory, sslpasswordcallback
|
*
|
||||||
* https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-v7wg-cpwc-24m4
|
* 使用 URL 全文 contains 匹配策略,覆盖所有参数分隔符:
|
||||||
*
|
* - 标准格式: ?key=value&key=value
|
||||||
|
* - H2 格式: ;KEY=value
|
||||||
|
* - MySQL multi-host: (host,key=value)
|
||||||
|
* - MySQL address-block: address=(key=value)
|
||||||
*/
|
*/
|
||||||
public static final String[] notAllowedProps = new String[]{"authenticationPluginClassName", "sslhostnameverifier", "socketFactory", "sslfactory", "sslpasswordcallback"};
|
private static final String[] UNSAFE_PARAMS = {
|
||||||
|
// === MySQL / MariaDB ===
|
||||||
|
// 文件读取相关
|
||||||
|
"allowloadlocalinfile", // LOAD DATA LOCAL INFILE
|
||||||
|
"allowurlinlocalinfile", // 通过 URL 读取远程文件
|
||||||
|
"allowloadlocalinfileinpath", // 指定路径文件读取
|
||||||
|
// 反序列化相关
|
||||||
|
"autodeserialize", // 启用反序列化
|
||||||
|
"queryinterceptors", // 查询拦截器 (反序列化触发点)
|
||||||
|
"statementinterceptors", // 语句拦截器 (反序列化触发点)
|
||||||
|
"detectcustomcollations", // 自定义排序规则检测 (反序列化触发点)
|
||||||
|
// 配合攻击
|
||||||
|
"maxallowedpacket", // 突破数据包大小限制
|
||||||
|
|
||||||
|
// === PostgreSQL ===
|
||||||
|
// https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-v7wg-cpwc-24m4
|
||||||
|
"socketfactory", // 任意类实例化 RCE
|
||||||
|
"socketfactoryarg", // socketFactory 构造参数
|
||||||
|
"sslfactory", // SSL 工厂类加载
|
||||||
|
"sslhostnameverifier", // SSL 主机名验证器类加载
|
||||||
|
"sslpasswordcallback", // SSL 密码回调类加载
|
||||||
|
"authenticationpluginclassname", // 认证插件类加载
|
||||||
|
"jaasapplicationname", // JAAS 认证攻击
|
||||||
|
|
||||||
|
// === H2 ===
|
||||||
|
"init=", // 连接初始化 SQL (带 '=' 防止匹配到正常单词 'init')
|
||||||
|
"runscript", // 远程/本地 SQL 脚本加载
|
||||||
|
"trace_level_system_out", // 系统信息泄露
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验sql是否有特定的key
|
* 允许的 JDBC 驱动类名白名单
|
||||||
* @param jdbcUrl
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public static void validate(String jdbcUrl){
|
private static final String[] ALLOWED_DRIVERS = {
|
||||||
if(oConvertUtils.isEmpty(jdbcUrl)){
|
// MySQL 数据库
|
||||||
|
"com.mysql.jdbc.Driver",
|
||||||
|
// MySQL5.7+ 数据库
|
||||||
|
"com.mysql.cj.jdbc.Driver",
|
||||||
|
// Oracle
|
||||||
|
"oracle.jdbc.OracleDriver",
|
||||||
|
"oracle.jdbc.driver.OracleDriver",
|
||||||
|
// SQLServer 数据库
|
||||||
|
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
|
||||||
|
// marialDB 数据库
|
||||||
|
"org.mariadb.jdbc.Driver",
|
||||||
|
// postgresql 数据库
|
||||||
|
"org.postgresql.Driver",
|
||||||
|
// 达梦 数据库
|
||||||
|
"dm.jdbc.driver.DmDriver",
|
||||||
|
// 人大金仓 数据库
|
||||||
|
"com.kingbase8.Driver",
|
||||||
|
// 神通 数据库
|
||||||
|
"com.oscar.Driver",
|
||||||
|
// SQLite 数据库
|
||||||
|
"org.sqlite.JDBC",
|
||||||
|
// DB2 数据库
|
||||||
|
"com.ibm.db2.jcc.DB2Driver",
|
||||||
|
// Hsqldb 数据库
|
||||||
|
"org.hsqldb.jdbc.JDBCDriver",
|
||||||
|
// Derby 数据库
|
||||||
|
"org.apache.derby.jdbc.ClientDriver",
|
||||||
|
// H2 数据库
|
||||||
|
"org.h2.Driver",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验 JDBC URL 是否包含危险参数
|
||||||
|
*
|
||||||
|
* @param jdbcUrl JDBC 连接地址
|
||||||
|
* @throws JeecgBootException 包含危险参数时抛出
|
||||||
|
*/
|
||||||
|
public static void validate(String jdbcUrl) {
|
||||||
|
if (oConvertUtils.isEmpty(jdbcUrl)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String urlConcatChar = "?";
|
|
||||||
if(jdbcUrl.indexOf(urlConcatChar)<0){
|
String lowerUrl = jdbcUrl.toLowerCase();
|
||||||
return;
|
|
||||||
}
|
for (String unsafeParam : UNSAFE_PARAMS) {
|
||||||
String argString = jdbcUrl.substring(jdbcUrl.indexOf(urlConcatChar)+1);
|
if (lowerUrl.contains(unsafeParam)) {
|
||||||
String[] keyAndValues = argString.split("&");
|
throw new JeecgBootException("连接地址有安全风险,包含不安全参数【" + unsafeParam + "】");
|
||||||
for(String temp: keyAndValues){
|
|
||||||
String key = temp.split("=")[0];
|
|
||||||
for(String prop: notAllowedProps){
|
|
||||||
if(prop.equalsIgnoreCase(key)){
|
|
||||||
throw new JeecgBootException("连接地址有安全风险,【"+key+"】");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* 校验驱动类名是否在白名单中
|
||||||
|
*
|
||||||
|
* @param driverClassName JDBC 驱动类名
|
||||||
|
* @throws JeecgBootException 驱动不在白名单时抛出
|
||||||
|
*/
|
||||||
|
public static void validateDriver(String driverClassName) {
|
||||||
|
if (oConvertUtils.isEmpty(driverClassName)) {
|
||||||
|
throw new JeecgBootException("数据库驱动类名不能为空");
|
||||||
|
}
|
||||||
|
for (String allowed : ALLOWED_DRIVERS) {
|
||||||
|
if (allowed.equals(driverClassName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new JeecgBootException("不支持的数据库驱动【" + driverClassName + "】,如需支持请联系管理员");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package org.jeecg.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.jeecg.ai.factory.AiModelFactory;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component("jeecgAiChatConfig")
|
||||||
|
@ConfigurationProperties(prefix = "jeecg.ai-chat")
|
||||||
|
public class AiChatConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* skills配置文件路径
|
||||||
|
*/
|
||||||
|
private String skillsDir;
|
||||||
|
/**
|
||||||
|
* shell命令行配置文件路径
|
||||||
|
*/
|
||||||
|
private String skillsShellDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI绘图(文生图)
|
||||||
|
*/
|
||||||
|
private ModelConfig aiModelDraw = new ModelConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI图生(图绘画)
|
||||||
|
*/
|
||||||
|
private ModelConfig aiModelPicDraw = new ModelConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI语音
|
||||||
|
*/
|
||||||
|
private VoiceModelConfig aiModelVoice = new VoiceModelConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI视频
|
||||||
|
*/
|
||||||
|
private VideoModelConfig aiModelVideo = new VideoModelConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI默认向量模型
|
||||||
|
*/
|
||||||
|
private ModelConfig aiModelEmbed = new ModelConfig();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ModelConfig {
|
||||||
|
/**
|
||||||
|
* 使用的模型
|
||||||
|
*/
|
||||||
|
private String model;
|
||||||
|
/**
|
||||||
|
* api秘钥
|
||||||
|
*/
|
||||||
|
private String apiKey;
|
||||||
|
/**
|
||||||
|
* api域名
|
||||||
|
*/
|
||||||
|
private String apiHost;
|
||||||
|
/**
|
||||||
|
* 超时时间
|
||||||
|
*/
|
||||||
|
private int timeout = 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 供应商
|
||||||
|
*/
|
||||||
|
private String provider = AiModelFactory.AIMODEL_TYPE_QWEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class VideoModelConfig extends ModelConfig {
|
||||||
|
/**
|
||||||
|
* ffmpeg 可执行文件路径,为空时自动查找
|
||||||
|
*/
|
||||||
|
private String ffmpegPath;
|
||||||
|
/**
|
||||||
|
* edge-tts 可执行文件路径,为空时自动查找
|
||||||
|
*/
|
||||||
|
private String edgeTtsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class VoiceModelConfig extends ModelConfig {
|
||||||
|
/**
|
||||||
|
* 默认声色
|
||||||
|
*/
|
||||||
|
private String voice = "alloy";
|
||||||
|
/**
|
||||||
|
* 默认倍速,范围0.25~4.0
|
||||||
|
*/
|
||||||
|
private double speed = 1.0;
|
||||||
|
/**
|
||||||
|
* 默认音量增益(dB)
|
||||||
|
*/
|
||||||
|
private double volume = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,4 +21,29 @@ public class AiRagConfigBean {
|
|||||||
* stdio mpc命令行功能开启,sql:AI流程SQL节点开启
|
* stdio mpc命令行功能开启,sql:AI流程SQL节点开启
|
||||||
*/
|
*/
|
||||||
private String allowSensitiveNodes = "";
|
private String allowSensitiveNodes = "";
|
||||||
|
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-15 for:Brave Search配置迁移到AiRagConfigBean,去掉enabled字段,apiKey为空即不启用-----------
|
||||||
|
/**
|
||||||
|
* Brave Search 联网检索配置
|
||||||
|
*/
|
||||||
|
private BraveSearchConfig braveSearch = new BraveSearchConfig();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class BraveSearchConfig {
|
||||||
|
/** Brave Search API Key;为空时联网检索不生效 */
|
||||||
|
private String apiKey;
|
||||||
|
/** API 端点,默认官方地址 */
|
||||||
|
private String endpoint = "https://api.search.brave.com/res/v1/web/search";
|
||||||
|
/** 默认返回结果条数,最大 20 */
|
||||||
|
private Integer count = 10;
|
||||||
|
/** 请求超时秒数 */
|
||||||
|
private Integer timeout = 15;
|
||||||
|
/**
|
||||||
|
* 搜索结果缓存时长(分钟)。
|
||||||
|
* 大于 0 时开启缓存,相同参数的查询直接返回缓存结果,不重复调用 API。
|
||||||
|
* 设为 0 或不配置则关闭缓存。
|
||||||
|
*/
|
||||||
|
private Integer cacheExpireMinutes = 60;
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-15 for:Brave Search配置迁移到AiRagConfigBean,去掉enabled字段,apiKey为空即不启用-----------
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ public class ShiroConfig {
|
|||||||
filterChainDefinitionMap.put("/xslmes/mesXslWarehouse/anon/**", "anon");
|
filterChainDefinitionMap.put("/xslmes/mesXslWarehouse/anon/**", "anon");
|
||||||
// MES库区管理免密接口(供桌面端调用)
|
// MES库区管理免密接口(供桌面端调用)
|
||||||
filterChainDefinitionMap.put("/xslmes/mesXslWarehouseArea/anon/**", "anon");
|
filterChainDefinitionMap.put("/xslmes/mesXslWarehouseArea/anon/**", "anon");
|
||||||
|
// MES密炼物料皮重策略免密接口(供桌面端调用)
|
||||||
|
filterChainDefinitionMap.put("/xslmes/mesXslMixerMaterialTareStrategy/anon/**", "anon");
|
||||||
|
// MES单位只读免密接口(供桌面端单位下拉调用)
|
||||||
|
filterChainDefinitionMap.put("/xslmes/mesXslUnit/anon/**", "anon");
|
||||||
// MES密炼物料管理免密接口(供桌面端调用)
|
// MES密炼物料管理免密接口(供桌面端调用)
|
||||||
filterChainDefinitionMap.put("/mes/material/mixerMaterial/anon/**", "anon");
|
filterChainDefinitionMap.put("/mes/material/mixerMaterial/anon/**", "anon");
|
||||||
// 打印模板免密接口(供桌面端调用)
|
// 打印模板免密接口(供桌面端调用)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.jeecgframework.boot3</groupId>
|
<groupId>org.jeecgframework.boot3</groupId>
|
||||||
<artifactId>jeecg-boot-module</artifactId>
|
<artifactId>jeecg-boot-module</artifactId>
|
||||||
<version>3.9.1</version>
|
<version>3.9.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>jeecg-boot-module-airag</artifactId>
|
<artifactId>jeecg-boot-module-airag</artifactId>
|
||||||
@@ -41,14 +41,14 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-bom</artifactId>
|
<artifactId>langchain4j-bom</artifactId>
|
||||||
<version>1.9.1</version>
|
<version>1.12.2</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-community-bom</artifactId>
|
<artifactId>langchain4j-community-bom</artifactId>
|
||||||
<version>1.9.1-beta17</version>
|
<version>1.12.1-beta21</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jeecgframework.boot3</groupId>
|
<groupId>org.jeecgframework.boot3</groupId>
|
||||||
<artifactId>jeecg-aiflow</artifactId>
|
<artifactId>jeecg-aiflow</artifactId>
|
||||||
<version>3.9.1-beta1</version>
|
<version>3.9.2-beta</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>commons-io</groupId>
|
<groupId>commons-io</groupId>
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<groupId>com.yomahub</groupId>
|
<groupId>com.yomahub</groupId>
|
||||||
<artifactId>liteflow-script-graaljs</artifactId>
|
<artifactId>liteflow-script-graaljs</artifactId>
|
||||||
<version>${liteflow.version}</version>
|
<version>${liteflow.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.yomahub</groupId>
|
<groupId>com.yomahub</groupId>
|
||||||
@@ -160,6 +160,10 @@
|
|||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-ollama</artifactId>
|
<artifactId>langchain4j-ollama</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-google-ai-gemini</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.langchain4j</groupId>
|
<groupId>dev.langchain4j</groupId>
|
||||||
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
<artifactId>langchain4j-community-zhipu-ai</artifactId>
|
||||||
@@ -233,12 +237,29 @@
|
|||||||
<artifactId>tika-parser-text-module</artifactId>
|
<artifactId>tika-parser-text-module</artifactId>
|
||||||
<version>${apache-tika.version}</version>
|
<version>${apache-tika.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!--skills-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-skills</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--命令模式-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.langchain4j</groupId>
|
||||||
|
<artifactId>langchain4j-experimental-skills-shell</artifactId>
|
||||||
|
<version>1.12.2-beta22</version>
|
||||||
|
</dependency>
|
||||||
<!-- word模版引擎 -->
|
<!-- word模版引擎 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.deepoove</groupId>
|
<groupId>com.deepoove</groupId>
|
||||||
<artifactId>poi-tl</artifactId>
|
<artifactId>poi-tl</artifactId>
|
||||||
<version>1.12.2</version>
|
<version>1.12.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- jsoup HTML parser library @ https://jsoup.org/ -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jsoup</groupId>
|
||||||
|
<artifactId>jsoup</artifactId>
|
||||||
|
<version>1.22.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package org.jeecg.modules.airag.api;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jeecg.common.airag.api.IAiragBaseApi;
|
||||||
|
import org.jeecg.common.api.vo.Result;
|
||||||
|
import org.jeecg.common.exception.JeecgBootBizTipException;
|
||||||
|
import org.jeecg.common.util.AssertUtils;
|
||||||
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
|
import org.jeecg.modules.airag.app.entity.AiragApp;
|
||||||
|
import org.jeecg.modules.airag.app.service.IAiragAppService;
|
||||||
|
import org.jeecg.modules.airag.app.service.IAiragVariableService;
|
||||||
|
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
||||||
|
import org.jeecg.modules.airag.llm.entity.AiragKnowledgeDoc;
|
||||||
|
import org.jeecg.modules.airag.llm.service.IAiragKnowledgeDocService;
|
||||||
|
import org.jeecg.modules.airag.prompts.entity.AiragPrompts;
|
||||||
|
import org.jeecg.modules.airag.prompts.service.IAiragPromptsService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* airag baseAPI 实现类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Primary
|
||||||
|
@Service("airagBaseApiImpl")
|
||||||
|
public class AiragBaseApiImpl implements IAiragBaseApi {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiragKnowledgeDocService airagKnowledgeDocService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String knowledgeWriteTextDocument(String knowledgeId, String title, String content, String segmentConfig) {
|
||||||
|
AssertUtils.assertNotEmpty("知识库ID不能为空", knowledgeId);
|
||||||
|
AssertUtils.assertNotEmpty("写入内容不能为空", content);
|
||||||
|
AiragKnowledgeDoc knowledgeDoc = new AiragKnowledgeDoc();
|
||||||
|
knowledgeDoc.setKnowledgeId(knowledgeId);
|
||||||
|
knowledgeDoc.setTitle(title);
|
||||||
|
knowledgeDoc.setType(LLMConsts.KNOWLEDGE_DOC_TYPE_TEXT);
|
||||||
|
knowledgeDoc.setContent(content);
|
||||||
|
// 将分段策略配置写入文档的metadata中,EmbeddingHandler会从中读取分段配置
|
||||||
|
if (oConvertUtils.isNotEmpty(segmentConfig)) {
|
||||||
|
knowledgeDoc.setMetadata(segmentConfig);
|
||||||
|
}
|
||||||
|
Result<?> result = airagKnowledgeDocService.editDocument(knowledgeDoc);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
throw new JeecgBootBizTipException(result.getMessage());
|
||||||
|
}
|
||||||
|
if (knowledgeDoc.getId() == null) {
|
||||||
|
throw new JeecgBootBizTipException("知识库文档ID为空");
|
||||||
|
}
|
||||||
|
log.info("[AI-KNOWLEDGE] 文档写入完成,知识库:{}, 文档ID:{}", knowledgeId, knowledgeDoc.getId());
|
||||||
|
return knowledgeDoc.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiragAppService airagAppService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiragVariableService airagVariableService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAiragPromptsService airagPromptsService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChatVariable(String appId, String username, String name) {
|
||||||
|
return airagVariableService.getVariable(username, appId, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChatVariable(String appId, String username, String name, String value) {
|
||||||
|
AssertUtils.assertNotEmpty("应用ID不能为空", appId);
|
||||||
|
AssertUtils.assertNotEmpty("用户名不能为空", username);
|
||||||
|
AssertUtils.assertNotEmpty("变量名不能为空", name);
|
||||||
|
airagVariableService.updateVariable(username, appId, name, value != null ? value : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMemoryIdByAppId(String appId) {
|
||||||
|
if (oConvertUtils.isEmpty(appId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<AiragApp> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(AiragApp::getId, appId)
|
||||||
|
.eq(AiragApp::getIzOpenMemory, 1)
|
||||||
|
.isNotNull(AiragApp::getMemoryId)
|
||||||
|
.ne(AiragApp::getMemoryId, "")
|
||||||
|
.select(AiragApp::getMemoryId);
|
||||||
|
AiragApp app = airagAppService.getOne(queryWrapper);
|
||||||
|
return app != null ? app.getMemoryId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPromptContent(String promptId) {
|
||||||
|
if (oConvertUtils.isEmpty(promptId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AiragPrompts prompt = airagPromptsService.getById(promptId);
|
||||||
|
if (prompt == null) {
|
||||||
|
log.warn("[AiragBaseApi]提示词不存在,promptId={}", promptId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return prompt.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -64,4 +64,27 @@ public class AiAppConsts {
|
|||||||
* AI写作redis请求前缀
|
* AI写作redis请求前缀
|
||||||
*/
|
*/
|
||||||
public static final String ARTICLE_WRITER_KEY = "airag:chat:article:write:{}";
|
public static final String ARTICLE_WRITER_KEY = "airag:chat:article:write:{}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ai绘画类型: 绘图
|
||||||
|
*/
|
||||||
|
public static final String AI_DRAW_TYPE_DRAW = "draw";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ai绘画类型: 换脸
|
||||||
|
*/
|
||||||
|
public static final String AI_DRAW_TYPE_FACE = "face";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ai绘画类型: 混图
|
||||||
|
*/
|
||||||
|
public static final String AI_DRAW_TYPE_MIX = "mix";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ai绘画 会话redis请求前缀
|
||||||
|
*/
|
||||||
|
public static final String POSTER_TASK_PREFIX = "airag:poster:task:";
|
||||||
|
/** 任务结果在 Redis 中保留 1 小时 */
|
||||||
|
public static final long POSTER_TASK_TTL = 3600L;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,4 +173,9 @@ public class Prompts {
|
|||||||
*/
|
*/
|
||||||
public static final String AI_TOUCHE_PROMPT = "请针对如下内容:[{}] 进行润色。 回复格式:{},语气:{},语言:{},长度:{}。";
|
public static final String AI_TOUCHE_PROMPT = "请针对如下内容:[{}] 进行润色。 回复格式:{},语气:{},语言:{},长度:{}。";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ai绘画提示词
|
||||||
|
*/
|
||||||
|
public static final String AI_DRAW_PROMPT = "风格:{},视角:{},人物镜头:{},灯光:{},图片尺寸:{};";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.jeecg.common.system.vo.DictModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: AI应用
|
* @Description: AI应用
|
||||||
@@ -62,6 +64,25 @@ public class AiragAppController extends JeecgController<AiragApp, IAiragAppServi
|
|||||||
return Result.OK(pageList);
|
return Result.OK(pageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典列表查询(不分页,按创建时间倒序)
|
||||||
|
*
|
||||||
|
* @param airagApp 支持通过实体字段动态过滤,如 type 等
|
||||||
|
* @param req HTTP请求
|
||||||
|
* @return 应用字典列表
|
||||||
|
*/
|
||||||
|
@GetMapping(value = "/listDict")
|
||||||
|
public Result<List<DictModel>> listDict(AiragApp airagApp, HttpServletRequest req) {
|
||||||
|
QueryWrapper<AiragApp> queryWrapper = QueryGenerator.initQueryWrapper(airagApp, req.getParameterMap());
|
||||||
|
queryWrapper.select("id", "name");
|
||||||
|
queryWrapper.orderByDesc("create_time");
|
||||||
|
List<AiragApp> list = airagAppService.list(queryWrapper);
|
||||||
|
List<DictModel> dictList = list.stream()
|
||||||
|
.map(app -> new DictModel(app.getId(), app.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Result.OK(dictList);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增或编辑
|
* 新增或编辑
|
||||||
*
|
*
|
||||||
@@ -70,10 +91,24 @@ public class AiragAppController extends JeecgController<AiragApp, IAiragAppServi
|
|||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
|
||||||
@RequiresPermissions("airag:app:edit")
|
@RequiresPermissions("airag:app:edit")
|
||||||
public Result<String> edit(@RequestBody AiragApp airagApp) {
|
public Result<String> edit(@RequestBody AiragApp airagApp, HttpServletRequest request) {
|
||||||
AssertUtils.assertNotEmpty("参数异常", airagApp);
|
AssertUtils.assertNotEmpty("参数异常", airagApp);
|
||||||
AssertUtils.assertNotEmpty("请输入应用名称", airagApp.getName());
|
AssertUtils.assertNotEmpty("请输入应用名称", airagApp.getName());
|
||||||
AssertUtils.assertNotEmpty("请选择应用类型", airagApp.getType());
|
AssertUtils.assertNotEmpty("请选择应用类型", airagApp.getType());
|
||||||
|
//update-begin---author:zhangdaihao ---date:20260415 for:[issues/9462]AI应用edit接口跨租户数据写入漏洞------------
|
||||||
|
//SaaS多租户隔离:禁止跨租户写入,防止通过请求体伪造tenantId污染其他租户数据
|
||||||
|
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
|
||||||
|
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
|
||||||
|
if (airagApp.getId() != null && !airagApp.getId().isEmpty()) {
|
||||||
|
AiragApp dbApp = airagAppService.getById(airagApp.getId());
|
||||||
|
if (dbApp == null || !dbApp.getTenantId().equals(currentTenantId)) {
|
||||||
|
return Result.error("保存AI应用失败,不能修改其他租户的AI应用!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//强制使用当前登录租户,忽略客户端传入值
|
||||||
|
airagApp.setTenantId(currentTenantId);
|
||||||
|
}
|
||||||
|
//update-end---author:zhangdaihao ---date:20260415 for:[issues/9462]AI应用edit接口跨租户数据写入漏洞------------
|
||||||
airagApp.setStatus(AiAppConsts.STATUS_ENABLE);
|
airagApp.setStatus(AiAppConsts.STATUS_ENABLE);
|
||||||
airagAppService.saveOrUpdate(airagApp);
|
airagAppService.saveOrUpdate(airagApp);
|
||||||
return Result.OK("保存完成!", airagApp.getId());
|
return Result.OK("保存完成!", airagApp.getId());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.jeecg.common.constant.CommonConstant;
|
|||||||
import org.jeecg.common.util.CommonUtils;
|
import org.jeecg.common.util.CommonUtils;
|
||||||
import org.jeecg.config.shiro.IgnoreAuth;
|
import org.jeecg.config.shiro.IgnoreAuth;
|
||||||
import org.jeecg.modules.airag.app.service.IAiragChatService;
|
import org.jeecg.modules.airag.app.service.IAiragChatService;
|
||||||
|
import org.jeecg.modules.airag.app.vo.AiDrawGenerateVo;
|
||||||
import org.jeecg.modules.airag.app.vo.AiWriteGenerateVo;
|
import org.jeecg.modules.airag.app.vo.AiWriteGenerateVo;
|
||||||
import org.jeecg.modules.airag.app.vo.ChatConversation;
|
import org.jeecg.modules.airag.app.vo.ChatConversation;
|
||||||
import org.jeecg.modules.airag.app.vo.ChatSendParams;
|
import org.jeecg.modules.airag.app.vo.ChatSendParams;
|
||||||
@@ -266,11 +267,31 @@ public class AiragChatController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@PostMapping("/genAiPoster")
|
@PostMapping("/genAiPoster")
|
||||||
public Result<String> genAiPoster(@RequestBody ChatSendParams chatSendParams){
|
public Result<String> genAiPoster(@RequestBody AiDrawGenerateVo aiDrawGenerateVo){
|
||||||
String imageUrl = chatService.genAiPoster(chatSendParams);
|
String imageUrl = chatService.genAiPoster(aiDrawGenerateVo);
|
||||||
return Result.OK(imageUrl);
|
return Result.OK(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
/**
|
||||||
|
* 异步提交AI海报生成任务,立即返回taskId
|
||||||
|
*/
|
||||||
|
@PostMapping("/genAiPosterAsync")
|
||||||
|
public Result<String> genAiPosterAsync(@RequestBody AiDrawGenerateVo aiDrawGenerateVo) {
|
||||||
|
String taskId = chatService.genAiPosterAsync(aiDrawGenerateVo);
|
||||||
|
return Result.OK(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询AI海报异步任务结果
|
||||||
|
* status: pending / success / failed
|
||||||
|
*/
|
||||||
|
@GetMapping("/getAiPosterResult/{taskId}")
|
||||||
|
public Result<?> getAiPosterResult(@PathVariable String taskId) {
|
||||||
|
return chatService.getAiPosterResult(taskId);
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成ai写作
|
* 生成ai写作
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.jeecg.modules.airag.app.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 图像编辑枚举
|
||||||
|
*
|
||||||
|
* @author: wangshuai
|
||||||
|
* @date: 2026/2/28 16:52
|
||||||
|
*/
|
||||||
|
public enum ImageEditEnum {
|
||||||
|
WANX2_1_IMAGEEDIT("wanx2.1-imageedit"),
|
||||||
|
WAN2_5_I2I_PREVIEW("wan2.5-i2i-preview");
|
||||||
|
|
||||||
|
private final String modelName;
|
||||||
|
|
||||||
|
ImageEditEnum(String modelName) {
|
||||||
|
this.modelName = modelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelName() {
|
||||||
|
return modelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查模型名称是否是图像编辑模型
|
||||||
|
* @param modelName 模型名称
|
||||||
|
* @return 是否是图像编辑模型
|
||||||
|
*/
|
||||||
|
public static boolean isImageEditModel(String modelName) {
|
||||||
|
for (ImageEditEnum model : values()) {
|
||||||
|
if (model.getModelName().equals(modelName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.jeecg.modules.airag.app.enums;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: 图片大小比例枚举
|
||||||
|
*
|
||||||
|
* @author: wangshuai
|
||||||
|
* @date: 2026/2/4 19:55
|
||||||
|
*/
|
||||||
|
public enum ImageSizeEnum {
|
||||||
|
SIZE_1024_1024("1024*1024", "1:1"),
|
||||||
|
SIZE_1280_720("1280*720", "16:9"),
|
||||||
|
SIZE_720_1280("720*1280", "9:16"),
|
||||||
|
SIZE_1024_768("1024*768", "4:3"),
|
||||||
|
SIZE_768_1024("768*1024", "3:4");
|
||||||
|
|
||||||
|
ImageSizeEnum(String size, String ratio) {
|
||||||
|
this.size = size;
|
||||||
|
this.ratio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大小
|
||||||
|
*/
|
||||||
|
private String size;
|
||||||
|
/**
|
||||||
|
* 比例
|
||||||
|
*/
|
||||||
|
private String ratio;
|
||||||
|
|
||||||
|
public String getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRatio() {
|
||||||
|
return ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据size获取ratio
|
||||||
|
*
|
||||||
|
* @param size
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getRatioBySize(String size) {
|
||||||
|
if (StringUtils.isBlank(size)) {
|
||||||
|
return "1:1";
|
||||||
|
}
|
||||||
|
for (ImageSizeEnum e : ImageSizeEnum.values()) {
|
||||||
|
if (e.size.equals(size)) {
|
||||||
|
return e.ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "1:1";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package org.jeecg.modules.airag.app.service;
|
package org.jeecg.modules.airag.app.service;
|
||||||
|
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.modules.airag.app.vo.AiWriteGenerateVo;
|
import org.jeecg.modules.airag.app.vo.*;
|
||||||
import org.jeecg.modules.airag.app.vo.AppDebugParams;
|
|
||||||
import org.jeecg.modules.airag.app.vo.ChatConversation;
|
|
||||||
import org.jeecg.modules.airag.app.vo.ChatSendParams;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,10 +123,26 @@ public interface IAiragChatService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成海报图片
|
* 生成海报图片
|
||||||
* @param chatSendParams
|
* @param aiDrawGenerateVo
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String genAiPoster(ChatSendParams chatSendParams);
|
String genAiPoster(AiDrawGenerateVo aiDrawGenerateVo);
|
||||||
|
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
/**
|
||||||
|
* 异步生成海报图片,立即返回taskId
|
||||||
|
* @param aiDrawGenerateVo
|
||||||
|
* @return taskId
|
||||||
|
*/
|
||||||
|
String genAiPosterAsync(AiDrawGenerateVo aiDrawGenerateVo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询异步海报任务结果
|
||||||
|
* @param taskId
|
||||||
|
* @return Result,data为图片URL(成功)或status=pending/failed
|
||||||
|
*/
|
||||||
|
Result<?> getAiPosterResult(String taskId);
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成ai创作
|
* 生成ai创作
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ public interface IAiragVariableService {
|
|||||||
*/
|
*/
|
||||||
void initVariable(String userId, String appId, String name, String defaultValue);
|
void initVariable(String userId, String appId, String name, String defaultValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取变量值
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param appId 应用ID
|
||||||
|
* @param name 变量名
|
||||||
|
* @return 变量值,不存在返回null
|
||||||
|
*/
|
||||||
|
String getVariable(String username, String appId, String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加变量更新工具
|
* 添加变量更新工具
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.jeecg.modules.airag.app.service.impl;
|
package org.jeecg.modules.airag.app.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import dev.langchain4j.agent.tool.ToolExecutionRequest;
|
import dev.langchain4j.agent.tool.ToolExecutionRequest;
|
||||||
@@ -19,21 +20,23 @@ import org.jeecg.common.api.vo.Result;
|
|||||||
import org.jeecg.common.constant.SymbolConstant;
|
import org.jeecg.common.constant.SymbolConstant;
|
||||||
import org.jeecg.common.exception.JeecgBootBizTipException;
|
import org.jeecg.common.exception.JeecgBootBizTipException;
|
||||||
import org.jeecg.common.exception.JeecgBootException;
|
import org.jeecg.common.exception.JeecgBootException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||||
import org.jeecg.common.system.util.JwtUtil;
|
import org.jeecg.common.system.util.JwtUtil;
|
||||||
import org.jeecg.common.util.*;
|
import org.jeecg.common.util.*;
|
||||||
|
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||||
|
import org.jeecg.config.AiChatConfig;
|
||||||
|
import org.jeecg.config.AiRagConfigBean;
|
||||||
import org.jeecg.config.JeecgBaseConfig;
|
import org.jeecg.config.JeecgBaseConfig;
|
||||||
import org.jeecg.config.vo.Path;
|
import org.jeecg.config.vo.Path;
|
||||||
import org.jeecg.modules.airag.app.consts.AiAppConsts;
|
import org.jeecg.modules.airag.app.consts.AiAppConsts;
|
||||||
import org.jeecg.modules.airag.app.consts.Prompts;
|
import org.jeecg.modules.airag.app.consts.Prompts;
|
||||||
import org.jeecg.modules.airag.app.entity.AiragApp;
|
import org.jeecg.modules.airag.app.entity.AiragApp;
|
||||||
|
import org.jeecg.modules.airag.app.enums.ImageSizeEnum;
|
||||||
import org.jeecg.modules.airag.app.mapper.AiragAppMapper;
|
import org.jeecg.modules.airag.app.mapper.AiragAppMapper;
|
||||||
import org.jeecg.modules.airag.app.service.IAiragChatService;
|
import org.jeecg.modules.airag.app.service.IAiragChatService;
|
||||||
import org.jeecg.modules.airag.app.service.IAiragVariableService;
|
import org.jeecg.modules.airag.app.service.IAiragVariableService;
|
||||||
import org.jeecg.modules.airag.app.vo.AiWriteGenerateVo;
|
import org.jeecg.modules.airag.app.vo.*;
|
||||||
import org.jeecg.modules.airag.app.vo.AppDebugParams;
|
|
||||||
import org.jeecg.modules.airag.app.vo.ChatConversation;
|
|
||||||
import org.jeecg.modules.airag.app.vo.ChatSendParams;
|
|
||||||
import org.jeecg.modules.airag.common.consts.AiragConsts;
|
import org.jeecg.modules.airag.common.consts.AiragConsts;
|
||||||
import org.jeecg.modules.airag.common.handler.AIChatParams;
|
import org.jeecg.modules.airag.common.handler.AIChatParams;
|
||||||
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
|
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
|
||||||
@@ -43,13 +46,17 @@ import org.jeecg.modules.airag.common.vo.MessageHistory;
|
|||||||
import org.jeecg.modules.airag.common.vo.event.EventData;
|
import org.jeecg.modules.airag.common.vo.event.EventData;
|
||||||
import org.jeecg.modules.airag.common.vo.event.EventFlowData;
|
import org.jeecg.modules.airag.common.vo.event.EventFlowData;
|
||||||
import org.jeecg.modules.airag.common.vo.event.EventMessageData;
|
import org.jeecg.modules.airag.common.vo.event.EventMessageData;
|
||||||
|
import org.jeecg.modules.airag.flow.context.JeecgFlowContext;
|
||||||
import org.jeecg.modules.airag.flow.consts.FlowConsts;
|
import org.jeecg.modules.airag.flow.consts.FlowConsts;
|
||||||
import org.jeecg.modules.airag.flow.entity.AiragFlow;
|
import org.jeecg.modules.airag.flow.entity.AiragFlow;
|
||||||
|
import org.jeecg.modules.airag.flow.helper.JeecgTagHelper;
|
||||||
import org.jeecg.modules.airag.flow.service.IAiragFlowService;
|
import org.jeecg.modules.airag.flow.service.IAiragFlowService;
|
||||||
import org.jeecg.modules.airag.flow.vo.api.FlowRunParams;
|
import org.jeecg.modules.airag.flow.vo.api.FlowRunParams;
|
||||||
|
import org.jeecg.modules.airag.flow.vo.tool.ToolExecutionVo;
|
||||||
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
||||||
import org.jeecg.modules.airag.llm.document.TikaDocumentParser;
|
import org.jeecg.modules.airag.llm.document.TikaDocumentParser;
|
||||||
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
||||||
|
import org.jeecg.modules.airag.flow.handler.BraveSearchToolBuilder;
|
||||||
import org.jeecg.modules.airag.llm.handler.AIChatHandler;
|
import org.jeecg.modules.airag.llm.handler.AIChatHandler;
|
||||||
import org.jeecg.modules.airag.llm.handler.JeecgToolsProvider;
|
import org.jeecg.modules.airag.llm.handler.JeecgToolsProvider;
|
||||||
import org.jeecg.modules.airag.llm.mapper.AiragModelMapper;
|
import org.jeecg.modules.airag.llm.mapper.AiragModelMapper;
|
||||||
@@ -60,7 +67,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.data.redis.core.BoundValueOperations;
|
import org.springframework.data.redis.core.BoundValueOperations;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -70,6 +77,7 @@ import java.io.InputStream;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
@@ -117,6 +125,12 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
JeecgBaseConfig jeecgBaseConfig;
|
JeecgBaseConfig jeecgBaseConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AiChatConfig aiChatConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AiRagConfigBean aiRagConfigBean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新接收消息
|
* 重新接收消息
|
||||||
@@ -193,6 +207,13 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
@Override
|
@Override
|
||||||
public Result<?> stop(String requestId) {
|
public Result<?> stop(String requestId) {
|
||||||
AssertUtils.assertNotEmpty("requestId不能为空", requestId);
|
AssertUtils.assertNotEmpty("requestId不能为空", requestId);
|
||||||
|
// 设置流程上下文的停止标志,通知正在执行的LLM节点停止输出
|
||||||
|
JeecgFlowContext flowContext = AiragLocalCache.get(AiragConsts.CACHE_TYPE_FLOW_CONTEXT, requestId);
|
||||||
|
if (flowContext != null) {
|
||||||
|
flowContext.setStopped(true);
|
||||||
|
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_FLOW_CONTEXT, requestId);
|
||||||
|
log.info("[AI-CHAT]已设置流程停止标志, requestId:{}", requestId);
|
||||||
|
}
|
||||||
// 从缓存中获取对应的SseEmitter
|
// 从缓存中获取对应的SseEmitter
|
||||||
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
|
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
|
||||||
if (emitter != null) {
|
if (emitter != null) {
|
||||||
@@ -307,26 +328,22 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
//update-begin---author:chenrui ---date:20251106 for:[issues/8545]新建AI应用的时候只能选择没有自定义参数的AI流程------------
|
//update-begin---author:chenrui ---date:20251106 for:[issues/8545]新建AI应用的时候只能选择没有自定义参数的AI流程------------
|
||||||
// 返回消息列表和会话设置信息
|
// 返回消息列表和会话设置信息
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
// 过滤掉工具调用相关的消息(前端不需要展示)
|
// 解析是否显示工具调用过程(默认为true)
|
||||||
|
boolean showToolProcess = true;
|
||||||
|
AiragApp chatApp = chatConversation.getApp();
|
||||||
|
if (chatApp != null && oConvertUtils.isNotEmpty(chatApp.getMetadata())) {
|
||||||
|
try {
|
||||||
|
JSONObject appMetadataJson = JSONObject.parseObject(chatApp.getMetadata());
|
||||||
|
if (appMetadataJson != null && "0".equals(appMetadataJson.getString("showToolProcess"))) {
|
||||||
|
showToolProcess = false;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 合并工具调用相关的消息
|
||||||
List<MessageHistory> messages = chatConversation.getMessages();
|
List<MessageHistory> messages = chatConversation.getMessages();
|
||||||
if (oConvertUtils.isObjectNotEmpty(messages)) {
|
if (oConvertUtils.isObjectNotEmpty(messages)) {
|
||||||
messages = messages.stream()
|
messages = mergeToolMessages(messages, showToolProcess);
|
||||||
.filter(msg -> !AiragConsts.MESSAGE_ROLE_TOOL.equals(msg.getRole()))
|
|
||||||
.map(msg -> {
|
|
||||||
// 克隆消息对象,移除工具执行请求信息(前端不需要)
|
|
||||||
MessageHistory displayMsg = MessageHistory.builder()
|
|
||||||
.conversationId(msg.getConversationId())
|
|
||||||
.topicId(msg.getTopicId())
|
|
||||||
.role(msg.getRole())
|
|
||||||
.content(msg.getContent())
|
|
||||||
.images(msg.getImages())
|
|
||||||
.files(msg.getFiles())
|
|
||||||
.datetime(msg.getDatetime())
|
|
||||||
.build();
|
|
||||||
// 不设置toolExecutionRequests和toolExecutionResult
|
|
||||||
return displayMsg;
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
result.put("messages", messages);
|
result.put("messages", messages);
|
||||||
result.put("flowInputs", chatConversation.getFlowInputs());
|
result.put("flowInputs", chatConversation.getFlowInputs());
|
||||||
@@ -339,6 +356,104 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
//update-end---author:chenrui ---date:20251106 for:[issues/8545]新建AI应用的时候只能选择没有自定义参数的AI流程------------
|
//update-end---author:chenrui ---date:20251106 for:[issues/8545]新建AI应用的时候只能选择没有自定义参数的AI流程------------
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并工具调用相关的历史记录,生成带有工具执行标签的AI消息
|
||||||
|
*
|
||||||
|
* @param histories 历史消息列表
|
||||||
|
* @param showToolProcess 是否显示工具调用过程
|
||||||
|
* @return 合并后的历史消息列表
|
||||||
|
*/
|
||||||
|
private List<MessageHistory> mergeToolMessages(List<MessageHistory> histories, boolean showToolProcess) {
|
||||||
|
List<MessageHistory> mergedMessages = new ArrayList<>();
|
||||||
|
if (oConvertUtils.isObjectEmpty(histories)) {
|
||||||
|
return mergedMessages;
|
||||||
|
}
|
||||||
|
// 缓存工具请求,便于后续快速匹配
|
||||||
|
Map<String, MessageHistory.ToolExecutionRequestHistory> requestCache = new HashMap<>();
|
||||||
|
// 当前正在合并的AI消息
|
||||||
|
MessageHistory currentAiMsg = null;
|
||||||
|
// 合并AI消息
|
||||||
|
BiConsumer<MessageHistory, Object> mergeMsg = (cacheMsg, obj) -> {
|
||||||
|
String currContent;
|
||||||
|
if (obj instanceof MessageHistory) {
|
||||||
|
MessageHistory currMsg = (MessageHistory) obj;
|
||||||
|
currContent = currMsg.getContent();
|
||||||
|
// 合并图片
|
||||||
|
if (CollectionUtils.isNotEmpty(currMsg.getImages())) {
|
||||||
|
List<MessageHistory.ImageHistory> images = CollectionUtils.isEmpty(cacheMsg.getImages()) ? new ArrayList<>() : cacheMsg.getImages();
|
||||||
|
images.addAll(currMsg.getImages());
|
||||||
|
cacheMsg.setImages(images);
|
||||||
|
}
|
||||||
|
// 合并文件
|
||||||
|
if (CollectionUtils.isNotEmpty(currMsg.getFiles())) {
|
||||||
|
List<MessageHistory.FileHistory> files = CollectionUtils.isEmpty(cacheMsg.getImages()) ? new ArrayList<>() : cacheMsg.getFiles();
|
||||||
|
files.addAll(currMsg.getFiles());
|
||||||
|
cacheMsg.setFiles(files);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currContent = obj.toString();
|
||||||
|
}
|
||||||
|
cacheMsg.setContent(cacheMsg.getContent() + currContent);
|
||||||
|
};
|
||||||
|
// 遍历所有消息,根据类型的不同做出不同处理
|
||||||
|
for (MessageHistory message : histories) {
|
||||||
|
// 用户消息原样保留,不参与合并
|
||||||
|
if (AiragConsts.MESSAGE_ROLE_USER.equals(message.getRole())) {
|
||||||
|
if (currentAiMsg != null) {
|
||||||
|
mergedMessages.add(currentAiMsg);
|
||||||
|
currentAiMsg = null;
|
||||||
|
}
|
||||||
|
mergedMessages.add(message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 从当前AI消息开始向后合并工具调用与连续AI消息
|
||||||
|
if (AiragConsts.MESSAGE_ROLE_AI.equals(message.getRole())) {
|
||||||
|
if (currentAiMsg == null) {
|
||||||
|
currentAiMsg = MessageHistory.builder()
|
||||||
|
.conversationId(message.getConversationId())
|
||||||
|
.topicId(message.getTopicId())
|
||||||
|
.role(message.getRole())
|
||||||
|
.content("")
|
||||||
|
.images(message.getImages())
|
||||||
|
.files(message.getFiles())
|
||||||
|
.datetime(message.getDatetime())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
mergeMsg.accept(currentAiMsg, message);
|
||||||
|
List<MessageHistory.ToolExecutionRequestHistory> toolReqs = message.getToolExecutionRequests();
|
||||||
|
if (CollectionUtils.isNotEmpty(toolReqs)) {
|
||||||
|
for (MessageHistory.ToolExecutionRequestHistory request : toolReqs) {
|
||||||
|
if (request != null) {
|
||||||
|
// 使用工具调用id作为唯一键,方便快速匹配结果
|
||||||
|
requestCache.put(request.getId(), request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (AiragConsts.MESSAGE_ROLE_TOOL.equals(message.getRole())) {
|
||||||
|
if (currentAiMsg == null || !showToolProcess) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String toolId = message.getContent();
|
||||||
|
MessageHistory.ToolExecutionRequestHistory request = requestCache.get(toolId);
|
||||||
|
if (request == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String toolResult = message.getToolExecutionResult();
|
||||||
|
ToolExecutionVo vo = ToolExecutionVo.build(toolId, request.getName(), request.getArguments(), toolResult);
|
||||||
|
String execTag = JeecgTagHelper.createTag(JeecgTagHelper.TAG_JEECG_TOOL_EXEC, JSON.toJSONString(vo));
|
||||||
|
mergeMsg.accept(currentAiMsg, execTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 避免最后一条消息没有放入列表
|
||||||
|
if (currentAiMsg != null) {
|
||||||
|
mergedMessages.add(currentAiMsg);
|
||||||
|
}
|
||||||
|
return mergedMessages;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<?> clearMessage(String conversationId, String sessionType) {
|
public Result<?> clearMessage(String conversationId, String sessionType) {
|
||||||
AssertUtils.assertNotEmpty("请先选择会话", conversationId);
|
AssertUtils.assertNotEmpty("请先选择会话", conversationId);
|
||||||
@@ -713,6 +828,8 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
break;
|
break;
|
||||||
case AiragConsts.MESSAGE_ROLE_AI:
|
case AiragConsts.MESSAGE_ROLE_AI:
|
||||||
// 重建AI消息,包括工具执行请求
|
// 重建AI消息,包括工具执行请求
|
||||||
|
// 获取内容,如果为空则使用空字符串(AiMessage不允许null)
|
||||||
|
String aiContent = oConvertUtils.getString(history.getContent(), "");
|
||||||
if (oConvertUtils.isObjectNotEmpty(history.getToolExecutionRequests())) {
|
if (oConvertUtils.isObjectNotEmpty(history.getToolExecutionRequests())) {
|
||||||
// 有工具执行请求,重建带工具调用的AiMessage
|
// 有工具执行请求,重建带工具调用的AiMessage
|
||||||
List<ToolExecutionRequest> toolRequests = history.getToolExecutionRequests().stream()
|
List<ToolExecutionRequest> toolRequests = history.getToolExecutionRequests().stream()
|
||||||
@@ -722,9 +839,9 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
.arguments(toolReq.getArguments())
|
.arguments(toolReq.getArguments())
|
||||||
.build())
|
.build())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
chatMessage = AiMessage.from(history.getContent(), toolRequests);
|
chatMessage = AiMessage.from(aiContent, toolRequests);
|
||||||
} else {
|
} else {
|
||||||
chatMessage = new AiMessage(history.getContent());
|
chatMessage = new AiMessage(aiContent);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AiragConsts.MESSAGE_ROLE_TOOL:
|
case AiragConsts.MESSAGE_ROLE_TOOL:
|
||||||
@@ -735,7 +852,10 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
.name("unknown") // 工具名称在重建时不重要,因为主要用于AI理解结果
|
.name("unknown") // 工具名称在重建时不重要,因为主要用于AI理解结果
|
||||||
.arguments("{}")
|
.arguments("{}")
|
||||||
.build();
|
.build();
|
||||||
chatMessage = ToolExecutionResultMessage.from(recreatedRequest, history.getToolExecutionResult());
|
//update-begin---author:scott ---date:20260416 for:【PR#9539】修复通义千问API不接受null消息内容-----------
|
||||||
|
String toolResult = history.getToolExecutionResult() != null ? history.getToolExecutionResult() : "";
|
||||||
|
chatMessage = ToolExecutionResultMessage.from(recreatedRequest, toolResult);
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9539】修复通义千问API不接受null消息内容-----------
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (null == chatMessage) {
|
if (null == chatMessage) {
|
||||||
@@ -765,7 +885,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
|
|
||||||
private void appendMessage(List<ChatMessage> messages, ChatMessage message, ChatConversation chatConversation, String topicId, List<String> files, String saveContent) {
|
private void appendMessage(List<ChatMessage> messages, ChatMessage message, ChatConversation chatConversation, String topicId, List<String> files, String saveContent) {
|
||||||
|
|
||||||
if (message.type().equals(ChatMessageType.SYSTEM)) {
|
if (message instanceof SystemMessage) {
|
||||||
// 系统消息,放到消息列表最前面,并且不记录历史
|
// 系统消息,放到消息列表最前面,并且不记录历史
|
||||||
messages.add(0, message);
|
messages.add(0, message);
|
||||||
return;
|
return;
|
||||||
@@ -778,18 +898,18 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
}
|
}
|
||||||
// 消息记录
|
// 消息记录
|
||||||
MessageHistory historyMessage = MessageHistory.builder().conversationId(chatConversation.getId()).topicId(topicId).datetime(DateUtils.now()).build();
|
MessageHistory historyMessage = MessageHistory.builder().conversationId(chatConversation.getId()).topicId(topicId).datetime(DateUtils.now()).build();
|
||||||
if (message.type().equals(ChatMessageType.USER)) {
|
if (message instanceof UserMessage) {
|
||||||
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_USER);
|
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_USER);
|
||||||
StringBuilder textContent = new StringBuilder();
|
StringBuilder textContent = new StringBuilder();
|
||||||
List<MessageHistory.ImageHistory> images = new ArrayList<>();
|
List<MessageHistory.ImageHistory> images = new ArrayList<>();
|
||||||
List<Content> contents = ((UserMessage) message).contents();
|
List<Content> contents = ((UserMessage) message).contents();
|
||||||
contents.forEach(content -> {
|
contents.forEach(content -> {
|
||||||
if (content.type().equals(ContentType.IMAGE)) {
|
if (content instanceof ImageContent) {
|
||||||
ImageContent imageContent = (ImageContent) content;
|
ImageContent imageContent = (ImageContent) content;
|
||||||
Image image = imageContent.image();
|
Image image = imageContent.image();
|
||||||
MessageHistory.ImageHistory imageMessage = MessageHistory.ImageHistory.from(image.url(), image.base64Data(), image.mimeType());
|
MessageHistory.ImageHistory imageMessage = MessageHistory.ImageHistory.from(image.url(), image.base64Data(), image.mimeType());
|
||||||
images.add(imageMessage);
|
images.add(imageMessage);
|
||||||
} else if (content.type().equals(ContentType.TEXT)) {
|
} else if (content instanceof TextContent) {
|
||||||
textContent.append(((TextContent) content).text()).append("\n");
|
textContent.append(((TextContent) content).text()).append("\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -809,10 +929,12 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
historyMessage.setFiles(fileHistories);
|
historyMessage.setFiles(fileHistories);
|
||||||
}
|
}
|
||||||
//update-end---author:wangshuai---date:2026-01-12---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
//update-end---author:wangshuai---date:2026-01-12---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
||||||
} else if (message.type().equals(ChatMessageType.AI)) {
|
} else if (message instanceof AiMessage) {
|
||||||
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_AI);
|
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_AI);
|
||||||
AiMessage aiMessage = (AiMessage) message;
|
AiMessage aiMessage = (AiMessage) message;
|
||||||
historyMessage.setContent(aiMessage.text());
|
//update-begin---author:scott ---date:20260416 for:【PR#9539】修复通义千问API不接受null消息内容-----------
|
||||||
|
historyMessage.setContent(aiMessage.text() != null ? aiMessage.text() : "");
|
||||||
|
//update-end---author:scott ---date:20260416 for:【PR#9539】修复通义千问API不接受null消息内容-----------
|
||||||
// 处理工具执行请求
|
// 处理工具执行请求
|
||||||
if (oConvertUtils.isObjectNotEmpty(aiMessage.toolExecutionRequests())) {
|
if (oConvertUtils.isObjectNotEmpty(aiMessage.toolExecutionRequests())) {
|
||||||
List<MessageHistory.ToolExecutionRequestHistory> toolRequests = new ArrayList<>();
|
List<MessageHistory.ToolExecutionRequestHistory> toolRequests = new ArrayList<>();
|
||||||
@@ -825,7 +947,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
}
|
}
|
||||||
historyMessage.setToolExecutionRequests(toolRequests);
|
historyMessage.setToolExecutionRequests(toolRequests);
|
||||||
}
|
}
|
||||||
} else if (message.type().equals(ChatMessageType.TOOL_EXECUTION_RESULT)) {
|
} else if (message instanceof ToolExecutionResultMessage) {
|
||||||
// 工具执行结果消息
|
// 工具执行结果消息
|
||||||
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_TOOL);
|
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_TOOL);
|
||||||
ToolExecutionResultMessage toolMessage = (ToolExecutionResultMessage) message;
|
ToolExecutionResultMessage toolMessage = (ToolExecutionResultMessage) message;
|
||||||
@@ -941,7 +1063,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
drawModelId = JSONObject.parseObject(metadata).getString("drawModelId");
|
drawModelId = JSONObject.parseObject(metadata).getString("drawModelId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssertUtils.assertNotEmpty("请选择绘画模型", drawModelId);
|
//AssertUtils.assertNotEmpty("请选择绘画模型", drawModelId);
|
||||||
try {
|
try {
|
||||||
List<String> images = sendParams.getImages();
|
List<String> images = sendParams.getImages();
|
||||||
List<Map<String, Object>> imageList;
|
List<Map<String, Object>> imageList;
|
||||||
@@ -1000,6 +1122,14 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
flowRunParams.setFlowId(flowId);
|
flowRunParams.setFlowId(flowId);
|
||||||
flowRunParams.setConversationId(chatConversation.getId());
|
flowRunParams.setConversationId(chatConversation.getId());
|
||||||
flowRunParams.setTopicId(topicId);
|
flowRunParams.setTopicId(topicId);
|
||||||
|
// 传入应用id(变量节点需要)
|
||||||
|
if (chatConversation.getApp() != null) {
|
||||||
|
flowRunParams.setAppId(chatConversation.getApp().getId());
|
||||||
|
}
|
||||||
|
// 传入记忆库id(记忆节点需要)
|
||||||
|
if (chatConversation.getApp() != null) {
|
||||||
|
flowRunParams.setMemoryId(chatConversation.getApp().getMemoryId());
|
||||||
|
}
|
||||||
// 支持流式
|
// 支持流式
|
||||||
flowRunParams.setResponseMode(FlowConsts.FLOW_RESPONSE_MODE_STREAMING);
|
flowRunParams.setResponseMode(FlowConsts.FLOW_RESPONSE_MODE_STREAMING);
|
||||||
Map<String, Object> flowInputParams = new HashMap<>();
|
Map<String, Object> flowInputParams = new HashMap<>();
|
||||||
@@ -1138,6 +1268,14 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
if (metadata.containsKey("maxTokens")) {
|
if (metadata.containsKey("maxTokens")) {
|
||||||
aiChatParams.setMaxTokens(metadata.getInteger("maxTokens"));
|
aiChatParams.setMaxTokens(metadata.getInteger("maxTokens"));
|
||||||
}
|
}
|
||||||
|
//update-begin---wangshuai---date:20260401 for:【issues/9455】AI应用中设定的RAG参数未生效------------
|
||||||
|
if (metadata.containsKey("topNumber")) {
|
||||||
|
aiChatParams.setTopNumber(metadata.getInteger("topNumber"));
|
||||||
|
}
|
||||||
|
if (metadata.containsKey("similarity")) {
|
||||||
|
aiChatParams.setSimilarity(metadata.getDouble("similarity"));
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai ---date:20260401 for:【issues/9455】AI应用中设定的RAG参数未生效------------
|
||||||
if (metadata.containsKey(FlowConsts.FLOW_NODE_OPTION_TIME_OUT)) {
|
if (metadata.containsKey(FlowConsts.FLOW_NODE_OPTION_TIME_OUT)) {
|
||||||
aiChatParams.setTimeout(oConvertUtils.getInt(metadata.getInteger(FlowConsts.FLOW_NODE_OPTION_TIME_OUT), 300));
|
aiChatParams.setTimeout(oConvertUtils.getInt(metadata.getInteger(FlowConsts.FLOW_NODE_OPTION_TIME_OUT), 300));
|
||||||
}
|
}
|
||||||
@@ -1163,9 +1301,9 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//流程不为空,构建插件
|
//流程不为空,构建插件(携带应用上下文参数,供变量/记忆节点使用)
|
||||||
if(oConvertUtils.isNotEmpty(flowId)){
|
if(oConvertUtils.isNotEmpty(flowId)){
|
||||||
Map<String, Object> result = airagFlowPluginService.getFlowsToPlugin(flowId);
|
Map<String, Object> result = airagFlowPluginService.getFlowsToPlugin(flowId, aiApp.getId(), memoryId);
|
||||||
this.addPluginToParams(aiChatParams, result);
|
this.addPluginToParams(aiChatParams, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1194,6 +1332,11 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
airagVariableService.addUpdateVariableTool(aiApp,username,aiChatParams);
|
airagVariableService.addUpdateVariableTool(aiApp,username,aiChatParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update-begin---author:wangshuai---date:2026-03-18---for:【QQYUN-14935】Langchain4j 新版支持 Agent Skills,重新定义 Java AI 应用的能力边界---
|
||||||
|
// 封装skills及上下文信息
|
||||||
|
fillSkillsParams(aiChatParams);
|
||||||
|
//update-end---author:wangshuai---date:2026-03-18---for:【QQYUN-14935】Langchain4j 新版支持 Agent Skills,重新定义 Java AI 应用的能力边界---
|
||||||
|
|
||||||
// 打印流程耗时日志
|
// 打印流程耗时日志
|
||||||
printChatDuration(requestId, "构造应用自定义参数完成");
|
printChatDuration(requestId, "构造应用自定义参数完成");
|
||||||
// 发消息
|
// 发消息
|
||||||
@@ -1228,6 +1371,49 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装skills参数及上下文信息
|
||||||
|
* 当配置了skillsPath时,将skills路径设置到参数中,并将Token、后台地址、租户ID拼接到用户消息后面
|
||||||
|
*
|
||||||
|
* @param aiChatParams AI聊天参数
|
||||||
|
*/
|
||||||
|
private void fillSkillsParams(AIChatParams aiChatParams) {
|
||||||
|
if (oConvertUtils.isEmpty(aiChatConfig.getSkillsDir()) && oConvertUtils.isEmpty(aiChatConfig.getSkillsShellDir())) {
|
||||||
|
log.info("[Skills] skillsPath OR shellSkillsDir is empty, skip skills loading");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oConvertUtils.isNotEmpty(aiChatConfig.getSkillsDir())){
|
||||||
|
aiChatParams.setSkillsDir(aiChatConfig.getSkillsDir());
|
||||||
|
log.info("[Skills] skillsDir set to: {}", aiChatParams.getSkillsDir());
|
||||||
|
}
|
||||||
|
if (oConvertUtils.isNotEmpty((aiChatConfig.getSkillsShellDir()))){
|
||||||
|
aiChatParams.setSkillsShellDir(aiChatConfig.getSkillsShellDir());
|
||||||
|
log.info("[Skills] shellSkillsDir set to: {}", aiChatParams.getSkillsShellDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注入运行时上下文:Token、后台API地址、租户ID,供Skills使用
|
||||||
|
try {
|
||||||
|
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||||
|
String token = TokenUtils.getTokenByRequest(request);
|
||||||
|
String tenantId = request.getHeader("X-Tenant-Id");
|
||||||
|
// 从当前请求构造后台API地址
|
||||||
|
String apiBase = CommonUtils.getBaseUrl(request);
|
||||||
|
StringBuilder context = new StringBuilder();
|
||||||
|
context.append("以下信息由系统自动注入,Skill执行时可直接使用:\n");
|
||||||
|
context.append("- **API_BASE**: `").append(apiBase).append("`\n");
|
||||||
|
if (oConvertUtils.isNotEmpty(token)) {
|
||||||
|
context.append("- **X-Access-Token**: `").append(token).append("`\n");
|
||||||
|
}
|
||||||
|
if (oConvertUtils.isNotEmpty(tenantId)) {
|
||||||
|
context.append("- **X-Tenant-Id**: `").append(tenantId).append("`\n");
|
||||||
|
}
|
||||||
|
aiChatParams.setSkillsContext(context.toString());
|
||||||
|
log.info("[Skills] context injected, apiBase: {}", apiBase);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("[Skills] Failed to inject context: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理聊天
|
* 处理聊天
|
||||||
* 向大模型发送消息并接受响应
|
* 向大模型发送消息并接受响应
|
||||||
@@ -1246,22 +1432,41 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
if (null == aiChatParams) {
|
if (null == aiChatParams) {
|
||||||
aiChatParams = new AIChatParams();
|
aiChatParams = new AIChatParams();
|
||||||
}
|
}
|
||||||
|
HttpServletRequest httpRequest = SpringContextUtils.getHttpServletRequest();
|
||||||
// 如果是默认app,加载系统默认工具
|
// 如果是默认app,加载系统默认工具
|
||||||
if(chatConversation.getApp().getId().equals(AiAppConsts.DEFAULT_APP_ID)){
|
if(chatConversation.getApp().getId().equals(AiAppConsts.DEFAULT_APP_ID)){
|
||||||
aiChatParams.setTools(jeecgToolsProvider.getDefaultTools());
|
// Security fix: 仅已登录用户可加载敏感业务工具(add_user,grant_user_roles等),匿名用户仍可正常使用AI聊天
|
||||||
|
String currentUser = getUsername(httpRequest);
|
||||||
|
if (oConvertUtils.isNotEmpty(currentUser)) {
|
||||||
|
aiChatParams.setTools(jeecgToolsProvider.getDefaultTools());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-15 for:Brave Search配置迁移到AiRagConfigBean,仅在联网搜索开启时注入工具-----------
|
||||||
|
// Brave Search 联网检索工具:前端 enableSearch=true 且 apiKey 已配置时才注入
|
||||||
|
if (Boolean.TRUE.equals(aiChatParams.getEnableSearch())) {
|
||||||
|
Map<ToolSpecification, ToolExecutor> braveTools = BraveSearchToolBuilder.buildTools(aiRagConfigBean.getBraveSearch());
|
||||||
|
if (!braveTools.isEmpty()) {
|
||||||
|
Map<ToolSpecification, ToolExecutor> existing = aiChatParams.getTools();
|
||||||
|
if (existing == null) {
|
||||||
|
existing = new HashMap<>();
|
||||||
|
}
|
||||||
|
existing.putAll(braveTools);
|
||||||
|
aiChatParams.setTools(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-15 for:Brave Search配置迁移到AiRagConfigBean,仅在联网搜索开启时注入工具-----------
|
||||||
if(CollectionUtils.isEmpty(aiChatParams.getKnowIds())){
|
if(CollectionUtils.isEmpty(aiChatParams.getKnowIds())){
|
||||||
aiChatParams.setKnowIds(chatConversation.getApp().getKnowIds());
|
aiChatParams.setKnowIds(chatConversation.getApp().getKnowIds());
|
||||||
} else {
|
} else {
|
||||||
aiChatParams.getKnowIds().addAll(chatConversation.getApp().getKnowIds());
|
aiChatParams.getKnowIds().addAll(chatConversation.getApp().getKnowIds());
|
||||||
}
|
}
|
||||||
aiChatParams.setMaxMsgNumber(oConvertUtils.getInt(chatConversation.getApp().getMsgNum(), 5));
|
aiChatParams.setMaxMsgNumber(oConvertUtils.getInt(chatConversation.getApp().getMsgNum(), 5));
|
||||||
aiChatParams.setCurrentHttpRequest(SpringContextUtils.getHttpServletRequest());
|
aiChatParams.setCurrentHttpRequest(httpRequest);
|
||||||
HttpServletRequest httpRequest = SpringContextUtils.getHttpServletRequest();
|
|
||||||
// for [QQYUN-9234] MCP服务连接关闭 - 保存参数引用用于在回调中关闭MCP连接
|
// for [QQYUN-9234] MCP服务连接关闭 - 保存参数引用用于在回调中关闭MCP连接
|
||||||
final AIChatParams finalAiChatParams = aiChatParams;
|
final AIChatParams finalAiChatParams = aiChatParams;
|
||||||
TokenStream chatStream;
|
TokenStream chatStream;
|
||||||
try {
|
try {
|
||||||
|
aiChatParams.setTimeout(5*30*1000);
|
||||||
// 打印流程耗时日志
|
// 打印流程耗时日志
|
||||||
printChatDuration(requestId, "开始向LLM发送消息");
|
printChatDuration(requestId, "开始向LLM发送消息");
|
||||||
if (oConvertUtils.isNotEmpty(modelId)) {
|
if (oConvertUtils.isNotEmpty(modelId)) {
|
||||||
@@ -1280,7 +1485,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
String errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||||
if(e instanceof JeecgBootException){
|
if(e instanceof JeecgBootException || e instanceof JeecgBootBizTipException){
|
||||||
errMsg = e.getMessage();
|
errMsg = e.getMessage();
|
||||||
}
|
}
|
||||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||||
@@ -1288,6 +1493,38 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
closeSSE(emitter, eventData);
|
closeSSE(emitter, eventData);
|
||||||
throw new JeecgBootBizTipException("调用大模型接口失败:" + e.getMessage());
|
throw new JeecgBootBizTipException("调用大模型接口失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 发送消息给前端
|
||||||
|
BiConsumer<String, String> send2Client = (resMessage, eventType) -> {
|
||||||
|
eventType = oConvertUtils.isNotEmpty(eventType) ? eventType : EventData.EVENT_MESSAGE;
|
||||||
|
|
||||||
|
EventData eventData = new EventData(requestId, null, eventType, chatConversation.getId(), topicId);
|
||||||
|
EventMessageData messageEventData = EventMessageData.builder().message(resMessage).build();
|
||||||
|
eventData.setData(messageEventData);
|
||||||
|
eventData.setRequestId(requestId);
|
||||||
|
// sse
|
||||||
|
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
|
||||||
|
if (null == emitter) {
|
||||||
|
log.warn("[AI应用]接收LLM返回会话已关闭");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendMessage2Client(emitter, eventData);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 解析是否显示工具调用过程(默认为true)
|
||||||
|
boolean showToolProcess = true;
|
||||||
|
String appMetadataStr = chatConversation.getApp().getMetadata();
|
||||||
|
if (oConvertUtils.isNotEmpty(appMetadataStr)) {
|
||||||
|
try {
|
||||||
|
JSONObject appMetadataJson = JSONObject.parseObject(appMetadataStr);
|
||||||
|
if (appMetadataJson != null && "0".equals(appMetadataJson.getString("showToolProcess"))) {
|
||||||
|
showToolProcess = false;
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final boolean finalShowToolProcess = showToolProcess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否正在思考
|
* 是否正在思考
|
||||||
*/
|
*/
|
||||||
@@ -1301,22 +1538,19 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
isThinking.set(false);
|
isThinking.set(false);
|
||||||
}
|
}
|
||||||
//update-end---author:wangshuai---date:2025-11-07---for:[issues/8506]/[issues/8260]/[issues/8166]新增推理模型的支持---
|
//update-end---author:wangshuai---date:2025-11-07---for:[issues/8506]/[issues/8260]/[issues/8166]新增推理模型的支持---
|
||||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
|
send2Client.accept(resMessage, EventData.EVENT_MESSAGE);
|
||||||
EventMessageData messageEventData = EventMessageData.builder().message(resMessage).build();
|
}).beforeToolExecution(beforeToolExecution -> {
|
||||||
eventData.setData(messageEventData);
|
// 监听工具执行请求(根据配置决定是否发送给前端)
|
||||||
eventData.setRequestId(requestId);
|
if (finalShowToolProcess) {
|
||||||
// sse
|
ToolExecutionVo vo = ToolExecutionVo.build(beforeToolExecution);
|
||||||
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
|
String execTag = JeecgTagHelper.createTag(JeecgTagHelper.TAG_JEECG_TOOL_EXEC, JSON.toJSONString(vo));
|
||||||
if (null == emitter) {
|
send2Client.accept(execTag, EventData.EVENT_TOOL_EXEC_BEFORE);
|
||||||
log.warn("[AI应用]接收LLM返回会话已关闭");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
sendMessage2Client(emitter, eventData);
|
|
||||||
}).onToolExecuted((toolExecution) -> {
|
}).onToolExecuted((toolExecution) -> {
|
||||||
// 打印工具执行结果
|
// 打印工具执行结果
|
||||||
log.debug("[AI应用]工具执行结果: toolName={}, toolId={}, result={}",
|
log.debug("[AI应用]工具执行结果: toolName={}, toolId={}, result={}",
|
||||||
toolExecution.request().name(),
|
toolExecution.request().name(),
|
||||||
toolExecution.request().id(),
|
toolExecution.request().id(),
|
||||||
toolExecution.result());
|
toolExecution.result());
|
||||||
// 将工具执行结果存储到消息历史中
|
// 将工具执行结果存储到消息历史中
|
||||||
ToolExecutionResultMessage toolResultMessage = ToolExecutionResultMessage.from(
|
ToolExecutionResultMessage toolResultMessage = ToolExecutionResultMessage.from(
|
||||||
@@ -1324,6 +1558,13 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
toolExecution.result()
|
toolExecution.result()
|
||||||
);
|
);
|
||||||
appendMessage(messages, toolResultMessage, chatConversation, topicId);
|
appendMessage(messages, toolResultMessage, chatConversation, topicId);
|
||||||
|
// 根据配置决定是否将工具调用过程发送给前端
|
||||||
|
if (finalShowToolProcess) {
|
||||||
|
ToolExecutionVo vo = ToolExecutionVo.build(toolExecution);
|
||||||
|
String execTag = JeecgTagHelper.createTag(JeecgTagHelper.TAG_JEECG_TOOL_EXEC, JSON.toJSONString(vo));
|
||||||
|
send2Client.accept(execTag, EventData.EVENT_TOOL_EXEC_DONE);
|
||||||
|
send2Client.accept(execTag, EventData.EVENT_MESSAGE);
|
||||||
|
}
|
||||||
}).onIntermediateResponse((chatResponse) -> {
|
}).onIntermediateResponse((chatResponse) -> {
|
||||||
// 中间响应:包含tool_calls的AI消息
|
// 中间响应:包含tool_calls的AI消息
|
||||||
AiMessage aiMessage = chatResponse.aiMessage();
|
AiMessage aiMessage = chatResponse.aiMessage();
|
||||||
@@ -1422,14 +1663,20 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
//update-end---author:chenrui ---date:20250425 for:[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
|
//update-end---author:chenrui ---date:20250425 for:[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
|
||||||
} else {
|
} else {
|
||||||
errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
errMsg = "调用大模型接口失败,详情请查看后台日志。";
|
||||||
|
boolean isFindErrorMsg = false;
|
||||||
// 根据常见异常关键字做细致翻译
|
// 根据常见异常关键字做细致翻译
|
||||||
for (Map.Entry<String, String> entry : AIChatHandler.MODEL_ERROR_MAP.entrySet()) {
|
for (Map.Entry<String, String> entry : AIChatHandler.MODEL_ERROR_MAP.entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
String value = entry.getValue();
|
String value = entry.getValue();
|
||||||
if (error.getMessage().contains(key)) {
|
if (error.getMessage().contains(key)) {
|
||||||
errMsg = value;
|
errMsg = value;
|
||||||
|
isFindErrorMsg = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String message = error.getMessage();
|
||||||
|
if(!isFindErrorMsg && message.contains("error")) {
|
||||||
|
errMsg = JSONObject.parseObject(message).get("error").toString();
|
||||||
|
}
|
||||||
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
|
||||||
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
|
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
|
||||||
closeSSE(emitter, eventData);
|
closeSSE(emitter, eventData);
|
||||||
@@ -1693,29 +1940,87 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
/**
|
/**
|
||||||
* ai海报生成
|
* ai海报生成
|
||||||
*
|
*
|
||||||
* @param chatSendParams
|
* @param aiDrawGenerateVo
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String genAiPoster(ChatSendParams chatSendParams) {
|
public String genAiPoster(AiDrawGenerateVo aiDrawGenerateVo) {
|
||||||
AssertUtils.assertNotEmpty("请选择绘画模型", chatSendParams.getDrawModelId());
|
AssertUtils.assertNotEmpty("请选择绘画模型", aiDrawGenerateVo.getDrawModelId());
|
||||||
AssertUtils.assertNotEmpty("请填写提示词", chatSendParams.getContent());
|
AssertUtils.assertNotEmpty("请填写提示词", aiDrawGenerateVo.getContent());
|
||||||
AIChatParams aiChatParams = new AIChatParams();
|
AIChatParams aiChatParams = new AIChatParams();
|
||||||
if(oConvertUtils.isNotEmpty(chatSendParams.getImageSize())){
|
//update-begin---author:wangshuai---date:2026-02-05---for:【QQYUN-14568】AI绘画功能---
|
||||||
aiChatParams.setImageSize(chatSendParams.getImageSize());
|
if(oConvertUtils.isNotEmpty(aiDrawGenerateVo.getImageSize())){
|
||||||
|
aiChatParams.setImageSize(aiDrawGenerateVo.getImageSize());
|
||||||
}
|
}
|
||||||
String image= chatSendParams.getImageUrl();
|
//aiChatParams.setNegativePrompt("面部扭曲,特征丢失,边缘模糊,比例失调,模糊,多余的手指");
|
||||||
|
//绘图
|
||||||
|
if(AiAppConsts.AI_DRAW_TYPE_DRAW.equals(aiDrawGenerateVo.getType())){
|
||||||
|
String format = StrUtil.format(Prompts.AI_DRAW_PROMPT, aiDrawGenerateVo.getStyle(), aiDrawGenerateVo.getVisualAngle(), aiDrawGenerateVo.getCharacterShot(), aiDrawGenerateVo.getLighting(), ImageSizeEnum.getRatioBySize(aiDrawGenerateVo.getImageSize()));
|
||||||
|
aiDrawGenerateVo.setContent(format + aiDrawGenerateVo.getContent());
|
||||||
|
}
|
||||||
|
if((AiAppConsts.AI_DRAW_TYPE_FACE.equals(aiDrawGenerateVo.getType()) || AiAppConsts.AI_DRAW_TYPE_MIX.equals(aiDrawGenerateVo.getType())) && oConvertUtils.isNotEmpty(aiDrawGenerateVo.getImageSize())){
|
||||||
|
aiDrawGenerateVo.setContent(aiDrawGenerateVo.getContent() + "比例:" + ImageSizeEnum.getRatioBySize(aiDrawGenerateVo.getImageSize()));
|
||||||
|
}
|
||||||
|
String image= aiDrawGenerateVo.getImageUrl();
|
||||||
|
//update-end---author:wangshuai---date:2026-02-05---for:【QQYUN-14568】AI绘画功能---
|
||||||
List<Map<String, Object>> imageList = new ArrayList<>();
|
List<Map<String, Object>> imageList = new ArrayList<>();
|
||||||
if(oConvertUtils.isEmpty(image)) {
|
if(oConvertUtils.isEmpty(image)) {
|
||||||
//生成图片
|
//生成图片
|
||||||
imageList = aiChatHandler.imageGenerate(chatSendParams.getDrawModelId(), chatSendParams.getContent(), aiChatParams);
|
imageList = aiChatHandler.imageGenerate(aiDrawGenerateVo.getDrawModelId(), aiDrawGenerateVo.getContent(), aiChatParams);
|
||||||
} else {
|
} else {
|
||||||
//图生图
|
//图生图
|
||||||
imageList = aiChatHandler.imageEdit(chatSendParams.getDrawModelId(), chatSendParams.getContent(), Arrays.asList(image.split(SymbolConstant.COMMA)), aiChatParams);
|
imageList = aiChatHandler.imageEdit(aiDrawGenerateVo.getDrawModelId(), aiDrawGenerateVo.getContent(), Arrays.asList(image.split(SymbolConstant.COMMA)), aiChatParams);
|
||||||
}
|
}
|
||||||
return imageList.stream().map(this::uploadImage).collect(Collectors.joining("\n"));
|
return imageList.stream().map(this::uploadImage).collect(Collectors.joining("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String genAiPosterAsync(AiDrawGenerateVo aiDrawGenerateVo) {
|
||||||
|
AssertUtils.assertNotEmpty("请选择绘画模型", aiDrawGenerateVo.getDrawModelId());
|
||||||
|
AssertUtils.assertNotEmpty("请填写提示词", aiDrawGenerateVo.getContent());
|
||||||
|
String taskId = java.util.UUID.randomUUID().toString().replace("-", "");
|
||||||
|
// 写入 pending 状态
|
||||||
|
JSONObject task = new JSONObject();
|
||||||
|
task.put("status", "pending");
|
||||||
|
redisUtil.set(AiAppConsts.POSTER_TASK_PREFIX + taskId, task.toJSONString(), AiAppConsts.POSTER_TASK_TTL);
|
||||||
|
// 异步执行生成
|
||||||
|
SSE_THREAD_POOL.execute(() -> {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
try {
|
||||||
|
String imageUrl = genAiPoster(aiDrawGenerateVo);
|
||||||
|
result.put("status", "success");
|
||||||
|
result.put("imageUrl", imageUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[AI海报]异步生成失败 taskId={}", taskId, e);
|
||||||
|
result.put("status", "failed");
|
||||||
|
result.put("message", e.getMessage());
|
||||||
|
}
|
||||||
|
redisUtil.set(AiAppConsts.POSTER_TASK_PREFIX + taskId, result.toJSONString(), AiAppConsts.POSTER_TASK_TTL);
|
||||||
|
});
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<?> getAiPosterResult(String taskId) {
|
||||||
|
Object val = redisUtil.get(AiAppConsts.POSTER_TASK_PREFIX + taskId);
|
||||||
|
if (val == null) {
|
||||||
|
return Result.error("任务不存在或已过期");
|
||||||
|
}
|
||||||
|
JSONObject task = JSONObject.parseObject(val.toString());
|
||||||
|
String status = task.getString("status");
|
||||||
|
if ("success".equals(status)) {
|
||||||
|
return Result.OK(task.getString("imageUrl"));
|
||||||
|
}
|
||||||
|
if ("failed".equals(status)) {
|
||||||
|
return Result.error(task.getString("message"));
|
||||||
|
}
|
||||||
|
// pending
|
||||||
|
return Result.OK("pending", null);
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-15 for:【QQYUN-14568】AI海报生成改为异步,支持切换菜单后重新获取结果-----------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传图片
|
* 上传图片
|
||||||
*
|
*
|
||||||
@@ -1738,6 +2043,13 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
data = Base64.getDecoder().decode(value);
|
data = Base64.getDecoder().decode(value);
|
||||||
} else {
|
} else {
|
||||||
//下载网络图片
|
//下载网络图片
|
||||||
|
//update-begin---author:zhangdaihao ---date:20260427 for:[issues/9579]AI海报图片下载 SSRF 校验,拒绝 loopback/link-local------------
|
||||||
|
// genAiPoster -> uploadImage -> getDownInputStream,攻击者可通过 imageUrl 触发服务端访问 localhost / 云元数据等敏感目标;
|
||||||
|
// 沿用与 #9553 一致的基础 SSRF 校验(拒绝 loopback / link-local),保留对企业内网 MinIO/OSS 的兼容。
|
||||||
|
if (oConvertUtils.isNotEmpty(value) && value.toLowerCase().startsWith("http")) {
|
||||||
|
SsrfFileTypeFilter.checkSsrfHttpUrl(value);
|
||||||
|
}
|
||||||
|
//update-end-----author:zhangdaihao ---date:20260427 for:[issues/9579]AI海报图片下载 SSRF 校验,拒绝 loopback/link-local------------
|
||||||
InputStream inputStream = FileDownloadUtils.getDownInputStream(value, "");
|
InputStream inputStream = FileDownloadUtils.getDownInputStream(value, "");
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
@@ -1796,7 +2108,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String parseFilesToText(List<String> files) {
|
private String parseFilesToText(List<String> files) {
|
||||||
if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(files)) {
|
if (CollectionUtils.isEmpty(files)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -1855,16 +2167,37 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
private File ensureLocalFile(String fileRef, String fileName) {
|
private File ensureLocalFile(String fileRef, String fileName) {
|
||||||
String uploadpath = jeecgBaseConfig.getPath().getUpload();
|
String uploadpath = jeecgBaseConfig.getPath().getUpload();
|
||||||
if (LLMConsts.WEB_PATTERN.matcher(fileRef).matches()) {
|
if (LLMConsts.WEB_PATTERN.matcher(fileRef).matches()) {
|
||||||
|
//update-begin---author:wangshuai ---date:2026-04-13 for:【issues/9519】AI附件处理路径遍历漏洞:下载文件名做安全过滤,临时目录隔离---
|
||||||
|
// 远程下载:使用 FilenameUtils.getName 剥离任何路径分隔符,再次校验防止 ..
|
||||||
|
String safeFileName = FilenameUtils.getName(fileName);
|
||||||
|
SsrfFileTypeFilter.checkPathTraversal(safeFileName);
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-13 for:【issues/9519】AI附件处理路径遍历漏洞:下载文件名做安全过滤,临时目录隔离---
|
||||||
String tempDir = uploadpath + File.separator + "chat" + File.separator + UUID.randomUUID() + File.separator;
|
String tempDir = uploadpath + File.separator + "chat" + File.separator + UUID.randomUUID() + File.separator;
|
||||||
File dir = new File(tempDir);
|
File dir = new File(tempDir);
|
||||||
if (!dir.exists() && !dir.mkdirs()) {
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String tempFilePath = tempDir + fileName;
|
String tempFilePath = tempDir + safeFileName;
|
||||||
|
//update-begin---author:zhangdaihao ---date:20260427 for:[issues/9578]AI附件下载 SSRF 校验,拒绝 loopback/link-local------------
|
||||||
|
// /airag/chat/send 端点为 @IgnoreAuth 无认证,AI 聊天解析附件存在 SSRF 风险;
|
||||||
|
// 沿用与 #9553 一致的基础 SSRF 校验(拒绝 loopback / link-local),保留对企业内网 MinIO/OSS 的兼容。
|
||||||
|
SsrfFileTypeFilter.checkSsrfHttpUrl(fileRef);
|
||||||
|
//update-end-----author:zhangdaihao ---date:20260427 for:[issues/9578]AI附件下载 SSRF 校验,拒绝 loopback/link-local------------
|
||||||
FileDownloadUtils.download2DiskFromNet(fileRef, tempFilePath);
|
FileDownloadUtils.download2DiskFromNet(fileRef, tempFilePath);
|
||||||
return new File(tempFilePath);
|
return new File(tempFilePath);
|
||||||
}
|
}
|
||||||
return new File(uploadpath + File.separator + fileRef);
|
//update-begin---author:wangshuai ---date:2026-04-13 for:【issues/9519】AI附件处理路径遍历漏洞:规范化路径并强制校验沙箱范围---
|
||||||
|
// 本地附件:1) 先做字符级路径遍历检查;2) 规范化路径后必须仍在 uploadpath 下,阻止 ../ 逃逸
|
||||||
|
java.nio.file.Path root = Paths.get(uploadpath).toAbsolutePath().normalize();
|
||||||
|
SsrfFileTypeFilter.checkPathTraversal(fileRef);
|
||||||
|
String relativePath = fileRef.replaceAll("^[\\\\/]+", "");
|
||||||
|
java.nio.file.Path target = root.resolve(relativePath).toAbsolutePath().normalize();
|
||||||
|
if (!target.startsWith(root)) {
|
||||||
|
log.error("检测到路径遍历攻击! fileRef: {}, 解析后: {}", relativePath, target);
|
||||||
|
throw new JeecgBootException("文件路径包含非法字符");
|
||||||
|
}
|
||||||
|
return target.toFile();
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-13 for:【issues/9519】AI附件处理路径遍历漏洞:规范化路径并强制校验沙箱范围---
|
||||||
}
|
}
|
||||||
//================================================= end【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档========================================
|
//================================================= end【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档========================================
|
||||||
|
|
||||||
@@ -1887,7 +2220,9 @@ public class AiragChatServiceImpl implements IAiragChatService {
|
|||||||
content = StrUtil.format(Prompts.AI_WRITER_PROMPT, aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
content = StrUtil.format(Prompts.AI_WRITER_PROMPT, aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
||||||
} else if(reply.equals(aiWriteGenerateVo.getActiveMode())){
|
} else if(reply.equals(aiWriteGenerateVo.getActiveMode())){
|
||||||
//回复
|
//回复
|
||||||
content = StrUtil.format(Prompts.AI_REPLY_PROMPT, aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getOriginalContent(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
//update-begin---author:wangshuai ---date:2026-04-20 for:【QQYUN-15179】ai写作 生成的内容不对,应该是以回复来生成,而不是内容-----------
|
||||||
|
content = StrUtil.format(Prompts.AI_REPLY_PROMPT, aiWriteGenerateVo.getOriginalContent(), aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-20 for:【QQYUN-15179】ai写作 生成的内容不对,应该是以回复来生成,而不是内容-----------
|
||||||
} else {
|
} else {
|
||||||
content = StrUtil.format(Prompts.AI_TOUCHE_PROMPT, aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
content = StrUtil.format(Prompts.AI_TOUCHE_PROMPT, aiWriteGenerateVo.getPrompt(), aiWriteGenerateVo.getFormat(), aiWriteGenerateVo.getTone(), aiWriteGenerateVo.getLanguage(), aiWriteGenerateVo.getLength());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.jeecg.modules.airag.app.service.impl;
|
|||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import dev.langchain4j.agent.tool.ToolSpecification;
|
import dev.langchain4j.agent.tool.ToolSpecification;
|
||||||
|
import dev.langchain4j.model.chat.request.json.JsonArraySchema;
|
||||||
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
|
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
|
||||||
import dev.langchain4j.service.tool.ToolExecutor;
|
import dev.langchain4j.service.tool.ToolExecutor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -53,6 +54,24 @@ public class AiragVariableServiceImpl implements IAiragVariableService {
|
|||||||
redisTemplate.opsForHash().putIfAbsent(key, name, defaultValue != null ? defaultValue : "");
|
redisTemplate.opsForHash().putIfAbsent(key, name, defaultValue != null ? defaultValue : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取变量值
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param appId 应用ID
|
||||||
|
* @param name 变量名
|
||||||
|
* @return 变量值,不存在返回null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getVariable(String username, String appId, String name) {
|
||||||
|
if (oConvertUtils.isEmpty(username) || oConvertUtils.isEmpty(appId) || oConvertUtils.isEmpty(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String key = CACHE_PREFIX + appId + ":" + username;
|
||||||
|
Object value = redisTemplate.opsForHash().get(key, name);
|
||||||
|
return value != null ? String.valueOf(value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 追加提示词
|
* 追加提示词
|
||||||
*
|
*
|
||||||
@@ -147,7 +166,9 @@ public class AiragVariableServiceImpl implements IAiragVariableService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//工具描述
|
//工具描述
|
||||||
StringBuilder descriptionBuilder = new StringBuilder("更新应用变量的值。仅当检测到变量的新值与当前值不一致时调用。如果已调用过或值未变,请勿重复调用。");
|
//update-begin---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
|
StringBuilder descriptionBuilder = new StringBuilder("批量更新应用变量的值。请将本次对话中所有需要更新的变量一次性传入updates数组,无需多次调用。仅当变量新值与当前值确实不同时才调用本工具。");
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
if (variableList != null && !variableList.isEmpty()) {
|
if (variableList != null && !variableList.isEmpty()) {
|
||||||
descriptionBuilder.append("\n\n可用变量列表:");
|
descriptionBuilder.append("\n\n可用变量列表:");
|
||||||
for (AppVariableVo var : variableList) {
|
for (AppVariableVo var : variableList) {
|
||||||
@@ -159,17 +180,30 @@ public class AiragVariableServiceImpl implements IAiragVariableService {
|
|||||||
descriptionBuilder.append(": ").append(var.getDescription());
|
descriptionBuilder.append(": ").append(var.getDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
descriptionBuilder.append("\n\n注意:variableName必须是上述列表中的名称之一。");
|
//update-begin---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
|
descriptionBuilder.append("\n\n注意:variableName必须是上述列表中的名称之一,且本工具每轮对话只需调用一次。");
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A: 参数改为批量数组,一次可更新多个变量
|
||||||
|
JsonObjectSchema itemSchema = JsonObjectSchema.builder()
|
||||||
|
.addStringProperty("variableName", "变量名称(必须是可用变量列表中的名称之一)")
|
||||||
|
.addStringProperty("value", "变量新值")
|
||||||
|
.required("variableName", "value")
|
||||||
|
.build();
|
||||||
|
|
||||||
//构建更新变量的工具
|
//构建更新变量的工具
|
||||||
ToolSpecification spec = ToolSpecification.builder()
|
ToolSpecification spec = ToolSpecification.builder()
|
||||||
.name("update_variable")
|
.name("update_variable")
|
||||||
.description(descriptionBuilder.toString())
|
.description(descriptionBuilder.toString())
|
||||||
.parameters(JsonObjectSchema.builder()
|
.parameters(JsonObjectSchema.builder()
|
||||||
.addStringProperty("variableName", "变量名称")
|
//update-begin---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
.addStringProperty("value", "变量值")
|
.addProperty("updates", JsonArraySchema.builder()
|
||||||
.required("variableName", "value")
|
.description("需要更新的变量列表,可包含多个变量")
|
||||||
|
.items(itemSchema)
|
||||||
|
.build())
|
||||||
|
.required("updates")
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -177,15 +211,38 @@ public class AiragVariableServiceImpl implements IAiragVariableService {
|
|||||||
ToolExecutor executor = (toolExecutionRequest, memoryId) -> {
|
ToolExecutor executor = (toolExecutionRequest, memoryId) -> {
|
||||||
try {
|
try {
|
||||||
JSONObject args = JSONObject.parseObject(toolExecutionRequest.arguments());
|
JSONObject args = JSONObject.parseObject(toolExecutionRequest.arguments());
|
||||||
String name = args.getString("variableName");
|
JSONArray updates = args.getJSONArray("updates");
|
||||||
String value = args.getString("value");
|
|
||||||
IAiragVariableService variableService = SpringContextUtils.getBean(IAiragVariableService.class);
|
IAiragVariableService variableService = SpringContextUtils.getBean(IAiragVariableService.class);
|
||||||
//更新变量值
|
//update-begin---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
variableService.updateVariable(username, aiApp.getId(), name, value);
|
// B: 返回结构化JSON,LLM可明确感知"已全部完成"
|
||||||
return "变量 " + name + " 已更新为: " + value;
|
JSONObject updatedMap = new JSONObject();
|
||||||
|
if (updates != null) {
|
||||||
|
for (int i = 0; i < updates.size(); i++) {
|
||||||
|
JSONObject item = updates.getJSONObject(i);
|
||||||
|
String name = item.getString("variableName");
|
||||||
|
String value = item.getString("value");
|
||||||
|
if (oConvertUtils.isNotEmpty(name)) {
|
||||||
|
variableService.updateVariable(username, aiApp.getId(), name, value);
|
||||||
|
updatedMap.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("updated", updatedMap);
|
||||||
|
result.put("count", updatedMap.size());
|
||||||
|
result.put("message", "已成功更新 " + updatedMap.size() + " 个变量,无需再次调用");
|
||||||
|
return result.toJSONString();
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新变量失败", e);
|
log.error("更新变量失败", e);
|
||||||
return "更新变量失败: " + e.getMessage();
|
//update-begin---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
|
JSONObject error = new JSONObject();
|
||||||
|
error.put("success", false);
|
||||||
|
error.put("message", "更新变量失败: " + e.getMessage());
|
||||||
|
return error.toJSONString();
|
||||||
|
//update-end---author:wangshuai ---date:2026-04-21 for:【AI变量】支持批量更新变量,返回结构化结果避免LLM重复调用-----------
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package org.jeecg.modules.airag.app.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: AI绘画
|
||||||
|
*
|
||||||
|
* @author: wangshuai
|
||||||
|
* @date: 2026/2/4 18:57
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AiDrawGenerateVo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘画模型的id
|
||||||
|
*/
|
||||||
|
private String drawModelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片尺寸
|
||||||
|
*/
|
||||||
|
private String imageSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一张图片或者多张图片,多张图片用逗号分隔
|
||||||
|
*/
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户输入的聊天内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 风格
|
||||||
|
*/
|
||||||
|
private String style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视角
|
||||||
|
*/
|
||||||
|
private String visualAngle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 人物镜头
|
||||||
|
*/
|
||||||
|
private String characterShot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灯光
|
||||||
|
*/
|
||||||
|
private String lighting;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型 poster: 海报,draw:绘图,face 换脸,mix 混图
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
}
|
||||||
@@ -88,11 +88,52 @@ public class LLMConsts {
|
|||||||
*/
|
*/
|
||||||
public static final String KNOWLEDGE_DOC_METADATA_SOURCES_PATH = "sourcesPath";
|
public static final String KNOWLEDGE_DOC_METADATA_SOURCES_PATH = "sourcesPath";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 知识库:文档元数据:网页URL
|
||||||
|
*/
|
||||||
|
public static final String KNOWLEDGE_DOC_METADATA_WEBSITE = "website";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEEPSEEK推理模型
|
* DEEPSEEK推理模型
|
||||||
*/
|
*/
|
||||||
public static final String DEEPSEEK_REASONER = "deepseek-reasoner";
|
public static final String DEEPSEEK_REASONER = "deepseek-reasoner";
|
||||||
|
|
||||||
|
//update-begin---author:scott ---date:20260429 for:[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash,流程中调用出现异常------------
|
||||||
|
/**
|
||||||
|
* DEEPSEEK 推理模型(返回 reasoning_content 字段、在多轮工具调用中要求把 reasoning_content 回传)集合,
|
||||||
|
* 后续 DeepSeek 新增推理模型时在此追加;非推理模型(如 deepseek-chat)不要加入。
|
||||||
|
* 触发场景:仅当对话存在工具调用导致的多轮请求时才会出现 "reasoning_content must be passed back" 错误,
|
||||||
|
* 单轮 Q&A(如 AI 应用聊天无工具)不会触发,但开启 sendThinking 也无副作用。
|
||||||
|
*/
|
||||||
|
public static final Set<String> DEEPSEEK_THINKING_MODELS = new HashSet<>(Arrays.asList(
|
||||||
|
"deepseek-reasoner",
|
||||||
|
"deepseek-v4-flash",
|
||||||
|
"deepseek-v4-pro"
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断指定模型名是否为 DeepSeek 推理模型(返回 reasoning_content 字段)
|
||||||
|
* 匹配规则:先做大小写不敏感的精确匹配,再做关键字包含匹配(reasoner/v4-flash/v4-pro)
|
||||||
|
* 以兼容带版本后缀的变体(如 deepseek-v4-flash-0428)
|
||||||
|
*
|
||||||
|
* @param modelName 模型名(大小写不敏感、首尾空白容错)
|
||||||
|
* @return true=推理模型;false=非推理模型或空
|
||||||
|
*/
|
||||||
|
public static boolean isDeepSeekThinkingModel(String modelName) {
|
||||||
|
if (modelName == null || modelName.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String name = modelName.trim().toLowerCase();
|
||||||
|
if (DEEPSEEK_THINKING_MODELS.contains(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 兼容带版本后缀或厂商前缀的变体
|
||||||
|
return name.contains("reasoner")
|
||||||
|
|| name.contains("v4-flash")
|
||||||
|
|| name.contains("v4-pro");
|
||||||
|
}
|
||||||
|
//update-end---author:scott ---date:20260429 for:[issues/9585]DeepSeek大模型切换为新发布deepseek-v4-flash,流程中调用出现异常------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 知识库类型:知识库
|
* 知识库类型:知识库
|
||||||
*/
|
*/
|
||||||
@@ -118,4 +159,63 @@ public class LLMConsts {
|
|||||||
*/
|
*/
|
||||||
public static final int CHAT_FILE_MAX_COUNT = 3;
|
public static final int CHAT_FILE_MAX_COUNT = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 知识库是否开启默认分段策略
|
||||||
|
*/
|
||||||
|
public static final String ENABLE_SEGMENT = "enableSegment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档分段策略:使用知识库默认分段策略
|
||||||
|
*/
|
||||||
|
public static final String USE_KNOWLEDGE_DEFAULT = "useKnowledgeDefault";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段策略
|
||||||
|
*/
|
||||||
|
public static final String SEGMENT_STRATEGY = "segmentStrategy";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段策略:auto 自动分段与清洗
|
||||||
|
*/
|
||||||
|
public static final String SEGMENT_STRATEGY_AUTO = "auto";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段策略:custom 自定义
|
||||||
|
*/
|
||||||
|
public static final String SEGMENT_STRATEGY_CUSTOM = "custom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段长度
|
||||||
|
*/
|
||||||
|
public static final String MAX_SEGMENT = "maxSegment";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重叠率 0-90%
|
||||||
|
*/
|
||||||
|
public static final String OVERLAP = "overlap";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段标识符(\\n:换行,\\n\\n:2个换行,。:中文句号,!:中文叹号,?:中文问号,. :英文句号,! :英文叹号,? :英文问号,custom:自定义)
|
||||||
|
*/
|
||||||
|
public static final String SEPARATOR = "separator";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段标识符自定义
|
||||||
|
*/
|
||||||
|
public static final String CUSTOM_SEPARATOR = "customSeparator";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本预处理规则(cleanSpaces:替换掉连续的空格、换行符和制表符,removeUrlsEmails:删除所有 URL 和电子邮箱地址)
|
||||||
|
*/
|
||||||
|
public static final String TEXT_RULES = "textRules";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换掉连续的空格、换行符和制表符
|
||||||
|
*/
|
||||||
|
public static final String TEXT_RULES_CLEAN_SPACES = "cleanSpaces";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除所有URL和电子邮箱地址
|
||||||
|
*/
|
||||||
|
public static final String TEXT_RULES_REMOVE_URLS_EMAILS = "removeUrlsEmails";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.jeecg.modules.airag.llm.controller;
|
package org.jeecg.modules.airag.llm.controller;
|
||||||
|
|
||||||
import org.jeecg.common.airag.api.IAiragBaseApi;
|
import org.jeecg.common.airag.api.IAiragBaseApi;
|
||||||
import org.jeecg.modules.airag.llm.service.impl.AiragBaseApiImpl;
|
import org.jeecg.modules.airag.api.AiragBaseApiImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@@ -23,9 +23,39 @@ public class AiragBaseApiController implements IAiragBaseApi {
|
|||||||
public String knowledgeWriteTextDocument(
|
public String knowledgeWriteTextDocument(
|
||||||
@RequestParam("knowledgeId") String knowledgeId,
|
@RequestParam("knowledgeId") String knowledgeId,
|
||||||
@RequestParam("title") String title,
|
@RequestParam("title") String title,
|
||||||
@RequestParam("content") String content
|
@RequestParam("content") String content,
|
||||||
|
@RequestParam(value = "segmentConfig", required = false) String segmentConfig
|
||||||
) {
|
) {
|
||||||
return airagBaseApi.knowledgeWriteTextDocument(knowledgeId, title, content);
|
return airagBaseApi.knowledgeWriteTextDocument(knowledgeId, title, content, segmentConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/airag/api/getChatVariable")
|
||||||
|
public String getChatVariable(
|
||||||
|
@RequestParam("appId") String appId,
|
||||||
|
@RequestParam("username") String username,
|
||||||
|
@RequestParam("name") String name
|
||||||
|
) {
|
||||||
|
return airagBaseApi.getChatVariable(appId, username, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/airag/api/setChatVariable")
|
||||||
|
public void setChatVariable(
|
||||||
|
@RequestParam("appId") String appId,
|
||||||
|
@RequestParam("username") String username,
|
||||||
|
@RequestParam("name") String name,
|
||||||
|
@RequestParam("value") String value
|
||||||
|
) {
|
||||||
|
airagBaseApi.setChatVariable(appId, username, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/airag/api/getMemoryIdByAppId")
|
||||||
|
public String getMemoryIdByAppId(@RequestParam("appId") String appId) {
|
||||||
|
return airagBaseApi.getMemoryIdByAppId(appId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/airag/api/getPromptContent")
|
||||||
|
public String getPromptContent(@RequestParam("promptId") String promptId) {
|
||||||
|
return airagBaseApi.getPromptContent(promptId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.jeecg.common.api.vo.Result;
|
import org.jeecg.common.api.vo.Result;
|
||||||
import org.jeecg.common.system.base.controller.JeecgController;
|
import org.jeecg.common.system.base.controller.JeecgController;
|
||||||
import org.jeecg.common.system.query.QueryGenerator;
|
import org.jeecg.common.system.query.QueryGenerator;
|
||||||
@@ -42,6 +43,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-分页列表查询")
|
@Operation(summary = "MCP-分页列表查询")
|
||||||
|
@RequiresPermissions("airag:mcp:list")
|
||||||
@GetMapping(value = "/list")
|
@GetMapping(value = "/list")
|
||||||
public Result<IPage<AiragMcp>> queryPageList(AiragMcp airagMcp,
|
public Result<IPage<AiragMcp>> queryPageList(AiragMcp airagMcp,
|
||||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||||
@@ -61,6 +63,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-保存")
|
@Operation(summary = "MCP-保存")
|
||||||
|
@RequiresPermissions("airag:mcp:save")
|
||||||
@PostMapping(value = "/save")
|
@PostMapping(value = "/save")
|
||||||
public Result<String> save(@RequestBody AiragMcp airagMcp) {
|
public Result<String> save(@RequestBody AiragMcp airagMcp) {
|
||||||
return airagMcpService.edit(airagMcp);
|
return airagMcpService.edit(airagMcp);
|
||||||
@@ -77,6 +80,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @date 2025/10/21 10:54
|
* @date 2025/10/21 10:54
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-保存并同步")
|
@Operation(summary = "MCP-保存并同步")
|
||||||
|
@RequiresPermissions("airag:mcp:save")
|
||||||
@PostMapping(value = "/saveAndSync")
|
@PostMapping(value = "/saveAndSync")
|
||||||
public Result<?> saveAndSync(@RequestBody AiragMcp airagMcp) {
|
public Result<?> saveAndSync(@RequestBody AiragMcp airagMcp) {
|
||||||
Result<String> saveResult = airagMcpService.edit(airagMcp);
|
Result<String> saveResult = airagMcpService.edit(airagMcp);
|
||||||
@@ -99,6 +103,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @date 2025/10/20 20:09
|
* @date 2025/10/20 20:09
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-同步MCP信息")
|
@Operation(summary = "MCP-同步MCP信息")
|
||||||
|
@RequiresPermissions("airag:mcp:save")
|
||||||
@PostMapping(value = "/sync/{id}")
|
@PostMapping(value = "/sync/{id}")
|
||||||
public Result<?> sync(@PathVariable(name = "id", required = true) String id) {
|
public Result<?> sync(@PathVariable(name = "id", required = true) String id) {
|
||||||
return airagMcpService.sync(id);
|
return airagMcpService.sync(id);
|
||||||
@@ -114,6 +119,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @date 2025/10/20 20:13
|
* @date 2025/10/20 20:13
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-启用/禁用MCP信息")
|
@Operation(summary = "MCP-启用/禁用MCP信息")
|
||||||
|
@RequiresPermissions("airag:mcp:save")
|
||||||
@PostMapping(value = "/status/{id}/{action}")
|
@PostMapping(value = "/status/{id}/{action}")
|
||||||
public Result<?> toggleStatus(@PathVariable(name = "id",required = true) String id,
|
public Result<?> toggleStatus(@PathVariable(name = "id",required = true) String id,
|
||||||
@PathVariable(name = "action", required = true) String action) {
|
@PathVariable(name = "action", required = true) String action) {
|
||||||
@@ -129,6 +135,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @date 2025/10/30
|
* @date 2025/10/30
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-保存插件工具")
|
@Operation(summary = "MCP-保存插件工具")
|
||||||
|
@RequiresPermissions("airag:mcp:save")
|
||||||
@PostMapping(value = "/saveTools")
|
@PostMapping(value = "/saveTools")
|
||||||
public Result<String> saveTools(@RequestBody SaveToolsDTO dto) {
|
public Result<String> saveTools(@RequestBody SaveToolsDTO dto) {
|
||||||
return airagMcpService.saveTools(dto.getId(), dto.getTools());
|
return airagMcpService.saveTools(dto.getId(), dto.getTools());
|
||||||
@@ -141,6 +148,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-通过id删除")
|
@Operation(summary = "MCP-通过id删除")
|
||||||
|
@RequiresPermissions("airag:mcp:delete")
|
||||||
@DeleteMapping(value = "/delete")
|
@DeleteMapping(value = "/delete")
|
||||||
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
|
||||||
airagMcpService.removeById(id);
|
airagMcpService.removeById(id);
|
||||||
@@ -154,6 +162,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Operation(summary = "MCP-通过id查询")
|
@Operation(summary = "MCP-通过id查询")
|
||||||
|
//@RequiresPermissions("airag:mcp:queryById")
|
||||||
@GetMapping(value = "/queryById")
|
@GetMapping(value = "/queryById")
|
||||||
public Result<AiragMcp> queryById(@RequestParam(name = "id", required = true) String id) {
|
public Result<AiragMcp> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||||
AiragMcp airagMcp = airagMcpService.getById(id);
|
AiragMcp airagMcp = airagMcpService.getById(id);
|
||||||
@@ -169,7 +178,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @param request
|
* @param request
|
||||||
* @param airagMcp
|
* @param airagMcp
|
||||||
*/
|
*/
|
||||||
// @RequiresPermissions("llm:airag_mcp:exportXls")
|
@RequiresPermissions("airag:mcp:export")
|
||||||
@RequestMapping(value = "/exportXls")
|
@RequestMapping(value = "/exportXls")
|
||||||
public ModelAndView exportXls(HttpServletRequest request, AiragMcp airagMcp) {
|
public ModelAndView exportXls(HttpServletRequest request, AiragMcp airagMcp) {
|
||||||
return super.exportXls(request, airagMcp, AiragMcp.class, "MCP");
|
return super.exportXls(request, airagMcp, AiragMcp.class, "MCP");
|
||||||
@@ -182,7 +191,7 @@ public class AiragMcpController extends JeecgController<AiragMcp, IAiragMcpServi
|
|||||||
* @param response
|
* @param response
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
// @RequiresPermissions("llm:airag_mcp:importExcel")
|
@RequiresPermissions("airag:mcp:import")
|
||||||
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
|
||||||
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
|
||||||
return super.importExcel(request, response, AiragMcp.class);
|
return super.importExcel(request, response, AiragMcp.class);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.jeecg.common.util.AssertUtils;
|
|||||||
import org.jeecg.common.util.TokenUtils;
|
import org.jeecg.common.util.TokenUtils;
|
||||||
import org.jeecg.common.util.oConvertUtils;
|
import org.jeecg.common.util.oConvertUtils;
|
||||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||||
|
import org.jeecg.modules.airag.app.enums.ImageEditEnum;
|
||||||
import org.jeecg.modules.airag.common.handler.AIChatParams;
|
import org.jeecg.modules.airag.common.handler.AIChatParams;
|
||||||
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
import org.jeecg.modules.airag.llm.consts.LLMConsts;
|
||||||
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
import org.jeecg.modules.airag.llm.entity.AiragModel;
|
||||||
@@ -29,7 +30,9 @@ import org.springframework.web.servlet.ModelAndView;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import java.util.Arrays;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,8 +85,6 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
|||||||
// 默认未激活
|
// 默认未激活
|
||||||
if(oConvertUtils.isObjectEmpty(airagModel.getActivateFlag())){
|
if(oConvertUtils.isObjectEmpty(airagModel.getActivateFlag())){
|
||||||
airagModel.setActivateFlag(0);
|
airagModel.setActivateFlag(0);
|
||||||
} else {
|
|
||||||
airagModel.setActivateFlag(1);
|
|
||||||
}
|
}
|
||||||
airagModelService.save(airagModel);
|
airagModelService.save(airagModel);
|
||||||
return Result.OK("添加成功!");
|
return Result.OK("添加成功!");
|
||||||
@@ -170,6 +171,8 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
|||||||
AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
|
AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
|
||||||
AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
|
AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
|
||||||
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
|
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
|
||||||
|
//测试连接默认为已激活状态
|
||||||
|
airagModel.setActivateFlag(1);
|
||||||
try {
|
try {
|
||||||
if(LLMConsts.MODEL_TYPE_LLM.equals(airagModel.getModelType())){
|
if(LLMConsts.MODEL_TYPE_LLM.equals(airagModel.getModelType())){
|
||||||
aiChatHandler.completions(airagModel, Collections.singletonList(UserMessage.from("To test whether it can be successfully called, simply return success")), null);
|
aiChatHandler.completions(airagModel, Collections.singletonList(UserMessage.from("To test whether it can be successfully called, simply return success")), null);
|
||||||
@@ -180,7 +183,16 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
|
|||||||
//update-begin---author:wangshuai---date:2026-01-07---for:【QQYUN-12145】【AI】AI 绘画创作---=
|
//update-begin---author:wangshuai---date:2026-01-07---for:【QQYUN-12145】【AI】AI 绘画创作---=
|
||||||
}else if(LLMConsts.MODEL_TYPE_IMAGE.equals(airagModel.getModelType())){
|
}else if(LLMConsts.MODEL_TYPE_IMAGE.equals(airagModel.getModelType())){
|
||||||
AIChatParams aiChatParams = new AIChatParams();
|
AIChatParams aiChatParams = new AIChatParams();
|
||||||
aiChatHandler.imageGenerate(airagModel, "To test whether it can be successfully called, simply return success", aiChatParams);
|
//update-begin---author:wangshuai---date:2026-03-02---for:兼容图生图模型测试---
|
||||||
|
String modelName = airagModel.getModelName();
|
||||||
|
if(ImageEditEnum.isImageEditModel(modelName)){
|
||||||
|
List<String> images = new ArrayList<>();
|
||||||
|
images.add("https://jeecgdev.oss-cn-beijing.aliyuncs.com/upload/test/jeecg_1772268161540.jpg");
|
||||||
|
aiChatHandler.imageEdit(airagModel, "Generate a picture of a cartoon cat", images,aiChatParams);
|
||||||
|
}else{
|
||||||
|
aiChatHandler.imageGenerate(airagModel, "Generate a picture of a cartoon cat", aiChatParams);
|
||||||
|
}
|
||||||
|
//update-end---author:wangshuai---date:2026-03-02---for:兼容图生图模型测试---
|
||||||
}
|
}
|
||||||
//update-end---author:wangshuai---date:2026-01-07---for:【QQYUN-12145】【AI】AI 绘画创作---
|
//update-end---author:wangshuai---date:2026-01-07---for:【QQYUN-12145】【AI】AI 绘画创作---
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
|
|||||||
@@ -71,26 +71,25 @@ public class TikaDocumentParser {
|
|||||||
|
|
||||||
public Document parse(File file) {
|
public Document parse(File file) {
|
||||||
AssertUtils.assertNotEmpty("请选择文件", file);
|
AssertUtils.assertNotEmpty("请选择文件", file);
|
||||||
try {
|
// 使用 Tika 自动检测 MIME 类型
|
||||||
|
String fileName = file.getName().toLowerCase();
|
||||||
|
//后缀
|
||||||
|
String ext = FilenameUtils.getExtension(fileName);
|
||||||
|
if (fileName.endsWith(".txt")
|
||||||
|
|| fileName.endsWith(".md")
|
||||||
|
|| fileName.endsWith(".pdf")) {
|
||||||
// 用于解析(使用FileInputStream避免file.toPath()在Linux非UTF-8环境下中文文件名报错)
|
// 用于解析(使用FileInputStream避免file.toPath()在Linux非UTF-8环境下中文文件名报错)
|
||||||
InputStream isForParsing = new FileInputStream(file);
|
try (InputStream isForParsing = new FileInputStream(file)) {
|
||||||
// 使用 Tika 自动检测 MIME 类型
|
|
||||||
String fileName = file.getName().toLowerCase();
|
|
||||||
//后缀
|
|
||||||
String ext = FilenameUtils.getExtension(fileName);
|
|
||||||
if (fileName.endsWith(".txt")
|
|
||||||
|| fileName.endsWith(".md")
|
|
||||||
|| fileName.endsWith(".pdf")) {
|
|
||||||
return extractByTika(isForParsing);
|
return extractByTika(isForParsing);
|
||||||
//update-begin---author:wangshuai---date:2026-01-09---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
} catch (IOException e) {
|
||||||
} else if (FILE_SUFFIX.contains(ext.toLowerCase())) {
|
throw new RuntimeException(e);
|
||||||
return parseDocExcelPdfUsingApachePoi(file);
|
|
||||||
//update-end---author:wangshuai---date:2026-01-09---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("不支持的文件格式: " + FilenameUtils.getExtension(fileName));
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
//update-begin---author:wangshuai---date:2026-01-09---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
||||||
throw new RuntimeException(e);
|
} else if (FILE_SUFFIX.contains(ext.toLowerCase())) {
|
||||||
|
return parseDocExcelPdfUsingApachePoi(file);
|
||||||
|
//update-end---author:wangshuai---date:2026-01-09---for:【QQYUN-14261】【AI】AI助手,支持多模态能力- 文档---
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("不支持的文件格式: " + FilenameUtils.getExtension(fileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,254 @@
|
|||||||
|
package org.jeecg.modules.airag.llm.document;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.nodes.Element;
|
||||||
|
import org.jsoup.nodes.Node;
|
||||||
|
import org.jsoup.nodes.TextNode;
|
||||||
|
import org.jsoup.select.Elements;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网页解析器,使用Jsoup爬取网页并转换为Markdown格式
|
||||||
|
*
|
||||||
|
* @author sjlei
|
||||||
|
* @date 2026/3/19
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class WebPageParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求超时时间(毫秒)
|
||||||
|
*/
|
||||||
|
private static final int TIMEOUT_MS = 15000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大body大小(5MB)
|
||||||
|
*/
|
||||||
|
private static final int MAX_BODY_SIZE = 5 * 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-Agent
|
||||||
|
*/
|
||||||
|
private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 爬取网页并转换为Markdown
|
||||||
|
*
|
||||||
|
* @param url 网页URL
|
||||||
|
* @return Markdown格式的文本内容
|
||||||
|
* @throws IOException 网络请求失败时抛出
|
||||||
|
*/
|
||||||
|
public String parseToMarkdown(String url) throws IOException {
|
||||||
|
Document doc = Jsoup.connect(url)
|
||||||
|
.userAgent(USER_AGENT)
|
||||||
|
.timeout(TIMEOUT_MS)
|
||||||
|
.maxBodySize(MAX_BODY_SIZE)
|
||||||
|
.followRedirects(true)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
// 移除脚本、样式、导航、页脚等无关元素
|
||||||
|
doc.select("script, style, nav, footer, header, iframe, noscript, svg, form, button, input, select, textarea, .sidebar, .nav, .menu, .footer, .header, .ad, .advertisement, .comment, .comments").remove();
|
||||||
|
|
||||||
|
// 优先提取正文区域
|
||||||
|
Element body = extractMainContent(doc);
|
||||||
|
|
||||||
|
StringBuilder markdown = new StringBuilder();
|
||||||
|
|
||||||
|
// 提取页面标题
|
||||||
|
String title = doc.title();
|
||||||
|
if (title != null && !title.trim().isEmpty()) {
|
||||||
|
markdown.append("# ").append(title.trim()).append("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将HTML转为Markdown
|
||||||
|
convertToMarkdown(body, markdown);
|
||||||
|
|
||||||
|
return cleanMarkdown(markdown.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取正文区域,优先使用article/main标签,否则使用body
|
||||||
|
*/
|
||||||
|
private Element extractMainContent(Document doc) {
|
||||||
|
// 按优先级尝试获取正文容器
|
||||||
|
String[] selectors = {"article", "main", "[role=main]", ".content", ".post-content", ".article-content", ".entry-content", "#content"};
|
||||||
|
for (String selector : selectors) {
|
||||||
|
Elements elements = doc.select(selector);
|
||||||
|
if (!elements.isEmpty() && elements.first().text().length() > 100) {
|
||||||
|
return elements.first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doc.body() != null ? doc.body() : doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归将HTML元素转换为Markdown
|
||||||
|
*/
|
||||||
|
private void convertToMarkdown(Element element, StringBuilder sb) {
|
||||||
|
for (Node child : element.childNodes()) {
|
||||||
|
if (child instanceof TextNode) {
|
||||||
|
String text = ((TextNode) child).text().trim();
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
sb.append(text);
|
||||||
|
}
|
||||||
|
} else if (child instanceof Element) {
|
||||||
|
Element el = (Element) child;
|
||||||
|
String tagName = el.tagName().toLowerCase();
|
||||||
|
|
||||||
|
switch (tagName) {
|
||||||
|
case "h1":
|
||||||
|
sb.append("\n\n# ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "h2":
|
||||||
|
sb.append("\n\n## ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "h3":
|
||||||
|
sb.append("\n\n### ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "h4":
|
||||||
|
sb.append("\n\n#### ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "h5":
|
||||||
|
sb.append("\n\n##### ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "h6":
|
||||||
|
sb.append("\n\n###### ").append(el.text().trim()).append("\n\n");
|
||||||
|
break;
|
||||||
|
case "p":
|
||||||
|
sb.append("\n\n");
|
||||||
|
convertToMarkdown(el, sb);
|
||||||
|
sb.append("\n\n");
|
||||||
|
break;
|
||||||
|
case "br":
|
||||||
|
sb.append("\n");
|
||||||
|
break;
|
||||||
|
case "strong":
|
||||||
|
case "b":
|
||||||
|
sb.append("**").append(el.text().trim()).append("**");
|
||||||
|
break;
|
||||||
|
case "em":
|
||||||
|
case "i":
|
||||||
|
sb.append("*").append(el.text().trim()).append("*");
|
||||||
|
break;
|
||||||
|
case "code":
|
||||||
|
sb.append("`").append(el.text()).append("`");
|
||||||
|
break;
|
||||||
|
case "pre":
|
||||||
|
sb.append("\n\n```\n").append(el.text()).append("\n```\n\n");
|
||||||
|
break;
|
||||||
|
case "a":
|
||||||
|
String href = el.attr("abs:href");
|
||||||
|
String linkText = el.text().trim();
|
||||||
|
if (!linkText.isEmpty() && !href.isEmpty()) {
|
||||||
|
sb.append("[").append(linkText).append("](").append(href).append(")");
|
||||||
|
} else if (!linkText.isEmpty()) {
|
||||||
|
sb.append(linkText);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "img":
|
||||||
|
String src = el.attr("abs:src");
|
||||||
|
String alt = el.attr("alt");
|
||||||
|
// 只保留http(s)开头的真实图片URL,过滤掉base64内联图片
|
||||||
|
if (!src.isEmpty() && src.startsWith("http")) {
|
||||||
|
sb.append(".append(src).append(")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ul":
|
||||||
|
sb.append("\n");
|
||||||
|
for (Element li : el.children()) {
|
||||||
|
if ("li".equals(li.tagName())) {
|
||||||
|
sb.append("- ").append(li.text().trim()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
break;
|
||||||
|
case "ol":
|
||||||
|
sb.append("\n");
|
||||||
|
int idx = 1;
|
||||||
|
for (Element li : el.children()) {
|
||||||
|
if ("li".equals(li.tagName())) {
|
||||||
|
sb.append(idx++).append(". ").append(li.text().trim()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
break;
|
||||||
|
case "blockquote":
|
||||||
|
String[] lines = el.text().trim().split("\n");
|
||||||
|
sb.append("\n");
|
||||||
|
for (String line : lines) {
|
||||||
|
sb.append("> ").append(line).append("\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
break;
|
||||||
|
case "table":
|
||||||
|
convertTableToMarkdown(el, sb);
|
||||||
|
break;
|
||||||
|
case "hr":
|
||||||
|
sb.append("\n\n---\n\n");
|
||||||
|
break;
|
||||||
|
case "div":
|
||||||
|
case "section":
|
||||||
|
case "span":
|
||||||
|
case "figure":
|
||||||
|
case "figcaption":
|
||||||
|
convertToMarkdown(el, sb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
convertToMarkdown(el, sb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将HTML表格转为Markdown表格
|
||||||
|
*/
|
||||||
|
private void convertTableToMarkdown(Element table, StringBuilder sb) {
|
||||||
|
Elements rows = table.select("tr");
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("\n\n");
|
||||||
|
boolean headerDone = false;
|
||||||
|
|
||||||
|
for (Element row : rows) {
|
||||||
|
Elements cells = row.select("th, td");
|
||||||
|
if (cells.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("|");
|
||||||
|
for (Element cell : cells) {
|
||||||
|
sb.append(" ").append(cell.text().trim()).append(" |");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
|
||||||
|
// 在第一行后添加分隔线
|
||||||
|
if (!headerDone) {
|
||||||
|
sb.append("|");
|
||||||
|
for (int i = 0; i < cells.size(); i++) {
|
||||||
|
sb.append(" --- |");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
headerDone = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理Markdown文本:去除多余空行、首尾空白
|
||||||
|
*/
|
||||||
|
private String cleanMarkdown(String markdown) {
|
||||||
|
// 去除连续3个以上换行为2个换行
|
||||||
|
markdown = markdown.replaceAll("\n{3,}", "\n\n");
|
||||||
|
// 去除行首尾空白(保留换行)
|
||||||
|
markdown = markdown.replaceAll("(?m)^[ \t]+|[ \t]+$", "");
|
||||||
|
return markdown.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user