Browse Source

初始化

heyiwen 5 days ago
commit
5e94ed188c
100 changed files with 404741 additions and 0 deletions
  1. 10 0
      .trae/rules/project_rules.md
  2. 41 0
      .trae/skills/common-database-fields/SKILL.md
  3. 65 0
      .trae/skills/common-query-dto-base-class/SKILL.md
  4. 69 0
      .trae/skills/data-caching-patterns/SKILL.md
  5. 48 0
      .trae/skills/database-entity-validation/SKILL.md
  6. 26 0
      .trae/skills/database-field-validation/SKILL.md
  7. 95 0
      .trae/skills/db-table-analyzer/SKILL.md
  8. 202 0
      .trae/skills/frontend-anti-duplication/SKILL.md
  9. 58 0
      .trae/skills/frontend-unused-variables-check/SKILL.md
  10. 158 0
      .trae/skills/function-permission-system/SKILL.md
  11. 52 0
      .trae/skills/multi-project-debugger/SKILL.md
  12. 216 0
      .trae/skills/web-dev-best-practices/SKILL.md
  13. 463 0
      .vscode/changelists.json
  14. 10 0
      FlinkDataSync/.idea/.gitignore
  15. 13 0
      FlinkDataSync/.idea/compiler.xml
  16. 7 0
      FlinkDataSync/.idea/encodings.xml
  17. 20 0
      FlinkDataSync/.idea/jarRepositories.xml
  18. 12 0
      FlinkDataSync/.idea/misc.xml
  19. 6 0
      FlinkDataSync/.idea/vcs.xml
  20. 75 0
      FlinkDataSync/dependency-reduced-pom.xml
  21. 13849 0
      FlinkDataSync/flink-sync.log
  22. 54695 0
      FlinkDataSync/flink-sync.log.1
  23. 54645 0
      FlinkDataSync/flink-sync.log.2
  24. 54897 0
      FlinkDataSync/flink-sync.log.3
  25. 54770 0
      FlinkDataSync/flink-sync.log.4
  26. 57134 0
      FlinkDataSync/flink-sync.log.5
  27. 54770 0
      FlinkDataSync/flink-sync.log.6
  28. 57134 0
      FlinkDataSync/flink-sync.log.7
  29. 136 0
      FlinkDataSync/pom.xml
  30. 89 0
      FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlCdcSync.java
  31. 684 0
      FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlSink.java
  32. 21 0
      FlinkDataSync/src/main/resources/log4j.properties
  33. BIN
      FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlCdcSync.class
  34. BIN
      FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlSink.class
  35. 21 0
      FlinkDataSync/target/classes/log4j.properties
  36. BIN
      FlinkDataSync/target/flink-data-sync-1.0-SNAPSHOT.jar
  37. 5 0
      FlinkDataSync/target/maven-archiver/pom.properties
  38. 2 0
      FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
  39. 2 0
      FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
  40. BIN
      FlinkDataSync/target/original-flink-data-sync-1.0-SNAPSHOT.jar
  41. 10 0
      JavaBackend/.idea/.gitignore
  42. 19 0
      JavaBackend/.idea/compiler.xml
  43. 6 0
      JavaBackend/.idea/encodings.xml
  44. 20 0
      JavaBackend/.idea/jarRepositories.xml
  45. 12 0
      JavaBackend/.idea/misc.xml
  46. 6 0
      JavaBackend/.idea/vcs.xml
  47. 1 0
      JavaBackend/.mvn/jvm.config
  48. 1 0
      JavaBackend/.mvn/wrapper/maven-wrapper.properties
  49. 166 0
      JavaBackend/hs_err_pid26756.log
  50. BIN
      JavaBackend/lib/HikariCP-4.0.3.jar
  51. BIN
      JavaBackend/lib/accessors-smart-2.4.11.jar
  52. BIN
      JavaBackend/lib/android-json-0.0.20131108.vaadin1.jar
  53. BIN
      JavaBackend/lib/antlr-2.7.7.jar
  54. BIN
      JavaBackend/lib/apiguardian-api-1.1.2.jar
  55. BIN
      JavaBackend/lib/asm-9.3.jar
  56. BIN
      JavaBackend/lib/aspectjweaver-1.9.7.jar
  57. BIN
      JavaBackend/lib/assertj-core-3.22.0.jar
  58. BIN
      JavaBackend/lib/byte-buddy-1.12.23.jar
  59. BIN
      JavaBackend/lib/byte-buddy-agent-1.12.23.jar
  60. BIN
      JavaBackend/lib/classmate-1.5.1.jar
  61. BIN
      JavaBackend/lib/commons-csv-1.9.0.jar
  62. BIN
      JavaBackend/lib/hamcrest-2.2.jar
  63. BIN
      JavaBackend/lib/hibernate-commons-annotations-5.1.2.Final.jar
  64. BIN
      JavaBackend/lib/hibernate-core-5.6.15.Final.jar
  65. BIN
      JavaBackend/lib/istack-commons-runtime-3.0.12.jar
  66. BIN
      JavaBackend/lib/jackson-annotations-2.13.5.jar
  67. BIN
      JavaBackend/lib/jackson-core-2.13.5.jar
  68. BIN
      JavaBackend/lib/jackson-databind-2.13.5.jar
  69. BIN
      JavaBackend/lib/jackson-datatype-jdk8-2.13.5.jar
  70. BIN
      JavaBackend/lib/jackson-datatype-jsr310-2.13.5.jar
  71. BIN
      JavaBackend/lib/jackson-module-parameter-names-2.13.5.jar
  72. BIN
      JavaBackend/lib/jakarta.activation-1.2.2.jar
  73. BIN
      JavaBackend/lib/jakarta.activation-api-1.2.2.jar
  74. BIN
      JavaBackend/lib/jakarta.annotation-api-1.3.5.jar
  75. BIN
      JavaBackend/lib/jakarta.persistence-api-2.2.3.jar
  76. BIN
      JavaBackend/lib/jakarta.transaction-api-1.3.3.jar
  77. BIN
      JavaBackend/lib/jakarta.xml.bind-api-2.3.3.jar
  78. BIN
      JavaBackend/lib/jandex-2.4.2.Final.jar
  79. BIN
      JavaBackend/lib/jaxb-runtime-2.3.8.jar
  80. BIN
      JavaBackend/lib/jboss-logging-3.4.3.Final.jar
  81. BIN
      JavaBackend/lib/jjwt-0.9.1.jar
  82. BIN
      JavaBackend/lib/json-path-2.7.0.jar
  83. BIN
      JavaBackend/lib/json-smart-2.4.11.jar
  84. BIN
      JavaBackend/lib/jsonassert-1.5.1.jar
  85. BIN
      JavaBackend/lib/jul-to-slf4j-1.7.36.jar
  86. BIN
      JavaBackend/lib/junit-jupiter-5.8.2.jar
  87. BIN
      JavaBackend/lib/junit-jupiter-api-5.8.2.jar
  88. BIN
      JavaBackend/lib/junit-jupiter-engine-5.8.2.jar
  89. BIN
      JavaBackend/lib/junit-jupiter-params-5.8.2.jar
  90. BIN
      JavaBackend/lib/junit-platform-commons-1.8.2.jar
  91. BIN
      JavaBackend/lib/junit-platform-engine-1.8.2.jar
  92. BIN
      JavaBackend/lib/log4j-api-2.17.2.jar
  93. BIN
      JavaBackend/lib/log4j-to-slf4j-2.17.2.jar
  94. BIN
      JavaBackend/lib/logback-classic-1.2.12.jar
  95. BIN
      JavaBackend/lib/logback-core-1.2.12.jar
  96. BIN
      JavaBackend/lib/lombok-1.18.28.jar
  97. BIN
      JavaBackend/lib/mockito-core-4.5.1.jar
  98. BIN
      JavaBackend/lib/mockito-junit-jupiter-4.5.1.jar
  99. BIN
      JavaBackend/lib/mysql-connector-java-8.0.28.jar
  100. 0 0
      JavaBackend/lib/objenesis-3.2.jar

+ 10 - 0
.trae/rules/project_rules.md

@@ -0,0 +1,10 @@
+获取Sys_DictionaryItem的方式不能只传递Value值,因为这是个字典集合,很多个字典都会有同样的Value,应当增加DictionaryCode的校验,数据库结构的markdown文件中的说明列里,会告诉你每个字段对应的是哪个DictionaryCode
+先找到项目里的.pdm文件或用户上传的上下文,如果能从中导出表和字段的信息,则以此为准,不能随意修改数据库的结构和数据
+上下文中的数据库设计文档集才是最终的数据库结构标准
+必须完全信任数据库文档,数据库的结构和文档必然是一致的
+单一SQL语句返回完整的列表信息是最优的选择
+SQLServer数据库的版本是2008 R2,选择使用它的存储过程和函数时,要注意版本兼容性问题,避免使用2012及以上版本的功能
+只有tugboatcommon和liandatugboatmis这两个库同时存在的表才需要读写分离,即写入到tugboatcommon库里,从liandatugboatmis库读取数据,其他表都在各自的数据库里读写即可
+为了方便理解,说明一下:tugboatcommon库是系统公共库,liandatugboatmis是分支机构的业务库,公共库存放所有机构共享的数据,而分支机构的业务库则存放各自机构的业务数据,他们并不是单纯的读库和写库的区别
+修改哪个项目就重新调试哪个项目,不要每次都全部重新启动调试
+技能文档永远是对的

+ 41 - 0
.trae/skills/common-database-fields/SKILL.md

@@ -0,0 +1,41 @@
+# 数据库表公共字段规范
+
+## 概述
+在系统中,大多数数据表都包含一组标准的公共字段,用于追踪记录的状态和审计信息。
+
+## 公共字段列表
+以下是系统中所有表的标准公共字段:
+
+1. **RecordStatus** (`int`)
+   - 含义:记录状态(如:1=正常,2=已调度,3=已完成,4=已取消等)
+   - 用途:表示记录的业务状态
+
+2. **CreateUserID** (`uniqueidentifier`)
+   - 含义:创建人ID
+   - 用途:记录首次创建该记录的用户ID
+
+3. **CreateTime** (`datetime`)
+   - 含义:创建时间
+   - 用途:记录首次创建的时间
+
+4. **ModifyUserID** (`uniqueidentifier`)
+   - 含义:修改人ID
+   - 用途:记录最后一次修改该记录的用户ID
+
+5. **ModifyTime** (`datetime`)
+   - 含义:修改时间
+   - 用途:记录最后一次修改的时间
+
+## 使用原则
+1. **复制数据时的处理**:在从一个表向另一个表复制数据时,不应覆盖这些公共字段
+2. **状态管理**:RecordStatus字段控制记录的业务状态流转
+3. **审计追踪**:Create和Modify字段提供完整的数据变更历史
+4. **一致性**:所有业务表都应该包含这些公共字段
+
+## 例外情况
+某些特殊表可能根据业务需要省略部分公共字段,但这种情况应有明确的业务理由。
+
+## 业务影响
+- 这些字段是系统权限控制、数据审计、状态管理的基础
+- 在数据迁移或同步时,必须特别注意这些字段的处理规则
+- 任何表结构变更都不得随意删除这些公共字段

+ 65 - 0
.trae/skills/common-query-dto-base-class/SKILL.md

@@ -0,0 +1,65 @@
+# 通用查询DTO基类设计技能
+
+## 技能描述
+创建通用的查询DTO基类,封装常用的分页和排序属性,便于后续创建的表单复用。
+
+## 设计原则
+
+### 1. 继承架构
+- 创建BaseQueryDTO作为所有查询DTO的基类
+- 封装通用的分页和排序属性
+- 子类继承基类并添加特定业务属性
+
+### 2. 通用属性
+- `page`: 页码,默认为1
+- `pageSize`: 每页大小,默认为20
+- `sortBy`: 排序列名
+- `sortOrder`: 排序方向(ASC/DESC)
+
+### 3. 验证机制
+- 在setSortOrder方法中验证排序方向的有效性
+- 只允许ASC或DESC两种排序方式(忽略大小写)
+
+## 实现模式
+
+### BaseQueryDTO基类
+```java
+public class BaseQueryDTO {
+    private Integer page = 1;    // 页码,默认第一页
+    private Integer pageSize = 20; // 每页大小,默认20条
+    private String sortBy;  // 排序列名
+    private String sortOrder;  // 排序方向,ASC或DESC
+    
+    // 相应的getter和setter方法
+}
+```
+
+### 业务DTO类
+```java
+public class BusinessQueryDTO extends BaseQueryDTO {
+    // 业务特定的属性
+    private String businessField;
+    
+    // 业务特定的getter和setter方法
+}
+```
+
+## 优势
+
+1. **代码复用**: 避免在每个查询DTO中重复编写相同的分页和排序属性
+2. **维护性**: 统一管理分页和排序相关的代码变更
+3. **一致性**: 确保所有查询接口使用相同的标准分页和排序参数
+4. **扩展性**: 便于在未来向所有查询DTO添加通用功能
+
+## 注意事项
+
+1. **向后兼容**: 确保API接口参数名称保持不变
+2. **验证逻辑**: 在基类中统一处理参数验证
+3. **文档更新**: 更新API文档以反映新的继承关系
+4. **测试覆盖**: 确保继承基类的子类仍能正常工作
+
+## 使用场景
+
+- 创建新的查询DTO时,优先考虑继承BaseQueryDTO
+- 当多个DTO需要相同的分页和排序功能时
+- 当需要标准化查询接口的分页参数时

+ 69 - 0
.trae/skills/data-caching-patterns/SKILL.md

@@ -0,0 +1,69 @@
+# 数据缓存最佳实践
+
+## 概述
+对于频繁访问且数据量不大的表,应使用缓存机制提高性能。
+
+## 缓存原则
+1. **适合缓存的表特征**:
+   - 数据量小(通常少于1000条记录)
+   - 访问频率高
+   - 更新频率低
+   - 作为基础配置数据使用
+
+2. **缓存策略**:
+   - 应用启动时预加载数据
+   - 使用ConcurrentHashMap保证线程安全
+   - 提供刷新缓存的方法
+   - 使用懒加载模式按需初始化
+
+## 实现模板
+```java
+@Component
+public class CacheService {
+    private static Map<Integer, DataType> cacheMap = new ConcurrentHashMap<>();
+    private static boolean isInitialized = false;
+    
+    @Autowired
+    private RepositoryType repository;
+    
+    @PostConstruct
+    public void initCache() {
+        loadDataToCache();
+    }
+    
+    public synchronized void refreshCache() {
+        cacheMap.clear();
+        loadDataToCache();
+    }
+    
+    private void loadDataToCache() {
+        List<DataType> dataList = repository.findAll();
+        cacheMap = dataList.stream()
+            .collect(Collectors.toConcurrentMap(DataType::getKeyField, Function.identity()));
+        isInitialized = true;
+    }
+    
+    public DataType getByKey(Integer key) {
+        if (!isInitialized) {
+            synchronized (CacheService.class) {
+                if (!isInitialized) {
+                    loadDataToCache();
+                }
+            }
+        }
+        return cacheMap.get(key);
+    }
+}
+```
+
+## 适用场景示例
+- 字典表数据
+- 作业类型设置
+- 配置参数表
+- 基础业务规则表
+
+## 注意事项
+- 确保缓存数据的一致性
+- 在源数据更新时同步更新缓存
+- 考虑内存占用和垃圾回收
+- 使用合适的并发容器保证线程安全

+ 48 - 0
.trae/skills/database-entity-validation/SKILL.md

@@ -0,0 +1,48 @@
+# 数据库实体验证最佳实践
+
+## 问题概述
+在开发过程中,经常出现实体类字段与数据库表结构不一致的情况。这通常是由于在不了解真实数据库结构的情况下,凭经验或猜测添加字段导致的。
+
+## 核心原则
+**始终以数据库文档为准**:在创建或修改实体类之前,必须先查阅数据库设计文档,确保实体类与数据库表结构完全一致。
+
+## 操作流程
+1. **查找数据库文档**:优先查找项目中的数据库设计文档(如 `.pdm` 文件、`.md` 文档等)
+2. **对照表结构**:逐一对比数据库表字段与实体类字段
+3. **验证字段映射**:
+   - 数据库字段名 ↔ 实体类字段名(使用 @Column 注解)
+   - 数据类型匹配
+   - 必填字段标识
+   - 主键/外键关系
+4. **同步更新**:确保实体类包含数据库表的所有字段
+
+## 常见陷阱
+- 仅凭业务逻辑推测字段名(如将 WorkTime 错误地映射为 PlanTime)
+- 遗漏重要字段(如 CreateTime/ModifyTime、CreateUserID/ModifyUserID)
+- 混淆相似字段(如 Status 与 RecordStatus)
+- 字段类型不匹配(如将 int 映射为 String)
+
+## 最佳实践
+- 在项目开始阶段建立完整的数据库文档查阅习惯
+- 创建实体类后,再次核对数据库文档确认一致性
+- 使用工具或脚本来验证实体类与数据库表的映射关系
+- 建立团队规范,要求所有实体类变更必须基于数据库文档
+
+## 检查清单
+- [ ] 数据库文档已查阅
+- [ ] 实体类字段与数据库表字段一一对应
+- [ ] Column 注解正确映射数据库字段名
+- [ ] 数据类型匹配
+- [ ] 必填字段正确标识
+- [ ] 时间戳字段(创建时间、修改时间)已包含
+- [ ] 用户ID字段(创建用户、修改用户)已包含
+- [ ] 状态字段(如 RecordStatus)已包含
+
+## 修复策略
+当发现实体类与数据库不一致时:
+1. 立即停止相关开发工作
+2. 重新查阅数据库文档
+3. 对照实体类,标识所有不匹配的字段
+4. 按照数据库文档完整更新实体类
+5. 更新相关的 Repository、Service 和 Controller 代码
+6. 重新编译和测试

+ 26 - 0
.trae/skills/database-field-validation/SKILL.md

@@ -0,0 +1,26 @@
+# 数据库字段验证最佳实践
+
+## 核心原则
+在判断实体类是否包含某个字段时,始终以数据库文档为准,而不是仅依赖实体类的当前实现。
+
+## 关键要点
+1. **数据库文档优先**:始终参考数据库设计文档来确认表结构和字段存在性
+2. **实体类同步**:确保实体类与数据库表结构保持同步
+3. **字段验证**:在实现业务逻辑前,先验证实体类是否包含所需的数据库字段
+
+## 实践步骤
+1. 检查数据库设计文档或数据库表结构
+2. 验证实体类中是否包含相应字段
+3. 如缺少字段,及时更新实体类以匹配数据库结构
+4. 添加相应的getter/setter方法
+5. 更新Repository接口以支持新字段的查询操作
+
+## 示例场景
+- 当业务需求涉及RecordStatus字段时,需要先确认该字段在目标表中是否存在
+- 如果Disp_Dispatcher表有RecordStatus字段,实体类也应包含此字段
+- 确保所有CRUD操作考虑到新增字段的存在
+
+## 注意事项
+- 在进行数据操作前,务必确保实体类结构与数据库表结构一致
+- 对于跨表关联的业务逻辑,特别注意各表之间的字段关系
+- 保持实体类、Repository接口和业务逻辑的一致性

+ 95 - 0
.trae/skills/db-table-analyzer/SKILL.md

@@ -0,0 +1,95 @@
+---
+name: "db-table-analyzer"
+description: "Analyzes database tables for read/write separation rules. Identifies which tables exist in both tugboatcommon and liandatugboatmis databases and need read/write separation. Invoke when checking or fixing data source configurations."
+---
+
+# Database Table Analyzer
+
+This skill helps identify which tables need data source separation based on the FlinkDataSync configuration and actual database architecture.
+
+## Database Architecture
+
+- **tugboatcommon**: System common library, stores data shared by all institutions
+- **liandatugboatmis**: Branch institution business library, stores business data for individual institutions
+
+This is not a simple read/write separation but rather a distinction between public/shared data and institutional business data.
+
+## Tables That Exist in Both Databases
+
+Based on FlinkDataSync's tableList configuration, the following tables exist in both tugboatcommon and liandatugboatmis databases and need proper data source routing:
+
+### Dispatch Tables (Disp_*)
+- `Disp_Port` - Port information
+- `Disp_PortDictionary` - Port dictionary
+- `Disp_Tugboat` - Tugboat information
+- `Disp_DeepLevel` - Deep level information
+- `Disp_Berthage` - Berthage information
+- `Disp_BerthageDictionary` - Berthage dictionary
+- `Disp_Pilot` - Pilot information
+- `Disp_Tugboatowner` - Tugboat owner information
+- `Disp_Waterway` - Waterway information
+
+### Business Tables (Bus_*)
+- `Bus_Holiday` - Holiday information
+- `Bus_ShipPaymentType` - Ship payment types
+- `Bus_ShipPaymentType_ShipType` - Relationship between ship payment types and ship types
+
+### Financial Tables (Fin_*)
+- `Fin_AssistCode` - Financial assist codes
+- `Fin_AssistCodeItem` - Financial assist code items
+- `Fin_Subject` - Financial subjects
+- `Fin_Subject_AssistCode` - Relationship between financial subjects and assist codes
+- `Fin_TaxRule` - Tax rules
+
+### Process Tables (Pro_*)
+- `Pro_FeeItemAssistCode` - Fee item assist codes
+- `Pro_FeeItemSettings` - Fee item settings
+
+### Salary Tables (Sal_*)
+- `Sal_Holiday` - Salary holiday information
+- `Sal_SocialInsuranceStandard` - Social insurance standards
+- `Sal_TaxLevel` - Tax levels
+
+### Tugboat Tables (Tug_*)
+- `Tug_Certificate` - Certificate information
+
+## Tables That Exist Only in One Database
+
+Tables that exist only in one database and should use the appropriate data source:
+
+### Customer Business Tables (exist in liandatugboatmis - branch business data)
+- `Bus_CustomerCompany` - Customer company information
+- `Bus_CustomerCompanyBusiness` - Customer company business relationships
+- `Bus_Customer_CustomerType` - Customer types
+- `Bus_Ship` - Ship information
+
+### System Tables (typically exist in tugboatcommon - common data)
+- `Sys_Menu` - System menus
+- `Sys_User` - System users
+- `Sys_DictionaryItem` - System dictionary items
+
+## Implementation Guidelines
+
+1. **For tables that exist in both databases**:
+   - Use `@DataSource(RoutingDataSourceConfig.DataSourceType.COMMON)` for common/shared data operations (tugboatcommon - system common library)
+   - Use `@DataSource(RoutingDataSourceConfig.DataSourceType.BRANCH)` for branch-specific operations (liandatugboatmis - branch institution business library)
+
+2. **For tables that exist only in liandatugboatmis (branch business data)**:
+   - Use `@DataSource(RoutingDataSourceConfig.DataSourceType.BRANCH)` consistently
+   - This applies to customer, ship, and business-related tables
+ 
+3. **For tables that exist only in tugboatcommon (common data)**:
+   - Use `@DataSource(RoutingDataSourceConfig.DataSourceType.COMMON)` consistently
+   - This applies to system configuration tables
+
+## Naming Convention
+
+The new naming convention clearly reflects the database architecture:
+- `COMMON` → tugboatcommon: System common library, stores data shared by all institutions
+- `BRANCH` → liandatugboatmis: Branch institution business library, stores business data for individual institutions
+
+This eliminates confusion compared to the previous READ/WRITE naming that suggested a simple read/write separation.
+
+## Validation
+
+Always verify that the `@DataSource` annotations align with the actual database architecture and business requirements.

+ 202 - 0
.trae/skills/frontend-anti-duplication/SKILL.md

@@ -0,0 +1,202 @@
+# 前端防重复点击最佳实践
+
+## 问题描述
+在Web应用中,当用户快速多次点击按钮或触发异步操作时,可能会导致:
+1. 多次发送相同的请求
+2. 数据重复提交
+3. 用户体验不佳
+4. 系统资源浪费
+
+## 解决方案
+在所有触发后台操作的前端事件中添加进度条和防重复点击机制。
+
+## 实现方法
+
+### 1. 使用loading状态控制
+在按钮上绑定loading属性,在请求期间禁用按钮并显示加载状态。
+
+```vue
+<template>
+  <el-button 
+    :loading="loading" 
+    @click="handleAction"
+    :disabled="loading">
+    {{ loading ? '处理中...' : '提交' }}
+  </el-button>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      loading: false
+    }
+  },
+  methods: {
+    async handleAction() {
+      if (this.loading) return; // 防止重复点击
+      
+      this.loading = true;
+      try {
+        // 执行后台操作
+        await this.apiCall();
+      } finally {
+        this.loading = false; // 确保无论成功失败都恢复状态
+      }
+    }
+  }
+}
+</script>
+```
+
+### 2. 全局Loading覆盖
+对于页面级别的操作,可以使用全局Loading遮罩:
+
+```javascript
+methods: {
+  async handleAction() {
+    const loadingInstance = this.$loading({
+      lock: true,
+      text: '处理中...',
+      spinner: 'el-icon-loading',
+      background: 'rgba(0, 0, 0, 0.7)'
+    });
+    
+    try {
+      await this.apiCall();
+    } finally {
+      loadingInstance.close();
+    }
+  }
+}
+```
+
+### 3. 防抖(debounce)和节流(throttle)技术
+对于搜索框等频繁触发的操作,可以使用防抖或节流:
+
+```javascript
+import { debounce } from 'lodash';
+
+export default {
+  methods: {
+    // 防抖:延迟执行,适用于搜索
+    search: debounce(function(query) {
+      this.performSearch(query);
+    }, 300),
+    
+    // 节流:固定频率执行,适用于滚动等
+    throttleClick: throttle(function() {
+      this.handleScroll();
+    }, 1000)
+  }
+}
+```
+
+### 4. 统一组件封装
+创建可复用的防重复点击组件:
+
+```vue
+<!-- ReusableButton.vue -->
+<template>
+  <el-button 
+    v-bind="$attrs" 
+    :loading="isLoading" 
+    @click="handleClick"
+    :disabled="isLoading || disabled">
+    <slot></slot>
+  </el-button>
+</template>
+
+<script>
+export default {
+  name: 'ReusableButton',
+  props: {
+    disabled: Boolean,
+    apiFunction: Function,
+    autoReset: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      isLoading: false
+    }
+  },
+  methods: {
+    async handleClick(event) {
+      if (this.isLoading || this.disabled) return;
+      
+      this.isLoading = true;
+      try {
+        await this.apiFunction();
+      } finally {
+        if (this.autoReset) {
+          this.isLoading = false;
+        }
+      }
+    }
+  }
+}
+</script>
+```
+
+### 5. 在表单提交中的应用
+```vue
+<template>
+  <el-form :model="form" ref="formRef">
+    <!-- 表单内容 -->
+    <el-form-item>
+      <el-button 
+        type="primary" 
+        :loading="submitting" 
+        @click="submitForm">
+        {{ submitting ? '提交中...' : '提交' }}
+      </el-button>
+    </el-form-item>
+  </el-form>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      submitting: false,
+      form: {}
+    }
+  },
+  methods: {
+    async submitForm() {
+      if (this.submitting) return; // 防重复提交
+      
+      this.submitting = true;
+      try {
+        await this.$refs.formRef.validate();
+        await this.submitToBackend(this.form);
+        this.$message.success('提交成功');
+      } catch (error) {
+        this.$message.error('提交失败');
+      } finally {
+        this.submitting = false;
+      }
+    }
+  }
+}
+</script>
+```
+
+## 应用场景
+以下操作都应该添加防重复点击机制:
+- 表单提交
+- 数据删除
+- 文件上传/下载
+- 搜索查询
+- 状态切换
+- 导出操作
+- 任何发起网络请求的操作
+
+## 注意事项
+1. 确保在finally块中重置loading状态,以防止异常情况下按钮一直处于loading状态
+2. 对于长时间操作,提供清晰的反馈信息
+3. 合理设置防抖/节流的时间间隔
+4. 在移动端尤其需要注意防重复点击,因为触摸操作更容易产生重复点击

+ 58 - 0
.trae/skills/frontend-unused-variables-check/SKILL.md

@@ -0,0 +1,58 @@
+# 前端未使用变量检查技能
+
+## 技能描述
+在修改前端页面时,需要检查是否有声明但未使用的变量,避免ESLint警告和代码质量问题。
+
+## 常见场景
+
+### 1. 解构赋值中的未使用变量
+- **问题**: 在函数参数解构或对象解构时,声明了变量但未实际使用
+- **示例**: `function({ usedVar, unusedVar })` 中只使用了 `usedVar`
+- **解决方案**: 只解构需要使用的变量,如 `function({ usedVar })`
+
+### 2. 函数参数中的未使用变量
+- **问题**: 函数定义了参数但未在函数体内使用
+- **示例**: `function(param1, param2)` 中只使用了 `param1`
+- **解决方案**: 移除未使用的参数或添加下划线前缀表示故意未使用
+
+### 3. 循环中的未使用索引
+- **问题**: 在 `forEach`, `map` 等循环中声明了索引参数但未使用
+- **示例**: `arr.forEach((item, index) => {...})` 中未使用 `index`
+- **解决方案**: 如果不需要索引,使用 `(item) =>` 或使用下划线 `(item, _) =>`
+
+### 4. 事件处理函数中的未使用参数
+- **问题**: 事件处理函数接收了多个参数但只使用部分参数
+- **示例**: `handleEvent({ column, prop, order })` 只使用了 `prop` 和 `order`
+- **解决方案**: 只解构需要的参数
+
+## 检查清单
+
+在修改前端页面时,请检查以下内容:
+
+- [ ] 函数参数中是否有未使用的变量
+- [ ] 解构赋值中是否有未使用的属性
+- [ ] 事件处理函数参数是否全部使用
+- [ ] 循环函数中的索引/键参数是否使用
+- [ ] 声明的局部变量是否都已使用
+- [ ] 从API或props中解构的变量是否都已使用
+
+## ESLint警告处理
+
+常见ESLint警告:
+- `'variable' is defined but never used`
+- `'parameter' is defined but never used`
+- `Expected to return a value in function`
+
+## 修复技巧
+
+1. **移除未使用变量**: 直接从解构或参数列表中移除
+2. **使用下划线前缀**: 对于故意不使用的参数,使用 `_` 前缀
+3. **重构函数签名**: 简化函数参数以匹配实际使用需求
+4. **注释说明**: 对于暂时未使用的变量添加注释说明未来用途
+
+## 测试验证
+
+修改完成后,执行以下验证:
+- 检查浏览器控制台是否有ESLint相关警告
+- 运行项目的lint命令(如 `npm run lint`)
+- 确认功能正常工作且无新增警告

+ 158 - 0
.trae/skills/function-permission-system/SKILL.md

@@ -0,0 +1,158 @@
+# 功能权限控制系统实现技能
+
+## 技能描述
+实现一个完整的功能权限控制系统,包括后端权限验证和前端按钮控制。
+
+## 系统架构
+
+### 1. 数据模型
+- `Sys_User`: 用户表
+- `Sys_Role`: 角色表
+- `Sys_FunctionCode`: 功能代码表
+- `Sys_User_Sys_Role`: 用户角色关联表
+- `Sys_Role_Sys_FunctionCode`: 角色功能代码关联表
+
+### 2. 认证服务实现(com.lianda.auth)
+
+#### 核心组件
+- **实体类**: 
+  - SysUser, SysRole, SysFunctionCode
+  - SysUserRole, SysRoleFunctionCode
+  - UserRoleKey, RoleFunctionCodeKey
+
+- **Repository层**:
+  - SysFunctionCodeRepository
+  - SysUserRoleRepository
+  - SysRoleFunctionCodeRepository
+
+- **Service层**:
+  - UserPermissionService: 获取用户功能代码
+  - UserPermissionServiceImpl: 实现PermissionService接口
+
+- **权限验证**:
+  - 在用户登录时获取用户的功能代码列表
+  - 将功能代码列表添加到JWT Token中
+
+#### JWT Token扩展
+- 登录时获取用户角色关联的功能代码
+- 将功能代码列表添加到JWT Token中
+- 前端和后端都可以从Token中解析权限信息
+
+### 3. 后端服务实现(JavaBackend)
+
+#### 权限验证
+- RequirePermission注解: 标记需要权限的方法
+- PermissionInterceptor: 拦截器验证权限
+- CurrentUserUtil: 从JWT Token中解析用户权限
+
+### 4. 前端实现
+
+#### 核心组件
+- **权限管理工具** (permission.js):
+  - 解析JWT Token中的功能代码
+  - 检查用户权限
+  - 管理本地权限缓存
+
+- **权限指令** (permission.js):
+  - Vue指令控制按钮显示
+  - 根据权限动态显示/隐藏元素
+
+- **Axios拦截器**:
+  - 响应拦截器提取功能代码
+  - 更新本地权限缓存
+  - 处理权限错误
+
+## 使用方法
+
+### 1. 后端使用
+
+#### 保护API端点
+```java
+@RestController
+@RequestMapping("/api/pilot-plan")
+public class PilotPlanController {
+    
+    @GetMapping("/list")
+    @RequirePermission("PILOT_PLAN_VIEW")  // 需要查看权限
+    public ResponseEntity<PageResponse<PilotPlanDTO>> getPilotPlans(PilotPlanQueryDTO queryDTO) {
+        // ...
+    }
+    
+    @PostMapping("/import")
+    @RequirePermission("PILOT_PLAN_IMPORT")  // 需要导入权限
+    public ResponseEntity<Map<String, Object>> importPilotPlans(@RequestBody Map<String, Object> request) {
+        // ...
+    }
+}
+```
+
+### 2. 前端使用
+
+#### 控制按钮显示
+```vue
+<template>
+  <div>
+    <!-- 只有拥有PILOT_PLAN_IMPORT权限的用户才能看到导入按钮 -->
+    <el-button type="primary" @click="importData" v-permission="'PILOT_PLAN_IMPORT'">
+      导入数据
+    </el-button>
+    
+    <!-- 只有拥有PILOT_PLAN_EXPORT权限的用户才能看到导出按钮 -->
+    <el-button type="success" @click="exportData" v-permission="'PILOT_PLAN_EXPORT'">
+      导出数据
+    </el-button>
+  </div>
+</template>
+```
+
+#### 编程方式检查权限
+```javascript
+import PermissionManager from '@/utils/permission.js'
+
+// 检查用户是否有特定权限
+if (PermissionManager.hasPermission('PILOT_PLAN_EDIT')) {
+  // 用户有编辑权限,执行相应操作
+  enableEditButtons();
+} else {
+  // 用户没有编辑权限,禁用相关功能
+  disableEditButtons();
+}
+```
+
+## 工作流程
+
+1. **用户登录** (在认证服务中):
+   - 验证用户名密码
+   - 查询用户的角色
+   - 根据角色获取功能代码列表
+   - 将功能代码列表编码到JWT Token中
+   - 返回Token给前端
+
+2. **前端存储权限**:
+   - 从JWT Token中解析功能代码
+   - 存储到localStorage中
+
+3. **前端权限控制**:
+   - 使用v-permission指令控制按钮显示
+   - 指令检查用户是否拥有指定功能代码
+   - 无权限时隐藏按钮
+
+4. **后端权限验证** (在JavaBackend中):
+   - 请求到达时检查@RequirePermission注解
+   - 从Token中解析功能代码
+   - 验证用户是否拥有所需权限
+   - 无权限时返回403错误
+
+## 安全考虑
+
+- 权限验证在后端进行,前端控制仅为用户体验优化
+- 即使绕过前端权限控制,后端仍有安全防护
+- JWT Token中的权限信息经过签名保护,防止篡改
+- 敏感操作必须经过后端权限验证
+
+## 维护要点
+
+- 权限配置在数据库中管理,无需修改代码
+- 可以灵活分配用户-角色-功能代码关系
+- Token过期后会重新获取最新权限信息
+- 前端权限缓存会在Token更新时同步更新

+ 52 - 0
.trae/skills/multi-project-debugger/SKILL.md

@@ -0,0 +1,52 @@
+# Multi-Project Debugger
+
+## Project Execution Guide
+
+This skill helps debug the following projects simultaneously:
+- FlinkDataSync
+- com.lianda.auth (Authentication Service)
+- JavaBackEnd (Main Backend Service)
+- vue-frontend (Frontend Application)
+
+## Accurate Execution Commands
+
+### 1. JavaBackend
+**Location:** `d:\Project\LiandaTugboat\Code\JavaBackend`
+**Command:** `mvn spring-boot:run`
+**Port:** 8080
+**Purpose:** Main backend service with business logic
+
+### 2. vue-frontend
+**Location:** `d:\Project\LiandaTugboat\Code\vue-frontend`
+**Command:** `npm run serve`
+**Port:** 8082
+**Purpose:** Frontend user interface
+
+### 3. com.lianda.auth
+**Location:** `d:\Project\LiandaTugboat\Code\com.lianda.auth`
+**Command:** `mvn spring-boot:run`
+**Port:** 8083
+**Context Path:** `/auth`
+**Purpose:** Authentication and authorization service
+
+### 4. FlinkDataSync
+**Location:** `d:\Project\LiandaTugboat\Code\FlinkDataSync`
+**Build Command:** `mvn clean package -DskipTests`
+**Run Command:** `java -cp target/flink-data-sync-1.0-SNAPSHOT.jar com.lianda.flink.sync.MySqlCdcSync`
+**Purpose:** Real-time data synchronization between databases using Flink CDC
+
+## When to Use
+Invoke this skill when the user asks to debug multiple projects, troubleshoot integration issues, or needs to understand how these systems work together.
+
+## Project Overview
+1. **FlinkDataSync**: Handles data synchronization processes using Apache Flink and CDC technology
+2. **com.lianda.auth**: Authentication and authorization service running on port 8083 with context path /auth
+3. **JavaBackEnd**: Main business logic and API layer running on port 8080
+4. **vue-frontend**: User interface layer that communicates with backend services
+
+## Troubleshooting Tips
+- If FlinkDataSync fails to start, ensure the build completes successfully first
+- Check that all database connections are properly configured
+- Verify that ports 8080, 8082, and 8083 are available
+- For FlinkDataSync, ensure proper database credentials are configured in the application
+- Monitor logs across all services to track inter-service communication

+ 216 - 0
.trae/skills/web-dev-best-practices/SKILL.md

@@ -0,0 +1,216 @@
+---
+name: "web-dev-best-practices"
+description: "Provides best practices for web development including date handling, internationalization, pagination, and data source configuration. Invoke when developing new pages or troubleshooting common web app issues."
+---
+
+# Web开发最佳实践指南
+
+## 1. 时间日期处理最佳实践
+
+### 问题类型
+- 前端日期字符串与后端Date对象转换问题
+- GET请求参数中的日期格式处理
+- JSON序列化中的日期格式处理
+- 前端显示的日期格式化
+
+### 解决方案
+- **后端接收GET参数**:使用 `@DateTimeFormat(pattern = "yyyy-MM-dd")`
+- **后端JSON序列化**:使用 `@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")`
+- **前后端同时使用**:两个注解同时使用确保兼容性
+- **前端显示格式化**:解析字符串日期并格式化为所需格式
+
+### 示例代码
+```java
+@DateTimeFormat(pattern = "yyyy-MM-dd")
+@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+private Date planTimeStart;
+```
+
+```javascript
+// 前端日期解析和格式化
+const parsedDate = new Date(dateString);
+if (!isNaN(parsedDate.getTime())) {
+  // 格式化为所需格式
+  return `${year}-${month}-${day} ${hours}:${minutes}`;
+}
+```
+
+## 2. 前端国际化配置
+
+### 问题类型
+- 组件库默认语言为英文
+- 日期控件显示语言不符
+
+### 解决方案
+- 在main.js中引入语言包
+- 配置Element Plus使用中文语言环境
+
+### 示例代码
+```javascript
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+
+app.use(ElementPlus, {
+  locale: zhCn
+})
+```
+
+## 3. 搜索和分页功能最佳实践
+
+### 问题类型
+- 搜索和列表功能分离
+- 大数据量性能问题
+- 用户体验不佳
+
+### 解决方案
+- 合并搜索和列表功能,通过参数判断
+- 实现数据库分页,减少内存占用
+- 添加瀑布流加载,提升用户体验
+- 使用 `COALESCE` 函数处理空值判断
+
+### 示例代码
+```java
+@Query("SELECT new com.example.dto(...) " +
+       "FROM ... " +
+       "WHERE ... AND (COALESCE(:query, '') = '' OR ...) ")
+Page<SearchResult> searchWithPage(@Param("query") String query, Pageable pageable);
+```
+
+## 4. 数据库读写分离和数据源配置
+
+### 问题类型
+- 误解数据库架构
+- 错误的数据源配置
+- 混淆读写分离概念
+
+### 解决方案
+- 明确区分两种数据库类型:
+  - **tugboatcommon**:系统公共库,存储所有机构共享数据
+  - **liandatugboatmis**:分支机构业务库,存储各机构业务数据
+- 使用有意义的命名而非READ/WRITE:
+  - `COMMON` → tugboatcommon
+  - `BRANCH` → liandatugboatmis
+- 只对同时存在于两库的表使用分离
+- 正确配置默认数据源
+
+### 示例代码
+```java
+public enum DataSourceType {
+    COMMON("common"),      // tugboatcommon: 系统公共库
+    BRANCH("branch");     // liandatugboatmis: 分支机构业务库
+}
+```
+
+## 5. 前端组件优化
+
+### 问题类型
+- 下拉选择器性能问题
+- 数据显示字段错误
+- 用户交互体验差
+
+### 解决方案
+- 实现虚拟滚动或分页加载
+- 明确显示字段与值字段的区别
+- 添加加载状态提示
+- 实现滚动加载更多功能
+
+### 示例代码
+```html
+<el-select
+  v-model="value"
+  filterable
+  remote
+  popper-class="custom-popper"
+  :remote-method="searchMethod">
+  <div v-if="loadingMore" class="loading">加载中...</div>
+  <el-option
+    v-for="item in options"
+    :key="item.id"
+    :label="item.displayField"  <!-- 显示字段 -->
+    :value="item.valueField">   <!-- 值字段 -->
+  </el-option>
+</el-select>
+```
+
+## 6. 调试和开发流程
+
+### 最佳实践
+- **针对性启动**:修改哪个项目就重新调试哪个项目,不要每次都全部重启
+- **渐进式开发**:先实现基础功能,再逐步优化
+- **前后端分离调试**:分别启动前后端服务进行调试
+- **日志监控**:关注错误信息和警告信息
+- **版本控制**:及时保存中间状态,避免丢失重要修改
+
+## 7. 前端防重复点击最佳实践
+
+### 问题类型
+- 用户快速多次点击按钮或触发异步操作
+- 多次发送相同请求
+- 数据重复提交
+- 用户体验不佳
+- 系统资源浪费
+
+### 解决方案
+在所有触发后台操作的前端事件中添加进度条和防重复点击机制。
+
+### 实现方法
+1. **使用loading状态控制**:在按钮上绑定loading属性,在请求期间禁用按钮
+2. **全局Loading覆盖**:对于页面级别的操作,使用全局Loading遮罩
+3. **防抖和节流技术**:对于频繁触发的操作,使用debounce或throttle
+4. **统一组件封装**:创建可复用的防重复点击组件
+
+### 示例代码
+```vue
+<template>
+  <el-button 
+    :loading="loading" 
+    @click="handleAction"
+    :disabled="loading">
+    {{ loading ? '处理中...' : '提交' }}
+  </el-button>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      loading: false
+    }
+  },
+  methods: {
+    async handleAction() {
+      if (this.loading) return; // 防止重复点击
+      
+      this.loading = true;
+      try {
+        // 执行后台操作
+        await this.apiCall();
+      } finally {
+        this.loading = false; // 确保无论成功失败都恢复状态
+      }
+    }
+  }
+}
+</script>
+```
+
+## 8. 常见陷阱和注意事项
+
+1. **注解使用场景**:
+   - `@DateTimeFormat` 用于请求参数绑定
+   - `@JsonFormat` 用于JSON序列化/反序列化
+
+2. **命名规范**:
+   - 避免使用可能引起误解的命名(如READ/WRITE)
+   - 使用业务含义明确的命名
+
+3. **数据一致性**:
+   - 确保前后端日期格式一致
+   - 保证显示字段与业务需求一致
+
+4. **性能考虑**:
+   - 大数据量时使用分页
+   - 合理使用缓存
+   - 避免不必要的数据传输
+
+## 使用时机
+在开发新页面功能、调试常见Web应用问题、处理日期时间、配置数据源、优化前端组件时使用此技能。

+ 463 - 0
.vscode/changelists.json

@@ -0,0 +1,463 @@
+{
+  "changelists": [
+    {
+      "name": "Default",
+      "files": [
+        ".trae/rules/project_rules.md",
+        ".trae/skills/common-database-fields/SKILL.md",
+        ".trae/skills/common-query-dto-base-class/SKILL.md",
+        ".trae/skills/data-caching-patterns/SKILL.md",
+        ".trae/skills/database-entity-validation/SKILL.md",
+        ".trae/skills/database-field-validation/SKILL.md",
+        ".trae/skills/db-table-analyzer/SKILL.md",
+        ".trae/skills/frontend-anti-duplication/SKILL.md",
+        ".trae/skills/frontend-unused-variables-check/SKILL.md",
+        ".trae/skills/function-permission-system/SKILL.md",
+        ".trae/skills/multi-project-debugger/SKILL.md",
+        ".trae/skills/web-dev-best-practices/SKILL.md",
+        "FlinkDataSync/.idea/.gitignore",
+        "FlinkDataSync/.idea/compiler.xml",
+        "FlinkDataSync/.idea/encodings.xml",
+        "FlinkDataSync/.idea/jarRepositories.xml",
+        "FlinkDataSync/.idea/misc.xml",
+        "FlinkDataSync/.idea/vcs.xml",
+        "FlinkDataSync/dependency-reduced-pom.xml",
+        "FlinkDataSync/flink-sync.log",
+        "FlinkDataSync/flink-sync.log.1",
+        "FlinkDataSync/flink-sync.log.2",
+        "FlinkDataSync/flink-sync.log.3",
+        "FlinkDataSync/flink-sync.log.4",
+        "FlinkDataSync/flink-sync.log.5",
+        "FlinkDataSync/flink-sync.log.6",
+        "FlinkDataSync/flink-sync.log.7",
+        "FlinkDataSync/pom.xml",
+        "FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlCdcSync.java",
+        "FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlSink.java",
+        "FlinkDataSync/src/main/resources/log4j.properties",
+        "FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlCdcSync.class",
+        "FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlSink.class",
+        "FlinkDataSync/target/classes/log4j.properties",
+        "FlinkDataSync/target/flink-data-sync-1.0-SNAPSHOT.jar",
+        "FlinkDataSync/target/maven-archiver/pom.properties",
+        "FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
+        "FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
+        "FlinkDataSync/target/original-flink-data-sync-1.0-SNAPSHOT.jar",
+        "JavaBackend/.idea/.gitignore",
+        "JavaBackend/.idea/compiler.xml",
+        "JavaBackend/.idea/encodings.xml",
+        "JavaBackend/.idea/jarRepositories.xml",
+        "JavaBackend/.idea/misc.xml",
+        "JavaBackend/.idea/vcs.xml",
+        "JavaBackend/.mvn/jvm.config",
+        "JavaBackend/.mvn/wrapper/maven-wrapper.properties",
+        "JavaBackend/hs_err_pid26756.log",
+        "JavaBackend/lib/HikariCP-4.0.3.jar",
+        "JavaBackend/lib/accessors-smart-2.4.11.jar",
+        "JavaBackend/lib/android-json-0.0.20131108.vaadin1.jar",
+        "JavaBackend/lib/antlr-2.7.7.jar",
+        "JavaBackend/lib/apiguardian-api-1.1.2.jar",
+        "JavaBackend/lib/asm-9.3.jar",
+        "JavaBackend/lib/aspectjweaver-1.9.7.jar",
+        "JavaBackend/lib/assertj-core-3.22.0.jar",
+        "JavaBackend/lib/byte-buddy-1.12.23.jar",
+        "JavaBackend/lib/byte-buddy-agent-1.12.23.jar",
+        "JavaBackend/lib/classmate-1.5.1.jar",
+        "JavaBackend/lib/commons-csv-1.9.0.jar",
+        "JavaBackend/lib/hamcrest-2.2.jar",
+        "JavaBackend/lib/hibernate-commons-annotations-5.1.2.Final.jar",
+        "JavaBackend/lib/hibernate-core-5.6.15.Final.jar",
+        "JavaBackend/lib/istack-commons-runtime-3.0.12.jar",
+        "JavaBackend/lib/jackson-annotations-2.13.5.jar",
+        "JavaBackend/lib/jackson-core-2.13.5.jar",
+        "JavaBackend/lib/jackson-databind-2.13.5.jar",
+        "JavaBackend/lib/jackson-datatype-jdk8-2.13.5.jar",
+        "JavaBackend/lib/jackson-datatype-jsr310-2.13.5.jar",
+        "JavaBackend/lib/jackson-module-parameter-names-2.13.5.jar",
+        "JavaBackend/lib/jakarta.activation-1.2.2.jar",
+        "JavaBackend/lib/jakarta.activation-api-1.2.2.jar",
+        "JavaBackend/lib/jakarta.annotation-api-1.3.5.jar",
+        "JavaBackend/lib/jakarta.persistence-api-2.2.3.jar",
+        "JavaBackend/lib/jakarta.transaction-api-1.3.3.jar",
+        "JavaBackend/lib/jakarta.xml.bind-api-2.3.3.jar",
+        "JavaBackend/lib/jandex-2.4.2.Final.jar",
+        "JavaBackend/lib/jaxb-runtime-2.3.8.jar",
+        "JavaBackend/lib/jboss-logging-3.4.3.Final.jar",
+        "JavaBackend/lib/jjwt-0.9.1.jar",
+        "JavaBackend/lib/json-path-2.7.0.jar",
+        "JavaBackend/lib/json-smart-2.4.11.jar",
+        "JavaBackend/lib/jsonassert-1.5.1.jar",
+        "JavaBackend/lib/jul-to-slf4j-1.7.36.jar",
+        "JavaBackend/lib/junit-jupiter-5.8.2.jar",
+        "JavaBackend/lib/junit-jupiter-api-5.8.2.jar",
+        "JavaBackend/lib/junit-jupiter-engine-5.8.2.jar",
+        "JavaBackend/lib/junit-jupiter-params-5.8.2.jar",
+        "JavaBackend/lib/junit-platform-commons-1.8.2.jar",
+        "JavaBackend/lib/junit-platform-engine-1.8.2.jar",
+        "JavaBackend/lib/log4j-api-2.17.2.jar",
+        "JavaBackend/lib/log4j-to-slf4j-2.17.2.jar",
+        "JavaBackend/lib/logback-classic-1.2.12.jar",
+        "JavaBackend/lib/logback-core-1.2.12.jar",
+        "JavaBackend/lib/lombok-1.18.28.jar",
+        "JavaBackend/lib/mockito-core-4.5.1.jar",
+        "JavaBackend/lib/mockito-junit-jupiter-4.5.1.jar",
+        "JavaBackend/lib/mysql-connector-java-8.0.28.jar",
+        "JavaBackend/lib/objenesis-3.2.jar",
+        "JavaBackend/lib/opentest4j-1.2.0.jar",
+        "JavaBackend/lib/protobuf-java-3.11.4.jar",
+        "JavaBackend/lib/slf4j-api-1.7.36.jar",
+        "JavaBackend/lib/snakeyaml-1.30.jar",
+        "JavaBackend/lib/spring-aop-5.3.29.jar",
+        "JavaBackend/lib/spring-aspects-5.3.29.jar",
+        "JavaBackend/lib/spring-beans-5.3.29.jar",
+        "JavaBackend/lib/spring-boot-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-autoconfigure-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-devtools-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-aop-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-data-jpa-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-jdbc-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-json-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-logging-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-security-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-test-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-tomcat-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-starter-web-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-test-2.7.15.jar",
+        "JavaBackend/lib/spring-boot-test-autoconfigure-2.7.15.jar",
+        "JavaBackend/lib/spring-context-5.3.29.jar",
+        "JavaBackend/lib/spring-core-5.3.29.jar",
+        "JavaBackend/lib/spring-data-commons-2.7.15.jar",
+        "JavaBackend/lib/spring-data-jpa-2.7.15.jar",
+        "JavaBackend/lib/spring-expression-5.3.29.jar",
+        "JavaBackend/lib/spring-jcl-5.3.29.jar",
+        "JavaBackend/lib/spring-jdbc-5.3.29.jar",
+        "JavaBackend/lib/spring-orm-5.3.29.jar",
+        "JavaBackend/lib/spring-security-config-5.7.10.jar",
+        "JavaBackend/lib/spring-security-core-5.7.10.jar",
+        "JavaBackend/lib/spring-security-crypto-5.7.10.jar",
+        "JavaBackend/lib/spring-security-web-5.7.10.jar",
+        "JavaBackend/lib/spring-test-5.3.29.jar",
+        "JavaBackend/lib/spring-tx-5.3.29.jar",
+        "JavaBackend/lib/spring-web-5.3.29.jar",
+        "JavaBackend/lib/spring-webmvc-5.3.29.jar",
+        "JavaBackend/lib/tomcat-embed-core-9.0.79.jar",
+        "JavaBackend/lib/tomcat-embed-el-9.0.79.jar",
+        "JavaBackend/lib/tomcat-embed-websocket-9.0.79.jar",
+        "JavaBackend/lib/txw2-2.3.8.jar",
+        "JavaBackend/lib/xmlunit-core-2.9.1.jar",
+        "JavaBackend/mvnvm.properties",
+        "JavaBackend/pom.xml",
+        "JavaBackend/src/main/java/com/lianda/backend/Application.java",
+        "JavaBackend/src/main/java/com/lianda/backend/annotation/RequirePermission.java",
+        "JavaBackend/src/main/java/com/lianda/backend/aspect/DataSourceAspect.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/AppConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/DataSource.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/DataSourceContextHolder.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/JpaConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/MultiDataSourceConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/RoutingDataSourceConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/SecurityConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/config/WebConfig.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/AuthController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/CustomerCompanyBusinessController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/DebugController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/MenuController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/PilotPlanController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/controller/PortController.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/BaseQueryDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/CustomerCompanyBusinessSearchResult.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/LoginRequest.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/LoginResponse.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PageResponse.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PilotPlanDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PilotPlanDetailDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PilotPlanImportDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PilotPlanProjection.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PilotPlanQueryDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/PortDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/dto/ShippingCompanyDTO.java",
+        "JavaBackend/src/main/java/com/lianda/backend/interceptor/PermissionInterceptor.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusCustomerCompany.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusCustomerCompanyBusiness.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusCustomerCustomerType.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusCustomerCustomerTypeId.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusPilotTypeSetting.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/BusShip.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispBerthage.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispBerthageDictionary.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispBerthageSetting.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispDispatcher.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispPilot.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispPilotPlan.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispPort.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispPortDictionary.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/DispWaterway.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/PilotPlan.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/SysDictionaryItem.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/SysMenu.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/SysUser.java",
+        "JavaBackend/src/main/java/com/lianda/backend/model/User.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCompanyBusinessRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCompanyBusinessWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCompanyRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCompanyWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCustomerTypeRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusCustomerCustomerTypeWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusPilotTypeSettingRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusShipReadRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusShipRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/BusShipWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispBerthageDictionaryRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispBerthageRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispBerthageSettingRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispBerthageSettingWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispBerthageWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispDispatcherRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPilotPlanReadRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPilotPlanRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPilotPlanWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPilotRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPilotWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPortDictionaryRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPortReadRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPortRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispPortWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispWaterwayRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/DispWaterwayWriteRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/PilotPlanRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/SysDictionaryItemRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/SysMenuRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/repository/UserRepository.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/AuthService.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/BusPilotTypeSettingCacheService.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/CommonDataService.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/MenuService.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/PilotPlanService.java",
+        "JavaBackend/src/main/java/com/lianda/backend/service/UserDetailsServiceImpl.java",
+        "JavaBackend/src/main/java/com/lianda/backend/test/DatabaseMenuUpdater.java",
+        "JavaBackend/src/main/java/com/lianda/backend/test/MD5Test.class",
+        "JavaBackend/src/main/java/com/lianda/backend/test/MD5Test.java",
+        "JavaBackend/src/main/java/com/lianda/backend/test/MenuUrlUpdater.java",
+        "JavaBackend/src/main/java/com/lianda/backend/util/CurrentUserUtil.java",
+        "JavaBackend/src/main/java/com/lianda/backend/util/JwtUtil.java",
+        "JavaBackend/src/main/resources/application-dev.properties",
+        "JavaBackend/src/main/resources/application.properties",
+        "JavaBackend/target/classes/application-dev.properties",
+        "JavaBackend/target/classes/application.properties",
+        "JavaBackend/target/classes/com/lianda/backend/Application.class",
+        "JavaBackend/target/classes/com/lianda/backend/annotation/RequirePermission.class",
+        "JavaBackend/target/classes/com/lianda/backend/aspect/DataSourceAspect.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/AppConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/DataSource.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/DataSourceContextHolder.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/JpaConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/MultiDataSourceConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/RoutingDataSourceConfig$DataSourceType.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/RoutingDataSourceConfig$RoutingDataSource.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/RoutingDataSourceConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/SecurityConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/config/WebConfig.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/AuthController.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/CustomerCompanyBusinessController$1.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/CustomerCompanyBusinessController$2.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/CustomerCompanyBusinessController.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/DebugController.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/MenuController.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/PilotPlanController.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/PortController$1.class",
+        "JavaBackend/target/classes/com/lianda/backend/controller/PortController.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/BaseQueryDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/CustomerCompanyBusinessSearchResult.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/LoginRequest.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/LoginResponse.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PageResponse.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PilotPlanDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PilotPlanDetailDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PilotPlanImportDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PilotPlanProjection.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PilotPlanQueryDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/PortDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/dto/ShippingCompanyDTO.class",
+        "JavaBackend/target/classes/com/lianda/backend/interceptor/PermissionInterceptor.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusCustomerCompany.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusCustomerCompanyBusiness.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusCustomerCustomerType.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusCustomerCustomerTypeId.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusPilotTypeSetting.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/BusShip.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispBerthage.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispBerthageDictionary.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispBerthageSetting.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispDispatcher.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispPilot.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispPilotPlan.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispPort.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispPortDictionary.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/DispWaterway.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/PilotPlan.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/SysDictionaryItem.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/SysMenu.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/SysUser.class",
+        "JavaBackend/target/classes/com/lianda/backend/model/User.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCompanyBusinessRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCompanyBusinessWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCompanyRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCompanyWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCustomerTypeRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusCustomerCustomerTypeWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusPilotTypeSettingRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusShipReadRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusShipRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/BusShipWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispBerthageDictionaryRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispBerthageRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispBerthageSettingRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispBerthageSettingWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispBerthageWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispDispatcherRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPilotPlanReadRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPilotPlanRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPilotPlanWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPilotRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPilotWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPortDictionaryRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPortReadRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPortRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispPortWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispWaterwayRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/DispWaterwayWriteRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/PilotPlanRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/SysDictionaryItemRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/SysMenuRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/repository/UserRepository.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/AuthService.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/BusPilotTypeSettingCacheService.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/CommonDataService.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/MenuService.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/PilotPlanService.class",
+        "JavaBackend/target/classes/com/lianda/backend/service/UserDetailsServiceImpl.class",
+        "JavaBackend/target/classes/com/lianda/backend/test/DatabaseMenuUpdater.class",
+        "JavaBackend/target/classes/com/lianda/backend/test/MD5Test.class",
+        "JavaBackend/target/classes/com/lianda/backend/test/MenuUrlUpdater.class",
+        "JavaBackend/target/classes/com/lianda/backend/util/CurrentUserUtil$1.class",
+        "JavaBackend/target/classes/com/lianda/backend/util/CurrentUserUtil.class",
+        "JavaBackend/target/classes/com/lianda/backend/util/JwtUtil.class",
+        "JavaBackend/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
+        "JavaBackend/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
+        "com.lianda.auth/.idea/.gitignore",
+        "com.lianda.auth/.idea/compiler.xml",
+        "com.lianda.auth/.idea/encodings.xml",
+        "com.lianda.auth/.idea/jarRepositories.xml",
+        "com.lianda.auth/.idea/misc.xml",
+        "com.lianda.auth/.idea/vcs.xml",
+        "com.lianda.auth/pom.xml",
+        "com.lianda.auth/src/main/java/com/lianda/auth/AuthApplication.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/config/AppConfig.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/config/CacheConfig.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/config/SecurityConfig.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/controller/AuthController.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/controller/UserPermissionController.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/dto/EncryptedLoginRequest.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/dto/LoginRequest.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/dto/LoginResponse.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/RoleFunctionCodeKey.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/SysFunctionCode.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/SysRole.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/SysRoleFunctionCode.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/SysUser.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/SysUserRole.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/User.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/entity/UserRoleKey.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/filter/JwtAuthenticationFilter.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/repository/SysFunctionCodeRepository.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/repository/SysRoleFunctionCodeRepository.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/repository/SysUserRoleRepository.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/repository/UserRepository.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/service/AuthService.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/service/PermissionService.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/service/UserDetailsServiceImpl.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/service/UserPermissionService.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/service/UserPermissionServiceImpl.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/util/EncryptionUtil.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/util/JwtUtil.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/util/MD5PasswordEncoder.java",
+        "com.lianda.auth/src/main/java/com/lianda/auth/util/TestEncryptionUtil.java",
+        "com.lianda.auth/src/main/resources/application.properties",
+        "com.lianda.auth/src/test/java/com/lianda/auth/util/EncryptionUtilTest.java",
+        "com.lianda.auth/target/classes/application.properties",
+        "com.lianda.auth/target/classes/com/lianda/auth/AuthApplication.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/config/AppConfig.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/config/CacheConfig.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/config/SecurityConfig.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/controller/AuthController.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/controller/UserPermissionController.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/dto/EncryptedLoginRequest.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/dto/LoginRequest.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/dto/LoginResponse$TokenData.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/dto/LoginResponse.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/RoleFunctionCodeKey.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/SysFunctionCode.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/SysRole.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/SysRoleFunctionCode.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/SysUser.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/SysUserRole.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/User.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/entity/UserRoleKey.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/filter/JwtAuthenticationFilter.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/repository/SysFunctionCodeRepository.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/repository/SysRoleFunctionCodeRepository.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/repository/SysUserRoleRepository.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/repository/UserRepository.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/service/AuthService.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/service/PermissionService.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/service/UserDetailsServiceImpl.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/service/UserPermissionService.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/service/UserPermissionServiceImpl.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/util/EncryptionUtil.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/util/JwtUtil.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/util/MD5PasswordEncoder.class",
+        "com.lianda.auth/target/classes/com/lianda/auth/util/TestEncryptionUtil.class",
+        "com.lianda.auth/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
+        "com.lianda.auth/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
+        "com.lianda.auth/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
+        "com.lianda.auth/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
+        "com.lianda.auth/target/test-classes/com/lianda/auth/util/EncryptionUtilTest.class",
+        "database_structure.md",
+        "vue-frontend/.gitignore",
+        "vue-frontend/.nvmdrc",
+        "vue-frontend/README.md",
+        "vue-frontend/babel.config.js",
+        "vue-frontend/consistency-test.js",
+        "vue-frontend/integration-test.js",
+        "vue-frontend/jsconfig.json",
+        "vue-frontend/package-lock.json",
+        "vue-frontend/package.json",
+        "vue-frontend/public/favicon.ico",
+        "vue-frontend/public/index.html",
+        "vue-frontend/src/App.vue",
+        "vue-frontend/src/assets/logo.png",
+        "vue-frontend/src/components/Announcement.vue",
+        "vue-frontend/src/components/HelloWorld.vue",
+        "vue-frontend/src/components/HomePage.vue",
+        "vue-frontend/src/components/HomeTabs.vue",
+        "vue-frontend/src/components/Login.vue",
+        "vue-frontend/src/components/MainIndex.vue",
+        "vue-frontend/src/components/MyNotice.vue",
+        "vue-frontend/src/components/PilotPlan.vue",
+        "vue-frontend/src/components/RoleManagement.vue",
+        "vue-frontend/src/components/UserManagement.vue",
+        "vue-frontend/src/directives/permission.js",
+        "vue-frontend/src/main.js",
+        "vue-frontend/src/router/index.js",
+        "vue-frontend/src/utils/permission.js",
+        "vue-frontend/src/utils/tokenRefreshManager.js",
+        "vue-frontend/src/utils/tokenUtils.js",
+        "vue-frontend/test-cryptojs-format.js",
+        "vue-frontend/test-encryption.js",
+        "vue-frontend/test-full-flow.js",
+        "vue-frontend/vue.config.js"
+      ]
+    }
+  ],
+  "active": "Default"
+}

+ 10 - 0
FlinkDataSync/.idea/.gitignore

@@ -0,0 +1,10 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Zeppelin ignored files
+/ZeppelinRemoteNotebooks/

+ 13 - 0
FlinkDataSync/.idea/compiler.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="flink-data-sync" />
+      </profile>
+    </annotationProcessing>
+  </component>
+</project>

+ 7 - 0
FlinkDataSync/.idea/encodings.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+    <file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
+  </component>
+</project>

+ 20 - 0
FlinkDataSync/.idea/jarRepositories.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+  </component>
+</project>

+ 12 - 0
FlinkDataSync/.idea/misc.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
+</project>

+ 6 - 0
FlinkDataSync/.idea/vcs.xml

@@ -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>

+ 75 - 0
FlinkDataSync/dependency-reduced-pom.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.lianda</groupId>
+  <artifactId>flink-data-sync</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.8.1</version>
+        <configuration>
+          <source>${maven.compiler.source}</source>
+          <target>${maven.compiler.target}</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>3.2.4</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <transformers>
+                <transformer>
+                  <mainClass>com.lianda.flink.sync.MySqlCdcSync</mainClass>
+                </transformer>
+                <transformer />
+              </transformers>
+              <filters>
+                <filter>
+                  <artifact>*:*</artifact>
+                  <excludes>
+                    <exclude>META-INF/*.SF</exclude>
+                    <exclude>META-INF/*.DSA</exclude>
+                    <exclude>META-INF/*.RSA</exclude>
+                  </excludes>
+                </filter>
+              </filters>
+              <relocations>
+                <relocation>
+                  <pattern>org.apache.flink.shaded.guava18</pattern>
+                  <shadedPattern>org.apache.flink.shaded.guava18</shadedPattern>
+                </relocation>
+              </relocations>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>1.7.36</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+  <properties>
+    <flink.version>1.14.5</flink.version>
+    <slf4j.version>1.7.36</slf4j.version>
+    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+    <maven.compiler.target>1.8</maven.compiler.target>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <log4j.version>1.2.17</log4j.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <mysql.cdc.version>2.2.1</mysql.cdc.version>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <fastjson.version>1.2.83</fastjson.version>
+  </properties>
+</project>

File diff suppressed because it is too large
+ 13849 - 0
FlinkDataSync/flink-sync.log


File diff suppressed because it is too large
+ 54695 - 0
FlinkDataSync/flink-sync.log.1


File diff suppressed because it is too large
+ 54645 - 0
FlinkDataSync/flink-sync.log.2


File diff suppressed because it is too large
+ 54897 - 0
FlinkDataSync/flink-sync.log.3


File diff suppressed because it is too large
+ 54770 - 0
FlinkDataSync/flink-sync.log.4


File diff suppressed because it is too large
+ 57134 - 0
FlinkDataSync/flink-sync.log.5


File diff suppressed because it is too large
+ 54770 - 0
FlinkDataSync/flink-sync.log.6


File diff suppressed because it is too large
+ 57134 - 0
FlinkDataSync/flink-sync.log.7


+ 136 - 0
FlinkDataSync/pom.xml

@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.lianda</groupId>
+    <artifactId>flink-data-sync</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <flink.version>1.14.5</flink.version>
+        <mysql.cdc.version>2.2.1</mysql.cdc.version>
+        <slf4j.version>1.7.36</slf4j.version>
+        <log4j.version>1.2.17</log4j.version>
+        <fastjson.version>1.2.83</fastjson.version>
+    </properties>
+
+    <dependencies>
+        <!-- Flink Core -->
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-java</artifactId>
+            <version>${flink.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-streaming-java_2.12</artifactId>
+            <version>${flink.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-clients_2.12</artifactId>
+            <version>${flink.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-api-java-bridge_2.12</artifactId>
+            <version>${flink.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-runtime_2.12</artifactId>
+            <version>${flink.version}</version>
+        </dependency>
+
+        <!-- Flink CDC -->
+        <dependency>
+            <groupId>com.ververica</groupId>
+            <artifactId>flink-connector-mysql-cdc</artifactId>
+            <version>2.4.2</version>
+        </dependency>
+
+        <!-- MySQL Connector -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.28</version>
+        </dependency>
+
+        <!-- Logging -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>${log4j.version}</version>
+        </dependency>
+        
+        <!-- JSON Processing -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${fastjson.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>com.lianda.flink.sync.MySqlCdcSync</mainClass>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+                            </transformers>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <relocations>
+                                <relocation>
+                                    <pattern>org.apache.flink.shaded.guava18</pattern>
+                                    <shadedPattern>org.apache.flink.shaded.guava18</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

File diff suppressed because it is too large
+ 89 - 0
FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlCdcSync.java


+ 684 - 0
FlinkDataSync/src/main/java/com/lianda/flink/sync/MySqlSink.java

@@ -0,0 +1,684 @@
+package com.lianda.flink.sync;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * MySQL 数据写入Sink
+ * 处理CDC事件并将数据写入目标数据库
+ */
+public class MySqlSink extends RichSinkFunction<String> {
+
+    private String hostname;
+    private int port;
+    private String database;
+    private String username;
+    private String password;
+    private Connection connection;
+    private final ConcurrentMap<String, Set<String>> primaryKeysCache = new ConcurrentHashMap<>(); // 缓存表的主键字段
+
+    public MySqlSink(String hostname, int port, String database, String username, String password) {
+        this.hostname = hostname;
+        this.port = port;
+        this.database = database;
+        this.username = username;
+        this.password = password;
+    }
+
+    @Override
+    public void open(Configuration parameters) throws Exception {
+        super.open(parameters);
+        // 加载MySQL驱动
+        Class.forName("com.mysql.cj.jdbc.Driver");
+        // 初始化数据库连接,添加UTF-8编码参数和正确的时区设置,同时指定MySQL使用utf8mb4
+        String url = String.format("jdbc:mysql://%s:%d/%s?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useUnicode=true&characterSetResults=utf8mb4&characterSetServer=utf8mb4", 
+                hostname, port, database);
+        connection = DriverManager.getConnection(url, username, password);
+        connection.setAutoCommit(false);
+    }
+
+    @Override
+    public void invoke(String value, Context context) throws Exception {
+        // 解析CDC事件的JSON格式
+        JSONObject event = JSON.parseObject(value);
+        String op = event.getString("op"); // c: create, u: update, d: delete, r: read
+        
+        // 获取表名,先尝试从source.table获取,如果为null则尝试从其他字段获取
+        String table = null;
+        JSONObject source = event.getJSONObject("source");
+        if (source != null) {
+            table = source.getString("table");
+            // 检查表名是否需要转换为下划线分隔的首字母大写格式
+            // 例如:bus_holiday -> Bus_Holiday
+            if (table.contains("_")) {
+                String[] parts = table.split("_");
+                StringBuilder properCaseTable = new StringBuilder();
+                for (int i = 0; i < parts.length; i++) {
+                    if (i > 0) {
+                        properCaseTable.append("_");
+                    }
+                    properCaseTable.append(parts[i].substring(0, 1).toUpperCase()).append(parts[i].substring(1).toLowerCase());
+                }
+                table = properCaseTable.toString();
+                System.out.println("表名转换为下划线分隔首字母大写格式: " + table);
+            }
+        }
+        
+        // 如果表名为null,跳过此事件
+        if (table == null) {
+            System.out.println("跳过表名为null的事件: " + event.toJSONString());
+            return;
+        }
+        
+        // 跳过读取操作(初始化快照)
+        if ("r".equals(op)) {
+            return;
+        }
+
+        try {
+            if ("c".equals(op) || "u".equals(op)) {
+                // 处理插入和更新操作
+                JSONObject after = event.getJSONObject("after");
+                // 输出原始数据,以便检查编码
+                System.out.println("处理插入/更新操作,表名: " + table + ",数据: " + after.toJSONString());
+                // 检查字符串字段的编码
+                for (String key : after.keySet()) {
+                    Object valueObj = after.get(key);
+                    if (valueObj instanceof String) {
+                        String strValue = (String) valueObj;
+                        // 检测并修复乱码
+                        String fixedValue = fixEncoding(strValue);
+                        // 输出修复前后的字符串
+                        if (!fixedValue.equals(strValue)) {
+                            System.out.println("字段: " + key + ",原始值: " + strValue + ",修复后: " + fixedValue + ",长度: " + fixedValue.length());
+                            // 更新JSON对象中的值
+                            after.put(key, fixedValue);
+                        } else {
+                            System.out.println("字段: " + key + ",值: " + strValue + ",长度: " + strValue.length());
+                        }
+                    }
+                }
+                handleUpsert(table, after);
+            } else if ("d".equals(op)) {
+                // 处理删除操作
+                JSONObject before = event.getJSONObject("before");
+                System.out.println("处理删除操作,表名: " + table + ",数据: " + before.toJSONString());
+                handleDelete(table, before);
+            }
+            connection.commit();
+        } catch (Exception e) {
+            connection.rollback();
+            System.err.println("处理事件失败,表名: " + table + ",操作: " + op);
+            System.err.println("事件数据: " + event.toJSONString());
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    private void handleUpsert(String table, JSONObject data) throws SQLException {
+        // 检查记录是否已存在
+        if (isRecordExists(table, data)) {
+            // 记录存在,执行更新操作
+            handleUpdate(table, data);
+        } else {
+            // 记录不存在,执行插入操作
+            handleInsert(table, data);
+        }
+    }
+    
+    /**
+     * 检查记录是否已存在
+     */
+    private boolean isRecordExists(String table, JSONObject data) throws SQLException {
+        // 构建WHERE条件
+        StringBuilder whereClause = new StringBuilder();
+        java.util.List<Object> whereValues = new java.util.ArrayList<>();
+        
+        // 尝试使用ID字段作为唯一标识
+        String idField = null;
+        Object idValue = null;
+        
+        // 检查是否有常见的ID字段
+        if (data.containsKey("HolidayID")) {
+            idField = "HolidayID";
+            idValue = data.get("HolidayID");
+        } else if (data.containsKey("ID")) {
+            idField = "ID";
+            idValue = data.get("ID");
+        } else if (data.containsKey("Id")) {
+            idField = "Id";
+            idValue = data.get("Id");
+        } else if (data.containsKey("id")) {
+            idField = "id";
+            idValue = data.get("id");
+        }
+        
+        // 如果找到ID字段,使用它构建WHERE条件
+        if (idField != null && idValue != null) {
+            whereClause.append(idField).append(" = ?");
+            whereValues.add(idValue);
+        } else {
+            // 如果没有找到ID字段,使用所有字段构建WHERE条件(不推荐,但作为后备方案)
+            for (Map.Entry<String, Object> entry : data.entrySet()) {
+                String column = entry.getKey();
+                Object value = entry.getValue();
+                whereClause.append(column).append(" = ? AND ");
+                whereValues.add(value);
+            }
+            
+            // 移除末尾的" AND "
+            if (whereClause.length() > 0) {
+                whereClause.setLength(whereClause.length() - 5);
+            }
+        }
+        
+        // 构建查询语句
+        String sql = String.format(
+                "SELECT COUNT(*) FROM %s WHERE %s",
+                table, whereClause.toString());
+        
+        System.out.println("执行检查记录是否存在的语句: " + sql);
+        System.out.print("检查参数值: [");
+        for (Object value : whereValues) {
+            System.out.print(value + ", ");
+        }
+        System.out.println("]");
+        
+        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+            int index = 1;
+            for (Object value : whereValues) {
+                setParameter(stmt, index++, value);
+            }
+            
+            try (java.sql.ResultSet rs = stmt.executeQuery()) {
+                if (rs.next()) {
+                    int count = rs.getInt(1);
+                    System.out.println("记录存在检查结果: " + (count > 0 ? "存在" : "不存在"));
+                    return count > 0;
+                }
+            }
+        } catch (Exception e) {
+            System.err.println("检查记录是否存在失败: " + sql);
+            e.printStackTrace();
+            // 如果检查失败,默认执行插入操作
+            return false;
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 处理插入操作
+     */
+    private void handleInsert(String table, JSONObject data) throws SQLException {
+        // 构建字段列表和值列表
+        StringBuilder columns = new StringBuilder();
+        StringBuilder values = new StringBuilder();
+        
+        // 检查是否需要添加rowguid字段
+        boolean hasRowguid = false;
+        try {
+            // 查询目标表的结构,检查是否有rowguid字段
+            String checkTableSql = "DESCRIBE " + table;
+            try (PreparedStatement checkStmt = connection.prepareStatement(checkTableSql)) {
+                java.sql.ResultSet rs = checkStmt.executeQuery();
+                while (rs.next()) {
+                    if ("rowguid".equals(rs.getString("Field"))) {
+                        hasRowguid = true;
+                        break;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // 如果查询表结构失败,忽略错误
+            System.err.println("查询表结构失败: " + e.getMessage());
+        }
+        
+        // 保存原始数据,用于后续设置参数
+        java.util.List<Object> originalValues = new java.util.ArrayList<>();
+        for (Map.Entry<String, Object> entry : data.entrySet()) {
+            String column = entry.getKey();
+            Object value = entry.getValue();
+            
+            columns.append(column).append(", ");
+            values.append("?,");
+            originalValues.add(value);
+        }
+        
+        // 如果目标表有rowguid字段,且数据中没有提供,则添加rowguid字段
+        if (hasRowguid && !data.containsKey("rowguid")) {
+            columns.append("rowguid, ");
+            values.append("?,");
+        }
+        
+        // 移除末尾的逗号和空格
+        if (columns.length() > 0) {
+            columns.setLength(columns.length() - 2);
+            values.setLength(values.length() - 1);
+        }
+        
+        // 构建INSERT语句
+        String sql = String.format(
+                "INSERT INTO %s (%s) VALUES (%s)",
+                table, columns.toString(), values.toString());
+        
+        System.out.println("执行INSERT语句: " + sql);
+        
+        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+            int index = 1;
+            // 打印参数值
+            System.out.print("INSERT参数值: [");
+            // 设置插入值
+            for (Object value : originalValues) {
+                System.out.print(value + ", ");
+                setParameter(stmt, index++, value);
+            }
+            // 如果添加了rowguid字段,生成UUID作为值
+            if (hasRowguid && !data.containsKey("rowguid")) {
+                String rowguid = java.util.UUID.randomUUID().toString();
+                System.out.print(rowguid + ", ");
+                stmt.setString(index++, rowguid);
+            }
+            System.out.println("]");
+            
+            int affectedRows = stmt.executeUpdate();
+            System.out.println("INSERT执行成功,影响行数: " + affectedRows);
+        } catch (Exception e) {
+            System.err.println("执行INSERT语句失败: " + sql);
+            e.printStackTrace();
+            throw e;
+        }
+    }
+    
+    /**
+     * 处理更新操作
+     */
+    private void handleUpdate(String table, JSONObject data) throws SQLException {
+        // 构建SET子句
+        StringBuilder setClause = new StringBuilder();
+        java.util.List<Object> setValues = new java.util.ArrayList<>();
+        
+        // 构建WHERE条件
+        StringBuilder whereClause = new StringBuilder();
+        java.util.List<Object> whereValues = new java.util.ArrayList<>();
+        
+        // 尝试使用ID字段作为唯一标识
+        String idField = null;
+        Object idValue = null;
+        
+        // 检查是否有常见的ID字段
+        if (data.containsKey("HolidayID")) {
+            idField = "HolidayID";
+            idValue = data.get("HolidayID");
+        } else if (data.containsKey("ID")) {
+            idField = "ID";
+            idValue = data.get("ID");
+        } else if (data.containsKey("Id")) {
+            idField = "Id";
+            idValue = data.get("Id");
+        } else if (data.containsKey("id")) {
+            idField = "id";
+            idValue = data.get("id");
+        }
+        
+        // 构建SET子句
+        for (Map.Entry<String, Object> entry : data.entrySet()) {
+            String column = entry.getKey();
+            Object value = entry.getValue();
+            
+            // 如果是ID字段,不包含在SET子句中
+            if (!column.equals(idField)) {
+                setClause.append(column).append(" = ?,");
+                setValues.add(value);
+            }
+        }
+        
+        // 移除SET子句末尾的逗号
+        if (setClause.length() > 0) {
+            setClause.setLength(setClause.length() - 1);
+        }
+        
+        // 构建WHERE条件
+        if (idField != null && idValue != null) {
+            whereClause.append(idField).append(" = ?");
+            whereValues.add(idValue);
+        } else {
+            // 如果没有找到ID字段,使用所有字段构建WHERE条件(不推荐,但作为后备方案)
+            for (Map.Entry<String, Object> entry : data.entrySet()) {
+                String column = entry.getKey();
+                Object value = entry.getValue();
+                whereClause.append(column).append(" = ? AND ");
+                whereValues.add(value);
+            }
+            
+            // 移除末尾的" AND "
+            if (whereClause.length() > 0) {
+                whereClause.setLength(whereClause.length() - 5);
+            }
+        }
+        
+        // 构建UPDATE语句
+        String sql = String.format(
+                "UPDATE %s SET %s WHERE %s",
+                table, setClause.toString(), whereClause.toString());
+        
+        System.out.println("执行UPDATE语句: " + sql);
+        
+        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+            int index = 1;
+            // 打印参数值
+            System.out.print("UPDATE参数值: [");
+            // 设置SET子句的值
+            for (Object value : setValues) {
+                System.out.print(value + ", ");
+                setParameter(stmt, index++, value);
+            }
+            // 设置WHERE子句的值
+            for (Object value : whereValues) {
+                System.out.print(value + ", ");
+                setParameter(stmt, index++, value);
+            }
+            System.out.println("]");
+            
+            int affectedRows = stmt.executeUpdate();
+            System.out.println("UPDATE执行成功,影响行数: " + affectedRows);
+        } catch (Exception e) {
+            System.err.println("执行UPDATE语句失败: " + sql);
+            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    /**
+     * 获取表的主键字段
+     */
+    private Set<String> getPrimaryKeys(String table) throws SQLException {
+        // 先从缓存中获取
+        Set<String> primaryKeys = primaryKeysCache.get(table);
+        if (primaryKeys != null) {
+            return primaryKeys;
+        }
+        
+        // 缓存中没有,查询数据库
+        primaryKeys = new HashSet<>();
+        DatabaseMetaData metaData = connection.getMetaData();
+        ResultSet rs = metaData.getPrimaryKeys(null, database, table);
+        
+        while (rs.next()) {
+            String columnName = rs.getString("COLUMN_NAME");
+            primaryKeys.add(columnName);
+        }
+        rs.close();
+        
+        // 缓存结果
+        primaryKeysCache.put(table, primaryKeys);
+        return primaryKeys;
+    }
+
+    private void handleDelete(String table, JSONObject data) throws SQLException {
+        // 获取表的主键字段
+        Set<String> primaryKeys = getPrimaryKeys(table);
+        
+        // 构建WHERE条件
+        StringBuilder whereClause = new StringBuilder();
+        List<Object> primaryKeyValues = new ArrayList<>();
+        
+        if (!primaryKeys.isEmpty()) {
+            // 使用主键字段作为条件
+            for (String key : primaryKeys) {
+                if (data.containsKey(key)) {
+                    whereClause.append(key).append(" = ? AND ");
+                    primaryKeyValues.add(data.get(key));
+                }
+            }
+        } else {
+            // 没有主键,使用所有字段作为条件(兼容旧逻辑)
+            for (Map.Entry<String, Object> entry : data.entrySet()) {
+                String column = entry.getKey();
+                whereClause.append(column).append(" = ? AND ");
+                primaryKeyValues.add(entry.getValue());
+            }
+        }
+        
+        // 移除末尾的"AND "
+        if (whereClause.length() > 0) {
+            whereClause.setLength(whereClause.length() - 5);
+        }
+        
+        // 构建DELETE语句
+        String sql = String.format(
+                "DELETE FROM %s WHERE %s",
+                table, whereClause.toString());
+        
+        System.out.println("执行DELETE语句: " + sql);
+        
+        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
+            int index = 1;
+            // 打印参数值
+            System.out.print("DELETE参数值: [");
+            // 设置WHERE条件的值
+            for (Object value : primaryKeyValues) {
+                System.out.print(value + ", ");
+                setParameter(stmt, index++, value);
+            }
+            System.out.println("]");
+            
+            int affectedRows = stmt.executeUpdate();
+            System.out.println("DELETE执行成功,影响行数: " + affectedRows);
+        } catch (Exception e) {
+            System.err.println("执行DELETE语句失败: " + sql);
+            e.printStackTrace();
+            throw e;
+        }
+    }
+    
+    /**
+     * 设置参数值,处理特殊类型如时间戳,并处理时区问题
+     */
+    private void setParameter(PreparedStatement stmt, int index, Object value) throws SQLException {
+        if (value == null) {
+            stmt.setNull(index, java.sql.Types.NULL);
+        } else if (value instanceof String) {
+            String strValue = (String) value;
+            // 检查是否为时间戳(毫秒数)
+            if (strValue.matches("\\d{13}")) {
+                try {
+                    long timestamp = Long.parseLong(strValue);
+                    // 调整时区,减去8小时偏移量
+                    long adjustedTimestamp = timestamp - 8 * 60 * 60 * 1000;
+                    // 使用调整后的时间戳创建Timestamp
+                    java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(adjustedTimestamp);
+                    stmt.setTimestamp(index, sqlTimestamp);
+                    System.out.println("时间戳调整(字符串):" + timestamp + " -> " + adjustedTimestamp);
+                } catch (NumberFormatException e) {
+                    // 直接设置字符串,不进行编码修复(已经在invoke方法中处理过了)
+                    stmt.setString(index, strValue);
+                }
+            } else {
+                // 直接设置字符串,不进行编码修复(已经在invoke方法中处理过了)
+                stmt.setString(index, strValue);
+            }
+        } else if (value instanceof Long) {
+            Long longValue = (Long) value;
+            // 检查是否为时间戳(毫秒数,长度为13位)
+            if (String.valueOf(longValue).length() == 13) {
+                // 调整时区,减去8小时偏移量
+                long adjustedTimestamp = longValue - 8 * 60 * 60 * 1000;
+                // 使用调整后的时间戳创建Timestamp
+                java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(adjustedTimestamp);
+                stmt.setTimestamp(index, sqlTimestamp);
+                System.out.println("时间戳调整(长整型):" + longValue + " -> " + adjustedTimestamp);
+            } else {
+                // 不是时间戳,直接设置为长整型
+                stmt.setLong(index, longValue);
+            }
+        } else if (value instanceof Integer) {
+            // 直接设置为整型,不是时间戳
+            stmt.setInt(index, (Integer) value);
+        } else if (value instanceof Boolean) {
+            // 处理布尔类型
+            stmt.setBoolean(index, (Boolean) value);
+        } else if (value instanceof java.util.Date) {
+            java.util.Date date = (java.util.Date) value;
+            // 调整时区,减去8小时偏移量
+            long adjustedTimestamp = date.getTime() - 8 * 60 * 60 * 1000;
+            // 使用调整后的时间戳创建Timestamp
+            java.sql.Timestamp sqlTimestamp = new java.sql.Timestamp(adjustedTimestamp);
+            stmt.setTimestamp(index, sqlTimestamp);
+            System.out.println("时间戳调整(Date):" + date.getTime() + " -> " + adjustedTimestamp);
+        } else {
+            stmt.setObject(index, value);
+        }
+    }
+    
+    /**
+     * 检测并修复乱码的字符串
+     * 尝试使用不同的编码组合来恢复原始的中文文本
+     */
+    private String fixEncoding(String str) {
+        if (str == null || str.isEmpty()) {
+            return str;
+        }
+        
+        try {
+            // 优先尝试最常见的编码问题情况:ISO-8859-1 -> UTF-8
+            try {
+                byte[] iso88591Bytes = str.getBytes("ISO-8859-1");
+                String fixed = new String(iso88591Bytes, "UTF-8");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: ISO-8859-1 -> UTF-8, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试ISO-8859-1 -> GBK的转换
+            try {
+                byte[] iso88591Bytes = str.getBytes("ISO-8859-1");
+                String fixed = new String(iso88591Bytes, "GBK");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: ISO-8859-1 -> GBK, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试GBK -> UTF-8的转换
+            try {
+                byte[] gbkBytes = str.getBytes("GBK");
+                String fixed = new String(gbkBytes, "UTF-8");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: GBK -> UTF-8, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试UTF-8 -> GBK的转换
+            try {
+                byte[] utf8Bytes = str.getBytes("UTF-8");
+                String fixed = new String(utf8Bytes, "GBK");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: UTF-8 -> GBK, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试UTF-8 -> ISO-8859-1 -> UTF-8的转换
+            try {
+                byte[] utf8Bytes = str.getBytes("UTF-8");
+                String iso88591Str = new String(utf8Bytes, "ISO-8859-1");
+                String fixed = new String(iso88591Str.getBytes("ISO-8859-1"), "UTF-8");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: UTF-8 -> ISO-8859-1 -> UTF-8, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试GBK -> ISO-8859-1 -> UTF-8的转换
+            try {
+                byte[] gbkBytes = str.getBytes("GBK");
+                String iso88591Str = new String(gbkBytes, "ISO-8859-1");
+                String fixed = new String(iso88591Str.getBytes("ISO-8859-1"), "UTF-8");
+                if (containsChinese(fixed)) {
+                    System.out.println("编码修复成功: GBK -> ISO-8859-1 -> UTF-8, 原始值: " + str + ", 修复后: " + fixed);
+                    return fixed;
+                }
+            } catch (Exception e) {
+                // 忽略异常
+            }
+            
+            // 尝试所有可能的编码组合
+            String[] encodings = {"ISO-8859-1", "UTF-8", "GBK", "GB2312"};
+            for (String fromEncoding : encodings) {
+                for (String toEncoding : encodings) {
+                    if (!fromEncoding.equals(toEncoding)) {
+                        try {
+                            byte[] bytes = str.getBytes(fromEncoding);
+                            String fixed = new String(bytes, toEncoding);
+                            if (containsChinese(fixed)) {
+                                System.out.println("编码修复成功: " + fromEncoding + " -> " + toEncoding + ", 原始值: " + str + ", 修复后: " + fixed);
+                                return fixed;
+                            }
+                        } catch (Exception e) {
+                            // 忽略编码转换异常
+                        }
+                    }
+                }
+            }
+            
+        } catch (Exception e) {
+            // 忽略所有异常,返回原始字符串
+            System.err.println("编码修复异常: " + e.getMessage());
+        }
+        
+        return str;
+    }
+    
+    /**
+     * 检查字符串是否包含中文字符
+     */
+    private boolean containsChinese(String str) {
+        if (str == null || str.isEmpty()) {
+            return false;
+        }
+        for (char c : str.toCharArray()) {
+            if (Character.UnicodeScript.of(c) == Character.UnicodeScript.HAN) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void close() throws Exception {
+        super.close();
+        // 关闭数据库连接
+        if (connection != null && !connection.isClosed()) {
+            connection.close();
+        }
+    }
+}

+ 21 - 0
FlinkDataSync/src/main/resources/log4j.properties

@@ -0,0 +1,21 @@
+# Root logger option
+log4j.rootLogger=INFO, stdout, file
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+# Direct log messages to a log file
+log4j.appender.file=org.apache.log4j.RollingFileAppender
+log4j.appender.file.File=flink-sync.log
+log4j.appender.file.MaxFileSize=10MB
+log4j.appender.file.MaxBackupIndex=10
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+# Flink specific logging
+log4j.logger.org.apache.flink=INFO
+log4j.logger.com.ververica=INFO
+log4j.logger.io.debezium=INFO

BIN
FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlCdcSync.class


BIN
FlinkDataSync/target/classes/com/lianda/flink/sync/MySqlSink.class


+ 21 - 0
FlinkDataSync/target/classes/log4j.properties

@@ -0,0 +1,21 @@
+# Root logger option
+log4j.rootLogger=INFO, stdout, file
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+# Direct log messages to a log file
+log4j.appender.file=org.apache.log4j.RollingFileAppender
+log4j.appender.file.File=flink-sync.log
+log4j.appender.file.MaxFileSize=10MB
+log4j.appender.file.MaxBackupIndex=10
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+# Flink specific logging
+log4j.logger.org.apache.flink=INFO
+log4j.logger.com.ververica=INFO
+log4j.logger.io.debezium=INFO

BIN
FlinkDataSync/target/flink-data-sync-1.0-SNAPSHOT.jar


+ 5 - 0
FlinkDataSync/target/maven-archiver/pom.properties

@@ -0,0 +1,5 @@
+#Generated by Maven
+#Tue Feb 24 17:52:45 CST 2026
+version=1.0-SNAPSHOT
+groupId=com.lianda
+artifactId=flink-data-sync

+ 2 - 0
FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst

@@ -0,0 +1,2 @@
+com\lianda\flink\sync\MySqlCdcSync.class
+com\lianda\flink\sync\MySqlSink.class

+ 2 - 0
FlinkDataSync/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

@@ -0,0 +1,2 @@
+D:\Project\LiandaTugboat\Code\FlinkDataSync\src\main\java\com\lianda\flink\sync\MySqlCdcSync.java
+D:\Project\LiandaTugboat\Code\FlinkDataSync\src\main\java\com\lianda\flink\sync\MySqlSink.java

BIN
FlinkDataSync/target/original-flink-data-sync-1.0-SNAPSHOT.jar


+ 10 - 0
JavaBackend/.idea/.gitignore

@@ -0,0 +1,10 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Zeppelin ignored files
+/ZeppelinRemoteNotebooks/

+ 19 - 0
JavaBackend/.idea/compiler.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="true" />
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="java-backend" />
+      </profile>
+    </annotationProcessing>
+  </component>
+  <component name="JavacSettings">
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
+      <module name="java-backend" options="-parameters" />
+    </option>
+  </component>
+</project>

+ 6 - 0
JavaBackend/.idea/encodings.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+  </component>
+</project>

+ 20 - 0
JavaBackend/.idea/jarRepositories.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RemoteRepositoriesConfiguration">
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Central Repository" />
+      <option name="url" value="https://repo.maven.apache.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="central" />
+      <option name="name" value="Maven Central repository" />
+      <option name="url" value="https://repo1.maven.org/maven2" />
+    </remote-repository>
+    <remote-repository>
+      <option name="id" value="jboss.community" />
+      <option name="name" value="JBoss Community repository" />
+      <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
+    </remote-repository>
+  </component>
+</project>

+ 12 - 0
JavaBackend/.idea/misc.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
+</project>

+ 6 - 0
JavaBackend/.idea/vcs.xml

@@ -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>

+ 1 - 0
JavaBackend/.mvn/jvm.config

@@ -0,0 +1 @@
+-Xmx512m -XX:MaxMetaspaceSize=128m

+ 1 - 0
JavaBackend/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1 @@
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip

File diff suppressed because it is too large
+ 166 - 0
JavaBackend/hs_err_pid26756.log


BIN
JavaBackend/lib/HikariCP-4.0.3.jar


BIN
JavaBackend/lib/accessors-smart-2.4.11.jar


BIN
JavaBackend/lib/android-json-0.0.20131108.vaadin1.jar


BIN
JavaBackend/lib/antlr-2.7.7.jar


BIN
JavaBackend/lib/apiguardian-api-1.1.2.jar


BIN
JavaBackend/lib/asm-9.3.jar


BIN
JavaBackend/lib/aspectjweaver-1.9.7.jar


BIN
JavaBackend/lib/assertj-core-3.22.0.jar


BIN
JavaBackend/lib/byte-buddy-1.12.23.jar


BIN
JavaBackend/lib/byte-buddy-agent-1.12.23.jar


BIN
JavaBackend/lib/classmate-1.5.1.jar


BIN
JavaBackend/lib/commons-csv-1.9.0.jar


BIN
JavaBackend/lib/hamcrest-2.2.jar


BIN
JavaBackend/lib/hibernate-commons-annotations-5.1.2.Final.jar


BIN
JavaBackend/lib/hibernate-core-5.6.15.Final.jar


BIN
JavaBackend/lib/istack-commons-runtime-3.0.12.jar


BIN
JavaBackend/lib/jackson-annotations-2.13.5.jar


BIN
JavaBackend/lib/jackson-core-2.13.5.jar


BIN
JavaBackend/lib/jackson-databind-2.13.5.jar


BIN
JavaBackend/lib/jackson-datatype-jdk8-2.13.5.jar


BIN
JavaBackend/lib/jackson-datatype-jsr310-2.13.5.jar


BIN
JavaBackend/lib/jackson-module-parameter-names-2.13.5.jar


BIN
JavaBackend/lib/jakarta.activation-1.2.2.jar


BIN
JavaBackend/lib/jakarta.activation-api-1.2.2.jar


BIN
JavaBackend/lib/jakarta.annotation-api-1.3.5.jar


BIN
JavaBackend/lib/jakarta.persistence-api-2.2.3.jar


BIN
JavaBackend/lib/jakarta.transaction-api-1.3.3.jar


BIN
JavaBackend/lib/jakarta.xml.bind-api-2.3.3.jar


BIN
JavaBackend/lib/jandex-2.4.2.Final.jar


BIN
JavaBackend/lib/jaxb-runtime-2.3.8.jar


BIN
JavaBackend/lib/jboss-logging-3.4.3.Final.jar


BIN
JavaBackend/lib/jjwt-0.9.1.jar


BIN
JavaBackend/lib/json-path-2.7.0.jar


BIN
JavaBackend/lib/json-smart-2.4.11.jar


BIN
JavaBackend/lib/jsonassert-1.5.1.jar


BIN
JavaBackend/lib/jul-to-slf4j-1.7.36.jar


BIN
JavaBackend/lib/junit-jupiter-5.8.2.jar


BIN
JavaBackend/lib/junit-jupiter-api-5.8.2.jar


BIN
JavaBackend/lib/junit-jupiter-engine-5.8.2.jar


BIN
JavaBackend/lib/junit-jupiter-params-5.8.2.jar


BIN
JavaBackend/lib/junit-platform-commons-1.8.2.jar


BIN
JavaBackend/lib/junit-platform-engine-1.8.2.jar


BIN
JavaBackend/lib/log4j-api-2.17.2.jar


BIN
JavaBackend/lib/log4j-to-slf4j-2.17.2.jar


BIN
JavaBackend/lib/logback-classic-1.2.12.jar


BIN
JavaBackend/lib/logback-core-1.2.12.jar


BIN
JavaBackend/lib/lombok-1.18.28.jar


BIN
JavaBackend/lib/mockito-core-4.5.1.jar


BIN
JavaBackend/lib/mockito-junit-jupiter-4.5.1.jar


BIN
JavaBackend/lib/mysql-connector-java-8.0.28.jar


+ 0 - 0
JavaBackend/lib/objenesis-3.2.jar


Some files were not shown because too many files changed in this diff