feat:初始化项目结构
This commit is contained in:
commit
e764df736a
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
/doc/
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
/logs/
|
||||
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
16
.idea/gradle.xml
Normal file
16
.idea/gradle.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
204
.idea/intellij-javadocs-4.0.1.xml
Normal file
204
.idea/intellij-javadocs-4.0.1.xml
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaDocConfiguration">
|
||||
<GENERAL>
|
||||
<MODE>UPDATE</MODE>
|
||||
<OVERRIDDEN_METHODS>false</OVERRIDDEN_METHODS>
|
||||
<SPLITTED_CLASS_NAME>true</SPLITTED_CLASS_NAME>
|
||||
<LEVELS>
|
||||
<LEVEL>TYPE</LEVEL>
|
||||
<LEVEL>FIELD</LEVEL>
|
||||
<LEVEL>METHOD</LEVEL>
|
||||
</LEVELS>
|
||||
<VISIBILITIES>
|
||||
<VISIBILITY>PUBLIC</VISIBILITY>
|
||||
<VISIBILITY>PROTECTED</VISIBILITY>
|
||||
<VISIBILITY>DEFAULT</VISIBILITY>
|
||||
</VISIBILITIES>
|
||||
</GENERAL>
|
||||
<TEMPLATES>
|
||||
<CLASSES>
|
||||
<CLASS>
|
||||
<KEY>^.*(public|protected|private)*.+interface\s+\w+.*</KEY>
|
||||
<VALUE>/**\n
|
||||
* The interface ${name}.\n
|
||||
<#if element.typeParameters?has_content> * \n
|
||||
</#if>
|
||||
<#list element.typeParameters as typeParameter>
|
||||
* @param <${typeParameter.name}> the type parameter\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</CLASS>
|
||||
<CLASS>
|
||||
<KEY>^.*(public|protected|private)*.+enum\s+\w+.*</KEY>
|
||||
<VALUE>/**\n
|
||||
* The enum ${name}.\n
|
||||
*/</VALUE>
|
||||
</CLASS>
|
||||
<CLASS>
|
||||
<KEY>^.*(public|protected|private)*.+class\s+\w+.*</KEY>
|
||||
<VALUE>/**\n
|
||||
* The type ${name}.\n
|
||||
<#if element.typeParameters?has_content> * \n
|
||||
</#if>
|
||||
<#list element.typeParameters as typeParameter>
|
||||
* @param <${typeParameter.name}> the type parameter\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</CLASS>
|
||||
<CLASS>
|
||||
<KEY>.+</KEY>
|
||||
<VALUE>/**\n
|
||||
* The type ${name}.\n
|
||||
*/</VALUE>
|
||||
</CLASS>
|
||||
</CLASSES>
|
||||
<CONSTRUCTORS>
|
||||
<CONSTRUCTOR>
|
||||
<KEY>.+</KEY>
|
||||
<VALUE>/**\n
|
||||
* Instantiates a new ${name}.\n
|
||||
<#if element.parameterList.parameters?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.parameterList.parameters as parameter>
|
||||
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
|
||||
</#list>
|
||||
<#if element.throwsList.referenceElements?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.throwsList.referenceElements as exception>
|
||||
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</CONSTRUCTOR>
|
||||
</CONSTRUCTORS>
|
||||
<METHODS>
|
||||
<METHOD>
|
||||
<KEY>^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+</KEY>
|
||||
<VALUE>/**\n
|
||||
* Gets ${partName}.\n
|
||||
<#if element.typeParameters?has_content> * \n
|
||||
</#if>
|
||||
<#list element.typeParameters as typeParameter>
|
||||
* @param <${typeParameter.name}> the type parameter\n
|
||||
</#list>
|
||||
<#if element.parameterList.parameters?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.parameterList.parameters as parameter>
|
||||
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
|
||||
</#list>
|
||||
<#if isNotVoid>
|
||||
*\n
|
||||
* @return the ${partName}\n
|
||||
</#if>
|
||||
<#if element.throwsList.referenceElements?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.throwsList.referenceElements as exception>
|
||||
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</METHOD>
|
||||
<METHOD>
|
||||
<KEY>^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+</KEY>
|
||||
<VALUE>/**\n
|
||||
* Sets ${partName}.\n
|
||||
<#if element.typeParameters?has_content> * \n
|
||||
</#if>
|
||||
<#list element.typeParameters as typeParameter>
|
||||
* @param <${typeParameter.name}> the type parameter\n
|
||||
</#list>
|
||||
<#if element.parameterList.parameters?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.parameterList.parameters as parameter>
|
||||
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
|
||||
</#list>
|
||||
<#if isNotVoid>
|
||||
*\n
|
||||
* @return the ${partName}\n
|
||||
</#if>
|
||||
<#if element.throwsList.referenceElements?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.throwsList.referenceElements as exception>
|
||||
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</METHOD>
|
||||
<METHOD>
|
||||
<KEY>^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+</KEY>
|
||||
<VALUE>/**\n
|
||||
* The entry point of application.\n
|
||||
|
||||
<#if element.parameterList.parameters?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
* @param ${element.parameterList.parameters[0].name} the input arguments\n
|
||||
<#if element.throwsList.referenceElements?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.throwsList.referenceElements as exception>
|
||||
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</METHOD>
|
||||
<METHOD>
|
||||
<KEY>.+</KEY>
|
||||
<VALUE>/**\n
|
||||
* ${name}<#if isNotVoid> ${return}</#if>.\n
|
||||
<#if element.typeParameters?has_content> * \n
|
||||
</#if>
|
||||
<#list element.typeParameters as typeParameter>
|
||||
* @param <${typeParameter.name}> the type parameter\n
|
||||
</#list>
|
||||
<#if element.parameterList.parameters?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.parameterList.parameters as parameter>
|
||||
* @param ${parameter.name} the ${paramNames[parameter.name]}\n
|
||||
</#list>
|
||||
<#if isNotVoid>
|
||||
*\n
|
||||
* @return the ${return}\n
|
||||
</#if>
|
||||
<#if element.throwsList.referenceElements?has_content>
|
||||
*\n
|
||||
</#if>
|
||||
<#list element.throwsList.referenceElements as exception>
|
||||
* @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n
|
||||
</#list>
|
||||
*/</VALUE>
|
||||
</METHOD>
|
||||
</METHODS>
|
||||
<FIELDS>
|
||||
<FIELD>
|
||||
<KEY>^.*(public|protected|private)*.+static.*(\w\s\w)+.+</KEY>
|
||||
<VALUE>/**\n
|
||||
* The constant ${element.getName()}.\n
|
||||
*/</VALUE>
|
||||
</FIELD>
|
||||
<FIELD>
|
||||
<KEY>^.*(public|protected|private)*.*(\w\s\w)+.+</KEY>
|
||||
<VALUE>/**\n
|
||||
<#if element.parent.isInterface()>
|
||||
* The constant ${element.getName()}.\n
|
||||
<#else>
|
||||
* The ${name}.\n
|
||||
</#if> */</VALUE>
|
||||
</FIELD>
|
||||
<FIELD>
|
||||
<KEY>.+</KEY>
|
||||
<VALUE>/**\n
|
||||
<#if element.parent.isEnum()>
|
||||
*${name} ${typeName}.\n
|
||||
<#else>
|
||||
* The ${name}.\n
|
||||
</#if>*/</VALUE>
|
||||
</FIELD>
|
||||
</FIELDS>
|
||||
</TEMPLATES>
|
||||
</component>
|
||||
</project>
|
||||
19
.idea/misc.xml
Normal file
19
.idea/misc.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ASMIdeaPluginConfiguration">
|
||||
<asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" />
|
||||
<groovy codeStyle="LEGACY" />
|
||||
</component>
|
||||
<component name="EntryPointsManager">
|
||||
<writeAnnotations>
|
||||
<writeAnnotation name="lombok.Getter" />
|
||||
</writeAnnotations>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal file
|
|
@ -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>
|
||||
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
76
README.MD
Normal file
76
README.MD
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# 查重逻辑划分
|
||||
|
||||
### **1. 核心模块划分**
|
||||
| 模块名 | 职责 | 包含内容 |
|
||||
|--------|------|----------|
|
||||
| **`core`** (核心逻辑) | 处理核心查重算法 | 文件遍历、哈希计算、查重逻辑 |
|
||||
| **`io`** (I/O处理) | 文件系统交互 | 文件读取、内存映射、并行I/O优化 |
|
||||
| **`model`** (数据模型) | 定义数据结构 | 文件元数据、哈希结果、查重报告 |
|
||||
| **`ui`** (用户界面) | GUI展示 | JavaFX面板、控制器、事件处理 |
|
||||
| **`util`** (工具类) | 公用功能 | 哈希工具、进度计算、日志记录 |
|
||||
|
||||
---
|
||||
|
||||
### **2. 模块依赖关系**
|
||||
```mermaid
|
||||
graph TD
|
||||
ui --> core
|
||||
ui --> model
|
||||
core --> io
|
||||
core --> model
|
||||
core --> util
|
||||
io --> util
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3. 进阶模块(可选扩展)**
|
||||
| 模块名 | 用途 |
|
||||
|--------|------|
|
||||
| **`api`** | 提供REST接口供其他系统调用 |
|
||||
| **`cli`** | 命令行界面(脱离JavaFX使用) |
|
||||
| **`plugin`** | 支持扩展哈希算法或存储后端 |
|
||||
|
||||
---
|
||||
|
||||
### **4. 目录结构示例**
|
||||
```
|
||||
src/
|
||||
├── main/
|
||||
│ ├── java/
|
||||
│ │ ├── top.r3.doc/
|
||||
│ │ │ ├── core/
|
||||
│ │ │ ├── io/
|
||||
│ │ │ ├── model/
|
||||
│ │ │ ├── ui/
|
||||
│ │ │ ├── util/
|
||||
│ │ │ └── Main.java (主入口)
|
||||
│ └── resources/ (FXML/CSS)
|
||||
└── test/ (对应模块的单元测试)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **5. 关键设计原则**
|
||||
1. **单一职责**:每个模块只做一件事(如`io`模块只处理I/O)
|
||||
2. **依赖倒置**:高层模块(如`ui`)依赖抽象(接口),不依赖具体实现
|
||||
3. **可测试性**:核心逻辑与UI分离,便于单元测试
|
||||
4. **扩展性**:通过策略模式(如`FileHashStrategy`)支持新哈希算法
|
||||
|
||||
---
|
||||
|
||||
### **6. 优化后的查重流程**
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
UI->>+Core: 启动查重(rootPath)
|
||||
Core->>+IO: 遍历文件(rootPath)
|
||||
IO-->>-Core: 返回文件列表
|
||||
loop 每个文件
|
||||
Core->>+IO: 读取文件内容
|
||||
IO-->>-Core: 返回字节数据
|
||||
Core->>+Util: 计算哈希
|
||||
Util-->>-Core: 返回哈希值
|
||||
end
|
||||
Core->>+Model: 生成查重报告
|
||||
Model-->>-UI: 显示结果
|
||||
```
|
||||
113
build.gradle
Normal file
113
build.gradle
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'io.franzbecker.gradle-lombok' version '3.0.0'
|
||||
id 'application'
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0'
|
||||
id 'org.beryx.jlink' version '2.26.0'
|
||||
}
|
||||
|
||||
group = project_group
|
||||
version = project_version
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ext {
|
||||
junitVersion = '5.10.0'
|
||||
}
|
||||
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.release = 17 // 明确指定Java版本
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
application {
|
||||
mainModule = 'top.r3944realms.docchecktoolrefactored'
|
||||
mainClass = 'top.r3944realms.docchecktoolrefactored.Main'
|
||||
}
|
||||
|
||||
javafx {
|
||||
version = '17.0.6'
|
||||
modules = ['javafx.controls', 'javafx.fxml']
|
||||
}
|
||||
|
||||
processResources {
|
||||
// exclude 'logback.xml'
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Logback classic (included slf4j &)
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.6'
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0'
|
||||
|
||||
// Lombok
|
||||
compileOnly("org.projectlombok:lombok:1.18.38")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.38")
|
||||
|
||||
testCompileOnly("org.projectlombok:lombok:1.18.38")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.38")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
configurations.configureEach {
|
||||
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('createLogDir') {
|
||||
doLast {
|
||||
mkdir "${projectDir}/logs"
|
||||
}
|
||||
|
||||
}
|
||||
// 打包sourcesJar任务
|
||||
tasks.register('sourcesJar', Jar) {
|
||||
dependsOn classes
|
||||
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
// 打包javadocJar任务
|
||||
tasks.register('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
// 解决javadoc打包乱码
|
||||
javadoc {
|
||||
options {
|
||||
encoding "UTF-8"
|
||||
charSet 'UTF-8'
|
||||
author true
|
||||
version true
|
||||
title "DG_LAB"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
jlink {
|
||||
imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip")
|
||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||
launcher {
|
||||
name = 'DocCheckTool'
|
||||
jvmArgs = ['-Dlogback.configurationFile=./config/logback.xml'] // 支持外部配置
|
||||
}
|
||||
mergedModule {
|
||||
requires 'java.logging'
|
||||
requires 'java.xml'
|
||||
}
|
||||
}
|
||||
|
||||
jlinkZip {
|
||||
group = 'distribution'
|
||||
}
|
||||
processResources.dependsOn createLogDir
|
||||
0
doc/request.md
Normal file
0
doc/request.md
Normal file
2
gradle.properties
Normal file
2
gradle.properties
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
project_group ='top.r3944realms.docchecktoolrefacored'
|
||||
project_version = '1.0-SNAPSHOT'
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
gradlew
vendored
Normal file
234
gradlew
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
|
@ -0,0 +1 @@
|
|||
rootProject.name = "DocCheckToolRefactored"
|
||||
18
src/main/java/module-info.java
Normal file
18
src/main/java/module-info.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
module top.r3944realms.docchecktoolrefactored {
|
||||
requires javafx.graphics;
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires static lombok;
|
||||
requires org.slf4j;
|
||||
|
||||
opens top.r3944realms.docchecktoolrefactored to javafx.fxml;
|
||||
opens top.r3944realms.docchecktoolrefactored.ui to javafx.fxml;
|
||||
opens top.r3944realms.docchecktoolrefactored.ui.module to javafx.fxml;
|
||||
|
||||
exports top.r3944realms.docchecktoolrefactored to javafx.graphics;
|
||||
exports top.r3944realms.docchecktoolrefactored.ui to javafx.fxml;
|
||||
exports top.r3944realms.docchecktoolrefactored.ui.module to javafx.fxml;
|
||||
exports top.r3944realms.docchecktoolrefactored.deprecated to javafx.graphics;
|
||||
opens top.r3944realms.docchecktoolrefactored.deprecated to javafx.fxml;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package top.r3944realms.docchecktoolrefactored;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
||||
|
||||
/**
|
||||
* The type Main.
|
||||
*/
|
||||
@Slf4j
|
||||
public class Main extends Application {
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
SceneManager.init(primaryStage);
|
||||
SceneManager.switchLoginView();
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point of application.
|
||||
*
|
||||
* @param args the input arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
log.info("Hello World!");
|
||||
launch(args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package top.r3944realms.docchecktoolrefactored.deprecated;
|
||||
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
public abstract class BaseFunctionPanel extends Pane {
|
||||
protected DocCheckToolMainFrame mainFrame;
|
||||
protected TextArea resultTextArea;
|
||||
protected static final Color PRIMARY_COLOR = Color.rgb(66, 133, 244);
|
||||
protected static final Color SECONDARY_COLOR = Color.rgb(30, 169, 80);
|
||||
protected static final Color BACKGROUND_COLOR = Color.rgb(245, 245, 245);
|
||||
protected static final Color BORDER_COLOR = Color.rgb(222, 226, 230);
|
||||
|
||||
public BaseFunctionPanel(DocCheckToolMainFrame mainFrame) {
|
||||
this.mainFrame = mainFrame;
|
||||
setStyle("-fx-background-color: rgb(245,245,245);");
|
||||
initComponents();
|
||||
}
|
||||
|
||||
protected abstract void initComponents();
|
||||
|
||||
public void showStatusMessage(String message) {
|
||||
if (resultTextArea != null) {
|
||||
resultTextArea.appendText(message + "\n");
|
||||
resultTextArea.positionCaret(resultTextArea.getText().length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package top.r3944realms.docchecktoolrefactored.deprecated;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class DocCheckToolMainFrame {
|
||||
private StackPane cardPanel;
|
||||
private static final Color PRIMARY_COLOR = Color.rgb(66, 133, 244);
|
||||
private static final Color BACKGROUND_COLOR = Color.rgb(245, 245, 245);
|
||||
|
||||
public void show(Stage primaryStage) {
|
||||
primaryStage.setTitle("淮阴区数字化档案检查验收系统");
|
||||
primaryStage.setWidth(1200);
|
||||
primaryStage.setHeight(600);
|
||||
primaryStage.setResizable(false);
|
||||
|
||||
// Create top button panel
|
||||
HBox topButtonPanel = new HBox(10);
|
||||
topButtonPanel.setPadding(new Insets(10));
|
||||
topButtonPanel.setAlignment(Pos.CENTER_LEFT);
|
||||
topButtonPanel.setStyle("-fx-background-color: rgb(245,245,245);");
|
||||
|
||||
String[] buttonTexts = {"1 查重复文件", "2 查遗漏、存储路径和命名规范", "3 查质量",
|
||||
"4 元数据", "5 查数据挂接", "6 查工作文件", "7 查存储载体"};
|
||||
|
||||
for (String text : buttonTexts) {
|
||||
Button btn = new Button(text);
|
||||
btn.setFont(Font.font("Microsoft YaHei", 14));
|
||||
btn.setTextFill(Color.WHITE);
|
||||
btn.setStyle("-fx-background-color: rgb(66,133,244); -fx-padding: 5 15 5 15;");
|
||||
btn.setOnAction(e -> switchPanel(text));
|
||||
topButtonPanel.getChildren().add(btn);
|
||||
}
|
||||
|
||||
// Initialize card panel
|
||||
cardPanel = new StackPane();
|
||||
cardPanel.setStyle("-fx-background-color: rgb(245,245,245);");
|
||||
|
||||
// Add all function panels to card panel
|
||||
cardPanel.getChildren().addAll(
|
||||
new DuplicateCheckPanel(this),
|
||||
new PathCheckPanel(this),
|
||||
new OtherFunctionPanel(" 3 查质量"),
|
||||
new OtherFunctionPanel("4 元数据"),
|
||||
new OtherFunctionPanel("5 查数据挂接"),
|
||||
new OtherFunctionPanel("6 查工作文件"),
|
||||
new OtherFunctionPanel("7 查存储载体")
|
||||
);
|
||||
|
||||
// Initially show the first panel
|
||||
switchPanel("1 查重复文件");
|
||||
|
||||
// Create main layout
|
||||
BorderPane root = new BorderPane();
|
||||
root.setTop(topButtonPanel);
|
||||
root.setCenter(cardPanel);
|
||||
root.setStyle("-fx-background-color: rgb(245,245,245);");
|
||||
|
||||
primaryStage.setScene(new Scene(root));
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
private void switchPanel(String panelName) {
|
||||
for (javafx.scene.Node node : cardPanel.getChildren()) {
|
||||
node.setVisible(false);
|
||||
}
|
||||
|
||||
int index = switch (panelName) {
|
||||
case "1 查重复文件" -> 0;
|
||||
case "2 查遗漏、存储路径和命名规范" -> 1;
|
||||
case "3 查质量" -> 2;
|
||||
case "4 元数据" -> 3;
|
||||
case "5 查数据挂接" -> 4;
|
||||
case "6 查工作文件" -> 5;
|
||||
case "7 查存储载体" -> 6;
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
cardPanel.getChildren().get(index).setVisible(true);
|
||||
}
|
||||
|
||||
public void showStatusMessage(String message) {
|
||||
for (javafx.scene.Node node : cardPanel.getChildren()) {
|
||||
if (node instanceof BaseFunctionPanel && node.isVisible()) {
|
||||
((BaseFunctionPanel) node).showStatusMessage(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showErrorMessage(String message) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("错误");
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
showStatusMessage("【错误】" + message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
package top.r3944realms.docchecktoolrefactored.deprecated;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
public class DuplicateCheckPanel extends BaseFunctionPanel {
|
||||
private TextField folderPathTextField;
|
||||
private TextArea feedbackTextArea;
|
||||
private Set<String> duplicateFolders = new HashSet<>();
|
||||
|
||||
public DuplicateCheckPanel(DocCheckToolMainFrame mainFrame) {
|
||||
super(mainFrame);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
protected void initComponents() {
|
||||
// Main layout with padding
|
||||
this.setPadding(new Insets(20));
|
||||
|
||||
// Folder selection components
|
||||
HBox folderSelectBox = new HBox(10);
|
||||
folderSelectBox.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
Label loadFolderLabel = new Label("载入文件夹");
|
||||
loadFolderLabel.setFont(Font.font("Microsoft YaHei", 14));
|
||||
loadFolderLabel.setTextFill(Color.DARKGRAY);
|
||||
|
||||
folderPathTextField = new TextField();
|
||||
folderPathTextField.setEditable(false);
|
||||
folderPathTextField.setFont(Font.font("Microsoft YaHei", 14));
|
||||
folderPathTextField.setStyle("-fx-background-color: white; -fx-border-color: #CCCCCC; -fx-border-width: 1; -fx-padding: 5 10 5 10;");
|
||||
HBox.setHgrow(folderPathTextField, Priority.ALWAYS);
|
||||
|
||||
Button selectFolderButton = new Button("选择文件夹");
|
||||
selectFolderButton.setFont(Font.font("Microsoft YaHei", 14));
|
||||
selectFolderButton.setTextFill(Color.WHITE);
|
||||
selectFolderButton.setStyle("-fx-background-color: #4285F4; -fx-padding: 5 15 5 15;");
|
||||
selectFolderButton.setOnAction(e -> {
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle("选择要检查的文件夹");
|
||||
File selectedFolder = directoryChooser.showDialog(new Stage());
|
||||
if (selectedFolder != null) {
|
||||
folderPathTextField.setText(selectedFolder.getAbsolutePath());
|
||||
}
|
||||
});
|
||||
|
||||
folderSelectBox.getChildren().addAll(loadFolderLabel, folderPathTextField, selectFolderButton);
|
||||
|
||||
Button startCheckButton = new Button("开始检查");
|
||||
startCheckButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 14));
|
||||
startCheckButton.setTextFill(Color.WHITE);
|
||||
startCheckButton.setStyle("-fx-background-color: #4285F4; -fx-padding: 5 20 5 20;");
|
||||
startCheckButton.setOnAction(e -> {
|
||||
String folderPath = folderPathTextField.getText();
|
||||
if (!folderPath.isEmpty()) {
|
||||
feedbackTextArea.setText("");
|
||||
File folder = new File(folderPath);
|
||||
duplicateFolders.clear();
|
||||
checkDuplicateFiles(folder);
|
||||
if (duplicateFolders.isEmpty()) {
|
||||
feedbackTextArea.setText("反馈结果:\n如无重复文件,则反馈:无重复文件;\n如有重复文件,则在此反馈档号。\n\n当前:无重复文件");
|
||||
} else {
|
||||
StringBuilder result = new StringBuilder("反馈结果:\n如无重复文件,则反馈:无重复文件;\n如有重复文件,则在此反馈档号。\n\n当前:");
|
||||
for (String folderName : duplicateFolders) {
|
||||
result.append("\n").append(folderName);
|
||||
}
|
||||
feedbackTextArea.setText(result.toString());
|
||||
}
|
||||
} else {
|
||||
mainFrame.showErrorMessage("请先选择一个文件夹!");
|
||||
}
|
||||
});
|
||||
|
||||
// Feedback area
|
||||
feedbackTextArea = new TextArea();
|
||||
feedbackTextArea.setEditable(false);
|
||||
feedbackTextArea.setFont(Font.font("Microsoft YaHei", 14));
|
||||
feedbackTextArea.setText("反馈结果:\n如无重复文件,则反馈:无重复文件;\n如有重复文件,则在此反馈档号。");
|
||||
feedbackTextArea.setStyle("-fx-background-color: white; -fx-border-color: #CCCCCC; -fx-border-width: 1; -fx-padding: 10;");
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane(feedbackTextArea);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
|
||||
|
||||
// Main layout
|
||||
VBox mainBox = new VBox(10);
|
||||
mainBox.getChildren().addAll(folderSelectBox, startCheckButton, scrollPane);
|
||||
this.getChildren().add(mainBox);
|
||||
|
||||
// Set result text area reference
|
||||
resultTextArea = feedbackTextArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively checks for duplicate fileMetadata in the specified folder
|
||||
*/
|
||||
private void checkDuplicateFiles(File folder) {
|
||||
Map<String, List<File>> fileHashMap = new HashMap<>();
|
||||
if (folder.isDirectory()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
checkDuplicateFiles(file);
|
||||
} else {
|
||||
try {
|
||||
String md5Hash = calculateMD5(file);
|
||||
fileHashMap.computeIfAbsent(md5Hash, k -> new ArrayList<>()).add(file);
|
||||
} catch (IOException | NoSuchAlgorithmException ex) {
|
||||
ex.printStackTrace();
|
||||
mainFrame.showErrorMessage("计算文件哈希值出错:" + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (List<File> fileList : fileHashMap.values()) {
|
||||
if (fileList.size() > 1) {
|
||||
for (File file : fileList) {
|
||||
File parentFolder = file.getParentFile();
|
||||
if (parentFolder != null) {
|
||||
duplicateFolders.add(parentFolder.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the MD5 hash of a file
|
||||
*/
|
||||
private String calculateMD5(File file) throws IOException, NoSuchAlgorithmException {
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
byte[] hashBytes = digest.digest();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : hashBytes) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package top.r3944realms.docchecktoolrefactored.deprecated;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
|
||||
public class OtherFunctionPanel extends BaseFunctionPanel {
|
||||
private String title;
|
||||
|
||||
public OtherFunctionPanel(String title) {
|
||||
super(null);
|
||||
this.title = title;
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initComponents() {
|
||||
// Set padding
|
||||
this.setPadding(new Insets(50));
|
||||
|
||||
//Create tip label
|
||||
Label tipLabel = new Label("【" + "" +"】功能界面待完善..."+title);
|
||||
tipLabel.setFont(Font.font("Microsoft YaHei", 28));
|
||||
tipLabel.setTextFill(javafx.scene.paint.Color.DARKGRAY);
|
||||
|
||||
// Use VBox for vertical layout and center alignment
|
||||
VBox container = new VBox(tipLabel);
|
||||
container.setAlignment(Pos.CENTER);
|
||||
|
||||
// Add to the panel
|
||||
this.getChildren().add(container);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
package top.r3944realms.docchecktoolrefactored.deprecated;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class PathCheckPanel extends BaseFunctionPanel {
|
||||
private TextField physicalFolderPathTextField;
|
||||
private TextField logicalFolderPathTextField;
|
||||
private TextArea checkResultTextArea;
|
||||
private Path physicalFolderPath;
|
||||
private Path logicalFolderPath;
|
||||
private List<String> physicalPaths = new ArrayList<>();
|
||||
private List<String> logicalPaths = new ArrayList<>();
|
||||
|
||||
public PathCheckPanel(DocCheckToolMainFrame mainFrame) {
|
||||
super(mainFrame);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initComponents() {
|
||||
// Main layout with spacing
|
||||
VBox mainLayout = new VBox(15);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// Physical path selection
|
||||
TitledPane physicalPathPane = createPathPane(
|
||||
"物理存储路径",
|
||||
"选择物理文件夹",
|
||||
"生成物理存储路径文件",
|
||||
this::selectPhysicalFolder,
|
||||
this::generatePhysicalPathFile
|
||||
);
|
||||
physicalFolderPathTextField = (TextField) ((HBox) physicalPathPane.getContent()).getChildren().get(0);
|
||||
|
||||
// Logical path selection
|
||||
TitledPane logicalPathPane = createPathPane(
|
||||
"逻辑存储路径",
|
||||
"选择逻辑文件夹",
|
||||
"生成逻辑存储路径文件",
|
||||
this::selectLogicalFolder,
|
||||
this::generateLogicalPathFile
|
||||
);
|
||||
logicalFolderPathTextField = (TextField) ((HBox) logicalPathPane.getContent()).getChildren().get(0);
|
||||
|
||||
// Check buttons
|
||||
HBox buttonBox = new HBox(15);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
Button checkFilesButton = new Button("检查文件");
|
||||
stylePrimaryButton(checkFilesButton, 16);
|
||||
checkFilesButton.setOnAction(e -> checkFiles());
|
||||
|
||||
Button checkDirButton = new Button("检查目录");
|
||||
checkDirButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 16));
|
||||
checkDirButton.setTextFill(Color.WHITE);
|
||||
checkDirButton.setStyle("-fx-background-color: #ef4444; -fx-border-color: #dc2626; -fx-border-width: 2; -fx-padding: 8 30 8 30;");
|
||||
checkDirButton.setOnAction(e -> checkDirectory());
|
||||
|
||||
buttonBox.getChildren().addAll(checkFilesButton, checkDirButton);
|
||||
|
||||
// Result area
|
||||
checkResultTextArea = new TextArea();
|
||||
checkResultTextArea.setEditable(false);
|
||||
checkResultTextArea.setFont(Font.font("Microsoft YaHei", 14));
|
||||
checkResultTextArea.setText("检查结果将显示在这里...\n请先选择文件夹并生成路径文件");
|
||||
checkResultTextArea.setStyle("-fx-background-color: white; -fx-border-color: #dee2e6; -fx-border-width: 1; -fx-padding: 10;");
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane(checkResultTextArea);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.setFitToHeight(true);
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
|
||||
|
||||
// Assemble main layout
|
||||
VBox pathPanes = new VBox(15, physicalPathPane, logicalPathPane);
|
||||
mainLayout.getChildren().addAll(pathPanes, scrollPane, buttonBox);
|
||||
this.getChildren().add(mainLayout);
|
||||
}
|
||||
|
||||
private TitledPane createPathPane(String title, String selectBtnText, String generateBtnText,
|
||||
Runnable selectAction, Runnable generateAction) {
|
||||
TextField pathField = new TextField();
|
||||
pathField.setEditable(false);
|
||||
pathField.setFont(Font.font("Microsoft YaHei", 14));
|
||||
pathField.setStyle("-fx-background-color: white; -fx-border-color: #dee2e6; -fx-border-width: 1; -fx-padding: 5 10 5 10;");
|
||||
|
||||
Button selectButton = new Button(selectBtnText);
|
||||
stylePrimaryButton(selectButton, 14);
|
||||
selectButton.setOnAction(e -> selectAction.run());
|
||||
|
||||
Button generateButton = new Button(generateBtnText);
|
||||
stylePrimaryButton(generateButton, 14);
|
||||
generateButton.setDisable(true);
|
||||
generateButton.setOnAction(e -> generateAction.run());
|
||||
|
||||
HBox content = new HBox(10, pathField, selectButton, generateButton);
|
||||
content.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
TitledPane pane = new TitledPane(title, content);
|
||||
pane.setFont(Font.font("Microsoft YaHei", 14));
|
||||
pane.setStyle("-fx-border-color: #dee2e6; -fx-border-width: 1;");
|
||||
|
||||
return pane;
|
||||
}
|
||||
|
||||
private void stylePrimaryButton(Button button, double fontSize) {
|
||||
button.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, fontSize));
|
||||
button.setTextFill(Color.WHITE);
|
||||
button.setStyle("-fx-background-color: #4285f4; -fx-border-color: #3b71ca; -fx-border-width: 2; -fx-padding: 8 30 8 30;");
|
||||
}
|
||||
|
||||
private void selectPhysicalFolder() {
|
||||
DirectoryChooser chooser = new DirectoryChooser();
|
||||
chooser.setTitle("选择物理存储文件夹");
|
||||
File selectedFolder = chooser.showDialog(new Stage());
|
||||
if (selectedFolder != null) {
|
||||
physicalFolderPathTextField.setText(selectedFolder.getAbsolutePath());
|
||||
physicalFolderPath = selectedFolder.toPath();
|
||||
|
||||
try {
|
||||
collectFilePaths(physicalFolderPath, physicalPaths);
|
||||
showStatusMessage("已收集 " + physicalPaths.size() + " 个物理文件路径");
|
||||
enableGenerateButton(physicalFolderPathTextField, true);
|
||||
} catch (IOException e) {
|
||||
mainFrame.showErrorMessage("读取物理文件夹失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectLogicalFolder() {
|
||||
DirectoryChooser chooser = new DirectoryChooser();
|
||||
chooser.setTitle("选择逻辑存储文件夹");
|
||||
File selectedFolder = chooser.showDialog(new Stage());
|
||||
if (selectedFolder != null) {
|
||||
logicalFolderPathTextField.setText(selectedFolder.getAbsolutePath());
|
||||
logicalFolderPath = selectedFolder.toPath();
|
||||
|
||||
try {
|
||||
collectFilePaths(logicalFolderPath, logicalPaths);
|
||||
showStatusMessage("已收集 " + logicalPaths.size() + " 个逻辑文件路径");
|
||||
enableGenerateButton(logicalFolderPathTextField, true);
|
||||
} catch (IOException e) {
|
||||
mainFrame.showErrorMessage("读取逻辑文件夹失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enableGenerateButton(TextField pathField, boolean enable) {
|
||||
// Implementation would find the generate button and enable it
|
||||
// This depends on your exact UI structure
|
||||
}
|
||||
|
||||
private void generatePhysicalPathFile() {
|
||||
if (physicalFolderPath == null || physicalPaths.isEmpty()) {
|
||||
mainFrame.showErrorMessage("请先选择有效的物理文件夹!");
|
||||
return;
|
||||
}
|
||||
|
||||
Path outputPath = Paths.get("物理存储路径文件.txt");
|
||||
writePathsToFile(physicalPaths, outputPath);
|
||||
showStatusMessage("物理存储路径文件已生成:" + outputPath.toAbsolutePath());
|
||||
}
|
||||
|
||||
private void generateLogicalPathFile() {
|
||||
if (logicalFolderPath == null || logicalPaths.isEmpty()) {
|
||||
mainFrame.showErrorMessage("请先选择有效的逻辑文件夹!");
|
||||
return;
|
||||
}
|
||||
|
||||
Path outputPath = Paths.get("逻辑存储路径文件.txt");
|
||||
writePathsToFile(logicalPaths, outputPath);
|
||||
showStatusMessage("逻辑存储路径文件已生成:" + outputPath.toAbsolutePath());
|
||||
}
|
||||
|
||||
// The following methods remain largely unchanged as they deal with file operations:
|
||||
private void collectFilePaths(Path folderPath, List<String> paths) throws IOException {
|
||||
paths.clear();
|
||||
try (Stream<Path> walk = Files.walk(folderPath)) {
|
||||
paths.addAll(walk
|
||||
.filter(Files::isRegularFile)
|
||||
.map(p -> folderPath.relativize(p).toString())
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
private void writePathsToFile(List<String> paths, Path outputPath) {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath.toFile()))) {
|
||||
for (String path : paths) {
|
||||
writer.write(path);
|
||||
writer.newLine();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
mainFrame.showErrorMessage("写入文件失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFiles() {
|
||||
if (physicalPaths.isEmpty() || logicalPaths.isEmpty()) {
|
||||
mainFrame.showErrorMessage("请先选择并生成物理和逻辑路径文件!");
|
||||
return;
|
||||
}
|
||||
|
||||
clearResultArea();
|
||||
showStatusMessage("正在进行文件检查...");
|
||||
|
||||
Set<String> missingInPhysical = new HashSet<>();
|
||||
for (String logicalPath : logicalPaths) {
|
||||
if (!physicalPaths.contains(logicalPath)) {
|
||||
missingInPhysical.add(logicalPath);
|
||||
}
|
||||
}
|
||||
|
||||
displayCheckResult("文件检查", logicalPaths.size(), missingInPhysical, null);
|
||||
}
|
||||
|
||||
private void checkDirectory() {
|
||||
if (physicalPaths.isEmpty() || logicalPaths.isEmpty()) {
|
||||
mainFrame.showErrorMessage("请先选择并生成物理和逻辑路径文件!");
|
||||
return;
|
||||
}
|
||||
|
||||
clearResultArea();
|
||||
showStatusMessage("正在进行目录检查...");
|
||||
|
||||
Set<String> missingInLogical = new HashSet<>();
|
||||
for (String physicalPath : physicalPaths) {
|
||||
if (!logicalPaths.contains(physicalPath)) {
|
||||
missingInLogical.add(physicalPath);
|
||||
}
|
||||
}
|
||||
|
||||
displayCheckResult("目录检查", physicalPaths.size(), null, missingInLogical);
|
||||
}
|
||||
|
||||
private void displayCheckResult(String checkType, int totalCount,
|
||||
Set<String> missingInPhysical, Set<String> missingInLogical) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("=== ").append(checkType).append(" ===\n");
|
||||
result.append("共检查 ").append(totalCount).append(" 个").append(checkType.equals("文件检查") ? "文件" : "目录项").append("\n\n");
|
||||
|
||||
if (checkType.equals("文件检查")) {
|
||||
if (missingInPhysical.isEmpty()) {
|
||||
result.append("✓ 未发现问题:所有逻辑文件在物理存储中均存在\n");
|
||||
} else {
|
||||
result.append("! 发现 ").append(missingInPhysical.size()).append(" 个问题:\n");
|
||||
missingInPhysical.forEach(path -> result.append(" - 物理存储中缺失文件:").append(path).append("\n"));
|
||||
}
|
||||
} else {
|
||||
if (missingInLogical.isEmpty()) {
|
||||
result.append("✓ 未发现问题:所有物理文件在逻辑目录中均存在\n");
|
||||
} else {
|
||||
result.append("! 发现 ").append(missingInLogical.size()).append(" 个问题:\n");
|
||||
missingInLogical.forEach(path -> result.append(" - 逻辑目录中缺失文件:").append(path).append("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
checkResultTextArea.appendText(result.toString());
|
||||
checkResultTextArea.appendText("\n\n");
|
||||
showStatusMessage(checkType + "完成");
|
||||
}
|
||||
|
||||
private void clearResultArea() {
|
||||
checkResultTextArea.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package top.r3944realms.docchecktoolrefactored.io.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* The interface File reader.
|
||||
*/
|
||||
public interface FileReader {
|
||||
/**
|
||||
* 读取文件内容
|
||||
*
|
||||
* @param file 要读取的文件
|
||||
* @return 文件内容字节数组 byte [ ]
|
||||
* @throws IOException the io exception
|
||||
*/
|
||||
byte[] readFully(Path file) throws IOException;
|
||||
|
||||
/**
|
||||
* 流式读取文件内容
|
||||
*
|
||||
* @param file 要读取的文件
|
||||
* @param consumer 内容消费者
|
||||
* @throws IOException the io exception
|
||||
*/
|
||||
void readStreaming(Path file, ByteBufferConsumer consumer) throws IOException;
|
||||
|
||||
/**
|
||||
* The interface Byte buffer consumer.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ByteBufferConsumer {
|
||||
/**
|
||||
* Consume.
|
||||
*
|
||||
* @param buffer the buffer
|
||||
* @param isLast the is last
|
||||
* @throws IOException the io exception
|
||||
*/
|
||||
void consume(ByteBuffer buffer, boolean isLast) throws IOException;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package top.r3944realms.docchecktoolrefactored.io.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
/**
|
||||
* The type Memory mapped file reader.
|
||||
*/
|
||||
public class MemoryMappedFileReader implements FileReader {
|
||||
private static final long MAX_MMAP_SIZE = 1 << 30; // 1GB
|
||||
|
||||
@Override
|
||||
public byte[] readFully(Path file) throws IOException {
|
||||
long size = Files.size(file);
|
||||
if (size > MAX_MMAP_SIZE) {
|
||||
throw new IOException("File too large for memory mapping: " + file);
|
||||
}
|
||||
|
||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {
|
||||
MappedByteBuffer buffer = channel.map(
|
||||
FileChannel.MapMode.READ_ONLY, 0, size);
|
||||
byte[] bytes = new byte[(int) size];
|
||||
buffer.get(bytes);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readStreaming(Path file, ByteBufferConsumer consumer) throws IOException {
|
||||
try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {
|
||||
long size = channel.size();
|
||||
long position = 0;
|
||||
long chunkSize = Math.min(size, 8 * 1024 * 1024); // 8MB chunks
|
||||
|
||||
while (position < size) {
|
||||
long remaining = size - position;
|
||||
long currentChunk = Math.min(remaining, chunkSize);
|
||||
|
||||
MappedByteBuffer buffer = channel.map(
|
||||
FileChannel.MapMode.READ_ONLY, position, currentChunk);
|
||||
consumer.consume(buffer, position + currentChunk >= size);
|
||||
position += currentChunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package top.r3944realms.docchecktoolrefactored.io.scanner;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* The interface File scanner.
|
||||
*/
|
||||
public interface FileScanner {
|
||||
/**
|
||||
* 扫描指定路径下的文件
|
||||
*
|
||||
* @param rootPath 根路径
|
||||
* @param listener 文件发现监听器
|
||||
*/
|
||||
void scan(Path rootPath, FileScanListener listener);
|
||||
|
||||
/**
|
||||
* 文件扫描监听器
|
||||
*/
|
||||
interface FileScanListener {
|
||||
/**
|
||||
* On file found.
|
||||
*
|
||||
* @param file the file
|
||||
*/
|
||||
void onFileFound(Path file);
|
||||
|
||||
/**
|
||||
* On scan complete.
|
||||
*/
|
||||
void onScanComplete();
|
||||
|
||||
/**
|
||||
* On error.
|
||||
*
|
||||
* @param file the file
|
||||
* @param e the e
|
||||
*/
|
||||
void onError(Path file, Exception e);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package top.r3944realms.docchecktoolrefactored.io.scanner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The type Parallel file scanner.
|
||||
*/
|
||||
public class ParallelFileScanner implements FileScanner,AutoCloseable {
|
||||
private final ForkJoinPool forkJoinPool;
|
||||
|
||||
/**
|
||||
* 使用默认并行度(CPU核心数)
|
||||
*/
|
||||
public ParallelFileScanner() {
|
||||
this(Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Parallel file scanner.
|
||||
*
|
||||
* @param parallelism 并行度(线程数)
|
||||
*/
|
||||
public ParallelFileScanner(int parallelism) {
|
||||
this.forkJoinPool = new ForkJoinPool(parallelism);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scan(Path rootPath, FileScanListener listener) {
|
||||
forkJoinPool.submit(() -> {
|
||||
try (
|
||||
Stream<Path> pathStream = Files.walk(rootPath)
|
||||
.parallel() // 使用ForkJoinPool的并行流
|
||||
.filter(Files::isRegularFile)
|
||||
){
|
||||
pathStream.forEach(file -> {
|
||||
try {
|
||||
listener.onFileFound(file);
|
||||
} catch (Exception e) {
|
||||
listener.onError(file, e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
).join();
|
||||
|
||||
listener.onScanComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
forkJoinPool.shutdown();
|
||||
try {
|
||||
if (!forkJoinPool.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||
forkJoinPool.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
forkJoinPool.shutdownNow();
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package top.r3944realms.docchecktoolrefactored.model;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The type Duplicate group.
|
||||
*
|
||||
* @param hash 文件内容哈希值
|
||||
* @param size 文件大小(字节)
|
||||
* @param fileMetas 关联的元数据列表
|
||||
*/
|
||||
public record DuplicateGroup(String hash, long size, List<FileMetadata> fileMetas) {
|
||||
|
||||
/**
|
||||
* Instantiates a new Duplicate group.
|
||||
*
|
||||
* @param hash the hash
|
||||
* @param size the size
|
||||
* @param fileMetas the file metas
|
||||
*/
|
||||
// 紧凑构造方法验证数据
|
||||
public DuplicateGroup {
|
||||
Objects.requireNonNull(hash);
|
||||
Objects.requireNonNull(fileMetas);
|
||||
fileMetas = Collections.unmodifiableList(fileMetas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is real duplicate boolean.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean isRealDuplicate() {
|
||||
return fileMetas.size() > 1; // 是否真实重复(至少两个文件)
|
||||
}
|
||||
|
||||
/**
|
||||
* Total wasted space long.
|
||||
*
|
||||
* @return the long
|
||||
*/
|
||||
public long totalWastedSpace() {
|
||||
return size * (fileMetas.size() - 1); // 浪费空间大小
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package top.r3944realms.docchecktoolrefactored.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The type Duplicate report.
|
||||
*/
|
||||
public class DuplicateReport {
|
||||
private List<DuplicateGroup> duplicates;
|
||||
|
||||
/**
|
||||
* Export to json.
|
||||
*/
|
||||
public void exportToJson() {
|
||||
//TODO:
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package top.r3944realms.docchecktoolrefactored.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* The type File metadata.
|
||||
*/
|
||||
@Data
|
||||
public class FileMetadata {
|
||||
private Path path;
|
||||
private String filename; // 文件名(不含路径)
|
||||
private long size;
|
||||
private String hash;
|
||||
private Instant lastModified;
|
||||
private String parentDir; // 父目录名
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* 登录场景控制器
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoginStageController implements Initializable {
|
||||
/**
|
||||
* The Login button.
|
||||
*/
|
||||
public Button loginButton;
|
||||
/**
|
||||
* The Main pane.
|
||||
*/
|
||||
public BorderPane mainPane;
|
||||
@FXML private TextField usernameField;
|
||||
@FXML private PasswordField passwordField;
|
||||
|
||||
@FXML
|
||||
private void handleLogin() {
|
||||
String username = usernameField.getText();
|
||||
String password = passwordField.getText();
|
||||
|
||||
if ("admin".equals(username) && "admin".equals(password)) {
|
||||
log.info("{} Login successful", username);
|
||||
SceneManager.switchMainView();
|
||||
} else {
|
||||
log.info("Invalid username or password");
|
||||
DialogUtil.showErrorDialog("错误", null, "用户名或密码错误!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
//添加 Ctrl + Enter 快捷键
|
||||
KeyCodeCombination enter_key_down = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
|
||||
// 监听场景属性变化
|
||||
SceneManager.tryGetSceneHandler(passwordField, window ->
|
||||
window.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
|
||||
if (enter_key_down.match(event)) {
|
||||
handleLogin();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Menu;
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
/**
|
||||
* The type Main stage controller.
|
||||
*/
|
||||
public class MainStageController {
|
||||
|
||||
@FXML private Tab step1T;
|
||||
@FXML private Tab step2T;
|
||||
@FXML private Tab step3T;
|
||||
@FXML private Tab step4T;
|
||||
@FXML private Tab step5T;
|
||||
@FXML private Tab step6T;
|
||||
@FXML private Tab step7T;
|
||||
|
||||
@FXML private Menu helpM;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui;
|
||||
|
||||
import javafx.animation.FadeTransition;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.Main;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The type Scene manager.
|
||||
*/
|
||||
@Slf4j
|
||||
public class SceneManager {
|
||||
@Getter
|
||||
@Setter
|
||||
private static Stage primaryStage;
|
||||
|
||||
@Getter
|
||||
private static final List<Stage> openStages = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Init.
|
||||
*
|
||||
* @param primaryStage the primary stage
|
||||
*/
|
||||
public static void init(Stage primaryStage) {
|
||||
SceneManager.primaryStage = primaryStage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch login view.
|
||||
*/
|
||||
public static void switchLoginView() {
|
||||
SceneManager.loadView("/fxml/login-view.fxml","淮阴区数字化档案检查验收系统 - 登录", 400, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch main view.
|
||||
*/
|
||||
public static void switchMainView() {
|
||||
SceneManager.loadView("/fxml/main-view.fxml", "淮阴区数字化档案检查验收系统 - 主界面", 1000, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load view.
|
||||
*
|
||||
* @param fxmlPath the fxml path
|
||||
* @param title the title
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
*/
|
||||
// 基础视图加载方法
|
||||
public static void loadView(String fxmlPath, String title, int width, int height) {
|
||||
loadView(fxmlPath, title, width, height, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load view.
|
||||
*
|
||||
* @param fxmlPath the fxml path
|
||||
* @param title the title
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param initializer the initializer
|
||||
*/
|
||||
public static void loadView(String fxmlPath, String title, int width, int height, Consumer<Stage> initializer) {
|
||||
try {
|
||||
Parent root = FXMLLoader.load(Objects.requireNonNull(Main.class.getResource(fxmlPath)));
|
||||
Scene newScene = new Scene(root, width, height);
|
||||
|
||||
// 淡入效果
|
||||
applyFadeTransition(root);
|
||||
|
||||
primaryStage.setScene(newScene);
|
||||
primaryStage.centerOnScreen();
|
||||
primaryStage.setTitle(title);
|
||||
|
||||
if (initializer != null) {
|
||||
initializer.accept(primaryStage);
|
||||
}
|
||||
|
||||
// 设置窗口关闭确认
|
||||
primaryStage.setOnCloseRequest(event -> {
|
||||
if (!DialogUtil.showExitConfirmation(primaryStage)) {
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to load view: {}", fxmlPath, e);
|
||||
DialogUtil.showErrorDialog("错误", "加载视图失败", "无法加载视图: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
private static void applyFadeTransition(Parent root) {
|
||||
root.setOpacity(0);
|
||||
FadeTransition fadeIn = new FadeTransition(Duration.millis(300), root);
|
||||
fadeIn.setToValue(1.0);
|
||||
fadeIn.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary scene handler.
|
||||
*
|
||||
* @param handler the handler
|
||||
*/
|
||||
public static void primarySceneHandler(Consumer<Scene> handler) {
|
||||
handler.accept(getPrimaryStage().getScene());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try get scene handler.
|
||||
*
|
||||
* @param node the node
|
||||
* @param handler the handler
|
||||
*/
|
||||
public static void tryGetSceneHandler(@NonNull Node node, Consumer<Scene> handler) {
|
||||
node.sceneProperty().addListener((observable, oldScene, newScene) -> Optional.ofNullable(newScene).ifPresent(handler));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* The type Duplicate document pane controller.
|
||||
*/
|
||||
public class DuplicateDocumentPaneController {
|
||||
@FXML private TextArea result1B;
|
||||
@FXML private TextField loadFolder1TF;
|
||||
@FXML private Button selectLoadFolder1B;
|
||||
@FXML private Button start1B;
|
||||
|
||||
/**
|
||||
* On select folder.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onSelectFolder(ActionEvent actionEvent) {
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle("选择要检查的文件夹");
|
||||
File selectedFolder = directoryChooser.showDialog(new Stage());
|
||||
if (selectedFolder != null) {
|
||||
loadFolder1TF.setText(selectedFolder.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On start.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onStart(ActionEvent actionEvent) {
|
||||
// 触发异步逻辑 -> 调用
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.TextField;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* The type Path check pane controller.
|
||||
*/
|
||||
public class PathCheckPaneController implements Initializable {
|
||||
@FXML private ChoiceBox<Mode> loadFolderType2CB;
|
||||
@FXML private TextArea result2TA;
|
||||
@FXML private Button start2B;
|
||||
@FXML private TextField loadCatalog2TF;
|
||||
@FXML private TextField loadJPGFolder2TF;
|
||||
@FXML private Button selectLoadCatalog2B;
|
||||
@FXML private Button selectJPGFolder2B;
|
||||
@FXML private Button generateLogicalAddress2B;
|
||||
@FXML private Button generatePhysicalAddress2B;
|
||||
|
||||
/**
|
||||
* On select lc.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onSelectLC(ActionEvent actionEvent) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On select jpgf.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onSelectJPGF(ActionEvent actionEvent) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On generate la.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onGenerateLA(ActionEvent actionEvent) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On generate pa.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onGeneratePA(ActionEvent actionEvent) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* On start.
|
||||
*
|
||||
* @param actionEvent the action event
|
||||
*/
|
||||
@FXML void onStart(ActionEvent actionEvent) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
loadFolderType2CB.getItems().addAll(Mode.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* The enum Mode.
|
||||
*/
|
||||
enum Mode {
|
||||
/**
|
||||
* Jpg mode.
|
||||
*/
|
||||
JPG("jpg"),
|
||||
/**
|
||||
* Pdf mode.
|
||||
*/
|
||||
PDF("pdf");
|
||||
/**
|
||||
* The Id.
|
||||
*/
|
||||
final String id;
|
||||
|
||||
Mode(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.TextField;
|
||||
|
||||
/**
|
||||
* The type Project info pane controller.
|
||||
*/
|
||||
public class ProjectInfoPaneController {
|
||||
@FXML private TextField projectNameTF;
|
||||
@FXML private TextField AcceptanceTimeTF;
|
||||
@FXML private TextField totalCatalogNumberTF;
|
||||
@FXML private TextField fileCategoriesTF;
|
||||
@FXML private TextField fileYearTF;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui.utils;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 对话框工具类
|
||||
*/
|
||||
public class DialogUtil {
|
||||
/**
|
||||
* Show exit confirmation boolean.
|
||||
*
|
||||
* @param owner the owner
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean showExitConfirmation(Window owner) {
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.initOwner(owner);
|
||||
alert.setTitle("确认退出");
|
||||
alert.setHeaderText("您确定要退出程序吗?");
|
||||
alert.setContentText("请确认您的操作");
|
||||
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
return result.isPresent() && result.get() == ButtonType.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show confirmation dialog boolean.
|
||||
*
|
||||
* @param title the title
|
||||
* @param header the header
|
||||
* @param content the content
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean showConfirmationDialog(String title, String header, String content) {
|
||||
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
return result.isPresent() && result.get() == ButtonType.OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show information dialog.
|
||||
*
|
||||
* @param title the title
|
||||
* @param header the header
|
||||
* @param content the content
|
||||
*/
|
||||
public static void showInformationDialog(String title, String header, String content) {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show warning dialog.
|
||||
*
|
||||
* @param title the title
|
||||
* @param header the header
|
||||
* @param content the content
|
||||
*/
|
||||
public static void showWarningDialog(String title, String header, String content) {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error dialog.
|
||||
*
|
||||
* @param title the title
|
||||
* @param header the header
|
||||
* @param content the content
|
||||
*/
|
||||
public static void showErrorDialog(String title, String header, String content) {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
}
|
||||
155
src/main/resources/css/custom-tab.css
Normal file
155
src/main/resources/css/custom-tab.css
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/* 基础Tab样式 */
|
||||
.tab {
|
||||
-fx-background-color: transparent; /* 改为透明 */
|
||||
-fx-background-insets: 0;
|
||||
-fx-padding: 0;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
/* 默认Tab形状容器 */
|
||||
.tab .tab-container {
|
||||
-fx-background-color: #13b72b;
|
||||
-fx-shape: "M0,5 L65,5 L75,15 L65,25 L0,25 L10,15 Z";
|
||||
-fx-background-insets: 0;
|
||||
-fx-border-width: 0; /* 确保无边框 */
|
||||
-fx-padding: 8 25 8 20;
|
||||
}
|
||||
|
||||
/* 起始Tab特殊形状 */
|
||||
#startTab .tab-container {
|
||||
-fx-shape: "M0,5 L75,5 L85,15 L75,25 L0,25 Z";
|
||||
-fx-padding: 8 20 8 25;
|
||||
}
|
||||
|
||||
/* 结束Tab特殊形状 */
|
||||
#endTab .tab-container {
|
||||
-fx-shape: "M0,5 L65,5 L65,15 L65,25 L0,25 L10,15 Z";
|
||||
-fx-padding: 8 20 8 25;
|
||||
}
|
||||
|
||||
/* 选中状态 - 完全移除蓝色边框 */
|
||||
.tab:selected,
|
||||
.tab:selected:focused {
|
||||
-fx-background-color: transparent; /* 关键设置 */
|
||||
-fx-background-insets: 0;
|
||||
-fx-border-width: 0;
|
||||
-fx-border-color: transparent;
|
||||
}
|
||||
|
||||
.tab:selected .tab-container {
|
||||
-fx-background-color: #9b0e0e; /* 只改变背景色 */
|
||||
-fx-effect: null; /* 移除所有效果 */
|
||||
}
|
||||
|
||||
/* 文字标签样式 */
|
||||
.tab .tab-label {
|
||||
-fx-text-fill: #000000;
|
||||
-fx-font-size: 16px;
|
||||
-fx-alignment: CENTER;
|
||||
-fx-padding: 0 10 0 10;
|
||||
-fx-max-width: 180px;
|
||||
-fx-translate-x: 6px;
|
||||
-fx-text-overrun: ellipsis;
|
||||
}
|
||||
|
||||
.tab:selected .tab-label {
|
||||
-fx-text-fill: #ffffff;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* TabPane整体设置 */
|
||||
.tab-pane {
|
||||
-fx-tab-min-height: 30px;
|
||||
-fx-tab-min-width: 80px;
|
||||
-fx-background-color: transparent;
|
||||
-fx-focus-color: transparent; /* 移除焦点边框 */
|
||||
-fx-faint-focus-color: transparent;
|
||||
}
|
||||
|
||||
/* Tab重叠调整 */
|
||||
.tab-pane > .tab-header-area > .headers-region > .tab {
|
||||
-fx-background-insets: 0 -15 0 0;
|
||||
-fx-min-width: 200px;
|
||||
-fx-pref-width: 300px;
|
||||
-fx-max-height: 300px;
|
||||
}
|
||||
|
||||
|
||||
/* 下拉菜单面板 - 增强视觉效果 */
|
||||
.context-menu {
|
||||
-fx-background-color: #ffffff;
|
||||
-fx-border-color: #b0b0b0;
|
||||
-fx-border-width: 1.5px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-padding: 5px 0;
|
||||
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 8, 0.2, 0, 2);
|
||||
}
|
||||
|
||||
/* 菜单项基础样式 */
|
||||
.context-menu .menu-item {
|
||||
-fx-background-color: transparent;
|
||||
-fx-padding: 8px 20px;
|
||||
-fx-cursor: hand;
|
||||
-fx-background-radius: 2px;
|
||||
-fx-border-width: 0;
|
||||
}
|
||||
|
||||
/* 菜单项悬停效果 */
|
||||
.context-menu .menu-item:hover {
|
||||
-fx-background-color: #f0f0f0;
|
||||
-fx-text-fill: #333333;
|
||||
}
|
||||
|
||||
/* 当前选中菜单项 */
|
||||
.context-menu .menu-item:selected {
|
||||
-fx-background-color: #9b0e0e; /* 与选中Tab相同颜色 */
|
||||
-fx-text-fill: white;
|
||||
}
|
||||
|
||||
/* 菜单项文本样式 */
|
||||
.context-menu .menu-item .label {
|
||||
-fx-text-fill: #333333;
|
||||
-fx-font-size: 14px;
|
||||
-fx-font-weight: normal;
|
||||
}
|
||||
|
||||
/* 选中菜单项文本样式 */
|
||||
.context-menu .menu-item:selected .label {
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* 分隔线样式 */
|
||||
.context-menu .separator:horizontal .line {
|
||||
-fx-border-color: #e0e0e0;
|
||||
-fx-border-width: 1px;
|
||||
-fx-padding: 3px 0;
|
||||
}
|
||||
|
||||
/* 下拉箭头按钮增强样式 */
|
||||
.tab-pane > .tab-header-area > .control-buttons-tab > .tab-down-button {
|
||||
-fx-background-color: #f5f5f5;
|
||||
-fx-background-radius: 0 3px 3px 0;
|
||||
-fx-padding: 5px 8px;
|
||||
-fx-border-color: #c0c0c0;
|
||||
-fx-border-width: 1px;
|
||||
-fx-border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
/* 下拉箭头图标样式 */
|
||||
.tab-pane > .tab-header-area > .control-buttons-tab > .tab-down-button .arrow {
|
||||
-fx-background-color: #666666;
|
||||
-fx-shape: "M 0 0 L 5 5 L 10 0 Z";
|
||||
-fx-padding: 4px;
|
||||
}
|
||||
|
||||
/* 悬停时箭头按钮效果 */
|
||||
.tab-pane > .tab-header-area > .control-buttons-tab > .tab-down-button:hover {
|
||||
-fx-background-color: #e0e0e0;
|
||||
-fx-border-color: #a0a0a0;
|
||||
}
|
||||
|
||||
.tab-pane > .tab-header-area > .control-buttons-tab > .tab-down-button:hover .arrow {
|
||||
-fx-background-color: #333333;
|
||||
}
|
||||
75
src/main/resources/fxml/login-view.fxml
Normal file
75
src/main/resources/fxml/login-view.fxml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<BorderPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="400.0" style="-fx-background-color: rgb(245,245,245);" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="top.r3944realms.docchecktoolrefactored.ui.LoginStageController">
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||
</padding>
|
||||
|
||||
<top>
|
||||
<Label alignment="CENTER" text="淮阴区数字化档案检查验收系统" textFill="rgb(66,133,244)">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="24.0" />
|
||||
</font>
|
||||
<BorderPane.margin>
|
||||
<Insets bottom="20.0" />
|
||||
</BorderPane.margin>
|
||||
</Label>
|
||||
</top>
|
||||
|
||||
<center>
|
||||
<GridPane alignment="CENTER" hgap="10.0" style="-fx-background-color: rgb(245,245,245);" vgap="15.0">
|
||||
<children>
|
||||
<Label text="用户名:" GridPane.columnIndex="0" GridPane.rowIndex="0">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
|
||||
<TextField fx:id="usernameField" style="-fx-background-color: white; -fx-border-color: rgb(222,226,230); -fx-border-width: 1; -fx-padding: 5 10 5 10;" GridPane.columnIndex="1" GridPane.rowIndex="0">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="14.0" />
|
||||
</font>
|
||||
</TextField>
|
||||
|
||||
<Label text="密码:" GridPane.columnIndex="0" GridPane.rowIndex="1">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
|
||||
<PasswordField fx:id="passwordField" style="-fx-background-color: white; -fx-border-color: rgb(222,226,230); -fx-border-width: 1; -fx-padding: 5 10 5 10;" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="14.0" />
|
||||
</font>
|
||||
</PasswordField>
|
||||
|
||||
<Button fx:id="loginButton" onAction="#handleLogin" prefHeight="46.0" prefWidth="158.0" style="-fx-background-color: rgb(66,133,244); -fx-padding: 5 15 5 15;" text="登录" textFill="WHITE" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<font>
|
||||
<Font name="Microsoft YaHei" size="14.0" />
|
||||
</font>
|
||||
</Button>
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints />
|
||||
<ColumnConstraints />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints />
|
||||
<RowConstraints />
|
||||
<RowConstraints />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
</center>
|
||||
</BorderPane>
|
||||
72
src/main/resources/fxml/main-view.fxml
Normal file
72
src/main/resources/fxml/main-view.fxml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Menu?>
|
||||
<?import javafx.scene.control.MenuBar?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.Tab?>
|
||||
<?import javafx.scene.control.TabPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="1000.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.MainStageController">
|
||||
<children>
|
||||
<MenuBar prefWidth="2558.0" VBox.vgrow="ALWAYS">
|
||||
<menus>
|
||||
<Menu mnemonicParsing="false" text="文件">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" text="关闭" />
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu mnemonicParsing="false" text="编辑">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" text="未完成" />
|
||||
</items>
|
||||
</Menu>
|
||||
<Menu fx:id="helpM" mnemonicParsing="false" text="帮助">
|
||||
<items>
|
||||
<MenuItem mnemonicParsing="false" text="关于" />
|
||||
</items>
|
||||
</Menu>
|
||||
</menus>
|
||||
</MenuBar>
|
||||
<fx:include source="module/project-info-pane.fxml" />
|
||||
<TabPane stylesheets="@../css/custom-tab.css" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
||||
<tabs>
|
||||
<Tab id="startTab" fx:id="step1T" text="1. 查重复文件">
|
||||
<content>
|
||||
<fx:include source="module/step-1-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="step2T" text="2. 查遗漏、存储路径和命名规范">
|
||||
<content>
|
||||
<fx:include source="module/step-2-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="step3T" text="3. 查质量">
|
||||
<content>
|
||||
<fx:include source="module/step-3-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="step4T" text="4. 元数据">
|
||||
<content>
|
||||
<fx:include source="module/step-4-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="step5T" text="5. 查数据挂载">
|
||||
<content>
|
||||
<fx:include source="module/step-5-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="step6T" text="6. 查工作文件">
|
||||
<content>
|
||||
<fx:include source="module/step-6-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab id="endTab" fx:id="step7T" text="7. 存储载体">
|
||||
<content>
|
||||
<fx:include source="module/step-7-pane.fxml" />
|
||||
</content>
|
||||
</Tab>
|
||||
</tabs>
|
||||
</TabPane>
|
||||
</children>
|
||||
</VBox>
|
||||
109
src/main/resources/fxml/module/project-info-pane.fxml
Normal file
109
src/main/resources/fxml/module/project-info-pane.fxml
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<GridPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.ProjectInfoPaneController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="10.0" prefWidth="80.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="10.0" prefWidth="80.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="项目名称:">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="验收时间:" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="全宗号:" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="档案门类:" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="档案年度:" GridPane.columnIndex="2" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<TextField fx:id="projectNameTF" GridPane.columnIndex="1" GridPane.columnSpan="3">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<TextField fx:id="AcceptanceTimeTF" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<TextField fx:id="totalCatalogNumberTF" GridPane.columnIndex="1" GridPane.columnSpan="3" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<TextField fx:id="fileCategoriesTF" GridPane.columnIndex="1" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<TextField fx:id="fileYearTF" GridPane.columnIndex="3" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
59
src/main/resources/fxml/module/step-1-pane.fxml
Normal file
59
src/main/resources/fxml/module/step-1-pane.fxml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.DuplicateDocumentPaneController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="288.0" minWidth="0.0" percentWidth="0.0" prefWidth="82.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1263.9999633789064" minWidth="0.0" prefWidth="745.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="873.0" minWidth="0.0" prefWidth="82.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="873.0" minWidth="10.0" prefWidth="407.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="173.0" minHeight="10.0" prefHeight="53.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="173.0" minHeight="10.0" prefHeight="83.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="173.0" minHeight="10.0" prefHeight="31.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="549.0" minHeight="10.0" prefHeight="549.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<TextArea fx:id="result1B" GridPane.columnSpan="3" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
<Label text="载入文件夹:" />
|
||||
<TextField fx:id="loadFolder1TF" GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<Button fx:id="selectLoadFolder1B" mnemonicParsing="false" onAction="#onSelectFolder" text="选择文件夹" GridPane.columnIndex="2" GridPane.columnSpan="2">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin></Button>
|
||||
<Button fx:id="start1B" minWidth="-Infinity" mnemonicParsing="false" onAction="#onStart" prefHeight="75.0" prefWidth="800.0" text="开始检查" GridPane.columnSpan="4" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<Label text="反馈结果:" GridPane.rowIndex="2" />
|
||||
<TextArea editable="false" prefHeight="530.0" prefWidth="300.0" text="在此反馈结果: 如无重复文件,则反馈:无重复文件; 如有重复文件,则在此反馈档号。 " GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="3">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
125
src/main/resources/fxml/module/step-2-pane.fxml
Normal file
125
src/main/resources/fxml/module/step-2-pane.fxml
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ChoiceBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.PathCheckPaneController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="0.0" percentWidth="0.0" prefWidth="104.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="10.0" percentWidth="0.0" prefWidth="104.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1881.0" minWidth="10.0" prefWidth="821.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="903.0" minWidth="10.0" prefWidth="734.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="965.0" minWidth="10.0" prefWidth="867.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="224.0" minHeight="0.0" prefHeight="34.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="289.0" minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="373.0" minHeight="10.0" prefHeight="131.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="224.0" minHeight="0.0" prefHeight="29.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints maxHeight="500.0" minHeight="10.0" prefHeight="493.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="载入目录:">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="载入文件夹:" GridPane.rowIndex="1">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<Label text="反馈结果:" GridPane.rowIndex="3">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<TextArea fx:id="result2TA" prefHeight="473.0" prefWidth="1754.0" GridPane.columnSpan="4" GridPane.rowIndex="4">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
<Button fx:id="start2B" mnemonicParsing="false" onAction="#onStart" prefHeight="75.0" prefWidth="800.0" text="开始比对" GridPane.columnSpan="5" GridPane.rowIndex="2">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<TextField fx:id="loadCatalog2TF" GridPane.columnIndex="1" GridPane.columnSpan="2">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<TextField fx:id="loadJPGFolder2TF" prefHeight="19.0" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<Button fx:id="selectLoadCatalog2B" mnemonicParsing="false" onAction="#onSelectLC" text="选择文件" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<Button fx:id="selectJPGFolder2B" mnemonicParsing="false" onAction="#onSelectJPGF" text="选择文件夹" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<Button fx:id="generateLogicalAddress2B" mnemonicParsing="false" onAction="#onGenerateLA" text="生成逻辑地址" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<Button fx:id="generatePhysicalAddress2B" mnemonicParsing="false" onAction="#onGeneratePA" text="生成物理地址" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</Button>
|
||||
<TextArea editable="false" prefHeight="473.0" prefWidth="673.0" text="在此反馈结果: 目录对应文件XXXX件,XXXX页; 实际文件XXXX件,XXXX页; 如比对一致,告知文件路径一致,文件名无误,无遗漏; 如有问题,则 ①文件路径不一致,请检查,可反馈档号,也可不反馈; ②反馈遗漏档号, ③或文件名错误,错误文件名为XXXX;" GridPane.columnIndex="4" GridPane.rowIndex="4">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
<ChoiceBox fx:id="loadFolderType2CB" prefHeight="23.0" prefWidth="98.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</ChoiceBox>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
</GridPane>
|
||||
7
src/main/resources/fxml/module/step-3-pane.fxml
Normal file
7
src/main/resources/fxml/module/step-3-pane.fxml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
</AnchorPane>
|
||||
7
src/main/resources/fxml/module/step-4-pane.fxml
Normal file
7
src/main/resources/fxml/module/step-4-pane.fxml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
</AnchorPane>
|
||||
7
src/main/resources/fxml/module/step-5-pane.fxml
Normal file
7
src/main/resources/fxml/module/step-5-pane.fxml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
</AnchorPane>
|
||||
7
src/main/resources/fxml/module/step-6-pane.fxml
Normal file
7
src/main/resources/fxml/module/step-6-pane.fxml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
</AnchorPane>
|
||||
7
src/main/resources/fxml/module/step-7-pane.fxml
Normal file
7
src/main/resources/fxml/module/step-7-pane.fxml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
|
||||
<AnchorPane xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
</AnchorPane>
|
||||
53
src/main/resources/logback.xml
Normal file
53
src/main/resources/logback.xml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="10 seconds">
|
||||
<property name="APP_NAME" value="DocCheckTool" />
|
||||
<property name="LOG_HOME" value="${log.dir:-logs}/${APP_NAME}"/>
|
||||
<property name="ENCODER_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" />
|
||||
<contextName>${APP_NAME}</contextName>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<Pattern>${ENCODER_PATTERN}</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/output.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>7</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${ENCODER_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="ERROR_FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>7</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${ENCODER_PATTERN}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>WARN</level>
|
||||
</filter>
|
||||
</appender>
|
||||
<appender name="SYNC_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/sync.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>7</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${ENCODER_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="log.sync" level="DEBUG" addtivity="true">
|
||||
<appender-ref ref="SYNC_FILE" />
|
||||
</logger>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
<appender-ref ref="ERROR_FILE" />
|
||||
</root>
|
||||
</configuration>
|
||||
Loading…
Reference in New Issue
Block a user