Repository: tapdata/tapdata (language: python)

Issue:
fix: cicd default branch had been set to main lead to cicd error



Discussion / hints:

Fix patch:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index e8c3129296..5ff5949816 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,1 +1,2 @@
+main @xbsura
 * @jackin-code @ply0011 @xbsura
diff --git a/.github/ISSUE_TEMPLATE/Blank-Issue-Template.yaml b/.github/ISSUE_TEMPLATE/Blank-Issue-Template.yaml
deleted file mode 100644
index a668dcb147..0000000000
--- a/.github/ISSUE_TEMPLATE/Blank-Issue-Template.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: Blank Issue
-description: Blank Issue
-body:
-  - type: textarea
-    id: issue-comment
-    attributes:
-      label: "Comment"
-      placeholder: "Leave a comment"
-    validations:
-      required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/Connector-Plugin-Template.yaml b/.github/ISSUE_TEMPLATE/Connector-Plugin-Template.yaml
deleted file mode 100644
index cc5e465ef8..0000000000
--- a/.github/ISSUE_TEMPLATE/Connector-Plugin-Template.yaml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Connector Plugin Issue
-description: Connector Plugin Guide
-labels: ["type/enhancement"]
-body:
-  - type: textarea
-    id: basic-info
-    attributes:
-      label: Comment
-    validations:
-      required: true
-  #   - type: textarea
-  #     id: Known-Issues
-  #     attributes:
-  #       label: 已知问题列表 Known Issues
-  #     validations:
-  - type: checkboxes
-    id: feature-as-source
-    attributes:
-      label: 插件实现的源功能 Implemented Features - as Source
-      options:
-        - label: 全量读 source-feature-full-sync
-        - label: 增量读 source-feature-cdc
-        - label: 全量总数统计 source-feature-count
-        - label: 增量指定时间 source-feature-cdc-time-point
-        - label: 高级查询 source-feature-advance-query
-        - label: 查询索引 source-feature-load-index
-        - label: 加载模型 source-feature-load-schema
-        - label: 获取表 source-feature-table-names
-        - label: DDL读取 source-feature-ddl
-  - type: checkboxes
-    id: feature-as-target
-    attributes:
-      label: 插件实现的目标功能 Implemented Features - as Target
-      options:
-        - label: 插入事件 target-feature-insert
-        - label: 删除事件 target-feature-delete
-        - label: 更新事件 target-feature-update
-        - label: 清除数据 target-feature-clear-data
-        - label: 创建表 target-feature-create-table
-        - label: 删除表 target-feature-drop-table
-        - label: 创建索引 target-feature-create-index
-        - label: 删除索引 target-feature-drop-index
-        - label: DDL写入 target-feature-ddl
-#   - type: textarea
-#     id: config-for-source
-#     attributes:
-#       label: 作为源时候的数据库配置 Configuration Notes for Source
-#   - type: textarea
-#     id: config-for-target
-#     attributes:
-#       label: 作为目标时候的数据库配置 Configuration Notes for Target
-#   - type: textarea
-#     id: type-mappings
-#     attributes:
-#       label: 类型映射表 Type Mappings
-#   - type: textarea
-#     id: change-log
-#     attributes:
-#       label: 版本历史 Change Log
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 34a8bd50f8..372d3582dd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -259,8 +259,6 @@ jobs:
           mkdir -p output
           rsync -r --links --progress --password-file=/tmp/rsync.passwd rsync://root@192.168.1.184:873/data/temp/${{ needs.Set-Branch.outputs.TAG_NAME }}/ output/
           bash build/build.sh -o docker -t ${{ needs.Set-Branch.outputs.TAG_NAME }}
-          echo '${{ secrets.HARBOR_PASS }}' | docker login harbor.internal.tapdata.io --username=${{ secrets.HARBOR_USER }} --password-stdin
-          docker push harbor.internal.tapdata.io/tapdata/tapdata:${{ needs.Set-Branch.outputs.TAG_NAME }}
       - name: Print Image Info
         run: |
           echo '**Google Docker Image:**' >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/mr-ci.yaml b/.github/workflows/mr-ci.yaml
index fe5fb042c9..2d82464259 100644
--- a/.github/workflows/mr-ci.yaml
+++ b/.github/workflows/mr-ci.yaml
@@ -12,18 +12,23 @@ on:
     - cron: "0 21 * * *"
   workflow_dispatch:
     inputs:
-      mrci_run:
-        description: 'MRCI run or not'
+      testsigma_run:
+        description: 'Testsigma run or not'
         required: true
         type: boolean
         default: false
+      tapdata_branch:
+        description: 'tapdata branch'
+        required: true
+        type: string
+        default: 'develop'
 
 jobs:
 
   Get-Stable-Branch:
     runs-on: ubuntu-latest
     outputs:
-      OPENSOURCE_BRANCH: ${{ steps.set-output.outputs.OPENSOURCE_BRANCH }}
+      TAPDATA_BRANCH: ${{ steps.set-output.outputs.TAPDATA_BRANCH }}
       ENTERPRISE_BRANCH: ${{ steps.set-output.outputs.ENTERPRISE_BRANCH }}
       FRONTEND_BRANCH: ${{ steps.set-output.outputs.FRONTEND_BRANCH }}
       TAG_NAME: ${{ steps.set-output.outputs.TAG_NAME }}
@@ -64,21 +69,29 @@ jobs:
           ref: "main"
           token: ${{ secrets.TAPDATA_ENT_CICD_TOKEN }}
           path: tapdata-application
-      - name: Get OpenSource Branch Name -- Common
+      - name: Get Tapdata Branch Name -- Common
         run: |
-          echo "OPENSOURCE_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
-      - name: Get OpenSource Branch Name -- Pr
+          echo "TAPDATA_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
+      - name: Get Tapdata Branch Name -- Pr
         if: github.event_name == 'pull_request'
         run: |
-          echo "OPENSOURCE_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV
+          echo "TAPDATA_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV
+      - name: Get Tapdata Branch Name -- Workflow Dispatch
+        if: github.event_name == 'workflow_dispatch'
+        run: |
+          echo "TAPDATA_BRANCH=${{ github.event.inputs.tapdata_branch }}" >> $GITHUB_ENV
+      - name: Get Tapdata Branch Name -- Schedule
+        if: github.event_name == 'schedule'
+        run: |
+          echo "TAPDATA_BRANCH=develop" >> $GITHUB_ENV
       - name: Get last stable branch
         id: set-output
         run: |
           cd tapdata-application
-          OPENSOURCE_BRANCH=${{ env.OPENSOURCE_BRANCH }}
+          TAPDATA_BRANCH=${{ env.TAPDATA_BRANCH }}
           ENTERPRISE_BRANCH="develop"
           FRONTEND_BRANCH="develop"
-          echo "::set-output name=OPENSOURCE_BRANCH::${OPENSOURCE_BRANCH}"
+          echo "::set-output name=TAPDATA_BRANCH::${TAPDATA_BRANCH}"
           echo "::set-output name=ENTERPRISE_BRANCH::${ENTERPRISE_BRANCH}"
           echo "::set-output name=FRONTEND_BRANCH::${FRONTEND_BRANCH}"
           echo "::set-output name=TAG_NAME::${TAG_NAME}"
@@ -91,7 +104,7 @@ jobs:
         uses: actions/checkout@v3
         with:
           repository: 'tapdata/tapdata'
-          ref: ${{ needs.Get-Stable-Branch.outputs.OPENSOURCE_BRANCH }}
+          ref: ${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }}
           token: ${{ secrets.TAPDATA_ENT_CICD_TOKEN }}
           path: tapdata
           fetch-depth: 0
@@ -124,20 +137,12 @@ jobs:
       - name: Checkout Tapdata Code
         run: |
           export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no"
-          git clone -b ${{ needs.Get-Stable-Branch.outputs.OPENSOURCE_BRANCH }} ${{ secrets.INTERNAL_REPO }}/tapdata/tapdata.git
+          git clone -b ${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }} ${{ secrets.INTERNAL_REPO }}/tapdata/tapdata.git
           cd tapdata && git fetch --tags
       - name: Patch Maven Dependens
         run: |
           echo "Gotapd8!" > /tmp/rsync.passwd && chmod 600 /tmp/rsync.passwd
           rsync --delete --password-file=/tmp/rsync.passwd -avz rsync://root@192.168.1.184:873/data/enterprise-temp/tapdata/ /root/.m2/
-      - name: Get OpenSource Branch Name -- Common
-        run: |
-          echo "OPENSOURCE_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
-      - name: Set OpenSource Branch Name -- Pull Request
-        if: ${{ github.event_name == 'pull_request' }}
-        run: |
-          branch_name="${{ github.event.pull_request.head.ref }}"
-          echo "OPENSOURCE_BRANCH=$branch_name" >> $GITHUB_ENV
       - name: Build Tapdata - And Analyze
         env:
           GITHUB_TOKEN: ${{ secrets.TAPDATA_ENT_CICD_TOKEN }}
@@ -146,7 +151,7 @@ jobs:
           cd tapdata && mvn clean test -T1C -Dmaven.compile.fork=true -P idaas && mvn sonar:sonar \
             -Dsonar.projectKey=tapdata -Dsonar.host.url=${{ secrets.SONAR_HOST }} \
             -Dsonar.login=${{ secrets.SONAR_USER }} -Dsonar.password=${{ secrets.SONAR_PASS }} \
-            -Dsonar.branch.name=${{ env.OPENSOURCE_BRANCH }}
+            -Dsonar.branch.name=${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }}
       - name: Prevent Skip Unittest
         run: |
           cd tapdata
@@ -165,12 +170,12 @@ jobs:
       - name: Send SonarQube Quality Gate to Pr Comment
         if: ${{ always() && steps.sonar.outcome == 'failure' }}
         run: |
-          cd tapdata/build/ && bash check_sonarqube.sh --project-key=tapdata --branch=${{ env.OPENSOURCE_BRANCH }} \
+          cd tapdata/build/ && bash check_sonarqube.sh --project-key=tapdata --branch=${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }} \
             --sonar-token=${{ secrets.SONAR_TOKEN }} --github-token=${{ secrets.TAPDATA_ENT_CICD_TOKEN }} \
             --repo=tapdata --pr-number=${{ github.event.pull_request.number }}
 
   Build-And-Deploy:
-    if: ${{ github.event_name == 'schedule' || inputs.mrci_run }}
+    if: ${{ github.event_name == 'schedule' || inputs.testsigma_run }}
     runs-on: ubuntu-latest
     needs:
       - Get-Stable-Branch
@@ -178,14 +183,6 @@ jobs:
       IP: ${{ steps.get_ip_port.outputs.IP }}
       PORT: ${{ steps.get_ip_port.outputs.PORT }}
     steps:
-      - name: Get OpenSource Branch Name -- Common
-        run: |
-          echo "OPENSOURCE_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
-      - name: Set OpenSource Branch Name -- Pull Request
-        if: ${{ github.event_name == 'pull_request' }}
-        run: |
-          branch_name="${{ github.event.pull_request.head.ref }}"
-          echo "OPENSOURCE_BRANCH=$branch_name" >> $GITHUB_ENV
       - name: Trigger - Build And Deploy
         uses: convictional/trigger-workflow-and-wait@v1.6.1
         with:
@@ -195,7 +192,7 @@ jobs:
           workflow_file_name: build-self-hosted.yaml
           ref: ${{ needs.Get-Stable-Branch.outputs.ENTERPRISE_BRANCH }}
           wait_interval: 10
-          client_payload: '{"FRONTEND_BRANCH": "${{ needs.Get-Stable-Branch.outputs.FRONTEND_BRANCH }}", "OPENSOURCE_BRANCH": "${{ env.OPENSOURCE_BRANCH }}", "FOR_AUTO_TEST": "true", "TAG_NAME": "${{ needs.Get-Stable-Branch.outputs.TAG_NAME }}"}'
+          client_payload: '{"FRONTEND_BRANCH": "${{ needs.Get-Stable-Branch.outputs.FRONTEND_BRANCH }}", "OPENSOURCE_BRANCH": "${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }}", "FOR_AUTO_TEST": "true", "TAG_NAME": "${{ needs.Get-Stable-Branch.outputs.TAG_NAME }}"}'
           propagate_failure: true
           trigger_workflow: true
           wait_workflow: true
@@ -226,7 +223,7 @@ jobs:
 
   MRCI:
     runs-on: office-scan
-    if: ${{ github.event_name == 'schedule' || inputs.mrci_run }}
+    if: ${{ github.event_name == 'schedule' || inputs.testsigma_run }}
     concurrency:
       group: staging_environment
     timeout-minutes: 120
@@ -238,19 +235,11 @@ jobs:
         run: |
           echo "IP=${{ needs.Build-And-Deploy.outputs.IP }}" >> $GITHUB_ENV
           echo "PORT=${{ needs.Build-And-Deploy.outputs.PORT }}" >> $GITHUB_ENV
-      - name: Check - If Push Action
-        if: ${{ github.event_name == 'push' }}
-        run: |
-          echo "CURRENT_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV
-      - name: Check - If Merge Request Action
-        if: ${{ github.event_name == 'pull_request' }}
-        run: |
-          echo "CURRENT_BRANCH=${{ github.ref }}" >> $GITHUB_ENV
       - name: Fetch Code - Main Repo
         run: |
           rm -rf tapdata
           export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no"
-          git clone -b ${{ needs.Get-Stable-Branch.outputs.OPENSOURCE_BRANCH }} --single-branch ${{ secrets.INTERNAL_REPO }}/tapdata/tapdata.git
+          git clone -b ${{ needs.Get-Stable-Branch.outputs.TAPDATA_BRANCH }} --single-branch ${{ secrets.INTERNAL_REPO }}/tapdata/tapdata.git
           cd tapdata && git fetch --tags
       - name: Compile - IEngine And Management
         run: |
@@ -261,7 +250,7 @@ jobs:
         run: |
           bash build/run_smoke_tests.sh --runtimedata="url=http://${{ env.IP }}:${{ env.PORT }}"
       - name: Run - Local Service - Schedule
-        if: ${{ github.event_name == 'schedule' || inputs.mrci_run }}
+        if: ${{ github.event_name == 'schedule' || inputs.testsigma_run }}
         run: |
           cd tapdata
           bash build/run_smoke_tests.sh --runtimedata="url=http://${{ env.IP }}:${{ env.PORT }}" --maxtimeinmins=120 --testplanid=724 --pollcount=12
@@ -281,7 +270,7 @@ jobs:
           echo "OK"
 
   CLOSE_ENV:
-    if: ${{ github.event_name == 'schedule' || failure() || inputs.mrci_run }}
+    if: ${{ github.event_name == 'schedule' || failure() || inputs.testsigma_run }}
     runs-on: ubuntu-latest
     needs:
       - MRCI
diff --git a/README.md b/README.md
index d4ee51d7de..ccc2f7aefe 100644
--- a/README.md
+++ b/README.md
@@ -1,58 +1,241 @@
-<img src="https://github.com/tapdata/tapdata-private/raw/master/assets/logo-orange-grey-bar.png" width="300px"/>
-<p align="center">
-    <a href="https://github.com/tapdata/tapdata/graphs/contributors" alt="Contributors">
-        <img src="https://img.shields.io/github/contributors/tapdata/tapdata" /></a>
-    <a href="https://github.com/tapdata/tapdata/pulse" alt="Activity">
-        <img src="https://img.shields.io/github/commit-activity/m/tapdata/tapdata" /></a>
-    <a href="https://tapdata.github.io/tapdata">
-        <img src="https://github.com/tapdata/tapdata/actions/workflows/docker-image.yml/badge.svg" alt="build status"></a>
-</p>
+<div align="center">
+<a href="https://tapdata.io/">
+<img src="./assets/tapdata-banner.jpg" width="900px"/>
+</a>
+<br/><br/>
+
+[![LICENSE](https://img.shields.io/github/license/tapdata/tapdata.svg)](https://github.com/tapdata/tapdata/blob/main/LICENSE)
+[![Contributors](https://img.shields.io/github/contributors/tapdata/tapdata)](https://github.com/tapdata/tapdata/graphs/contributors)
+[![Activity](https://img.shields.io/github/commit-activity/m/tapdata/tapdata)](https://github.com/tapdata/tapdata/pulse)
+[![Release](https://img.shields.io/github/v/tag/tapdata/tapdata.svg?sort=semver)](https://github.com/tapdata/tapdata/releases)
+
+</div>
 
+---
 
-## Online Document: https://docs.tapdata.io/
-## Cloud Service: https://cloud.tapdata.io/
+[![Try Cloud Service](<https://img.shields.io/badge/-Try%20Cloud%20Service%20%E2%86%92-rgb(255,140,0)?style=for-the-badge>)](https://cloud.tapdata.net)
+[![Official Website](<https://img.shields.io/badge/-Official%20Website%20%E2%86%92-rgb(59,71,229)?style=for-the-badge>)](https://tapdata.io)
+[![Docs](<https://img.shields.io/badge/-Online%20Documentation%20%E2%86%92-rgb(0,205,102)?style=for-the-badge>)](https://docs.tapdata.io)
 
-## What is Tapdata?
-Tapdata is a real-time data integration platform that enables data to be synchronized in real-time among various systems such as databases, SaaS services, applications, and files.
+## What is Tapdata ?
+Tapdata is a Change Data Capture(CDC) based, Real-Time data integration platform that enables data to be synchronized in real-time among various systems such as databases, SaaS services, applications, and files.
 The synchronization tasks can be easily built through drag-and-drop operations, from table creation to full and incremental synchronization, all processes are fully automated.
 
-Once the MongoDB-based data centralized storage feature is enabled, Tapdata immediately transforms into an integrated data service platform.
-Through a user-friendly visual interface, you can form real-time, complete, and accurate document-based data in MongoDB from dispersed but interrelated data from various sources. This allows business developers to develop their applications more quickly, and data analysis engineers can rapidly construct their own metrics systems. Once integrated, the data can be used ubiquitously.
+1. [Supported Connectors](https://docs.tapdata.io/introduction/supported-databases)
+2. [FAQ](https://docs.tapdata.io/faq/)
+
+For more details, please read [docs](https://docs.tapdata.io/)
+
+Based on different distribution strategies and functionalities, Tapdata has three distribution versions: Community Edition, Cloud Service, and Enterprise Edition. All content under this open-source project corresponds to the Community Edition.
+
+For the differences between the three versions, you can refer to: https://tapdata.io/
+
+## Table of contents
+<p align="left">
+  1. <a href="#key-features" target="_blank"><b>Key Features</b></a><br/>
+  2. <a href="#primary-use-cases"><b>Use Cases</b></a><br/>
+  3. <a href="#quick-start" target="_blank"><b>Quick Start</b></a><br/>
+  4. <a href="#examples"><b>Examples</b></a><br/>
+  5. <a href="#license"><b>License</b></a><br/>
+  6. <a href="#contact-us"><b>Contact Us</b></a><br/>
+  7. <a href="./building.md"><b>Build From Source</b></a><br/>
+  8. <a href="./contributing.md"><b>Contributing</b></a><br/>
+</p>
+
+## Key Features
+### End-To-End Visual UI
+
+|                                                                                                                                                                                                                                                                                                                                                       |                                       |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
+| <b>Full Process Automation</b><br/>Automatic table creation, and Automatic switching between Full and Incremental synchronization.<br/><br/><b>Drag-And-Drop</b><br/>Operate through a visual interface, Easy to use<br/><br/><b>Monitor</b><br/>sync performance, task progress, key events, and logs.                                                                    | <img src="./assets/monitor.jpg"/>     |
+
+<br />
 
+### Lightweight ETL Solution
+
+|                                                                                                                                                                                                                                                                                                                                                       |                                       |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
+| <b>Table Filter and Mapping</b><br/>Rich visual processors that allow table selection, renaming, adding or deleting fields, and renaming.<br/><br/><b>Record Transformation</b><br/>Easily use JS/Python code to do transformation<br/><br/><b>Union</b><br/>Union multi tables to one target.                                                   | <img src="./assets/tapdata-etl.jpg"/> |
+
+<br />
+
+### Build materialized views in MongoDB
+
+|                                                                                                                                                                                                                                                                                                                                                       |                                        |
+| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
+| <b>Document-style view</b><br/>Support Embedded documents and arrays, deeply integrated with MongoDB.<br/><br/><b>Multi-table association</b><br/>hierarchical nesting, and convenient construction of 1:1 and 1:N models.<br /><br/><b>Multi-table stream merging</b><br/>Unified batch and stream processing, multi-table updates in any order. | <img src="./assets/tapdata-view.jpg"/> |
+
+<br />
+
+## Primary Use Cases
+1. Synchronize data from traditional RDBMS to modern databases such as MongoDB, Elasticsearch (ES), or Redis to support new business uses.
+2. Consolidate data from various databases into a unified data warehouse.
+3. Stream database changes to Kafka.
+4. Perform data synchronization between heterogeneous databases.
+5. Use MongoDB to build your unified data hub.
  
-### Quick Start
-1. Click https://cloud.tapdata.io/, start your real-time data journey immediately, Free Trial(NO Credit Card Required)
-2. You can easily deploy the service in your local environment with 2 Steps:
-    1. Install Docker
-    2. Exec: `docker run -itd -p 3030:3030 github.com/tapdata/tapdata-opensource:latest bash`, then you can get tapdata server by visting http://localhost:3030,
-    default username is: admin@admin.com, default password is admin
+## Quick Start
+### Start with docker
+To run the complete service, the basic resource requirements are:
+1. At least 5GB of available memory
+2. At least 20GB of available disk space
+3. At least 1 free CPU core
 
-### Example Usage
-Before starting the task, please ensure that you can see the following content through your browser.
-![cluster status](./assets/cluster-status.png)
+RUN `docker run -d -p 3030:3030 ghcr.io/tapdata/tapdata:latest`, wait for 3 minutes, then you can get it from http://localhost:3030/, if everything is ok, you can see login page like this:
+<img src="./assets/tapdata-login.jpg" width="900px"/>
+default username is: admin@admin.com, default password is admin
 
-#### Sync Data From Mysql To MongoDB
-1. Add Mysql Connection
-![](./assets/mysql-conn.png)
+### Start with cloud service
+Tapdata service is available in cloud service, you can use fully-managed service, or deploy engine to your private network
 
-2. Add MongoDB Connection
-![](./assets/mongodb-conn.png)
+Try on https://cloud.tapdata.io/, support google and github account login, free trial, NO credit card needed, start your real-time data journey immediately.
 
-3. Click Data Pipelines => Replications, Create a new job
+## Examples
+<details>
+    <summary><h4>🗂️ Create Datasource and Test it</h4></summary>
 
-4. Drag Mysql Connection and MongoDB Connection from left side bar, Drag a line from the MySQL connection node and connect it to the MongoDB node
-5. Click Mysql Node, select tables what you want to sync
-![](./assets/mysql-mongodb-config.png)
+1. Login tapdata platform
 
-6. Start Job and watch data keep syncing!
-![](./assets/mysql-mongodb-monitor.png)
+2. In the left navigation panel, click Connections
 
+3. On the right side of the page, click Create
 
-## License
-Defaults to Apache V2 License.
+4. In the pop-up dialog, search and select MySQL
+
+5. On the page that you are redirected to, follow the instructions below to fill in the connection information for MySQL
+
+<img src="./assets/example-1-create-mysql-connection.jpg"></img>
+
+6. Click Test, make sure all test pass, then click Save
+
+<img src="./assets/example-1-test.jpg"></img>
+
+</details>
+
+<details>
+    <summary><h4>🗂️ Sync Data From MySQL To MongoDB</h4></summary>
+
+1. Create MySQL and MongoDB data source
+
+2. In the left navigation panel, click Data Pipelines -> Data Replications
+
+3. On the right side of the page, click Create
+
+4. Drag and drop MySQL and MongoDB data sources onto the canvas
+
+5. Drag a line from the MySQL data source to MongoDB
+
+6. Configure the MySQL data source and select the data tables you want to synchronize
+
+<img src='./assets/example-2-config-mysql.jpg'></img>
+
+7. Click the Save button in the upper right corner, then click the Start button
+
+8. Observe the indicators and events on the task page until data is in sync
+
+<img src='./assets/example-2-metrics.jpg'></img>
+
+</details>
+
+<details>
+    <summary><h4>🗂️ MySQL To PostgreSQL with Simple ETL</h4></summary>
+
+1. Create MySQL and PostgreSQL data source
+
+2. In the left navigation panel, click Data Pipelines -> Data Transformation
+
+3. On the right side of the page, click Create
+
+4. Drag and drop MySQL and PostgreSQL data sources onto the canvas
+
+5. Drag a line from the MySQL data source to PostgreSQL
 
-For Connectors, the license is Apache V2.
+6. Click the plus sign on the connection line and select Field Rename
+
+<img src='./assets/example-3-field-rename-1.jpg'></img>
+
+7. Click Field Rename node, change i_price to price, i_data to data in config form
+
+<img src='./assets/example-3-field-rename-2.jpg'></img>
+
+8. Click the Save button in the upper right corner, then click the Start button
+
+9. Observe the indicators and events on the task page until data is in sync
+
+<img src='./assets/example-3-metrics.jpg'></img>
+
+</details>
+
+<details>
+    <summary><h4>🗂️ Making materialized views in MongoDB</h4></summary>
+
+Materialized view is a special feature of tapdata, You can give full play to the characteristics of MongoDB document database and create the data model you need, try enjoy it !
+
+In this example, I will make a view using 2 tables in MySQL: order and product, make product as a embedded document of order, here is the step:
+
+1. Create MySQL and MongoDB data source
+
+2. In the left navigation panel, click Data Pipelines -> Data Transformation
+
+3. On the right side of the page, click Create
+
+4. Click mysql data source in left up side, then drag and drop order table and product table onto the canvas
+
+5. Drag and drop "Master-slave merge" node in left bottom side onto the canvas
+
+6. Drag a line from the order table to Master-slave merge
+
+7. Drag a line from the product table to Master-slave merge
+
+8. Drag and drop MongoDB data source onto the canvas, and drag a line from the "Master-slave merge" node to MongoDB node
+
+<img src='./assets/example-4-1.jpg'></img>
+
+9. Click "Master-slave merge" node, then drag product table into order table in the right side in "Table Name"
+
+<img src='./assets/example-4-2.jpg'></img>
+
+10. Click "Master-slave merge" node, then click product table, config Data write model to "Match and Merge", Field write path to "product", Association Conditions to "order_id" => "order_id", then you can see Schema in bottom changed
+
+11. Click MongoDB node, and config target table name as order_with_product, update condition field config as "order_id"
+
+<img src='./assets/example-4-2.jpg'></img>
+
+12. Click the Save button in the upper right corner, then click the Start button
+
+13. Observe the indicators and events on the task page until data is in sync
+
+14. Check collection order_with_product in MongoDB, and you will see the data model
+
+</details>
+
+<details>
+    <summary><h4>🗂️ Data consistency check</h4></summary>
+
+Using the data verification feature, you can quickly check whether the synchronized data is consistent and accurate
+
+1. In the left navigation panel, click Data Pipelines -> Data Validation
+
+2. On the right side of the page, click Task Consistency Validation
+
+3. Choose 1 task, and valid type choose "All Fields Validation", it means system will check all fields for all record
+
+<img src='./assets/example-5-config.jpg'></img>
+
+4. Click Save, then click Execute in the task list
+
+5. Wait validation task finished, click Result in the task list, and check the validation result
+
+<img src='./assets/example-5-result.jpg'></img>
+
+</details>
+
+## Architecture
+![Alt Text](./assets/architecture.jpg)
+
+## License
+Tapdata Community is under the Apache 2.0 license. See the [LICENSE](https://github.com/tapdata/tapdata/blob/main/LICENSE) file for details.
 
-## Join now
+## Contact Us
 - [Send Email](mailto:team@tapdata.io)
 - [Slack channel](https://join.slack.com/t/tapdatacommunity/shared_invite/zt-1biraoxpf-NRTsap0YLlAp99PHIVC9eA)
diff --git a/README.zh-CN.md b/README.zh-CN.md
deleted file mode 100644
index 78b5acab28..0000000000
--- a/README.zh-CN.md
+++ /dev/null
@@ -1,191 +0,0 @@
-<img src="https://github.com/tapdata/tapdata-private/raw/master/assets/logo-orange-grey-bar.png" width="300px"/>
-<p align="center">
-    <a href="https://github.com/tapdata/tapdata/graphs/contributors" alt="Contributors">
-        <img src="https://img.shields.io/github/contributors/tapdata/tapdata" /></a>
-    <a href="https://github.com/tapdata/tapdata/pulse" alt="Activity">
-        <img src="https://img.shields.io/github/commit-activity/m/tapdata/tapdata" /></a>
-    <a href="https://tapdata.github.io/tapdata">
-        <img src="https://github.com/tapdata/tapdata/actions/workflows/docker-image.yml/badge.svg" alt="build status"></a>
-</p>
-
-[中文简要文档地址](https://github.com/tapdata/tapdata/blob/master/README.zh-CN.md)
-
-[English Readme](https://github.com/tapdata/tapdata/blob/master/README.md)
-
-## 完整在线文档地址: https://tapdata.github.io/
-## Tapdata 为什么而存在
-Tapdata 是新一代的实时数据平台, 通过把企业核心数据实时集中到中央化数据平台的方式并通过API 或者反向同步方式, 为下游的交互式应用, 微服务或交互式分析提供新鲜实时的数据
-
-你也可以使用Tapdata作为一个实时数据集成（ETL）工具, 提供各种异构数据库之间的实时复制能力
-
-## Tapdata 架构图 
-<img src="https://github.com/tapdata/tapdata/raw/master/assets/tapdata-ldp.png" width="900px"/>
-
-## 安装准备
-### 环境准备
-1. 在开始之前, 请保证您的环境安装了 Docker
-2. 当前工具仅在 Linux 下进行过完整测试, 其他操作系统的适配正在进行中
-3. 克隆当前仓库代码到本地: `git clone https://github.com/tapdata/tapdata.git && cd tapdata`
-
-### 快速启动
-1. 执行 `bash build/quick-use.sh` 会快速启动一个使用环境, 然后会自动进入 tapshell 交互客户端
-2. 下次进入环境时, 可执行 `bash bin/tapshell.sh` 进入交互命令行工具
- 
-### 从源码编译启动
-1. 执行 `bash build/quick-dev.sh` 会从源码编译, 并启动一个完整的使用环境,  然后会自动进入 tapshell 交互客户端
-2. 下次进入环境时, 可执行 `bash bin/tapshell.sh` 进入交互命令行工具
-
-### 环境清理
-1. 执行 `bash build/clean.sh` 会清理包括编译中间产物, 编译镜像, 运行容器在内的全部内容, 但是会保留运行的任务配置与进度等信息
-2. 如果需要删除任务运行配置, 请删除主目录的 data 目录即可
-
-## 使用说明
-1. 在环境启动后, 可通过 `bash bin/tapshell.sh` 进入交互客户端
-
-交互客户端可使用命令模式, 或者 Shell API 模式进行实时数据平台的使用
-
-### 基本概念
-1. 数据连接器: 平台支持的数据连接类型, 比如 Mysql, PG, MongoDB
-2. 数据源: 通过连接器创建的具体的数据来源
-3. 数据表: 具有一定数据结构的数据集合
-4. 任务: 
-
-### 查看资源
-1. 查看支持的数据连接器
-```
->>> show connectors
-1839d8 MongoDB
-183a77 Mysql
-183af5 PostgreSQL
-```
-
-2. 查看创建的数据源
-```
->>> show datasources
-id         status     database_type        name
-183afa     ready      MongoDB              mongodb
-```
-
-3. 查看数据源下的表
-```
->>> use mongodb
-datasource switch to: mongodb
-
->>> show tables
-```
-
-4. 查看数据表的结构
-```
->>> use mongodb
-datasource switch to: mongodb
-
->>> desc CAR_CLAIM
-{
-    "_id": "OBJECT_ID",
-    "SETTLED_DATE": "DATE_TIME",
-    "CLAIM_ID": "STRING",
-    "SETTLED_AMOUNT": "INT32",
-    "CLAIM_REASON": "STRING",
-    "POLICY_ID": "STRING",
-    "CLAIM_DATE": "DATE_TIME",
-    "LAST_CHANGE": "DATE_TIME",
-    "CLAIM_AMOUNT": "INT32"
-}
-```
-
-5. 查看任务列表, 分别为 任务id, 名字, 状态, 类型
-```
->>> show jobs
-system has 3 jobs
-18415a: migrate                                    running      sync/initial_sync+cdc
-1843e1: migrate2                                   error        sync/initial_sync+cdc
-```
-
-### 操作数据源
-```
-# mongodb
->>> source = DataSource("mongodb", "$name").uri("$uri")
-
-# mysql
->>> source = DataSource("mysql", "$name").host("$host").port($port).username("$username").port($port).db("$db")
-
-# pg
->>> source = DataSource("postgres", "$name").host("$host").port($port).username("$username").port($port).db("$db").schema("$schema").logPluginName("wal2json")
-
-# 保存数据源, 并加载表结构
->>> source.save()
-
-# 重新加载表结构
->>> validate datasource $name
-
-# 删除数据源
->>> delete datasource $name
-```
-
-### 操作任务
-1. 同步一张表, 默认为全量+增量同步
-```
-# 创建一个工作流
->>> p = Pipeline("$name")
-
-# 使用 readFrom 从源读取数据, 使用 writeTo 将其写向目标
->>> p.readFrom("$source_name.$table").writeTo("$sink_name.$table")
-
-# 启动任务
->>> p.start()
-
-# 监控工作流的任务, 查看指标与日志
->>> p.monitor()
->>> p.logs()
-
-# 停止任务
->>> p.stop()
-
-# 列出任务
->>> show jobs
-
-# 监控任务, 查看指标与日志
->>> monitor job $name
->>> logs job $name [tail=False] [limit=10] [t=30]
-
-# 停止任务
->>> stop job $name
-
-# 删除任务
->>> delete job $name
-```
-
-2. 同步一张表, 并使用自定义函数进行一些简单数据处理, 目前你可以使用 Python3 语法来进行函数的定义
-```
-# 1. 定义一个方法, 对 record 进行变换, 并返回 record
->>> def fn(record):
-        record["x"] = 1
-        return record
-
-# 2. 使用 processor 算子指定变换方法
->>> p.readFrom(...).processor(fn).writeTo(...)
-```
-
-3. 同步多张表
-```
-# 创建一个工作流
->>> p = Pipeline("$name")
-
-# 新建一个包含多张数据表的数据读取源, 支持正则匹配
->>> source = Source("$datasource_name", ["table1", "table2"...])
->>> source = Source("$datasource_name", table_re="xxx.*")
-
-# 通过 writeTo 方法, 可修改同步表的前后缀
->>> p.readFrom(source).writeTo("$datasource_name", prefix="", suffix="")
-```
-
-
-## 开源 License
-Tapdata 使用 Apache V2 License
-
-## 加入我们
-- 微信
-<img src="https://github.com/tapdata/tapdata/raw/master/assets/wechat-qr-code.jpg" width="300px"/>
-
-- [Slack](https://join.slack.com/t/tapdatacommunity/shared_invite/zt-1biraoxpf-NRTsap0YLlAp99PHIVC9eA)
-
diff --git a/assets/559f2a22-1ffd-4ac0-972f-aee706f51469.gif b/assets/559f2a22-1ffd-4ac0-972f-aee706f51469.gif
new file mode 100644
index 0000000000..107542610b
Binary files /dev/null and b/assets/559f2a22-1ffd-4ac0-972f-aee706f51469.gif differ
diff --git a/assets/architecture.jpg b/assets/architecture.jpg
new file mode 100644
index 0000000000..49c751fa84
Binary files /dev/null and b/assets/architecture.jpg differ
diff --git a/assets/architecture.png b/assets/architecture.png
new file mode 100644
index 0000000000..93d02bf22a
Binary files /dev/null and b/assets/architecture.png differ
diff --git a/assets/example-1-create-mysql-connection.jpg b/assets/example-1-create-mysql-connection.jpg
new file mode 100644
index 0000000000..9881ecc125
Binary files /dev/null and b/assets/example-1-create-mysql-connection.jpg differ
diff --git a/assets/example-2-config-mysql.jpg b/assets/example-2-config-mysql.jpg
new file mode 100644
index 0000000000..362c1797d2
Binary files /dev/null and b/assets/example-2-config-mysql.jpg differ
diff --git a/assets/example-2-metrics.jpg b/assets/example-2-metrics.jpg
new file mode 100644
index 0000000000..25ceacf175
Binary files /dev/null and b/assets/example-2-metrics.jpg differ
diff --git a/assets/example-3-field-rename-1.jpg b/assets/example-3-field-rename-1.jpg
new file mode 100644
index 0000000000..1df6aef03f
Binary files /dev/null and b/assets/example-3-field-rename-1.jpg differ
diff --git a/assets/example-3-field-rename-2.jpg b/assets/example-3-field-rename-2.jpg
new file mode 100644
index 0000000000..797eeb6c77
Binary files /dev/null and b/assets/example-3-field-rename-2.jpg differ
diff --git a/assets/example-3-metrics.jpg b/assets/example-3-metrics.jpg
new file mode 100644
index 0000000000..e2be69131d
Binary files /dev/null and b/assets/example-3-metrics.jpg differ
diff --git a/assets/example-4-1.jpg b/assets/example-4-1.jpg
new file mode 100644
index 0000000000..7aee568998
Binary files /dev/null and b/assets/example-4-1.jpg differ
diff --git a/assets/example-4-2.jpg b/assets/example-4-2.jpg
new file mode 100644
index 0000000000..cfec2eeae3
Binary files /dev/null and b/assets/example-4-2.jpg differ
diff --git a/assets/example-4-3.jpg b/assets/example-4-3.jpg
new file mode 100644
index 0000000000..e74cabca95
Binary files /dev/null and b/assets/example-4-3.jpg differ
diff --git a/assets/example-5-config.jpg b/assets/example-5-config.jpg
new file mode 100644
index 0000000000..5c3c2d82fa
Binary files /dev/null and b/assets/example-5-config.jpg differ
diff --git a/assets/example-5-result.jpg b/assets/example-5-result.jpg
new file mode 100644
index 0000000000..f30c1f7058
Binary files /dev/null and b/assets/example-5-result.jpg differ
diff --git a/assets/login.jpg b/assets/login.jpg
new file mode 100644
index 0000000000..78fcc41093
Binary files /dev/null and b/assets/login.jpg differ
diff --git a/assets/logo-orange-grey-bar.png b/assets/logo-orange-grey-bar.png
old mode 100755
new mode 100644
diff --git a/assets/monitor.jpg b/assets/monitor.jpg
new file mode 100644
index 0000000000..5134282eee
Binary files /dev/null and b/assets/monitor.jpg differ
diff --git a/assets/tapdata-banner.jpg b/assets/tapdata-banner.jpg
new file mode 100644
index 0000000000..3e3e756e4e
Binary files /dev/null and b/assets/tapdata-banner.jpg differ
diff --git a/assets/tapdata-etl.jpg b/assets/tapdata-etl.jpg
new file mode 100644
index 0000000000..706b8cb613
Binary files /dev/null and b/assets/tapdata-etl.jpg differ
diff --git a/assets/tapdata-login.jpg b/assets/tapdata-login.jpg
new file mode 100644
index 0000000000..f1b683b0d2
Binary files /dev/null and b/assets/tapdata-login.jpg differ
diff --git a/assets/tapdata-view.jpg b/assets/tapdata-view.jpg
new file mode 100644
index 0000000000..5d3bb67382
Binary files /dev/null and b/assets/tapdata-view.jpg differ
diff --git a/build/.connectors_list b/build/.connectors_list
new file mode 100644
index 0000000000..87cabf958d
--- /dev/null
+++ b/build/.connectors_list
@@ -0,0 +1,14 @@
+bigquery-connector-v1.0-SNAPSHOT.jar
+clickhouse-connector-v1.0-SNAPSHOT.jar
+doris-connector-v1.0-SNAPSHOT.jar
+dummy-connector-v1.0-SNAPSHOT.jar
+elasticsearch-connector-v1.0-SNAPSHOT.jar
+kafka-connector-v1.0-SNAPSHOT.jar
+mariadb-connector-v1.0-SNAPSHOT.jar
+mongodb-connector-v1.0-SNAPSHOT.jar
+mongodb-lower-connector-v1.0-SNAPSHOT.jar
+mongodb-atlas-connector-v1.0-SNAPSHOT.jar
+mysql-connector-v1.0-SNAPSHOT.jar
+postgres-connector-v1.0-SNAPSHOT.jar
+redis-connector-v1.0-SNAPSHOT.jar
+tdengine-connector-v1.0-SNAPSHOT.jar
\ No newline at end of file
diff --git a/build/build.sh b/build/build.sh
index b1e49232b4..18c558b10c 100755
--- a/build/build.sh
+++ b/build/build.sh
@@ -29,6 +29,8 @@ OUTPUT_DIR="$PROJECT_ROOT_DIR/output"
 PACKAGE_COMPONENTS=""
 # output type (docker or tar)
 OUTPUT_TYPE=""
+# filter connectors list
+CONNECTORS_LIST=$(cat $SCRIPT_BASE_DIR/.connectors_list)
 
 while getopts 'c:l:u:p:t:o:' OPT; do
 	case "$OPT" in
@@ -85,7 +87,6 @@ done
 
 make_package_tapdata() {
   mkdir -p $OUTPUT_DIR/etc/init/ $OUTPUT_DIR/components/ $OUTPUT_DIR/lib/
-  mkdir -p $OUTPUT_DIR/bin/manager/ $OUTPUT_DIR/bin/iengine/
   cd $OUTPUT_DIR/
   cp $TAPDATA_DIR/manager/tm/target/classes/logback.xml etc/logback.xml
   cp $TAPDATA_DIR/manager/tm/target/classes/application.yml etc/application-tm.yml
@@ -93,12 +94,16 @@ make_package_tapdata() {
   cp $TAPDATA_DIR/manager/tm/target/tm-*-exec.jar components/tm.jar
   cp $TAPDATA_DIR/iengine/ie.jar components/tapdata-agent.jar
   cp $TAPDATA_DIR/tapdata-cli/target/pdk.jar lib/pdk-deploy.jar
-  # copy script files to start manager and iengine
-  cp $TAPDATA_DIR/manager/build/start.sh $OUTPUT_DIR/bin/manager/start.sh
-  cp $TAPDATA_DIR/iengine/build/start.sh $OUTPUT_DIR/bin/iengine/start.sh
 }
 
 make_package_connectors() {
+  # filter connectors
+  mv $CONNECTOR_DIR/connectors/dist $CONNECTOR_DIR/connectors/backup
+  mkdir -p $CONNECTOR_DIR/connectors/dist/
+  for item in $CONNECTORS_LIST; do
+    find $CONNECTOR_DIR/connectors/backup/ -type f -name "${item}" | xargs -I {} mv {} $CONNECTOR_DIR/connectors/dist/
+  done
+
   mkdir -p $OUTPUT_DIR/connectors/dist/
   cd $OUTPUT_DIR/
   tar cfz connectors/dist.tar.gz -C $CONNECTOR_DIR/connectors/ dist/
@@ -127,13 +132,19 @@ make_docker() {
   cd $OUTPUT_DIR/
   cp $TAPDATA_DIR/build/image/Dockerfile .
   cp $TAPDATA_DIR/build/image/docker-entrypoint.sh .
-  docker build -t harbor.internal.tapdata.io/tapdata/tapdata:$TAG_NAME .
+  cp -r $TAPDATA_DIR/build/image/bin .
+  cp -r $TAPDATA_DIR/build/image/supervisor .
+  # docker build -t harbor.internal.tapdata.io/tapdata/tapdata:$TAG_NAME .
+  docker buildx create --use --name multi-platform --platform linux/amd64,linux/arm64
+  docker buildx build --platform linux/arm64,linux/amd64 -t harbor.internal.tapdata.io/tapdata/tapdata:$TAG_NAME . --push
 }
 
 make_tar() {
   cd $OUTPUT_DIR/
   cp $TAPDATA_DIR/build/image/docker-entrypoint.sh ./start.sh
-  chmod +x start.sh
+  rsync -a $TAPDATA_DIR/build/image/bin/ ./
+  rsync -a $TAPDATA_DIR/build/image/supervisor ./
+  chmod +x start.sh stop.sh status.sh
   tar cfz tapdata-$TAG_NAME.tar.gz *
 }
 
diff --git a/build/image/Dockerfile b/build/image/Dockerfile
index eb97ee8eb1..8863cf6642 100644
--- a/build/image/Dockerfile
+++ b/build/image/Dockerfile
@@ -1,20 +1,19 @@
-FROM ghcr.io/tapdata/tapdata/tldp/runtime-ee:0.5
+FROM ghcr.io/tapdata/base:0.1
 
 ENV JAVA_TOOL_OPTIONS -Dfile.encoding=UTF8
 COPY etc/ /tapdata/apps/etc
 COPY connectors/dist.tar.gz /tapdata/apps/connectors/dist.tar.gz
 COPY lib/ /tapdata/apps/lib
 COPY components/ /tapdata/apps/components
-COPY docker-entrypoint.sh /tapdata/docker-entrypoint.sh
-COPY bin/ /tapdata/apps/bin
+COPY docker-entrypoint.sh /tapdata/apps/start.sh
+COPY bin/ /tapdata/apps
 
-# Add tini
-ENV TINI_VERSION v0.19.0
-ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
-RUN chmod +x /tini
+RUN apt install supervisor -y
+
+COPY supervisor/ /tapdata/apps/supervisor/
 
 ENV TAPDATA_WORK_DIR /tapdata/apps
 
 ENTRYPOINT ["/tini", "--"]
 
-CMD ["bash", "-c", "/tapdata/docker-entrypoint.sh"]
+CMD ["bash", "-c", "/tapdata/apps/start.sh"]
\ No newline at end of file
diff --git a/build/image/bin/start.sh b/build/image/bin/start.sh
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/build/image/bin/status.sh b/build/image/bin/status.sh
index e69de29bb2..a42d1cb2a4 100644
--- a/build/image/bin/status.sh
+++ b/build/image/bin/status.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+SCRIPT_BASE_DIR=$(dirname "$0")
+cd $SCRIPT_BASE_DIR
+
+print_message() {
+    # Print message with color and bold
+    local message="$1"
+    local color="$2"
+    local is_bold="$3"
+
+    local reset="\e[0m"
+    local color_code=""
+    local bold_code=""
+
+    # Set color code
+    case $color in
+    "red") color_code="\e[31m" ;;
+    "green") color_code="\e[32m" ;;
+    "yellow") color_code="\e[33m" ;;
+    "blue") color_code="\e[34m" ;;
+    "magenta") color_code="\e[35m" ;;
+    "cyan") color_code="\e[36m" ;;
+    esac
+
+    # Set bold code
+    if [[ $is_bold == true ]]; then
+    bold_code="\e[1m"
+    fi
+
+    # Print message with color and bold
+    echo -e "${bold_code}${color_code}${message}${reset}"
+}
+
+service_status() {
+    local service_name="$1"
+    if [[ -f .$service_name.pid ]]; then
+        ps -p $(cat .$service_name.pid) > /dev/null && print_message "$service_name running, pid $(cat .$service_name.pid)" "green" false
+        if [[ $? -ne 0 ]]; then
+            print_message "$service_name not running." "yellow" false
+        fi
+    else
+        echo "$service_name not running, or no .$service_name.pid file found."
+    fi
+}
+
+help() {
+    print_message "Usage: status.sh" "green" true
+    print_message "Check the status of Manager, IEngine and Mongodb services." "cyan" false
+}
+
+if [[ $1 == "help" || $1 == "--help" || $1 == "-h" ]]; then
+    help
+    exit 0
+fi
+
+if [[ `cat .launch_supervisor` == 'true' ]]; then
+    supervisorctl -c supervisor/supervisord.conf status
+else
+    service_status "manager"
+    service_status "iengine"
+    service_status "mongodb"
+fi
\ No newline at end of file
diff --git a/build/image/bin/stop.sh b/build/image/bin/stop.sh
index e69de29bb2..067a51bdd2 100644
--- a/build/image/bin/stop.sh
+++ b/build/image/bin/stop.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+SCRIPT_BASE_DIR=$(dirname "$0")
+cd $SCRIPT_BASE_DIR
+
+components=${1:-"all"}
+
+print_message() {
+    # Print message with color and bold
+    local message="$1"
+    local color="$2"
+    local is_bold="$3"
+
+    local reset="\e[0m"
+    local color_code=""
+    local bold_code=""
+
+    # Set color code
+    case $color in
+    "red") color_code="\e[31m" ;;
+    "green") color_code="\e[32m" ;;
+    "yellow") color_code="\e[33m" ;;
+    "blue") color_code="\e[34m" ;;
+    "magenta") color_code="\e[35m" ;;
+    "cyan") color_code="\e[36m" ;;
+    esac
+
+    # Set bold code
+    if [[ $is_bold == true ]]; then
+    bold_code="\e[1m"
+    fi
+
+    # Print message with color and bold
+    echo -e "${bold_code}${color_code}${message}${reset}"
+}
+
+help() {
+    print_message "Usage: stop.sh [all|manager|iengine|mongodb]" "green" true
+    print_message "Stop Manager and IEngine services." "cyan" false
+    print_message "  [all]: stop manager and service" "cyan" false
+    print_message "  manager: stop Manager service" "cyan" false
+    print_message "  iengine: stop IEngine service" "cyan" false
+    print_message "  mongodb: stop MongoDB service" "cyan" false
+}
+
+print_message "Stopping Manager and IEngine..." "green" true
+
+if [[ $components == "help" || $components == "--help" || $components == "-h" ]]; then
+    help
+    exit 0
+fi
+
+stop_service() {
+    local service_name="$1"
+    if [[ -f .$service_name.pid ]]; then
+        ps -p $(cat .$service_name.pid) > /dev/null && kill -9 $(cat .$service_name.pid)
+        print_message "$service_name stopped." "green" false
+        rm -rf .$service_name.pid
+    else
+        print_message "$service_name not running, or no .$service_name.pid file found." "yellow" false
+    fi
+}
+
+if [[ `cat .launch_supervisor` == "false" ]]; then
+
+    if [[ $components == "all" ]]; then
+        # stop manager
+        stop_service "manager"
+        # stop iengine
+        stop_service "iengine"
+    elif [[ $components == "manager" ]]; then
+        # stop manager
+        stop_service "manager"
+    elif [[ $components == "iengine" ]]; then
+        # stop iengine
+        stop_service "iengine"
+    elif [[ $components == "mongodb" ]]; then
+        # stop mongodb
+        stop_service "mongodb"
+    fi
+else
+    if [[ $components == "all" ]]; then
+        # stop manager and iengine
+        supervisorctl -c supervisor/supervisord.conf stop iengine
+        supervisorctl -c supervisor/supervisord.conf stop manager
+    elif [[ $components == "manager" ]]; then
+        # stop manager
+        supervisorctl -c supervisor/supervisord.conf stop manager
+    elif [[ $components == "iengine" ]]; then
+        # stop iengine
+        supervisorctl -c supervisor/supervisord.conf stop iengine
+    elif [[ $components == "supervisord" ]]; then
+        # stop supervisord
+        supervisorctl -c supervisor/supervisord.conf shutdown
+    elif [[ $components == "mongodb" ]]; then
+        # stop mongodb
+        supervisorctl -c supervisor/supervisord.conf stop mongodb
+    fi
+fi
diff --git a/build/image/docker-entrypoint.sh b/build/image/docker-entrypoint.sh
index 45d040f25a..7163fa865d 100755
--- a/build/image/docker-entrypoint.sh
+++ b/build/image/docker-entrypoint.sh
@@ -1,5 +1,7 @@
 #! /bin/bash
 
+SCRIPT_BASE_DIR=$(dirname "$0")
+
 print_message() {
   # Print message with color and bold
   local message="$1"
@@ -29,21 +31,148 @@ print_message() {
   echo -e "${bold_code}${color_code}${message}${reset}"
 }
 
+install_supervisor() {
+    print_message "Supervisor Not Found, it's better to install supervisor first" "yellow" false
+    # install by pip/pip3
+    print_message "Try to installing Supervisor by pip/pip3" "yellow" false
+    which pip > /dev/null
+    if [[ $? -ne 0 ]]; then
+        which pip3 > /dev/null
+        if [[ $? -ne 0 ]]; then
+            print_message "pip or pip3 Not Found" "yellow" false
+            export LAUNCH_SUPERVISOR=false
+        else
+            pip3 install supervisor
+        fi
+    else
+        pip install supervisor
+    fi
+    if [[ $? -eq 0 ]]; then
+        which supervisord > /dev/null
+        if [[ $? -eq 0 ]]; then
+            print_message "Install Supervisor Success" "green" false
+            export LAUNCH_SUPERVISOR=true
+            return 0
+        fi
+    fi
+    print_message "Install Supervisor by pip/pip3 Failed" "yellow" false
+    print_message "Try to install by system package manager" "yellow" false
+    # Linux or Macos
+    if [[ -f /etc/redhat-release ]]; then
+        yum install -y supervisor
+    elif [[ -f /etc/lsb-release ]]; then
+        apt-get install -y supervisor
+    elif [[ $(uname) == "Darwin" ]]; then
+        brew install supervisor
+    else
+        print_message "Not Support OS" "yellow" false
+        export LAUNCH_SUPERVISOR=false
+        return 1
+    fi
+    if [[ $? -ne 0 ]]; then
+        print_message "Install Supervisor Failed" "yellow" false
+        export LAUNCH_SUPERVISOR=false
+        return 1
+    else
+        which supervisord > /dev/null
+        if [[ $? -ne 0 ]]; then
+            print_message "Install Supervisor Failed" "yellow" false
+            export LAUNCH_SUPERVISOR=false
+            return 1
+        fi
+        export LAUNCH_SUPERVISOR=true
+        print_message "Install Supervisor Success" "green" false
+        return 0
+    fi
+}
+
+start_supervisord() {
+    # install supervisor first
+    which supervisord > /dev/null
+    if [[ $? -ne 0 ]]; then
+        install_supervisor
+    fi
+    if [[ $LAUNCH_SUPERVISOR == "false" ]]; then
+        print_message "Skip Start Supervisor" "yellow" false
+        echo 'false' > .launch_supervisor
+        return 0
+    else
+        echo 'true' > .launch_supervisor
+    fi
+
+    # if supervisord is already running, skip start
+    ps -ef | grep -v grep | grep supervisord > /dev/null
+    if [[ $? -eq 0 ]]; then
+        print_message "Supervisor is Already Running" "green" false
+        return 0
+    fi
+
+    # create paths
+    mkdir -p supervisor/logs/ supervisor/run/ supervisor/conf.d/
+    touch supervisor/logs/supervisord.log # create supervisord log file
+    touch supervisor/logs/manager.log supervisor/logs/manager.err # create manager log file
+    touch supervisor/logs/iengine.log supervisor/logs/iengine.err # create iengine log file
+    touch supervisor/logs/mongodb.log supervisor/logs/mongodb.err # create mongodb log file
+    # start supervisor
+    supervisord -c supervisor/supervisord.conf
+    # check supervisor start
+    ps -ef | grep -v grep | grep supervisord > /dev/null
+    if [[ $? -ne 0 ]]; then
+        export LAUNCH_SUPERVISOR=false
+        print_message "Supervisor Start Failed" "red" false
+        return 1
+    fi
+}
+
 get_env() {
+
     # Set default env
+    export MONGO_URI=${MONGO_URI:-"mongodb://127.0.0.1:27017/tapdata"}  # mongodb uri
+    export dbMem=${dbMem:-"1"} # db memory
+    export engineMem=${engineMem:-"1G"} # engine memory
+    export managerMem=${managerMem:-"1G"} # manager memory
+    export tm_port=${tm_port:-"3030"} # manager port
+    export ACCESS_CODE=${ACCESS_CODE:-"3324cfdf-7d3e-4792-bd32-571638d4562f"}  # access code
+    export LAUNCH_SUPERVISOR=${LAUNCH_SUPERVISOR:-"false"}  # launch supervisor
+    export REPORT_DATA_OSS=true # report data oss
+
+    # Export env
+    export MONGO_URI dbMem engineMem managerMem tm_port ACCESS_CODE LAUNCH_SUPERVISOR REPORT_DATA_OSS
 
-    MONGO_URI=${MONGO_URI}  # mongodb uri
-    ACCESS_CODE=${ACCESS_CODE:-"3324cfdf-7d3e-4792-bd32-571638d4562f"}  # access code
+cat <<EOF
+MONGO_URI         : $MONGO_URI
+dbMem             : $dbMem
+engineMem         : $engineMem
+managerMem        : $managerMem
+tm_port           : $tm_port
+ACCESS_CODE       : $ACCESS_CODE
+LAUNCH_SUPERVISOR : $LAUNCH_SUPERVISOR
+REPORT_DATA_OSS   : $REPORT_DATA_OSS
+EOF
 
-    print_message "MONGO_URI  :   $MONGO_URI" "blue" false
-    print_message "ACCESS_CODE:   $ACCESS_CODE" "blue" false
+    if [[ $LAUNCH_SUPERVISOR == "true" ]]; then
+        start_supervisord
+        if [[ $? -ne 0 ]]; then
+            echo 'false' > .launch_supervisor
+        else
+            echo 'true' > .launch_supervisor
+        fi
+    else
+        echo 'false' > .launch_supervisor
+    fi
 }
 
 start_mongo() {
     # start a mongodb replSet
 
     mkdir -p /tapdata/data/logs /tapdata/data/db/
-    mongod --dbpath=/tapdata/data/db/ --replSet=rs0 --wiredTigerCacheSizeGB=1 --bind_ip_all --logpath=/tapdata/data/logs/mongod.log --fork
+    if [[ $LAUNCH_SUPERVISOR == "true" ]]; then
+        supervisorctl -c supervisor/supervisord.conf start mongodb
+    else
+        mongod --dbpath=/tapdata/data/db/ --wiredTigerCacheSizeGB=$dbMem --replSet=rs0 --bind_ip_all --logpath=/tapdata/data/logs/mongod.log --fork
+        sleep 2
+        pgrep mongod > .mongodb.pid
+    fi
     while [[ 1 ]]; do
         mongo --quiet --eval "db" &> /dev/null
         if [[ $? -eq 0 ]]; then
@@ -76,7 +205,7 @@ wait_tm_start() {
         local seconds_left=$((timeout - SECONDS))
         printf "\r* Wait Starting, Left %02d / 300 Seconds..." "$seconds_left"
         sleep 1
-        curl --fail "http://localhost:3000" &> /dev/null
+        curl --fail "http://localhost:$tm_port" &> /dev/null
         if [[ $? -ne 0 ]]; then
             continue
         else
@@ -106,7 +235,7 @@ exec_with_log() {
 
 _register_connectors() {
     print_message "* Register Connector: $i" "blue" false
-    java -jar $dir/lib/pdk-deploy.jar register -a $ACCESS_CODE -f GA -t http://localhost:3000 $dir/connectors/dist 2>&1
+    java -jar $dir/lib/pdk-deploy.jar register -a $ACCESS_CODE -t http://localhost:$tm_port $dir/connectors/dist 2>&1
     if [[ $? -ne 0 ]]; then
         print_message "* Register Connector: $i Failed" "red" false
         exit 1
@@ -124,37 +253,54 @@ register_connectors() {
     _register_connectors
 }
 
+start_tm() {
+    if [[ $LAUNCH_SUPERVISOR == "true" ]]; then
+        supervisorctl -c supervisor/supervisord.conf start manager
+    else
+        mkdir -p logs/ && touch logs/nohup.out
+        nohup java -Xmx$managerMem -jar -Dserver.port=$tm_port -server components/tm.jar --spring.config.additional-location=file:etc/ --logging.config=file:etc/logback.xml --spring.data.mongodb.default.uri=$MONGO_URI --spring.data.mongodb.obs.uri=$MONGO_URI --spring.data.mongodb.log.uri=$MONGO_URI &> logs/nohup.out &
+        echo $! > .manager.pid
+    fi
+}
+
+start_iengine() {
+    if [[ $LAUNCH_SUPERVISOR == "true" ]]; then
+        supervisorctl -c supervisor/supervisord.conf start iengine
+    else
+        mkdir -p logs/iengine/ && touch logs/iengine/tapdata-agent.jar.log
+        export app_type="DAAS"
+        export backend_url="http://127.0.0.1:$tm_port/api/"
+        export TAPDATA_MONGO_URI=$MONGO_URI
+        nohup java -Xmx$engineMem -jar components/tapdata-agent.jar &> logs/iengine/tapdata-agent.jar.log &
+        echo $! > .iengine.pid
+    fi
+}
+
 start_server() {
     # 1. start manager server
     # 2. register all connectors
     # 3. start iengine server
     #
-    # 1. start manager server
-    if [[ -d /tapdata/apps/manager ]]; then
-      mkdir -p /tapdata/apps/logs /tapdata/apps/logs/iengine
-      exec_with_log "cd /tapdata/apps/ && bash bin/manager/start.sh $MONGO_URI" "Start Manager Server" "blue" || return 1
-    else
-      mkdir -p ./logs ./logs/iengine
-      exec_with_log "bash bin/manager/start.sh $MONGO_URI" "Start Manager Server" "blue" || return 1
+    if [[ $LAUNCH_SUPERVISOR == "true" ]]; then
+        supervisorctl -c supervisor/supervisord.conf reread && supervisorctl -c supervisor/supervisord.conf update
     fi
+    # 1. start manager server
+    print_message "Start Manager Server" "blue" false
+    start_tm
     # 2. register all connectors
     # waiting for manager server start
     exec_with_log wait_tm_start "Waiting for Manager Server Start" "blue" || return 1
     # Register all connectors
     exec_with_log register_connectors "Register all connectors" "blue" || return 1
     # 3. start iengine server
-    if [[ -d /tapdata/apps/iengine ]]; then
-      exec_with_log "cd /tapdata/apps/ && bash bin/iengine/start.sh" "Start Iengine Server" "blue" || return 1
-    else
-      exec_with_log "bash bin/iengine/start.sh" "Start Iengine Server" "blue" || return 1
-    fi
+    start_iengine
 }
 
 unzip_files() {
-    if [[ -d /tapdata/apps/connectors/ ]]; then
+    if [[ -f /tapdata/apps/connectors/dist.tar.gz ]]; then
       tar xzf /tapdata/apps/connectors/dist.tar.gz -C /tapdata/apps/connectors
       rm -rf /tapdata/apps/connectors/dist.tar.gz
-    elif [[ -d ./connectors ]]; then
+    elif [[ -f ./connectors/dist.tar.gz ]]; then
       tar xzf ./connectors/dist.tar.gz -C connectors
       rm -rf ./connectors/dist.tar.gz
     fi
@@ -170,6 +316,7 @@ cat << "EOF"
 EOF
 
 _main() {
+    cd $SCRIPT_BASE_DIR
     # 1. get env settings
     # 2. unzip connectors
     # 3. start mongo if $MONGO_URI is not set
@@ -184,18 +331,18 @@ _main() {
     print_message ">>> Unzip Connectors [START]" "green" true
     unzip_files
     print_message "<<< Unzip Connectors [SUCCESS]" "green" true
-    # 3. start mongo if $MONGO_URI is not set
-    print_message ">>> Start Mongo [START]" "green" true
-    if [[ -z $MONGO_URI ]]; then
+    # 3. start mongo if $MONGO_URI is not set and in docker
+    ps -ef | grep -v grep | grep mongo > /dev/null
+    if [[ -f /.dockerenv && $? -ne 0 ]]; then
+        print_message ">>> Start Mongo [START]" "green" true
         start_mongo
         ps -ef | grep -v grep | grep mongo > /dev/null
         if [[ $? -ne 0 ]]; then
             print_message "<<< Mongodb is Not Running" "red" true
             exit 1
         fi
-        MONGO_URI="mongodb://127.0.0.1:27017/tapdata"
+        print_message "<<< Mongodb is Already Running" "green" true
     fi
-    print_message "<<< Mongodb is Already Running" "green" true
     # 4. start tm server, register connectors and start iengine
     print_message ">>> Start Server [START]" "green" true
     start_server
@@ -205,10 +352,12 @@ _main() {
     fi
     print_message "<<< Start Server [SUCCESS]" "green" true
     # Print Visit Url
-    print_message "All Done, Please Visit http://localhost:3000" "green" true
+    print_message "All Done, Please Visit http://localhost:$tm_port" "green" true
 
     # 5. hold the container
-    sleep infinity
+    if [[ -f /.dockerenv ]]; then
+        sleep infinity
+    fi
 }
 
 _main "$@"
diff --git a/build/image/supervisor/conf.d/iengine.conf b/build/image/supervisor/conf.d/iengine.conf
new file mode 100644
index 0000000000..030a42438c
--- /dev/null
+++ b/build/image/supervisor/conf.d/iengine.conf
@@ -0,0 +1,12 @@
+[program:iengine]
+command = java -Xmx%(ENV_engineMem)s -jar components/tapdata-agent.jar
+environment = app_type="DAAS", tm_port=%(ENV_tm_port)s, backend_url="http://127.0.0.1:%(ENV_tm_port)s/api/", MONGO_URI='%(ENV_MONGO_URI)s', TAPDATA_MONGO_URI='%(ENV_MONGO_URI)s', engineMem=%(ENV_engineMem)s
+directory = %(here)s/../../
+autostart = false
+autoresart = false
+stdout_logfile_maxbytes = 50MB
+stdout_logfile_backups = 10
+stdout_logfile = %(here)s/../logs/iengine.log
+stderr_logfile = %(here)s/../logs/iengine.err
+stderr_logfile_maxbytes = 50MB
+stderr_logfile_backups = 10
diff --git a/build/image/supervisor/conf.d/manager.conf b/build/image/supervisor/conf.d/manager.conf
new file mode 100644
index 0000000000..2f9944b1ab
--- /dev/null
+++ b/build/image/supervisor/conf.d/manager.conf
@@ -0,0 +1,11 @@
+[program:manager]
+command = java -Xmx%(ENV_managerMem)s -jar -Dserver.port=%(ENV_tm_port)s -server components/tm.jar --spring.config.additional-location=file:etc/ --logging.config=file:etc/logback.xml --spring.data.mongodb.default.uri=%(ENV_MONGO_URI)s --spring.data.mongodb.obs.uri=%(ENV_MONGO_URI)s --spring.data.mongodb.log.uri=%(ENV_MONGO_URI)s
+directory = %(here)s/../../
+autostart = false
+autoresart = false
+stdout_logfile_maxbytes = 50MB
+stdout_logfile_backups = 10
+stdout_logfile = %(here)s/../logs/manager.log
+stderr_logfile = %(here)s/../logs/manager.err
+stderr_logfile_maxbytes = 50MB
+stderr_logfile_backups = 10
diff --git a/build/image/supervisor/conf.d/mongodb.conf b/build/image/supervisor/conf.d/mongodb.conf
new file mode 100644
index 0000000000..2d42214791
--- /dev/null
+++ b/build/image/supervisor/conf.d/mongodb.conf
@@ -0,0 +1,10 @@
+[program:mongodb]
+command = mongod --dbpath=/tapdata/data/db/ --replSet=rs0 --wiredTigerCacheSizeGB=%(ENV_dbMem)s --bind_ip_all --logpath=/tapdata/data/logs/mongod.log
+autostart = false
+autoresart = false
+stdout_logfile_maxbytes = 50MB
+stdout_logfile_backups = 10
+stdout_logfile = %(here)s/../logs/mongodb.log
+stderr_logfile = %(here)s/../logs/mongodb.err
+stderr_logfile_maxbytes = 50MB
+stderr_logfile_backups = 10
diff --git a/build/image/supervisor/supervisord.conf b/build/image/supervisor/supervisord.conf
new file mode 100644
index 0000000000..f735b639bb
--- /dev/null
+++ b/build/image/supervisor/supervisord.conf
@@ -0,0 +1,19 @@
+[unix_http_server]
+file=%(here)s/run/supervisor.sock
+chmod=0700
+
+[supervisord]
+logfile=%(here)s/logs/supervisord.log
+pidfile=%(here)s/run/supervisord.pid
+logfile_maxbytes=50MB
+logfile_backups=10
+loglevel=info
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
+
+[supervisorctl]
+serverurl=unix://%(here)s/run/supervisor.sock
+
+[include]
+files = %(here)s/conf.d/*.conf
diff --git a/building.md b/building.md
new file mode 100644
index 0000000000..6560bacbec
--- /dev/null
+++ b/building.md
@@ -0,0 +1,144 @@
+# Building Guide
+Welcome to the project build guide. This guide will help you compile and run the project successfully on your local machine.
+
+## Prerequisites
+Before you begin, ensure that your system has the following software and tools installed:
+1. git
+2. node v16.14.2 or v16 version higher
+3. jdk 1.8, or 11, and mvn installed
+4. bash
+5. network is available
+
+building is only tested in linux and mac, if you want to build tapdata in other system, please try it, thanks to report bug to us if you find some problems
+
+### Building Guidelines
+build all libs and package from source using command:
+
+```
+p=`pwd`
+rm -rf ~/.m2/repository/com/tapdata/
+rm -rf ~/.m2/repository/io/tapdata/
+mkdir -p $p/build
+cd $p/build
+git clone git@github.com:tapdata/tapdata-web.git
+git clone git@github.com:tapdata/tapdata.git
+git clone git@github.com:tapdata/tapdata-connectors.git
+git clone git@github.com:tapdata/tapdata-common-lib.git
+
+rm -rf $p/build/tapdata/iengine/ie.jar
+rm -rf $p/build/tapdata/manager/tm/target/tm-0.0.1-SNAPSHOT-exec.jar
+rm -rf $p/build/tapdata-web/dist
+
+cd tapdata-common-lib
+git fetch
+git checkout -f
+git checkout main
+git rebase
+git pull --rebase
+echo "building tapdata-common-lib..."
+mvn clean install -DskipTests
+if [[ $? -ne 0 ]]; then
+    echo "build tapdata-common-lib failed, please check logs to get cause"
+    exit 1
+fi
+
+cd $p/build
+cd tapdata-web
+git fetch
+git checkout -f
+git checkout develop
+git rebase
+git pull --rebase
+echo "building frontend..."
+bash build/build.sh
+if [[ $? -ne 0 ]]; then
+    echo "build frontend failed, please check logs to get cause"
+    exit 1
+fi
+
+cd $p/build
+cd tapdata
+git fetch
+git checkout -f
+git checkout develop
+git rebase
+git pull --rebase
+echo "building engine..."
+mvn clean install -DskipTests -U
+if [[ $? -ne 0 ]]; then
+    echo "build engine failed, please check logs to get cause"
+    exit 1
+fi
+
+cd $p/build
+cd tapdata-connectors
+git fetch
+git checkout -f
+git checkout develop
+git rebase
+git pull --rebase
+echo "building connectors..."
+mvn clean install -DskipTests -U
+if [[ $? -ne 0 ]]; then
+    echo "build connectors failed, please check logs to get cause"
+    exit 1
+fi
+```
+
+If everything is ok, you will get:
+1. engine jar: build/tapdata/iengine/ie.jar
+2. manager jar: build/tapdata/manager/tm/target/tm-0.0.1-SNAPSHOT-exec.jar
+3. webui: build/tapdata-web/dist
+
+### Start Guidelines
+
+If you want to start the complete Tapdata service on your machine, please follow these steps:
+### Instructions for Starting the Complete Tapdata Service on Your Machine
+
+1. **Deploy a MongoDB Replica Set and Record the Access URI**:
+    - Set up a MongoDB replica set and make a note of the URI that you will use to access it.
+
+2. **Create a `webroot` Directory and Copy WebUI Files**:
+    - In the directory where the management JAR file is located, create a `webroot` directory.
+    - Copy all files from the `dist` directory of the WebUI into the `webroot` directory.
+
+3. **Set Environment Variables**:
+    - Set some environment variable. This URI is your MongoDB replica set connection string:
+    
+        ```sh
+        export mongodb_uri='mongodb://127.0.0.1:27017/tapdata'
+        export TAPDATA_MONGO_URI=$mongodb_uri
+        export app_type='DAAS'
+        export backend_url='http://127.0.0.1:3030/api/'
+        export TAPDATA_WORK_DIR=<path-to-your-management-directory>
+        ```
+
+4. **Start the Management Service**:
+    - Run the following command to start the management service:
+
+        ```sh
+        java -jar -Dserver.port=3030 -server -Xmx2G tm.jar -Dspring.data.mongodb.default.uri=${mongodb_uri} -Dspring.data.mongodb.log.uri=${mongodb_uri} -Dspring.data.mongodb.obs.uri=${mongodb_uri}
+        ```
+
+5. **Start the Engine**:
+    - Run the following command to start the engine:
+
+        ```sh
+        java -Xmx2G -jar components/ie.jar
+        ```
+
+### HA Deployment
+The open-source version supports all high-availability features of the enterprise version, but it does not support visual multi-node management. You can follow the steps below to deploy a high-availability cluster.
+
+#### Meta Database
+Tapdata uses MongoDB as the metadata repository. You can deploy a MongoDB replica set with more than three nodes to provide high availability for the metadata database.
+
+#### Manager
+Tapdata Manager is a stateless node. If you need high availability for this component, simply start multiple instances on different machines.
+
+Please note that if you deploy multiple Manager nodes, when starting the engine, you need to add the addresses of all Manager nodes to `backend_url`, separating multiple addresses with commas.
+
+#### Engine
+You only need to deploy multiple engines on different machines. In the event of an engine failure, tasks will automatically transfer between different engines. You may observe that some tasks have no progress for a period, but they will automatically resume. The default high availability switch time is 10 minutes, which we understand to be an appropriate value. This ensures that tasks can switch in a timely manner when a failure occurs and avoids unnecessary task migration due to network fluctuations.
+
+By following these steps, you can start the complete Tapdata service on your machine.
diff --git a/contributing.md b/contributing.md
new file mode 100644
index 0000000000..35baf67f29
--- /dev/null
+++ b/contributing.md
@@ -0,0 +1,36 @@
+# Contributing Guide
+First off, thanks for taking the time to contribute! 🎉
+
+The following is a set of guidelines for contributing to tapdata. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
+
+## How to Contribute
+To successfully run this open-source project, there are three key repositories you need to focus on:
+
+1. **Main Project Repository**: 
+   - URL: [https://github.com/tapdata/tapdata](https://github.com/tapdata/tapdata)
+   - This repository contains the manager backend service, and engine service which run real tasks
+
+2. **Frontend Repository**: 
+   - URL: [https://github.com/tapdata/tapdata-web](https://github.com/tapdata/tapdata-web)
+   - This repository contains the code for the frontend application of the project, including all UI components.
+
+3. **Connectors Repository**: 
+   - URL: [https://github.com/tapdata/tapdata-connectors](https://github.com/tapdata/tapdata-connectors)
+   - This repository contains all connectors supported by tapdata service
+
+### Guidelines
+1. If you have any ideas, create an issue in the corresponding repository. This issue can be a bug report or a feature suggestion.
+2. Fork the corresponding project to your own space.
+3. Create a branch in your project space. The branch name should start with the issue number from the first step, such as `25-fix-xxx`.
+4. Complete your code submission and self-test to ensure the changes achieve the desired modification.
+5. Submit a PR to the `develop` branch of the original project.
+6. In the PR review, please describe through comments how the modification should be validated.
+7. Wait for the PR review.
+
+During the PR review, we will follow these rules:
+1. First, we will perform a static code check to ensure the code changes are logically sound.
+2. Second, we will compile the entire project using your modified code and conduct basic regression testing, which includes about 100 test cases, to ensure that no fundamental quality issues are introduced.
+3. Third, we will add a test case for the part of the modification that needs to be verified and test its stable changes before and after the code modification.
+4. Finally, we will merge your changes.
+
+The time required for code merging depends on the daily work schedule of the development team, and we cannot guarantee a specific timeframe. If you have an urgent need for the PR to be merged, please feel free to remind us in the group.
diff --git a/iengine/build/start.sh b/iengine/build/start.sh
index a40fc82822..6cc4259f7c 100644
--- a/iengine/build/start.sh
+++ b/iengine/build/start.sh
@@ -2,10 +2,9 @@ ulimit -c unlimited
 
 sbin_file="tapdata-agent.jar"
 export app_type=DAAS
-export backend_url=http://127.0.0.1:3000/api/
-if [[ -z $MONGO_URI ]]; then
-  MONGO_URI="mongodb://127.0.0.1:27017/tapdata"
-fi
+export tm_port=${tm_port:-3030}
+export backend_url=http://127.0.0.1:$tm_port/api/
+export MONGO_URI=${MONGO_URI:-"mongodb://127.0.0.1:27017/tapdata"}
 export TAPDATA_MONGO_URI=$MONGO_URI
 
 mkdir -p logs/iengine && touch logs/iengine/$sbin_file.log
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNode.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNode.java
index a08c575ff0..10fbc1382c 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNode.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNode.java
@@ -59,6 +59,7 @@
 import io.tapdata.flow.engine.V2.util.SyncTypeEnum;
 import io.tapdata.flow.engine.V2.util.TapCodecUtil;
 import io.tapdata.flow.engine.V2.util.TapEventUtil;
+import io.tapdata.flow.engine.util.TaskDtoUtil;
 import io.tapdata.observable.logging.ObsLogger;
 import io.tapdata.observable.logging.ObsLoggerFactory;
 import io.tapdata.pdk.core.utils.CommonUtils;
@@ -564,6 +565,9 @@ public synchronized TapCodeException errorHandle(Throwable throwable) {
 	}
 
 	public synchronized TapCodeException errorHandle(Throwable throwable, String errorMessage) {
+		if (null == clientMongoOperator) {
+			clientMongoOperator = initClientMongoOperator();
+		}
 		TapCodeException currentEx = wrapTapCodeException(throwable);
 		TaskDto taskDto = processorBaseContext.getTaskDto();
 		handleWhenTestRun(taskDto, currentEx);
@@ -578,13 +582,21 @@ public synchronized TapCodeException errorHandle(Throwable throwable, String err
 				message = currentEx.getMessage();
 			}
 			String finalMessage = StringUtils.isBlank(message) ? errorMessage:message;
-			errorEvent.set(new ErrorEvent(finalMessage,currentEx.getCode(),Log4jUtil.getStackString(currentEx)));
+			ErrorEvent event = new ErrorEvent(finalMessage, currentEx.getCode(), Log4jUtil.getStackString(currentEx));
+			errorEvent.set(event);
 			if(whetherToSkip(taskDto.getErrorEvents(), errorEvent.get(), skipError)){
-				obsLogger.info("The current exception match the skip exception strategy, message: "+errorMessage);
+				obsLogger.info("Exception skipping - The current exception match the skip exception strategy, message: {}", finalMessage);
 				isSkip.set(true);
+			} else {
+				obsLogger.info("Exception skipping - The current exception does not match the skip exception strategy, message: {}", finalMessage);
 			}
 		},err->obsLogger.warn("Skip error failed:{}",err.getMessage()));
-		if(isSkip.get())return null;
+		if (isSkip.get()) {
+			List<ErrorEvent> errorEvents = taskDto.getErrorEvents();
+			errorEvents.remove(errorEvent.get());
+			TaskDtoUtil.updateErrorEvent(clientMongoOperator, taskDto.getErrorEvents(), taskDto.getId(), obsLogger, "Update task'error events failed: {}");
+			return null;
+		}
 		try {
 			if (globalErrorIsNull()) {
 				this.error = currentEx;
@@ -858,11 +870,7 @@ protected void saveErrorEvent(List<ErrorEvent> errorEvents,ErrorEvent event,Obje
 		if (errorEvents.size() > ERROR_EVENT_LIMIT) {
 			return;
 		}
-		try {
-			clientMongoOperator.insertOne(errorEvents,ConnectorConstant.TASK_COLLECTION + "/errorEvents/" + taskId);
-		}catch (Exception e){
-			obsLogger.warn("Save error event failed:{}",e.getMessage());
-		}
+		TaskDtoUtil.updateErrorEvent(clientMongoOperator, errorEvents, taskId, obsLogger, "Save error event failed: {}");
 	}
 
 	public static class TapValueTransform {
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNode.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNode.java
index 170c8d22e0..2cfad010a3 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNode.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNode.java
@@ -69,6 +69,8 @@
 public abstract class HazelcastPdkBaseNode extends HazelcastDataBaseNode {
 	public static final int DEFAULT_READ_BATCH_SIZE = 2000;
 	public static final int DEFAULT_INCREASE_BATCH_SIZE = 1;
+	public static final String OLD_VERSION_TIMEZONE = "oldVersionTimezone";
+	public static final String OLD_VERSION_TIME_ZONE_PROP_KEY = "OLD_VERSION_TIME_ZONE";
 	private final Logger logger = LogManager.getLogger(HazelcastPdkBaseNode.class);
 	private static final String TAG = HazelcastPdkBaseNode.class.getSimpleName();
 	protected static final String COMPLETED_INITIAL_SYNC_KEY_PREFIX = "COMPLETED-INITIAL-SYNC-";
@@ -79,6 +81,8 @@ public abstract class HazelcastPdkBaseNode extends HazelcastDataBaseNode {
 
 	protected Integer readBatchSize;
 	protected Integer increaseReadSize;
+
+	public static final String FUNCTION_RETRY_STATUS = "functionRetryStatus";
 	private static final String DOUBLE_ACTIVE = "doubleActive";
 	protected TapRecordSkipDetector skipDetector;
 	private PdkStateMap pdkStateMap;
@@ -150,16 +154,20 @@ public PDKMethodInvoker createPdkMethodInvoker() {
 	public void signFunctionRetry(String taskId) {
 		CommonUtils.ignoreAnyError(() -> {
 			Update update = new Update();
-			update.set("functionRetryStatus", TaskDto.RETRY_STATUS_RUNNING);
+			update.set(FUNCTION_RETRY_STATUS, TaskDto.RETRY_STATUS_RUNNING);
 			update.set("taskRetryStartTime", System.currentTimeMillis());
-			clientMongoOperator.update(Query.query(Criteria.where("_id").is(new ObjectId(taskId))), update, ConnectorConstant.TASK_COLLECTION);
+			Criteria functionRetryStatusExists = Criteria.where(FUNCTION_RETRY_STATUS).exists(false);
+			Criteria functionRetryStatusNone = Criteria.where(FUNCTION_RETRY_STATUS).is(TaskDto.RETRY_STATUS_NONE);
+			Query query = Query.query(Criteria.where("_id").is(new ObjectId(taskId))
+					.orOperator(functionRetryStatusExists, functionRetryStatusNone));
+			clientMongoOperator.update(query, update, ConnectorConstant.TASK_COLLECTION);
 		}, "Faild to sign function retry status");
 	}
 
 	public void cleanFuctionRetry(String taskId) {
 		CommonUtils.ignoreAnyError(() -> {
 			Update update = new Update();
-			update.set("functionRetryStatus", TaskDto.RETRY_STATUS_NONE);
+			update.set(FUNCTION_RETRY_STATUS, TaskDto.RETRY_STATUS_NONE);
 			update.set("taskRetryStartTime", 0);
 			clientMongoOperator.update(Query.query(Criteria.where("_id").is(new ObjectId(taskId))), update, ConnectorConstant.TASK_COLLECTION);
 		}, "Faild to clean function retry status");
@@ -189,7 +197,7 @@ protected void createPdkConnectorNode(DataProcessorContext dataProcessorContext,
 		Node<?> node = dataProcessorContext.getNode();
 		ConnectorCapabilities connectorCapabilities = ConnectorCapabilities.create();
 		initDmlPolicy(node, connectorCapabilities);
-		Map<String, Object> nodeConfig = generateNodeConfig(node, taskDto.getDoubleActive());
+		Map<String, Object> nodeConfig = generateNodeConfig(node, taskDto);
 		this.associateId = ConnectorNodeService.getInstance().putConnectorNode(
 				PdkUtil.createNode(taskDto.getId().toHexString(),
 						databaseType,
@@ -210,7 +218,7 @@ protected void createPdkConnectorNode(DataProcessorContext dataProcessorContext,
 		AspectUtils.executeAspect(PDKNodeInitAspect.class, () -> new PDKNodeInitAspect().dataProcessorContext((DataProcessorContext) processorBaseContext));
 	}
 
-	protected Map<String, Object> generateNodeConfig(Node<?> node, Boolean doubleActive) {
+	protected Map<String, Object> generateNodeConfig(Node<?> node, TaskDto taskDto) {
 		Map<String, Object> nodeConfig = null;
 		if (node instanceof TableNode) {
 			nodeConfig = ((TableNode) node).getNodeConfig();
@@ -222,7 +230,10 @@ protected Map<String, Object> generateNodeConfig(Node<?> node, Boolean doubleAct
 		if (null == nodeConfig) {
 			nodeConfig = new HashMap<>();
 		}
-		nodeConfig.put(DOUBLE_ACTIVE, doubleActive);
+		nodeConfig.put(DOUBLE_ACTIVE, taskDto.getDoubleActive());
+		Boolean oldVersionTimezone = taskDto.getOldVersionTimezone();
+		oldVersionTimezone = CommonUtils.getPropertyBool(OLD_VERSION_TIME_ZONE_PROP_KEY, oldVersionTimezone);
+		nodeConfig.put(OLD_VERSION_TIMEZONE, oldVersionTimezone);
 		return nodeConfig;
 	}
 
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkBaseNode.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkBaseNode.java
index a6c5a42ab9..886d6d6588 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkBaseNode.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkBaseNode.java
@@ -1257,6 +1257,7 @@ protected Long doBatchCountFunction(BatchCountFunction batchCountFunction, TapTa
 
 	protected AutoCloseable doAsyncTableCount(BatchCountFunction batchCountFunction, String tableName) {
 		if (null == batchCountFunction) return () -> {
+			obsLogger.warn("Node can not support batchCountFunction");
 		};
 
 		CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNode.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNode.java
index 2b7d4b3aa8..2672a1afda 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNode.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNode.java
@@ -819,6 +819,7 @@ protected StreamReadConsumer generateStreamReadConsumer(ConnectorNode connectorN
 				if (streamReadFuncAspect != null)
 					executeAspect(streamReadFuncAspect.state(StreamReadFuncAspect.STATE_STREAM_STARTED).streamStartedTime(System.currentTimeMillis()));
 				sendCdcStartedEvent();
+				PDKInvocationMonitor.invokerRetrySetter(pdkMethodInvoker);
 				obsLogger.info("Connector {} incremental start succeed, tables: {}, data change syncing", connectorNode.getTapNodeInfo().getTapNodeSpecification().getName(), streamReadFuncAspect != null ? streamReadFuncAspect.getTables() : null);
 			}
 		});
@@ -1293,4 +1294,46 @@ protected void excludeRemoveTable(List<String> tableNames) {
 		tableNames.removeAll(removeTables);
 	}
 
+	@Override
+	protected Long doBatchCountFunction(BatchCountFunction batchCountFunction, TapTable table) {
+		AtomicReference<Long> counts = new AtomicReference<>();
+
+		executeDataFuncAspect(
+				TableCountFuncAspect.class,
+				() -> new TableCountFuncAspect().dataProcessorContext(getDataProcessorContext()).start(),
+				tableCountFuncAspect -> PDKInvocationMonitor.invoke(getConnectorNode(), PDKMethod.SOURCE_BATCH_COUNT, createPdkMethodInvoker().runnable(() -> {
+					try {
+						Node<?> node = getNode();
+						if (node instanceof TableNode) {
+							TableNode tableNode = (TableNode) dataProcessorContext.getNode();
+							if (isTableFilter(tableNode) || isPollingCDC(tableNode)) {
+								ConnectorNode connectorNode = getConnectorNode();
+								ConnectorFunctions connectorFunctions = connectorNode.getConnectorFunctions();
+								CountByPartitionFilterFunction countByPartitionFilterFunction = connectorFunctions.getCountByPartitionFilterFunction();
+								if (countByPartitionFilterFunction == null) {
+									counts.set(batchCountFunction.count(getConnectorNode().getConnectorContext(), table));
+								}else {
+									TapAdvanceFilter tapAdvanceFilter = batchFilterRead();
+									counts.set(countByPartitionFilterFunction.countByPartitionFilter(connectorNode.getConnectorContext(), table, tapAdvanceFilter));
+								}
+							} else {
+								counts.set(batchCountFunction.count(getConnectorNode().getConnectorContext(), table));
+							}
+						} else {
+							counts.set(batchCountFunction.count(getConnectorNode().getConnectorContext(), table));
+						}
+
+
+						if (null != tableCountFuncAspect) {
+							AspectUtils.accept(tableCountFuncAspect.state(TableCountFuncAspect.STATE_COUNTING).getTableCountConsumerList(), table.getName(), counts.get());
+						}
+					} catch (Throwable e) {
+						throw new NodeException("Query table '" + table.getName() + "'  count failed: " + e.getMessage(), e)
+								.context(getProcessorBaseContext());
+					}
+				}))
+		);
+		return counts.get();
+	}
+
 }
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNode.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNode.java
index 7e254bd3fe..a1b63481c0 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNode.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNode.java
@@ -1,77 +1,64 @@
 package io.tapdata.flow.engine.V2.node.hazelcast.processor;
 
-import com.google.common.collect.Maps;
 import com.tapdata.entity.TapdataEvent;
 import com.tapdata.entity.task.context.ProcessorBaseContext;
 import com.tapdata.tm.commons.dag.process.TableRenameProcessNode;
 import com.tapdata.tm.commons.dag.vo.TableRenameTableInfo;
 import io.tapdata.entity.event.TapBaseEvent;
 import io.tapdata.entity.event.TapEvent;
-import io.tapdata.entity.event.ddl.table.TapCreateTableEvent;
 import io.tapdata.entity.event.ddl.table.TapDropTableEvent;
+import io.tapdata.entity.event.ddl.table.TapRenameTableEvent;
 import io.tapdata.flow.engine.V2.util.TapEventUtil;
-import org.apache.commons.lang3.StringUtils;
 
-import java.util.LinkedHashSet;
 import java.util.Map;
-import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.BiConsumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
 public class HazelcastRenameTableProcessorNode extends HazelcastProcessorBaseNode {
 
-	/**
-	 * key: 源表名
-	 */
-	private Map<String, TableRenameTableInfo> tableNameMappingMap;
+    /**
+     * key: 源表名
+     */
+    private Map<String, TableRenameTableInfo> tableNameMappingMap;
+    private final Map<String, String> innerTableCacheMap = new ConcurrentHashMap<>(); // Used to speed up the name change operation
 
-	public HazelcastRenameTableProcessorNode(ProcessorBaseContext processorBaseContext) {
-		super(processorBaseContext);
-		initTableNameMapping();
-	}
+    public HazelcastRenameTableProcessorNode(ProcessorBaseContext processorBaseContext) {
+        super(processorBaseContext);
+        initTableNameMapping();
+    }
 
-	private void initTableNameMapping() {
-		TableRenameProcessNode tableRenameProcessNode = (TableRenameProcessNode) getNode();
-		LinkedHashSet<TableRenameTableInfo> tableNames = tableRenameProcessNode.getTableNames();
+    private void initTableNameMapping() {
+        TableRenameProcessNode tableRenameProcessNode = (TableRenameProcessNode) getNode();
+        this.tableNameMappingMap = tableRenameProcessNode.previousMap();
+    }
 
-		if (Objects.isNull(tableNames) || tableNames.isEmpty()) {
-			this.tableNameMappingMap = Maps.newLinkedHashMap();
-		}
+    @Override
+    protected void tryProcess(TapdataEvent tapdataEvent, BiConsumer<TapdataEvent, ProcessResult> consumer) {
+        TapEvent tapEvent = tapdataEvent.getTapEvent();
 
-		this.tableNameMappingMap = tableNames.stream()
-				.collect(Collectors.toMap(TableRenameTableInfo::getPreviousTableName, Function.identity()));
-	}
+        if (tapEvent instanceof TapBaseEvent) {
+            String newTableName = getTgtTableNameFromTapEvent(tapEvent);
+            ((TapBaseEvent) tapEvent).setTableId(newTableName);
+        }
+        consumer.accept(tapdataEvent, getProcessResult(TapEventUtil.getTableId(tapEvent)));
+    }
 
-	@Override
-	protected void tryProcess(TapdataEvent tapdataEvent, BiConsumer<TapdataEvent, ProcessResult> consumer) {
-		TapEvent tapEvent = tapdataEvent.getTapEvent();
-		String tableId = TapEventUtil.getTableId(tapEvent);
-
-		if (tapEvent instanceof TapBaseEvent) {
-			TableRenameTableInfo tableRenameTableInfo = tableNameMappingMap.get(tableId);
-			if (tableRenameTableInfo != null && StringUtils.isNotEmpty(tableRenameTableInfo.getCurrentTableName())) {
-				((TapBaseEvent) tapEvent).setTableId(tableRenameTableInfo.getCurrentTableName());
-			}
-		}
-		consumer.accept(tapdataEvent, getProcessResult(TapEventUtil.getTableId(tapEvent)));
-	}
-
-	@Override
-	public String getTgtTableNameFromTapEvent(TapEvent tapEvent) {
-		String tableId = TapEventUtil.getTableId(tapEvent);
-		TableRenameTableInfo tableRenameTableInfo = tableNameMappingMap.get(tableId);
-		if (tableRenameTableInfo != null && StringUtils.isNotEmpty(tableRenameTableInfo.getCurrentTableName())) {
-			return tableRenameTableInfo.getCurrentTableName();
-		}
-		return super.getTgtTableNameFromTapEvent(tapEvent);
-	}
-
-	protected void updateNodeConfig(TapdataEvent tapdataEvent) {
-		super.updateNodeConfig(tapdataEvent);
-		TapEvent tapEvent = tapdataEvent.getTapEvent();
-		if (tapEvent instanceof TapCreateTableEvent || tapEvent instanceof TapDropTableEvent) {
-			initTableNameMapping();
-		}
-	}
+    @Override
+    public String getTgtTableNameFromTapEvent(TapEvent tapEvent) {
+        String tableId = TapEventUtil.getTableId(tapEvent);
+        TableRenameProcessNode tableRenameProcessNode = (TableRenameProcessNode) getNode();
+        if (tapEvent instanceof TapRenameTableEvent) {
+            String newTableId = tableRenameProcessNode.convertTableName(tableNameMappingMap, tableId, true);
+            innerTableCacheMap.put(tableId, newTableId);
+            return newTableId;
+        } else if (tapEvent instanceof TapDropTableEvent) {
+            String newTableId = innerTableCacheMap.remove(tableId);
+            if (null == newTableId) {
+                newTableId = tableRenameProcessNode.convertTableName(tableNameMappingMap, tableId, false);
+            }
+            return newTableId;
+        } else {
+            return innerTableCacheMap.computeIfAbsent(tableId, id -> tableRenameProcessNode.convertTableName(tableNameMappingMap, tableId, false));
+        }
+    }
 }
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskService.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskService.java
index 9f4cedb7af..265058bf0a 100644
--- a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskService.java
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskService.java
@@ -1,5 +1,6 @@
 package io.tapdata.flow.engine.V2.task.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.hazelcast.config.Config;
 import com.hazelcast.core.Hazelcast;
 import com.hazelcast.core.HazelcastInstance;
@@ -45,6 +46,7 @@
 import com.tapdata.tm.commons.dag.process.ProcessorNode;
 import com.tapdata.tm.commons.dag.process.TableRenameProcessNode;
 import com.tapdata.tm.commons.dag.vo.ReadPartitionOptions;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
 import com.tapdata.tm.commons.task.dto.TaskDto;
 import io.tapdata.aspect.TaskStartAspect;
 import io.tapdata.aspect.TaskStopAspect;
@@ -87,6 +89,7 @@
 import io.tapdata.flow.engine.V2.task.TaskClient;
 import io.tapdata.flow.engine.V2.task.TaskService;
 import io.tapdata.flow.engine.V2.util.*;
+import io.tapdata.flow.engine.util.TaskDtoUtil;
 import io.tapdata.observable.logging.ObsLogger;
 import io.tapdata.observable.logging.ObsLoggerFactory;
 import io.tapdata.pdk.core.utils.CommonUtils;
@@ -170,6 +173,10 @@ public TaskClient<TaskDto> startTask(TaskDto taskDto) {
 		try {
 			taskDto.setDag(taskDto.getDag());
 			ObsLogger obsLogger = ObsLoggerFactory.getInstance().getObsLogger(taskDto);
+
+			//Sprint 90- TAP2489: Unchecked errors will be cleared directly upon startup
+			cleanAllUnselectedError(taskDto, obsLogger);
+
 			AspectUtils.executeAspect(new TaskStartAspect().task(taskDto).log(InstanceFactory.instance(LogFactory.class).getLog(taskDto)));
 //            return threadPoolExecutorEx.submitSync(() -> {
 			JobConfig jobConfig = new JobConfig();
@@ -188,6 +195,18 @@ public TaskClient<TaskDto> startTask(TaskDto taskDto) {
 		}
 	}
 
+	protected void cleanAllUnselectedError(TaskDto taskDto, ObsLogger obsLogger) {
+		List<ErrorEvent> errorEvents = taskDto.getErrorEvents();
+		if (!CollUtil.isEmpty(errorEvents)) {
+			List<ErrorEvent> newErrorEvents = errorEvents.stream()
+					.filter(Objects::nonNull)
+					.filter(ErrorEvent::getSkip)
+					.collect(Collectors.toList());
+			taskDto.setErrorEvents(newErrorEvents);
+			TaskDtoUtil.updateErrorEvent(clientMongoOperator, newErrorEvents, taskDto.getId(), obsLogger, "Task initialization error list error, message: {}");
+		}
+	}
+
 	@Override
 	public TaskClient<TaskDto> startTestTask(TaskDto taskDto) {
 		try {
diff --git a/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/util/TaskDtoUtil.java b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/util/TaskDtoUtil.java
new file mode 100644
index 0000000000..5f31521a50
--- /dev/null
+++ b/iengine/iengine-app/src/main/java/io/tapdata/flow/engine/util/TaskDtoUtil.java
@@ -0,0 +1,20 @@
+package io.tapdata.flow.engine.util;
+
+import com.tapdata.constant.ConnectorConstant;
+import com.tapdata.mongo.ClientMongoOperator;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
+import io.tapdata.observable.logging.ObsLogger;
+import org.bson.types.ObjectId;
+
+import java.util.List;
+
+public class TaskDtoUtil {
+    private TaskDtoUtil() {}
+    public static void updateErrorEvent(ClientMongoOperator clientMongoOperator, List<ErrorEvent> errorEvents, ObjectId taskId, ObsLogger obsLogger, String errorMsg) {
+        try {
+            clientMongoOperator.insertOne(errorEvents, ConnectorConstant.TASK_COLLECTION + "/errorEvents/" + taskId);
+        } catch (Exception e){
+            obsLogger.warn(errorMsg, e.getMessage());
+        }
+    }
+}
diff --git a/iengine/iengine-app/src/main/resources/pluginKit.properties b/iengine/iengine-app/src/main/resources/pluginKit.properties
index 7479e56ed0..e728e11254 100644
--- a/iengine/iengine-app/src/main/resources/pluginKit.properties
+++ b/iengine/iengine-app/src/main/resources/pluginKit.properties
@@ -12,7 +12,7 @@ maven.compiler.target=1.8
 project.build.sourceEncoding=UTF-8
 project.reporting.outputEncoding=UTF-8
 proxy.file.name=plugin-kit-v1.0-SNAPSHOT
-tapdata.api.verison=1.3.6-SNAPSHOT
-tapdata.pdk.api.verison=1.3.6-SNAPSHOT
+tapdata.api.verison=1.3.7-SNAPSHOT
+tapdata.pdk.api.verison=1.3.7-SNAPSHOT
 tapdata.pdk.connector.core.version=1.0-SNAPSHOT
 tapdata.pdk.runner.verison=1.7-SNAPSHOT
diff --git a/iengine/iengine-common/src/main/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtil.java b/iengine/iengine-common/src/main/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtil.java
index 98e3341453..3d0f0e60dc 100644
--- a/iengine/iengine-common/src/main/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtil.java
+++ b/iengine/iengine-common/src/main/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtil.java
@@ -6,10 +6,13 @@
 import io.tapdata.entity.event.ddl.entity.ValueChange;
 import io.tapdata.entity.event.ddl.table.TapRenameTableEvent;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 public class BatchOffsetUtil {
+    protected static final String BATCH_READ_CONNECTOR_OFFSET = "batch_read_connector_offset";
+    protected static final String BATCH_READ_CONNECTOR_STATUS = "batch_read_connector_status";
     private BatchOffsetUtil(){
 
     }
@@ -19,6 +22,9 @@ public static boolean batchIsOverOfTable(SyncProgress syncProgress, String table
         if (offsetValue instanceof BatchOffset) {
             /** 86 Iteration New Function - Full Scale Synchronization Breakpoint **/
             return TableBatchReadStatus.OVER.name().equals(((BatchOffset)offsetValue).getStatus());
+        } else if (offsetValue instanceof Map
+                && ((Map<?, ?>)offsetValue).containsKey(BATCH_READ_CONNECTOR_STATUS)) {
+            return TableBatchReadStatus.OVER.name().equals(((Map<String, Object>)offsetValue).get(BATCH_READ_CONNECTOR_STATUS));
         }
         /** history data*/
         return false;
@@ -29,6 +35,9 @@ public static Object getBatchOffsetOfTable(SyncProgress syncProgress, String tab
         if (offsetValue instanceof BatchOffset) {
             /** 86 Iteration New Function - Full Scale Synchronization Breakpoint **/
             return ((BatchOffset) offsetValue).getOffset();
+        } else if (offsetValue instanceof Map
+                && ((Map<?, ?>)offsetValue).containsKey(BATCH_READ_CONNECTOR_OFFSET)) {
+            return ((Map<String, Object>)offsetValue).get(BATCH_READ_CONNECTOR_OFFSET);
         }
 
         /** history data*/
@@ -47,18 +56,27 @@ public static void updateBatchOffset(SyncProgress syncProgress, String tableId,
         Object batchOffsetObj = syncProgress.getBatchOffsetObj();
         if (batchOffsetObj instanceof Map) {
             Map<String, Object> batchOffsetObjTemp = (Map<String, Object>) batchOffsetObj;
-            Object batchOffsetObject = batchOffsetObjTemp.computeIfAbsent(tableId, k -> new BatchOffset(offset, isOverTag));
-            if (batchOffsetObject instanceof BatchOffset) {
-                BatchOffset batchOffset = (BatchOffset) batchOffsetObject;
-                batchOffset.setOffset(offset);
-                batchOffset.setStatus(isOverTag);
+            Object batchOffsetObject = batchOffsetObjTemp.computeIfAbsent(tableId, k -> new HashMap<>());
+            if (batchOffsetObject instanceof Map
+                    && ((Map<?, ?>)batchOffsetObject).containsKey(BATCH_READ_CONNECTOR_OFFSET)
+                    && ((Map<?, ?>)batchOffsetObject).containsKey(BATCH_READ_CONNECTOR_STATUS)) {
+                updateBatchOffset((Map<String, Object>)batchOffsetObject, offset, isOverTag);
             } else {
                 /** update history offset of table which id is ${tableId} **/
-                batchOffsetObjTemp.put(tableId, new BatchOffset(offset, isOverTag));
+                batchOffsetObjTemp.put(tableId, updateBatchOffset(new HashMap<>(), offset, isOverTag));
             }
         }
     }
 
+    protected static Object updateBatchOffset(Map<String, Object> offsetMap, Object offset, String isOverTag) {
+        if (null == offsetMap) {
+            offsetMap = new HashMap<>();
+        }
+        offsetMap.put(BATCH_READ_CONNECTOR_STATUS, isOverTag);
+        offsetMap.put(BATCH_READ_CONNECTOR_OFFSET, offset);
+        return offsetMap;
+    }
+
     protected static void tableUpdateName(SyncProgress syncProgress, String oldName, String newName) {
         Object batchTableOffsetObj = syncProgress.getBatchOffsetObj();
         if (batchTableOffsetObj instanceof Map) {
diff --git a/iengine/iengine-common/src/main/java/io/tapdata/error/TaskProcessorExCode_11.java b/iengine/iengine-common/src/main/java/io/tapdata/error/TaskProcessorExCode_11.java
index 224dff206c..951270b873 100644
--- a/iengine/iengine-common/src/main/java/io/tapdata/error/TaskProcessorExCode_11.java
+++ b/iengine/iengine-common/src/main/java/io/tapdata/error/TaskProcessorExCode_11.java
@@ -153,4 +153,10 @@ public interface TaskProcessorExCode_11 {
 			describeCN = "在封装tapdata事件时，源节点克隆批量断点信息失败"
 	)
 	String SOURCE_CLONE_BATCH_OFFSET_FAILED = "11028";
+
+	@TapExCode(
+			describe = "Source node can not support countByPartitionFilterFunction",
+			describeCN = "源节点在不支持过滤count的方法"
+	)
+	String SOURCE_NOT_SUPPORT_COUNT_BY_PARTITION_FILTER_FUNCTION = "11029";
 }
diff --git a/iengine/pom.xml b/iengine/pom.xml
index 7ace683364..de383dc875 100644
--- a/iengine/pom.xml
+++ b/iengine/pom.xml
@@ -810,7 +810,7 @@
             <dependency>
                 <groupId>com.tapdata</groupId>
                 <artifactId>tapdata-api</artifactId>
-                <version>1.3.6-SNAPSHOT</version>
+                <version>1.3.7-SNAPSHOT</version>
             </dependency>
             <dependency>
                 <groupId>com.tapdata</groupId>
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/base/service/BaseService.java b/manager/tm-api/src/main/java/com/tapdata/tm/base/service/BaseService.java
index c40d142136..1e4bb89aff 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/base/service/BaseService.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/base/service/BaseService.java
@@ -53,7 +53,7 @@ public abstract class BaseService<Dto extends BaseDto, Entity extends BaseEntity
     protected Class<Entity> entityClass;
 
 
-    protected boolean isAgentReq() {
+    public boolean isAgentReq() {
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
         HttpServletRequest request = attributes.getRequest();
         String userAgent = request.getHeader("user-agent");
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/dto/InspectDto.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/dto/InspectDto.java
index 0a424d4811..780de78f5d 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/dto/InspectDto.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/dto/InspectDto.java
@@ -1,6 +1,7 @@
 package com.tapdata.tm.inspect.dto;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.tapdata.tm.commons.base.IDataPermissionDto;
 import com.tapdata.tm.commons.base.dto.SchedulableDto;
 import com.tapdata.tm.commons.schema.bean.PlatformInfo;
 import com.tapdata.tm.commons.task.dto.alarm.AlarmRuleVO;
@@ -8,6 +9,7 @@
 import com.tapdata.tm.inspect.bean.Limit;
 import com.tapdata.tm.inspect.bean.Task;
 import com.tapdata.tm.inspect.bean.Timing;
+import com.tapdata.tm.inspect.vo.InspectTaskVo;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
@@ -19,7 +21,7 @@
  */
 @EqualsAndHashCode(callSuper = true)
 @Data
-public class InspectDto extends SchedulableDto {
+public class InspectDto extends SchedulableDto implements IDataPermissionDto {
     /**
      *
      */
@@ -28,6 +30,10 @@ public class InspectDto extends SchedulableDto {
      *
      */
     private String flowId;
+    /**
+     * 查询详情的时候需要返回关联的task详情
+     * */
+    private InspectTaskVo taskDto;
     /**
      *
      */
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/entity/InspectEntity.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/entity/InspectEntity.java
index 08f23b0687..3b6f469fc3 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/entity/InspectEntity.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/entity/InspectEntity.java
@@ -1,6 +1,7 @@
 package com.tapdata.tm.inspect.entity;
 
 import com.tapdata.tm.base.entity.SchedulableEntity;
+import com.tapdata.tm.commons.base.IDataPermissionEntity;
 import com.tapdata.tm.commons.schema.bean.PlatformInfo;
 import com.tapdata.tm.commons.task.dto.alarm.AlarmRuleVO;
 import com.tapdata.tm.commons.task.dto.alarm.AlarmSettingVO;
@@ -20,7 +21,7 @@
 @EqualsAndHashCode(callSuper = true)
 @Data
 @Document("Inspect")
-public class InspectEntity extends SchedulableEntity {
+public class InspectEntity extends SchedulableEntity implements IDataPermissionEntity {
     private String name;        // 校验任务名称
     private String flowId;      // 任务ID
     private String mode;        // 运行方式: 手工执行, 定时调度执行, manual/cron
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectCronJob.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectCronJob.java
index 3650e4d1cd..bac4e90717 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectCronJob.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectCronJob.java
@@ -51,7 +51,7 @@ public void execute(JobExecutionContext jobExecutionContext) throws JobExecution
 
             Where where=new Where();
             where.put("id",inspectId);
-            inspectService.executeInspect(where,inspectDto,userDetail);
+            inspectService.doExecuteInspect(where,inspectDto,userDetail);
         }
     }
 }
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectService.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectService.java
index 382ea347e3..2b7d84877d 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectService.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectService.java
@@ -1,6 +1,7 @@
 package com.tapdata.tm.inspect.service;
 
 import com.mongodb.client.result.UpdateResult;
+import com.tapdata.tm.base.dto.Field;
 import com.tapdata.tm.base.dto.Filter;
 import com.tapdata.tm.base.dto.Page;
 import com.tapdata.tm.base.dto.Where;
@@ -18,11 +19,15 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 public abstract class InspectService extends BaseService<InspectDto, InspectEntity, ObjectId, InspectRepository> {
     public InspectService(@NonNull InspectRepository repository) {
         super(repository, InspectDto.class, InspectEntity.class);
     }
+
+    public abstract Supplier<InspectDto> dataPermissionFindById(ObjectId taskId, Field fields);
+
     @Transactional
     public abstract Map<String, Long> delete(String id, UserDetail user);
 
@@ -42,9 +47,7 @@ public InspectDto save(InspectDto inspectDto, UserDetail user){
 
     public abstract UpdateResult deleteByTaskId(String taskId);
 
-    public abstract InspectDto updateInspectByWhere(Where where, InspectDto updateDto, UserDetail user);
-
-    public abstract InspectDto executeInspect(Where where, InspectDto updateDto, UserDetail user);
+    public abstract InspectDto doExecuteInspect(Where where, InspectDto updateDto, UserDetail user);
 
     public abstract InspectDto updateById(ObjectId objectId, InspectDto inspectDto, UserDetail userDetail);
 
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectTaskService.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectTaskService.java
new file mode 100644
index 0000000000..16600d1753
--- /dev/null
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/service/InspectTaskService.java
@@ -0,0 +1,29 @@
+package com.tapdata.tm.inspect.service;
+
+import com.tapdata.tm.base.dto.Where;
+import com.tapdata.tm.commons.schema.DataSourceConnectionDto;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.inspect.dto.InspectDto;
+
+import java.util.List;
+
+public interface InspectTaskService {
+    InspectDto inspectTaskRun(Where where, InspectDto updateDto, UserDetail user);
+
+    InspectDto inspectTaskStop(String id, InspectDto updateDto, UserDetail user);
+
+    InspectDto inspectTaskError(String id, InspectDto updateDto, UserDetail user);
+
+    InspectDto inspectTaskDone(String id, InspectDto updateDto, UserDetail user);
+
+    InspectDto executeInspect(Where where, InspectDto updateDto, UserDetail user);
+
+    String startInspectTask(InspectDto inspectDto, String processId);
+
+    void stopInspectTask(InspectDto inspectDto);
+
+    List<TaskDto> findTaskList(UserDetail user);
+
+    List<DataSourceConnectionDto> findConnectionList(UserDetail user);
+}
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/inspect/vo/InspectTaskVo.java b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/vo/InspectTaskVo.java
new file mode 100644
index 0000000000..d406e8459d
--- /dev/null
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/inspect/vo/InspectTaskVo.java
@@ -0,0 +1,26 @@
+package com.tapdata.tm.inspect.vo;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.tapdata.tm.commons.base.convert.DagDeserialize;
+import com.tapdata.tm.commons.base.convert.DagSerialize;
+import com.tapdata.tm.commons.base.convert.ObjectIdDeserialize;
+import com.tapdata.tm.commons.base.convert.ObjectIdSerialize;
+import com.tapdata.tm.commons.dag.DAG;
+import lombok.Data;
+import org.bson.types.ObjectId;
+
+@Data
+public class InspectTaskVo {
+    @JsonSerialize( using = ObjectIdSerialize.class)
+    @JsonDeserialize( using = ObjectIdDeserialize.class)
+    ObjectId id;
+
+    String name;
+
+    String syncType;
+
+    @JsonSerialize( using = DagSerialize.class)
+    @JsonDeserialize( using = DagDeserialize.class)
+    DAG dag;
+}
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/DataPermissionHelper.java b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/DataPermissionHelper.java
index 954bdbab02..3e029d7454 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/DataPermissionHelper.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/DataPermissionHelper.java
@@ -85,280 +85,3 @@ public static String signDecode(HttpServletRequest request, String id) {
         return iDataPermissionHelper.signDecode(request, id);
     }
 }
-/*
-package com.tapdata.tm.permissions;
-
-import com.tapdata.tm.base.entity.BaseEntity;
-import com.tapdata.tm.base.reporitory.BaseRepository;
-import com.tapdata.tm.commons.base.DataPermissionAction;
-import com.tapdata.tm.commons.base.IDataPermissionDto;
-import com.tapdata.tm.commons.base.IDataPermissionEntity;
-import com.tapdata.tm.commons.base.dto.BaseDto;
-import com.tapdata.tm.config.security.UserDetail;
-import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
-import com.tapdata.tm.permissions.constants.DataPermissionDataTypeEnums;
-import com.tapdata.tm.permissions.constants.DataPermissionMenuEnums;
-import com.tapdata.tm.permissions.constants.DataPermissionTypeEnums;
-import com.tapdata.tm.permissions.service.DataPermissionService;
-import com.tapdata.tm.permissions.vo.DataPermissionAuthInfoVo;
-import com.tapdata.tm.permissions.vo.DataPermissionTypeVo;
-import com.tapdata.tm.utils.AES256Util;
-import org.bson.types.ObjectId;
-import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import static com.tapdata.tm.permissions.DataPermissionHelper.*;
-
-*//**
- * All data permissions are processed by the current class
- *
- * @author <a href="mailto:harsen_lin@163.com">Harsen</a>
- * @version v1.0 2023/8/8 15:57 Create
- *//*
-public class DataPermissionHelper {
-
-    public static final String FIELD_NAME = "permissions";
-    public static final String AUTH_TYPE = String.join(".", FIELD_NAME, "type");
-    public static final String AUTH_TYPE_ID = String.join(".", FIELD_NAME, "typeId");
-    public static final String AUTH_ACTIONS = String.join(".", FIELD_NAME, "actions");
-    private static final ThreadLocal<DataPermissionAuthInfoVo> THREAD_LOCAL = new ThreadLocal<>();
-
-    private static boolean isAgentReq() {
-        try {
-            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
-            HttpServletRequest request = attributes.getRequest();
-            String userAgent = request.getHeader("user-agent");
-            return org.apache.commons.lang3.StringUtils.isNotBlank(userAgent) && (userAgent.contains("Java") || userAgent.contains("Node") || userAgent.contains("FlowEngine"));
-        } catch (Exception ignore) {
-            return false;
-        }
-    }
-
-    private static boolean isUnCheckAuth(UserDetail userDetail) {
-        return DataPermissionService.isCloud() || userDetail.isFreeAuth() || isAgentReq();
-    }
-
-    private static boolean isMenuAuth(DataPermissionMenuEnums menuEnums, Set<String> roleIdSet) {
-        return DataPermissionService.getInstance().isMenuAuth(
-                menuEnums.getAllDataPermissionName(), roleIdSet.stream().map(ObjectId::new).collect(Collectors.toSet())
-        );
-    }
-
-    private static <T> T executeByAuthInfo(DataPermissionAuthInfoVo vo, Supplier<T> supplier) {
-        try {
-            THREAD_LOCAL.set(vo);
-            return supplier.get();
-        } finally {
-            THREAD_LOCAL.remove();
-        }
-    }
-
-    private static DataPermissionAuthInfoVo getAuthInfo(
-            UserDetail userDetail,
-            DataPermissionMenuEnums menuEnums,
-            DataPermissionActionEnums actionEnums,
-            DataPermissionDataTypeEnums dataTypeEnums,
-            String id
-    ) {
-        if (isUnCheckAuth(userDetail)) return null;
-
-        DataPermissionAuthInfoVo authInfoVo = new DataPermissionAuthInfoVo()
-                .id(id)
-                .userId(userDetail.getUserId())
-                .menuEnums(menuEnums)
-                .dataTypeEnums(dataTypeEnums)
-                .actionEnums(actionEnums)
-                .created(System.currentTimeMillis());
-
-        // check admin auth
-        if (userDetail.isRoot()) return authInfoVo.authWithAdmin();
-
-        DataPermissionTypeVo[] typeVos = null;
-        Set<String> roleSet = DataPermissionService.getInstance().getRoleIds(userDetail.getUserId());
-        if (!(null == roleSet || roleSet.isEmpty())) {
-            authInfoVo.setRoleSet(roleSet);
-
-            // check menu auth
-            if (null != menuEnums && isMenuAuth(menuEnums, roleSet)) return authInfoVo.authWithMenu();
-
-            typeVos = new DataPermissionTypeVo[]{
-                    new DataPermissionTypeVo(DataPermissionTypeEnums.Role, roleSet)
-            };
-        }
-
-        // check data and role auth
-        if (null != id) {
-            return DataPermissionService.getInstance().setDataActions(authInfoVo, typeVos);
-        }
-        return authInfoVo;
-    }
-
-    public static Set<String> mergeActions(Set<String> actions, Set<String> roleIds, List<DataPermissionAction> permissionItems) {
-        if (null != permissionItems) {
-            for (DataPermissionAction p : permissionItems) {
-                if (DataPermissionTypeEnums.Role.name().equals(p.getType()) && roleIds.contains(p.getTypeId()) && null != p.getActions()) {
-                    actions.addAll(p.getActions());
-                }
-            }
-        }
-        return actions;
-    }
-
-    public static boolean setFilterConditions(boolean need2SetFilter, Query query, UserDetail userDetail) {
-        DataPermissionAuthInfoVo vo = THREAD_LOCAL.get();
-        if (null == vo || !need2SetFilter) return false;
-
-        if (!vo.isSetQueryFilter()) return true;
-
-        Set<String> roleIds = vo.getRoleSet();
-        if (null == roleIds) return false;
-
-        BaseRepository.removeFilter("user_id", query);
-        try {
-            if (!query.getFieldsObject().isEmpty()) {
-                query.fields().include(FIELD_NAME);
-            }
-            BaseRepository.addOrFilter(query,
-                    Criteria.where("user_id").is(userDetail.getUserId())
-                    , Criteria.where(AUTH_TYPE).is(DataPermissionTypeEnums.Role)
-                            .and(AUTH_TYPE_ID).in(roleIds)
-                            .and(AUTH_ACTIONS).in(vo.getActionEnums())
-            );
-            return true;
-        } catch (Exception e) {
-            throw new RuntimeException("Set auth condition failed: " + e.getMessage(), e);
-        }
-    }
-
-    public static <E extends BaseEntity, D extends BaseDto> void convert(E entity, D dto) {
-        if (DataPermissionService.isCloud()) return;
-
-        DataPermissionAuthInfoVo vo = THREAD_LOCAL.get();
-        if (null != vo && entity instanceof IDataPermissionEntity && dto instanceof IDataPermissionDto) {
-            // menu and admin role auth not has role ids, set all actions
-            Set<String> roleIds = vo.getRoleSet();
-            if (null == roleIds) {
-                dto.setPermissionActions(vo.getDataTypeEnums().allActions());
-                return;
-            }
-
-            // creator has all actions
-            String userId = vo.getUserId();
-            if (userId.equals(entity.getUserId())) {
-                dto.setPermissionActions(vo.getDataTypeEnums().allActions());
-                return;
-            }
-
-            Set<String> actions = new HashSet<>();
-            mergeActions(actions, roleIds, ((IDataPermissionEntity) entity).getPermissions());
-            dto.setPermissionActions(actions);
-        }
-    }
-
-    public static void cleanAuthOfRoleDelete(Set<String> roleIds) {
-        if (DataPermissionService.isCloud()) return;
-        DataPermissionService.getInstance().cleanAuthOfRoleDelete(roleIds);
-    }
-
-    *//**
-     * @param userDetail     user info
-     * @param menuEnums      menu type, check menu auth if not null
-     * @param actionEnums    action type, not null
-     * @param dataTypeEnums  data type, not null
-     * @param id             data id, query and check data auth if not null
-     * @param supplier       call in normal
-     * @param noAuthSupplier call if data un auth
-     * @param <T>            result type
-     * @return call data
-     *//*
-    public static  <T> T check(
-            UserDetail userDetail,
-            DataPermissionMenuEnums menuEnums,
-            DataPermissionActionEnums actionEnums,
-            DataPermissionDataTypeEnums dataTypeEnums,
-            String id,
-            Supplier<T> supplier,
-            Supplier<T> noAuthSupplier
-    ) {
-        DataPermissionAuthInfoVo vo = getAuthInfo(userDetail, menuEnums, actionEnums, dataTypeEnums, id);
-        if (null != vo) {
-            if (vo.isUnAuth()) return noAuthSupplier.get();
-            return executeByAuthInfo(vo, supplier);
-        }
-        return supplier.get();
-    }
-
-    public static  <T, D extends BaseDto> T checkOfQuery(
-            UserDetail userDetail,
-            DataPermissionDataTypeEnums dataTypeEnums,
-            DataPermissionActionEnums actionEnums,
-            Supplier<D> querySupplier,
-            Function<D, DataPermissionMenuEnums> menuEnumsFun,
-            Supplier<T> supplier,
-            Supplier<T> unAuthSupplier
-    ) {
-        if (isUnCheckAuth(userDetail)) return supplier.get();
-
-        DataPermissionAuthInfoVo vo = new DataPermissionAuthInfoVo()
-                .userId(userDetail.getUserId())
-                .dataTypeEnums(dataTypeEnums)
-                .actionEnums(actionEnums);
-
-        // check admin auth
-        if (userDetail.isRoot()) {
-            return executeByAuthInfo(vo.authWithAdmin(), supplier);
-        }
-
-        vo.setRoleSet(DataPermissionService.getInstance().getRoleIds(userDetail.getUserId()));
-
-        D dto = executeByAuthInfo(vo, querySupplier);
-        if (null == dto) return unAuthSupplier.get();
-
-        vo.id(dto.getId().toHexString()).menuEnums(menuEnumsFun.apply(dto));
-
-        if (userDetail.getUserId().equals(dto.getUserId())) {
-            //check user auth
-            return executeByAuthInfo(vo.authWithUserData(), supplier);
-        } else if (null != dto.getPermissionActions() && dto.getPermissionActions().contains(actionEnums.name())) {
-            //check role auth
-            return executeByAuthInfo(vo.authWithUserData(), supplier);
-        }
-
-        // check menu auth
-        if (null != vo.getMenuEnums() && isMenuAuth(vo.getMenuEnums(), vo.getRoleSet())) {
-            return executeByAuthInfo(vo.authWithMenu(), supplier);
-        }
-
-        return unAuthSupplier.get();
-    }
-
-    public static String signEncode(String currentId, String parentId) {
-        return AES256Util.Aes256Encode(String.join(",", currentId, parentId));
-    }
-
-    public static String signDecode(HttpServletRequest request, String id) {
-        if (null == id) return null;
-
-        String parentTaskSign = request.getHeader("parent_task_sign");
-        if (null != parentTaskSign) {
-            String str = AES256Util.Aes256Decode(parentTaskSign);
-            if (!str.equals(parentTaskSign)) {
-                String[] arr = str.split(",");
-                if (arr.length == 2 && id.equals(arr[0])) return arr[1];
-            }
-        }
-        return null;
-    }
-}*/
-
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnums.java b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnums.java
index e015398560..50ee46075a 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnums.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnums.java
@@ -22,7 +22,14 @@ public enum DataPermissionDataTypeEnums implements IDataPermissionAction {
 		DataPermissionActionEnums.Reset,
 		DataPermissionActionEnums.Start,
 		DataPermissionActionEnums.Stop
-	),
+	), INSPECT(
+		"Inspect",
+		DataPermissionActionEnums.View,
+		DataPermissionActionEnums.Edit,
+		DataPermissionActionEnums.Delete,
+		DataPermissionActionEnums.Start,
+		DataPermissionActionEnums.Stop
+	)
 	;
 	private final String collection;
 	private final LinkedHashSet<String> allActions;
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnums.java b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnums.java
index a54933749a..281d81ad28 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnums.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnums.java
@@ -19,6 +19,7 @@ public enum DataPermissionMenuEnums {
 	LogCollectorTack(DataPermissionDataTypeEnums.Task, ""),
 	ConnHeartbeatTack(DataPermissionDataTypeEnums.Task, ""),
 	MemCacheTack(DataPermissionDataTypeEnums.Task, ""),
+	INSPECT_TACK(DataPermissionDataTypeEnums.INSPECT, ""),
 	;
 
 	private final DataPermissionDataTypeEnums dataType;
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/task/entity/TaskEntity.java b/manager/tm-api/src/main/java/com/tapdata/tm/task/entity/TaskEntity.java
index 68f45f4fb4..df3528a3cc 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/task/entity/TaskEntity.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/task/entity/TaskEntity.java
@@ -280,6 +280,12 @@ public class TaskEntity extends BaseEntity implements IDataPermissionEntity {
     private Boolean doubleActive = false;
 
     private List<ErrorEvent> errorEvents;
+    /**
+     * Compatible with old versions of timezone processing
+     * true: The data source reads the date according to the old version logic
+     * false: The data source reads the date according to the new version logic
+     */
+    private Boolean oldVersionTimezone = true;
 
     public String getAccessNodeProcessId() {
         if (AccessNodeTypeEnum.isGroupManually(accessNodeType)) {
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/task/service/TaskService.java b/manager/tm-api/src/main/java/com/tapdata/tm/task/service/TaskService.java
index c9bb93528b..00492f38fa 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/task/service/TaskService.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/task/service/TaskService.java
@@ -258,8 +258,6 @@ public abstract TaskDto createShareCacheTask(SaveShareCacheParam saveShareCacheP
 
     public abstract void updateTaskLogSetting(String taskId, LogSettingParam logSettingParam, UserDetail userDetail);
 
-    public abstract Chart6Vo chart6(UserDetail user);
-
     public abstract void stopTaskIfNeedByAgentId(String agentId, UserDetail userDetail);
 
     public abstract List<TaskDto> getTaskStatsByTableNameOrConnectionId(String connectionId, String tableName, UserDetail userDetail);
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/utils/QueryUtil.java b/manager/tm-api/src/main/java/com/tapdata/tm/utils/QueryUtil.java
new file mode 100644
index 0000000000..d99f883646
--- /dev/null
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/utils/QueryUtil.java
@@ -0,0 +1,19 @@
+package com.tapdata.tm.utils;
+
+import com.tapdata.tm.base.dto.Where;
+import org.bson.Document;
+import org.springframework.data.mongodb.core.query.Criteria;
+
+public class QueryUtil {
+    private QueryUtil() {}
+    /**
+     * Convert filter to Criteria
+     *
+     * @param where
+     * @return
+     */
+    public static Criteria parseWhereToCriteria(Where where) {
+        Document doc = new Document(where);
+        return Criteria.matchingDocumentStructure(() -> doc);
+    }
+}
diff --git a/manager/tm-api/src/main/java/com/tapdata/tm/webhook/enums/ConstVariable.java b/manager/tm-api/src/main/java/com/tapdata/tm/webhook/enums/ConstVariable.java
index 76b9c84c87..df95313e94 100644
--- a/manager/tm-api/src/main/java/com/tapdata/tm/webhook/enums/ConstVariable.java
+++ b/manager/tm-api/src/main/java/com/tapdata/tm/webhook/enums/ConstVariable.java
@@ -1,6 +1,7 @@
 package com.tapdata.tm.webhook.enums;
 
 public class ConstVariable {
+    private ConstVariable() {}
     public static final String HOOK_ID = "hookId";
     public static final String HOOK_EVENT = "hookEvent";
     public static final String DELETED = "deleted";
diff --git a/manager/tm-common/src/main/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNode.java b/manager/tm-common/src/main/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNode.java
index fcd01105f8..e06eee152a 100644
--- a/manager/tm-common/src/main/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNode.java
+++ b/manager/tm-common/src/main/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNode.java
@@ -4,15 +4,13 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.tapdata.tm.commons.dag.DAG;
-import com.tapdata.tm.commons.dag.Node;
 import com.tapdata.tm.commons.dag.NodeEnum;
 import com.tapdata.tm.commons.dag.NodeType;
 import com.tapdata.tm.commons.dag.vo.TableRenameTableInfo;
 import com.tapdata.tm.commons.schema.Schema;
 import com.tapdata.tm.commons.schema.SchemaUtils;
-import io.tapdata.entity.event.ddl.TapDDLEvent;
-import io.tapdata.entity.event.ddl.table.TapCreateTableEvent;
-import io.tapdata.entity.event.ddl.table.TapDropTableEvent;
+import com.tapdata.tm.error.TapDynamicTableNameExCode_35;
+import io.tapdata.exception.TapCodeException;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
@@ -20,12 +18,9 @@
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import static com.tapdata.tm.commons.base.convert.ObjectIdDeserialize.toObjectId;
-
 @NodeType("table_rename_processor")
 @Getter
 @Setter
@@ -33,7 +28,6 @@
 public class TableRenameProcessNode extends MigrateProcessorNode {
     /**
      * 创建处理器节点
-     *
      **/
     public TableRenameProcessNode() {
         super(NodeEnum.table_rename_processor.name(), NodeCatalog.processor);
@@ -57,9 +51,8 @@ public void setTableNames(LinkedHashSet<TableRenameTableInfo> tableNames) {
     private String transferCase;
 
 
-
-    public Map<String, TableRenameTableInfo> originalMap () {
-        if (Objects.isNull(tableNames) || tableNames.isEmpty()) {
+    public Map<String, TableRenameTableInfo> originalMap() {
+        if (null == tableNames) {
             return Maps.newLinkedHashMap();
         }
 
@@ -67,124 +60,71 @@ public Map<String, TableRenameTableInfo> originalMap () {
     }
 
     public Map<String, TableRenameTableInfo> currentMap() {
-        if (Objects.isNull(tableNames) || tableNames.isEmpty()) {
+        if (null == tableNames) {
             return Maps.newLinkedHashMap();
         }
 
         return tableNames.stream().collect(Collectors.toMap(TableRenameTableInfo::getCurrentTableName, Function.identity()));
     }
 
-    @Override
-    public List<Schema> mergeSchema(List<List<Schema>> inputSchemas, List<Schema> schemas, DAG.Options options) {
-        if (CollectionUtils.isEmpty(inputSchemas)) {
-            return Lists.newArrayList();
+    public Map<String, TableRenameTableInfo> previousMap() {
+        Map<String, TableRenameTableInfo> resultMap = Maps.newLinkedHashMap();
+        if (null == tableNames) {
+            return resultMap;
         }
 
-        if (Objects.isNull(tableNames) || tableNames.isEmpty()) {
-            return inputSchemas.get(0);
+        for (TableRenameTableInfo tableRenameTableInfo : tableNames) {
+            resultMap.put(tableRenameTableInfo.getPreviousTableName(), tableRenameTableInfo);
         }
-
-        inputSchemas.get(0).forEach(schema -> {
-            String originalName = schema.getAncestorsName();
-            Map<String, TableRenameTableInfo> originalMap = originalMap();
-            if (originalMap.containsKey(originalName)) {
-                String currentTableName = originalMap.get(originalName).getCurrentTableName();
-                schema.setName(currentTableName);
-                schema.setOriginalName(currentTableName);
-                schema.setAncestorsName(originalName);
-                //schema.setDatabaseId(null);
-                //schema.setQualifiedName(MetaDataBuilderUtils.generateQualifiedName(MetaType.processor_node.name(), getId()));
-            } else {
-                String currentTableName = convertTableName(schema.getOriginalName());
-                TableRenameTableInfo tableInfo = new TableRenameTableInfo(originalName, schema.getOriginalName(), currentTableName);
-                tableNames.add(tableInfo);
-                schema.setName(currentTableName);
-                schema.setOriginalName(currentTableName);
-                schema.setAncestorsName(originalName);
-            }
-        });
-
-        Set<String> lastTable = inputSchemas.get(0).stream().map(Schema::getAncestorsName).collect(Collectors.toSet());
-
-        List<TableRenameTableInfo> removeTables = new ArrayList<>();
-        if (CollectionUtils.isNotEmpty(tableNames)) {
-            for (TableRenameTableInfo tableName : tableNames) {
-                if (!lastTable.contains(tableName.getOriginTableName())) {
-                    removeTables.add(tableName);
-                }
-            }
-
-            removeTables.forEach(tableNames::remove);
-        }
-
-        return inputSchemas.get(0);
+        return resultMap;
     }
 
-
-
-
     @Override
-    public void fieldDdlEvent(TapDDLEvent event) throws Exception {
-        if (event instanceof TapCreateTableEvent) {
-            String tableName = event.getTableId();
-
-            List<Node> sources = this.getDag().getSources();
-            Node node = sources.get(0);
-            LinkedList<TableRenameProcessNode> linkedList = new LinkedList<>();
-            while (!node.getId().equals(this.getId())) {
-                if (node instanceof TableRenameProcessNode) {
-                    linkedList.add((TableRenameProcessNode) node);
-                }
-                List successors = node.successors();
-                if (CollectionUtils.isEmpty(successors)) {
-                    break;
-                }
-                node = (Node) successors.get(0);
-            }
-            TableRenameTableInfo tableInfo = null;
-            if (CollectionUtils.isNotEmpty(linkedList)) {
-                for (TableRenameProcessNode node1 : linkedList) {
-                    Map<String, TableRenameTableInfo> tableRenameTableInfoMap = node1.originalMap();
-                    TableRenameTableInfo tableInfo1 = tableRenameTableInfoMap.get(tableName);
-                    if (tableInfo1 != null) {
-                        tableInfo = tableInfo1;
+    public List<Schema> mergeSchema(List<List<Schema>> inputSchemas, List<Schema> schemas, DAG.Options options) {
+        if (CollectionUtils.isEmpty(inputSchemas)) {
+            return Lists.newArrayList();
+        }
+
+        // 'schemas' is null because node not exists physical model
+        List<Schema> outputSchemas = SchemaUtils.cloneSchema(inputSchemas.get(0));
+
+        outputSchemas.forEach(schema -> {
+            Map<String, TableRenameTableInfo> originaledMap = originalMap();
+            String ancestorsName = schema.getAncestorsName();
+            String currentTableName = convertTableName(originaledMap, ancestorsName, false);
+            schema.setName(currentTableName);
+            schema.setOriginalName(currentTableName);
+            Optional.ofNullable(options.getIncludes()).ifPresent(includes -> {
+                for (int i = 0; i < includes.size(); i++) {
+                    if (ancestorsName.equals(includes.get(i))) {
+                        includes.set(i, currentTableName);
                     }
                 }
-            }
+            });
+        });
 
-            String lastTableName = tableName;
-            if (tableInfo != null) {
-                lastTableName = tableInfo.getCurrentTableName();
-            }
+        return outputSchemas;
+    }
 
-            if (CollectionUtils.isEmpty(tableNames))  {
-                tableNames = new LinkedHashSet<>();
-            }
 
-            boolean already = tableNames.stream().anyMatch(t -> t.getOriginTableName().equals(tableName));
-            if (!already) {
-                String currentTableName = convertTableName(lastTableName);
-                boolean exist = tableNames.stream().anyMatch(t -> t.getCurrentTableName().equals(currentTableName));
-                if (exist) {
-                    throw new IllegalArgumentException("The new table [" + currentTableName + "] already exists when it is converted as  [" + currentTableName + "]");
-                }
-                tableInfo = new TableRenameTableInfo(tableName, lastTableName, currentTableName);
-                tableNames.add(tableInfo);
-            } else {
-                log.info("The table [{}] has been added to the configuration", tableName);
+    public String convertTableName(Map<String, TableRenameTableInfo> infoMap, String tableName, boolean isRenameDDL) {
+        TableRenameTableInfo tableInfo = infoMap.get(tableName);
+        if (null != tableInfo) {
+            if (isRenameDDL) {
+                throw new TapCodeException(TapDynamicTableNameExCode_35.RENAME_DDL_CONFLICTS_WITH_CUSTOM_TABLE_NAME
+                    , String.format("Can't apply table rename DDL because it conflicts with custom-table-name from '%s' to '%s'", tableName, tableInfo.getCurrentTableName())
+                );
             }
-        } else if (event instanceof TapDropTableEvent) {
-            String tableName = event.getTableId();
-            for (TableRenameTableInfo tableInfo : tableNames) {
-                if (tableInfo.getOriginTableName().equals(tableName)) {
-                    tableNames.remove(tableInfo);
-                    break;
-                }
+            String currentTableName = tableInfo.getCurrentTableName();
+            if (StringUtils.isNotBlank(currentTableName)) {
+                return currentTableName;
             }
+            return tableName;
         }
+        return convertTableName(tableName);
     }
 
-    private String convertTableName(String tableName) {
+    public String convertTableName(String tableName) {
         if (StringUtils.isNotBlank(prefix)) {
             tableName = prefix + tableName;
         }
diff --git a/manager/tm-common/src/main/java/com/tapdata/tm/commons/task/dto/TaskDto.java b/manager/tm-common/src/main/java/com/tapdata/tm/commons/task/dto/TaskDto.java
index beeefbdaab..783104fb97 100644
--- a/manager/tm-common/src/main/java/com/tapdata/tm/commons/task/dto/TaskDto.java
+++ b/manager/tm-common/src/main/java/com/tapdata/tm/commons/task/dto/TaskDto.java
@@ -207,6 +207,7 @@ public boolean isAutoInspect() {
      * 任务导入导出需要用到
      * */
     private Map<String, Object> agentGroupInfo;
+    private Boolean oldVersionTimezone = true;
 
     public DAG getDag() {
         if (dag != null) {
diff --git a/manager/tm-common/src/main/java/com/tapdata/tm/error/TapDynamicTableNameExCode_35.java b/manager/tm-common/src/main/java/com/tapdata/tm/error/TapDynamicTableNameExCode_35.java
index 75467123e6..24e87a06b1 100644
--- a/manager/tm-common/src/main/java/com/tapdata/tm/error/TapDynamicTableNameExCode_35.java
+++ b/manager/tm-common/src/main/java/com/tapdata/tm/error/TapDynamicTableNameExCode_35.java
@@ -53,4 +53,14 @@ public interface TapDynamicTableNameExCode_35 {
             level = TapExLevel.NORMAL
     )
     String DYNAMIC_METHOD_ACCESS_FAILED = "35005";
+
+    @TapExCode(
+        describe = "Table renamed DDL conflicts with the custom table name.\n" +
+            "Reason:\n1. The task assigns a new table name to the table, which cannot be automatically processed when the table is renamed DDL.",
+        solution = "1. The task configuration needs to be analyzed and resolved.",
+        describeCN = "表改名DDL与自定义表名冲突\n" +
+            "原因\n1. 任务为表指定新表名，这个表发生改名DDL时无法自动处理",
+        solutionCN = "1. 需要分析任务配置，并进行解决"
+    )
+    String RENAME_DDL_CONFLICTS_WITH_CUSTOM_TABLE_NAME = "35006";
 }
diff --git a/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectServiceImpl.java b/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectServiceImpl.java
index f58f93c6cf..4ffbb5a7f2 100644
--- a/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectServiceImpl.java
+++ b/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectServiceImpl.java
@@ -1,81 +1,26 @@
 package com.tapdata.tm.inspect.service;
 
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.bean.copier.CopyOptions;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.extra.cglib.CglibUtil;
-import com.google.common.collect.Maps;
 import com.mongodb.client.result.UpdateResult;
-import com.tapdata.tm.Settings.constant.CategoryEnum;
-import com.tapdata.tm.Settings.constant.KeyEnum;
-import com.tapdata.tm.Settings.entity.Settings;
-import com.tapdata.tm.Settings.service.AlarmSettingService;
-import com.tapdata.tm.Settings.service.SettingsService;
-import com.tapdata.tm.alarm.constant.AlarmComponentEnum;
-import com.tapdata.tm.alarm.constant.AlarmStatusEnum;
-import com.tapdata.tm.alarm.constant.AlarmTypeEnum;
-import com.tapdata.tm.alarm.entity.AlarmInfo;
-import com.tapdata.tm.alarm.service.AlarmService;
+import com.tapdata.tm.base.dto.Field;
 import com.tapdata.tm.base.dto.Filter;
 import com.tapdata.tm.base.dto.Page;
 import com.tapdata.tm.base.dto.Where;
 import com.tapdata.tm.base.exception.BizException;
-import com.tapdata.tm.commons.dag.nodes.DatabaseNode;
-import com.tapdata.tm.commons.dag.nodes.TableNode;
-import com.tapdata.tm.commons.schema.MetadataInstancesDto;
-import com.tapdata.tm.commons.schema.bean.PlatformInfo;
-import com.tapdata.tm.commons.task.constant.AlarmKeyEnum;
-import com.tapdata.tm.commons.task.constant.AlarmSettingTypeEnum;
-import com.tapdata.tm.commons.task.constant.NotifyEnum;
 import com.tapdata.tm.commons.task.dto.TaskDto;
-import com.tapdata.tm.commons.task.dto.alarm.AlarmSettingDto;
-import com.tapdata.tm.commons.task.dto.alarm.AlarmSettingVO;
-import com.tapdata.tm.commons.util.JsonUtil;
 import com.tapdata.tm.config.security.UserDetail;
-import com.tapdata.tm.dataflow.dto.DataFlowDto;
-import com.tapdata.tm.dataflow.service.DataFlowService;
-import com.tapdata.tm.inspect.bean.*;
-import com.tapdata.tm.inspect.constant.InspectMethod;
-import com.tapdata.tm.inspect.constant.InspectResultEnum;
+import com.tapdata.tm.inspect.bean.Task;
 import com.tapdata.tm.inspect.constant.InspectStatusEnum;
-import com.tapdata.tm.inspect.constant.Mode;
 import com.tapdata.tm.inspect.dto.InspectDto;
-import com.tapdata.tm.inspect.dto.InspectResultDto;
-import com.tapdata.tm.inspect.entity.InspectEntity;
 import com.tapdata.tm.inspect.repository.InspectRepository;
-import com.tapdata.tm.inspect.vo.InspectDetailVo;
-import com.tapdata.tm.message.constant.Level;
-import com.tapdata.tm.message.constant.MsgTypeEnum;
-import com.tapdata.tm.message.service.MessageService;
-import com.tapdata.tm.messagequeue.dto.MessageQueueDto;
-import com.tapdata.tm.messagequeue.service.MessageQueueService;
-import com.tapdata.tm.metadatainstance.service.MetadataInstancesService;
-import com.tapdata.tm.task.constant.SyncType;
-import com.tapdata.tm.userLog.constant.Modular;
-import com.tapdata.tm.userLog.constant.Operation;
-import com.tapdata.tm.userLog.service.UserLogService;
-import com.tapdata.tm.utils.*;
-import com.tapdata.tm.worker.service.WorkerService;
+import com.tapdata.tm.webhook.enums.ConstVariable;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
 import org.bson.types.ObjectId;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Sort;
-import org.springframework.data.mongodb.core.query.Criteria;
-import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
-import java.util.*;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 /**
  * @Author:
@@ -89,117 +34,117 @@ public InspectServiceImpl(@NonNull InspectRepository repository) {
         super(repository);
     }
 
+    @Override
+    public Supplier<InspectDto> dataPermissionFindById(ObjectId taskId, Field fields) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
     @Override
     protected void beforeSave(InspectDto dto, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public Map<String, Long> delete(String id, UserDetail user) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public Page<InspectDto> list(Filter filter, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public InspectDto findById(Filter filter, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public void saveInspect(TaskDto taskDto, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public InspectDto createCheckByTask(TaskDto taskDto, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public List<InspectDto> findByTaskIdList(List<String> taskIdList) {
-        Query query = Query.query(Criteria.where("flowId").in(taskIdList).and("is_deleted").ne(true));
-        return findAll(query);
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public UpdateResult deleteByTaskId(String taskId) {
-        return null;
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
-    public InspectDto updateInspectByWhere(Where where, InspectDto updateDto, UserDetail user) {
-        throw new BizException("TapOssNonSupportFunctionException");
-    }
-
-    @Override
-    public InspectDto executeInspect(Where where, InspectDto updateDto, UserDetail user) {
-        throw new BizException("TapOssNonSupportFunctionException");
+    public InspectDto doExecuteInspect(Where where, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public InspectDto updateById(ObjectId objectId, InspectDto inspectDto, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public List<InspectDto> findByName(String name) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public void importData(String json, String upsert, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public void setRepeatInspectTask() {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public UpdateResult updateStatusById(String id, InspectStatusEnum inspectStatusEnum) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public UpdateResult updateStatusByIds(List<ObjectId> idList, InspectStatusEnum status) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public Map<String, Integer> inspectPreview(UserDetail user) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public List<InspectDto> findByStatus(InspectStatusEnum inspectStatusEnum) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public List<InspectDto> findByResult(boolean passed) {
-        throw new BizException("TapOssNonSupportFunctionException");
-    }
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);    }
 
     @Override
     public void cleanDeadInspect() {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public void supplementAlarm(InspectDto inspectDto, UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public List<InspectDto> findAllByIds(List<String> inspectIds) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 
     @Override
     public void fieldHandler(List<Task> tasks,UserDetail userDetail) {
-        throw new BizException("TapOssNonSupportFunctionException");
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
     }
 }
diff --git a/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImpl.java b/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImpl.java
new file mode 100644
index 0000000000..b6bdfbc187
--- /dev/null
+++ b/manager/tm-oss/src/main/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImpl.java
@@ -0,0 +1,60 @@
+package com.tapdata.tm.inspect.service;
+
+import com.tapdata.tm.base.dto.Where;
+import com.tapdata.tm.base.exception.BizException;
+import com.tapdata.tm.commons.schema.DataSourceConnectionDto;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.inspect.dto.InspectDto;
+import com.tapdata.tm.webhook.enums.ConstVariable;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class InspectTaskServiceOOSImpl implements InspectTaskService {
+    @Override
+    public InspectDto inspectTaskRun(Where where, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public InspectDto inspectTaskStop(String id, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public InspectDto inspectTaskError(String id, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public InspectDto inspectTaskDone(String id, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public InspectDto executeInspect(Where where, InspectDto updateDto, UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public String startInspectTask(InspectDto inspectDto, String processId) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public void stopInspectTask(InspectDto inspectDto) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public List<TaskDto> findTaskList(UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+
+    @Override
+    public List<DataSourceConnectionDto> findConnectionList(UserDetail user) {
+        throw new BizException(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION);
+    }
+}
diff --git a/manager/tm-sdk/pom.xml b/manager/tm-sdk/pom.xml
index 2e849fb384..2ea6d3b14a 100644
--- a/manager/tm-sdk/pom.xml
+++ b/manager/tm-sdk/pom.xml
@@ -22,7 +22,7 @@
 		<dependency>
 			<groupId>io.tapdata</groupId>
 			<artifactId>tapdata-api</artifactId>
-			<version>1.3.6-SNAPSHOT</version>
+			<version>1.3.7-SNAPSHOT</version>
 		</dependency>
 
     <dependency>
diff --git a/manager/tm/src/main/java/com/tapdata/tm/TMApplication.java b/manager/tm/src/main/java/com/tapdata/tm/TMApplication.java
index e2e42b385a..6495102874 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/TMApplication.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/TMApplication.java
@@ -3,6 +3,8 @@
 import com.tapdata.tm.discovery.service.DefaultDataDirectoryService;
 import com.tapdata.tm.ds.service.impl.RepairCreateTimeComponent;
 import com.tapdata.tm.listener.StartupListener;
+import com.tapdata.tm.report.dto.RunsNumBatch;
+import com.tapdata.tm.report.service.UserDataReportService;
 import com.tapdata.tm.task.service.LdpService;
 import com.tapdata.tm.user.dto.UserDto;
 import com.tapdata.tm.user.service.UserService;
@@ -56,6 +58,11 @@ public static void main(String[] args) {
 				.build().run(args);
 		SpringContextHelper.applicationContext = applicationContext;
 
+		UserDataReportService userDataReportService = applicationContext.getBean(UserDataReportService.class);
+		long currentTimeMillis = System.currentTimeMillis();
+		RunsNumBatch runsNumBatch = new RunsNumBatch();
+		runsNumBatch.setTimestamp(currentTimeMillis);
+		userDataReportService.produceData(runsNumBatch);
 		new Thread(()->{
 			RepairCreateTimeComponent repairCreateTimeComponent = applicationContext.getBean("repairCreateTimeComponent", RepairCreateTimeComponent.class);
 			repairCreateTimeComponent.repair();
diff --git a/manager/tm/src/main/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImpl.java b/manager/tm/src/main/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImpl.java
index 47b9b4ae8c..e9fde0723f 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImpl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImpl.java
@@ -1,7 +1,6 @@
 package com.tapdata.tm.ds.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.lang.Assert;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -18,7 +17,6 @@
 import com.tapdata.tm.base.dto.Page;
 import com.tapdata.tm.base.dto.Where;
 import com.tapdata.tm.base.exception.BizException;
-import com.tapdata.tm.base.service.BaseService;
 import com.tapdata.tm.classification.dto.ClassificationDto;
 import com.tapdata.tm.classification.service.ClassificationService;
 import com.tapdata.tm.commons.base.dto.BaseDto;
@@ -41,6 +39,7 @@
 import com.tapdata.tm.ds.dto.UpdateTagsDto;
 import com.tapdata.tm.ds.entity.DataSourceEntity;
 import com.tapdata.tm.ds.repository.DataSourceRepository;
+import com.tapdata.tm.ds.utils.DataSourceServiceUtil;
 import com.tapdata.tm.ds.utils.UriRootConvertUtils;
 import com.tapdata.tm.ds.vo.SupportListVo;
 import com.tapdata.tm.ds.vo.ValidateTableVo;
@@ -57,6 +56,8 @@
 import com.tapdata.tm.metadatainstance.vo.SourceTypeEnum;
 import com.tapdata.tm.modules.dto.ModulesDto;
 import com.tapdata.tm.modules.service.ModulesService;
+import com.tapdata.tm.report.dto.ConfigureSourceBatch;
+import com.tapdata.tm.report.service.UserDataReportService;
 import com.tapdata.tm.proxy.dto.SubscribeDto;
 import com.tapdata.tm.proxy.dto.SubscribeResponseDto;
 import com.tapdata.tm.proxy.service.impl.ProxyService;
@@ -64,7 +65,6 @@
 import com.tapdata.tm.task.service.LdpService;
 import com.tapdata.tm.task.service.LogCollectorService;
 import com.tapdata.tm.task.service.TaskService;
-import com.tapdata.tm.user.entity.User;
 import com.tapdata.tm.user.service.UserService;
 import com.tapdata.tm.utils.*;
 import com.tapdata.tm.worker.entity.Worker;
@@ -166,6 +166,8 @@ public class DataSourceServiceImpl extends DataSourceService{
 
 	@Autowired
     private AgentGroupService agentGroupService;
+    @Autowired
+    private UserDataReportService userDataReportService;
 
     public DataSourceServiceImpl(@NonNull DataSourceRepository repository) {
         super(repository);
@@ -183,6 +185,9 @@ public DataSourceConnectionDto add(DataSourceConnectionDto connectionDto, UserDe
         sendTestConnection(connectionDto, updateSchema, submit, userDetail);
         connectionDto.setConfig(null);
         defaultDataDirectoryService.addConnection(connectionDto, userDetail);
+        ConfigureSourceBatch configureSourceBatch = new ConfigureSourceBatch();
+        configureSourceBatch.setPdkId(connectionDto.getDefinitionPdkId());
+        userDataReportService.produceData(configureSourceBatch);
         return connectionDto;
     }
 
@@ -243,6 +248,8 @@ public DataSourceConnectionDto update(UserDetail user, DataSourceConnectionDto u
                 updateDto.setLastStatus(oldConnection.getStatus());
             }
         }
+        //Fix: TAP-2686, After manually specifying the agent and saving it, the specified agent information should not be automatically cleared
+        DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, updateDto);
         Map<String, Object> config = updateDto.getConfig();
         if (oldConnection != null) {
 
@@ -266,7 +273,6 @@ public DataSourceConnectionDto update(UserDetail user, DataSourceConnectionDto u
         }
 
         DataSourceEntity entity = convertToEntity(DataSourceEntity.class, updateDto);
-        entity.setAccessNodeProcessIdList(updateDto.getAccessNodeProcessIdList());
 
         Update update = repository.buildUpdateSet(entity, user);
 
@@ -648,7 +654,7 @@ private void checkMongoUri(DataSourceConnectionDto dto) {
         }
     }
 
-    private void hiddenMqPasswd(DataSourceConnectionDto item) {
+    protected void hiddenMqPasswd(DataSourceConnectionDto item) {
         if (item != null && !isAgentReq() && !Objects.isNull(item.getConfig())
                 && !item.getConfig().isEmpty()) {
             if (item.getConfig().containsKey("password")) {
@@ -663,17 +669,13 @@ private void hiddenMqPasswd(DataSourceConnectionDto item) {
                 String uri = (String) item.getConfig().get("uri");
                 ConnectionString connectionString = null;
                 try {
-                    connectionString = new ConnectionString(uri);
-                } catch (Exception e) {
-                    if (uri.startsWith("mongodb+srv:")) {
-                        try {
-                            connectionString = new ConnectionString(uri.replace("mongodb+srv:", "mongodb:"));
-                        } catch (Exception e1) {
-                            log.error("Parse connection string failed ({}) {}", uri, e.getMessage());
-                        }
-                    } else {
-                        log.error("Parse connection string failed ({}) {}", uri, e.getMessage());
+                    if(uri.startsWith("mongodb+srv:")){
+                        connectionString = new ConnectionString(uri.replace("mongodb+srv:", "mongodb:"));
+                    }else{
+                        connectionString = new ConnectionString(uri);
                     }
+                } catch (Exception e) {
+                    log.error("Parse connection string failed ({}) {}", uri, e.getMessage());
                 }
 
                 if (connectionString != null) {
diff --git a/manager/tm/src/main/java/com/tapdata/tm/ds/utils/DataSourceServiceUtil.java b/manager/tm/src/main/java/com/tapdata/tm/ds/utils/DataSourceServiceUtil.java
new file mode 100644
index 0000000000..e437e1b4d0
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/ds/utils/DataSourceServiceUtil.java
@@ -0,0 +1,24 @@
+package com.tapdata.tm.ds.utils;
+
+import com.tapdata.tm.commons.schema.DataSourceConnectionDto;
+
+public class DataSourceServiceUtil {
+    private DataSourceServiceUtil() {
+
+    }
+
+    public static void setAccessNodeInfoFromOldConnectionDto(DataSourceConnectionDto oldConnection, DataSourceConnectionDto update) {
+        if (null == oldConnection){
+            return;
+        }
+        if (null == update) {
+            return;
+        }
+        if (null == update.getAccessNodeType()) {
+            update.setAccessNodeType(oldConnection.getAccessNodeType());
+        }
+        if (null == update.getAccessNodeProcessId()) {
+            update.setAccessNodeProcessId(oldConnection.getAccessNodeProcessId());
+        }
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectController.java b/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectController.java
index 4933b6afd9..c9ee9a3922 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectController.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectController.java
@@ -2,25 +2,57 @@
 
 import com.alibaba.fastjson.JSON;
 import com.tapdata.tm.base.controller.BaseController;
-import com.tapdata.tm.base.dto.*;
+import com.tapdata.tm.base.dto.Field;
+import com.tapdata.tm.base.dto.Filter;
+import com.tapdata.tm.base.dto.Page;
+import com.tapdata.tm.base.dto.ResponseMessage;
+import com.tapdata.tm.base.dto.Where;
 import com.tapdata.tm.base.exception.BizException;
 import com.tapdata.tm.commons.schema.bean.PlatformInfo;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.commons.util.JsonUtil;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.inspect.constant.InspectStatusEnum;
 import com.tapdata.tm.inspect.dto.InspectDto;
-import com.tapdata.tm.inspect.service.InspectResultService;
 import com.tapdata.tm.inspect.service.InspectService;
+import com.tapdata.tm.inspect.service.InspectTaskService;
+import com.tapdata.tm.inspect.vo.InspectTaskVo;
+import com.tapdata.tm.permissions.DataPermissionHelper;
+import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
+import com.tapdata.tm.permissions.constants.DataPermissionDataTypeEnums;
+import com.tapdata.tm.permissions.constants.DataPermissionMenuEnums;
+import com.tapdata.tm.task.service.TaskService;
+import com.tapdata.tm.utils.Lists;
+import com.tapdata.tm.utils.MongoUtils;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.bson.types.ObjectId;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+import java.util.StringJoiner;
+import java.util.function.Supplier;
 
 
 /**
@@ -31,13 +63,62 @@
 @Slf4j
 @RestController
 @RequestMapping("/api/Inspects")
+@Setter(onMethod_ = {@Autowired})
 public class InspectController extends BaseController {
-
-    @Autowired
     private InspectService inspectService;
+    private InspectTaskService inspectTaskService;
+    private TaskService taskService;
 
-    @Autowired
-    private InspectResultService inspectResultService;
+    private <T> T dataPermissionUnAuth(DataPermissionActionEnums action, List<DataPermissionActionEnums> need) {
+        throw new BizException("insufficient.permissions",
+                needAction(DataPermissionDataTypeEnums.INSPECT, Lists.newArrayList(action)),
+                needAction(DataPermissionDataTypeEnums.INSPECT, need)
+                );
+    }
+
+    protected String needAction(DataPermissionDataTypeEnums dataTypeEnums, List<DataPermissionActionEnums> need) {
+        StringJoiner joiner = new StringJoiner(", ");
+        need.forEach(a ->
+            joiner.add(String.format("%s.%s", dataTypeEnums.getCollection().toLowerCase(), a.name().toLowerCase()))
+        );
+        return joiner.toString();
+    }
+
+    private <T> T dataPermissionCheckOfMenu(UserDetail userDetail,
+                                            DataPermissionActionEnums actionEnums,
+                                            List<DataPermissionActionEnums> need,
+                                            Supplier<T> supplier) {
+        return DataPermissionHelper.check(userDetail,
+                DataPermissionMenuEnums.INSPECT_TACK,
+                actionEnums,
+                DataPermissionDataTypeEnums.INSPECT,
+                null,
+                supplier,
+                () -> dataPermissionUnAuth(actionEnums, need));
+    }
+
+    private <T> T dataPermissionCheckOfId(HttpServletRequest request,
+                                          UserDetail userDetail,
+                                          ObjectId id,
+                                          DataPermissionActionEnums actionEnums,
+                                          List<DataPermissionActionEnums> need,
+                                          Supplier<T> supplier) {
+        id = Optional.ofNullable(DataPermissionHelper.signDecode(request, id.toHexString())).map(MongoUtils::toObjectId).orElse(id);
+        return DataPermissionHelper.checkOfQuery(
+                userDetail,
+                DataPermissionDataTypeEnums.INSPECT,
+                actionEnums,
+                inspectService.dataPermissionFindById(id, new Field()),
+                (dto) -> DataPermissionMenuEnums.INSPECT_TACK,
+                supplier,
+                () -> dataPermissionUnAuth(actionEnums, need)
+        );
+    }
+
+    @GetMapping("/{currentId}/parent-task-sign")
+    public ResponseMessage<String> dataPermissionTaskSign(@PathVariable String currentId, @RequestParam String parentId) {
+        return success(DataPermissionHelper.signEncode(currentId, parentId));
+    }
 
     /**
      * Create a new instance of the model and persist it into the data source
@@ -75,15 +156,20 @@ public ResponseMessage<InspectDto> save(@RequestBody InspectDto inspect) {
      * @return
      */
     @PatchMapping()
-    public ResponseMessage<InspectDto> updateById(@RequestBody InspectDto inspect) {
-        InspectDto inspectDto = inspectService.updateById(inspect.getId(), inspect, getLoginUser());
-        return success(inspect);
+    public ResponseMessage<InspectDto> updateById(HttpServletRequest request, @RequestBody InspectDto inspect) {
+        InspectDto resultTask = dataPermissionCheckOfId(request,
+                getLoginUser(),
+                inspect.getId(),
+                DataPermissionActionEnums.Edit,
+                Lists.newArrayList(DataPermissionActionEnums.Edit),
+                () -> inspectService.updateById(inspect.getId(), inspect, getLoginUser())
+        );
+        return success(resultTask);
     }
 
 
     /**
-     * @param filterJson
-     * 获取校验任务列表
+     * @param filterJson Get inspect task list
      * @return
      */
     @GetMapping
@@ -92,9 +178,40 @@ public ResponseMessage<Page<InspectDto>> find(@RequestParam(value = "filter", re
         if (filter == null) {
             filter = new Filter();
         }
-        return success(inspectService.list(filter, getLoginUser()));
+        Filter finalFilter = filter;
+        Supplier<Page<InspectDto>> supplier = () -> inspectService.list(finalFilter, getLoginUser());
+        Page<InspectDto> result = Optional.ofNullable(
+                DataPermissionHelper.check(getLoginUser(),
+                        DataPermissionMenuEnums.INSPECT_TACK,
+                        DataPermissionActionEnums.View,
+                        DataPermissionDataTypeEnums.INSPECT,
+                        null,
+                        supplier,
+                        () -> dataPermissionUnAuth(DataPermissionActionEnums.View, Lists.newArrayList(DataPermissionActionEnums.View)))
+        )
+                .orElseGet(() -> inspectService.list(finalFilter, getLoginUser()));
+        return success(result);
     }
 
+    /**
+     * Get inspect task details
+     *
+     * @return
+     */
+    @Operation(summary = "Find a model instance by {{id}} from the data source")
+    @GetMapping("find/{id}")
+    public ResponseMessage<InspectDto> findOneById(HttpServletRequest request, @PathVariable("id") String id) {
+        InspectDto inspectDto = dataPermissionCheckOfId(
+                request,
+                getLoginUser(),
+                MongoUtils.toObjectId(id),
+                DataPermissionActionEnums.View,
+                Lists.newArrayList(DataPermissionActionEnums.View),
+                () -> inspectService.findOne(Query.query(Criteria.where("_id").is(MongoUtils.toObjectId(id))))
+        );
+        inspectDto.setTaskDto(findTaskDto(inspectDto.getFlowId()));
+        return success(inspectDto);
+    }
 
     /**
      * 获取校验详情
@@ -105,7 +222,13 @@ public ResponseMessage<Page<InspectDto>> find(@RequestParam(value = "filter", re
     @GetMapping("findById")
     public ResponseMessage<InspectDto> findById(@RequestParam("filter") String filterStr) {
         Filter filter = parseFilter(filterStr);
-        InspectDto inspectDto = inspectService.findById(filter, getLoginUser());
+        InspectDto inspectDto = dataPermissionCheckOfMenu(
+                getLoginUser(),
+                DataPermissionActionEnums.View,
+                Lists.newArrayList(DataPermissionActionEnums.View),
+                () -> inspectService.findById(filter, getLoginUser())
+        );
+        inspectDto.setTaskDto(findTaskDto(inspectDto.getFlowId()));
         return success(inspectDto);
     }
 
@@ -118,8 +241,16 @@ public ResponseMessage<InspectDto> findById(@RequestParam("filter") String filte
      */
     @Operation(summary = "Delete a model instance by {{id}} from the data source")
     @DeleteMapping("{id}")
-    public ResponseMessage<Map<String, Long>> delete(@PathVariable("id") String id) {
-        return success(inspectService.delete(id, getLoginUser()));
+    public ResponseMessage<Map<String, Long>> delete(HttpServletRequest request, @PathVariable("id") String id) {
+        Map<String, Long> deleteResult = dataPermissionCheckOfId(
+                request,
+                getLoginUser(),
+                MongoUtils.toObjectId(id),
+                DataPermissionActionEnums.Delete,
+                Lists.newArrayList(DataPermissionActionEnums.Delete),
+                () -> inspectService.delete(id, getLoginUser())
+        );
+        return success(deleteResult);
     }
 
 
@@ -140,28 +271,99 @@ public ResponseMessage<InspectDto> findOne(
         if (filter == null) {
             filter = new Filter();
         }
-        return success(inspectService.findOne(filter, getLoginUser()));
+        Filter finalFilter = filter;
+        InspectDto inspectDto = dataPermissionCheckOfMenu(
+                getLoginUser(),
+                DataPermissionActionEnums.View,
+                Lists.newArrayList(DataPermissionActionEnums.View),
+                () -> inspectService.findOne(finalFilter, getLoginUser())
+        );
+        inspectDto.setTaskDto(findTaskDto(inspectDto.getFlowId()));
+        return success(inspectDto);
+    }
+
+    protected InspectTaskVo findTaskDto(String taskId) {
+        ObjectId id = MongoUtils.toObjectId(taskId);
+        InspectTaskVo vo = new InspectTaskVo();
+        if (null != id) {
+            Query query = Query.query(Criteria.where("_id").is(id));
+            query.fields().include("id", "name", "syncType", "dag");
+            TaskDto dto = Optional.ofNullable(taskService.findOne(query)).orElse(new TaskDto());
+            vo.setDag(dto.getDag());
+            vo.setId(dto.getId());
+            vo.setName(dto.getName());
+            vo.setSyncType(dto.getSyncType());
+        }
+        return vo;
     }
 
 
     /**
-     * 页面点击  和engine更新inspect的时候，都会调用这个方法
+     * 页面点击 更新inspect的时候，会调用这个方法
      *
      * @param whereJson
      * @param inspect
      * @return
      */
-    @Operation(summary = "页面点击  和 engine 更新inspect的时候，都会调用这个方法")
-    @PostMapping("update")
-    public ResponseMessage updateByWhere(@RequestParam("where") String whereJson, @RequestBody InspectDto inspect) throws Exception {
-        log.info("InspectController--updateByWhere。whereJson：{}， InspectDto：{} ", whereJson, JSON.toJSONString(inspect));
+    @Operation(summary = "页面点击 更新inspect的时候，会调用这个方法")
+    @PostMapping("execute")
+    public ResponseMessage<InspectDto> updateByWhere(HttpServletRequest request, @RequestParam("where") String whereJson, @RequestBody InspectDto inspect) {
+        log.info("Inspect Controller -- do execute -  where json：{}, inspect：{} ", whereJson, JSON.toJSONString(inspect));
         whereJson = whereJson.replace("\"_id\"", "\"id\"");
         Where where = parseWhere(whereJson);
-        InspectDto inspectDto = inspectService.updateInspectByWhere(where, inspect, getLoginUser());
+        ObjectId id = Optional.ofNullable(inspect.getId()).orElse(MongoUtils.toObjectId(String.valueOf(where.get("id"))));
+        String status = inspect.getStatus();
+        DataPermissionActionEnums verifyAction = null;
+        List<DataPermissionActionEnums> needActionEnums = Lists.newArrayList(DataPermissionActionEnums.Edit);
+        //按照操作类型来确认需要校验的权限
+        if (InspectStatusEnum.SCHEDULING.getValue().equals(status)) {
+            verifyAction = DataPermissionActionEnums.Start;
+            needActionEnums.add(verifyAction);
+        } else if (InspectStatusEnum.STOPPING.getValue().equals(status)) {
+            verifyAction = DataPermissionActionEnums.Stop;
+            needActionEnums.add(verifyAction);
+        }
+        //检查是否有编辑权限
+        dataPermissionCheckOfId(
+                request,
+                getLoginUser(),
+                id,
+                DataPermissionActionEnums.Edit,
+                needActionEnums,
+                () -> inspect);
+
+        InspectDto inspectDto;
+        if (null != verifyAction) {
+            inspectDto = dataPermissionCheckOfId(
+                    request,
+                    getLoginUser(),
+                    id,
+                    verifyAction,
+                    needActionEnums,
+                    () -> inspectService.doExecuteInspect(where, inspect, getLoginUser())
+            );
+        } else {
+            inspectDto = inspectService.doExecuteInspect(where, inspect, getLoginUser());
+        }
         return success(inspectDto);
     }
 
 
+    @Operation(summary = "engine 更新inspect的时候，都会调用这个方法")
+    @PostMapping("update")
+    public ResponseMessage<InspectDto> updateByWhere(@RequestParam("where") String whereJson, @RequestBody InspectDto inspect) {
+        log.info("engine 调用了 updateByWhere.  where :{}  InspectDto:{} ", whereJson, JsonUtil.toJson(inspect));
+        whereJson = whereJson.replace("\"_id\"", "\"id\"");
+        Where where = parseWhere(whereJson);
+        return success(inspectService.doExecuteInspect(where, inspect, getLoginUser()));
+    }
+
+    @Operation(summary = "详情页查询任务列表的时候，都会调用这个方法")
+    @GetMapping("task-list")
+    public ResponseMessage<List<TaskDto>> getTaskDtoList() {
+        return success(inspectTaskService.findTaskList(getLoginUser()));
+    }
+
     /**
      * 给engin调用，更新inspect状态的
      * engin  调用该方法，result  总是返回空ss字符串，导致inspect表更新总是不正确，因此需要另作调整
diff --git a/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectResultController.java b/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectResultController.java
index 27e58c89e8..79988d043e 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectResultController.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/inspect/controller/InspectResultController.java
@@ -1,26 +1,49 @@
 package com.tapdata.tm.inspect.controller;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
 import com.alibaba.fastjson.JSON;
-import com.tapdata.tm.commons.util.JsonUtil;
 import com.tapdata.tm.base.controller.BaseController;
-import com.tapdata.tm.base.dto.*;
+import com.tapdata.tm.base.dto.Filter;
+import com.tapdata.tm.base.dto.Page;
+import com.tapdata.tm.base.dto.ResponseMessage;
+import com.tapdata.tm.base.dto.Where;
+import com.tapdata.tm.base.exception.BizException;
+import com.tapdata.tm.commons.util.JsonUtil;
 import com.tapdata.tm.inspect.bean.GroupByFilter;
 import com.tapdata.tm.inspect.dto.InspectResultDto;
 import com.tapdata.tm.inspect.param.SaveInspectResultParam;
 import com.tapdata.tm.inspect.param.UpdateInspectResultParam;
 import com.tapdata.tm.inspect.service.InspectResultService;
+import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
+import com.tapdata.tm.permissions.constants.DataPermissionDataTypeEnums;
+import com.tapdata.tm.permissions.service.DataPermissionService;
 import com.tapdata.tm.utils.MongoUtils;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
+import org.bson.types.ObjectId;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
 
 
 /**
@@ -31,12 +54,10 @@
 @RestController
 @RequestMapping("/api/InspectResults")
 @Slf4j
+@Setter(onMethod_ = {@Autowired})
 public class InspectResultController extends BaseController {
-
-    @Autowired
     private InspectResultService inspectResultService;
-
-
+    private DataPermissionService dataPermissionService;
     /**
      *  engine 通过这个接口 保存校验结果，，这时候会把inspect的内容整个传过来，保存到数据库中。
      *  因为校验历史，需要看到每一次校验的内容，所以InspectReuslt 里面还得保存Inspect的具体数据，不能去掉
@@ -67,6 +88,24 @@ public ResponseMessage<InspectResultDto> update(@RequestBody InspectResultDto in
         return success(save);
     }
 
+    protected void checkInspect(String inspectId, DataPermissionActionEnums... enums) {
+        Set<String> dataActions = dataPermissionService.findDataActions(getLoginUser(), DataPermissionDataTypeEnums.INSPECT, MongoUtils.toObjectId(inspectId));
+        if (CollUtil.isEmpty(dataActions)) {
+            throw new BizException("insufficient.permissions", "", needAction(DataPermissionDataTypeEnums.INSPECT, enums));
+        }
+        for (DataPermissionActionEnums dataAction : enums) {
+            if (!dataActions.contains(dataAction.name())) {
+                throw new BizException("insufficient.permissions", needAction(DataPermissionDataTypeEnums.INSPECT, dataAction), needAction(DataPermissionDataTypeEnums.INSPECT, enums));
+            }
+        }
+    }
+    protected String needAction(DataPermissionDataTypeEnums dataTypeEnums, DataPermissionActionEnums... need) {
+        StringJoiner joiner = new StringJoiner(", ");
+        for (DataPermissionActionEnums a : need) {
+            joiner.add(String.format("%s.%s", dataTypeEnums.getCollection().toLowerCase(), a.name().toLowerCase()));
+        }
+        return joiner.toString();
+    }
 
     /**
      * 查询校验结果列表
@@ -90,6 +129,15 @@ public ResponseMessage<Page<InspectResultDto>> find(
                 inspectGroupByFirstCheckId = filter.getInspectGroupByFirstCheckId();
             }
         }
+        String inspectId = String.valueOf(filter.getWhere().get("inspect_id"));
+        if (null == MongoUtils.toObjectId(inspectId)) {
+            InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(MongoUtils.toObjectId(String.valueOf(filter.getWhere().get("id"))))));
+            if (null == one || null == one.getInspect_id()) {
+                throw new BizException("inspect.result.not.exists", filter.getWhere().get("id"));
+            }
+            inspectId = one.getInspect_id();
+        }
+        checkInspect(inspectId, DataPermissionActionEnums.View);
         return success(inspectResultService.find(filter, getLoginUser(), inspectGroupByFirstCheckId));
     }
 
@@ -101,6 +149,12 @@ public ResponseMessage<Page<InspectResultDto>> find(
     @Operation(summary = "Replace an existing model instance or insert a new one into the data source")
     @PutMapping
     public ResponseMessage<InspectResultDto> put(@RequestBody InspectResultDto inspectResult) {
+        InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(inspectResult.getId())));
+        if (null == one || null == one.getInspect_id()) {
+            throw new BizException("inspect.result.not.exists", inspectResult.getId());
+        }
+        String inspectId = one.getInspect_id();
+        checkInspect(inspectId, DataPermissionActionEnums.Edit);
         return success(inspectResultService.replaceOrInsert(inspectResult, getLoginUser()));
     }
 
@@ -115,6 +169,12 @@ public ResponseMessage<InspectResultDto> put(@RequestBody InspectResultDto inspe
     @PatchMapping("{id}")
     public ResponseMessage<InspectResultDto> updateById(@PathVariable("id") String id, @RequestBody InspectResultDto inspectResult) {
         log.info("InspectResultController--updateById。 inspectResult：{} ",  JSON.toJSONString(inspectResult));
+        InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(inspectResult.getId())));
+        if (null == one || null == one.getInspect_id()) {
+            throw new BizException("inspect.result.not.exists", id);
+        }
+        String inspectId = one.getInspect_id();
+        checkInspect(inspectId, DataPermissionActionEnums.Edit);
         inspectResult.setId(MongoUtils.toObjectId(id));
         return success(inspectResultService.save(inspectResult, getLoginUser()));
     }
@@ -129,7 +189,13 @@ public ResponseMessage<InspectResultDto> updateById(@PathVariable("id") String i
     @GetMapping("findById")
     public ResponseMessage<InspectResultDto> findById(@RequestParam("filter") String filter) {
         Filter f = parseFilter(filter);
-        return success(inspectResultService.findById(f,getLoginUser()));
+        InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(f.getWhere().get("id"))));
+        if (null == one || null == one.getInspect_id()) {
+            throw new BizException("inspect.result.not.exists", f.getWhere().get("id"));
+        }
+        String inspectId = one.getInspect_id();
+        checkInspect(inspectId, DataPermissionActionEnums.View);
+        return success(inspectResultService.findById(f, getLoginUser()));
     }
 
     /**
@@ -140,6 +206,12 @@ public ResponseMessage<InspectResultDto> findById(@RequestParam("filter") String
     @Operation(summary = "Delete a model instance by {{id}} from the data source")
     @DeleteMapping("{id}")
     public ResponseMessage<Void> delete(@PathVariable("id") String id) {
+        InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(id)));
+        if (null == one || null == one.getInspect_id()) {
+            throw new BizException("inspect.result.not.exists", id);
+        }
+        String inspectId = one.getInspect_id();
+        checkInspect(inspectId, DataPermissionActionEnums.Delete);
         inspectResultService.deleteById(MongoUtils.toObjectId(id), getLoginUser());
         return success();
     }
@@ -161,6 +233,15 @@ public ResponseMessage<InspectResultDto> findOne(
         if (filter == null) {
             filter = new Filter();
         }
+        Object id = filter.getWhere().get("id");
+        if (null != id) {
+            InspectResultDto one = inspectResultService.findOne(Query.query(Criteria.where("_id").is(id)));
+            if (null == one || null == one.getInspect_id()) {
+                throw new BizException("inspect.result.not.exists", id);
+            }
+            String inspectId = one.getInspect_id();
+            checkInspect(inspectId, DataPermissionActionEnums.Delete);
+        }
         return success(inspectResultService.findOne(filter, getLoginUser()));
     }
 
@@ -175,6 +256,8 @@ public ResponseMessage<Map<String, Long>> updateByWhere(@RequestParam("where") S
         log.info("InspectResultController--updateByWhere 。whereJson：{}， InspectDto：{} ", whereJson, JSON.toJSONString(updateInspectResultParam));
         Where where = parseWhere(whereJson);
         InspectResultDto inspectResultDto= BeanUtil.copyProperties(updateInspectResultParam,InspectResultDto.class);
+        String inspectId = inspectResultDto.getInspect_id();
+        checkInspect(inspectId, DataPermissionActionEnums.Edit);
         long count = inspectResultService.updateByWhere(where, inspectResultDto, getLoginUser());
         HashMap<String, Long> countValue = new HashMap<>();
         countValue.put("count", count);
diff --git a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/controller/MetadataDefinitionController.java b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/controller/MetadataDefinitionController.java
index d4c405377e..9bd9ba5ec3 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/controller/MetadataDefinitionController.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/controller/MetadataDefinitionController.java
@@ -1,5 +1,6 @@
 package com.tapdata.tm.metadatadefinition.controller;
 
+import com.tapdata.tm.Settings.service.SettingsService;
 import com.tapdata.tm.base.controller.BaseController;
 import com.tapdata.tm.base.dto.*;
 import com.tapdata.tm.metadatadefinition.dto.MetadataDefinitionDto;
@@ -11,6 +12,9 @@
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import io.tapdata.utils.AppType;
+import lombok.Setter;
+import org.bson.Document;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,12 +29,11 @@
  */
 @Tag(name = "MetadataDefinition", description = "MetadataDefinition相关接口")
 @RestController
-@RequestMapping(value = {"/api/MetadataDefinition","/api/MetadataDefinitions"})
+@RequestMapping(value = {"/api/MetadataDefinition","/api/MetadataDefinitions", "/api/metadata-definitions"})
+@Setter(onMethod_ = {@Autowired})
 public class MetadataDefinitionController extends BaseController {
-
-    @Autowired
     private MetadataDefinitionService metadataDefinitionService;
-
+    private SettingsService settingsService;
     /**
      * 创建分类
      * @param metadataDefinition
@@ -86,6 +89,7 @@ public ResponseMessage<Page<MetadataDefinitionDto>> find(
      */
     @Operation(summary = "Find all instances of the model matched by filter from the data source")
     @GetMapping("and/child_account")
+    @Deprecated
     public ResponseMessage<Page<MetadataDefinitionDto>> findAndChildAccount(
             @Parameter(in = ParameterIn.QUERY,
                     description = "Filter defining fields, where, sort, skip, and limit - must be a JSON-encoded string (`{\"where\":{\"something\":\"value\"},\"fields\":{\"something\":true|false},\"sort\": [\"name desc\"],\"page\":1,\"size\":20}`)."
@@ -98,7 +102,17 @@ public ResponseMessage<Page<MetadataDefinitionDto>> findAndChildAccount(
 
         //数据目录不需要分页
         filter.setLimit(10000);
-        return success(metadataDefinitionService.findAndChildAccount(filter, getLoginUser()));
+        return success(metadataDefinitionService.findAndChildAccount(filter, settingsService.isCloud(), getLoginUser()));
+    }
+
+    @Operation(summary = "Find all MetadataDefinition of the model matched by filter from the data source")
+    @GetMapping("all-tag/{type}")
+    public ResponseMessage<Page<MetadataDefinitionDto>> findAllMetadataDefinition(@PathVariable("type") String type) {
+        Filter filter = new Filter();
+        Where where = Where.where("or", Lists.newArrayList(new Document().append("item_type", type)));
+        filter.setWhere(where);
+        filter.setLimit(10000);
+        return success(metadataDefinitionService.findAndChildAccount(filter, !settingsService.isCloud(), getLoginUser()));
     }
 
     /**
diff --git a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/repository/MetadataDefinitionRepository.java b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/repository/MetadataDefinitionRepository.java
index 2a39c3964f..b5868c89b6 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/repository/MetadataDefinitionRepository.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/repository/MetadataDefinitionRepository.java
@@ -14,7 +14,9 @@
 
 import java.util.List;
 
-import static com.tapdata.tm.utils.MongoUtils.*;
+import static com.tapdata.tm.utils.MongoUtils.applyField;
+import static com.tapdata.tm.utils.MongoUtils.applySort;
+import static com.tapdata.tm.utils.MongoUtils.buildCriteria;
 
 /**
  * @Author:
@@ -48,7 +50,7 @@ public Query applyUserDetail(Query query, UserDetail userDetail) {
         return query;
     }
 
-    public List<MetadataDefinitionEntity> findAllAndChildAccount(Filter filter, UserDetail userDetail) {
+    public List<MetadataDefinitionEntity> findAllAndChildAccount(Filter filter, boolean searchAll, UserDetail userDetail) {
 
         if (filter == null)
             filter = new Filter();
@@ -67,7 +69,9 @@ public List<MetadataDefinitionEntity> findAllAndChildAccount(Filter filter, User
         applyField(query, filter.getFields());
         applySort(query, filter.getSort());
 
-        super.applyUserDetail(query, userDetail);
+        if (!searchAll) {
+            super.applyUserDetail(query, userDetail);
+        }
 
         return mongoOperations.find(query, entityInformation.getJavaType(), entityInformation.getCollectionName());
     }
diff --git a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/service/MetadataDefinitionService.java b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/service/MetadataDefinitionService.java
index 27674df0d1..cc63e38e8d 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/service/MetadataDefinitionService.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/metadatadefinition/service/MetadataDefinitionService.java
@@ -11,6 +11,7 @@
 import com.tapdata.tm.commons.base.dto.BaseDto;
 import com.tapdata.tm.commons.schema.DataSourceDefinitionDto;
 import com.tapdata.tm.commons.schema.Tag;
+import com.tapdata.tm.config.security.UserDetail;
 import com.tapdata.tm.discovery.service.DiscoveryService;
 import com.tapdata.tm.ds.entity.DataSourceEntity;
 import com.tapdata.tm.ds.service.impl.DataSourceDefinitionService;
@@ -18,7 +19,6 @@
 import com.tapdata.tm.metadatadefinition.entity.MetadataDefinitionEntity;
 import com.tapdata.tm.metadatadefinition.param.BatchUpdateParam;
 import com.tapdata.tm.metadatadefinition.repository.MetadataDefinitionRepository;
-import com.tapdata.tm.config.security.UserDetail;
 import com.tapdata.tm.metadatainstance.service.MetadataInstancesService;
 import com.tapdata.tm.modules.entity.ModulesEntity;
 import com.tapdata.tm.task.constant.LdpDirEnum;
@@ -37,7 +37,13 @@
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.stereotype.Service;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -294,14 +300,14 @@ private List<MetadataDefinitionDto> findChild(List<MetadataDefinitionDto> metada
     }
 
 
-    public Page<MetadataDefinitionDto> findAndChildAccount(Filter filter, UserDetail user) {
+    public Page<MetadataDefinitionDto> findAndChildAccount(Filter filter, boolean searchAll, UserDetail user) {
         if (filter == null) {
             filter = new Filter();
         }
 
-        List<MetadataDefinitionEntity> entityList = repository.findAllAndChildAccount(filter, user);
+        List<MetadataDefinitionEntity> entityList = repository.findAllAndChildAccount(filter, searchAll, user);
 
-        long total = repository.count(filter.getWhere(), user);
+        long total = searchAll ?  repository.count(filter.getWhere()) : repository.count(filter.getWhere(), user);
 
         List<MetadataDefinitionDto> items = convertToDto(entityList, dtoClass);
 
diff --git a/manager/tm/src/main/java/com/tapdata/tm/monitor/service/MeasurementServiceV2Impl.java b/manager/tm/src/main/java/com/tapdata/tm/monitor/service/MeasurementServiceV2Impl.java
index 81ca77bba3..16de223506 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/monitor/service/MeasurementServiceV2Impl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/monitor/service/MeasurementServiceV2Impl.java
@@ -40,7 +40,17 @@
 import org.springframework.data.mongodb.MongoExpression;
 import org.springframework.data.mongodb.core.BulkOperations;
 import org.springframework.data.mongodb.core.MongoTemplate;
-import org.springframework.data.mongodb.core.aggregation.*;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationExpression;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.aggregation.Fields;
+import org.springframework.data.mongodb.core.aggregation.GroupOperation;
+import org.springframework.data.mongodb.core.aggregation.LimitOperation;
+import org.springframework.data.mongodb.core.aggregation.MatchOperation;
+import org.springframework.data.mongodb.core.aggregation.SortOperation;
+import org.springframework.data.mongodb.core.aggregation.UnwindOperation;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
@@ -48,7 +58,16 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicLong;
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/dto/ConfigureSourceBatch.java b/manager/tm/src/main/java/com/tapdata/tm/report/dto/ConfigureSourceBatch.java
new file mode 100644
index 0000000000..05aee7be7f
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/dto/ConfigureSourceBatch.java
@@ -0,0 +1,17 @@
+package com.tapdata.tm.report.dto;
+
+import lombok.Data;
+
+@Data
+public class ConfigureSourceBatch {
+    private String machineId;
+
+    private String pdkId;
+    @Override
+    public String toString() {
+        return "{" +
+                "\"machineId\": \"" + machineId + "\"," +
+                "\"pdkId\": \""+ pdkId + "\""+
+                "}";
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunDaysBatch.java b/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunDaysBatch.java
new file mode 100644
index 0000000000..3fca02bc81
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunDaysBatch.java
@@ -0,0 +1,14 @@
+package com.tapdata.tm.report.dto;
+
+import lombok.Data;
+
+@Data
+public class RunDaysBatch {
+    private String machineId;
+    @Override
+    public String toString() {
+        return "{" +
+                "\"machineId\": \"" + machineId + "\"" +
+                "}";
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunsNumBatch.java b/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunsNumBatch.java
new file mode 100644
index 0000000000..6981fa57dc
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/dto/RunsNumBatch.java
@@ -0,0 +1,17 @@
+package com.tapdata.tm.report.dto;
+
+import lombok.Data;
+
+@Data
+public class RunsNumBatch {
+    private String machineId;
+    private long timestamp;
+
+    @Override
+    public String toString() {
+        return "{" +
+                "\"machineId\": \"" + machineId + "\"," +
+                "\"timestamp\": \""+ timestamp + "\""+
+                "}";
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/dto/TasksNumBatch.java b/manager/tm/src/main/java/com/tapdata/tm/report/dto/TasksNumBatch.java
new file mode 100644
index 0000000000..1c84f2686d
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/dto/TasksNumBatch.java
@@ -0,0 +1,16 @@
+package com.tapdata.tm.report.dto;
+
+import lombok.Data;
+
+@Data
+public class TasksNumBatch {
+    private String machineId;
+    private String taskType;
+    @Override
+    public String toString() {
+        return "{" +
+                "\"machineId\": \"" + machineId + "\"," +
+                "\"taskType\": \""+ taskType + "\""+
+                "}";
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/dto/UniqueInstallBatch.java b/manager/tm/src/main/java/com/tapdata/tm/report/dto/UniqueInstallBatch.java
new file mode 100644
index 0000000000..41a037fb35
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/dto/UniqueInstallBatch.java
@@ -0,0 +1,16 @@
+package com.tapdata.tm.report.dto;
+
+import lombok.Data;
+
+@Data
+public class UniqueInstallBatch {
+    private String machineId;
+    private long timestamp;
+    @Override
+    public String toString() {
+        return "{" +
+                "\"machineId\": \"" + machineId + "\"," +
+                "\"timestamp\": \""+ timestamp + "\""+
+                "}";
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/schedule/RunDaysSchedule.java b/manager/tm/src/main/java/com/tapdata/tm/report/schedule/RunDaysSchedule.java
new file mode 100644
index 0000000000..5349fae717
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/schedule/RunDaysSchedule.java
@@ -0,0 +1,21 @@
+package com.tapdata.tm.report.schedule;
+
+import com.tapdata.tm.report.dto.RunDaysBatch;
+import com.tapdata.tm.report.service.UserDataReportService;
+
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+@Setter(onMethod_ = {@Autowired})
+public class RunDaysSchedule {
+    private UserDataReportService userDataReportService;
+
+    @Scheduled(cron = "0 0 * * * ?")
+    public void schedule() {
+        Thread.currentThread().setName(getClass().getSimpleName() + "-schedule");
+        userDataReportService.produceData(new RunDaysBatch());
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/service/UserDataReportService.java b/manager/tm/src/main/java/com/tapdata/tm/report/service/UserDataReportService.java
new file mode 100644
index 0000000000..6d50475506
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/service/UserDataReportService.java
@@ -0,0 +1,107 @@
+package com.tapdata.tm.report.service;
+
+import com.tapdata.tm.report.dto.*;
+import com.tapdata.tm.report.service.platform.ReportPlatform;
+import com.tapdata.tm.report.util.UserDataReportUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.concurrent.*;
+
+@Service
+@Slf4j
+public class UserDataReportService {
+    public static final String CONFIGURE_DATASOURCE_EVENT = "configure_datasource_event";
+    public static final String RUNS_NUM_EVENT = "runs_num_event";
+    public static final String RUN_DAYS_EVENT = "run_days_event";
+    public static final String UNIQUE_INSTALL_EVENT = "unique_install_event";
+    public static final String TASKS_NUM_EVENT = "tasks_num_event";
+
+    private static String machineId = UserDataReportUtils.generateMachineId();
+
+    @Value("${report.data.oss}")
+    private boolean acceptReportData;
+    protected static LinkedBlockingQueue reportQueue = new LinkedBlockingQueue(100);
+    @Autowired
+    private ReportPlatform reportPlatform;
+
+    @PostConstruct
+    protected void initReportDataThread(){
+        if (!acceptReportData) return;
+        ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
+        CompletableFuture.runAsync(() -> {
+            Thread.currentThread().setName("report-user-data-thread");
+            Object reportData;
+            while (!Thread.currentThread().isInterrupted()){
+                try {
+                    reportData = reportQueue.poll(1, TimeUnit.SECONDS);
+                    if (null == reportData) continue;
+                    consumeData(reportData);
+                }catch (InterruptedException e){
+                    Thread.currentThread().interrupt();
+                } catch (Exception e){
+                    log.info("report event poll failed", e);
+                }
+            }
+        }, executorService);
+    }
+    protected void consumeData(Object reportData) {
+        if (reportData instanceof RunsNumBatch){
+            runsNum((RunsNumBatch) reportData);
+        } else if (reportData instanceof RunDaysBatch){
+            runDays((RunDaysBatch) reportData);
+        } else if (reportData instanceof UniqueInstallBatch){
+            uniqueInstall((UniqueInstallBatch) reportData);
+        } else if (reportData instanceof ConfigureSourceBatch){
+            configureDatasource((ConfigureSourceBatch) reportData);
+        } else if (reportData instanceof TasksNumBatch){
+            tasksNum((TasksNumBatch) reportData);
+        }
+    }
+
+    public void produceData(Object object){
+        if (!acceptReportData) return;
+        try {
+            reportQueue.offer(object, 100, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    protected void runsNum(RunsNumBatch runsNumBatch) {
+        if (!acceptReportData) return;
+        runsNumBatch.setMachineId(machineId);
+        reportPlatform.sendRequest(RUNS_NUM_EVENT, runsNumBatch.toString());
+    }
+    protected void runDays(RunDaysBatch runDaysBatch) {
+        if (!acceptReportData) return;
+        runDaysBatch.setMachineId(machineId);
+        reportPlatform.sendRequest(RUN_DAYS_EVENT, runDaysBatch.toString());
+    }
+    protected void uniqueInstall(UniqueInstallBatch uniqueInstallBatch) {
+        if (!acceptReportData) return;
+        uniqueInstallBatch.setMachineId(machineId);
+        reportPlatform.sendRequest(UNIQUE_INSTALL_EVENT, uniqueInstallBatch.toString());
+    }
+    protected void configureDatasource(ConfigureSourceBatch configureSourceBatch) {
+        if (!acceptReportData) return;
+        configureSourceBatch.setMachineId(machineId);
+        reportPlatform.sendRequest(CONFIGURE_DATASOURCE_EVENT, configureSourceBatch.toString());
+    }
+    protected void tasksNum(TasksNumBatch tasksNumBatch) {
+        if (!acceptReportData) return;
+        tasksNumBatch.setMachineId(machineId);
+        reportPlatform.sendRequest(TASKS_NUM_EVENT, tasksNumBatch.toString());
+    }
+
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatform.java b/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatform.java
new file mode 100644
index 0000000000..309ba2a7e9
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatform.java
@@ -0,0 +1,78 @@
+package com.tapdata.tm.report.service.platform;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.HttpRequestRetryHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+public class GoogleAnalyticsPlatform implements ReportPlatform{
+    public static final String CONNECT_TIMEOUT = System.getenv().getOrDefault("GOOGLE_CLIENT_CONNECT_TIMEOUT","5000");
+    public static final String SOCKET_TIMEOUT = System.getenv().getOrDefault("GOOGLE_CLIENT_SOCKET_TIMEOUT","5000");
+    public static final String CONNECTION_REQUEST_TIMEOUT = System.getenv().getOrDefault("GOOGLE_CLIENT_CONNECTION_REQUEST_TIMEOUT","5000");
+    @Value("${report.url.measurementId}")
+    private String measurementId;
+    @Value("${report.url.apiSecret}")
+    private String apiSecret;
+    @Value("${report.url.clientId}")
+    private String clientId;
+    private final CloseableHttpClient client;
+
+    protected CloseableHttpClient getClient() {
+        return client;
+    }
+
+    public GoogleAnalyticsPlatform(){
+        RequestConfig requestConfig = RequestConfig.custom()
+                .setConnectTimeout(processParamIfNeed(CONNECT_TIMEOUT))
+                .setSocketTimeout(processParamIfNeed(SOCKET_TIMEOUT))
+                .setConnectionRequestTimeout(processParamIfNeed(CONNECTION_REQUEST_TIMEOUT))
+                .build();
+
+        HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(3, true);
+        client = HttpClientBuilder.create()
+                .setDefaultRequestConfig(requestConfig)
+                .evictExpiredConnections()
+                .setRetryHandler(retryHandler)
+                .build();
+    }
+
+    @Override
+    public void sendRequest(String eventName, String params) {
+        String url = "https://www.google-analytics.com/mp/collect?measurement_id=" + measurementId + "&api_secret=" + apiSecret;
+
+        // build JSON data
+        String jsonBody = "{\n" +
+                "    \"client_id\": \"%s\",\n" +
+                "    \"events\": [{\n" +
+                "        \"name\": \"%s\",\n" +
+                "        \"params\": %s\n" +
+                "    }]\n" +
+                "}";
+        try {
+            jsonBody = String.format(jsonBody, clientId, eventName, params);
+            HttpPost httpPost = new HttpPost(url);
+            StringEntity entity = new StringEntity(jsonBody);
+            httpPost.setEntity(entity);
+            httpPost.setHeader("Content-Type", "application/json");
+            client.execute(httpPost);
+        } catch (Exception e) {
+            log.info("report user data failed", e);
+        }
+    }
+    protected int processParamIfNeed(String value){
+        int param = 5000;
+        try {
+            return Integer.parseInt(value);
+        }catch (Exception e){
+            return param;
+        }
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/ReportPlatform.java b/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/ReportPlatform.java
new file mode 100644
index 0000000000..53413aa4d1
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/service/platform/ReportPlatform.java
@@ -0,0 +1,5 @@
+package com.tapdata.tm.report.service.platform;
+
+public interface ReportPlatform {
+    void sendRequest(String eventName, String params);
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/report/util/UserDataReportUtils.java b/manager/tm/src/main/java/com/tapdata/tm/report/util/UserDataReportUtils.java
new file mode 100644
index 0000000000..8e81b1e37b
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/report/util/UserDataReportUtils.java
@@ -0,0 +1,42 @@
+package com.tapdata.tm.report.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+@Slf4j
+public class UserDataReportUtils {
+
+    public static String PARAM_MAX_LENGTH = System.getenv().getOrDefault("PRELOAD_SCHEMA_WAIT_TIME","100");
+    private UserDataReportUtils(){
+    }
+
+    public static String generateMachineId() {
+        List<String> machineIdList = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface networkInterface = networkInterfaces.nextElement();
+                byte[] mac = networkInterface.getHardwareAddress();
+                if (mac != null) {
+                    StringBuilder macAddress = new StringBuilder();
+                    for (int i = 0; i < mac.length; i++) {
+                        macAddress.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
+                    }
+                    machineIdList.add(macAddress.toString());
+                }
+            }
+        } catch (SocketException e) {
+            log.error("get mac address failed", e);
+        }
+        String machineId = machineIdList.toString();
+        int lengthLimit = Integer.parseInt(PARAM_MAX_LENGTH);
+        if (null != machineId && machineId.length() > lengthLimit){
+            machineId = machineId.substring(0,lengthLimit);
+        }
+        return machineId;
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/main/java/com/tapdata/tm/schedule/ChartSchedule.java b/manager/tm/src/main/java/com/tapdata/tm/schedule/ChartSchedule.java
index 768f9836a2..cd7380ccef 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/schedule/ChartSchedule.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/schedule/ChartSchedule.java
@@ -2,7 +2,7 @@
 
 import com.tapdata.tm.config.security.UserDetail;
 import com.tapdata.tm.task.bean.Chart6Vo;
-import com.tapdata.tm.task.service.TaskService;
+import com.tapdata.tm.task.service.chart.ChartViewService;
 import com.tapdata.tm.user.service.UserService;
 import lombok.Setter;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -16,11 +16,9 @@
 @Component
 @Setter(onMethod_ = {@Autowired})
 public class ChartSchedule {
-
-
     public static Map<String, Chart6Vo> cache = new HashMap<>();
 
-    private TaskService taskService;
+    private ChartViewService chartViewService;
 
     private UserService userService;
 
@@ -28,13 +26,13 @@ public class ChartSchedule {
     public void chart6() {
         List<UserDetail> userDetails = userService.loadAllUser();
         for (UserDetail userDetail : userDetails) {
-            Chart6Vo chart6Vo = taskService.chart6(userDetail);
+            Chart6Vo chart6Vo = chartViewService.transmissionOverviewChartData(userDetail);
             put(userDetail.getUserId(), chart6Vo);
         }
     }
 
 
-    public static void  put(String key, Chart6Vo chart6Vo) {
+    public static void put(String key, Chart6Vo chart6Vo) {
         if (chart6Vo != null && !chart6Vo.empty()) {
             cache.put(key, chart6Vo);
         }
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/controller/TaskController.java b/manager/tm/src/main/java/com/tapdata/tm/task/controller/TaskController.java
index 61f76ec88e..1929ade606 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/task/controller/TaskController.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/controller/TaskController.java
@@ -95,6 +95,7 @@ public class TaskController extends BaseController {
     private TaskRecordService taskRecordService;
     private WorkerService workerService;
     private UserService userService;
+    private TaskErrorEventService taskErrorEventService;
 
 		private <T> T dataPermissionUnAuth() {
 			throw new RuntimeException("Un auth");
@@ -1288,11 +1289,14 @@ public ResponseMessage<Void> updateErrorEvents(@PathVariable("taskId") String ta
         return success();
     }
 
+    @GetMapping("errors/{taskId}")
+    public ResponseMessage<List<ErrorEvent>> getErrorEventByTaskId(@PathVariable("taskId") String taskId) {
+        return success(taskErrorEventService.getErrorEventByTaskId(taskId, getLoginUser()));
+    }
+
     @PostMapping("skipErrorEvents/{taskId}")
     public ResponseMessage<Void> skipErrorEvents(@PathVariable("taskId") String taskId,@RequestBody List<String> ids) {
-        Criteria criteria = Criteria.where("_id").is(taskId);
-        Update update = new Update().set("errorEvents.$[element].skip",true).filterArray(Criteria.where("element._id").in(ids.stream().map(MongoUtils::toObjectId).collect(Collectors.toList())));
-        taskService.update(new Query(criteria),update);
+        taskErrorEventService.signSkipErrorEvents(taskId, ids);
         return success();
     }
 
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskErrorEventService.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskErrorEventService.java
new file mode 100644
index 0000000000..60a42826a2
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskErrorEventService.java
@@ -0,0 +1,56 @@
+package com.tapdata.tm.task.service;
+
+import cn.hutool.core.collection.CollUtil;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.task.repository.TaskRepository;
+import com.tapdata.tm.utils.Lists;
+import com.tapdata.tm.utils.MongoUtils;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.BulkOperations;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@Setter(onMethod_ = {@Autowired})
+public class TaskErrorEventService {
+    protected static final String ERROR_EVENTS_SKIP_KEY = "errorEvents.$[element].skip";
+    protected static final String ELEMENT_ID = "element._id";
+    private TaskRepository taskRepository;
+    private TaskService taskService;
+
+    public List<ErrorEvent> getErrorEventByTaskId(String taskId, UserDetail user) {
+        TaskDto taskDto = taskService.findOne(Query.query(Criteria.where("_id").is(MongoUtils.toObjectId(taskId))), user);
+        if (null == taskDto) {
+            return Lists.newArrayList();
+        }
+        return Optional.ofNullable(taskDto.getErrorEvents()).orElse(Lists.newArrayList());
+    }
+
+    public void signSkipErrorEvents(String taskId, List<String> ids) {
+        Criteria criteria = Criteria.where("_id").is(MongoUtils.toObjectId(taskId));
+        Query query = Query.query(criteria);
+        if (CollUtil.isEmpty(ids)) {
+            taskService.update(query, new Update().set(ERROR_EVENTS_SKIP_KEY, false).filterArray(Criteria.where(ELEMENT_ID).nin(Lists.newArrayList())));
+            return;
+        }
+        BulkOperations bulkOperations = taskRepository.bulkOperations(BulkOperations.BulkMode.UNORDERED);
+        List<ObjectId> objectIds = ids.stream().map(MongoUtils::toObjectId).collect(Collectors.toList());
+        bulkOperations.updateMulti(query, new Update().set(ERROR_EVENTS_SKIP_KEY, true)
+                    .filterArray(Criteria.where(ELEMENT_ID).in(objectIds)))
+                .updateMulti(query, new Update().set(ERROR_EVENTS_SKIP_KEY, false)
+                    .filterArray(Criteria.where(ELEMENT_ID).nin(objectIds)));
+        bulkOperations.execute();
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskServiceImpl.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskServiceImpl.java
index f11cb0b945..782c0e99e2 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskServiceImpl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/TaskServiceImpl.java
@@ -18,25 +18,62 @@
 import com.tapdata.tm.autoinspect.service.TaskAutoInspectResultsService;
 import com.tapdata.tm.autoinspect.utils.AutoInspectUtil;
 import com.tapdata.tm.base.dto.Field;
-import com.tapdata.tm.base.dto.*;
+import com.tapdata.tm.base.dto.Filter;
+import com.tapdata.tm.base.dto.MutiResponseMessage;
+import com.tapdata.tm.base.dto.Page;
+import com.tapdata.tm.base.dto.ResponseMessage;
+import com.tapdata.tm.base.dto.TmPageable;
+import com.tapdata.tm.base.dto.Where;
 import com.tapdata.tm.base.exception.BizException;
 import com.tapdata.tm.base.handler.ExceptionHandler;
 import com.tapdata.tm.commons.base.dto.BaseDto;
-import com.tapdata.tm.commons.dag.*;
-import com.tapdata.tm.commons.dag.nodes.*;
-import com.tapdata.tm.commons.dag.process.*;
+import com.tapdata.tm.commons.dag.AccessNodeTypeEnum;
+import com.tapdata.tm.commons.dag.DAG;
+import com.tapdata.tm.commons.dag.DmlPolicyEnum;
+import com.tapdata.tm.commons.dag.Edge;
+import com.tapdata.tm.commons.dag.Node;
+import com.tapdata.tm.commons.dag.nodes.CacheNode;
+import com.tapdata.tm.commons.dag.nodes.DataNode;
+import com.tapdata.tm.commons.dag.nodes.DataParentNode;
+import com.tapdata.tm.commons.dag.nodes.DatabaseNode;
+import com.tapdata.tm.commons.dag.nodes.TableNode;
+import com.tapdata.tm.commons.dag.process.CustomProcessorNode;
+import com.tapdata.tm.commons.dag.process.JoinProcessorNode;
+import com.tapdata.tm.commons.dag.process.JsProcessorNode;
+import com.tapdata.tm.commons.dag.process.MergeTableNode;
+import com.tapdata.tm.commons.dag.process.MigrateFieldRenameProcessorNode;
+import com.tapdata.tm.commons.dag.process.MigrateJsProcessorNode;
+import com.tapdata.tm.commons.dag.process.UnwindProcessNode;
 import com.tapdata.tm.commons.dag.process.script.ScriptProcessNode;
 import com.tapdata.tm.commons.dag.process.script.py.MigratePyProcessNode;
 import com.tapdata.tm.commons.dag.process.script.py.PyProcessNode;
-import com.tapdata.tm.commons.dag.vo.*;
+import com.tapdata.tm.commons.dag.vo.SyncObjects;
+import com.tapdata.tm.commons.dag.vo.TestRunDto;
 import com.tapdata.tm.commons.externalStorage.ExternalStorageDto;
-import com.tapdata.tm.commons.schema.*;
+import com.tapdata.tm.commons.schema.DataSourceConnectionDto;
+import com.tapdata.tm.commons.schema.DataSourceDefinitionDto;
+import com.tapdata.tm.commons.schema.MetadataInstancesDto;
+import com.tapdata.tm.commons.schema.MetadataTransformerDto;
+import com.tapdata.tm.commons.schema.MetadataTransformerItemDto;
+import com.tapdata.tm.commons.schema.Tag;
+import com.tapdata.tm.commons.schema.TransformerWsMessageDto;
 import com.tapdata.tm.commons.task.constant.NotifyEnum;
-import com.tapdata.tm.commons.task.dto.*;
+import com.tapdata.tm.commons.task.dto.Dag;
+import com.tapdata.tm.commons.task.dto.DataSyncMq;
+import com.tapdata.tm.commons.task.dto.MergeTableProperties;
+import com.tapdata.tm.commons.task.dto.Message;
+import com.tapdata.tm.commons.task.dto.Milestone;
+import com.tapdata.tm.commons.task.dto.ParentTaskDto;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.commons.task.dto.TaskHistory;
+import com.tapdata.tm.commons.task.dto.TaskRunHistoryDto;
 import com.tapdata.tm.commons.task.dto.alarm.AlarmSettingVO;
 import com.tapdata.tm.commons.task.dto.migrate.MigrateTableDto;
 import com.tapdata.tm.commons.task.dto.progress.TaskSnapshotProgress;
-import com.tapdata.tm.commons.util.*;
+import com.tapdata.tm.commons.util.ConnHeartbeatUtils;
+import com.tapdata.tm.commons.util.JsonUtil;
+import com.tapdata.tm.commons.util.MetaDataBuilderUtils;
+import com.tapdata.tm.commons.util.ThrowableUtils;
 import com.tapdata.tm.config.security.UserDetail;
 import com.tapdata.tm.customNode.dto.CustomNodeDto;
 import com.tapdata.tm.customNode.service.CustomNodeService;
@@ -50,7 +87,6 @@
 import com.tapdata.tm.inspect.bean.Source;
 import com.tapdata.tm.inspect.bean.Stats;
 import com.tapdata.tm.inspect.constant.InspectResultEnum;
-import com.tapdata.tm.inspect.constant.InspectStatusEnum;
 import com.tapdata.tm.inspect.dto.InspectDto;
 import com.tapdata.tm.inspect.dto.InspectResultDto;
 import com.tapdata.tm.inspect.service.InspectResultService;
@@ -71,14 +107,30 @@
 import com.tapdata.tm.monitoringlogs.service.MonitoringLogsService;
 import com.tapdata.tm.permissions.DataPermissionHelper;
 import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
+import com.tapdata.tm.permissions.constants.DataPermissionDataTypeEnums;
 import com.tapdata.tm.permissions.constants.DataPermissionMenuEnums;
+import com.tapdata.tm.report.dto.TasksNumBatch;
+import com.tapdata.tm.report.service.UserDataReportService;
 import com.tapdata.tm.schedule.ChartSchedule;
 import com.tapdata.tm.schedule.service.ScheduleService;
 import com.tapdata.tm.statemachine.enums.DataFlowEvent;
 import com.tapdata.tm.statemachine.model.StateMachineResult;
 import com.tapdata.tm.statemachine.service.StateMachineService;
-import com.tapdata.tm.task.bean.*;
-import com.tapdata.tm.task.constant.*;
+import com.tapdata.tm.task.bean.Chart6Vo;
+import com.tapdata.tm.task.bean.FullSyncVO;
+import com.tapdata.tm.task.bean.LogCollectorResult;
+import com.tapdata.tm.task.bean.RunTimeInfo;
+import com.tapdata.tm.task.bean.SampleTaskVo;
+import com.tapdata.tm.task.bean.SyncTaskStatusDto;
+import com.tapdata.tm.task.bean.TableStatusInfoDto;
+import com.tapdata.tm.task.bean.TaskUpAndLoadDto;
+import com.tapdata.tm.task.constant.InputNumCache;
+import com.tapdata.tm.task.constant.SyncStatus;
+import com.tapdata.tm.task.constant.SyncType;
+import com.tapdata.tm.task.constant.TableStatusEnum;
+import com.tapdata.tm.task.constant.TaskEnum;
+import com.tapdata.tm.task.constant.TaskOpStatusEnum;
+import com.tapdata.tm.task.constant.TaskStatusEnum;
 import com.tapdata.tm.task.dto.CheckEchoOneNodeParam;
 import com.tapdata.tm.task.entity.TaskEntity;
 import com.tapdata.tm.task.entity.TaskRecord;
@@ -86,9 +138,9 @@
 import com.tapdata.tm.task.param.SaveShareCacheParam;
 import com.tapdata.tm.task.repository.TaskRepository;
 import com.tapdata.tm.task.service.batchin.ParseRelMig;
-import com.tapdata.tm.task.service.batchin.ParseRelMigFile;
 import com.tapdata.tm.task.service.batchin.entity.ParseParam;
 import com.tapdata.tm.task.service.batchup.BatchUpChecker;
+import com.tapdata.tm.task.service.chart.ChartViewService;
 import com.tapdata.tm.task.service.utils.TaskServiceUtil;
 import com.tapdata.tm.task.vo.ShareCacheDetailVo;
 import com.tapdata.tm.task.vo.ShareCacheVo;
@@ -99,14 +151,27 @@
 import com.tapdata.tm.user.service.UserService;
 import com.tapdata.tm.userLog.constant.Modular;
 import com.tapdata.tm.userLog.service.UserLogService;
-import com.tapdata.tm.utils.*;
+import com.tapdata.tm.utils.FunctionUtils;
+import com.tapdata.tm.utils.GZIPUtil;
+import com.tapdata.tm.utils.Lists;
+import com.tapdata.tm.utils.MapUtils;
+import com.tapdata.tm.utils.MessageUtil;
+import com.tapdata.tm.utils.MongoUtils;
+import com.tapdata.tm.utils.NumberUtil;
+import com.tapdata.tm.utils.SpringContextHelper;
+import com.tapdata.tm.utils.TimeUtil;
+import com.tapdata.tm.utils.UUIDUtil;
 import com.tapdata.tm.worker.entity.Worker;
 import com.tapdata.tm.worker.service.WorkerService;
 import com.tapdata.tm.worker.vo.CalculationEngineVo;
 import com.tapdata.tm.ws.enums.MessageType;
 import io.tapdata.common.sample.request.Sample;
 import io.tapdata.exception.TapCodeException;
-import lombok.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+import lombok.Setter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.RandomStringUtils;
@@ -135,7 +200,20 @@
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
@@ -144,6 +222,14 @@
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.DONE;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.ERROR;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.FAILED;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.PASSED;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.RUNNING;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.SCHEDULING;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.STOPPING;
+import static com.tapdata.tm.inspect.constant.InspectStatusEnum.WAITING;
 import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
 import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
 
@@ -285,6 +371,8 @@ private static String getTableName() {
     private AgentGroupService agentGroupService;
     private DataSourceDefinitionService dataSourceDefinitionService;
     private BatchUpChecker batchUpChecker;
+    private ChartViewService chartViewService;
+    private UserDataReportService userDataReportService;
 
     public TaskServiceImpl(@NonNull TaskRepository repository) {
         super(repository);
@@ -360,6 +448,7 @@ public TaskDto create(TaskDto taskDto, UserDetail user) {
 
         //模型推演
         setDefault(taskDto);
+        taskDto.setOldVersionTimezone(false);
         taskDto = save(taskDto, user);
         if (dag != null) {
             dag.setTaskId(taskDto.getId());
@@ -2033,20 +2122,8 @@ protected TaskDto parseCacheToTaskDto(SaveShareCacheParam saveShareCacheParam, T
      */
     public Map<String, Object> chart(UserDetail user) {
         Map<String, Object> resultChart = new HashMap<>();
-        Criteria criteria = new Criteria()
-                .and(IS_DELETED).ne(true)
-                .and(SYNC_TYPE).in(TaskDto.SYNC_TYPE_MIGRATE, TaskDto.SYNC_TYPE_SYNC)
-                .and(STATUS).nin(TaskDto.STATUS_DELETING, TaskDto.STATUS_DELETE_FAILED)
-                //共享缓存的任务设计的有点问题
-                .and("shareCache").ne(true);
-
-
-        Query query = Query.query(criteria);
-        query.fields().include(SYNC_TYPE, STATUS, "statuses");
         //把任务都查询出来
-        List<TaskDto> taskDtoList = DataPermissionMenuEnums.MigrateTack.checkAndSetFilter(
-                user, DataPermissionActionEnums.View, () -> findAllDto(query, user)
-        );
+        List<TaskDto> taskDtoList = chartViewService.getViewTaskDtoByUser(user);
         Map<String, List<TaskDto>> syncTypeToTaskList = taskDtoList.stream().collect(Collectors.groupingBy(TaskDto::getSyncType));
 
         List<TaskDto> migrateList =  syncTypeToTaskList.getOrDefault(SyncType.MIGRATE.getValue(), Collections.emptyList());
@@ -2058,51 +2135,62 @@ public Map<String, Object> chart(UserDetail user) {
         resultChart.put("chart5", inspectChart(user));
         Chart6Vo chart6Vo = ChartSchedule.cache.get(user.getUserId());
         if (chart6Vo == null) {
-            chart6Vo = chart6(user);
+            chart6Vo = chartViewService.transmissionOverviewChartData(taskDtoList);
             ChartSchedule.put(user.getUserId(), chart6Vo);
         }
         resultChart.put("chart6", chart6Vo);
         return resultChart;
     }
 
+    protected List<InspectDto> inspectTaskList(Filter filter, UserDetail user) {
+        return DataPermissionHelper.check(
+                user,
+                DataPermissionMenuEnums.INSPECT_TACK,
+                DataPermissionActionEnums.View,
+                DataPermissionDataTypeEnums.INSPECT,
+                null,
+                () -> inspectService.list(filter, user).getItems(),
+                ArrayList::new);
 
-    public Map<String, Integer> inspectChart(UserDetail user) {
-        Criteria criteria = Criteria.where(SYNC_TYPE).is(TaskDto.SYNC_TYPE_MIGRATE)
-                .and("isAutoInspect").is(true)
-                .and(IS_DELETED).ne(true);
-        Query query = new Query(criteria);
-        query.fields().include("_id", STATUS, "isAutoInspect", "canOpenInspect", "attrs.autoInspectProgress.tableCounts", "attrs.autoInspectProgress.tableIgnore", "attrs.autoInspectProgress.step");
-
-        int openTaskNum = 0;
-        int canTaskNum = 0;
-        int errorTaskNum = 0;
-        int diffTaskNum = 0;
-        List<TaskDto> taskDtos = findAllDto(query, user);
-
-        Set<String> taskSet = taskAutoInspectResultsService.groupByTask(user);
-        if (CollectionUtils.isNotEmpty(taskDtos)) {
-            openTaskNum = taskDtos.size();
-            for (TaskDto taskDto : taskDtos) {
-                if (taskDto.getCanOpenInspect() != null && taskDto.getCanOpenInspect()) {
-                    canTaskNum++;
-                }
-
-                if (TaskDto.STATUS_ERROR.equals(taskDto.getStatus())) {
-                    errorTaskNum++;
-                }
+    }
 
-                if (taskSet.contains(taskDto.getId().toHexString())) {
-                    diffTaskNum++;
+    public Map<String, Integer> inspectChart(UserDetail user) {
+        int error = 0;
+        int running = 0;
+        int done = 0;
+        int waiting = 0;
+        int scheduling = 0;
+        int stopping = 0;
+        int total = 0;
+        List<InspectDto> inspectDtoList = inspectTaskList(new Filter(), user);
+        if (CollectionUtils.isNotEmpty(inspectDtoList)) {
+            total = inspectDtoList.size();
+            for (InspectDto taskDto : inspectDtoList) {
+                String status = String.valueOf(taskDto.getStatus()).toLowerCase();
+                if (ERROR.getValue().equals(status)) {
+                    error++;
+                } else if(RUNNING.getValue().equals(status)) {
+                    running++;
+                } else if (DONE.getValue().equals(status)) {
+                    done++;
+                } else if (WAITING.getValue().equals(status)) {
+                    waiting++;
+                }else if (SCHEDULING.getValue().equals(status)) {
+                    scheduling++;
+                }else if (STOPPING.getValue().equals(status)) {
+                    stopping++;
                 }
-
             }
         }
 
         Map<String, Integer> chart5 = new HashMap<>();
-        chart5.put(TOTAL, openTaskNum);
-        chart5.put("error", errorTaskNum);
-        chart5.put("can", canTaskNum);
-        chart5.put("diff", diffTaskNum);
+        chart5.put(TOTAL, total);
+        chart5.put("error", error);
+        chart5.put("running", running);
+        chart5.put("done", done);
+        chart5.put("waiting", waiting);
+        chart5.put("scheduling", scheduling);
+        chart5.put("stopping", stopping);
         return chart5;
     }
 
@@ -3389,6 +3477,10 @@ public void start(TaskDto taskDto, UserDetail user, String startFlag, boolean sy
      *                  第二位 是否开启打点任务      1 是   0 否
      */
     public void start(TaskDto taskDto, UserDetail user, String startFlag) {
+        String taskType = taskDto.getSyncType();
+        TasksNumBatch tasksNumBatch = new TasksNumBatch();
+        tasksNumBatch.setTaskType(taskType);
+        userDataReportService.produceData(tasksNumBatch);
 
         if (taskDto.getShareCdcEnable() && !TaskDto.SYNC_TYPE_LOG_COLLECTOR.equals(taskDto.getSyncType())) {
             //如果是共享挖掘任务给一个队列，避免混乱
@@ -3439,6 +3531,11 @@ public void start(TaskDto taskDto, UserDetail user, String startFlag) {
         //模型推演,如果模型已经存在，则需要推演
 //        DAG dag = taskDto.getDag();
 
+        //filter heartbeat task when status is renew failed for automatic test
+        if (TaskDto.SYNC_TYPE_CONN_HEARTBEAT.equals(taskDto.getSyncType()) && TaskDto.STATUS_RENEW_FAILED.equals(taskDto.getStatus())) {
+            log.warn("heartbeat task current status not allow to start, task = {}, status = {}, please restore the task manually.", taskDto.getName(), taskDto.getStatus());
+            return;
+        }
         //校验当前状态是否允许启动。
         if (!TaskOpStatusEnum.to_start_status.v().contains(taskDto.getStatus())) {
             log.warn("task current status not allow to start, task = {}, status = {}", taskDto.getName(), taskDto.getStatus());
@@ -4054,77 +4151,6 @@ public void updateTaskLogSetting(String taskId, LogSettingParam logSettingParam,
         updateById(taskObjectId, update, userDetail);
     }
 
-    public Chart6Vo chart6(UserDetail user) {
-        Criteria criteria = Criteria.where(IS_DELETED).ne(true).and(SYNC_TYPE).in(TaskDto.SYNC_TYPE_SYNC, TaskDto.SYNC_TYPE_MIGRATE);
-        Query query = new Query(criteria);
-        query.fields().include("_id");
-        List<TaskDto> allDto = findAllDto(query, user);
-        List<String> ids = allDto.stream().map(a->a.getId().toHexString()).collect(Collectors.toList());
-
-        List<MeasurementEntity>  allMeasurements = new ArrayList<>();
-        if (CollectionUtils.isNotEmpty(ids)) {
-            ids.stream().forEach(id -> {
-                MeasurementEntity measurement = measurementServiceV2.findLastMinuteByTaskId(id);
-                if (measurement != null) {
-                    allMeasurements.add(measurement);
-                }
-            });
-        }
-
-        BigInteger output = BigInteger.ZERO;
-        BigInteger input = BigInteger.ZERO;
-        BigInteger insert = BigInteger.ZERO;
-        BigInteger update = BigInteger.ZERO;
-        BigInteger delete = BigInteger.ZERO;
-
-        for (MeasurementEntity allMeasurement : allMeasurements) {
-            if (allMeasurement == null) {
-                continue;
-            }
-            List<Sample> samples = allMeasurement.getSamples();
-            if (CollectionUtils.isNotEmpty(samples)) {
-                Optional<Sample> max = samples.stream().max(Comparator.comparing(Sample::getDate));
-                if (max.isPresent()) {
-                    Sample sample = max.get();
-                    Map<String, Number> vs = sample.getVs();
-                    BigInteger inputInsertTotal = NumberUtil.parseDataTotal(vs.get("inputInsertTotal"));
-                    BigInteger inputOthersTotal = NumberUtil.parseDataTotal(vs.get("inputOthersTotal"));
-                    BigInteger inputDdlTotal = NumberUtil.parseDataTotal(vs.get("inputDdlTotal"));
-                    BigInteger inputUpdateTotal = NumberUtil.parseDataTotal(vs.get("inputUpdateTotal"));
-                    BigInteger inputDeleteTotal = NumberUtil.parseDataTotal(vs.get("inputDeleteTotal"));
-
-                    BigInteger outputInsertTotal = NumberUtil.parseDataTotal(vs.get("outputInsertTotal"));
-                    BigInteger outputOthersTotal = NumberUtil.parseDataTotal(vs.get("outputOthersTotal"));
-                    BigInteger outputDdlTotal = NumberUtil.parseDataTotal(vs.get("outputDdlTotal"));
-                    BigInteger outputUpdateTotal = NumberUtil.parseDataTotal(vs.get("outputUpdateTotal"));
-                    BigInteger outputDeleteTotal = NumberUtil.parseDataTotal(vs.get("outputDeleteTotal"));
-                    output = output.add(outputInsertTotal);
-                    output = output.add(outputOthersTotal);
-                    output = output.add(outputDdlTotal);
-                    output = output.add(outputUpdateTotal);
-                    output = output.add(outputDeleteTotal);
-
-                    input = input.add(inputInsertTotal);
-                    input = input.add(inputOthersTotal);
-                    input = input.add(inputDdlTotal);
-                    input = input.add(inputUpdateTotal);
-                    input = input.add(inputDeleteTotal);
-
-                    insert = insert.add(inputInsertTotal);
-                    update = update.add(inputUpdateTotal);
-                    delete = delete.add(inputDeleteTotal);
-
-                }
-            }
-        }
-
-
-        Chart6Vo chart6Vo = Chart6Vo.builder().outputTotal(output).inputTotal(input)
-                .insertedTotal(insert).updatedTotal(update).deletedTotal(delete)
-                .build();
-        return chart6Vo;
-    }
-
     public void stopTaskIfNeedByAgentId(String agentId, UserDetail userDetail) {
         Query query = Query.query(Criteria.where(AGENT_ID).is(agentId).and(STATUS).is(TaskDto.STATUS_STOPPING));
         query.fields().include("_id");
@@ -4228,11 +4254,11 @@ public boolean judgeTargetInspect(String connectionId, String tableName, UserDet
             for (Stats stats : statsList) {
                 Source target = stats.getTarget();
                 if (connectionId.equals(target.getConnectionId()) && tableName.equals(target.getTable())) {
-                    if (StringUtils.equalsAny(stats.getStatus(), InspectStatusEnum.FAILED.name(),
-                            InspectStatusEnum.ERROR.name())) {
+                    if (StringUtils.equalsAny(stats.getStatus(), FAILED.name(),
+                            ERROR.name())) {
                         return false;
-                    } else if (StringUtils.equalsAny(stats.getStatus(), InspectStatusEnum.DONE.name(),
-                            InspectStatusEnum.PASSED.name())) {
+                    } else if (StringUtils.equalsAny(stats.getStatus(), DONE.name(),
+                            PASSED.name())) {
                         return InspectResultEnum.PASSED.name().equals(stats.getResult());
                     } else {
                         return true;
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/chart/ChartViewService.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/chart/ChartViewService.java
new file mode 100644
index 0000000000..f28ed69121
--- /dev/null
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/chart/ChartViewService.java
@@ -0,0 +1,126 @@
+package com.tapdata.tm.task.service.chart;
+
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.monitor.entity.MeasurementEntity;
+import com.tapdata.tm.monitor.service.MeasurementServiceV2;
+import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
+import com.tapdata.tm.permissions.constants.DataPermissionMenuEnums;
+import com.tapdata.tm.task.bean.Chart6Vo;
+import com.tapdata.tm.task.service.TaskService;
+import com.tapdata.tm.task.service.TaskServiceImpl;
+import com.tapdata.tm.utils.NumberUtil;
+import io.tapdata.common.sample.request.Sample;
+import lombok.Setter;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+@Setter(onMethod_ = {@Autowired})
+public class ChartViewService {
+    private TaskService taskService;
+    private MeasurementServiceV2 measurementServiceV2;
+
+    public List<TaskDto> getViewTaskDtoByUser(UserDetail user) {
+        return DataPermissionMenuEnums.MigrateTack.checkAndSetFilter(user, DataPermissionActionEnums.View, () -> getViewTaskDto(user));
+    }
+
+    protected List<TaskDto> getViewTaskDto(UserDetail user) {
+        Criteria criteria = new Criteria()
+                .and(TaskServiceImpl.IS_DELETED).ne(true)
+                .and(TaskServiceImpl.SYNC_TYPE).in(TaskDto.SYNC_TYPE_MIGRATE, TaskDto.SYNC_TYPE_SYNC)
+                .and(TaskServiceImpl.STATUS).nin(TaskDto.STATUS_DELETING, TaskDto.STATUS_DELETE_FAILED)
+                //共享缓存的任务设计的有点问题
+                .and("shareCache").ne(true);
+        Query query = Query.query(criteria);
+        query.fields().include(TaskServiceImpl.SYNC_TYPE, TaskServiceImpl.STATUS, "statuses");
+        return taskService.findAllDto(query, user);
+    }
+
+    public Chart6Vo transmissionOverviewChartData(UserDetail user) {
+        List<TaskDto> allDto = getViewTaskDtoByUser(user);
+        return transmissionOverviewChartData(allDto);
+    }
+
+    public Chart6Vo transmissionOverviewChartData(List<TaskDto> allDto) {
+        List<String> ids = allDto.stream()
+                .filter(Objects::nonNull)
+                .map(a -> a.getId().toHexString())
+                .collect(Collectors.toList());
+
+        List<MeasurementEntity> allMeasurements = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(ids)) {
+            ids.forEach(id -> {
+                MeasurementEntity measurement = measurementServiceV2.findLastMinuteByTaskId(id);
+                if (measurement != null) {
+                    allMeasurements.add(measurement);
+                }
+            });
+        }
+
+        BigInteger output = BigInteger.ZERO;
+        BigInteger input = BigInteger.ZERO;
+        BigInteger insert = BigInteger.ZERO;
+        BigInteger update = BigInteger.ZERO;
+        BigInteger delete = BigInteger.ZERO;
+
+        for (MeasurementEntity allMeasurement : allMeasurements) {
+            if (allMeasurement == null) {
+                continue;
+            }
+            List<Sample> samples = allMeasurement.getSamples();
+            if (CollectionUtils.isNotEmpty(samples)) {
+                Optional<Sample> max = samples.stream().max(Comparator.comparing(Sample::getDate));
+                if (max.isPresent()) {
+                    Sample sample = max.get();
+                    Map<String, Number> vs = sample.getVs();
+                    BigInteger inputInsertTotal = NumberUtil.parseDataTotal(vs.get("inputInsertTotal"));
+                    BigInteger inputOthersTotal = NumberUtil.parseDataTotal(vs.get("inputOthersTotal"));
+                    BigInteger inputDdlTotal = NumberUtil.parseDataTotal(vs.get("inputDdlTotal"));
+                    BigInteger inputUpdateTotal = NumberUtil.parseDataTotal(vs.get("inputUpdateTotal"));
+                    BigInteger inputDeleteTotal = NumberUtil.parseDataTotal(vs.get("inputDeleteTotal"));
+
+                    BigInteger outputInsertTotal = NumberUtil.parseDataTotal(vs.get("outputInsertTotal"));
+                    BigInteger outputOthersTotal = NumberUtil.parseDataTotal(vs.get("outputOthersTotal"));
+                    BigInteger outputDdlTotal = NumberUtil.parseDataTotal(vs.get("outputDdlTotal"));
+                    BigInteger outputUpdateTotal = NumberUtil.parseDataTotal(vs.get("outputUpdateTotal"));
+                    BigInteger outputDeleteTotal = NumberUtil.parseDataTotal(vs.get("outputDeleteTotal"));
+                    output = output.add(outputInsertTotal);
+                    output = output.add(outputOthersTotal);
+                    output = output.add(outputDdlTotal);
+                    output = output.add(outputUpdateTotal);
+                    output = output.add(outputDeleteTotal);
+
+                    input = input.add(inputInsertTotal);
+                    input = input.add(inputOthersTotal);
+                    input = input.add(inputDdlTotal);
+                    input = input.add(inputUpdateTotal);
+                    input = input.add(inputDeleteTotal);
+
+                    insert = insert.add(inputInsertTotal);
+                    update = update.add(inputUpdateTotal);
+                    delete = delete.add(inputDeleteTotal);
+
+                }
+            }
+        }
+        return Chart6Vo.builder().outputTotal(output)
+                .inputTotal(input)
+                .insertedTotal(insert)
+                .updatedTotal(update)
+                .deletedTotal(delete)
+                .build();
+    }
+}
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskNodeServiceImpl.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskNodeServiceImpl.java
index e5e20b4bca..57d8c64380 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskNodeServiceImpl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskNodeServiceImpl.java
@@ -388,16 +388,6 @@ private Page<MetadataTransformerItemDto> getMetadataTransformerItemDtoPage(UserD
         }
         predecessors.add(currentNode);
 
-        // table rename
-        LinkedList<TableRenameProcessNode> tableRenameProcessNodes = predecessors
-                .stream()
-                .filter(node -> node instanceof TableRenameProcessNode)
-                .map(node -> (TableRenameProcessNode) node)
-                .collect(Collectors.toCollection(LinkedList::new));
-        Map<String, TableRenameTableInfo> tableNameMapping = null;
-        if (CollectionUtils.isNotEmpty(tableRenameProcessNodes)) {
-            tableNameMapping = tableRenameProcessNodes.getLast().originalMap();
-        }
         // field rename
         LinkedList<MigrateFieldRenameProcessorNode> fieldRenameProcessorNodes = predecessors
                 .stream()
@@ -455,12 +445,8 @@ private Page<MetadataTransformerItemDto> getMetadataTransformerItemDtoPage(UserD
 
             MetadataTransformerItemDto item = new MetadataTransformerItemDto();
             item.setSourceObjectName(tableName);
-            String sinkTableName = metadataInstancesDto.getOriginalName();
-            String previousTableName = metadataInstancesDto.getOriginalName();
-            if (Objects.nonNull(tableNameMapping) && !tableNameMapping.isEmpty() && Objects.nonNull(tableNameMapping.get(tableName))) {
-                sinkTableName = tableNameMapping.get(tableName).getCurrentTableName();
-                previousTableName = tableNameMapping.get(tableName).getPreviousTableName();
-            }
+            String sinkTableName = metadataInstancesDto.getName();
+            String previousTableName = metadataInstancesDto.getAncestorsName();
             item.setSinkObjectName(sinkTableName);
 
             List<FieldsMapping> fieldsMapping = new LinkedList<>();
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskSaveServiceImpl.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskSaveServiceImpl.java
index e23aaeb9d5..69aea6485e 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskSaveServiceImpl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/TaskSaveServiceImpl.java
@@ -278,8 +278,6 @@ private void nodeCheckData(List<Node<List<Schema>>> nodes, List<String> tableNam
 
             if (CollectionUtils.isEmpty(tableNames)) {
                 tableNode.setTableNames(new LinkedHashSet<>());
-            } else if (CollectionUtils.isNotEmpty(tableNode.getTableNames())) {
-                tableNode.getTableNames().removeIf(t -> !tableNames.contains(t.getOriginTableName()));
             }
 
             if (CollectionUtils.isNotEmpty(tableNode.getTableNames())) {
diff --git a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/dagcheckstrategy/TargetSettingStrategyImpl.java b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/dagcheckstrategy/TargetSettingStrategyImpl.java
index 87bc863cb9..e60047d181 100644
--- a/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/dagcheckstrategy/TargetSettingStrategyImpl.java
+++ b/manager/tm/src/main/java/com/tapdata/tm/task/service/impl/dagcheckstrategy/TargetSettingStrategyImpl.java
@@ -1,7 +1,5 @@
 package com.tapdata.tm.task.service.impl.dagcheckstrategy;
 
-import cn.hutool.core.date.DateUtil;
-import com.google.common.collect.Sets;
 import com.tapdata.tm.commons.dag.DAG;
 import com.tapdata.tm.commons.dag.Node;
 import com.tapdata.tm.commons.dag.nodes.DataParentNode;
@@ -30,8 +28,11 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.text.MessageFormat;
-import java.util.*;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -53,11 +54,7 @@ public class TargetSettingStrategyImpl implements DagLogStrategy {
     @Override
     public List<TaskDagCheckLog> getLogs(TaskDto taskDto, UserDetail userDetail, Locale locale) {
         String taskId = taskDto.getId().toHexString();
-        String current = DateUtil.now();
-        Date now = new Date();
-
         List<TaskDagCheckLog> result = Lists.newArrayList();
-        Set<String> nameSet = Sets.newHashSet();
         DAG dag = taskDto.getDag();
 
         if (Objects.isNull(dag) || CollectionUtils.isEmpty(dag.getTargets())) {
@@ -82,20 +79,12 @@ public List<TaskDagCheckLog> getLogs(TaskDto taskDto, UserDetail userDetail, Loc
                 result.add(log);
             }
 
-//            boolean keepTargetSchema = false;
             AtomicReference<List<String>> tableNames = new AtomicReference<>();
-//            List<String> existDataModeList = Lists.newArrayList("keepData", "removeData");
             if (TaskDto.SYNC_TYPE_MIGRATE.equals(taskDto.getSyncType())) {
                 DatabaseNode databaseNode = (DatabaseNode) node;
                 Optional.ofNullable(databaseNode.getSyncObjects()).ifPresent(list -> tableNames.set(list.get(0).getObjectNames()));
-//                if (existDataModeList.contains(databaseNode.getExistDataProcessMode())) {
-//                    keepTargetSchema = true;
-//                }
             } else {
                 TableNode tableNode = (TableNode) node;
-//                if (existDataModeList.contains(tableNode.getExistDataProcessMode())) {
-//                    keepTargetSchema = true;
-//                }
                 tableNames.set(Lists.newArrayList(tableNode.getTableName()));
                 if ("updateOrInsert".equals(tableNode.getWriteStrategy())) {
                     List<String> updateConditionFields = tableNode.getUpdateConditionFields();
@@ -116,38 +105,6 @@ public List<TaskDagCheckLog> getLogs(TaskDto taskDto, UserDetail userDetail, Loc
                     }*/
                 }
             }
-
-//            if (keepTargetSchema) {
-//                List<MetadataInstancesDto> schemaList = metadataInstancesService.findSourceSchemaBySourceId(connectionId, tableNames.get(), userDetail);
-//                List<String> collect = schemaList.stream().map(MetadataInstancesDto::getName).collect(Collectors.toList());
-//                if (CollectionUtils.isNotEmpty(collect)) {
-//                    List<String> list = new ArrayList<>(tableNames.get());
-//                    list.removeAll(collect);
-//                    if (CollectionUtils.isNotEmpty(list)) {
-//                        TaskDagCheckLog log = TaskDagCheckLog.builder()
-//                                .taskId(taskId)
-//                                .checkType(templateEnum.name())
-//                                .log(MessageFormat.format(MessageUtil.getDagCheckMsg(locale, "TARGET_SETTING_CHECK_SCHAME"), node.getName(), JSON.toJSONString(list)))
-//                                .grade(Level.ERROR)
-//                                .nodeId(node.getId()).build();
-//                        log.setCreateAt(now);
-//                        log.setCreateUser(userId);
-//
-//                        result.add(log);
-//                    }
-//                } else {
-//                    TaskDagCheckLog log = TaskDagCheckLog.builder()
-//                            .taskId(taskId)
-//                            .checkType(templateEnum.name())
-//                            .log(MessageFormat.format(MessageUtil.getDagCheckMsg(locale, "TARGET_SETTING_CHECK_SCHAME"), node.getName(), JSON.toJSONString(tableNames.get())))
-//                            .grade(Level.ERROR)
-//                            .nodeId(node.getId()).build();
-//                    log.setCreateAt(now);
-//                    log.setCreateUser(userId);
-//
-//                    result.add(log);
-//                }
-//            }
             checkNodeExistDataMode(locale, taskId, result, userId, node, name);
             checkNodeSyncIndex(locale, taskId, result, userId, node, name);
             checkTargetUpdateField(locale, taskId ,result , userId ,node , name ,connectionId);
@@ -195,26 +152,6 @@ public List<TaskDagCheckLog> getLogs(TaskDto taskDto, UserDetail userDetail, Loc
                     }
                 }
             }
-
-            String template;
-            Level grade;
-            if (nameSet.contains(name)) {
-                template = MessageUtil.getDagCheckMsg(locale, "TARGET_NODE_ERROR");
-                grade = Level.ERROR;
-            } else {
-                template = MessageUtil.getDagCheckMsg(locale, "TARGET_NODE_INFO");
-                grade = Level.INFO;
-            }
-            nameSet.add(name);
-
-            String content = MessageFormat.format(template, current, name);
-
-            TaskDagCheckLog log = TaskDagCheckLog.builder().taskId(taskId).checkType(templateEnum.name()).log(content)
-                    .grade(grade).nodeId(nodeId).build();
-
-            log.setCreateAt(now);
-            log.setCreateUser(userId);
-            result.add(log);
         });
 
         return result;
diff --git a/manager/tm/src/main/resources/application-default.yml b/manager/tm/src/main/resources/application-default.yml
index 4cca04a79a..d5d1aece62 100644
--- a/manager/tm/src/main/resources/application-default.yml
+++ b/manager/tm/src/main/resources/application-default.yml
@@ -125,3 +125,11 @@ task:
     times: 2  #任务重置重试次数
     interval: 30000 #任务重置重试间隔，单位毫秒
     timeoutInterval: 50 #任务重置引擎没有返回时的超时时间，单位秒
+
+report:
+  url:
+    measurementId: 'G-XWF69Y4KY3'
+    apiSecret: 'noI4OfyaSuCOaFbr7AKbxA'
+    clientId: '796104786.1713955139'
+  data:
+    oss: ${REPORT_DATA_OSS:false}
\ No newline at end of file
diff --git a/manager/tm/src/main/resources/dagCheck_en_US.properties b/manager/tm/src/main/resources/dagCheck_en_US.properties
index 571028e40f..43f478352a 100644
--- a/manager/tm/src/main/resources/dagCheck_en_US.properties
+++ b/manager/tm/src/main/resources/dagCheck_en_US.properties
@@ -29,8 +29,6 @@ TARGET_SETTING_WRAN_UPDATEFIELD=$date [Target node Settings detection] : To ensu
 BATCH_READ_FUNCTION=full
 STREAM_READ_FUNCTION=cdc
 TABLE_EDIT_NODE_INFO=$date [$taskName][Table edit node setting check]: Node {1} passed the check
-TARGET_NODE_INFO=$date [$taskName][Target node setting check]: Node {1} passed the check
-TARGET_NODE_ERROR=$date [$taskName][Target node setting check]: Node {1} failed the check, abnormal item attribute: {Node name}, abnormal reason: Node name is duplicated, please reset
 SOURCE_CONNECT_INFO=$date [$taskName][Source connection check]: Connection {0} passed the check
 SOURCE_CONNECT_ERROR=$date [$taskName][Source connection check]: Connection {0} failed the check. \n {1}
 TARGET_CONNECT_INFO=$date [$taskName][Target connection check]: Connection {0} passed the check
diff --git a/manager/tm/src/main/resources/dagCheck_zh_CN.properties b/manager/tm/src/main/resources/dagCheck_zh_CN.properties
index 48be919bbf..aabbc0b92e 100644
--- a/manager/tm/src/main/resources/dagCheck_zh_CN.properties
+++ b/manager/tm/src/main/resources/dagCheck_zh_CN.properties
@@ -29,8 +29,6 @@ TARGET_SETTING_WRAN_UPDATEFIELD=$date\u3010\u76EE\u6807\u8282\u70B9\u8BBE\u7F6E\
 BATCH_READ_FUNCTION=\u5168\u91CF
 STREAM_READ_FUNCTION=\u589E\u91CF
 TABLE_EDIT_NODE_INFO=$date\u3010$taskName\u3011\u3010\u8868\u7F16\u8F91\u8282\u70B9\u8BBE\u7F6E\u68C0\u6D4B\u3011\uFF1A\u8282\u70B9{1}\u68C0\u6D4B\u901A\u8FC7
-TARGET_NODE_INFO=$date\u3010$taskName\u3011\u3010\u76EE\u6807\u8282\u70B9\u8BBE\u7F6E\u68C0\u6D4B\u3011\uFF1A\u8282\u70B9{1}\u68C0\u6D4B\u901A\u8FC7
-TARGET_NODE_ERROR=$date\u3010$taskName\u3011\u3010\u76EE\u6807\u8282\u70B9\u8BBE\u7F6E\u68C0\u6D4B\u3011\uFF1A\u8282\u70B9{1}\u68C0\u6D4B\u672A\u901A\u8FC7\uFF0C\u5F02\u5E38\u9879\u5C5E\u6027\uFF1A{\u8282\u70B9\u540D\u79F0}\uFF0C\u5F02\u5E38\u539F\u56E0\uFF1A\u8282\u70B9\u540D\u79F0\u91CD\u590D\uFF0C\u8BF7\u91CD\u65B0\u8BBE\u7F6E
 SOURCE_CONNECT_INFO=$date\u3010$taskName\u3011\u3010\u6E90\u8FDE\u63A5\u68C0\u6D4B\u3011\uFF1A\u8FDE\u63A5{0}\u68C0\u6D4B\u901A\u8FC7
 SOURCE_CONNECT_ERROR=$date\u3010$taskName\u3011\u3010\u6E90\u8FDE\u63A5\u68C0\u6D4B\u3011\uFF1A\u8FDE\u63A5{0}\u68C0\u6D4B\u672A\u901A\u8FC7\u3002 \n {1}
 TARGET_CONNECT_INFO=$date\u3010$taskName\u3011\u3010\u76EE\u6807\u8FDE\u63A5\u68C0\u6D4B\u3011\uFF1A\u8FDE\u63A5{0}\u68C0\u6D4B\u901A\u8FC7
diff --git a/manager/tm/src/main/resources/dagCheck_zh_TW.properties b/manager/tm/src/main/resources/dagCheck_zh_TW.properties
index 2793da0d8e..e8365ec03b 100644
--- a/manager/tm/src/main/resources/dagCheck_zh_TW.properties
+++ b/manager/tm/src/main/resources/dagCheck_zh_TW.properties
@@ -29,8 +29,6 @@ TARGET_SETTING_WRAN_UPDATEFIELD=$date\u3010\u76EE\u6A19\u7BC0\u9EDE\u8A2D\u7F6E\
 BATCH_READ_FUNCTION=\u5168\u91CF
 STREAM_READ_FUNCTION=\u589E\u91CF
 TABLE_EDIT_NODE_INFO=$date\u3010$taskName\u3011\u3010\u8868\u7DE8\u8F2F\u7BC0\u9EDE\u8A2D\u7F6E\u6AA2\u6E2C\u3011\uFF1A\u7BC0\u9EDE{1}\u6AA2\u6E2C\u901A\u904E
-TARGET_NODE_INFO=$date\u3010$taskName\u3011\u3010\u76EE\u6A19\u7BC0\u9EDE\u8A2D\u7F6E\u6AA2\u6E2C\u3011\uFF1A\u7BC0\u9EDE{1}\u6AA2\u6E2C\u901A\u904E
-TARGET_NODE_ERROR=$date\u3010$taskName\u3011\u3010\u76EE\u6A19\u7BC0\u9EDE\u8A2D\u7F6E\u6AA2\u6E2C\u3011\uFF1A\u7BC0\u9EDE{1}\u6AA2\u6E2C\u672A\u901A\u904E\uFF0C\u7570\u5E38\u9805\u5C6C\u6027\uFF1A{\u7BC0\u9EDE\u540D\u7A31}\uFF0C\u7570\u5E38\u539F\u56E0\uFF1A\u7BC0\u9EDE\u540D\u7A31\u91CD\u5FA9\uFF0C\u8ACB\u91CD\u65B0\u8A2D\u7F6E
 SOURCE_CONNECT_INFO=$date\u3010$taskName\u3011\u3010\u6E90\u9023\u63A5\u6AA2\u6E2C\u3011\uFF1A\u9023\u63A5{0}\u6AA2\u6E2C\u901A\u904E
 SOURCE_CONNECT_ERROR=$date\u3010$taskName\u3011\u3010\u6E90\u9023\u63A5\u6AA2\u6E2C\u3011\uFF1A\u9023\u63A5{0}\u6AA2\u6E2C\u672A\u901A\u904E\u3002\n {1}
 TARGET_CONNECT_INFO=$date\u3010$taskName\u3011\u3010\u76EE\u6A19\u9023\u63A5\u6AA2\u6E2C\u3011\uFF1A\u9023\u63A5{0}\u6AA2\u6E2C\u901A\u904E
diff --git a/manager/tm/src/main/resources/messages.properties b/manager/tm/src/main/resources/messages.properties
index 4e7d896039..c3d0c1c690 100644
--- a/manager/tm/src/main/resources/messages.properties
+++ b/manager/tm/src/main/resources/messages.properties
@@ -253,3 +253,8 @@ webhook.reOpen.failed=WebHook re-open failed, please retry again
 webhook.history.hook.id.error=WebHook ID must not be empty
 webhook.history.page.from.error=The paging parameter [pageFrom] is invalid, the starting value should be greater than or equal to 0, actual value is: {0}
 webhook.history.page.size.error=The paging parameter [pageSize] is invalid, the page size value should be greater than to 0, actual value is: {0}
+
+
+#auth
+insufficient.permissions=Insufficient permissions: {0}, current operation requires permission: {1}
+inspect.result.not.exists=The verification record for the current verification task no longer exists: {0}
diff --git a/manager/tm/src/main/resources/messages_en_US.properties b/manager/tm/src/main/resources/messages_en_US.properties
index b4a8bed480..cfb9cb4aa8 100644
--- a/manager/tm/src/main/resources/messages_en_US.properties
+++ b/manager/tm/src/main/resources/messages_en_US.properties
@@ -184,3 +184,7 @@ webhook.reOpen.failed=WebHook re-open failed, please retry again
 webhook.history.hook.id.error=WebHook ID must not be empty
 webhook.history.page.from.error=The paging parameter [pageFrom] is invalid, the starting value should be greater than or equal to 0, actual value is: {0}
 webhook.history.page.size.error=The paging parameter [pageSize] is invalid, the page size value should be greater than to 0, actual value is: {0}
+
+#auth
+insufficient.permissions=Insufficient permissions: {0}, current operation requires permission: {1}
+inspect.result.not.exists=The verification record for the current verification task no longer exists: {0}
diff --git a/manager/tm/src/main/resources/messages_zh_CN.properties b/manager/tm/src/main/resources/messages_zh_CN.properties
index 6be09d61d8..4d2b55c2ea 100644
--- a/manager/tm/src/main/resources/messages_zh_CN.properties
+++ b/manager/tm/src/main/resources/messages_zh_CN.properties
@@ -285,3 +285,7 @@ webhook.history.hook.id.error=WebHook ID \u4E0D\u80FD\u4E3A\u7A7A
 webhook.history.page.from.error=\u5206\u9875\u53C2\u6570[pageFrom]\u65E0\u6548\uFF0C\u8D77\u59CB\u503C\u5E94\u8BE5\u5927\u4E8E\u7B49\u4E8E0\uFF0C\u5B9E\u9645\u503C\u4E3A\uFF1A{0}
 webhook.history.page.size.error=\u5206\u9875\u53C2\u6570[pageSize]\u65E0\u6548\uFF0C\u6BCF\u9875\u5927\u5C0F\u5E94\u8BE5\u5927\u4E8E0\uFF0C\u5B9E\u9645\u503C\u4E3A\uFF1A{0}
 
+#auth
+insufficient.permissions=\u6743\u9650\u4E0D\u8DB3: {0}\uFF0C\u5F53\u524D\u64CD\u4F5C\u9700\u8981\u6743\u9650\uFF1A{1}
+inspect.result.not.exists=\u5F53\u524D\u6821\u9A8C\u4EFB\u52A1\u7684\u6821\u9A8C\u8BB0\u5F55\u5DF2\u7ECF\u4E0D\u5B58\u5728\uFF1A{0}
+
diff --git a/manager/tm/src/main/resources/messages_zh_TW.properties b/manager/tm/src/main/resources/messages_zh_TW.properties
index 677e23c916..0b14e3a24f 100644
--- a/manager/tm/src/main/resources/messages_zh_TW.properties
+++ b/manager/tm/src/main/resources/messages_zh_TW.properties
@@ -121,3 +121,7 @@ webhook.reOpen.failed=WebHook\u91CD\u65B0\u6253\u958B\u5931\u6557\uFF0C\u8ACB\u9
 webhook.history.hook.id.error=WebHook ID \u4E0D\u80FD\u70BA\u7A7A
 webhook.history.page.from.error=\u5206\u9801\u53C3\u6578[pageFrom]\u7121\u6548\uFF0C\u8D77\u59CB\u503C\u61C9\u8A72\u5927\u65BC\u7B49\u65BC0\uFF0C\u5BE6\u969B\u503C\u70BA\uFF1A{0}
 webhook.history.page.size.error=\u5206\u9801\u53C3\u6578[pageSize]\u7121\u6548\uFF0C\u6BCF\u9801\u5927\u5C0F\u61C9\u8A72\u5927\u65BC0\uFF0C\u5BE6\u969B\u503C\u70BA\uFF1A{0}
+
+#auth
+insufficient.permissions=\u8A31\u53EF\u6B0A\u4E0D\u8DB3: {0}\uFF0C\u5F53\u524D\u64CD\u4F5C\u9700\u8981\u6743\u9650\uFF1A{1}
+inspect.result.not.exists=\u7576\u524D\u6821\u9A57\u4EFB\u52D9\u7684\u6821\u9A57\u8A18\u9304\u5DF2\u7D93\u4E0D\u5B58\u5728\uFF1A{0}


Test patch:
diff --git a/assets/example-1-test.jpg b/assets/example-1-test.jpg
new file mode 100644
index 0000000000..85c262ecc7
Binary files /dev/null and b/assets/example-1-test.jpg differ
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNodeTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNodeTest.java
index 8ba4321bda..262ade7cb5 100644
--- a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNodeTest.java
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/HazelcastBaseNodeTest.java
@@ -8,6 +8,7 @@
 import com.hazelcast.jet.core.JobStatus;
 import com.hazelcast.jet.core.Outbox;
 import com.tapdata.constant.BeanUtil;
+import com.tapdata.constant.ConnectorConstant;
 import com.tapdata.entity.MessageEntity;
 import com.tapdata.entity.OperationType;
 import com.tapdata.entity.TapdataEvent;
@@ -1354,11 +1355,17 @@ void testCloseWhenDoCloseError() {
 	@Nested
 	@DisplayName("ErrorHandle method test")
 	class ErrorHandleTest {
+		HttpClientMongoOperator clientMongoOperator;
+		ObsLogger obsLogger;
 		@BeforeEach
 		void beforeEach() {
+			obsLogger = mock(ObsLogger.class);
+			clientMongoOperator = mock(HttpClientMongoOperator.class);
 			hazelcastBaseNode = spy(hazelcastBaseNode);
-			ReflectionTestUtils.setField(hazelcastBaseNode,"clientMongoOperator",mock(HttpClientMongoOperator.class));
+			ReflectionTestUtils.setField(hazelcastBaseNode,"clientMongoOperator", clientMongoOperator);
 			ReflectionTestUtils.setField(hazelcastBaseNode,"obsLogger",mock(ObsLogger.class));
+			doNothing().when(clientMongoOperator).insertOne(anyList(), anyString());
+			doNothing().when(obsLogger).warn(anyString(), anyString());
 		}
 
 		@Test
@@ -1426,6 +1433,14 @@ void testErrorHandleSkipTapMessageIsNull() {
 			TapCodeException tapCodeException = new TapCodeException("1001");
 			Assertions.assertNull(hazelcastBaseNode.errorHandle(tapCodeException,"error3"));
 		}
+
+		@Test
+		void testClientMongoOperatorIsNull() {
+			ReflectionTestUtils.setField(hazelcastBaseNode,"clientMongoOperator", null);
+			when(hazelcastBaseNode.initClientMongoOperator()).thenReturn(clientMongoOperator);
+			TapCodeException tapCodeException = new TapCodeException("1001");
+			Assertions.assertNull(hazelcastBaseNode.errorHandle(tapCodeException,"error3"));
+		}
 	}
 
 	@Nested
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNodeTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNodeTest.java
index ab25b82eaa..9f14b8d9cd 100644
--- a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNodeTest.java
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastPdkBaseNodeTest.java
@@ -19,7 +19,6 @@
 import io.tapdata.entity.simplify.TapSimplify;
 import io.tapdata.flow.engine.V2.entity.PdkStateMap;
 import io.tapdata.flow.engine.V2.filter.TapRecordSkipDetector;
-import io.tapdata.observable.logging.ObsLogger;
 import io.tapdata.pdk.apis.entity.ConnectionOptions;
 import io.tapdata.pdk.apis.entity.ConnectorCapabilities;
 import io.tapdata.pdk.core.entity.params.PDKMethodInvoker;
@@ -27,6 +26,7 @@
 import org.bson.Document;
 import org.bson.types.ObjectId;
 import org.junit.jupiter.api.*;
+import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Update;
 import org.springframework.test.util.ReflectionTestUtils;
 
@@ -358,12 +358,18 @@ void cleanFunctionRetryTest(){
 		}
 
 		@Test
-		void signFunctionRetryTest(){
+		void signFunctionRetryTest() {
 			when(mongoCollection.update(any(), any(), anyString())).thenAnswer(a -> {
-				Update update = (Update)a.getArgument(1);
-				Document updateObject = (Document)update.getUpdateObject().get("$set");
+				Query query = (Query) a.getArgument(0);
+				assertEquals(true, query.getQueryObject().containsKey("_id"));
+				Document orCriteria = (Document) query.getQueryObject().get("$or");
+				Document existDocument = (Document) orCriteria.get("functionRetryStatus");
+				assertEquals(true, existDocument.get("$exists"));
+				assertEquals(TaskDto.RETRY_STATUS_NONE, orCriteria.get("functionRetryStatus"));
+				Update update = (Update) a.getArgument(1);
+				Document updateObject = (Document) update.getUpdateObject().get("$set");
 				String functionRetryStatus = (String) updateObject.get("functionRetryStatus");
-				assertEquals("Retrying",functionRetryStatus);
+				assertEquals("Retrying", functionRetryStatus);
 				return null;
 			});
 			spyhazelcastPdkBaseNode.signFunctionRetry("65aa211475a5ac694df51c69");
@@ -446,55 +452,89 @@ void test2(){
 	}
 
 	@Nested
+	@DisplayName("Method generateNodeConfig test")
 	class GenerateNodeConfigTest {
 
 		@BeforeEach
 		void setUp(){
 			hazelcastPdkBaseNode = mock(HazelcastPdkBaseNode.class);
-			doCallRealMethod().when(hazelcastPdkBaseNode).generateNodeConfig(any(), any());
+			when(hazelcastPdkBaseNode.generateNodeConfig(any(Node.class), any(TaskDto.class))).thenCallRealMethod();
 		}
 
 		@Test
+		@DisplayName("test TableNode")
 		void testTableNode() {
 			Map<String, Object> nodeConfig = TapSimplify.map(TapSimplify.entry("key", "value"));
 			TableNode node1 = new TableNode();
 			node1.setNodeConfig(null);
 			TableNode node2 = new TableNode();
 			node2.setNodeConfig(nodeConfig);
-			Assertions.assertEquals(1, hazelcastPdkBaseNode.generateNodeConfig(node1, true).size());
-			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, true).containsKey("doubleActive"));
-			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node2, true).size());
+			taskDto.setDoubleActive(true);
+			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).size());
+			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).containsKey("doubleActive"));
+			Assertions.assertEquals(3, hazelcastPdkBaseNode.generateNodeConfig(node2, taskDto).size());
 		}
 
 		@Test
+		@DisplayName("test DatabaseNode")
 		void testDatabaseNode() {
 			Map<String, Object> nodeConfig = TapSimplify.map(TapSimplify.entry("key", "value"));
 			DatabaseNode node1 = new DatabaseNode();
 			node1.setNodeConfig(null);
 			DatabaseNode node2 = new DatabaseNode();
 			node2.setNodeConfig(nodeConfig);
-			Assertions.assertEquals(1, hazelcastPdkBaseNode.generateNodeConfig(node1, true).size());
-			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, true).containsKey("doubleActive"));
-			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node2, true).size());
+			taskDto.setDoubleActive(true);
+			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).size());
+			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).containsKey("doubleActive"));
+			Assertions.assertEquals(3, hazelcastPdkBaseNode.generateNodeConfig(node2, taskDto).size());
 		}
 
 		@Test
+		@DisplayName("test LogCollectorNode")
 		void testLogCollectorNode() {
 			Map<String, Object> nodeConfig = TapSimplify.map(TapSimplify.entry("key", "value"));
 			LogCollectorNode node1 = new LogCollectorNode();
 			node1.setNodeConfig(null);
 			LogCollectorNode node2 = new LogCollectorNode();
 			node2.setNodeConfig(nodeConfig);
-			Assertions.assertEquals(1, hazelcastPdkBaseNode.generateNodeConfig(node1, true).size());
-			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, true).containsKey("doubleActive"));
-			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node2, true).size());
+			taskDto.setDoubleActive(true);
+			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).size());
+			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node1, taskDto).containsKey("doubleActive"));
+			Assertions.assertEquals(3, hazelcastPdkBaseNode.generateNodeConfig(node2, taskDto).size());
 		}
 
 		@Test
+		@DisplayName("test other node type")
 		void testOtherNode() {
 			CacheNode node = new CacheNode();
-			Assertions.assertEquals(1, hazelcastPdkBaseNode.generateNodeConfig(node, true).size());
-			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node, true).containsKey("doubleActive"));
+			taskDto.setDoubleActive(true);
+			Assertions.assertEquals(2, hazelcastPdkBaseNode.generateNodeConfig(node, taskDto).size());
+			Assertions.assertTrue(hazelcastPdkBaseNode.generateNodeConfig(node, taskDto).containsKey("doubleActive"));
+		}
+
+		@Test
+		@DisplayName("test oldVersionTimezone")
+		void testOldVersionTimezone() {
+			TableNode node = new TableNode();
+			taskDto.setOldVersionTimezone(true);
+			Map<String, Object> nodeConfig = hazelcastPdkBaseNode.generateNodeConfig(node, taskDto);
+			assertTrue(nodeConfig.containsKey(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
+			assertTrue((Boolean) nodeConfig.get(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
+			taskDto.setOldVersionTimezone(false);
+			nodeConfig = hazelcastPdkBaseNode.generateNodeConfig(node, taskDto);
+			assertTrue(nodeConfig.containsKey(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
+			assertFalse((Boolean) nodeConfig.get(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
+		}
+
+		@Test
+		@DisplayName("test oldVersionTimezone when set system property")
+		void testOldVersionTimezone1() {
+			TableNode node = new TableNode();
+			taskDto.setOldVersionTimezone(true);
+			System.setProperty(HazelcastPdkBaseNode.OLD_VERSION_TIME_ZONE_PROP_KEY, "false");
+			Map<String, Object> nodeConfig = hazelcastPdkBaseNode.generateNodeConfig(node, taskDto);
+			assertTrue(nodeConfig.containsKey(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
+			assertFalse((Boolean) nodeConfig.get(HazelcastPdkBaseNode.OLD_VERSION_TIMEZONE));
 		}
 	}
 }
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNodeTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNodeTest.java
index a2a258b2b5..3b3940c3da 100644
--- a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNodeTest.java
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/data/pdk/HazelcastSourcePdkDataNodeTest.java
@@ -8,9 +8,12 @@
 import com.tapdata.entity.dataflow.SyncProgress;
 import com.tapdata.entity.dataflow.TableBatchReadStatus;
 import com.tapdata.entity.dataflow.batch.BatchOffsetUtil;
+import com.tapdata.entity.task.config.TaskConfig;
+import com.tapdata.entity.task.config.TaskRetryConfig;
 import com.tapdata.entity.task.context.DataProcessorContext;
 import com.tapdata.entity.task.context.ProcessorBaseContext;
 import com.tapdata.tm.commons.dag.Node;
+import com.tapdata.tm.commons.dag.nodes.DatabaseNode;
 import com.tapdata.tm.commons.dag.nodes.TableNode;
 import io.tapdata.aspect.BatchReadFuncAspect;
 import io.tapdata.aspect.SourceStateAspect;
@@ -27,11 +30,13 @@
 import io.tapdata.entity.schema.TapField;
 import io.tapdata.entity.schema.TapTable;
 import io.tapdata.entity.schema.type.TapDateTime;
+import io.tapdata.entity.schema.type.TapString;
 import io.tapdata.entity.schema.value.DateTime;
 import io.tapdata.error.TaskProcessorExCode_11;
 import io.tapdata.exception.NodeException;
 import io.tapdata.exception.TapCodeException;
 import io.tapdata.observable.logging.ObsLogger;
+import io.tapdata.pdk.apis.consumer.StreamReadConsumer;
 import io.tapdata.pdk.apis.context.TapConnectorContext;
 import io.tapdata.pdk.apis.entity.QueryOperator;
 import io.tapdata.pdk.apis.entity.TapTimeForm;
@@ -40,17 +45,21 @@
 import io.tapdata.pdk.apis.functions.PDKMethod;
 import io.tapdata.pdk.apis.functions.connector.source.BatchCountFunction;
 import io.tapdata.pdk.apis.functions.connector.source.BatchReadFunction;
+import io.tapdata.pdk.apis.functions.connector.source.CountByPartitionFilterFunction;
 import io.tapdata.pdk.apis.functions.connector.source.ExecuteCommandFunction;
 import io.tapdata.pdk.apis.functions.connector.target.QueryByAdvanceFilterFunction;
+import io.tapdata.pdk.apis.spec.TapNodeSpecification;
 import io.tapdata.pdk.core.api.ConnectorNode;
 import io.tapdata.pdk.core.entity.params.PDKMethodInvoker;
 import io.tapdata.pdk.core.monitor.PDKInvocationMonitor;
+import io.tapdata.pdk.core.tapnode.TapNodeInfo;
 import io.tapdata.pdk.core.utils.CommonUtils;
 import io.tapdata.schema.TapTableMap;
 import lombok.SneakyThrows;
 import org.junit.Assert;
 import org.junit.jupiter.api.*;
 import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 import org.springframework.test.util.ReflectionTestUtils;
 
 import java.time.LocalDateTime;
@@ -1418,4 +1427,169 @@ void test1(){
 			Assertions.assertEquals(2,result.size());
 		}
 	}
+	@Nested
+	class StreamReadConsumerStateListenerTest{
+		private ConnectorNode connectorNode;
+		@BeforeEach
+		void before(){
+			connectorNode = mock(ConnectorNode.class);
+			TapNodeInfo tapNodeInfo = mock(TapNodeInfo.class);
+			when(connectorNode.getTapNodeInfo()).thenReturn(tapNodeInfo);
+			TapNodeSpecification tapNodeSpecification = mock(TapNodeSpecification.class);
+			when(tapNodeInfo.getTapNodeSpecification()).thenReturn(tapNodeSpecification);
+			when(tapNodeSpecification.getName()).thenReturn("123");
+		}
+		@DisplayName("test stateListener STATE_STREAM_READ_STARTED")
+		@Test
+		void test1(){
+			try (MockedStatic<PDKInvocationMonitor> pdkInvocationMonitorMockedStatic = mockStatic(PDKInvocationMonitor.class)) {
+				ObsLogger obsLogger = mock(ObsLogger.class);
+				ReflectionTestUtils.setField(hazelcastSourcePdkDataNode, "obsLogger", obsLogger);
+				doNothing().when(obsLogger).info(anyString(), any(), any());
+				PDKMethodInvoker pdkMethodInvoker = mock(PDKMethodInvoker.class);
+				pdkInvocationMonitorMockedStatic.when(() -> PDKInvocationMonitor.invokerRetrySetter(pdkMethodInvoker)).thenAnswer((invocationOnMock) -> {
+					PDKMethodInvoker pdkMethodArgs = (PDKMethodInvoker) invocationOnMock.getArgument(0);
+					assertEquals(pdkMethodInvoker, pdkMethodArgs);
+					return null;
+				});
+				StreamReadConsumer streamReadConsumer = hazelcastSourcePdkDataNode.generateStreamReadConsumer(connectorNode, pdkMethodInvoker);
+				streamReadConsumer.streamReadStarted();
+				verify(obsLogger, times(1)).info(anyString(), any(), any());
+			}
+		}
+
+	}
+
+	@Nested
+	class doBatchCountFunction {
+		long expectedValue = 99;
+		TapTable testTable = new TapTable("testTable");
+		BatchCountFunction mockBatchCountFunction = mock(BatchCountFunction.class);
+
+		@BeforeEach
+		void init(){
+			TaskConfig taskConfig = TaskConfig.create();
+			taskConfig.taskRetryConfig(TaskRetryConfig.create());
+			taskConfig.getTaskRetryConfig().retryIntervalSecond(1000L);
+			when(dataProcessorContext.getTaskConfig()).thenReturn(taskConfig);
+
+		}
+
+		@Test
+		@SneakyThrows
+		@DisplayName("Exception test")
+		void testException() {
+
+			when(dataProcessorContext.getNode()).thenReturn((Node) new DatabaseNode());
+
+			HazelcastSourcePdkBaseNode spyInstance = Mockito.spy(hazelcastSourcePdkDataNode);
+			doReturn(new ConnectorNode()).when(spyInstance).getConnectorNode();
+			when(mockBatchCountFunction.count(any(), any())).thenThrow(new Exception());
+
+			try {
+				spyInstance.doBatchCountFunction(mockBatchCountFunction, testTable);
+			} catch (Exception e) {
+				assertTrue(null != e.getCause() && null != e.getCause().getCause() && (e.getCause().getCause() instanceof NodeException), e.getMessage());
+			}
+		}
+
+		@Test
+		@SneakyThrows
+		@DisplayName("Normal  BatchCount test")
+		void testNormalBatchCount() {
+
+			when(dataProcessorContext.getNode()).thenReturn((Node) new TableNode());
+			HazelcastSourcePdkBaseNode spyInstance = Mockito.spy(hazelcastSourcePdkDataNode);
+			doReturn(new ConnectorNode()).when(spyInstance).getConnectorNode();
+			when(mockBatchCountFunction.count(any(), any())).thenReturn(expectedValue);
+
+
+			long actualData = spyInstance.doBatchCountFunction(mockBatchCountFunction, testTable);
+			Assertions.assertTrue(expectedValue == actualData);
+		}
+
+
+		@Test
+		@SneakyThrows
+		@DisplayName("Normal  BatchCount WithDataNode test")
+		void testNormalBatchCountWithDataNode() {
+
+			when(dataProcessorContext.getNode()).thenReturn((Node) new DatabaseNode());
+			HazelcastSourcePdkBaseNode spyInstance = Mockito.spy(hazelcastSourcePdkDataNode);
+			doReturn(new ConnectorNode()).when(spyInstance).getConnectorNode();
+			when(mockBatchCountFunction.count(any(), any())).thenReturn(expectedValue);
+
+
+			long actualData = spyInstance.doBatchCountFunction(mockBatchCountFunction, testTable);
+			Assertions.assertTrue(expectedValue == actualData);
+		}
+		@Test
+		@SneakyThrows
+		@DisplayName("Filter BatchCount test")
+		void testFilterBatchCount() {
+
+			TableNode testTableTemp = new TableNode();
+			testTableTemp.setIsFilter(true);
+			testTableTemp.setTableName("testTable");
+			List<QueryOperator> conditions = new ArrayList<>();
+			QueryOperator queryOperator = new QueryOperator();
+			queryOperator.setKey("id");
+			queryOperator.setValue("1");
+			queryOperator.setOperator(5);
+
+			conditions.add(queryOperator);
+			testTableTemp.setConditions(conditions);
+			when(dataProcessorContext.getNode()).thenReturn((Node) testTableTemp);
+			HazelcastSourcePdkBaseNode spyInstance = Mockito.spy(hazelcastSourcePdkDataNode);
+			ConnectorNode connectorNode = new ConnectorNode();
+			ConnectorFunctions connectorFunctions = new ConnectorFunctions();
+			CountByPartitionFilterFunction countByPartitionFilterFunction = mock(CountByPartitionFilterFunction.class);
+			connectorFunctions.supportCountByPartitionFilterFunction(countByPartitionFilterFunction);
+			ReflectionTestUtils.setField(connectorNode, "connectorFunctions", connectorFunctions);
+			doReturn(connectorNode).when(spyInstance).getConnectorNode();
+			LinkedHashMap<String, TapField> nameFieldMap = new LinkedHashMap<>();
+			TapField tapField = new TapField();
+			tapField.setTapType(new TapString());
+			nameFieldMap.put("id",tapField);
+			testTable.setNameFieldMap(nameFieldMap);
+			TapTableMap<String, TapTable> tapTableMap = TapTableMap.create("testTable", testTable);
+			when(dataProcessorContext.getTapTableMap()).thenReturn(tapTableMap);
+			when(countByPartitionFilterFunction.countByPartitionFilter(any(),any(),any())).thenReturn(expectedValue);
+
+			long actualData = spyInstance.doBatchCountFunction(mockBatchCountFunction, testTable);
+			Assertions.assertTrue(expectedValue == actualData);
+
+		}
+
+
+		@Test
+		@SneakyThrows
+		@DisplayName("Filter filterBatchCount  not support")
+		void testFilterBatchCountNotSupport() {
+
+			TableNode testTableTemp = new TableNode();
+			testTableTemp.setIsFilter(true);
+			testTableTemp.setTableName("testTable");
+			List<QueryOperator> conditions = new ArrayList<>();
+			QueryOperator queryOperator = new QueryOperator();
+			queryOperator.setKey("id");
+			queryOperator.setValue("1");
+			queryOperator.setOperator(5);
+
+			conditions.add(queryOperator);
+			testTableTemp.setConditions(conditions);
+			when(dataProcessorContext.getNode()).thenReturn((Node) testTableTemp);
+			HazelcastSourcePdkBaseNode spyInstance = Mockito.spy(hazelcastSourcePdkDataNode);
+			ConnectorNode connectorNode = new ConnectorNode();
+			ConnectorFunctions connectorFunctions = new ConnectorFunctions();
+			ReflectionTestUtils.setField(connectorNode, "connectorFunctions", connectorFunctions);
+			doReturn(connectorNode).when(spyInstance).getConnectorNode();
+			when(mockBatchCountFunction.count(any(), any())).thenReturn(expectedValue);
+
+			long actualData = spyInstance.doBatchCountFunction(mockBatchCountFunction, testTable);
+			Assertions.assertTrue(expectedValue == actualData);
+
+		}
+
+	}
 }
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNodeTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNodeTest.java
new file mode 100644
index 0000000000..821a5b789e
--- /dev/null
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/node/hazelcast/processor/HazelcastRenameTableProcessorNodeTest.java
@@ -0,0 +1,92 @@
+package io.tapdata.flow.engine.V2.node.hazelcast.processor;
+
+import base.hazelcast.BaseHazelcastNodeTest;
+import com.tapdata.entity.TapdataEvent;
+import com.tapdata.tm.commons.dag.process.TableRenameProcessNode;
+import io.tapdata.entity.event.TapBaseEvent;
+import io.tapdata.entity.event.control.StopEvent;
+import io.tapdata.entity.event.ddl.entity.ValueChange;
+import io.tapdata.entity.event.ddl.table.TapCreateTableEvent;
+import io.tapdata.entity.event.ddl.table.TapDropTableEvent;
+import io.tapdata.entity.event.ddl.table.TapRenameTableEvent;
+import io.tapdata.entity.event.dml.TapInsertRecordEvent;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Collections;
+
+import static org.mockito.Mockito.doCallRealMethod;
+
+/**
+ * @author <a href="mailto:harsen_lin@163.com">Harsen</a>
+ * @version v1.0 2024/5/21 15:38 Create
+ */
+class HazelcastRenameTableProcessorNodeTest extends BaseHazelcastNodeTest {
+
+    private static final String tableName = "TEST";
+    private static final String expected = tableName;
+    private HazelcastRenameTableProcessorNode instance;
+
+    @BeforeEach
+    void beforeSetUp() {
+        super.allSetup();
+
+        TableRenameProcessNode node = new TableRenameProcessNode();
+        ReflectionTestUtils.setField(processorBaseContext, "node", node);
+        doCallRealMethod().when(processorBaseContext).getNode();
+
+        instance = new HazelcastRenameTableProcessorNode(processorBaseContext);
+    }
+
+    @Test
+    void testTryProcess() {
+        TapInsertRecordEvent insertRecord = TapInsertRecordEvent.create().table(tableName);
+        TapdataEvent event = new TapdataEvent();
+        event.setTapEvent(insertRecord);
+
+        instance.tryProcess(event, (tapdataEvent, processResult) -> {
+            TapBaseEvent tapEvent = (TapBaseEvent) tapdataEvent.getTapEvent();
+            Assertions.assertEquals(expected, tapEvent.getTableId(), String.format("Case '%s' to '%s'", tableName, expected));
+        });
+
+        // Verify un-TapBaseEvent has not exceptions.
+        TapdataEvent tmpEvent = new TapdataEvent();
+        tmpEvent.setTapEvent(new StopEvent());
+        instance.tryProcess(tmpEvent, (tapdataEvent, processResult) -> {
+        });
+    }
+
+    @Nested
+    class GetTgtTableNameFromTapEventTest {
+        @Test
+        void testTableIUDAndRename() {
+
+            // create table
+            TapCreateTableEvent createTable = new TapCreateTableEvent();
+            createTable.setTableId(tableName);
+            String currentTableName = instance.getTgtTableNameFromTapEvent(createTable);
+            Assertions.assertEquals(expected, currentTableName);
+
+            // rename table
+            TapRenameTableEvent tableEvent = new TapRenameTableEvent();
+            tableEvent.setTableId(tableName);
+            tableEvent.setNameChanges(Collections.singletonList(new ValueChange<>(tableName, expected)));
+            currentTableName = instance.getTgtTableNameFromTapEvent(tableEvent);
+            Assertions.assertEquals(expected, currentTableName);
+
+            // drop table
+            TapDropTableEvent dropTable = new TapDropTableEvent();
+            dropTable.setTableId(tableName);
+            currentTableName = instance.getTgtTableNameFromTapEvent(dropTable);
+            Assertions.assertEquals(expected, currentTableName);
+
+            // second stop table test tableName not in cache
+            currentTableName = instance.getTgtTableNameFromTapEvent(dropTable);
+            Assertions.assertEquals(expected, currentTableName);
+        }
+    }
+
+}
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskServiceTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskServiceTest.java
index 15903bdae3..d28fc563b1 100644
--- a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskServiceTest.java
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/V2/task/impl/HazelcastTaskServiceTest.java
@@ -16,6 +16,7 @@
 import com.tapdata.tm.commons.dag.process.*;
 import com.tapdata.tm.commons.dag.vo.ReadPartitionOptions;
 import com.tapdata.tm.commons.externalStorage.ExternalStorageDto;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
 import com.tapdata.tm.commons.task.dto.TaskDto;
 import io.tapdata.common.SettingService;
 import io.tapdata.entity.schema.TapTable;
@@ -25,8 +26,11 @@
 import io.tapdata.flow.engine.V2.node.hazelcast.processor.*;
 import io.tapdata.flow.engine.V2.node.hazelcast.processor.join.HazelcastJoinProcessor;
 import io.tapdata.flow.engine.V2.util.ExternalStorageUtil;
+import io.tapdata.flow.engine.util.TaskDtoUtil;
+import io.tapdata.observable.logging.ObsLogger;
 import io.tapdata.schema.TapTableMap;
 import lombok.SneakyThrows;
+import org.bson.types.ObjectId;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
@@ -383,4 +387,56 @@ void testCreateNode21(){
             assertEquals(HazelcastAddDateFieldProcessNode.class, actual.getClass());
         }
     }
+
+    @Nested
+    class CleanAllUnselectedErrorTest {
+        HazelcastTaskService hazelcastTaskService;
+        TaskDto taskDto;
+        ObsLogger obsLogger;
+        ClientMongoOperator clientMongoOperator;
+
+        List<ErrorEvent> errorEvents;
+        ErrorEvent errorEvent;
+        @BeforeEach
+        void init() {
+            clientMongoOperator = mock(ClientMongoOperator.class);
+            hazelcastTaskService = mock(HazelcastTaskService.class);
+            ReflectionTestUtils.setField(hazelcastTaskService, "clientMongoOperator", clientMongoOperator);
+
+            taskDto = mock(TaskDto.class);
+            obsLogger = mock(ObsLogger.class);
+
+            errorEvents = new ArrayList<>();
+            errorEvent = new ErrorEvent();
+
+
+            when(taskDto.getErrorEvents()).thenReturn(errorEvents);
+            doNothing().when(taskDto).setErrorEvents(anyList());
+            when(taskDto.getId()).thenReturn(new ObjectId());
+
+            doCallRealMethod().when(hazelcastTaskService).cleanAllUnselectedError(taskDto, obsLogger);
+        }
+        @Test
+        void testNormal() {
+            errorEvents.add(errorEvent);
+            try(MockedStatic<TaskDtoUtil> tdu = mockStatic(TaskDtoUtil.class)) {
+                tdu.when(() -> TaskDtoUtil.updateErrorEvent(any(ClientMongoOperator.class), anyList(), any(ObjectId.class), any(ObsLogger.class), anyString())).thenAnswer(a -> null);
+                hazelcastTaskService.cleanAllUnselectedError(taskDto, obsLogger);
+                verify(taskDto).getErrorEvents();
+                verify(taskDto).setErrorEvents(anyList());
+                verify(taskDto).getId();
+            }
+        }
+
+        @Test
+        void testEmpty() {
+            try(MockedStatic<TaskDtoUtil> tdu = mockStatic(TaskDtoUtil.class)) {
+                tdu.when(() -> TaskDtoUtil.updateErrorEvent(any(ClientMongoOperator.class), anyList(), any(ObjectId.class), any(ObsLogger.class), anyString())).thenAnswer(a -> null);
+                hazelcastTaskService.cleanAllUnselectedError(taskDto, obsLogger);
+                verify(taskDto).getErrorEvents();
+                verify(taskDto, times(0)).setErrorEvents(anyList());
+                verify(taskDto, times(0)).getId();
+            }
+        }
+    }
 }
diff --git a/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/util/TaskDtoUtilTest.java b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/util/TaskDtoUtilTest.java
new file mode 100644
index 0000000000..3d12543533
--- /dev/null
+++ b/iengine/iengine-app/src/test/java/io/tapdata/flow/engine/util/TaskDtoUtilTest.java
@@ -0,0 +1,59 @@
+package io.tapdata.flow.engine.util;
+
+import com.tapdata.mongo.ClientMongoOperator;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
+import io.tapdata.observable.logging.ObsLogger;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class TaskDtoUtilTest {
+
+    @Nested
+    class UpdateErrorEventTest {
+        ClientMongoOperator clientMongoOperator;
+        List<ErrorEvent> errorEvents;
+        ObsLogger obsLogger;
+        String errorMsg;
+        ObjectId taskId;
+        @BeforeEach
+        void init() {
+            clientMongoOperator = mock(ClientMongoOperator.class);
+            errorEvents = mock(List.class);
+            obsLogger = mock(ObsLogger.class);
+            errorMsg = "errorMsg";
+            taskId = mock(ObjectId.class);
+            doNothing().when(clientMongoOperator).insertOne(anyList(), anyString());
+            doNothing().when(obsLogger).warn(anyString(), anyString());
+        }
+
+        @Test
+        void testNormal() {
+            Assertions.assertDoesNotThrow(() -> TaskDtoUtil.updateErrorEvent(clientMongoOperator, errorEvents, taskId, obsLogger, errorMsg));
+            verify(clientMongoOperator).insertOne(anyList(), anyString());
+            verify(obsLogger, times(0)).warn(anyString(), anyString());
+        }
+
+        @Test
+        void testException() {
+            doAnswer(a -> {
+                throw new Exception("xxx");
+            }).when(clientMongoOperator).insertOne(anyList(), anyString());
+            Assertions.assertDoesNotThrow(() -> TaskDtoUtil.updateErrorEvent(clientMongoOperator, errorEvents, taskId, obsLogger, errorMsg));
+            verify(clientMongoOperator).insertOne(anyList(), anyString());
+            verify(obsLogger).warn(anyString(), anyString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetTest.java b/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetTest.java
index ad9e9c4eee..eb993c6157 100644
--- a/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetTest.java
+++ b/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetTest.java
@@ -8,6 +8,11 @@
 import java.util.Map;
 
 class BatchOffsetTest {
+    @Test
+    void testClass() {
+        Class<?>[] interfaces = BatchOffset.class.getInterfaces();
+        Assertions.assertEquals(2, interfaces.length);
+    }
     @Nested
     class CloneTest {
         @Test
diff --git a/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtilTest.java b/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtilTest.java
index 1da7a4ba86..c3b49ce762 100644
--- a/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtilTest.java
+++ b/iengine/iengine-common/src/test/java/com/tapdata/entity/dataflow/batch/BatchOffsetUtilTest.java
@@ -3,9 +3,6 @@
 
 import com.tapdata.entity.dataflow.SyncProgress;
 import com.tapdata.entity.dataflow.TableBatchReadStatus;
-import com.tapdata.entity.dataflow.batch.BatchOffset;
-import com.tapdata.entity.dataflow.batch.BatchOffsetUtil;
-import io.tapdata.entity.event.TapEvent;
 import io.tapdata.entity.event.ddl.entity.ValueChange;
 import io.tapdata.entity.event.ddl.table.TapAlterFieldNameEvent;
 import io.tapdata.entity.event.ddl.table.TapRenameTableEvent;
@@ -15,16 +12,16 @@
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedStatic;
-import org.springframework.test.util.ReflectionTestUtils;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyMap;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
@@ -47,7 +44,7 @@ void init() {
         batchOffset.setStatus(TableBatchReadStatus.OVER.name());
         batchOffset.setOffset(0L);
         batchOffset = new BatchOffset(0L, TableBatchReadStatus.OVER.name());
-        batchOffsetObj = mock(Map.class);
+        batchOffsetObj = new HashMap<>();
         when(syncProgress.getBatchOffsetObj()).thenReturn(batchOffsetObj);
     }
 
@@ -78,6 +75,35 @@ void testNotIsBatchOffsetMap() {
                 Assertions.assertFalse(BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId));
             }
         }
+
+        @Test
+        void testNotContainsKey() {
+            try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId)).thenCallRealMethod();
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(new HashMap<>());
+                Assertions.assertFalse(BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId));
+            }
+        }
+
+        @Test
+        void testContainsKey() {
+            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS, "0");
+            when(syncProgress.getBatchOffsetObj()).thenReturn(objectObjectHashMap);
+            try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId)).thenCallRealMethod();
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(objectObjectHashMap);
+                Assertions.assertFalse(BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId));
+            }
+        }
+        @Test
+        void testNotContainsAnyKey() {
+            try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId)).thenCallRealMethod();
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(0L);
+                Assertions.assertFalse(BatchOffsetUtil.batchIsOverOfTable(syncProgress, tableId));
+            }
+        }
     }
 
     @Nested
@@ -86,7 +112,7 @@ class GetTableOffsetInfoTest {
 
         @BeforeEach
         void init() {
-            when(batchOffsetObj.get(tableId)).thenReturn(0);
+            batchOffsetObj.put(tableId, 0);
         }
 
         @Test
@@ -95,7 +121,6 @@ void testNormal() {
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
                 bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId));
-                verify(batchOffsetObj, times(1)).get(tableId);
             }
         }
 
@@ -105,7 +130,6 @@ void testBatchOffsetObjNotMap() {
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
                 bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId));
-                verify(batchOffsetObj, times(0)).get(tableId);
             }
         }
     }
@@ -130,6 +154,32 @@ void testNotIsBatchOffsetMap() {
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId));
             }
         }
+        @Test
+        void testNotContainsKey() {
+            try (MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(new HashMap<>());
+                bou.when(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId)).thenCallRealMethod();
+                Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId));
+            }
+        }
+        @Test
+        void testContainsKey() {
+            Map<String, Object> map = new HashMap<>();
+            map.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_OFFSET, 0);
+            try (MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(map);
+                bou.when(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId)).thenCallRealMethod();
+                Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId));
+            }
+        }
+        @Test
+        void testNotContainsAnyKey() {
+            try (MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.getTableOffsetInfo(syncProgress, tableId)).thenReturn(0L);
+                bou.when(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId)).thenCallRealMethod();
+                Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.getBatchOffsetOfTable(syncProgress, tableId));
+            }
+        }
     }
 
     @Nested
@@ -160,6 +210,7 @@ void testNormal() {
                 return tableOffsetObjTemp;
             });
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.updateBatchOffset(anyMap(), any(), anyString())).thenCallRealMethod();
                 bou.when(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
@@ -177,6 +228,7 @@ void offsetNotMap() {
                 return tableOffsetObjTemp;
             });
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.updateBatchOffset(anyMap(), any(), anyString())).thenCallRealMethod();
                 bou.when(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
@@ -188,15 +240,70 @@ void offsetNotMap() {
         void testTableOffsetNotBatchOffset() {
             when(syncProgress.getBatchOffsetObj()).thenReturn(batchOffsetObjTemp);
             when(batchOffsetObjTemp.computeIfAbsent(anyString(), any())).thenReturn(0);
-            when(batchOffsetObjTemp.put(anyString(), any(BatchOffset.class))).thenReturn(0);
+            when(batchOffsetObjTemp.put(anyString(), any(Map.class))).thenReturn(0);
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
+                bou.when(() -> BatchOffsetUtil.updateBatchOffset(anyMap(), any(), anyString())).thenCallRealMethod();
                 bou.when(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.updateBatchOffset(syncProgress, tableId, offset, isOverTag));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
                 verify(batchOffsetObjTemp, times(1)).computeIfAbsent(anyString(), any());
-                verify(batchOffsetObjTemp, times(1)).put(anyString(), any(BatchOffset.class));
+                verify(batchOffsetObjTemp, times(1)).put(anyString(), any(Map.class));
             }
         }
+
+        @Test
+        void testUpdateBatchOffset() {
+            Object offset = BatchOffsetUtil.updateBatchOffset(null, 1L, "OK");
+            Assertions.assertNotNull(offset);
+            Assertions.assertEquals(HashMap.class.getName(), offset.getClass().getName());
+            Assertions.assertEquals(1L, ((HashMap<String, Object>)offset).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_OFFSET));
+            Assertions.assertEquals("OK", ((HashMap<String, Object>)offset).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS));
+        }
+
+        @Test
+        void testUpdateBatchOffsetWithOffsetMap() {
+            Object offset = BatchOffsetUtil.updateBatchOffset(null, 1L, "OK");
+            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put("tableId", offset);
+            SyncProgress syncProgress = new SyncProgress();
+            syncProgress.setBatchOffsetObj(objectObjectHashMap);
+            BatchOffsetUtil.updateBatchOffset(syncProgress, "tableId", offset, "NOT_OK");
+            Assertions.assertEquals("NOT_OK", ((Map<String, Object>)offset).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS));
+        }
+        @Test
+        void testUpdateBatchOffsetWithOffsetMapNotHaveOffset() {
+            Map<String, Object> offset = new HashMap<>();
+            offset.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS, "OK");
+            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put("tableId", offset);
+            SyncProgress syncProgress = new SyncProgress();
+            syncProgress.setBatchOffsetObj(objectObjectHashMap);
+            BatchOffsetUtil.updateBatchOffset(syncProgress, "tableId", offset, "NOT_OK");
+            Assertions.assertEquals("NOT_OK", ((Map<String, Object>)objectObjectHashMap.get("tableId")).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS));
+        }
+        @Test
+        void testUpdateBatchOffsetWithOffsetMapNotHaveStatus() {
+            Map<String, Object> offset = new HashMap<>();
+            offset.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_OFFSET, "OK");
+            Map<Object, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put("tableId", offset);
+            SyncProgress syncProgress = new SyncProgress();
+            syncProgress.setBatchOffsetObj(objectObjectHashMap);
+            BatchOffsetUtil.updateBatchOffset(syncProgress, "tableId", offset, "NOT_OK");
+            Assertions.assertEquals("NOT_OK", ((Map<String, Object>)objectObjectHashMap.get("tableId")).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS));
+        }
+        @Test
+        void testUpdateBatchOffsetWithOffsetMapAllHaveStatus() {
+            Map<String, Object> offset = new HashMap<>();
+            offset.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_OFFSET, "OK");
+            offset.put(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS, "OK");
+            HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
+            objectObjectHashMap.put("tableId", offset);
+            SyncProgress syncProgress = new SyncProgress();
+            syncProgress.setBatchOffsetObj(objectObjectHashMap);
+            BatchOffsetUtil.updateBatchOffset(syncProgress, "tableId", offset, "NOT_OK");
+            Assertions.assertEquals("NOT_OK", ((Map<String, Object>)objectObjectHashMap.get("tableId")).get(BatchOffsetUtil.BATCH_READ_CONNECTOR_STATUS));
+        }
     }
 
     @Nested
@@ -207,10 +314,7 @@ void init() {
             oldName = "oldName";
             newName = "newName";
 
-            when(batchOffsetObj.containsKey(oldName)).thenReturn(true);
-            when(batchOffsetObj.get(oldName)).thenReturn(0);
-            when(batchOffsetObj.remove(oldName)).thenReturn(0);
-            when(batchOffsetObj.put(newName, 0)).thenReturn(0);
+            batchOffsetObj.put(oldName, 0);
         }
         @Test
         void testNormal() {
@@ -218,10 +322,6 @@ void testNormal() {
                 bou.when(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
-                verify(batchOffsetObj, times(1)).containsKey(oldName);
-                verify(batchOffsetObj, times(1)).get(oldName);
-                verify(batchOffsetObj, times(1)).remove(oldName);
-                verify(batchOffsetObj, times(1)).put(newName, 0);
             }
         }
         @Test
@@ -231,23 +331,15 @@ void testOffsetNotMap() {
                 bou.when(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
-                verify(batchOffsetObj, times(0)).containsKey(oldName);
-                verify(batchOffsetObj, times(0)).get(oldName);
-                verify(batchOffsetObj, times(0)).remove(oldName);
-                verify(batchOffsetObj, times(0)).put(newName, 0);
             }
         }
         @Test
         void testOffsetNotContainsKey() {
-            when(batchOffsetObj.containsKey(oldName)).thenReturn(false);
+            batchOffsetObj.remove(oldName);
             try(MockedStatic<BatchOffsetUtil> bou = mockStatic(BatchOffsetUtil.class)) {
                 bou.when(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName)).thenCallRealMethod();
                 Assertions.assertDoesNotThrow(() -> BatchOffsetUtil.tableUpdateName(syncProgress, oldName, newName));
                 verify(syncProgress, times(1)).getBatchOffsetObj();
-                verify(batchOffsetObj, times(1)).containsKey(oldName);
-                verify(batchOffsetObj, times(0)).get(oldName);
-                verify(batchOffsetObj, times(0)).remove(oldName);
-                verify(batchOffsetObj, times(0)).put(newName, 0);
             }
         }
     }
diff --git a/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnumsTest.java b/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnumsTest.java
new file mode 100644
index 0000000000..baf3a65d3a
--- /dev/null
+++ b/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionDataTypeEnumsTest.java
@@ -0,0 +1,20 @@
+package com.tapdata.tm.permissions.constants;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.LinkedHashSet;
+
+class DataPermissionDataTypeEnumsTest {
+    @Test
+    void testInspect() {
+        Assertions.assertEquals("Inspect", DataPermissionDataTypeEnums.INSPECT.getCollection());
+        LinkedHashSet<String> actions = DataPermissionDataTypeEnums.INSPECT.allActions();
+        Assertions.assertEquals(5, actions.size());
+    }
+
+    @Test
+    void testParse() {
+        Assertions.assertNotNull(DataPermissionDataTypeEnums.parse("Inspect"));
+    }
+}
\ No newline at end of file
diff --git a/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnumsTest.java b/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnumsTest.java
new file mode 100644
index 0000000000..22fc10cb7e
--- /dev/null
+++ b/manager/tm-api/src/test/java/com/tapdata/tm/permissions/constants/DataPermissionMenuEnumsTest.java
@@ -0,0 +1,12 @@
+package com.tapdata.tm.permissions.constants;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class DataPermissionMenuEnumsTest {
+    @Test
+    void testInspect() {
+        Assertions.assertEquals("", DataPermissionMenuEnums.INSPECT_TACK.getAllDataPermissionName());
+        Assertions.assertEquals(DataPermissionDataTypeEnums.INSPECT, DataPermissionMenuEnums.INSPECT_TACK.getDataType());
+    }
+}
\ No newline at end of file
diff --git a/manager/tm-api/src/test/java/com/tapdata/tm/utils/QueryUtilTest.java b/manager/tm-api/src/test/java/com/tapdata/tm/utils/QueryUtilTest.java
new file mode 100644
index 0000000000..ad4828627b
--- /dev/null
+++ b/manager/tm-api/src/test/java/com/tapdata/tm/utils/QueryUtilTest.java
@@ -0,0 +1,16 @@
+package com.tapdata.tm.utils;
+
+import com.tapdata.tm.base.dto.Where;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class QueryUtilTest {
+    @Nested
+    class ParseWhereToCriteriaTest {
+        @Test
+        void test() {
+            Assertions.assertNotNull(QueryUtil.parseWhereToCriteria(new Where()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/manager/tm-common/src/test/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNodeTest.java b/manager/tm-common/src/test/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNodeTest.java
new file mode 100644
index 0000000000..132e825d92
--- /dev/null
+++ b/manager/tm-common/src/test/java/com/tapdata/tm/commons/dag/process/TableRenameProcessNodeTest.java
@@ -0,0 +1,175 @@
+package com.tapdata.tm.commons.dag.process;
+
+import com.tapdata.tm.commons.dag.DAG;
+import com.tapdata.tm.commons.dag.vo.TableRenameTableInfo;
+import com.tapdata.tm.commons.schema.Schema;
+import io.tapdata.exception.TapCodeException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.util.*;
+import java.util.function.Function;
+
+/**
+ * @author <a href="mailto:harsen_lin@163.com">Harsen</a>
+ * @version v1.0 2024/5/21 11:13 Create
+ */
+class TableRenameProcessNodeTest {
+
+    private final static String prefix = "T_";
+    private final static String suffix = "2024";
+    private final static String replaceBefore = "Before";
+    private final static String replaceAfter = "After";
+    private final static String transferCase = "toUpperCase";
+
+    private final static String customOrigin = "Custom";
+    private final static String customPrevious = "CustomPrevious";
+    private final static String customCurrent = "CustomCurrent";
+    private final static String customEmptyCurrent = "CustomEmptyCurrent";
+
+    private TableRenameProcessNode instance;
+
+    @BeforeEach
+    public void setUp() {
+        instance = new TableRenameProcessNode();
+        instance.setPrefix(prefix);
+        instance.setSuffix(suffix);
+        instance.setReplaceBefore(replaceBefore);
+        instance.setReplaceAfter(replaceAfter);
+        instance.setTransferCase(transferCase);
+        instance.setTableNames(new LinkedHashSet<>());
+        instance.getTableNames().add(new TableRenameTableInfo(customOrigin, customPrevious, customCurrent));
+        instance.getTableNames().add(new TableRenameTableInfo(customEmptyCurrent, customEmptyCurrent, ""));
+    }
+
+    private Schema initSchema(Map<String, String> expectedMap, String tableName, String expectedName) {
+        Schema schema = new Schema();
+        schema.setAncestorsName(tableName);
+        schema.setOriginalName(tableName);
+        schema.setName(tableName);
+        expectedMap.put(tableName, expectedName);
+        return schema;
+    }
+
+    private String expectedTableName(String tableName) {
+        return (prefix + tableName + suffix).toUpperCase(Locale.ROOT);
+    }
+
+
+
+    @Nested
+    class InfoMapTest {
+        String originalName = "origin";
+        String previousName = "previous";
+        String currentName = "current";
+
+        private void testTableNamesIsNull(Function<TableRenameProcessNode, Map<String, TableRenameTableInfo>> mapGetter) {
+            TableRenameProcessNode instance = new TableRenameProcessNode();
+            Map<String, TableRenameTableInfo> map = mapGetter.apply(instance);
+            Assertions.assertNotNull(map);
+            Assertions.assertTrue(map.isEmpty());
+        }
+
+        private void testKeyWithPrevious(String expectedValue, Function<TableRenameProcessNode, Map<String, TableRenameTableInfo>> mapGetter) {
+            TableRenameTableInfo info = new TableRenameTableInfo(originalName, previousName, currentName);
+
+            instance.setTableNames(new LinkedHashSet<>());
+            instance.getTableNames().add(info);
+
+            Map<String, TableRenameTableInfo> map = mapGetter.apply(instance);
+            Assertions.assertNotNull(map);
+            Assertions.assertNotNull(map.get(expectedValue));
+            Assertions.assertEquals(info, map.get(expectedValue));
+        }
+
+        @Test
+        void testOriginalMap() {
+            Function<TableRenameProcessNode, Map<String, TableRenameTableInfo>> mapGetter = TableRenameProcessNode::originalMap;
+
+            testTableNamesIsNull(mapGetter);
+            testKeyWithPrevious(originalName, mapGetter);
+        }
+
+        @Test
+        void testPreviousMap() {
+            Function<TableRenameProcessNode, Map<String, TableRenameTableInfo>> mapGetter = TableRenameProcessNode::previousMap;
+
+            testTableNamesIsNull(mapGetter);
+            testKeyWithPrevious(previousName, mapGetter);
+        }
+
+        @Test
+        void testCurrentMap() {
+            Function<TableRenameProcessNode, Map<String, TableRenameTableInfo>> mapGetter = TableRenameProcessNode::currentMap;
+
+            testTableNamesIsNull(mapGetter);
+            testKeyWithPrevious(currentName, mapGetter);
+        }
+    }
+
+    @Test
+    void testMergeSchema() {
+        // init config
+        Map<String, String> expectedMap = new HashMap<>();
+
+        DAG.Options options = new DAG.Options();
+        options.setIncludes(new ArrayList<>());
+        options.getIncludes().add("TEST");
+
+        // test empty schemas
+        List<List<Schema>> inputSchemas = new ArrayList<>();
+        List<Schema> outputSchemas = instance.mergeSchema(inputSchemas, null, options);
+        Assertions.assertTrue(outputSchemas.isEmpty());
+
+        // test rules
+        inputSchemas.add(Arrays.asList(
+            initSchema(expectedMap, "TEST", expectedTableName("TEST")), // prefix + suffix + upperCase
+            initSchema(expectedMap, "TestBefore", expectedTableName("TestAfter")), // keywords
+            initSchema(expectedMap, customOrigin, customCurrent) // custom table name
+        ));
+
+        outputSchemas = instance.mergeSchema(inputSchemas, null, options);
+        Assertions.assertNotNull(outputSchemas);
+        for (Schema schema : outputSchemas) {
+            Assertions.assertNotNull(schema);
+
+            String expectedName = expectedMap.get(schema.getAncestorsName());
+            Assertions.assertNotNull(expectedName);
+            Assertions.assertEquals(expectedName, schema.getName());
+            Assertions.assertEquals(expectedName, schema.getOriginalName());
+        }
+    }
+
+    @Test
+    void testConvertTableName() {
+        String tableName = "TEST";
+        String expectedValue;
+        Map<String, TableRenameTableInfo> infoMap = instance.originalMap();
+
+        // test normal
+        expectedValue = expectedTableName(tableName);
+        Assertions.assertEquals(expectedValue, instance.convertTableName(infoMap, tableName, false));
+
+        // test in custom-table-name
+        Assertions.assertEquals(customCurrent, instance.convertTableName(infoMap, customOrigin, false));
+
+        // test in custom-table-name, conflict with RenameDDL
+        Assertions.assertThrows(TapCodeException.class, () -> instance.convertTableName(infoMap, customOrigin, true));
+
+        // test empty currentName
+        Assertions.assertEquals(customEmptyCurrent, instance.convertTableName(infoMap, customEmptyCurrent, false));
+
+        // test empty rules
+        instance = new TableRenameProcessNode();
+        Assertions.assertEquals(tableName, instance.convertTableName(tableName));
+        // test illegal transferCase
+        instance.setTransferCase("illegalTransferCase");
+        instance.setReplaceBefore("test replaceBefore not blank & replaceAfter is null");
+        Assertions.assertEquals(tableName, instance.convertTableName(tableName));
+        // test toLowerCase
+        instance.setTransferCase("toLowerCase");
+        Assertions.assertEquals(tableName.toLowerCase(Locale.ROOT), instance.convertTableName(tableName));
+    }
+}
diff --git a/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectServiceImplTest.java b/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectServiceImplTest.java
index 84c4d03795..8dbd96a15b 100644
--- a/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectServiceImplTest.java
+++ b/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectServiceImplTest.java
@@ -1,18 +1,410 @@
 package com.tapdata.tm.inspect.service;
 
+import com.tapdata.tm.base.dto.Field;
+import com.tapdata.tm.base.dto.Filter;
+import com.tapdata.tm.base.dto.Where;
 import com.tapdata.tm.base.exception.BizException;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.inspect.constant.InspectStatusEnum;
+import com.tapdata.tm.inspect.dto.InspectDto;
 import com.tapdata.tm.inspect.repository.InspectRepository;
 import com.tapdata.tm.utils.MessageUtil;
+import com.tapdata.tm.webhook.enums.ConstVariable;
+import org.bson.types.ObjectId;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
 
 public class InspectServiceImplTest {
+    InspectServiceImpl impl;
+    @BeforeEach
+    void init() {
+        impl = mock(InspectServiceImpl.class);
+    }
+
+    @Test
+    void testDataPermissionFindById() {
+        when(impl.dataPermissionFindById(any(ObjectId.class), any(Field.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.dataPermissionFindById(mock(ObjectId.class), mock(Field.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void beforeSave() {
+        doCallRealMethod().when(impl).beforeSave(any(InspectDto.class), any(UserDetail.class));
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.beforeSave(mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void delete() {
+        when(impl.delete(anyString(), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.delete("id", mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void list() {
+        when(impl.list(any(Filter.class), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.list(mock(Filter.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findById() {
+        when(impl.findById(any(Filter.class), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findById(mock(Filter.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void saveInspect() {
+        doCallRealMethod().when(impl).saveInspect(any(TaskDto.class), any(UserDetail.class));
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.saveInspect(mock(TaskDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void createCheckByTask() {
+        when(impl.createCheckByTask(any(TaskDto.class), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.createCheckByTask(mock(TaskDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findByTaskIdList() {
+        when(impl.findByTaskIdList(anyList())).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findByTaskIdList(mock(List.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void deleteByTaskId() {
+        when(impl.deleteByTaskId(anyString())).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.deleteByTaskId("id");
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void doExecuteInspect() {
+        when(impl.doExecuteInspect(any(Where.class), any(InspectDto.class), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.doExecuteInspect(mock(Where.class), mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void updateById() {
+        when(impl.updateById(any(ObjectId.class), any(InspectDto.class), any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.updateById(mock(ObjectId.class), mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findByName() {
+        when(impl.findByName(anyString())).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findByName("id");
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void importData() {
+        doCallRealMethod().when(impl).importData(anyString(), anyString(), any(UserDetail.class));
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.importData("{}", "{}", mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void setRepeatInspectTask() {
+        doCallRealMethod().when(impl).setRepeatInspectTask();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.setRepeatInspectTask();
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void updateStatusById() {
+        when(impl.updateStatusById(anyString(), any(InspectStatusEnum.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.updateStatusById("id", mock(InspectStatusEnum.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void updateStatusByIds() {
+        when(impl.updateStatusByIds(anyList(), any(InspectStatusEnum.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.updateStatusByIds(mock(List.class), mock(InspectStatusEnum.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void inspectPreview() {
+        when(impl.inspectPreview(any(UserDetail.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.inspectPreview(mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findByStatus() {
+        when(impl.findByStatus(any(InspectStatusEnum.class))).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findByStatus(mock(InspectStatusEnum.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findByResult() {
+        when(impl.findByResult(anyBoolean())).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findByResult(true);
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void cleanDeadInspect() {
+        doCallRealMethod().when(impl).cleanDeadInspect();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.cleanDeadInspect();
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void supplementAlarm() {
+        doCallRealMethod().when(impl).supplementAlarm(any(InspectDto.class), any(UserDetail.class));
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.supplementAlarm(mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findAllByIds() {
+        when(impl.findAllByIds(anyList())).thenCallRealMethod();
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findAllByIds(mock(List.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void fieldHandler() {
+        doCallRealMethod().when(impl).fieldHandler(anyList(), any(UserDetail.class));
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.fieldHandler(mock(List.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
     @Nested
     class FieldHandler{
         @Test
@@ -23,7 +415,5 @@ void test(){
                 Assertions.assertThrows(BizException.class,()->inspectService.fieldHandler(null,null));
             }
         }
-
-
     }
 }
diff --git a/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImplTest.java b/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImplTest.java
new file mode 100644
index 0000000000..e289d75354
--- /dev/null
+++ b/manager/tm-oss/src/test/java/com/tapdata/tm/inspect/service/InspectTaskServiceOOSImplTest.java
@@ -0,0 +1,159 @@
+package com.tapdata.tm.inspect.service;
+
+import com.tapdata.tm.base.dto.Where;
+import com.tapdata.tm.base.exception.BizException;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.inspect.dto.InspectDto;
+import com.tapdata.tm.utils.MessageUtil;
+import com.tapdata.tm.webhook.enums.ConstVariable;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+
+class InspectTaskServiceOOSImplTest {
+    InspectTaskServiceOOSImpl impl;
+    @BeforeEach
+    void init() {
+        impl = new InspectTaskServiceOOSImpl();
+    }
+
+    @Test
+    void inspectTaskRun() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.inspectTaskRun(mock(Where.class), mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void inspectTaskStop() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.inspectTaskStop("id", mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void inspectTaskError() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.inspectTaskError("id", mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void inspectTaskDone() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.inspectTaskDone("id", mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void executeInspect() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.executeInspect(mock(Where.class), mock(InspectDto.class), mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void startInspectTask() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.startInspectTask(mock(InspectDto.class), "id");
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void stopInspectTask() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.stopInspectTask(mock(InspectDto.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findTaskList() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findTaskList(mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+
+    @Test
+    void findConnectionList() {
+        try(MockedStatic<MessageUtil> messageUtilMockedStatic = Mockito.mockStatic(MessageUtil.class)) {
+            messageUtilMockedStatic.when(() -> MessageUtil.getMessage(anyString())).thenReturn("error");
+            Assertions.assertThrows(BizException.class, () -> {
+                try {
+                    impl.findConnectionList(mock(UserDetail.class));
+                } catch (BizException e) {
+                    Assertions.assertEquals(ConstVariable.TA_OSS_NON_SUPPORT_FUNCTION_EXCEPTION, e.getErrorCode());
+                    throw e;
+                }
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImplTest.java b/manager/tm/src/test/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImplTest.java
index 4216fd189c..d807e2d355 100644
--- a/manager/tm/src/test/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImplTest.java
+++ b/manager/tm/src/test/java/com/tapdata/tm/ds/service/impl/DataSourceServiceImplTest.java
@@ -13,6 +13,7 @@
 import com.tapdata.tm.ds.repository.DataSourceRepository;
 import com.tapdata.tm.metadatainstance.service.MetadataInstancesService;
 import com.tapdata.tm.permissions.DataPermissionHelper;
+import com.tapdata.tm.report.service.UserDataReportService;
 import com.tapdata.tm.worker.service.WorkerService;
 import org.bson.types.ObjectId;
 import org.junit.jupiter.api.*;
@@ -140,6 +141,8 @@ void before(){
         }
         @Test
         void test() {
+            UserDataReportService userDataReportService = mock(UserDataReportService.class);
+            ReflectionTestUtils.setField(dataSourceService,"userDataReportService",userDataReportService);
             doNothing().when(dataSourceService).beforeSave(any(),any());
             when(dataSourceRepository.save(any(),any())).thenReturn(mock(DataSourceEntity.class));
             DataSourceConnectionDto result = dataSourceService.add(new DataSourceConnectionDto(),mock(UserDetail.class));
@@ -299,4 +302,79 @@ void testFindByIdReturnNull() {
             verify(dataSourceRepository, never()).update(any(Query.class), any(Update.class), eq(userDetail));
         }
     }
+
+    @Nested
+    @DisplayName("Method hiddenMqPasswd test")
+    class hiddenMqPasswdTest {
+        private DataSourceServiceImpl dataSourceService;
+        private DataSourceRepository dataSourceRepository;
+
+        @BeforeEach
+        void setUp() {
+            dataSourceRepository = mock(DataSourceRepository.class);
+            dataSourceService = spy(new DataSourceServiceImpl(dataSourceRepository));
+        }
+
+        @Test
+        void testHiddenMqPasswd_NullOrEmpty() {
+            DataSourceConnectionDto dto = null;
+            doReturn(false).when(dataSourceService).isAgentReq();
+            dataSourceService.hiddenMqPasswd(dto);
+
+            dto = new DataSourceConnectionDto();
+            dataSourceService.hiddenMqPasswd(dto);
+            Assertions.assertNull(dto.getConfig(), "Config should be null when DataSourceConnectionDto is empty");
+
+            dto.setConfig(new HashMap<>());
+            dataSourceService.hiddenMqPasswd(dto);
+            Assertions.assertTrue(dto.getConfig().isEmpty(), "Config should be empty when DataSourceConnectionDto is empty");
+        }
+        @Test
+        void testHiddenMqPasswd_WithMongoUri() {
+            String originalUri = "mongodb://user:password@localhost:27017/db";
+            Map<String, Object> config = new HashMap<>();
+            config.put("uri", originalUri);
+
+            DataSourceConnectionDto dto = new DataSourceConnectionDto();
+            dto.setConfig(config);
+            dto.setDatabase_type("mongo");
+            doReturn(false).when(dataSourceService).isAgentReq();
+            dataSourceService.hiddenMqPasswd(dto);
+
+            Assertions.assertEquals("mongodb://user:******@localhost:27017/db", dto.getConfig().get("uri"),
+                    "MongoDB URI password should be masked");
+        }
+
+        @Test
+        void testHiddenMqPasswd_WithMongoAtlasUri() {
+            String originalUri = "mongodb+srv://tapdata:123456@test/test";
+            Map<String, Object> config = new HashMap<>();
+            config.put("uri", originalUri);
+
+            DataSourceConnectionDto dto = new DataSourceConnectionDto();
+            dto.setConfig(config);
+            dto.setDatabase_type("mongo");
+            doReturn(false).when(dataSourceService).isAgentReq();
+            dataSourceService.hiddenMqPasswd(dto);
+
+            Assertions.assertEquals("mongodb+srv://tapdata:******@test/test", dto.getConfig().get("uri"),
+                    "MongoDB URI password should be masked");
+        }
+
+        @Test
+        void testHiddenMqPasswd_WithMongoAtlasUriError() {
+            String originalUri = "test";
+            Map<String, Object> config = new HashMap<>();
+            config.put("uri", originalUri);
+
+            DataSourceConnectionDto dto = new DataSourceConnectionDto();
+            dto.setConfig(config);
+            dto.setDatabase_type("mongo");
+            doReturn(false).when(dataSourceService).isAgentReq();
+            dataSourceService.hiddenMqPasswd(dto);
+
+            Assertions.assertEquals("test", dto.getConfig().get("uri"),
+                    "MongoDB URI password should be masked");
+        }
+    }
 }
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/ds/utils/DataSourceServiceUtilTest.java b/manager/tm/src/test/java/com/tapdata/tm/ds/utils/DataSourceServiceUtilTest.java
new file mode 100644
index 0000000000..1a97c9be21
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/ds/utils/DataSourceServiceUtilTest.java
@@ -0,0 +1,57 @@
+package com.tapdata.tm.ds.utils;
+
+import com.tapdata.tm.commons.dag.AccessNodeTypeEnum;
+import com.tapdata.tm.commons.schema.DataSourceConnectionDto;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class DataSourceServiceUtilTest {
+
+    @Nested
+    class SetAccessNodeInfoFromOldConnectionDtoTest {
+        DataSourceConnectionDto oldConnection;
+        DataSourceConnectionDto update;
+        @Test
+        void testNormal() {
+            oldConnection = new DataSourceConnectionDto();
+            update = new DataSourceConnectionDto();
+            update.setAccessNodeType(AccessNodeTypeEnum.MANUALLY_SPECIFIED_BY_THE_USER.name());
+            update.setAccessNodeProcessId("id");
+            DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, update);
+            Assertions.assertEquals(AccessNodeTypeEnum.MANUALLY_SPECIFIED_BY_THE_USER.name(), update.getAccessNodeType());
+            Assertions.assertEquals("id", update.getAccessNodeProcessId());
+        }
+        @Test
+        void testOldConnectionIsNull() {
+            oldConnection = null;
+            update = new DataSourceConnectionDto();
+            DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, update);
+            Assertions.assertNull(update.getAccessNodeType());
+            Assertions.assertNull(update.getAccessNodeProcessId());
+        }
+        @Test
+        void tesUpdateIsNull() {
+            oldConnection = new DataSourceConnectionDto();
+            Assertions.assertDoesNotThrow(() -> DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, null));
+        }
+        @Test
+        void testUpdateAccessNodeTypeIsNull() {
+            oldConnection = new DataSourceConnectionDto();
+            update = new DataSourceConnectionDto();
+            update.setAccessNodeProcessId("id");
+            DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, update);
+            Assertions.assertEquals(oldConnection.getAccessNodeType(), update.getAccessNodeType());
+            Assertions.assertEquals("id", update.getAccessNodeProcessId());
+        }
+        @Test
+        void testUpdateAccessNodeProcessIdIsNull() {
+            oldConnection = new DataSourceConnectionDto();
+            update = new DataSourceConnectionDto();
+            update.setAccessNodeType(AccessNodeTypeEnum.MANUALLY_SPECIFIED_BY_THE_USER.name());
+            DataSourceServiceUtil.setAccessNodeInfoFromOldConnectionDto(oldConnection, update);
+            Assertions.assertEquals(AccessNodeTypeEnum.MANUALLY_SPECIFIED_BY_THE_USER.name(), update.getAccessNodeType());
+            Assertions.assertEquals(oldConnection.getAccessNodeProcessId(), update.getAccessNodeProcessId());
+        }
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/inspect/controller/InspectControllerTest.java b/manager/tm/src/test/java/com/tapdata/tm/inspect/controller/InspectControllerTest.java
new file mode 100644
index 0000000000..9c3f1a4c50
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/inspect/controller/InspectControllerTest.java
@@ -0,0 +1,60 @@
+package com.tapdata.tm.inspect.controller;
+
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.inspect.service.InspectService;
+import com.tapdata.tm.inspect.service.InspectTaskService;
+import com.tapdata.tm.task.service.TaskService;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.query.Query;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+class InspectControllerTest {
+    InspectController controller;
+    InspectService inspectService;
+    InspectTaskService inspectTaskService;
+    TaskService taskService;
+    @BeforeEach
+    void init() {
+        controller = mock(InspectController.class);
+        inspectService = mock(InspectService.class);
+        inspectTaskService = mock(InspectTaskService.class);
+        taskService = mock(TaskService.class);
+        doCallRealMethod().when(controller).setInspectService(inspectService);
+        doCallRealMethod().when(controller).setInspectTaskService(inspectTaskService);
+        doCallRealMethod().when(controller).setTaskService(taskService);
+        controller.setInspectService(inspectService);
+        controller.setInspectTaskService(inspectTaskService);
+        controller.setTaskService(taskService);
+    }
+    @Nested
+    class findTaskDtoTest {
+        @BeforeEach
+        void init() {
+            when(controller.findTaskDto(anyString())).thenCallRealMethod();
+            when(controller.findTaskDto(null)).thenCallRealMethod();
+            when(taskService.findOne(any(Query.class))).thenReturn(new TaskDto());
+        }
+        @Test
+        void testNormal() {
+            Assertions.assertNotNull(controller.findTaskDto(new ObjectId().toHexString()));
+            verify(taskService).findOne(any(Query.class));
+        }
+        @Test
+        void testNull() {
+            Assertions.assertNotNull(controller.findTaskDto("id"));
+            verify(taskService, times(0)).findOne(any(Query.class));
+        }
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/metadatainstance/service/MetadataInstancesServiceImplTest.java b/manager/tm/src/test/java/com/tapdata/tm/metadatainstance/service/MetadataInstancesServiceImplTest.java
index e7e1311f3d..6487dcf863 100644
--- a/manager/tm/src/test/java/com/tapdata/tm/metadatainstance/service/MetadataInstancesServiceImplTest.java
+++ b/manager/tm/src/test/java/com/tapdata/tm/metadatainstance/service/MetadataInstancesServiceImplTest.java
@@ -128,18 +128,22 @@ void testMainProcess() {
 			metadataInstancesDto1.setLastUpdate(1L);
 			metadataInstancesDto1.setSource(sourceDto1);
 			metadataInstancesDto1.setConnectionId(connectionId1);
+			metadataInstancesDto1.setQualifiedName("qualifiedName1");
 			metadataInstancesDtoList.add(metadataInstancesDto1);
 			MetadataInstancesDto metadataInstancesDto2 = new MetadataInstancesDto();
 			metadataInstancesDto2.setSource(sourceDto1);
 			metadataInstancesDto2.setLastUpdate(null);
+			metadataInstancesDto2.setQualifiedName("qualifiedName2");
 			metadataInstancesDtoList.add(metadataInstancesDto2);
 			MetadataInstancesDto metadataInstancesDto3 = new MetadataInstancesDto();
 			metadataInstancesDto3.setSource(sourceDto1);
 			metadataInstancesDto3.setLastUpdate(null);
+			metadataInstancesDto3.setQualifiedName("qualifiedName3");
 			metadataInstancesDtoList.add(metadataInstancesDto3);
 			MetadataInstancesDto metadataInstancesDto4 = new MetadataInstancesDto();
 			metadataInstancesDto4.setSource(sourceDto2);
 			metadataInstancesDto4.setLastUpdate(null);
+			metadataInstancesDto4.setQualifiedName("qualifiedName4");
 			metadataInstancesDtoList.add(metadataInstancesDto4);
 
 			doReturn(time1).when(metadataInstancesService).findDatabaseMetadataInstanceLastUpdate(connectionId1, userDetail);
diff --git a/manager/tm/src/test/java/com/tapdata/tm/monitor/service/MeasurementServiceV2ImplTest.java b/manager/tm/src/test/java/com/tapdata/tm/monitor/service/MeasurementServiceV2ImplTest.java
index b45e1d67b0..014574a65c 100644
--- a/manager/tm/src/test/java/com/tapdata/tm/monitor/service/MeasurementServiceV2ImplTest.java
+++ b/manager/tm/src/test/java/com/tapdata/tm/monitor/service/MeasurementServiceV2ImplTest.java
@@ -3,16 +3,24 @@
 
 import com.tapdata.tm.commons.task.dto.TaskDto;
 import com.tapdata.tm.monitor.constant.KeyWords;
+import com.tapdata.tm.monitor.entity.MeasurementEntity;
 import com.tapdata.tm.monitor.vo.TableSyncStaticVo;
+import org.bson.types.ObjectId;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.test.util.ReflectionTestUtils;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -79,4 +87,34 @@ void testNotFINSH() {
             verify(vo, times(0)).setSyncRate(BigDecimal.ONE);
         }
     }
+
+    @Nested
+    class FindLastMinuteByTaskIdTest {
+        MongoTemplate mongoOperations;
+
+        @BeforeEach
+        void init() {
+            mongoOperations = mock(MongoTemplate.class);
+            ReflectionTestUtils.setField(measurementServiceV2, "mongoOperations", mongoOperations);
+            when(measurementServiceV2.findLastMinuteByTaskId(anyString())).thenCallRealMethod();
+            when(mongoOperations.findOne(any(Query.class), any(Class.class), anyString())).thenReturn(mock(MeasurementEntity.class));
+        }
+
+        @Test
+        void testNormal() {
+            measurementServiceV2.findLastMinuteByTaskId("");
+            verify(mongoOperations).findOne(any(Query.class), any(Class.class), anyString());
+        }
+
+        @Test
+        void testOne() {
+            measurementServiceV2.findLastMinuteByTaskId(new ObjectId().toHexString());
+            verify(mongoOperations).findOne(any(Query.class), any(Class.class), anyString());
+        }
+        @Test
+        void testOneHasResult() {
+            measurementServiceV2.findLastMinuteByTaskId(new ObjectId().toHexString());
+            verify(mongoOperations).findOne(any(Query.class), any(Class.class), anyString());
+        }
+    }
 }
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/report/schedule/RunDaysScheduleTest.java b/manager/tm/src/test/java/com/tapdata/tm/report/schedule/RunDaysScheduleTest.java
new file mode 100644
index 0000000000..7f70bce186
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/report/schedule/RunDaysScheduleTest.java
@@ -0,0 +1,32 @@
+package com.tapdata.tm.report.schedule;
+
+import com.tapdata.tm.report.dto.RunDaysBatch;
+import com.tapdata.tm.report.service.UserDataReportService;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.internal.verification.Times;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class RunDaysScheduleTest {
+    @Nested
+    class scheduleTest{
+        private RunDaysSchedule runDaysSchedule = new RunDaysSchedule();
+        @Test
+        void testNormal(){
+            UserDataReportService userDataReportService = mock(UserDataReportService.class);
+            ReflectionTestUtils.setField(runDaysSchedule,"userDataReportService",userDataReportService);
+            Thread testThread = new Thread(()->{
+                runDaysSchedule.schedule();
+                String name = Thread.currentThread().getName();
+                assertEquals("RunDaysSchedule-schedule",name);
+                verify(userDataReportService,new Times(1)).produceData(any(RunDaysBatch.class));
+            });
+            testThread.start();
+        }
+    }
+}
diff --git a/manager/tm/src/test/java/com/tapdata/tm/report/service/UserDataReportServiceTest.java b/manager/tm/src/test/java/com/tapdata/tm/report/service/UserDataReportServiceTest.java
new file mode 100644
index 0000000000..d780109bbe
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/report/service/UserDataReportServiceTest.java
@@ -0,0 +1,262 @@
+package com.tapdata.tm.report.service;
+
+import com.tapdata.tm.report.dto.*;
+import com.tapdata.tm.report.service.platform.GoogleAnalyticsPlatform;
+import com.tapdata.tm.report.service.platform.ReportPlatform;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.*;
+import org.mockito.*;
+import org.mockito.internal.verification.Times;
+import org.springframework.test.util.ReflectionTestUtils;
+import java.util.concurrent.*;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+public class UserDataReportServiceTest {
+    private UserDataReportService userDataReportService;
+    private ReportPlatform reportPlatform;
+    @BeforeEach
+    void beforeEach(){
+        userDataReportService = mock(UserDataReportService.class);
+        reportPlatform = mock(GoogleAnalyticsPlatform.class);
+        ReflectionTestUtils.setField(userDataReportService,"reportPlatform",reportPlatform);
+        ReflectionTestUtils.setField(userDataReportService,"machineId","111");
+    }
+    @Nested
+    class InitReportDataThreadTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).initReportDataThread();
+        }
+        @Test
+        @DisplayName("test initReportDataThread method when acceptReportData is false")
+        void test1(){
+            try (MockedStatic<CompletableFuture> mb = Mockito
+                    .mockStatic(CompletableFuture.class)) {
+                mb.when(() -> CompletableFuture.runAsync(any(Runnable.class),any(Executor.class))).thenReturn(mock(CompletableFuture.class));
+                ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+                userDataReportService.initReportDataThread();
+                mb.verify(() -> CompletableFuture.runAsync(any(Runnable.class),any(Executor.class)), new Times(0));
+            }
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test initReportDataThread method when acceptReportData is true")
+        void test2(){
+            try (MockedStatic<CompletableFuture> mb = Mockito
+                    .mockStatic(CompletableFuture.class)) {
+                mb.when(() -> CompletableFuture.runAsync(any(Runnable.class),any(ExecutorService.class))).thenAnswer((invocationOnMock -> {
+                    ExecutorService actual = invocationOnMock.getArgument(1, ExecutorService.class);
+                    assertEquals(1, ((ThreadPoolExecutor)actual).getCorePoolSize());
+                    assertEquals(1, ((ThreadPoolExecutor)actual).getMaximumPoolSize());
+                    assertEquals(0, ((ThreadPoolExecutor)actual).getKeepAliveTime(TimeUnit.MILLISECONDS));
+                    return null;
+                }));
+                ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+                userDataReportService.initReportDataThread();
+                mb.verify(() -> CompletableFuture.runAsync(any(Runnable.class),any(Executor.class)), new Times(1));
+            }
+        }
+    }
+    @Nested
+    class ConsumeDataTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).consumeData(any(Object.class));
+        }
+        @Test
+        @DisplayName("test consumeData method for RunsNumBatch")
+        void test1(){
+            RunsNumBatch runsNumBatch = new RunsNumBatch();
+            userDataReportService.consumeData(runsNumBatch);
+            verify(userDataReportService,new Times(1)).runsNum(runsNumBatch);
+        }
+        @Test
+        @DisplayName("test consumeData method for RunDaysBatch")
+        void test2(){
+            RunDaysBatch runDaysBatch = new RunDaysBatch();
+            userDataReportService.consumeData(runDaysBatch);
+            verify(userDataReportService,new Times(1)).runDays(runDaysBatch);
+        }
+        @Test
+        @DisplayName("test consumeData method for UniqueInstallBatch")
+        void test3(){
+            UniqueInstallBatch uniqueInstallBatch = new UniqueInstallBatch();
+            userDataReportService.consumeData(uniqueInstallBatch);
+            verify(userDataReportService,new Times(1)).uniqueInstall(uniqueInstallBatch);
+        }
+        @Test
+        @DisplayName("test consumeData method for ConfigureSourceBatch")
+        void test4(){
+            ConfigureSourceBatch configureSourceBatch = new ConfigureSourceBatch();
+            userDataReportService.consumeData(configureSourceBatch);
+            verify(userDataReportService,new Times(1)).configureDatasource(configureSourceBatch);
+        }
+        @Test
+        @DisplayName("test consumeData method for TasksNumBatch")
+        void test5(){
+            TasksNumBatch tasksNumBatch = new TasksNumBatch();
+            userDataReportService.consumeData(tasksNumBatch);
+            verify(userDataReportService,new Times(1)).tasksNum(tasksNumBatch);
+        }
+        @Test
+        @DisplayName("test consumeData method for Object")
+        void test6(){
+            userDataReportService.consumeData(mock(Object.class));
+            verify(userDataReportService,new Times(0)).runsNum(any(RunsNumBatch.class));
+            verify(userDataReportService,new Times(0)).runDays(any(RunDaysBatch.class));
+            verify(userDataReportService,new Times(0)).uniqueInstall(any(UniqueInstallBatch.class));
+            verify(userDataReportService,new Times(0)).configureDatasource(any(ConfigureSourceBatch.class));
+            verify(userDataReportService,new Times(0)).tasksNum(any(TasksNumBatch.class));
+        }
+    }
+    @Nested
+    class ProduceDataTest{
+        private LinkedBlockingQueue reportQueue;
+        private Object obj;
+        @BeforeEach
+        void beforeEach(){
+            reportQueue = spy(LinkedBlockingQueue.class);
+            ReflectionTestUtils.setField(userDataReportService,"reportQueue",reportQueue);
+            obj = mock(Object.class);
+            doCallRealMethod().when(userDataReportService).produceData(obj);
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test produceData method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.produceData(obj);
+            verify(reportQueue,never()).offer(obj, 100, TimeUnit.MILLISECONDS);
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test produceData method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.produceData(obj);
+            verify(reportQueue,new Times(1)).offer(obj, 100, TimeUnit.MILLISECONDS);
+            Object actual = reportQueue.poll();
+            assertEquals(obj, actual);
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test produceData method when offer occur exception")
+        void test3(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            doThrow(InterruptedException.class).when(reportQueue).offer(obj,100,TimeUnit.MILLISECONDS);
+            userDataReportService.produceData(obj);
+            verify(reportQueue,new Times(1)).offer(obj, 100, TimeUnit.MILLISECONDS);
+            Object actual = reportQueue.poll();
+            assertNull(actual);
+        }
+    }
+    @Nested
+    class RunsNumTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).runsNum(any(RunsNumBatch.class));
+        }
+        @Test
+        @DisplayName("test runsNum method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.runsNum(mock(RunsNumBatch.class));
+            verify(reportPlatform,new Times(0)).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @DisplayName("test runsNum method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.runsNum(mock(RunsNumBatch.class));
+            verify(reportPlatform,new Times(1)).sendRequest(anyString(),anyString());
+        }
+    }
+    @Nested
+    class RunDaysTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).runDays(any(RunDaysBatch.class));
+        }
+        @Test
+        @DisplayName("test runDays method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.runDays(mock(RunDaysBatch.class));
+            verify(reportPlatform,new Times(0)).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @DisplayName("test runDays method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.runDays(mock(RunDaysBatch.class));
+            verify(reportPlatform,new Times(1)).sendRequest(anyString(),anyString());
+        }
+    }
+    @Nested
+    class UniqueInstallTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).uniqueInstall(any(UniqueInstallBatch.class));
+        }
+        @Test
+        @DisplayName("test uniqueInstall method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.uniqueInstall(mock(UniqueInstallBatch.class));
+            verify(reportPlatform,new Times(0)).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @DisplayName("test uniqueInstall method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.uniqueInstall(mock(UniqueInstallBatch.class));
+            verify(reportPlatform,new Times(1)).sendRequest(anyString(),anyString());
+        }
+    }
+    @Nested
+    class ConfigureDatasourceTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).configureDatasource(any(ConfigureSourceBatch.class));
+        }
+        @Test
+        @DisplayName("test configureDatasource method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.configureDatasource(mock(ConfigureSourceBatch.class));
+            verify(reportPlatform,new Times(0)).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @DisplayName("test configureDatasource method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.configureDatasource(mock(ConfigureSourceBatch.class));
+            verify(reportPlatform,new Times(1)).sendRequest(anyString(),anyString());
+        }
+    }
+    @Nested
+    class TasksNumTest{
+        @BeforeEach
+        void beforeEach(){
+            doCallRealMethod().when(userDataReportService).tasksNum(any(TasksNumBatch.class));
+        }
+        @Test
+        @DisplayName("test tasksNum method when acceptReportData is false")
+        void test1(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",false);
+            userDataReportService.tasksNum(mock(TasksNumBatch.class));
+            verify(reportPlatform,new Times(0)).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @DisplayName("test tasksNum method when acceptReportData is true")
+        void test2(){
+            ReflectionTestUtils.setField(userDataReportService,"acceptReportData",true);
+            userDataReportService.tasksNum(mock(TasksNumBatch.class));
+            verify(reportPlatform,new Times(1)).sendRequest(anyString(),anyString());
+        }
+    }
+}
diff --git a/manager/tm/src/test/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatformTest.java b/manager/tm/src/test/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatformTest.java
new file mode 100644
index 0000000000..e030d14a4a
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/report/service/platform/GoogleAnalyticsPlatformTest.java
@@ -0,0 +1,88 @@
+package com.tapdata.tm.report.service.platform;
+
+import lombok.SneakyThrows;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.junit.jupiter.api.*;
+import org.mockito.internal.verification.Times;
+import org.slf4j.Logger;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+public class GoogleAnalyticsPlatformTest {
+    @Nested
+    class GoogleAnalyticsPlatformConstructMethodTest{
+        @Test
+        void testNormal(){
+            GoogleAnalyticsPlatform platform = new GoogleAnalyticsPlatform();
+            CloseableHttpClient client = platform.getClient();
+            assertNotNull(client);
+            try {
+                client.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+    @Nested
+    class SendRequestTest{
+        private GoogleAnalyticsPlatform platform;
+        private CloseableHttpClient client;
+        @BeforeEach
+        void beforeEach(){
+            platform = mock(GoogleAnalyticsPlatform.class);
+            client = mock(CloseableHttpClient.class);
+            ReflectionTestUtils.setField(platform,"client",client);
+            doCallRealMethod().when(platform).sendRequest(anyString(),anyString());
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test sendRequest method normal")
+        void test1(){
+            when(client.execute(any(HttpPost.class))).thenReturn(null);
+            platform.sendRequest("test_event","{\"test_param\":\"111\"}");
+            verify(client,new Times(1)).execute(any(HttpPost.class));
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test sendRequest method with exception")
+        void test2(){
+            Logger logger = mock(Logger.class);
+            Field log = GoogleAnalyticsPlatform.class.getDeclaredField("log");
+            log.setAccessible(true);
+            Field modifiers = Field.class.getDeclaredField("modifiers");
+            modifiers.setAccessible(true);
+            modifiers.setInt(log, log.getModifiers() & ~Modifier.FINAL);
+            log.set(platform, logger);
+            when(client.execute(any(HttpPost.class))).thenThrow(IOException.class);
+            platform.sendRequest("test_event","{\"test_param\":\"111\"}");
+            verify(logger,new Times(1)).info(anyString(),any(Exception.class));
+        }
+    }
+    @Nested
+    class ProcessParamIfNeedTest{
+        @Test
+        void testProcessParamIfNeedNormal(){
+            String value = "100";
+            GoogleAnalyticsPlatform platform = mock(GoogleAnalyticsPlatform.class);
+            doCallRealMethod().when(platform).processParamIfNeed(value);
+            int actual = platform.processParamIfNeed(value);
+            assertEquals(100, actual);
+        }
+        @Test
+        void testProcessParamIfNeedWithEx(){
+            String value = "100l";
+            GoogleAnalyticsPlatform platform = mock(GoogleAnalyticsPlatform.class);
+            doCallRealMethod().when(platform).processParamIfNeed(value);
+            int actual = platform.processParamIfNeed(value);
+            assertEquals(5000, actual);
+        }
+    }
+}
diff --git a/manager/tm/src/test/java/com/tapdata/tm/report/util/UserDataReportUtilsTest.java b/manager/tm/src/test/java/com/tapdata/tm/report/util/UserDataReportUtilsTest.java
new file mode 100644
index 0000000000..4e62e48af7
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/report/util/UserDataReportUtilsTest.java
@@ -0,0 +1,114 @@
+package com.tapdata.tm.report.util;
+
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.Vector;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class UserDataReportUtilsTest {
+    @Nested
+    class GenerateMachineIdTest{
+        @Test
+        @SneakyThrows
+        @DisplayName("test generateMachineId method normal")
+        void test1(){
+            NetworkInterface networkInterface = mock(NetworkInterface.class);
+            Vector v = new Vector();
+            v.addElement(networkInterface);
+            Enumeration<NetworkInterface> interfaces = v.elements();
+            byte[] mac = new byte[1];
+            mac[0] = 1;
+            try (MockedStatic<NetworkInterface> mb = Mockito
+                    .mockStatic(NetworkInterface.class)) {
+                mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(interfaces);
+                when(networkInterface.getHardwareAddress()).thenReturn(mac);
+                String actual = UserDataReportUtils.generateMachineId();
+                assertEquals("[01]", actual);
+            }
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test generateMachineId method when mac is null")
+        void test2(){
+            NetworkInterface networkInterface = mock(NetworkInterface.class);
+            Vector v = new Vector();
+            v.addElement(networkInterface);
+            Enumeration<NetworkInterface> interfaces = v.elements();
+            try (MockedStatic<NetworkInterface> mb = Mockito
+                    .mockStatic(NetworkInterface.class)) {
+                mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(interfaces);
+                when(networkInterface.getHardwareAddress()).thenReturn(null);
+                String actual = UserDataReportUtils.generateMachineId();
+                assertEquals("[]", actual);
+            }
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test generateMachineId method with exception")
+        void test3(){
+            NetworkInterface networkInterface = mock(NetworkInterface.class);
+            Vector v = new Vector();
+            v.addElement(networkInterface);
+            try (MockedStatic<NetworkInterface> mb = Mockito
+                    .mockStatic(NetworkInterface.class)) {
+                mb.when(NetworkInterface::getNetworkInterfaces).thenThrow(SocketException.class);
+                when(networkInterface.getHardwareAddress()).thenReturn(null);
+                String actual = UserDataReportUtils.generateMachineId();
+                assertEquals("[]", actual);
+            }
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test generateMachineId method when mac address exceed param max length")
+        void test4(){
+            NetworkInterface networkInterface = mock(NetworkInterface.class);
+            Vector v = new Vector();
+            v.addElement(networkInterface);
+            Enumeration<NetworkInterface> interfaces = v.elements();
+            byte[] mac = new byte[50];
+            for (int i=0;i<50;i++){
+                mac[i] = 1;
+            }
+            try (MockedStatic<NetworkInterface> mb = Mockito
+                    .mockStatic(NetworkInterface.class)) {
+                mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(interfaces);
+                when(networkInterface.getHardwareAddress()).thenReturn(mac);
+                String actual = UserDataReportUtils.generateMachineId();
+                assertEquals(100, actual.length());
+            }
+        }
+        @Test
+        @SneakyThrows
+        @DisplayName("test generateMachineId method when mac address less than param max length")
+        void test5(){
+            NetworkInterface networkInterface = mock(NetworkInterface.class);
+            Vector v = new Vector();
+            v.addElement(networkInterface);
+            Enumeration<NetworkInterface> interfaces = v.elements();
+            byte[] mac = new byte[20];
+            for (int i=0;i<20;i++){
+                mac[i] = 1;
+            }
+            try (MockedStatic<NetworkInterface> mb = Mockito
+                    .mockStatic(NetworkInterface.class)) {
+                mb.when(NetworkInterface::getNetworkInterfaces).thenReturn(interfaces);
+                when(networkInterface.getHardwareAddress()).thenReturn(mac);
+                String actual = UserDataReportUtils.generateMachineId();
+                assertNotEquals(100, actual.length());
+            }
+        }
+    }
+}
diff --git a/manager/tm/src/test/java/com/tapdata/tm/schedule/ChartScheduleTest.java b/manager/tm/src/test/java/com/tapdata/tm/schedule/ChartScheduleTest.java
new file mode 100644
index 0000000000..6460077595
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/schedule/ChartScheduleTest.java
@@ -0,0 +1,90 @@
+package com.tapdata.tm.schedule;
+
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.task.bean.Chart6Vo;
+import com.tapdata.tm.task.service.chart.ChartViewService;
+import com.tapdata.tm.user.service.UserService;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ChartScheduleTest {
+    ChartSchedule chartSchedule;
+    ChartViewService chartViewService;
+    UserService userService;
+    @BeforeEach
+    void init() {
+        chartSchedule = new ChartSchedule();
+        chartViewService = mock(ChartViewService.class);
+        userService = mock(UserService.class);
+        chartSchedule.setChartViewService(chartViewService);
+        chartSchedule.setUserService(userService);
+    }
+
+    @Nested
+    class Chart6Test {
+        List<UserDetail> userDetails;
+        UserDetail user;
+        Chart6Vo chart6Vo;
+        @BeforeEach
+        void init() {
+            userDetails = new ArrayList<>();
+            chart6Vo = Chart6Vo.builder().build();
+            user = mock(UserDetail.class);
+            userDetails.add(user);
+            when(userService.loadAllUser()).thenReturn(userDetails);
+            when(chartViewService.transmissionOverviewChartData(user)).thenReturn(chart6Vo);
+            when(user.getUserId()).thenReturn("gavin-id");
+        }
+
+        @Test
+        void testNormal() {
+            chart6Vo.setDeletedTotal(new BigInteger(new byte[]{100, 100}));
+            chartSchedule.chart6();
+            Assertions.assertNotNull(ChartSchedule.cache.get("gavin-id"));
+            verify(userService).loadAllUser();
+            verify(chartViewService).transmissionOverviewChartData(user);
+            verify(user).getUserId();
+        }
+
+        @Test
+        void testVoIsNull() {
+            when(chartViewService.transmissionOverviewChartData(user)).thenReturn(null);
+            chartSchedule.chart6();
+            Assertions.assertNull(ChartSchedule.cache.get("gavin-id"));
+            verify(userService).loadAllUser();
+            verify(chartViewService).transmissionOverviewChartData(user);
+            verify(user).getUserId();
+        }
+
+        @Test
+        void testVoIsEmpty() {
+            chart6Vo.setDeletedTotal(BigInteger.ZERO);
+            chart6Vo.setInputTotal(BigInteger.ZERO);
+            chart6Vo.setInsertedTotal(BigInteger.ZERO);
+            chart6Vo.setOutputTotal(BigInteger.ZERO);
+            chart6Vo.setUpdatedTotal(BigInteger.ZERO);
+            when(chartViewService.transmissionOverviewChartData(user)).thenReturn(chart6Vo);
+            chartSchedule.chart6();
+            Assertions.assertNull(ChartSchedule.cache.get("gavin-id"));
+            verify(userService).loadAllUser();
+            verify(chartViewService).transmissionOverviewChartData(user);
+            verify(user).getUserId();
+        }
+    }
+
+    @AfterEach
+    void close() {
+        ChartSchedule.cache.clear();
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskErrorEventServiceTest.java b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskErrorEventServiceTest.java
new file mode 100644
index 0000000000..4f38013f5d
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskErrorEventServiceTest.java
@@ -0,0 +1,98 @@
+package com.tapdata.tm.task.service;
+
+import com.mongodb.bulk.BulkWriteResult;
+import com.mongodb.client.result.UpdateResult;
+import com.tapdata.tm.commons.task.dto.ErrorEvent;
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.task.repository.TaskRepository;
+import com.tapdata.tm.utils.Lists;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.BulkOperations;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+class TaskErrorEventServiceTest {
+    TaskErrorEventService taskErrorEventService;
+    TaskRepository taskRepository;
+    TaskService taskService;
+    UserDetail user;
+
+    @BeforeEach
+    void init() {
+        user = mock(UserDetail.class);
+        taskErrorEventService = new TaskErrorEventService();
+        taskRepository = mock(TaskRepository.class);
+        taskService = mock(TaskService.class);
+
+        taskErrorEventService.setTaskRepository(taskRepository);
+        taskErrorEventService.setTaskService(taskService);
+    }
+
+    @Nested
+    class GetErrorEventByTaskIdTest {
+        TaskDto taskDto;
+        @BeforeEach
+        void init() {
+            taskDto = mock(TaskDto.class);
+            when(taskDto.getErrorEvents()).thenReturn(new ArrayList<>());
+        }
+
+        @Test
+        void testNormal() {
+            when(taskService.findOne(any(Query.class), any(UserDetail.class))).thenReturn(taskDto);
+            List<ErrorEvent> errorEventByTaskId = taskErrorEventService.getErrorEventByTaskId(new ObjectId().toHexString(), user);
+            Assertions.assertNotNull(errorEventByTaskId);
+        }
+
+        @Test
+        void testTaskDtoIsNull() {
+            when(taskService.findOne(any(Query.class), any(UserDetail.class))).thenReturn(null);
+            List<ErrorEvent> errorEventByTaskId = taskErrorEventService.getErrorEventByTaskId(new ObjectId().toHexString(), user);
+            Assertions.assertNotNull(errorEventByTaskId);
+        }
+    }
+
+    @Nested
+    class SignSkipErrorEventsTest {
+        BulkOperations bulkOperations;
+        @BeforeEach
+        void init() {
+            bulkOperations = mock(BulkOperations.class);
+            when(taskService.update(any(Query.class), any(Update.class))).thenReturn(mock(UpdateResult.class));
+            when(taskRepository.bulkOperations(BulkOperations.BulkMode.UNORDERED)).thenReturn(bulkOperations);
+            when(bulkOperations.updateMulti(any(Query.class), any(Update.class))).thenReturn(bulkOperations);
+            when(bulkOperations.execute()).thenReturn(mock(BulkWriteResult.class));
+        }
+        @Test
+        void testNormal() {
+            taskErrorEventService.signSkipErrorEvents(new ObjectId().toHexString(), Lists.newArrayList(new ObjectId().toHexString()));
+            verify(taskService, times(0)).update(any(Query.class), any(Update.class));
+            verify(taskRepository, times(1)).bulkOperations(BulkOperations.BulkMode.UNORDERED);
+            verify(bulkOperations, times(2)).updateMulti(any(Query.class), any(Update.class));
+            verify(bulkOperations, times(1)).execute();
+        }
+        @Test
+        void testEmpty() {
+            taskErrorEventService.signSkipErrorEvents(new ObjectId().toHexString(), Lists.newArrayList());
+            verify(taskService, times(1)).update(any(Query.class), any(Update.class));
+            verify(taskRepository, times(0)).bulkOperations(BulkOperations.BulkMode.UNORDERED);
+            verify(bulkOperations, times(0)).updateMulti(any(Query.class), any(Update.class));
+            verify(bulkOperations, times(0)).execute();
+        }
+    }
+}
\ No newline at end of file
diff --git a/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceImplTest.java b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceImplTest.java
index 910505423e..fb3b5bb766 100644
--- a/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceImplTest.java
+++ b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceImplTest.java
@@ -34,6 +34,7 @@
 import com.tapdata.tm.externalStorage.service.ExternalStorageService;
 import com.tapdata.tm.inspect.bean.Source;
 import com.tapdata.tm.inspect.bean.Stats;
+import com.tapdata.tm.inspect.constant.InspectStatusEnum;
 import com.tapdata.tm.inspect.dto.InspectDto;
 import com.tapdata.tm.inspect.dto.InspectResultDto;
 import com.tapdata.tm.inspect.service.InspectResultService;
@@ -56,6 +57,7 @@
 import com.tapdata.tm.permissions.constants.DataPermissionActionEnums;
 import com.tapdata.tm.permissions.constants.DataPermissionMenuEnums;
 import com.tapdata.tm.permissions.service.DataPermissionService;
+import com.tapdata.tm.report.service.UserDataReportService;
 import com.tapdata.tm.schedule.service.ScheduleService;
 import com.tapdata.tm.statemachine.enums.DataFlowEvent;
 import com.tapdata.tm.statemachine.model.StateMachineResult;
@@ -70,6 +72,7 @@
 import com.tapdata.tm.task.repository.TaskRepository;
 import com.tapdata.tm.task.service.batchin.ParseRelMigFile;
 import com.tapdata.tm.task.service.batchin.entity.ParseParam;
+import com.tapdata.tm.task.service.chart.ChartViewService;
 import com.tapdata.tm.task.service.utils.TaskServiceUtil;
 import com.tapdata.tm.task.vo.ShareCacheDetailVo;
 import com.tapdata.tm.task.vo.ShareCacheVo;
@@ -131,6 +134,7 @@ class TaskServiceImplTest {
     WorkerService workerService;
     TaskDto taskDto;
     UserDetail user;
+    ChartViewService chartViewService;
     @BeforeEach
     void init() {
         taskService = mock(TaskServiceImpl.class);
@@ -138,6 +142,8 @@ void init() {
         ReflectionTestUtils.setField(taskService, "agentGroupService", agentGroupService);
         workerService = mock(WorkerService.class);
         ReflectionTestUtils.setField(taskService, "workerService", workerService);
+        chartViewService = mock(ChartViewService.class);
+        ReflectionTestUtils.setField(taskService, "chartViewService", chartViewService);
         taskDto = mock(TaskDto.class);
         user = mock(UserDetail.class);
     }
@@ -592,6 +598,7 @@ void test1(){
             doCallRealMethod().when(taskService).create(taskDto,user);
             TaskDto actual = taskService.create(taskDto, user);
             assertEquals(taskDto, actual);
+            assertFalse(actual.getOldVersionTimezone());
         }
         @Test
         @DisplayName("test create method when cron schedule error")
@@ -1950,30 +1957,59 @@ void test2(){
     }
     @Nested
     class InspectChartTest{
-        private TaskAutoInspectResultsService taskAutoInspectResultsService;
+        List<InspectDto> list;
         @BeforeEach
         void beforeEach(){
-            taskAutoInspectResultsService = mock(TaskAutoInspectResultsService.class);
-            ReflectionTestUtils.setField(taskService,"taskAutoInspectResultsService",taskAutoInspectResultsService);
+            list = new ArrayList<>();
+            InspectDto error = new InspectDto();
+            error.setStatus(InspectStatusEnum.ERROR.getValue());
+            list.add(error);
+            InspectDto running = new InspectDto();
+            running.setStatus(InspectStatusEnum.RUNNING.getValue());
+            list.add(running);
+            InspectDto done = new InspectDto();
+            done.setStatus(InspectStatusEnum.DONE.getValue());
+            list.add(done);
+            InspectDto waiting = new InspectDto();
+            waiting.setStatus(InspectStatusEnum.WAITING.getValue());
+            list.add(waiting);
+            InspectDto scheduling = new InspectDto();
+            scheduling.setStatus(InspectStatusEnum.SCHEDULING.getValue());
+            list.add(scheduling);
+            InspectDto stopping = new InspectDto();
+            stopping.setStatus(InspectStatusEnum.STOPPING.getValue());
+            list.add(stopping);
+            InspectDto o = new InspectDto();
+            o.setStatus(InspectStatusEnum.PASSED.getValue());
+            list.add(o);
+            when(taskService.inspectTaskList(any(Filter.class), any(UserDetail.class))).thenReturn(list);
+            when(taskService.inspectChart(user)).thenCallRealMethod();
         }
         @Test
         void testInspectChartNormal(){
-            List<TaskDto> taskDtos = new ArrayList<>();
-            TaskDto dto = mock(TaskDto.class);
-            taskDtos.add(dto);
-            when(taskService.findAllDto(any(Query.class),any(UserDetail.class))).thenReturn(taskDtos);
-            when(dto.getCanOpenInspect()).thenReturn(true);
-            when(dto.getStatus()).thenReturn("error");
-            ObjectId id = mock(ObjectId.class);
-            when(dto.getId()).thenReturn(id);
-            Set<String> taskSet = new HashSet<>();
-            taskSet.add(id.toHexString());
-            when(taskAutoInspectResultsService.groupByTask(user)).thenReturn(taskSet);
-            doCallRealMethod().when(taskService).inspectChart(user);
             Map<String, Integer> actual = taskService.inspectChart(user);
-            assertEquals(1,actual.get("error"));
-            assertEquals(1,actual.get("can"));
-            assertEquals(1,actual.get("diff"));
+            Assertions.assertNotNull(actual);
+            assertEquals(list.size(), actual.get("total"));
+            assertEquals(1, actual.get("error"));
+            assertEquals(1, actual.get("running"));
+            assertEquals(1, actual.get("done"));
+            assertEquals(1, actual.get("waiting"));
+            assertEquals(1, actual.get("scheduling"));
+            assertEquals(1, actual.get("stopping"));
+        }
+
+        @Test
+        void testEmpty() {
+            list.clear();
+            Map<String, Integer> actual = taskService.inspectChart(user);
+            Assertions.assertNotNull(actual);
+            assertEquals(list.size(), actual.get("total"));
+            assertEquals(0, actual.get("error"));
+            assertEquals(0, actual.get("running"));
+            assertEquals(0, actual.get("done"));
+            assertEquals(0, actual.get("waiting"));
+            assertEquals(0, actual.get("scheduling"));
+            assertEquals(0, actual.get("stopping"));
         }
     }
     @Nested
@@ -2339,6 +2375,8 @@ void beforeEach(){
             lockControlService = mock(LockControlService.class);
             disruptorService = mock(DisruptorService.class);
             logCollectorService = mock(LogCollectorService.class);
+            UserDataReportService userDataReportService = mock(UserDataReportService.class);
+            ReflectionTestUtils.setField(taskService,"userDataReportService",userDataReportService);
             ReflectionTestUtils.setField(taskService,"lockControlService",lockControlService);
             ReflectionTestUtils.setField(taskService,"disruptorService",disruptorService);
             ReflectionTestUtils.setField(taskService,"logCollectorService",logCollectorService);
@@ -2401,11 +2439,22 @@ void test5(){
             when(taskDto.getTaskRecordId()).thenReturn("");
             when(taskDto.getDag()).thenReturn(mock(DAG.class));
             when(taskDto.getStatus()).thenReturn("edit");
-            when(taskDto.getSyncType()).thenReturn("heartbeat");
+            when(taskDto.getSyncType()).thenReturn("connHeartbeat");
             doCallRealMethod().when(taskService).start(taskDto,user,startFlag);
             taskService.start(taskDto,user,startFlag);
             verify(taskService,new Times(1)).run(taskDto,user);
         }
+        @Test
+        @DisplayName("test start method when sync type heartbeat and status is renew failed")
+        void test6(){
+            when(taskDto.getTaskRecordId()).thenReturn("");
+            when(taskDto.getDag()).thenReturn(mock(DAG.class));
+            when(taskDto.getStatus()).thenReturn("renew_failed");
+            when(taskDto.getSyncType()).thenReturn("connHeartbeat");
+            doCallRealMethod().when(taskService).start(taskDto,user,startFlag);
+            taskService.start(taskDto,user,startFlag);
+            verify(taskService,new Times(0)).run(taskDto,user);
+        }
     }
     @Nested
     class UpdateTaskRecordsStatusTest{
@@ -3348,7 +3397,7 @@ void testChartNormal(){
                 when(permission.MigrateTack.checkAndSetFilter(user, DataPermissionActionEnums.View, () -> taskService.findAllDto(any(),any()))).thenReturn(taskDtoList);
                 doReturn(new HashMap()).when(taskService).inspectChart(user);
                 Chart6Vo chart6Vo = mock(Chart6Vo.class);
-                doReturn(chart6Vo).when(taskService).chart6(user);
+                doReturn(chart6Vo).when(chartViewService).transmissionOverviewChartData(taskDtoList);
                 Map<String, Object> actual = taskService.chart(user);
                 Map chart1 = (Map) actual.get("chart1");
                 assertEquals(3,chart1.get("total"));
@@ -4333,95 +4382,7 @@ void test3(){
             verify(taskService).updateById(any(ObjectId.class),any(Update.class),any(UserDetail.class));
         }
     }
-    @Nested
-    class Chart6Test{
-        private MeasurementServiceV2 measurementServiceV2;
-        @BeforeEach
-        void beforeEach(){
-            measurementServiceV2 = mock(MeasurementServiceV2.class);
-            ReflectionTestUtils.setField(taskService,"measurementServiceV2",measurementServiceV2);
-        }
-        @Test
-        @DisplayName("test chart6 method normal")
-        void test1(){
-            List<TaskDto> allDto = new ArrayList<>();
-            TaskDto task = new TaskDto();
-            String id = "65bc933c6129fe73d7858b40";
-            task.setId(new ObjectId(id));
-            allDto.add(task);
-            when(taskService.findAllDto(any(Query.class),any(UserDetail.class))).thenReturn(allDto);
-            MeasurementEntity measurement = new MeasurementEntity();
-            List<Sample> samples = new ArrayList<>();
-            Sample sample = new Sample();
-            sample.setDate(new Date());
-            Map<String, Number> vs = new HashMap<>();
-            vs.put("inputInsertTotal",1);
-            vs.put("outputInsertTotal",1);
-            vs.put("inputUpdateTotal",1);
-            vs.put("inputDeleteTotal",1);
-            sample.setVs(vs);
-            samples.add(sample);
-            measurement.setSamples(samples);
-            when(measurementServiceV2.findLastMinuteByTaskId(id)).thenReturn(measurement);
-            doCallRealMethod().when(taskService).chart6(user);
-            Chart6Vo actual = taskService.chart6(user);
-            assertNotEquals(3,actual.getInputTotal());
-            assertNotEquals(1,actual.getInsertedTotal());
-            assertNotEquals(1,actual.getOutputTotal());
-            assertNotEquals(1,actual.getUpdatedTotal());
-            assertNotEquals(1,actual.getDeletedTotal());
-        }
-        @Test
-        @DisplayName("test chart6 method when ids is empty")
-        void test2(){
-            List<TaskDto> allDto = new ArrayList<>();
-            when(taskService.findAllDto(any(Query.class),any(UserDetail.class))).thenReturn(allDto);
-            doCallRealMethod().when(taskService).chart6(user);
-            Chart6Vo actual = taskService.chart6(user);
-            assertNotEquals(0,actual.getInputTotal());
-            assertNotEquals(0,actual.getInsertedTotal());
-            assertNotEquals(0,actual.getOutputTotal());
-            assertNotEquals(0,actual.getUpdatedTotal());
-            assertNotEquals(0,actual.getDeletedTotal());
-        }
-        @Test
-        @DisplayName("test chart6 method when measurement is null")
-        void test3(){
-            List<TaskDto> allDto = new ArrayList<>();
-            TaskDto task = new TaskDto();
-            String id = "65bc933c6129fe73d7858b40";
-            task.setId(new ObjectId(id));
-            allDto.add(task);
-            when(taskService.findAllDto(any(Query.class),any(UserDetail.class))).thenReturn(allDto);
-            when(measurementServiceV2.findLastMinuteByTaskId(id)).thenReturn(null);
-            doCallRealMethod().when(taskService).chart6(user);
-            Chart6Vo actual = taskService.chart6(user);
-            assertNotEquals(0,actual.getInputTotal());
-            assertNotEquals(0,actual.getInsertedTotal());
-            assertNotEquals(0,actual.getOutputTotal());
-            assertNotEquals(0,actual.getUpdatedTotal());
-            assertNotEquals(0,actual.getDeletedTotal());
-        }
-        @Test
-        @DisplayName("test chart6 method when samples is empty")
-        void test4(){
-            List<TaskDto> allDto = new ArrayList<>();
-            TaskDto task = new TaskDto();
-            String id = "65bc933c6129fe73d7858b40";
-            task.setId(new ObjectId(id));
-            allDto.add(task);
-            when(taskService.findAllDto(any(Query.class),any(UserDetail.class))).thenReturn(allDto);
-            MeasurementEntity measurement = new MeasurementEntity();
-            when(measurementServiceV2.findLastMinuteByTaskId(id)).thenReturn(measurement);
-            doCallRealMethod().when(taskService).chart6(user);
-            Chart6Vo actual = taskService.chart6(user);
-            assertNotEquals(0,actual.getInputTotal());
-            assertNotEquals(0,actual.getInsertedTotal());
-            assertNotEquals(0,actual.getOutputTotal());
-            assertNotEquals(0,actual.getUpdatedTotal());
-            assertNotEquals(0,actual.getDeletedTotal());
-        }
-    }
+
     @Nested
     class StopTaskIfNeedByAgentIdTest{
         @Test
diff --git a/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceTest.java b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceTest.java
index ac9ff8dafa..4e31202b2b 100644
--- a/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceTest.java
+++ b/manager/tm/src/test/java/com/tapdata/tm/task/service/TaskServiceTest.java
@@ -27,6 +27,7 @@
 import com.tapdata.tm.task.repository.TaskRepository;
 import com.tapdata.tm.task.service.batchin.ParseRelMigFile;
 import com.tapdata.tm.task.service.batchin.entity.ParseParam;
+import com.tapdata.tm.task.service.chart.ChartViewService;
 import com.tapdata.tm.userLog.service.UserLogService;
 import com.tapdata.tm.utils.MongoUtils;
 import com.tapdata.tm.utils.SpringContextHelper;
@@ -63,6 +64,12 @@
 @ExtendWith(MockitoExtension.class)
 public class TaskServiceTest {
     private TaskServiceImpl taskService;
+    ChartViewService chartViewService;
+
+    @BeforeEach
+    void init() {
+        chartViewService = mock(ChartViewService.class);
+    }
 
     @Test
     void testFindRunningTasksByAgentIdWithoutId() {
@@ -621,6 +628,7 @@ void testChartNormal() {
                     .mockStatic(DataPermissionService.class)) {
                 mb.when(DataPermissionService::isCloud).thenReturn(true);
                 taskService = spy(new TaskServiceImpl(taskRepository));
+                ReflectionTestUtils.setField(taskService, "chartViewService", chartViewService);
                 UserDetail user = mock(UserDetail.class);
                 DataPermissionMenuEnums permission = mock(DataPermissionMenuEnums.class);
                 List<TaskDto> taskDtoList = new ArrayList<>();
@@ -648,7 +656,7 @@ void testChartNormal() {
                 when(permission.MigrateTack.checkAndSetFilter(user, DataPermissionActionEnums.View, () -> taskService.findAllDto(any(), any()))).thenReturn(taskDtoList);
                 doReturn(new HashMap()).when(taskService).inspectChart(user);
                 Chart6Vo chart6Vo = mock(Chart6Vo.class);
-                doReturn(chart6Vo).when(taskService).chart6(user);
+                doReturn(chart6Vo).when(chartViewService).transmissionOverviewChartData(taskDtoList);
                 Map<String, Object> actual = taskService.chart(user);
                 Map chart1 = (Map) actual.get("chart1");
                 assertEquals(3, chart1.get("total"));
diff --git a/manager/tm/src/test/java/com/tapdata/tm/task/service/chart/ChartViewServiceTest.java b/manager/tm/src/test/java/com/tapdata/tm/task/service/chart/ChartViewServiceTest.java
new file mode 100644
index 0000000000..757cb2e903
--- /dev/null
+++ b/manager/tm/src/test/java/com/tapdata/tm/task/service/chart/ChartViewServiceTest.java
@@ -0,0 +1,155 @@
+package com.tapdata.tm.task.service.chart;
+
+import com.tapdata.tm.commons.task.dto.TaskDto;
+import com.tapdata.tm.config.security.UserDetail;
+import com.tapdata.tm.monitor.entity.MeasurementEntity;
+import com.tapdata.tm.monitor.service.MeasurementServiceV2;
+import com.tapdata.tm.task.bean.Chart6Vo;
+import com.tapdata.tm.task.service.TaskService;
+import com.tapdata.tm.utils.Lists;
+import io.tapdata.common.sample.request.Sample;
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.query.Query;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ChartViewServiceTest {
+    UserDetail user;
+    ChartViewService chartViewService;
+    TaskService taskService;
+    MeasurementServiceV2 measurementServiceV2;
+    List<TaskDto> allDto;
+
+    @BeforeEach
+    void init() {
+        allDto = new ArrayList<>();
+        user = mock(UserDetail.class);
+        chartViewService = mock(ChartViewService.class);
+        taskService = mock(TaskService.class);
+        measurementServiceV2 = mock(MeasurementServiceV2.class);
+
+        doCallRealMethod().when(chartViewService).setMeasurementServiceV2(measurementServiceV2);
+        doCallRealMethod().when(chartViewService).setTaskService(taskService);
+        when(chartViewService.getViewTaskDtoByUser(user)).thenReturn(allDto);
+        when(chartViewService.getViewTaskDto(user)).thenCallRealMethod();
+        when(chartViewService.transmissionOverviewChartData(user)).thenCallRealMethod();
+        when(chartViewService.transmissionOverviewChartData(allDto)).thenCallRealMethod();
+
+
+        chartViewService.setMeasurementServiceV2(measurementServiceV2);
+        chartViewService.setTaskService(taskService);
+    }
+
+    @Nested
+    class Chart6Test {
+        @Test
+        @DisplayName("test chart6 method normal")
+        void test1() {
+            allDto.clear();
+            TaskDto task = new TaskDto();
+            String id = "65bc933c6129fe73d7858b40";
+            task.setId(new ObjectId(id));
+            allDto.add(task);
+            when(taskService.findAllDto(any(Query.class), any(UserDetail.class))).thenReturn(allDto);
+            MeasurementEntity measurement = new MeasurementEntity();
+
+            List<Sample> samples = new ArrayList<>();
+            Sample sample = new Sample();
+            sample.setDate(new Date());
+            Map<String, Number> vs = new HashMap<>();
+            vs.put("inputInsertTotal", 1);
+            vs.put("outputInsertTotal", 1);
+            vs.put("inputUpdateTotal", 1);
+            vs.put("inputDeleteTotal", 1);
+            sample.setVs(vs);
+            samples.add(sample);
+            measurement.setSamples(samples);
+
+            when(measurementServiceV2.findLastMinuteByTaskId(anyString())).thenReturn(measurement);
+            Chart6Vo actual = chartViewService.transmissionOverviewChartData(user);
+            assertNotEquals(3, actual.getInputTotal());
+            assertNotEquals(1, actual.getInsertedTotal());
+            assertNotEquals(1, actual.getOutputTotal());
+            assertNotEquals(1, actual.getUpdatedTotal());
+            assertNotEquals(1, actual.getDeletedTotal());
+        }
+
+        @Test
+        @DisplayName("test chart6 method when ids is empty")
+        void test2() {
+            allDto.clear();
+            when(taskService.findAllDto(any(Query.class), any(UserDetail.class))).thenReturn(allDto);
+            Chart6Vo actual = chartViewService.transmissionOverviewChartData(user);
+            assertNotEquals(0, actual.getInputTotal());
+            assertNotEquals(0, actual.getInsertedTotal());
+            assertNotEquals(0, actual.getOutputTotal());
+            assertNotEquals(0, actual.getUpdatedTotal());
+            assertNotEquals(0, actual.getDeletedTotal());
+        }
+
+        @Test
+        @DisplayName("test chart6 method when measurement is null")
+        void test3() {
+            allDto.clear();
+            TaskDto task = new TaskDto();
+            String id = "65bc933c6129fe73d7858b40";
+            task.setId(new ObjectId(id));
+            allDto.add(task);
+            when(taskService.findAllDto(any(Query.class), any(UserDetail.class))).thenReturn(allDto);
+
+            when(measurementServiceV2.findLastMinuteByTaskId(anyString())).thenReturn(null);
+
+            Chart6Vo actual = chartViewService.transmissionOverviewChartData(user);
+            assertNotEquals(0, actual.getInputTotal());
+            assertNotEquals(0, actual.getInsertedTotal());
+            assertNotEquals(0, actual.getOutputTotal());
+            assertNotEquals(0, actual.getUpdatedTotal());
+            assertNotEquals(0, actual.getDeletedTotal());
+        }
+
+        @Test
+        @DisplayName("test chart6 method when samples is empty")
+        void test4() {
+            allDto.clear();
+            TaskDto task = new TaskDto();
+            String id = "65bc933c6129fe73d7858b40";
+            task.setId(new ObjectId(id));
+            allDto.add(task);
+            when(taskService.findAllDto(any(Query.class), any(UserDetail.class))).thenReturn(allDto);
+            MeasurementEntity measurement = new MeasurementEntity();
+            when(measurementServiceV2.findLastMinuteByTaskId(anyString())).thenReturn(measurement);
+            Chart6Vo actual = chartViewService.transmissionOverviewChartData(user);
+            assertNotEquals(0, actual.getInputTotal());
+            assertNotEquals(0, actual.getInsertedTotal());
+            assertNotEquals(0, actual.getOutputTotal());
+            assertNotEquals(0, actual.getUpdatedTotal());
+            assertNotEquals(0, actual.getDeletedTotal());
+        }
+    }
+
+    @Nested
+    class GetViewTaskDtoTest {
+        @Test
+        void testNormal() {
+            when(taskService.findAllDto(any(Query.class), any(UserDetail.class))).thenReturn(allDto);
+            List<TaskDto> viewTaskDto = chartViewService.getViewTaskDto(user);
+            verify(taskService).findAllDto(any(Query.class), any(UserDetail.class));
+        }
+    }
+}
\ No newline at end of file