zhangyeguang
4 weeks ago
commit
8721d53c77
68 changed files with 8715 additions and 0 deletions
@ -0,0 +1,38 @@ |
|||||
|
target/ |
||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||
|
!**/src/main/**/target/ |
||||
|
!**/src/test/**/target/ |
||||
|
|
||||
|
### IntelliJ IDEA ### |
||||
|
.idea/modules.xml |
||||
|
.idea/jarRepositories.xml |
||||
|
.idea/compiler.xml |
||||
|
.idea/libraries/ |
||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
|
||||
|
### Eclipse ### |
||||
|
.apt_generated |
||||
|
.classpath |
||||
|
.factorypath |
||||
|
.project |
||||
|
.settings |
||||
|
.springBeans |
||||
|
.sts4-cache |
||||
|
|
||||
|
### NetBeans ### |
||||
|
/nbproject/private/ |
||||
|
/nbbuild/ |
||||
|
/dist/ |
||||
|
/nbdist/ |
||||
|
/.nb-gradle/ |
||||
|
build/ |
||||
|
!**/src/main/**/build/ |
||||
|
!**/src/test/**/build/ |
||||
|
|
||||
|
### VS Code ### |
||||
|
.vscode/ |
||||
|
|
||||
|
### Mac OS ### |
||||
|
.DS_Store |
@ -0,0 +1,8 @@ |
|||||
|
# Default ignored files |
||||
|
/shelf/ |
||||
|
/workspace.xml |
||||
|
# Editor-based HTTP Client requests |
||||
|
/httpRequests/ |
||||
|
# Datasource local storage ignored files |
||||
|
/dataSources/ |
||||
|
/dataSources.local.xml |
@ -0,0 +1,7 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="Encoding"> |
||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> |
||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,14 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" /> |
||||
|
<component name="MavenProjectsManager"> |
||||
|
<option name="originalFiles"> |
||||
|
<list> |
||||
|
<option value="$PROJECT_DIR$/pom.xml" /> |
||||
|
</list> |
||||
|
</option> |
||||
|
</component> |
||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> |
||||
|
<output url="file://$PROJECT_DIR$/out" /> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,124 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="Palette2"> |
||||
|
<group name="Swing"> |
||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> |
||||
|
</item> |
||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> |
||||
|
<initial-values> |
||||
|
<property name="text" value="Button" /> |
||||
|
</initial-values> |
||||
|
</item> |
||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> |
||||
|
<initial-values> |
||||
|
<property name="text" value="RadioButton" /> |
||||
|
</initial-values> |
||||
|
</item> |
||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> |
||||
|
<initial-values> |
||||
|
<property name="text" value="CheckBox" /> |
||||
|
</initial-values> |
||||
|
</item> |
||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> |
||||
|
<initial-values> |
||||
|
<property name="text" value="Label" /> |
||||
|
</initial-values> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
||||
|
<preferred-size width="150" height="-1" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
||||
|
<preferred-size width="150" height="-1" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> |
||||
|
<preferred-size width="150" height="-1" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> |
||||
|
<preferred-size width="150" height="50" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> |
||||
|
<preferred-size width="200" height="200" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> |
||||
|
<preferred-size width="200" height="200" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> |
||||
|
<preferred-size width="-1" height="20" /> |
||||
|
</default-constraints> |
||||
|
</item> |
||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> |
||||
|
</item> |
||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false"> |
||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> |
||||
|
</item> |
||||
|
</group> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,4 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="VcsDirectoryMappings" defaultProject="true" /> |
||||
|
</project> |
File diff suppressed because it is too large
@ -0,0 +1,110 @@ |
|||||
|
INFO 2024-11-25 18:06:48.905 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Start loading |
||||
|
INFO 2024-11-25 18:06:48.937 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Plugin starting |
||||
|
INFO 2024-11-25 18:06:49.008 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:06:49.009 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @type_json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:06:49.012 [-main][*][o.noear.solon.Solon]: |
||||
|
Session: Local session state plugin is loaded |
||||
|
INFO 2024-11-25 18:06:49.016 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Bean scanning |
||||
|
INFO 2024-11-25 18:06:49.125 [-main][*][o.noear.solon.Solon]: |
||||
|
solon.connector:main: smarthttp: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:9198} |
||||
|
INFO 2024-11-25 18:06:49.126 [-main][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Started (smart http 2.0/3.0.3) @66ms |
||||
|
INFO 2024-11-25 18:06:49.128 [-main][*][o.noear.solon.Solon]: |
||||
|
App: End loading elapsed=543ms pid=58141 v=3.0.3 |
||||
|
INFO 2024-11-25 18:06:53.368 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Has Stopped (smart http 2.0/3.0.3) |
||||
|
INFO 2024-11-25 18:06:53.368 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
App: Stopped |
||||
|
INFO 2024-11-25 18:07:06.300 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Start loading |
||||
|
INFO 2024-11-25 18:07:06.334 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Plugin starting |
||||
|
INFO 2024-11-25 18:07:06.400 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:07:06.401 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @type_json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:07:06.403 [-main][*][o.noear.solon.Solon]: |
||||
|
Session: Local session state plugin is loaded |
||||
|
INFO 2024-11-25 18:07:06.407 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Bean scanning |
||||
|
INFO 2024-11-25 18:07:06.499 [-main][*][o.noear.solon.Solon]: |
||||
|
solon.connector:main: smarthttp: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:9198} |
||||
|
INFO 2024-11-25 18:07:06.499 [-main][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Started (smart http 2.0/3.0.3) @55ms |
||||
|
INFO 2024-11-25 18:07:06.501 [-main][*][o.noear.solon.Solon]: |
||||
|
App: End loading elapsed=521ms pid=58150 v=3.0.3 |
||||
|
INFO 2024-11-25 18:09:44.098 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Has Stopped (smart http 2.0/3.0.3) |
||||
|
INFO 2024-11-25 18:09:44.100 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
App: Stopped |
||||
|
INFO 2024-11-25 18:09:47.177 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Start loading |
||||
|
INFO 2024-11-25 18:09:47.218 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Plugin starting |
||||
|
INFO 2024-11-25 18:09:47.287 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:09:47.288 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @type_json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:09:47.291 [-main][*][o.noear.solon.Solon]: |
||||
|
Session: Local session state plugin is loaded |
||||
|
INFO 2024-11-25 18:09:47.295 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Bean scanning |
||||
|
INFO 2024-11-25 18:09:47.404 [-main][*][o.noear.solon.Solon]: |
||||
|
solon.connector:main: smarthttp: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:9198} |
||||
|
INFO 2024-11-25 18:09:47.404 [-main][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Started (smart http 2.0/3.0.3) @59ms |
||||
|
INFO 2024-11-25 18:09:47.406 [-main][*][o.noear.solon.Solon]: |
||||
|
App: End loading elapsed=560ms pid=58210 v=3.0.3 |
||||
|
INFO 2024-11-25 18:11:27.711 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Has Stopped (smart http 2.0/3.0.3) |
||||
|
INFO 2024-11-25 18:11:27.712 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
App: Stopped |
||||
|
INFO 2024-11-25 18:12:31.162 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Start loading |
||||
|
INFO 2024-11-25 18:12:31.193 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Plugin starting |
||||
|
INFO 2024-11-25 18:12:31.295 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:12:31.296 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @type_json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:12:31.297 [-main][*][o.noear.solon.Solon]: |
||||
|
Session: Local session state plugin is loaded |
||||
|
INFO 2024-11-25 18:12:31.301 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Bean scanning |
||||
|
INFO 2024-11-25 18:12:31.408 [-main][*][o.noear.solon.Solon]: |
||||
|
solon.connector:main: smarthttp: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:9198} |
||||
|
INFO 2024-11-25 18:12:31.410 [-main][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Started (smart http 2.0/3.0.3) @69ms |
||||
|
INFO 2024-11-25 18:12:31.417 [-main][*][o.noear.solon.Solon]: |
||||
|
App: End loading elapsed=622ms pid=58274 v=3.0.3 |
||||
|
INFO 2024-11-25 18:13:51.719 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Has Stopped (smart http 2.0/3.0.3) |
||||
|
INFO 2024-11-25 18:13:51.721 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
App: Stopped |
||||
|
INFO 2024-11-25 18:13:53.668 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Start loading |
||||
|
INFO 2024-11-25 18:13:53.700 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Plugin starting |
||||
|
INFO 2024-11-25 18:13:53.793 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:13:53.794 [-main][*][o.noear.solon.Solon]: |
||||
|
Render mapping: @type_json=StringSerializerRender#snack3-json |
||||
|
INFO 2024-11-25 18:13:53.795 [-main][*][o.noear.solon.Solon]: |
||||
|
Session: Local session state plugin is loaded |
||||
|
INFO 2024-11-25 18:13:53.800 [-main][*][o.noear.solon.Solon]: |
||||
|
App: Bean scanning |
||||
|
INFO 2024-11-25 18:13:53.894 [-main][*][o.noear.solon.Solon]: |
||||
|
solon.connector:main: smarthttp: Started ServerConnector@{HTTP/1.1,[http/1.1]}{http://localhost:9198} |
||||
|
INFO 2024-11-25 18:13:53.894 [-main][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Started (smart http 2.0/3.0.3) @59ms |
||||
|
INFO 2024-11-25 18:13:53.896 [-main][*][o.noear.solon.Solon]: |
||||
|
App: End loading elapsed=555ms pid=58312 v=3.0.3 |
||||
|
INFO 2024-11-25 18:14:20.671 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
Server:main: smarthttp: Has Stopped (smart http 2.0/3.0.3) |
||||
|
INFO 2024-11-25 18:14:20.672 [-Thread-1][*][o.noear.solon.Solon]: |
||||
|
App: Stopped |
@ -0,0 +1,197 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
<groupId>com.yeguang</groupId> |
||||
|
<artifactId>siliqua-recognition-solon</artifactId> |
||||
|
<version>1.0-SNAPSHOT</version> |
||||
|
<parent> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-parent</artifactId> |
||||
|
<version>3.0.3</version> |
||||
|
</parent> |
||||
|
<properties> |
||||
|
<maven.compiler.source>8</maven.compiler.source> |
||||
|
<maven.compiler.target>8</maven.compiler.target> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
<mybatis-plus.version>3.5.9</mybatis-plus.version> |
||||
|
<mybatis-flex.version>1.9.8</mybatis-flex.version> |
||||
|
<mapstruct.version>1.6.1</mapstruct.version> |
||||
|
<hutool.version>5.8.22</hutool.version> |
||||
|
|
||||
|
</properties> |
||||
|
|
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-web</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon.web.cors</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-logging-logback</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-net-httputils</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-openapi2-knife4j</artifactId> |
||||
|
</dependency> |
||||
|
<!-- 数据库相关依赖--> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-data-sqlutils</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.zaxxer</groupId> |
||||
|
<artifactId>HikariCP</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>mybatis-solon-plugin</artifactId> |
||||
|
</dependency> |
||||
|
<!-- <dependency>--> |
||||
|
<!-- <groupId>com.mybatis-flex</groupId>--> |
||||
|
<!-- <artifactId>mybatis-flex-solon-plugin</artifactId>--> |
||||
|
<!-- <version>${mybatis-flex.version}</version>--> |
||||
|
<!-- <exclusions>--> |
||||
|
<!-- <exclusion>--> |
||||
|
<!-- <groupId>org.noear</groupId>--> |
||||
|
<!-- <artifactId>mybatis-solon-plugin</artifactId>--> |
||||
|
<!-- </exclusion>--> |
||||
|
<!-- </exclusions>--> |
||||
|
<!-- </dependency>--> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>com.baomidou</groupId> |
||||
|
<artifactId>mybatis-plus-solon-plugin</artifactId> |
||||
|
<version>${mybatis-plus.version}</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.baomidou</groupId> |
||||
|
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId> |
||||
|
<version>${mybatis-plus.version}</version> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>mysql</groupId> |
||||
|
<artifactId>mysql-connector-java</artifactId> |
||||
|
<version>8.0.33</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
<version>1.18.34</version> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-cache-redisson</artifactId> |
||||
|
<!-- 或者用 (如果不需要 CacheService,直接用它) |
||||
|
<artifactId>redisson-solon-plugin</artifactId> |
||||
|
--> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>sa-token-solon-plugin</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>sa-token-dao-redisson-jackson</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>cn.dev33</groupId> |
||||
|
<artifactId>sa-token-jwt</artifactId> |
||||
|
<version>1.39.0</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-cloud</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-config-plus</artifactId> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba.fastjson2</groupId> |
||||
|
<artifactId>fastjson2</artifactId> |
||||
|
<version>2.0.43</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>commons-io</groupId> |
||||
|
<artifactId>commons-io</artifactId> |
||||
|
<version>2.16.1</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.commons</groupId> |
||||
|
<artifactId>commons-email</artifactId> |
||||
|
<version>1.5</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.mapstruct</groupId> |
||||
|
<artifactId>mapstruct</artifactId> |
||||
|
<version>${mapstruct.version}</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.commons</groupId> |
||||
|
<artifactId>commons-collections4</artifactId> |
||||
|
<version>4.4</version> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>commons-io</groupId> |
||||
|
<artifactId>commons-io</artifactId> |
||||
|
<version>2.17.0</version> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>easyexcel</artifactId> |
||||
|
<version>4.0.2</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.huaweicloud</groupId> |
||||
|
<artifactId>esdk-obs-java</artifactId> |
||||
|
<version>3.21.11</version> |
||||
|
<exclusions> |
||||
|
<exclusion> |
||||
|
<groupId>com.fasterxml.jackson.core</groupId> |
||||
|
<artifactId>jackson-annotations</artifactId> |
||||
|
</exclusion> |
||||
|
<exclusion> |
||||
|
<groupId>com.fasterxml.jackson.core</groupId> |
||||
|
<artifactId>jackson-databind</artifactId> |
||||
|
</exclusion> |
||||
|
<exclusion> |
||||
|
<groupId>com.fasterxml.jackson.core</groupId> |
||||
|
<artifactId>jackson-core</artifactId> |
||||
|
</exclusion> |
||||
|
</exclusions> |
||||
|
</dependency> |
||||
|
</dependencies> |
||||
|
<build> |
||||
|
<finalName>${project.artifactId}</finalName> |
||||
|
|
||||
|
<plugins> |
||||
|
<plugin> |
||||
|
<groupId>org.noear</groupId> |
||||
|
<artifactId>solon-maven-plugin</artifactId> |
||||
|
</plugin> |
||||
|
|
||||
|
</plugins> |
||||
|
</build> |
||||
|
</project> |
@ -0,0 +1,12 @@ |
|||||
|
package com.yeguang; |
||||
|
|
||||
|
|
||||
|
import org.noear.solon.Solon; |
||||
|
import org.noear.solon.annotation.SolonMain; |
||||
|
|
||||
|
@SolonMain |
||||
|
public class AppMain { |
||||
|
public static void main(String[] args) { |
||||
|
Solon.start(AppMain.class, args); |
||||
|
} |
||||
|
} |
@ -0,0 +1,64 @@ |
|||||
|
package com.yeguang.common; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
|
||||
|
@Getter |
||||
|
public enum BizCode { |
||||
|
General_Success(200, "接口调用成功"), |
||||
|
ServerError(10001, "服务器异常"), |
||||
|
General_Failure(10004, "接口调用失败"), |
||||
|
General_DBError(10005, "DB错误"), |
||||
|
General_ParameterInvalid(12001, "参数校验失败"), |
||||
|
/** |
||||
|
* 通用类 |
||||
|
*/ |
||||
|
USER_UNREGISTERED(12001, "用户未注册"), |
||||
|
NOT_FOUND(12002, "数据不存在"), |
||||
|
LOGIN_TYPE_ERROR(12005, "登录类型错误"), |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 文件服务 |
||||
|
*/ |
||||
|
FILE_DOWNLOAD_ERROR(90001, "文件下载错误"), |
||||
|
|
||||
|
FILE_UPLOAD_ERROR(90002, "文件上传失败"), |
||||
|
|
||||
|
|
||||
|
|
||||
|
USER_NOT_FOUND(40004, "用户不存在"), |
||||
|
|
||||
|
CAPTCHA_ERROR(40003, "验证码错误"), |
||||
|
|
||||
|
PASSWORD_ERROR(40002, "密码错误"), |
||||
|
|
||||
|
TOKEN_TIMEOUT_ERROR(40006, "token已过期"), |
||||
|
|
||||
|
USER_STOP_FOUND(40008, "用户被禁用"), |
||||
|
USER_STATUS_ERROR(40009,"用户未审核通过"), |
||||
|
|
||||
|
USER_NOT_LOGIN(40011, "用户未登录,请登录后再操作"), |
||||
|
|
||||
|
ACCESS_TOKEN_INVALID(40040, "用户token无效,请重新登录"), |
||||
|
|
||||
|
ACCESS_NOT_ALLOWABLE(50001, "服务不允许直接访问"), |
||||
|
|
||||
|
PERMISSION_NOT_FOUND(40001,"该用户无操作权限"), |
||||
|
|
||||
|
USER_PHONE_EXIST(40012, "用户手机号已存在"), |
||||
|
|
||||
|
|
||||
|
|
||||
|
; |
||||
|
|
||||
|
|
||||
|
private final Integer code; |
||||
|
|
||||
|
private final String msg; |
||||
|
|
||||
|
BizCode(Integer code, String msg) { |
||||
|
this.code = code; |
||||
|
this.msg = msg; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
package com.yeguang.common; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class BusinessException extends RuntimeException { |
||||
|
|
||||
|
private int code; |
||||
|
|
||||
|
private String message; |
||||
|
|
||||
|
public BusinessException(int code, String message) { |
||||
|
super(message); |
||||
|
this.code = code; |
||||
|
this.message = message; |
||||
|
} |
||||
|
|
||||
|
public BusinessException(String message, Throwable cause, int code, String message1) { |
||||
|
super(message, cause); |
||||
|
this.code = code; |
||||
|
this.message = message1; |
||||
|
} |
||||
|
|
||||
|
public BusinessException(Throwable cause, int code, String message) { |
||||
|
super(cause); |
||||
|
this.code = code; |
||||
|
this.message = message; |
||||
|
} |
||||
|
|
||||
|
public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, int code, String message1) { |
||||
|
super(message, cause, enableSuppression, writableStackTrace); |
||||
|
this.code = code; |
||||
|
this.message = message1; |
||||
|
} |
||||
|
|
||||
|
public BusinessException(BizCode biz) { |
||||
|
super(); |
||||
|
this.code = biz.getCode(); |
||||
|
this.message = biz.getMsg(); |
||||
|
} |
||||
|
|
||||
|
public BusinessException(String message) { |
||||
|
super(message); |
||||
|
this.code= BizCode.General_ParameterInvalid.getCode(); |
||||
|
this.message =message; |
||||
|
} |
||||
|
|
||||
|
public BusinessException(BizCode biz, String msg) { |
||||
|
super(); |
||||
|
this.code = biz.getCode(); |
||||
|
this.message = msg; |
||||
|
} |
||||
|
} |
@ -0,0 +1,143 @@ |
|||||
|
package com.yeguang.common; |
||||
|
|
||||
|
/** |
||||
|
* 用户常量信息 |
||||
|
* |
||||
|
* @author ruoyi |
||||
|
*/ |
||||
|
public interface UserConstants { |
||||
|
|
||||
|
/** |
||||
|
* 平台内系统用户的唯一标志 |
||||
|
*/ |
||||
|
String SYS_SESSION = "CURRENT_USER"; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 正常状态 |
||||
|
*/ |
||||
|
String NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 异常状态 |
||||
|
*/ |
||||
|
String EXCEPTION = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 用户正常状态 |
||||
|
*/ |
||||
|
String USER_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 用户封禁状态 |
||||
|
*/ |
||||
|
String USER_DISABLE = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 角色正常状态 |
||||
|
*/ |
||||
|
String ROLE_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 角色封禁状态 |
||||
|
*/ |
||||
|
String ROLE_DISABLE = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 部门正常状态 |
||||
|
*/ |
||||
|
String DEPT_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 部门停用状态 |
||||
|
*/ |
||||
|
String DEPT_DISABLE = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 岗位正常状态 |
||||
|
*/ |
||||
|
String POST_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 岗位停用状态 |
||||
|
*/ |
||||
|
String POST_DISABLE = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 字典正常状态 |
||||
|
*/ |
||||
|
String DICT_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 是否为系统默认(是) |
||||
|
*/ |
||||
|
String YES = "Y"; |
||||
|
|
||||
|
/** |
||||
|
* 是否菜单外链(是) |
||||
|
*/ |
||||
|
String YES_FRAME = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 是否菜单外链(否) |
||||
|
*/ |
||||
|
String NO_FRAME = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 菜单正常状态 |
||||
|
*/ |
||||
|
String MENU_NORMAL = "0"; |
||||
|
|
||||
|
/** |
||||
|
* 菜单停用状态 |
||||
|
*/ |
||||
|
String MENU_DISABLE = "1"; |
||||
|
|
||||
|
/** |
||||
|
* 菜单类型(目录) |
||||
|
*/ |
||||
|
String TYPE_DIR = "M"; |
||||
|
|
||||
|
/** |
||||
|
* 菜单类型(菜单) |
||||
|
*/ |
||||
|
String TYPE_MENU = "C"; |
||||
|
|
||||
|
/** |
||||
|
* 菜单类型(按钮) |
||||
|
*/ |
||||
|
String TYPE_BUTTON = "F"; |
||||
|
|
||||
|
/** |
||||
|
* Layout组件标识 |
||||
|
*/ |
||||
|
String LAYOUT = "Layout"; |
||||
|
|
||||
|
/** |
||||
|
* ParentView组件标识 |
||||
|
*/ |
||||
|
String PARENT_VIEW = "ParentView"; |
||||
|
|
||||
|
/** |
||||
|
* InnerLink组件标识 |
||||
|
*/ |
||||
|
String INNER_LINK = "InnerLink"; |
||||
|
|
||||
|
/** |
||||
|
* 用户名长度限制 |
||||
|
*/ |
||||
|
int USERNAME_MIN_LENGTH = 2; |
||||
|
int USERNAME_MAX_LENGTH = 20; |
||||
|
|
||||
|
/** |
||||
|
* 密码长度限制 |
||||
|
*/ |
||||
|
int PASSWORD_MIN_LENGTH = 5; |
||||
|
int PASSWORD_MAX_LENGTH = 20; |
||||
|
|
||||
|
/** |
||||
|
* 超级管理员ID |
||||
|
*/ |
||||
|
Long SUPER_ADMIN_ID = 1L; |
||||
|
|
||||
|
} |
@ -0,0 +1,105 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.util.concurrent.*; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class MonitorThreadPoolExecutor extends ThreadPoolExecutor { |
||||
|
|
||||
|
private final ThreadLocal<Long> executeStartTime; |
||||
|
protected String poolName; |
||||
|
private final int slowTaskThreshold; |
||||
|
private final int queueTimeThreshold; |
||||
|
|
||||
|
private final int queueSizeThreshold; |
||||
|
|
||||
|
private static final int DEFAULT_SLOW_TASK_TIME = 1000; |
||||
|
|
||||
|
private static final int DEFAULT_QUEUE_TIME = 100; |
||||
|
|
||||
|
private static final int DEFAULT_QUEUE_SIZE = 450; |
||||
|
|
||||
|
|
||||
|
public MonitorThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, String poolName) { |
||||
|
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new NameThreadFactory(poolName), new AbortPolicy(), poolName); |
||||
|
} |
||||
|
|
||||
|
public MonitorThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, String poolName) { |
||||
|
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new AbortPolicy(), poolName); |
||||
|
} |
||||
|
|
||||
|
public MonitorThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, String poolName) { |
||||
|
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new NameThreadFactory(poolName), handler, poolName); |
||||
|
} |
||||
|
|
||||
|
public MonitorThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, String poolName) { |
||||
|
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); |
||||
|
this.poolName = poolName; |
||||
|
executeStartTime = new ThreadLocal<>(); |
||||
|
slowTaskThreshold = 0; |
||||
|
queueTimeThreshold = 0; |
||||
|
queueSizeThreshold = 0; |
||||
|
} |
||||
|
|
||||
|
public MonitorThreadPoolExecutor(ThreadPoolConfig poolConfig) { |
||||
|
super(poolConfig.getCorePoolSize(), |
||||
|
poolConfig.getMaxPoolSize(), |
||||
|
poolConfig.getKeepAliveSeconds(), |
||||
|
TimeUnit.SECONDS, |
||||
|
new ArrayBlockingQueue<>(poolConfig.getQueueCapacity()), new NameThreadFactory(poolConfig.getThreadName()), |
||||
|
(r, executor1) -> log.info("任务被中断")); |
||||
|
this.poolName = poolConfig.getThreadName(); |
||||
|
executeStartTime = new ThreadLocal<>(); |
||||
|
this.slowTaskThreshold = poolConfig.getTaskThreshold() > 0 ? poolConfig.getTaskThreshold() : DEFAULT_SLOW_TASK_TIME; |
||||
|
this.queueTimeThreshold = poolConfig.getQueueTimeThreshold() > 0 ? poolConfig.getQueueTimeThreshold() : DEFAULT_QUEUE_TIME; |
||||
|
this.queueSizeThreshold = poolConfig.getQueueSizeThreshold() > 0 ? poolConfig.getQueueSizeThreshold() : DEFAULT_QUEUE_SIZE; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void execute(Runnable command) { |
||||
|
super.execute(command); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void beforeExecute(Thread t, Runnable r) { |
||||
|
super.beforeExecute(t, r); |
||||
|
executeStartTime.set(System.nanoTime()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void afterExecute(Runnable r, Throwable t) { |
||||
|
MonitoredRunnable monitored = null; |
||||
|
try { |
||||
|
long executeEndNano = System.nanoTime(); |
||||
|
Long executeStartTime = this.executeStartTime.get(); |
||||
|
monitored = (MonitoredRunnable) r; |
||||
|
long queueNanoTime = monitored.getInQueueNanoTime(); |
||||
|
int queueTime = (int) ((executeStartTime - queueNanoTime) / 1000000L); |
||||
|
int executeTime = (int) ((executeEndNano - executeStartTime) / 1000000L); |
||||
|
ThreadStatistics packStatistics = null; |
||||
|
if (monitored.getThreadLocal() != null) { |
||||
|
packStatistics = monitored.getThreadLocal().get(); |
||||
|
} |
||||
|
if (executeTime > this.slowTaskThreshold || queueTime > this.queueTimeThreshold || getQueue().size() >= queueSizeThreshold) { |
||||
|
log.warn("线程池({}),触发告警,当前任务执行时间={},当前任务排队时间={},队列线程总数={} ", Thread.currentThread().getName(), executeTime, queueTime, getQueue().size()); |
||||
|
if (packStatistics != null) { |
||||
|
log.warn("当前任务各阶段耗时统计:{}", packStatistics); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (t != null) { |
||||
|
log.error("线程池名称 = {}, 执行异常的任务数+1", poolName); |
||||
|
} |
||||
|
} catch (Exception ignore) { |
||||
|
} finally { |
||||
|
executeStartTime.remove(); |
||||
|
if (monitored != null && monitored.getThreadLocal() != null) { |
||||
|
monitored.getThreadLocal().remove(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
|
||||
|
|
||||
|
public class MonitoredRunnable implements Runnable { |
||||
|
|
||||
|
private final Runnable runnable; |
||||
|
private ThreadLocal<ThreadStatistics> threadLocal; |
||||
|
private final long inQueueNanoTime; |
||||
|
|
||||
|
public MonitoredRunnable(Runnable runnable, ThreadLocal<ThreadStatistics> threadLocal) { |
||||
|
this.runnable = runnable; |
||||
|
this.threadLocal = threadLocal; |
||||
|
this.inQueueNanoTime = System.nanoTime(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public ThreadLocal<ThreadStatistics> getThreadLocal() { |
||||
|
return threadLocal; |
||||
|
} |
||||
|
|
||||
|
public void setThreadLocal(ThreadLocal<ThreadStatistics> threadLocal) { |
||||
|
this.threadLocal = threadLocal; |
||||
|
} |
||||
|
|
||||
|
public long getInQueueNanoTime() { |
||||
|
return inQueueNanoTime; |
||||
|
} |
||||
|
|
||||
|
public MonitoredRunnable(Runnable runnable) { |
||||
|
this.runnable = runnable; |
||||
|
this.inQueueNanoTime = System.nanoTime(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void run() { |
||||
|
this.runnable.run(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
import java.util.concurrent.ThreadFactory; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
|
||||
|
public class NameThreadFactory implements ThreadFactory { |
||||
|
|
||||
|
private String namePrefix; |
||||
|
|
||||
|
private AtomicInteger nextId = new AtomicInteger(1); |
||||
|
|
||||
|
public NameThreadFactory(String namePrefix) { |
||||
|
this.namePrefix = namePrefix; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Thread newThread(Runnable r) { |
||||
|
return new Thread(r, namePrefix + nextId.getAndIncrement()); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
|
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Bean; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
|
||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Configuration |
||||
|
@RequiredArgsConstructor |
||||
|
public class ThreadExecutorConfig { |
||||
|
|
||||
|
private final ThreadPoolConfig threadPoolConfig; |
||||
|
|
||||
|
|
||||
|
// @Primary
|
||||
|
@Bean(name = "customThreadPool") |
||||
|
public ThreadPoolExecutor getCustomExecutor(@Inject("${thread.enabled}") boolean enabled) { |
||||
|
if (enabled) { |
||||
|
return generateExecutor(threadPoolConfig); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private ThreadPoolExecutor generateExecutor(ThreadPoolConfig threadPoolConfig) { |
||||
|
return new MonitorThreadPoolExecutor(threadPoolConfig); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
|
||||
|
|
||||
|
@Data |
||||
|
@Configuration |
||||
|
@Inject("${thread.pool}") |
||||
|
public class ThreadPoolConfig { |
||||
|
|
||||
|
private int corePoolSize; |
||||
|
|
||||
|
private int maxPoolSize; |
||||
|
|
||||
|
private int keepAliveSeconds; |
||||
|
|
||||
|
private int queueCapacity; |
||||
|
|
||||
|
private String threadName; |
||||
|
|
||||
|
private int taskThreshold; |
||||
|
|
||||
|
private int queueTimeThreshold; |
||||
|
|
||||
|
private int queueSizeThreshold; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,118 @@ |
|||||
|
package com.yeguang.common.thread; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
public class ThreadStatistics { |
||||
|
|
||||
|
private String imei; |
||||
|
|
||||
|
private long totalCost; |
||||
|
|
||||
|
private long start; |
||||
|
|
||||
|
private long finish; |
||||
|
|
||||
|
private Map<StatisticsEnum, Item> phases = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
/** |
||||
|
* 指定阶段处理统计 |
||||
|
* |
||||
|
* @param statisticsEnum |
||||
|
*/ |
||||
|
public void startPhase(StatisticsEnum statisticsEnum) { |
||||
|
phases.computeIfAbsent(statisticsEnum, key -> new Item()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 完成指定部分的处理统计 |
||||
|
* |
||||
|
* @param statisticsEnum 指定部分 |
||||
|
*/ |
||||
|
public void finishPhase(StatisticsEnum statisticsEnum) { |
||||
|
Item item = phases.getOrDefault(statisticsEnum, null); |
||||
|
if (item != null) { |
||||
|
item.finish(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void finishStatistics() { |
||||
|
this.finish = System.currentTimeMillis(); |
||||
|
this.totalCost = this.finish - this.start; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public ThreadStatistics(long start) { |
||||
|
this.start = start; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Data |
||||
|
public static class Item { |
||||
|
public Item() { |
||||
|
start = System.currentTimeMillis(); |
||||
|
} |
||||
|
|
||||
|
long start; |
||||
|
|
||||
|
volatile long finish; |
||||
|
|
||||
|
volatile long cost; |
||||
|
|
||||
|
/** |
||||
|
* 完成处理统计 |
||||
|
*/ |
||||
|
public void finish() { |
||||
|
if (finish != 0) { |
||||
|
return; |
||||
|
} |
||||
|
this.finish = System.currentTimeMillis(); |
||||
|
this.cost = this.finish - start; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "Item{" + |
||||
|
"cost=" + cost + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public enum StatisticsEnum { |
||||
|
/** |
||||
|
* 基础信息解析 |
||||
|
*/ |
||||
|
base_info, |
||||
|
/** |
||||
|
* 解密 |
||||
|
*/ |
||||
|
decrypt, |
||||
|
/** |
||||
|
* 数据包解析 |
||||
|
*/ |
||||
|
deserialization, |
||||
|
|
||||
|
/** |
||||
|
* 数据保存到ck |
||||
|
*/ |
||||
|
save; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "Statistics{" + |
||||
|
"imei='" + imei + '\'' + |
||||
|
", totalCost=" + totalCost + |
||||
|
", start=" + start + |
||||
|
", finish=" + finish + |
||||
|
", phases=" + phases + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
import cn.dev33.satoken.exception.NotLoginException; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.cloud.CloudClient; |
||||
|
import org.noear.solon.core.exception.StatusException; |
||||
|
import org.noear.solon.core.handle.Context; |
||||
|
import org.noear.solon.core.handle.Filter; |
||||
|
import org.noear.solon.core.handle.FilterChain; |
||||
|
import org.noear.solon.core.handle.Result; |
||||
|
import org.noear.solon.validation.ValidatorException; |
||||
|
import org.slf4j.MDC; |
||||
|
|
||||
|
@Component(index = -100) //index 为顺序位(不加,则默认为0)
|
||||
|
public class AppFilter implements Filter { |
||||
|
@Override |
||||
|
public void doFilter(Context ctx, FilterChain chain) throws Throwable { |
||||
|
try { |
||||
|
String traceId = CloudClient.trace().getTraceId(); |
||||
|
MDC.put("X-TraceId", traceId); |
||||
|
chain.doFilter(ctx); |
||||
|
} catch (NotLoginException e) { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); |
||||
|
} catch (ValidatorException e) { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); //e.getResult().getDescription()
|
||||
|
} catch (StatusException e) { |
||||
|
if (e.getCode() == 404) { |
||||
|
ctx.status(e.getCode()); |
||||
|
} else { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
ctx.render(Result.failure(500, "服务端运行出错")); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
import cn.dev33.satoken.exception.NotLoginException; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.core.exception.StatusException; |
||||
|
import org.noear.solon.core.handle.Context; |
||||
|
import org.noear.solon.core.handle.Handler; |
||||
|
import org.noear.solon.core.handle.Result; |
||||
|
import org.noear.solon.core.route.RouterInterceptor; |
||||
|
import org.noear.solon.core.route.RouterInterceptorChain; |
||||
|
import org.noear.solon.validation.ValidatorException; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component(index = 0) //index 为顺序位(不加,则默认为0)
|
||||
|
public class AppRouterInterceptor implements RouterInterceptor { |
||||
|
@Override |
||||
|
public void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain) throws Throwable { |
||||
|
try { |
||||
|
chain.doIntercept(ctx, mainHandler); |
||||
|
} catch (NotLoginException e) { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); |
||||
|
} catch (ValidatorException e) { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); //e.getResult().getDescription()
|
||||
|
} catch (StatusException e) { |
||||
|
if (e.getCode() == 404) { |
||||
|
ctx.status(e.getCode()); |
||||
|
} else { |
||||
|
ctx.render(Result.failure(e.getCode(), e.getMessage())); |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
log.error("服务端运行出错", e); |
||||
|
ctx.render(Result.failure(500, "服务端运行出错")); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
import com.github.xiaoymin.knife4j.solon.extension.OpenApiExtensionResolver; |
||||
|
import io.swagger.models.Scheme; |
||||
|
import org.noear.solon.annotation.Bean; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.docs.DocDocket; |
||||
|
import org.noear.solon.docs.models.ApiContact; |
||||
|
import org.noear.solon.docs.models.ApiInfo; |
||||
|
|
||||
|
/** |
||||
|
* 自定义 Knife4j 接口文档的配置 |
||||
|
*/ |
||||
|
@Configuration |
||||
|
public class Knife4jConfig { |
||||
|
@Inject |
||||
|
OpenApiExtensionResolver openApiExtensionResolver; |
||||
|
@Bean(value = "defaultApi2") |
||||
|
public DocDocket defaultApi2() { |
||||
|
//根据情况增加 "knife4j.setting" (可选)
|
||||
|
return new DocDocket() |
||||
|
.basicAuth(openApiExtensionResolver.getSetting().getBasic()) |
||||
|
.vendorExtensions(openApiExtensionResolver.buildExtensions()) |
||||
|
.groupName("demo接口文档") |
||||
|
.info(new ApiInfo().title("在线文档") |
||||
|
.description("demo在线API文档") |
||||
|
.termsOfService("https://gitee.com/noear/solon") |
||||
|
.contact(new ApiContact().name("yeguangzhang") |
||||
|
.url("https://gitee.com/noear/solon") |
||||
|
.email("yeguangzhang@126.com")) |
||||
|
.version("1.0")) |
||||
|
.schemes(String.valueOf(Scheme.HTTP)) |
||||
|
.apis("com.yeguang.controller"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,32 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
|
||||
|
|
||||
|
import com.obs.services.ObsClient; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.noear.solon.annotation.Bean; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
|
||||
|
@Data |
||||
|
@Configuration |
||||
|
@NoArgsConstructor |
||||
|
@Inject("${huawei.obs}") |
||||
|
public class ObsConfig { |
||||
|
|
||||
|
|
||||
|
private String endPoint; |
||||
|
private String ak; |
||||
|
private String sk; |
||||
|
|
||||
|
private String bucketName; |
||||
|
private String pathPrefix; |
||||
|
|
||||
|
@Bean |
||||
|
public ObsClient obsClient() { |
||||
|
|
||||
|
ObsClient obsClient = new ObsClient(ak, sk, endPoint); |
||||
|
return obsClient; |
||||
|
} |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
import cn.dev33.satoken.dao.SaTokenDao; |
||||
|
import cn.dev33.satoken.solon.dao.SaTokenDaoOfRedissonJackson; |
||||
|
import org.noear.solon.annotation.Bean; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.cache.redisson.RedissonCacheService; |
||||
|
import org.noear.solon.cache.redisson.RedissonClientOriginalSupplier; |
||||
|
import org.noear.solon.data.cache.CacheService; |
||||
|
import org.redisson.api.RedissonClient; |
||||
|
|
||||
|
@Configuration |
||||
|
public class RedisConfig { |
||||
|
// 构建 redis client(如直接用);RedissonClientOriginalSupplier 支持 Redisson 的原始风格配置
|
||||
|
@Bean |
||||
|
public RedissonClient redisClient(@Inject("${app.redis}") RedissonClientOriginalSupplier supplier) { |
||||
|
return supplier.get(); |
||||
|
} |
||||
|
|
||||
|
//构建 Cache Service(给 @Cache 用)
|
||||
|
@Bean |
||||
|
public CacheService cacheService(@Inject RedissonClient client){ |
||||
|
return new RedissonCacheService(client, 30); |
||||
|
} |
||||
|
|
||||
|
//构建 SaToken Dao //v2.4.3 后支持
|
||||
|
@Bean |
||||
|
public SaTokenDao saTokenDao(@Inject RedissonClient client){ |
||||
|
return new SaTokenDaoOfRedissonJackson(client); |
||||
|
} |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package com.yeguang.config; |
||||
|
|
||||
|
import cn.dev33.satoken.context.SaHolder; |
||||
|
import cn.dev33.satoken.jwt.StpLogicJwtForSimple; |
||||
|
import cn.dev33.satoken.solon.integration.SaTokenInterceptor; |
||||
|
import cn.dev33.satoken.stp.StpLogic; |
||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Bean; |
||||
|
import org.noear.solon.annotation.Configuration; |
||||
|
import org.noear.solon.core.mvc.ActionDefault; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Configuration |
||||
|
public class SaInterceptorConfig { |
||||
|
private final String[] EXCLUDE_PATH = { |
||||
|
"/favicon.ico", |
||||
|
"/doc.html", |
||||
|
"/swagger-ui/**", |
||||
|
// "/swagger-resources",
|
||||
|
"/swagger-resources**", |
||||
|
"/swagger/v2", |
||||
|
"/v2/api-docs/**", |
||||
|
"/webjars/**", |
||||
|
"/static/**", |
||||
|
"/error", |
||||
|
"/user/login", |
||||
|
"/user/register", |
||||
|
"/user/logout", |
||||
|
"/record/export/**" |
||||
|
}; |
||||
|
|
||||
|
@Bean(index = -200) //-100,是顺序位(低值优先)
|
||||
|
public SaTokenInterceptor saTokenInterceptor() { |
||||
|
return new SaTokenInterceptor() |
||||
|
// 指定 [拦截路由] 与 [放行路由]
|
||||
|
.addInclude("/**").addExclude(EXCLUDE_PATH) |
||||
|
|
||||
|
// 认证函数: 每次请求执行
|
||||
|
.setAuth(req -> { |
||||
|
StpUtil.checkLogin(); |
||||
|
}) |
||||
|
|
||||
|
// 异常处理函数:每次认证函数发生异常时执行此函数 //包括注解异常
|
||||
|
// .setError(e -> {
|
||||
|
// log.error("sa interceptor", e);
|
||||
|
// })
|
||||
|
|
||||
|
// 前置函数:在每次认证函数之前执行
|
||||
|
.setBeforeAuth(req -> { |
||||
|
// ---------- 设置一些安全响应头 ----------
|
||||
|
SaHolder.getResponse() |
||||
|
// 服务器名称
|
||||
|
.setServer("sa-server") |
||||
|
// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
|
||||
|
.setHeader("X-Frame-Options", "SAMEORIGIN") |
||||
|
// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
|
||||
|
.setHeader("X-XSS-Protection", "1; mode=block") |
||||
|
// 禁用浏览器内容嗅探
|
||||
|
.setHeader("X-Content-Type-Options", "nosniff"); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Bean |
||||
|
public StpLogic getStpLogicJwt() { |
||||
|
// Sa-Token 整合 jwt (简单模式)
|
||||
|
return new StpLogicJwtForSimple(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.yeguang.controller; |
||||
|
|
||||
|
import com.yeguang.service.InfoService; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Controller; |
||||
|
import org.noear.solon.annotation.Get; |
||||
|
import org.noear.solon.annotation.Mapping; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Controller |
||||
|
@Mapping("/info") |
||||
|
@RequiredArgsConstructor |
||||
|
@Api(tags = "信息") |
||||
|
public class InfoController { |
||||
|
|
||||
|
private final InfoService infoService; |
||||
|
|
||||
|
@ApiOperation("获取信息") |
||||
|
@Get |
||||
|
@Mapping("/hello") |
||||
|
public String hello() { |
||||
|
log.info("hello api has been called"); |
||||
|
return infoService.hello(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,79 @@ |
|||||
|
package com.yeguang.controller; |
||||
|
|
||||
|
import cn.hutool.core.map.MapUtil; |
||||
|
import com.yeguang.model.*; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import com.yeguang.service.RecordService; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Controller; |
||||
|
import org.noear.solon.annotation.Mapping; |
||||
|
import org.noear.solon.core.handle.Context; |
||||
|
import org.noear.solon.core.handle.MethodType; |
||||
|
import org.noear.solon.core.handle.Result; |
||||
|
import org.noear.solon.validation.annotation.Validated; |
||||
|
|
||||
|
@Api(tags = "识别记录") |
||||
|
@Slf4j |
||||
|
@Controller |
||||
|
@RequiredArgsConstructor |
||||
|
@Mapping("/record") |
||||
|
public class RecordController { |
||||
|
private final RecordService recordService; |
||||
|
|
||||
|
|
||||
|
@ApiOperation(value = "创建识别记录") |
||||
|
@Mapping(value = "/createRecord", multipart = true, produces = "application/json", method = MethodType.POST) |
||||
|
public Result createRecord(Context context) { |
||||
|
CreateRecordRequest request = context.paramAsBean(CreateRecordRequest.class); |
||||
|
request.setFiles(context.fileValues("files")); |
||||
|
String taskId = recordService.createRecordAsync(request); |
||||
|
return Result.succeed(MapUtil.of("taskId", taskId)); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "识别记录处理进度") |
||||
|
@Mapping(value = "/taskProgress/{taskId}", method = MethodType.GET) |
||||
|
public Float getTaskProgress(String taskId) { |
||||
|
return recordService.getTaskProgress(taskId); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "获取识别记录详情") |
||||
|
@Mapping(value = "/getRecordDetail/{recordId}", method = MethodType.GET) |
||||
|
public Result<RecordDetail> getRecordDetail(Long recordId) { |
||||
|
return Result.succeed(recordService.getRecordDetail(recordId)); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "导出识别记录", notes = "下载文件(响应类型为文件流)") |
||||
|
@Mapping(value = "/export/{recordId}", produces = "application/octet-stream", method = MethodType.GET) |
||||
|
public void exportRecord(Long recordId, Context context) { |
||||
|
recordService.exportRecord(recordId, context); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "分页获取识别记录") |
||||
|
@Mapping(value = "/page", method = MethodType.POST) |
||||
|
public Result<PageResult<RecordItem>> getPages(PageRequest pageRequest) { |
||||
|
return Result.succeed(recordService.getPages(pageRequest)); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "更新识别记录内容") |
||||
|
@Mapping(value = "/updateRecordContent", method = MethodType.PUT) |
||||
|
public void updateRecord(RecordContentEntity recordContentEntity) { |
||||
|
recordService.updateRecordContent(recordContentEntity); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@ApiOperation(value = "获取模型列表") |
||||
|
@Mapping(value = "/models", method = MethodType.GET) |
||||
|
public Result<RecordModel> getModels() { |
||||
|
return Result.succeed(recordService.getModels()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "比例尺设置") |
||||
|
@Mapping(value = "/scale", method = MethodType.PUT) |
||||
|
public void updateScale(UpdateScaleRequest request) { |
||||
|
recordService.updateScale(request); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,64 @@ |
|||||
|
package com.yeguang.controller; |
||||
|
|
||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||
|
import com.yeguang.model.LoginRequest; |
||||
|
import com.yeguang.model.LoginResponse; |
||||
|
import com.yeguang.model.UserRequest; |
||||
|
import com.yeguang.service.UserService; |
||||
|
import io.swagger.annotations.Api; |
||||
|
import io.swagger.annotations.ApiOperation; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Controller; |
||||
|
import org.noear.solon.annotation.Get; |
||||
|
import org.noear.solon.annotation.Mapping; |
||||
|
import org.noear.solon.core.handle.Result; |
||||
|
import org.noear.solon.validation.annotation.Validated; |
||||
|
|
||||
|
|
||||
|
@Api(tags = "用户") |
||||
|
@Slf4j |
||||
|
@Controller |
||||
|
@Mapping("/user") |
||||
|
@RequiredArgsConstructor |
||||
|
public class UserController { |
||||
|
|
||||
|
private final UserService userService; |
||||
|
|
||||
|
@Get |
||||
|
@Mapping("/info") |
||||
|
public String info() { |
||||
|
log.info("info"); |
||||
|
return "info"; |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "登录") |
||||
|
@Mapping("/login") |
||||
|
public Result<LoginResponse> login(@Validated LoginRequest loginBody) { |
||||
|
LoginResponse loginVo = userService.login(loginBody); |
||||
|
return Result.succeed(loginVo); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "登出") |
||||
|
@Mapping("/logout") |
||||
|
public Result<String> logout() { |
||||
|
StpUtil.logout(); |
||||
|
return Result.succeed("logout"); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "注册") |
||||
|
@Mapping(value = "/register", consumes = "application/json") |
||||
|
public Result<Void> register(@Validated UserRequest user) { |
||||
|
userService.register(user); |
||||
|
return Result.succeed(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@ApiOperation(value = "审批") |
||||
|
@Mapping(value = "/approval/{userId}") |
||||
|
public Result<Void> approval(Long userId) { |
||||
|
userService.approval(userId); |
||||
|
return Result.succeed(); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package com.yeguang.handler; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.yeguang.model.ModelPredictResponse; |
||||
|
|
||||
|
import java.sql.ResultSet; |
||||
|
import java.sql.SQLException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class ModelPredictResponseListTypeHandler extends JacksonTypeHandler { |
||||
|
public ModelPredictResponseListTypeHandler(Class<?> type) { |
||||
|
super(type); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Object getResult(ResultSet rs, String columnName) throws SQLException { |
||||
|
String json = rs.getString(columnName); |
||||
|
try { |
||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||
|
return objectMapper.readValue(json, new TypeReference<List<ModelPredictResponse>>(){}); |
||||
|
} catch (Exception e) { |
||||
|
throw new SQLException("Error deserializing recognition_data", e); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package com.yeguang.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Param; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface RecordContentMapper extends BaseMapper<RecordContentEntity> { |
||||
|
|
||||
|
List<RecordContentEntity> selectListByRecordId(@Param("recordId") Long recordId); |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package com.yeguang.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.yeguang.model.RecordDetail; |
||||
|
import com.yeguang.model.RecordItem; |
||||
|
import com.yeguang.model.entity.RecordEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
|
||||
|
@Mapper |
||||
|
public interface RecordMapper extends BaseMapper<RecordEntity> { |
||||
|
|
||||
|
RecordDetail getRecordDetail(long id); |
||||
|
|
||||
|
Page<RecordItem> pageList(Page<RecordItem> page, Object o); |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.yeguang.mapper; |
||||
|
|
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.yeguang.model.LoginUser; |
||||
|
import com.yeguang.model.entity.UserEntity; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Param; |
||||
|
|
||||
|
@Mapper |
||||
|
public interface UserMapper extends BaseMapper<UserEntity> { |
||||
|
LoginUser selectLoginUserByPhone(@Param("phone") String phone); |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.noear.solon.core.handle.UploadedFile; |
||||
|
import org.noear.solon.validation.annotation.*; |
||||
|
|
||||
|
|
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@ApiModel("创建记录请求") |
||||
|
public class CreateRecordRequest { |
||||
|
@ApiModelProperty(value = "图片列表", required = true) |
||||
|
@NotEmpty(message = "文件不能为空") |
||||
|
private UploadedFile[] files; |
||||
|
@ApiModelProperty(value = "模型名称", required = true) |
||||
|
@NotBlank(message = "模型名称不能为空") |
||||
|
private String modelName; |
||||
|
@ApiModelProperty(value = "重叠度", required = true) |
||||
|
@NotNull(message = "IOU 不能为空") |
||||
|
@Min(value = 0, message = "IOU 不能小于 0") |
||||
|
@Max(value = 1, message = "IOU 不能大于 1") |
||||
|
private Float iou; |
||||
|
@ApiModelProperty(value = "置信度", required = true) |
||||
|
@NotNull(message = "Conf 不能为空") |
||||
|
@Min(value = 0, message = "Conf 不能小于 0") |
||||
|
@Max(value = 1, message = "Conf 不能大于 1") |
||||
|
private Float conf; |
||||
|
@ApiModelProperty(value = "任务名称", required = true) |
||||
|
@NotBlank(message = "任务名称不能为空") |
||||
|
private String taskName; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
|
||||
|
import com.yeguang.common.UserConstants; |
||||
|
import lombok.Data; |
||||
|
import org.noear.solon.validation.annotation.Length; |
||||
|
import org.noear.solon.validation.annotation.NotBlank; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
|
||||
|
@Data |
||||
|
public class LoginRequest implements Serializable { |
||||
|
|
||||
|
@NotBlank(message = "{user.phone.not.blank}") |
||||
|
private String phone; |
||||
|
@NotBlank(message = "{user.password.not.blank}") |
||||
|
@Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}") |
||||
|
private String password; |
||||
|
|
||||
|
private String grantType = "password"; |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 登录验证信息 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class LoginResponse { |
||||
|
|
||||
|
/** |
||||
|
* 授权令牌 |
||||
|
*/ |
||||
|
@JsonProperty("access_token") |
||||
|
private String accessToken; |
||||
|
|
||||
|
/** |
||||
|
* 刷新令牌 |
||||
|
*/ |
||||
|
@JsonProperty("refresh_token") |
||||
|
private String refreshToken; |
||||
|
|
||||
|
/** |
||||
|
* 授权令牌 access_token 的有效期 |
||||
|
*/ |
||||
|
@JsonProperty("expire_in") |
||||
|
private Long expireIn; |
||||
|
|
||||
|
/** |
||||
|
* 刷新令牌 refresh_token 的有效期 |
||||
|
*/ |
||||
|
@JsonProperty("refresh_expire_in") |
||||
|
private Long refreshExpireIn; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
/** |
||||
|
* @ClassName LoginUser |
||||
|
* @author: zhangyeguang |
||||
|
* @create: 2024-09-01 09:28 |
||||
|
* @Version 1.0 |
||||
|
* @description: |
||||
|
**/ |
||||
|
@Data |
||||
|
@NoArgsConstructor |
||||
|
public class LoginUser implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 用户ID |
||||
|
*/ |
||||
|
@JsonSerialize(using = ToStringSerializer.class) |
||||
|
private Long userId; |
||||
|
|
||||
|
/** |
||||
|
* 部门 |
||||
|
*/ |
||||
|
private String unitName; |
||||
|
|
||||
|
/** |
||||
|
* 用户唯一标识 |
||||
|
*/ |
||||
|
private String token; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 登录时间 |
||||
|
*/ |
||||
|
private Long loginTime; |
||||
|
|
||||
|
/** |
||||
|
* 过期时间 |
||||
|
*/ |
||||
|
private Long expireTime; |
||||
|
|
||||
|
/** |
||||
|
* 登录IP地址 |
||||
|
*/ |
||||
|
private String ipaddr; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 用户名 |
||||
|
*/ |
||||
|
private String username; |
||||
|
|
||||
|
/** |
||||
|
* 用户昵称 |
||||
|
*/ |
||||
|
private String realName; |
||||
|
/** |
||||
|
* 手机号 |
||||
|
*/ |
||||
|
private String phone; |
||||
|
@JsonIgnore |
||||
|
private String password; |
||||
|
|
||||
|
private String email; |
||||
|
/** |
||||
|
* 头像 |
||||
|
*/ |
||||
|
private String avatar; |
||||
|
|
||||
|
private LocalDateTime createTime; |
||||
|
|
||||
|
private Integer status; |
||||
|
|
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class ModelPredictResponse { |
||||
|
private Integer classify; |
||||
|
private String name; |
||||
|
private int[][] points; |
||||
|
private Float length; |
||||
|
} |
||||
|
|
@ -0,0 +1,23 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* @ClassName PageInfo |
||||
|
* @author: zhangyeguang |
||||
|
* @create: 2024-09-02 15:21 |
||||
|
* @Version 1.0 |
||||
|
* @description: |
||||
|
**/ |
||||
|
@Data |
||||
|
public class PageRequest<T> implements java.io.Serializable { |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
private int pageNum; |
||||
|
private int pageSize; |
||||
|
private String oderByColumn; |
||||
|
|
||||
|
private T request; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ClassName PageResult |
||||
|
* @author: zhangyeguang |
||||
|
* @create: 2024-09-02 15:22 |
||||
|
* @Version 1.0 |
||||
|
* @description: |
||||
|
**/ |
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
public class PageResult<T> { |
||||
|
private int total; |
||||
|
private int pageSize; |
||||
|
private int pageNum; |
||||
|
private int pages; |
||||
|
private List<T> records; |
||||
|
|
||||
|
public static <T> PageResult<T> of(int total, int pageSize, int pageNum, List<T> records) { |
||||
|
PageResult<T> pageResult = new PageResult<>(); |
||||
|
pageResult.setTotal(total); |
||||
|
pageResult.setPageSize(pageSize); |
||||
|
pageResult.setPageNum(pageNum); |
||||
|
pageResult.setPages(total==0||pageSize==0?0:total % pageSize == 0 ? total / pageSize : total / pageSize + 1); |
||||
|
pageResult.setRecords(records); |
||||
|
return pageResult; |
||||
|
} |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.alibaba.excel.annotation.ExcelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class RecordContentExcel { |
||||
|
|
||||
|
@ExcelProperty("文件名") |
||||
|
private String name; |
||||
|
@ExcelProperty("总喙数") |
||||
|
private int beakNum; |
||||
|
@ExcelProperty("总柄数") |
||||
|
private int handleNum; |
||||
|
@ExcelProperty("总角果数") |
||||
|
private int rapeNum; |
||||
|
@ExcelProperty("置信度") |
||||
|
private float conf; |
||||
|
@ExcelProperty("重叠度") |
||||
|
private float iou; |
||||
|
@ExcelProperty("比例系数") |
||||
|
private int scale; |
||||
|
|
||||
|
@ExcelProperty("角果长") |
||||
|
private float rapeLength; |
||||
|
|
||||
|
@ExcelProperty("喙长") |
||||
|
private float beakLength; |
||||
|
@ExcelProperty("柄长") |
||||
|
private float handleLength; |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
public class RecordDetail { |
||||
|
private Long id; |
||||
|
|
||||
|
private String name; |
||||
|
|
||||
|
private int imageNum; |
||||
|
|
||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
private LocalDateTime createTime; |
||||
|
; |
||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
private LocalDateTime updateTime; |
||||
|
|
||||
|
private List<RecordContentEntity> content; |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.alibaba.excel.annotation.ExcelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class RecordExcel { |
||||
|
|
||||
|
@ExcelProperty("总图片数") |
||||
|
private int imageNum; |
||||
|
@ExcelProperty("总喙数") |
||||
|
private int beakNum; |
||||
|
@ExcelProperty("总柄数") |
||||
|
private int handleNum; |
||||
|
@ExcelProperty("总角果数") |
||||
|
private int rapeNum; |
||||
|
@ExcelProperty("平均角果数") |
||||
|
private float avgRapeNum; |
||||
|
@ExcelProperty("最大角果数") |
||||
|
private int maxRapeNum; |
||||
|
@ExcelProperty("最少角果数") |
||||
|
private int minRapeNum; |
||||
|
|
||||
|
@ExcelProperty("平均角果长度") |
||||
|
private float avgRapeLength; |
||||
|
|
||||
|
@ExcelProperty("平均喙长度") |
||||
|
private float avgBeakLength; |
||||
|
@ExcelProperty("平均柄长度") |
||||
|
private float avgHandleLength; |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
public class RecordItem { |
||||
|
|
||||
|
private String recordName; |
||||
|
|
||||
|
private Long recordId; |
||||
|
|
||||
|
private int imageNum; |
||||
|
|
||||
|
private String userName; |
||||
|
|
||||
|
private Long userId; |
||||
|
|
||||
|
private String unitName; |
||||
|
|
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
private LocalDateTime createTime; |
||||
|
|
||||
|
private String phone; |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
public class RecordModel { |
||||
|
private List<String> loaded_models; |
||||
|
|
||||
|
private List<ModelDetail> models; |
||||
|
|
||||
|
@Data |
||||
|
public static class ModelDetail { |
||||
|
|
||||
|
private float conf; |
||||
|
|
||||
|
private float iou; |
||||
|
|
||||
|
private String name; |
||||
|
|
||||
|
private String desc; |
||||
|
|
||||
|
private String file; |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
public class Scale { |
||||
|
private int scale; |
||||
|
private float size; |
||||
|
private int[][] points; |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@ApiModel("更新比例尺") |
||||
|
public class UpdateScaleRequest { |
||||
|
@ApiModelProperty("记录id") |
||||
|
private long recordId; |
||||
|
@ApiModelProperty("记录内容id") |
||||
|
private long recordContentId; |
||||
|
@ApiModelProperty("比例尺类型,1=统一、2=自用、3=延用") |
||||
|
private int scaleType; |
||||
|
@ApiModelProperty("比例尺") |
||||
|
private Scale scale; |
||||
|
|
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
package com.yeguang.model; |
||||
|
|
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.noear.solon.validation.annotation.Pattern; |
||||
|
import org.noear.solon.validation.annotation.Size; |
||||
|
|
||||
|
/** |
||||
|
* @ClassName UserRequest |
||||
|
* @author: zhangyeguang |
||||
|
* @create: 2024-08-31 10:00 |
||||
|
* @Version 1.0 |
||||
|
* @description: |
||||
|
**/ |
||||
|
@Data |
||||
|
@NoArgsConstructor |
||||
|
public class UserRequest { |
||||
|
|
||||
|
/** |
||||
|
* 用户昵称 |
||||
|
*/ |
||||
|
@Size(min = 2, max = 30, message = "用户姓名长度不能超过{max}个字符") |
||||
|
private String realName; |
||||
|
|
||||
|
/** |
||||
|
* 手机号码 |
||||
|
*/ |
||||
|
@Pattern(value = "^1[34578]\\d{9}$", message = "手机号码格式不正确") |
||||
|
private String phone; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 密码 |
||||
|
*/ |
||||
|
private String password; |
||||
|
|
||||
|
|
||||
|
private String unitName; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
package com.yeguang.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; |
||||
|
import com.yeguang.model.ModelPredictResponse; |
||||
|
import com.yeguang.model.Scale; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
@TableName(value = "record_content", autoResultMap = true) |
||||
|
public class RecordContentEntity { |
||||
|
@TableId(type = IdType.AUTO) |
||||
|
private Long id; |
||||
|
|
||||
|
private Long recordId; |
||||
|
|
||||
|
private String imageName; |
||||
|
|
||||
|
private String imageUrl; |
||||
|
@TableField(typeHandler = JacksonTypeHandler.class) |
||||
|
private List<ModelPredictResponse> recognitionData; |
||||
|
|
||||
|
private int siliquaNum; |
||||
|
|
||||
|
private int handleNum; |
||||
|
|
||||
|
private int beakNum; |
||||
|
//比例尺类型 ,1=统一/2=自用/3=延用/0=无
|
||||
|
private int scaleType; |
||||
|
//比例
|
||||
|
@TableField(typeHandler = JacksonTypeHandler.class) |
||||
|
private Scale scale; |
||||
|
|
||||
|
private float conf; |
||||
|
|
||||
|
private float iou; |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.yeguang.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("record_info") |
||||
|
public class RecordEntity { |
||||
|
@TableId(type = IdType.AUTO) |
||||
|
private Long id; |
||||
|
|
||||
|
private String name; |
||||
|
|
||||
|
private Long userId; |
||||
|
|
||||
|
private int imageNum; |
||||
|
|
||||
|
private LocalDateTime createTime; |
||||
|
|
||||
|
private LocalDateTime updateTime; |
||||
|
|
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.yeguang.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
@Data |
||||
|
@TableName("siliqua_user") |
||||
|
@Accessors(chain = true) |
||||
|
public class UserEntity { |
||||
|
@TableId |
||||
|
private long id; |
||||
|
|
||||
|
private String phone; |
||||
|
private String password; |
||||
|
private String realName; |
||||
|
|
||||
|
private String unitName; |
||||
|
|
||||
|
private LocalDateTime createTime; |
||||
|
|
||||
|
private int status; |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
package com.yeguang.remote; |
||||
|
|
||||
|
import com.alibaba.fastjson2.JSON; |
||||
|
import com.yeguang.model.ModelPredictResponse; |
||||
|
import com.yeguang.model.RecordModel; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.net.http.HttpUtils; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class RecognitionModelClient { |
||||
|
@Inject("${recognition.model.url}") |
||||
|
private String modelUrl; |
||||
|
|
||||
|
public List<ModelPredictResponse> predict(File file, String modeName, float lou, float conf) { |
||||
|
try { |
||||
|
if (!file.exists()) { |
||||
|
throw new IllegalArgumentException("文件不存在: " + file.getAbsolutePath()); |
||||
|
} |
||||
|
|
||||
|
String response = HttpUtils.http(modelUrl + "/api/predict") |
||||
|
.multipart(true) // 开启 multipart/form-data 模式
|
||||
|
.data("image", file) |
||||
|
.data("model_name", modeName) |
||||
|
.data("lou", String.valueOf(lou)) |
||||
|
.data("conf", String.valueOf(conf)) |
||||
|
.post(); |
||||
|
return JSON.parseArray(response, ModelPredictResponse.class); |
||||
|
|
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public RecordModel models() { |
||||
|
try { |
||||
|
return HttpUtils.http(modelUrl + "/api/models").getAs(RecordModel.class); |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
public interface InfoService { |
||||
|
String hello(); |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
import org.noear.solon.annotation.Component; |
||||
|
|
||||
|
|
||||
|
@Component |
||||
|
public class InfoServiceImpl implements InfoService { |
||||
|
@Override |
||||
|
public String hello() { |
||||
|
return "hello solon"; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
|
||||
|
import com.yeguang.model.*; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import org.noear.solon.core.handle.Context; |
||||
|
|
||||
|
public interface RecordService { |
||||
|
|
||||
|
|
||||
|
String createRecordAsync(CreateRecordRequest request); |
||||
|
|
||||
|
Float getTaskProgress(String taskId); |
||||
|
|
||||
|
RecordDetail getRecordDetail(Long recordId); |
||||
|
|
||||
|
void exportRecord(Long recordId, Context context); |
||||
|
|
||||
|
PageResult<RecordItem> getPages(PageRequest pageRequest); |
||||
|
|
||||
|
void updateRecordContent(RecordContentEntity recordContentEntity); |
||||
|
|
||||
|
RecordModel getModels(); |
||||
|
|
||||
|
void updateScale(UpdateScaleRequest request); |
||||
|
} |
@ -0,0 +1,299 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
|
||||
|
import com.alibaba.excel.EasyExcel; |
||||
|
import com.alibaba.excel.ExcelWriter; |
||||
|
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; |
||||
|
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; |
||||
|
import com.alibaba.excel.write.metadata.WriteSheet; |
||||
|
import com.alibaba.excel.write.metadata.style.WriteCellStyle; |
||||
|
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; |
||||
|
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
|
import com.yeguang.mapper.RecordContentMapper; |
||||
|
import com.yeguang.mapper.RecordMapper; |
||||
|
import com.yeguang.model.*; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import com.yeguang.remote.RecognitionModelClient; |
||||
|
import com.yeguang.service.handler.RecordTaskHandler; |
||||
|
import com.yeguang.service.handler.RecordTaskManager; |
||||
|
import com.yeguang.service.handler.RecordTaskProcessor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.collections4.CollectionUtils; |
||||
|
import org.apache.poi.ss.usermodel.HorizontalAlignment; |
||||
|
import org.apache.poi.ss.usermodel.VerticalAlignment; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.core.handle.Context; |
||||
|
import org.noear.solon.data.annotation.Tran; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
import java.math.RoundingMode; |
||||
|
import java.net.URLEncoder; |
||||
|
import java.text.DecimalFormat; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class RecordServiceImpl implements RecordService { |
||||
|
@Inject("customThreadPool") |
||||
|
private ThreadPoolExecutor customThreadPool; |
||||
|
@Inject |
||||
|
private RecordTaskProcessor taskProcessor; |
||||
|
@Inject |
||||
|
private RecordMapper recordMapper; |
||||
|
@Inject |
||||
|
private RecordContentMapper recordContentMapper; |
||||
|
@Inject |
||||
|
private RecognitionModelClient recognitionModelClient; |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public String createRecordAsync(CreateRecordRequest request) { |
||||
|
String taskId = UUID.randomUUID().toString(); |
||||
|
RecordTaskHandler recordTaskHandler = new RecordTaskHandler(customThreadPool, taskProcessor); |
||||
|
recordTaskHandler.createTask(request, taskId); |
||||
|
RecordTaskManager.addRecordTask(taskId, recordTaskHandler); |
||||
|
return taskId; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public Float getTaskProgress(String taskId) { |
||||
|
RecordTaskHandler recordTask = RecordTaskManager.getRecordTask(taskId); |
||||
|
if (recordTask == null) { |
||||
|
return 100f; |
||||
|
} |
||||
|
if (recordTask.getRecordTask() == null) { |
||||
|
return 100f; |
||||
|
} |
||||
|
|
||||
|
return recordTask.getRecordTask().getProgress(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public RecordDetail getRecordDetail(Long recordId) { |
||||
|
return recordMapper.getRecordDetail(recordId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void exportRecord(Long recordId, Context context) { |
||||
|
RecordDetail recordDetail = this.getRecordDetail(recordId); |
||||
|
List<RecordContentEntity> content = recordDetail.getContent(); |
||||
|
if (CollectionUtils.isNotEmpty(content)) { |
||||
|
RecordExcel recordExcel = new RecordExcel(); |
||||
|
recordExcel.setImageNum(recordDetail.getImageNum()); |
||||
|
int beakNum = 0; |
||||
|
int rapeNum = 0; |
||||
|
int handleNum = 0; |
||||
|
int rapeMax = 0; |
||||
|
int rapeMin = 10000; |
||||
|
List<Double> avgBeakLen = new ArrayList<>(); |
||||
|
List<Double> avgRapeLen = new ArrayList<>(); |
||||
|
List<Double> avgHandleLen = new ArrayList<>(); |
||||
|
List<RecordContentExcel> recordContentExcelList = new ArrayList<>(); |
||||
|
List<OnceAbsoluteMergeStrategy> mergeStrategies = new ArrayList<>(); |
||||
|
int startRowIndex = 1; |
||||
|
int endRowIndex = 0; |
||||
|
//处理记录详情数据
|
||||
|
for (RecordContentEntity record : content) { |
||||
|
//统计各分类总数
|
||||
|
beakNum += record.getBeakNum(); |
||||
|
rapeNum += record.getSiliquaNum(); |
||||
|
handleNum += record.getHandleNum(); |
||||
|
if (record.getSiliquaNum() > rapeMax) { |
||||
|
rapeMax = record.getSiliquaNum(); |
||||
|
} |
||||
|
if (record.getSiliquaNum() < rapeMin) { |
||||
|
rapeMin = record.getSiliquaNum(); |
||||
|
} |
||||
|
List<Float> rapeLenList = new ArrayList<>(); |
||||
|
List<Float> beakLenList = new ArrayList<>(); |
||||
|
List<Float> handleLenList = new ArrayList<>(); |
||||
|
|
||||
|
List<ModelPredictResponse> recognitionData = record.getRecognitionData(); |
||||
|
Scale scaleDto = record.getScale(); |
||||
|
//把识别数据进行分类
|
||||
|
float size = scaleDto.getSize(); |
||||
|
int scale = scaleDto.getScale(); |
||||
|
for (ModelPredictResponse item : recognitionData) { |
||||
|
float len; |
||||
|
if (size > 0 && scale > 0) { |
||||
|
BigDecimal b = new BigDecimal(item.getLength() * size / scale); |
||||
|
len = b.setScale(2, RoundingMode.HALF_UP).floatValue(); |
||||
|
} else { |
||||
|
len = item.getLength(); |
||||
|
} |
||||
|
|
||||
|
switch (item.getClassify()) { |
||||
|
case 0: |
||||
|
rapeLenList.add(len); |
||||
|
break; |
||||
|
case 1: |
||||
|
beakLenList.add(len); |
||||
|
break; |
||||
|
default: |
||||
|
handleLenList.add(len); |
||||
|
} |
||||
|
} |
||||
|
int len = Math.max(rapeLenList.size(), Math.max(handleLenList.size(), beakLenList.size())); |
||||
|
//创建表格行记录
|
||||
|
for (int i = 0; i < len; i++) { |
||||
|
endRowIndex++; |
||||
|
RecordContentExcel contentExcel = new RecordContentExcel(); |
||||
|
//需要合并的列开始
|
||||
|
contentExcel.setName(record.getImageName()); |
||||
|
contentExcel.setBeakNum(record.getBeakNum()); |
||||
|
contentExcel.setRapeNum(record.getSiliquaNum()); |
||||
|
contentExcel.setHandleNum(record.getHandleNum()); |
||||
|
contentExcel.setScale(scale); |
||||
|
contentExcel.setConf(record.getConf()); |
||||
|
contentExcel.setIou(record.getIou()); |
||||
|
//需要合并的列结束
|
||||
|
recordContentExcelList.add(contentExcel); |
||||
|
if (i <= rapeLenList.size() - 1) { |
||||
|
contentExcel.setRapeLength(rapeLenList.get(i)); |
||||
|
} |
||||
|
if (i <= beakLenList.size() - 1) { |
||||
|
contentExcel.setBeakLength(beakLenList.get(i)); |
||||
|
} |
||||
|
if (i <= handleLenList.size() - 1) { |
||||
|
contentExcel.setHandleLength(handleLenList.get(i)); |
||||
|
} |
||||
|
} |
||||
|
//创建单元格的合并策略, 前7列数据需要合并
|
||||
|
for (int i = 0; i <= 6; i++) { |
||||
|
OnceAbsoluteMergeStrategy mergeStrategy = new OnceAbsoluteMergeStrategy( |
||||
|
startRowIndex, |
||||
|
endRowIndex, |
||||
|
i, i); |
||||
|
mergeStrategies.add(mergeStrategy); |
||||
|
} |
||||
|
|
||||
|
startRowIndex += len; |
||||
|
avgHandleLen.add(handleLenList.stream().mapToDouble(Float::doubleValue).average().orElse(0.0)); |
||||
|
avgRapeLen.add(rapeLenList.stream().mapToDouble(Float::doubleValue).average().orElse(0.0)); |
||||
|
avgBeakLen.add(beakLenList.stream().mapToDouble(Float::doubleValue).average().orElse(0.0)); |
||||
|
} |
||||
|
recordExcel.setBeakNum(beakNum); |
||||
|
recordExcel.setRapeNum(rapeNum); |
||||
|
recordExcel.setHandleNum(handleNum); |
||||
|
recordExcel.setMaxRapeNum(rapeMax); |
||||
|
recordExcel.setMinRapeNum(rapeMin); |
||||
|
DecimalFormat dfnum = new DecimalFormat("##0.00"); |
||||
|
float avgRapeNum = Float.parseFloat(dfnum.format((float) rapeNum / content.size())); |
||||
|
recordExcel.setAvgRapeNum(avgRapeNum); |
||||
|
double v = avgBeakLen.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); |
||||
|
BigDecimal b = new BigDecimal(v); |
||||
|
recordExcel.setAvgBeakLength(b.setScale(2, RoundingMode.HALF_UP).floatValue()); |
||||
|
double r = avgRapeLen.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); |
||||
|
BigDecimal b1 = new BigDecimal(r); |
||||
|
recordExcel.setAvgRapeLength(b1.setScale(2, RoundingMode.HALF_UP).floatValue()); |
||||
|
double h = avgHandleLen.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); |
||||
|
BigDecimal b2 = new BigDecimal(h); |
||||
|
recordExcel.setAvgHandleLength(b2.setScale(2, RoundingMode.HALF_UP).floatValue()); |
||||
|
try { |
||||
|
context.contentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
||||
|
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
|
||||
|
String fileName = URLEncoder.encode(recordDetail.getName(), "UTF-8").replaceAll("\\+", "%20"); |
||||
|
context.headerSet("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); |
||||
|
//创建 Cell 样式
|
||||
|
WriteCellStyle writeCellStyle = new WriteCellStyle(); |
||||
|
//设置垂直对齐方式
|
||||
|
writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
||||
|
//设置水平对齐方式
|
||||
|
writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); |
||||
|
ExcelWriter writer = EasyExcel.write(context.outputStream()).autoCloseStream(false).build(); |
||||
|
//总览表格的sheet
|
||||
|
WriteSheet recordSheet = EasyExcel.writerSheet("总览").head(RecordExcel.class).build(); |
||||
|
//详情表格的sheet
|
||||
|
ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet("详情") |
||||
|
.registerWriteHandler(new HorizontalCellStyleStrategy(null, writeCellStyle)) |
||||
|
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) |
||||
|
.head(RecordContentExcel.class); |
||||
|
//添加单元格合并策略
|
||||
|
for (OnceAbsoluteMergeStrategy mergeStrategy : mergeStrategies) { |
||||
|
sheetBuilder.registerWriteHandler(mergeStrategy); |
||||
|
} |
||||
|
WriteSheet contentSheet = sheetBuilder.build(); |
||||
|
writer.write(Collections.singletonList(recordExcel), recordSheet); |
||||
|
writer.write(recordContentExcelList, contentSheet); |
||||
|
writer.finish(); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
log.error("下载文件失败", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public PageResult<RecordItem> getPages(PageRequest pageRequest) { |
||||
|
Page<RecordItem> page = new Page(pageRequest.getPageNum(), pageRequest.getPageSize()); |
||||
|
Page<RecordItem> result = recordMapper.pageList(page, null); |
||||
|
return PageResult.of((int) result.getTotal(), (int) result.getSize(), (int) result.getCurrent(), result.getRecords()); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void updateRecordContent(RecordContentEntity recordContentEntity) { |
||||
|
recordContentMapper.updateById(recordContentEntity); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public RecordModel getModels() { |
||||
|
return recognitionModelClient.models(); |
||||
|
} |
||||
|
|
||||
|
@Tran |
||||
|
@Override |
||||
|
public void updateScale(UpdateScaleRequest request) { |
||||
|
QueryWrapper<RecordContentEntity> queryWrapper = Wrappers.query(); |
||||
|
RecordContentEntity own = null; |
||||
|
RecordContentEntity other = null; |
||||
|
switch (request.getScaleType()) { |
||||
|
case 1: |
||||
|
own = new RecordContentEntity(); |
||||
|
own.setScale(request.getScale()); |
||||
|
own.setScaleType(request.getScaleType()); |
||||
|
own.setId(request.getRecordContentId()); |
||||
|
other = new RecordContentEntity().setScale(request.getScale()); |
||||
|
queryWrapper.eq("record_id", request.getRecordId()); |
||||
|
queryWrapper.eq("scale_type", 3); |
||||
|
break; |
||||
|
case 2: |
||||
|
own = recordContentMapper.selectById(request.getRecordContentId()); |
||||
|
if (own.getScaleType() == 1) { |
||||
|
queryWrapper.eq("record_id", own.getRecordId()); |
||||
|
queryWrapper.eq("scale_type", 3); |
||||
|
other = new RecordContentEntity().setScaleType(0).setScale(null); |
||||
|
} |
||||
|
own.setScale(request.getScale()); |
||||
|
own.setScaleType(request.getScaleType()); |
||||
|
break; |
||||
|
case 3: |
||||
|
default: |
||||
|
own = new RecordContentEntity().setScaleType(3); |
||||
|
queryWrapper.eq("record_id", request.getRecordId()); |
||||
|
queryWrapper.eq("scale_type", 1); |
||||
|
RecordContentEntity recordContentEntity = recordContentMapper.selectOne(queryWrapper); |
||||
|
if (recordContentEntity != null) { |
||||
|
own.setScale(recordContentEntity.getScale()); |
||||
|
} |
||||
|
own.setId(request.getRecordContentId()); |
||||
|
|
||||
|
} |
||||
|
recordContentMapper.updateById(own); |
||||
|
if (other != null) { |
||||
|
recordContentMapper.update(other, queryWrapper); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
|
||||
|
import com.yeguang.model.LoginRequest; |
||||
|
import com.yeguang.model.LoginResponse; |
||||
|
import com.yeguang.model.UserRequest; |
||||
|
|
||||
|
public interface UserService { |
||||
|
LoginResponse login(LoginRequest loginBody); |
||||
|
|
||||
|
void register(UserRequest user); |
||||
|
|
||||
|
void approval(Long userId); |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
package com.yeguang.service; |
||||
|
|
||||
|
import cn.dev33.satoken.secure.BCrypt; |
||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||
|
import com.yeguang.common.BizCode; |
||||
|
import com.yeguang.common.BusinessException; |
||||
|
import com.yeguang.common.UserConstants; |
||||
|
import com.yeguang.mapper.UserMapper; |
||||
|
import com.yeguang.model.LoginRequest; |
||||
|
import com.yeguang.model.LoginResponse; |
||||
|
import com.yeguang.model.LoginUser; |
||||
|
import com.yeguang.model.UserRequest; |
||||
|
import com.yeguang.model.entity.UserEntity; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.ibatis.solon.annotation.Db; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class UserServiceImpl implements UserService { |
||||
|
@Db |
||||
|
private UserMapper userMapper; |
||||
|
// @Inject
|
||||
|
// private EmailUtil emailUtil;
|
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public LoginResponse login(LoginRequest loginBody) { |
||||
|
String phone = loginBody.getPhone(); |
||||
|
String password = loginBody.getPassword(); |
||||
|
LoginUser user = userMapper.selectLoginUserByPhone(phone); |
||||
|
if (user == null) { |
||||
|
throw new BusinessException(BizCode.USER_NOT_FOUND); |
||||
|
} |
||||
|
if (user.getStatus() != 1) { |
||||
|
throw new BusinessException(BizCode.USER_STATUS_ERROR); |
||||
|
} |
||||
|
boolean checkPw = BCrypt.checkpw(password, user.getPassword()); |
||||
|
if (!checkPw) { |
||||
|
throw new BusinessException(BizCode.PASSWORD_ERROR); |
||||
|
} |
||||
|
StpUtil.login(user.getUserId()); |
||||
|
user.setLoginTime(System.currentTimeMillis()); |
||||
|
StpUtil.getSession().set(UserConstants.SYS_SESSION, user); |
||||
|
LoginResponse loginVO = new LoginResponse(); |
||||
|
loginVO.setAccessToken(StpUtil.getTokenValue()); |
||||
|
loginVO.setExpireIn(StpUtil.getTokenTimeout()); |
||||
|
return loginVO; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void register(UserRequest user) { |
||||
|
UserEntity userEntity = new UserEntity() |
||||
|
.setCreateTime(LocalDateTime.now()) |
||||
|
.setStatus(2) |
||||
|
.setRealName(user.getRealName()) |
||||
|
.setUnitName(user.getUnitName()) |
||||
|
.setPhone(user.getPhone()) |
||||
|
.setPassword(BCrypt.hashpw(user.getPassword())); |
||||
|
userMapper.insert(userEntity); |
||||
|
// emailUtil.sendEmail(null, user.getPhone(), user.getUnitName(), user.getRealName());
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void approval(Long userId) { |
||||
|
UserEntity userEntity = userMapper.selectById(userId); |
||||
|
userEntity.setStatus(1); |
||||
|
userMapper.updateById(userEntity); |
||||
|
} |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
package com.yeguang.service.handler; |
||||
|
|
||||
|
|
||||
|
import com.yeguang.common.thread.MonitoredRunnable; |
||||
|
import com.yeguang.model.CreateRecordRequest; |
||||
|
import com.yeguang.model.LoginUser; |
||||
|
import com.yeguang.util.LoginUtil; |
||||
|
import lombok.Data; |
||||
|
import lombok.SneakyThrows; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.noear.solon.core.handle.UploadedFile; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.net.URL; |
||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
@Slf4j |
||||
|
public class RecordTaskHandler { |
||||
|
|
||||
|
private RecordTask recordTask; |
||||
|
private ThreadPoolExecutor theadPoolExecutor; |
||||
|
private RecordTaskProcessor processor; |
||||
|
|
||||
|
|
||||
|
|
||||
|
public RecordTaskHandler(ThreadPoolExecutor threadPoolExecutor, RecordTaskProcessor processor) { |
||||
|
this.theadPoolExecutor = threadPoolExecutor; |
||||
|
this.processor = processor; |
||||
|
} |
||||
|
|
||||
|
protected void processTask() { |
||||
|
theadPoolExecutor.execute(new MonitoredRunnable(() -> { |
||||
|
try { |
||||
|
processor.handle(recordTask); |
||||
|
log.info("任务完成,任务ID:{}", recordTask.getTaskId()); |
||||
|
} finally { |
||||
|
for (File file : recordTask.getFiles()) { |
||||
|
log.info("删除临时文件:{}", file.getAbsolutePath()); |
||||
|
if (file.exists()) { |
||||
|
if (!file.delete()) |
||||
|
log.error("删除临时文件失败:{}", file.getAbsolutePath()); |
||||
|
} |
||||
|
} |
||||
|
RecordTaskManager.removeRecordTask(recordTask.getTaskId()); |
||||
|
recordTask = null; |
||||
|
|
||||
|
} |
||||
|
})); |
||||
|
} |
||||
|
|
||||
|
@SneakyThrows |
||||
|
public void createTask(CreateRecordRequest request, String taskId) { |
||||
|
URL resource = RecordTaskHandler.class.getClassLoader().getResource(""); |
||||
|
assert resource != null; |
||||
|
String path = resource.getPath(); |
||||
|
File[] files = new File[request.getFiles().length]; |
||||
|
for (int i = 0; i < request.getFiles().length; i++) { |
||||
|
UploadedFile file = request.getFiles()[i]; |
||||
|
File file1 = new File(path + file.getName()); |
||||
|
file.transferTo(file1); |
||||
|
files[i] = file1; |
||||
|
} |
||||
|
|
||||
|
this.recordTask = new RecordTask() |
||||
|
.setFiles(files) |
||||
|
.setModelName(request.getModelName()) |
||||
|
.setIou(request.getIou()) |
||||
|
.setConf(request.getConf()) |
||||
|
.setProgress(5f) |
||||
|
.setTaskName(request.getTaskName()) |
||||
|
.setTaskId(taskId) |
||||
|
.setLoginUser(LoginUtil.getLoginUser()); |
||||
|
processTask(); |
||||
|
} |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
public static class RecordTask { |
||||
|
private String taskId; |
||||
|
private File[] files; |
||||
|
private String modelName; |
||||
|
private Float iou; |
||||
|
private Float conf; |
||||
|
private Float progress; |
||||
|
private String taskName; |
||||
|
private LoginUser loginUser; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package com.yeguang.service.handler; |
||||
|
|
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
public class RecordTaskManager { |
||||
|
|
||||
|
private static final ConcurrentHashMap<String, RecordTaskHandler> recordTasks = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public static void addRecordTask(String taskId, RecordTaskHandler handler) { |
||||
|
recordTasks.put(taskId, handler); |
||||
|
} |
||||
|
|
||||
|
public static RecordTaskHandler getRecordTask(String taskId) { |
||||
|
return recordTasks.get(taskId); |
||||
|
} |
||||
|
|
||||
|
public static void removeRecordTask(String taskId) { |
||||
|
recordTasks.remove(taskId); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,115 @@ |
|||||
|
package com.yeguang.service.handler; |
||||
|
|
||||
|
|
||||
|
import com.yeguang.mapper.RecordContentMapper; |
||||
|
import com.yeguang.mapper.RecordMapper; |
||||
|
import com.yeguang.model.ModelPredictResponse; |
||||
|
import com.yeguang.model.entity.RecordContentEntity; |
||||
|
import com.yeguang.model.entity.RecordEntity; |
||||
|
import com.yeguang.remote.RecognitionModelClient; |
||||
|
import com.yeguang.util.HuaweiObs; |
||||
|
import lombok.NonNull; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.collections4.CollectionUtils; |
||||
|
import org.apache.ibatis.solon.annotation.Db; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.data.annotation.Tran; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.math.BigDecimal; |
||||
|
import java.math.RoundingMode; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class RecordTaskProcessor { |
||||
|
@Db |
||||
|
private RecordMapper recordMapper; |
||||
|
@Db |
||||
|
private RecordContentMapper recordContentMapper; |
||||
|
@Inject |
||||
|
private RecognitionModelClient modelClient; |
||||
|
@Inject |
||||
|
private HuaweiObs huaweiObs; |
||||
|
|
||||
|
|
||||
|
@Tran |
||||
|
public void handle(@NonNull RecordTaskHandler.RecordTask recordTask) { |
||||
|
log.info("开始处理任务:{}", recordTask.getTaskId()); |
||||
|
float progress = 5f; |
||||
|
RecordEntity recordEntity = new RecordEntity().setName(recordTask.getTaskName()) |
||||
|
.setImageNum(recordTask.getFiles().length) |
||||
|
.setUserId(recordTask.getLoginUser().getUserId()) |
||||
|
.setCreateTime(LocalDateTime.now()) |
||||
|
.setUpdateTime(LocalDateTime.now()); |
||||
|
recordMapper.insert(recordEntity); |
||||
|
recordTask.setProgress(progress); |
||||
|
for (File file : recordTask.getFiles()) { |
||||
|
List<ModelPredictResponse> predict = modelClient.predict(file, recordTask.getModelName(), recordTask.getIou(), recordTask.getConf()); |
||||
|
int handleNum = 0; |
||||
|
int rapeNum = 0; |
||||
|
int beakNum = 0; |
||||
|
for (ModelPredictResponse item : predict) { |
||||
|
item.setLength(getMaxLength(item.getPoints())); |
||||
|
switch (item.getClassify()) { |
||||
|
case 0: |
||||
|
rapeNum++; |
||||
|
break; |
||||
|
case 1: |
||||
|
beakNum++; |
||||
|
break; |
||||
|
default: |
||||
|
handleNum++; |
||||
|
} |
||||
|
} |
||||
|
progress += (float) (100 / recordTask.getFiles().length - 10); |
||||
|
recordTask.setProgress(progress); |
||||
|
if (!CollectionUtils.isEmpty(predict)) { |
||||
|
String imageUrl; |
||||
|
try { |
||||
|
imageUrl = huaweiObs.uploadFile(file); |
||||
|
progress += 2f; |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
file.deleteOnExit(); |
||||
|
} |
||||
|
RecordContentEntity recordContentEntity = new RecordContentEntity() |
||||
|
.setRecordId(recordEntity.getId()).setImageUrl(imageUrl) |
||||
|
.setRecognitionData(predict) |
||||
|
.setImageName(file.getName()) |
||||
|
.setConf(recordTask.getConf()) |
||||
|
.setIou(recordTask.getIou()) |
||||
|
.setHandleNum(handleNum) |
||||
|
.setBeakNum(beakNum) |
||||
|
.setSiliquaNum(rapeNum); |
||||
|
recordContentMapper.insert(recordContentEntity); |
||||
|
progress += 3f; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
private static double calculateDistance(int x1, int y1, int x2, int y2) { |
||||
|
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); |
||||
|
} |
||||
|
|
||||
|
private float getMaxLength(int[][] points) { |
||||
|
// 计算四条边的长度
|
||||
|
double ab = calculateDistance(points[0][0], points[0][1], points[1][0], points[1][1]); // AB
|
||||
|
double bc = calculateDistance(points[1][0], points[1][1], points[2][0], points[2][1]); // BC
|
||||
|
double cd = calculateDistance(points[2][0], points[2][1], points[3][0], points[3][1]); // CD
|
||||
|
double da = calculateDistance(points[3][0], points[3][1], points[0][0], points[0][1]); // DA
|
||||
|
double max = Math.max(Math.max(ab, bc), Math.max(cd, da)); |
||||
|
BigDecimal b = new BigDecimal(max); |
||||
|
// 找出最大边长
|
||||
|
return b.setScale(2, RoundingMode.HALF_UP).floatValue(); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package com.yeguang.util; |
||||
|
|
||||
|
import cn.hutool.core.convert.Convert; |
||||
|
import cn.hutool.core.util.ObjectUtil; |
||||
|
import com.alibaba.excel.converters.Converter; |
||||
|
import com.alibaba.excel.enums.CellDataTypeEnum; |
||||
|
import com.alibaba.excel.metadata.GlobalConfiguration; |
||||
|
import com.alibaba.excel.metadata.data.ReadCellData; |
||||
|
import com.alibaba.excel.metadata.data.WriteCellData; |
||||
|
import com.alibaba.excel.metadata.property.ExcelContentProperty; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.math.BigDecimal; |
||||
|
|
||||
|
/** |
||||
|
* 大数值转换 |
||||
|
* Excel 数值长度位15位 大于15位的数值转换位字符串 |
||||
|
* |
||||
|
* @author Lion Li |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class ExcelBigNumberConvert implements Converter<Long> { |
||||
|
|
||||
|
@Override |
||||
|
public Class<Long> supportJavaTypeKey() { |
||||
|
return Long.class; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public CellDataTypeEnum supportExcelTypeKey() { |
||||
|
return CellDataTypeEnum.STRING; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { |
||||
|
return Convert.toLong(cellData.getData()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { |
||||
|
if (ObjectUtil.isNotNull(object)) { |
||||
|
String str = Convert.toStr(object); |
||||
|
if (str.length() > 15) { |
||||
|
return new WriteCellData<>(str); |
||||
|
} |
||||
|
} |
||||
|
WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object)); |
||||
|
cellData.setType(CellDataTypeEnum.NUMBER); |
||||
|
return cellData; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,303 @@ |
|||||
|
package com.yeguang.util; |
||||
|
|
||||
|
import com.alibaba.fastjson2.JSON; |
||||
|
import com.obs.services.ObsClient; |
||||
|
import com.obs.services.exception.ObsException; |
||||
|
import com.obs.services.model.*; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.io.IOUtils; |
||||
|
import org.noear.solon.annotation.Component; |
||||
|
import org.noear.solon.annotation.Inject; |
||||
|
import org.noear.solon.core.handle.UploadedFile; |
||||
|
|
||||
|
import java.io.ByteArrayInputStream; |
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.Calendar; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
/** |
||||
|
* @author Lenovo |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class HuaweiObs { |
||||
|
|
||||
|
//Access Key Id
|
||||
|
//private String ak = "Access Key Id";
|
||||
|
|
||||
|
//Secret Access Key
|
||||
|
//private String sk = "Secret Access Key";
|
||||
|
|
||||
|
//桶名称
|
||||
|
private String bucketName = "jg-iot"; |
||||
|
|
||||
|
// 终端节点访问Endpoint
|
||||
|
//private String endpoint = "Endpoint";
|
||||
|
|
||||
|
// 文件目录
|
||||
|
private String prifix = "/recognition"; |
||||
|
|
||||
|
// 访问域名 在域名后面或文件目录前加“/”
|
||||
|
private String path = "/recognition/"; |
||||
|
|
||||
|
|
||||
|
@Inject |
||||
|
private ObsClient obsClient; |
||||
|
|
||||
|
/** |
||||
|
* 文件上传 |
||||
|
* |
||||
|
* @param file |
||||
|
* @return |
||||
|
* @throws IOException |
||||
|
*/ |
||||
|
public String upload(UploadedFile file) throws Exception { |
||||
|
//ObsClient obsClient = null;
|
||||
|
|
||||
|
Calendar cal = Calendar.getInstance(); |
||||
|
int year = cal.get(Calendar.YEAR); |
||||
|
int month = cal.get(Calendar.MONTH); |
||||
|
int day = cal.get(Calendar.DATE); |
||||
|
|
||||
|
String fileName = Md5Util.calculateMD5(file.getContent()); |
||||
|
|
||||
|
String objectName = prifix + "/" + year + "/" + month + "/" + day + "/" + fileName; |
||||
|
//obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
PutObjectResult response = obsClient.putObject(bucketName, objectName, file.getContent()); |
||||
|
log.info(JSON.toJSONString(response)); |
||||
|
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
||||
|
int statusCode = response.getStatusCode(); |
||||
|
|
||||
|
if (200 == statusCode) { |
||||
|
|
||||
|
return response.getObjectUrl(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件--流式 |
||||
|
* |
||||
|
* @param fileName 上传文件名称 |
||||
|
* @param is 文件流 |
||||
|
* @return |
||||
|
*/ |
||||
|
public String uploadFile(String fileName, InputStream is) { |
||||
|
try { |
||||
|
if (is.available() == 0) { |
||||
|
log.error("InputStream is empty."); |
||||
|
return null; |
||||
|
} |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
|
||||
|
Calendar cal = Calendar.getInstance(); |
||||
|
int year = cal.get(Calendar.YEAR); |
||||
|
int month = cal.get(Calendar.MONTH); |
||||
|
int day = cal.get(Calendar.DATE); |
||||
|
String objectName = prifix + "/" + year + "/" + month + "/" + day + "/" + fileName; |
||||
|
PutObjectResult response = obsClient.putObject(bucketName, objectName, is); |
||||
|
log.info(JSON.toJSONString(response)); |
||||
|
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
||||
|
int statusCode = response.getStatusCode(); |
||||
|
|
||||
|
if (200 == statusCode) { |
||||
|
|
||||
|
return response.getObjectUrl(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public String uploadFile(File file) { |
||||
|
|
||||
|
try { |
||||
|
String subbfix = file.getName().substring(file.getName().indexOf(".")); |
||||
|
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + subbfix; |
||||
|
Calendar cal = Calendar.getInstance(); |
||||
|
int year = cal.get(Calendar.YEAR); |
||||
|
int month = cal.get(Calendar.MONTH); |
||||
|
int day = cal.get(Calendar.DATE); |
||||
|
String objectName = prifix + "/" + year + "/" + month + "/" + fileName; |
||||
|
PutObjectRequest putObjectRequest = new PutObjectRequest(); |
||||
|
putObjectRequest.setFile(file); |
||||
|
putObjectRequest.setBucketName(bucketName); |
||||
|
putObjectRequest.setObjectKey(objectName); |
||||
|
PutObjectResult response = obsClient.putObject(putObjectRequest); |
||||
|
log.info(JSON.toJSONString(response)); |
||||
|
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
||||
|
int statusCode = response.getStatusCode(); |
||||
|
if (200 == statusCode) { |
||||
|
return response.getObjectUrl(); |
||||
|
} |
||||
|
} catch (ObsException e) { |
||||
|
log.error("文件上传失败:http code={},error code={},error message={}", e.getResponseCode(), e.getErrorCode(), e.getErrorMessage(), e); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件--字节数组 |
||||
|
* |
||||
|
* @param fileName 上传文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @param is 文件流 |
||||
|
* @return |
||||
|
*/ |
||||
|
public boolean uploadFileByte(String fileName, FileType fileType, byte[] is) { |
||||
|
//ObsClient obsClient = null;
|
||||
|
try { |
||||
|
String objectName = fileType.getType().concat("/").concat(fileName); |
||||
|
//obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
HeaderResponse response = obsClient.putObject(bucketName, objectName, new ByteArrayInputStream(is)); |
||||
|
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
||||
|
int statusCode = response.getStatusCode(); |
||||
|
if (200 == statusCode) { |
||||
|
return true; |
||||
|
} |
||||
|
obsClient.close(); |
||||
|
} catch (IOException e) { |
||||
|
log.info("文件上传失败:{}", e.getMessage(), e); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 下载文件 |
||||
|
* |
||||
|
* @param fileName 文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @return |
||||
|
*/ |
||||
|
public String getDownloadUrl(String fileName, FileType fileType) { |
||||
|
//ObsClient obsClient = null;
|
||||
|
//obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
// URL有效期,3600秒.5分钟
|
||||
|
long expireSeconds = 3600L; |
||||
|
String objectName = fileType.getType().concat("/").concat(fileName); |
||||
|
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds); |
||||
|
request.setBucketName(bucketName); |
||||
|
request.setObjectKey(objectName); |
||||
|
TemporarySignatureResponse response = obsClient.createTemporarySignature(request); |
||||
|
return response.getSignedUrl(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取上传地址 |
||||
|
* |
||||
|
* @param fileName 文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @return |
||||
|
*/ |
||||
|
public String getUploadUrl(String fileName, FileType fileType) { |
||||
|
try { |
||||
|
// 创建ObsClient实例
|
||||
|
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
// URL有效期,3600秒
|
||||
|
long expireSeconds = 3600L; |
||||
|
Map<String, String> headers = new HashMap<String, String>(); |
||||
|
headers.put("Content-Type", "application/octet-stream"); |
||||
|
String objectName = fileType.getType().concat("/").concat(fileName); |
||||
|
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds); |
||||
|
request.setBucketName(bucketName); |
||||
|
request.setObjectKey(objectName); |
||||
|
request.setHeaders(headers); |
||||
|
TemporarySignatureResponse response = obsClient.createTemporarySignature(request); |
||||
|
return response.getSignedUrl(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("获取上传地址异常:{}", e.getMessage(), e); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 下载文件返回字节数组 |
||||
|
* |
||||
|
* @param fileName 文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @return |
||||
|
*/ |
||||
|
public byte[] downloadFile(String fileName, FileType fileType) { |
||||
|
try { |
||||
|
InputStream inputStream = downloadFileInputStream(fileName, fileType); |
||||
|
byte[] bytes = IOUtils.toByteArray(inputStream); |
||||
|
return bytes; |
||||
|
} catch (Exception e) { |
||||
|
log.error("下载文件异常:{}", e.getMessage(), e); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传视频 |
||||
|
* |
||||
|
* @param fileName 文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @return |
||||
|
*/ |
||||
|
public boolean uploadFileVideo(String fileName, FileType fileType, InputStream is) { |
||||
|
try { |
||||
|
String objectName = fileType.getType().concat("/").concat(fileName); |
||||
|
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
// 添加 ContentType (添加后可在浏览器中直接浏览,而非下载链接)
|
||||
|
obsClient.putObject(bucketName, objectName, is); |
||||
|
obsClient.close(); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
log.error("上传视频文件异常:{}", e.getMessage(), e); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 下载文件返回流式 |
||||
|
* |
||||
|
* @param fileName 文件名称 |
||||
|
* @param fileType 文件路径 |
||||
|
* @return |
||||
|
*/ |
||||
|
public InputStream downloadFileInputStream(String fileName, FileType fileType) { |
||||
|
try { |
||||
|
String objectName = fileType.getType().concat("/").concat(fileName); |
||||
|
// 用户拿到STS临时凭证后,通过其中的安全令牌(SecurityToken)和临时访问密钥(AccessKeyId和AccessKeySecret)生成OSSClient。
|
||||
|
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
||||
|
ObsObject obsObject = obsClient.getObject(bucketName, objectName); |
||||
|
obsClient.close(); |
||||
|
return obsObject.getObjectContent(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("下载文件异常:{}", e.getMessage(), e); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public enum FileType { |
||||
|
TEST("TEST", "测试"); |
||||
|
|
||||
|
private String type; |
||||
|
private String desc; |
||||
|
|
||||
|
FileType(String type, String desc) { |
||||
|
this.type = type; |
||||
|
this.desc = desc; |
||||
|
} |
||||
|
|
||||
|
public String getType() { |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
public String getDesc() { |
||||
|
return desc; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,14 @@ |
|||||
|
package com.yeguang.util; |
||||
|
|
||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||
|
import com.yeguang.common.UserConstants; |
||||
|
import com.yeguang.model.LoginUser; |
||||
|
|
||||
|
public class LoginUtil { |
||||
|
|
||||
|
|
||||
|
public static LoginUser getLoginUser() { |
||||
|
return (LoginUser) StpUtil.getSession().get(UserConstants.SYS_SESSION); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package com.yeguang.util; |
||||
|
|
||||
|
import java.io.InputStream; |
||||
|
import java.security.MessageDigest; |
||||
|
|
||||
|
public class Md5Util { |
||||
|
public static String calculateMD5(InputStream inputStream) throws Exception { |
||||
|
// 创建 MD5 消息摘要实例
|
||||
|
MessageDigest digest = MessageDigest.getInstance("MD5"); |
||||
|
|
||||
|
byte[] buffer = new byte[1024]; // 缓冲区大小
|
||||
|
int bytesRead; |
||||
|
|
||||
|
// 读取 InputStream 并更新 MD5 摘要
|
||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) { |
||||
|
digest.update(buffer, 0, bytesRead); |
||||
|
} |
||||
|
|
||||
|
// 获取 MD5 摘要结果
|
||||
|
byte[] md5Bytes = digest.digest(); |
||||
|
|
||||
|
// 转换为 16 进制字符串
|
||||
|
StringBuilder hexString = new StringBuilder(); |
||||
|
for (byte b : md5Bytes) { |
||||
|
String hex = Integer.toHexString(0xff & b); |
||||
|
if (hex.length() == 1) { |
||||
|
hexString.append('0'); |
||||
|
} |
||||
|
hexString.append(hex); |
||||
|
} |
||||
|
|
||||
|
return hexString.toString(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,146 @@ |
|||||
|
server: |
||||
|
port: 9198 |
||||
|
request: |
||||
|
max-body-size: 10MB |
||||
|
max-file-size: 10MB |
||||
|
max-header-size: 8kb |
||||
|
solon.app: |
||||
|
name: solon-web-test |
||||
|
solon.dataSources: |
||||
|
db1!: |
||||
|
class: com.zaxxer.hikari.HikariDataSource |
||||
|
jdbc-url: jdbc:mysql://101.34.243.138:7306/siliqua_recognition?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true |
||||
|
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
|
username: ams |
||||
|
password: ams@1234 |
||||
|
maximum-pool-size: 10 |
||||
|
minimum-idle: 5 |
||||
|
idle-timeout: 30000 |
||||
|
connection-timeout: 30000 |
||||
|
max-lifetime: 1800000 |
||||
|
pool-name: siliqua-recognition |
||||
|
|
||||
|
#app: |
||||
|
# redis: |
||||
|
# server: 192.168.10.111 |
||||
|
# port: 6379 |
||||
|
# db: 9 |
||||
|
# password: ams@1234 |
||||
|
app.redis: |
||||
|
config: | |
||||
|
singleServerConfig: |
||||
|
address: "redis://192.168.10.111:6379" |
||||
|
database: 9 |
||||
|
|
||||
|
mybatis.db1: |
||||
|
typeAliases: |
||||
|
- "com.yeguang.model.entity" |
||||
|
mappers: |
||||
|
- "com.yeguang.mapper.*Mapper" |
||||
|
- "classpath:mapper/*.xml" |
||||
|
configuration: |
||||
|
mapperVerifyEnabled: false |
||||
|
cacheEnabled: false |
||||
|
map-underscore-to-camel-case: true |
||||
|
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl |
||||
|
global-config: |
||||
|
db-config: |
||||
|
id-type: auto |
||||
|
field-strategy: not_null |
||||
|
update-strategy: not_null |
||||
|
banner: false |
||||
|
|
||||
|
#分页组件的配置 |
||||
|
pagehelper: |
||||
|
offsetAsPageNum: true |
||||
|
rowBoundsWithCount: true |
||||
|
pageSizeZero: true |
||||
|
reasonable: false |
||||
|
params: pageNum=pageHelperStart;pageSize=pageHelperRows; |
||||
|
supportMethodsArguments: false |
||||
|
|
||||
|
# sa-token配置 |
||||
|
sa-token: |
||||
|
# token名称 (同时也是cookie名称) |
||||
|
token-name: Authorization |
||||
|
token-prefix: Bearer |
||||
|
# token有效期,单位s 默认30天, -1代表永不过期 |
||||
|
timeout: 2592000 |
||||
|
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 |
||||
|
active-timeout: -1 |
||||
|
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) |
||||
|
is-concurrent: true #旧版曾用名(allow-concurrent-login) |
||||
|
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) |
||||
|
is-share: true |
||||
|
# token风格 |
||||
|
token-style: uuid |
||||
|
# 是否输出操作日志 |
||||
|
is-log: false |
||||
|
is-print: false |
||||
|
jwt-secret-key: asdfghjklzxcvbnmqwertyuiop1234567890 |
||||
|
|
||||
|
|
||||
|
knife4j: |
||||
|
enable: true |
||||
|
basic: |
||||
|
enable: true |
||||
|
username: admin |
||||
|
password: 123456 |
||||
|
setting: |
||||
|
enableOpenApi: false |
||||
|
enableSwaggerModels: false |
||||
|
enableFooter: false |
||||
|
|
||||
|
|
||||
|
solon: |
||||
|
logging: |
||||
|
appender: |
||||
|
console: |
||||
|
level: TRACE |
||||
|
enable: true #是否启用 |
||||
|
pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(${PID:-}) - %X{X-TraceId:-} - %-15([%15.15thread]) %-56(%cyan(%-40.40logger{39}[%L])) : %msg%n" |
||||
|
file: |
||||
|
name: "logs/${solon.app.name}" |
||||
|
rolling: "logs/${solon.app.name}_%d{yyyy-MM-dd}_%i.log" |
||||
|
level: INFO |
||||
|
enable: true #是否启用 |
||||
|
extension: ".log" #v2.2.18 后支持(例:.log, .log.gz, .log.zip) |
||||
|
maxFileSize: "10 MB" |
||||
|
maxHistory: "7" |
||||
|
pattern: "%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level ${PID:-} - %X{X-TraceId:-} - %-15([%15.15thread]) %-56(%-40.40logger{39}[%L]) : %msg%n" |
||||
|
cloud: |
||||
|
level: INFO |
||||
|
enable: true #是否启用 |
||||
|
logger: |
||||
|
"root": #默认记录器配置 |
||||
|
level: INFO |
||||
|
"com.zaxxer.hikari": |
||||
|
level: WARN |
||||
|
"com.yeguang.mapper": |
||||
|
level: DEBUG |
||||
|
|
||||
|
|
||||
|
|
||||
|
recognition: |
||||
|
model: |
||||
|
url: http://api.camacloud.org.cn/recognition |
||||
|
|
||||
|
|
||||
|
thread: |
||||
|
enabled: true |
||||
|
pool: |
||||
|
core-pool-size: 5 |
||||
|
max-pool-size: 8 |
||||
|
queue-capacity: 500 |
||||
|
thread-name: siliqua-recognition |
||||
|
task-threshold: 300000 |
||||
|
queue-size-threshold: 200 |
||||
|
queue-time-threshold: 1000 |
||||
|
|
||||
|
huawei: |
||||
|
obs: |
||||
|
end-point: "obs.cn-east-2.myhuaweicloud.com" |
||||
|
ak: "NMUAWG3TN50OSJESCIRV" |
||||
|
sk: "qG0VFwhiTAtiCuUaMAxJL3zsusp3W4GHs7THR9pC" |
||||
|
bucket-name: iot |
||||
|
path-prefix: recognition |
@ -0,0 +1,27 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="com.yeguang.mapper.RecordContentMapper"> |
||||
|
<resultMap id="RecordContentMap" type="com.yeguang.model.entity.RecordContentEntity"> |
||||
|
<id column="id" property="id"/> |
||||
|
<result column="record_id" property="recordId"/> |
||||
|
<result column="image_name" property="imageName"/> |
||||
|
<result column="image_url" property="imageUrl"/> |
||||
|
<result column="recognition_data" property="recognitionData" |
||||
|
typeHandler="com.yeguang.handler.ModelPredictResponseListTypeHandler" |
||||
|
javaType="java.util.List"/> |
||||
|
<result column="siliqua_num" property="siliquaNum"/> |
||||
|
<result column="handle_num" property="handleNum"/> |
||||
|
<result column="beak_num" property="beakNum"/> |
||||
|
<result column="scale_type" property="scaleType"/> |
||||
|
<result column="scale" property="scale" javaType="com.yeguang.model.Scale" |
||||
|
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> |
||||
|
<result column="conf" property="conf"/> |
||||
|
<result column="iou" property="iou"/> |
||||
|
</resultMap> |
||||
|
<select id="selectListByRecordId" resultMap="RecordContentMap"> |
||||
|
select * |
||||
|
from record_content |
||||
|
where record_id = #{recordId} |
||||
|
order by id asc |
||||
|
</select> |
||||
|
</mapper> |
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="com.yeguang.mapper.RecordMapper"> |
||||
|
<resultMap id="RecordDetailMap" type="com.yeguang.model.RecordDetail"> |
||||
|
<id property="id" column="id"/> |
||||
|
<collection property="content" javaType="java.util.List" column="id" |
||||
|
select="com.yeguang.mapper.RecordContentMapper.selectListByRecordId"/> |
||||
|
|
||||
|
</resultMap> |
||||
|
|
||||
|
<select id="getRecordDetail" resultMap="RecordDetailMap"> |
||||
|
select * |
||||
|
from record_info |
||||
|
where id = #{id} |
||||
|
</select> |
||||
|
<select id="pageList" resultType="com.yeguang.model.RecordItem"> |
||||
|
select r.id as recordId, |
||||
|
r.name as recordName, |
||||
|
r.user_id, |
||||
|
r.image_num as imageNum, |
||||
|
r.create_time as createTime, |
||||
|
u.real_name as userName, |
||||
|
u.unit_name as unitName, |
||||
|
u.phone |
||||
|
from record_info r |
||||
|
left join siliqua_user u on r.user_id = u.id |
||||
|
order by r.create_time desc |
||||
|
|
||||
|
</select> |
||||
|
</mapper> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="com.yeguang.mapper.UserMapper"> |
||||
|
|
||||
|
|
||||
|
<select id="selectLoginUserByPhone" resultType="com.yeguang.model.LoginUser"> |
||||
|
select id as user_id, |
||||
|
real_name, |
||||
|
phone, |
||||
|
create_time, |
||||
|
password, |
||||
|
unit_name, |
||||
|
status |
||||
|
from siliqua_user |
||||
|
where phone = #{phone} |
||||
|
and status != 0 |
||||
|
</select> |
||||
|
|
||||
|
|
||||
|
</mapper> |
Loading…
Reference in new issue